Replace the custom page size prompt with a dialog

window.prompt() looks out of place and behaves badly in an installed
PWA, especially on iPad. The new dialog has width/height fields in mm,
preset chips and a swap button, validates the 50–5000 pt range, and
keeps preset and unchanged sizes exact instead of rounding them to
whole millimetres.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GaoZtJQZwLXkEgWs8twCia
This commit is contained in:
agent 2026-09-27 21:30:31 +00:00
parent 5a501a5c87
commit f018012cdd
2 changed files with 191 additions and 10 deletions

View file

@ -21,6 +21,7 @@
import { StrokeRecorder, hitStroke } from '$lib/ink/stroke';
import { pageCssVars } from '$lib/editor/pageStyle';
import { downloadCanvas } from './download';
import PageSizeDialog from './PageSizeDialog.svelte';
import PageView from './PageView.svelte';
import Toolbar from './Toolbar.svelte';
@ -241,15 +242,9 @@
menu.show(x, y, items);
}
function customSize(id: string) {
const page = doc.pages[id];
const mm = (pt: number) => Math.round((pt / 72) * 25.4);
const answer = prompt('Page size in millimetres (width × height)', `${mm(page.width)} × ${mm(page.height)}`);
const m = answer && /^\s*(\d+(?:\.\d+)?)\s*[x×*,\s]\s*(\d+(?:\.\d+)?)\s*$/i.exec(answer);
if (!m) return;
const pt = (v: string) => Math.min(5000, Math.max(50, (parseFloat(v) / 25.4) * 72));
keepAnchor(id, () => doc.resizePage(id, pt(m[1]), pt(m[2])));
}
/** Page whose custom-size dialog is open. */
let sizing = $state<string | null>(null);
const customSize = (id: string) => (sizing = id);
const activeMenuPos = $derived.by(() => {
const id = doc.activeId;
@ -551,7 +546,7 @@
}
function onKeyDown(e: KeyboardEvent) {
if (workspace.settingsOpen) return;
if (workspace.settingsOpen || sizing) return;
const mod = e.ctrlKey || e.metaKey;
const k = e.key.toLowerCase();
@ -707,6 +702,19 @@
{/if}
</div>
{#if sizing && doc.pages[sizing]}
{@const id = sizing}
<PageSizeDialog
width={doc.pages[id].width}
height={doc.pages[id].height}
onclose={() => (sizing = null)}
onapply={(w, h) => {
sizing = null;
keepAnchor(id, () => doc.resizePage(id, w, h));
}}
/>
{/if}
<Toolbar {doc} onfit={() => fitPage(doc.activeId)} onfitall={fitAll} />
</div>