@magicpages/kalotyp-ui 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/canvas/position-handles.d.ts.map +1 -1
- package/dist/canvas/preview-canvas.d.ts +1 -1
- package/dist/canvas/preview-canvas.d.ts.map +1 -1
- package/dist/canvas/render-overlay.d.ts.map +1 -1
- package/dist/index.d.ts +42 -42
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5506 -5184
- package/dist/index.js.map +1 -1
- package/dist/kalotyp-ui.css +2 -0
- package/dist/output/popover.d.ts.map +1 -1
- package/dist/plugins/annotate/mount.d.ts.map +1 -1
- package/dist/plugins/annotate/panel.d.ts.map +1 -1
- package/dist/plugins/annotate/plugin.d.ts.map +1 -1
- package/dist/plugins/annotate/render.d.ts.map +1 -1
- package/dist/plugins/annotate/selection.d.ts.map +1 -1
- package/dist/plugins/annotate/text-editor.d.ts.map +1 -1
- package/dist/plugins/annotate/tools.d.ts.map +1 -1
- package/dist/plugins/crop/interaction.d.ts.map +1 -1
- package/dist/plugins/crop/mount.d.ts +1 -1
- package/dist/plugins/crop/mount.d.ts.map +1 -1
- package/dist/plugins/crop/plugin.d.ts.map +1 -1
- package/dist/plugins/filter/mount.d.ts.map +1 -1
- package/dist/plugins/filter/thumbnails.d.ts.map +1 -1
- package/dist/plugins/finetune/mount.d.ts.map +1 -1
- package/dist/plugins/finetune/plugin.d.ts.map +1 -1
- package/dist/plugins/finetune/preview.d.ts.map +1 -1
- package/dist/plugins/flip/mount.d.ts.map +1 -1
- package/dist/plugins/flip/plugin.d.ts.map +1 -1
- package/dist/plugins/frame/mount.d.ts.map +1 -1
- package/dist/plugins/frame/plugin.d.ts.map +1 -1
- package/dist/plugins/redact/mount.d.ts.map +1 -1
- package/dist/plugins/redact/plugin.d.ts.map +1 -1
- package/dist/plugins/redact/render.d.ts.map +1 -1
- package/dist/plugins/redact/selection.d.ts.map +1 -1
- package/dist/plugins/resize/mount.d.ts.map +1 -1
- package/dist/plugins/resize/plugin.d.ts.map +1 -1
- package/dist/plugins/rotate/mount.d.ts.map +1 -1
- package/dist/plugins/rotate/plugin.d.ts.map +1 -1
- package/package.json +7 -7
- package/dist/styles.css +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/arrow-right.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/check.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/chevron-down.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/circle.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/flip-horizontal-2.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/flip-vertical-2.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/highlighter.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/keyboard.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/link-2-off.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/link-2.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/mouse-pointer-2.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/pencil.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/plus.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/redo-2.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/settings.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/square.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/trash-2.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/type.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/undo-2.mjs","../../../node_modules/.pnpm/lucide@1.14.0/node_modules/lucide/dist/esm/icons/x.mjs","../src/icons.ts","../src/dom/build-shell-dom.ts","../src/shell.ts","../../core/src/canvas/viewport.ts","../../core/src/canvas/bake-canvas.ts","../../core/src/geometry/rect.ts","../../core/src/plugins/crop/aspect-ratio.ts","../../core/src/plugins/crop/resize.ts","../../core/src/plugins/crop/preset-filter.ts","../../core/src/plugins/crop/state.ts","../../core/src/plugins/crop/bake.ts","../../core/src/output/state.ts","../../core/src/plugins/flip/state.ts","../../core/src/plugins/flip/bake.ts","../../core/src/plugins/rotate/state.ts","../../core/src/plugins/rotate/inscribe.ts","../../core/src/plugins/rotate/bake.ts","../../core/src/plugins/resize/state.ts","../../core/src/plugins/resize/bake.ts","../../core/src/plugins/finetune/state.ts","../../core/src/plugins/finetune/math.ts","../../core/src/plugins/finetune/bake.ts","../../core/src/plugins/filter/presets.ts","../../core/src/plugins/annotate/state.ts","../../core/src/plugins/annotate/geometry.ts","../../core/src/plugins/annotate/hit-test.ts","../../core/src/plugins/annotate/smooth.ts","../../core/src/plugins/annotate/bake.ts","../../core/src/plugins/redact/state.ts","../../core/src/plugins/redact/bake.ts","../../core/src/plugins/frame/state.ts","../../core/src/plugins/frame/bake.ts","../src/canvas/position-handles.ts","../src/canvas/render-image.ts","../src/canvas/render-overlay.ts","../src/dom/build-preset-row.ts","../src/dom/build-stage.ts","../src/plugins/crop/interaction.ts","../src/plugins/crop/mount.ts","../src/plugins/crop/plugin.ts","../src/canvas/preview-canvas.ts","../src/plugins/flip/mount.ts","../src/plugins/flip/plugin.ts","../src/plugins/rotate/mount.ts","../src/plugins/rotate/plugin.ts","../src/plugins/resize/mount.ts","../src/plugins/resize/plugin.ts","../src/plugins/finetune/preview.ts","../src/plugins/finetune/mount.ts","../src/plugins/finetune/plugin.ts","../src/plugins/filter/thumbnails.ts","../src/plugins/filter/mount.ts","../src/plugins/filter/plugin.ts","../src/plugins/annotate/coord-inputs.ts","../src/plugins/annotate/panel.ts","../src/plugins/annotate/pointer-drag.ts","../src/plugins/annotate/render.ts","../src/plugins/annotate/selection.ts","../src/plugins/annotate/stage.ts","../src/plugins/annotate/text-editor.ts","../src/plugins/annotate/tools.ts","../src/plugins/annotate/mount.ts","../src/plugins/annotate/plugin.ts","../src/plugins/redact/coord-inputs.ts","../src/plugins/redact/panel.ts","../src/plugins/redact/render.ts","../src/plugins/redact/selection.ts","../src/plugins/redact/stage.ts","../src/plugins/redact/mount.ts","../src/plugins/redact/plugin.ts","../src/plugins/frame/mount.ts","../src/plugins/frame/plugin.ts","../src/dom/build-util-nav.ts","../src/dom/focus-trap.ts","../src/canvas/stage-gestures.ts","../src/dom/nested-modal.ts","../src/output/popover.ts","../src/preferences/storage.ts","../src/preferences/modal.ts","../src/keyboard-shortcuts.ts","../src/cheatsheet/modal.ts"],"sourcesContent":["/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst ArrowRight = [\n [\"path\", { d: \"M5 12h14\" }],\n [\"path\", { d: \"m12 5 7 7-7 7\" }]\n];\n\nexport { ArrowRight as default };\n//# sourceMappingURL=arrow-right.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Check = [[\"path\", { d: \"M20 6 9 17l-5-5\" }]];\n\nexport { Check as default };\n//# sourceMappingURL=check.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst ChevronDown = [[\"path\", { d: \"m6 9 6 6 6-6\" }]];\n\nexport { ChevronDown as default };\n//# sourceMappingURL=chevron-down.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Circle = [[\"circle\", { cx: \"12\", cy: \"12\", r: \"10\" }]];\n\nexport { Circle as default };\n//# sourceMappingURL=circle.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst FlipHorizontal2 = [\n [\"path\", { d: \"m3 7 5 5-5 5V7\" }],\n [\"path\", { d: \"m21 7-5 5 5 5V7\" }],\n [\"path\", { d: \"M12 20v2\" }],\n [\"path\", { d: \"M12 14v2\" }],\n [\"path\", { d: \"M12 8v2\" }],\n [\"path\", { d: \"M12 2v2\" }]\n];\n\nexport { FlipHorizontal2 as default };\n//# sourceMappingURL=flip-horizontal-2.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst FlipVertical2 = [\n [\"path\", { d: \"m17 3-5 5-5-5h10\" }],\n [\"path\", { d: \"m17 21-5-5-5 5h10\" }],\n [\"path\", { d: \"M4 12H2\" }],\n [\"path\", { d: \"M10 12H8\" }],\n [\"path\", { d: \"M16 12h-2\" }],\n [\"path\", { d: \"M22 12h-2\" }]\n];\n\nexport { FlipVertical2 as default };\n//# sourceMappingURL=flip-vertical-2.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Highlighter = [\n [\"path\", { d: \"m9 11-6 6v3h9l3-3\" }],\n [\"path\", { d: \"m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4\" }]\n];\n\nexport { Highlighter as default };\n//# sourceMappingURL=highlighter.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Keyboard = [\n [\"path\", { d: \"M10 8h.01\" }],\n [\"path\", { d: \"M12 12h.01\" }],\n [\"path\", { d: \"M14 8h.01\" }],\n [\"path\", { d: \"M16 12h.01\" }],\n [\"path\", { d: \"M18 8h.01\" }],\n [\"path\", { d: \"M6 8h.01\" }],\n [\"path\", { d: \"M7 16h10\" }],\n [\"path\", { d: \"M8 12h.01\" }],\n [\"rect\", { width: \"20\", height: \"16\", x: \"2\", y: \"4\", rx: \"2\" }]\n];\n\nexport { Keyboard as default };\n//# sourceMappingURL=keyboard.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Link2Off = [\n [\"path\", { d: \"M9 17H7A5 5 0 0 1 7 7\" }],\n [\"path\", { d: \"M15 7h2a5 5 0 0 1 4 8\" }],\n [\"line\", { x1: \"8\", x2: \"12\", y1: \"12\", y2: \"12\" }],\n [\"line\", { x1: \"2\", x2: \"22\", y1: \"2\", y2: \"22\" }]\n];\n\nexport { Link2Off as default };\n//# sourceMappingURL=link-2-off.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Link2 = [\n [\"path\", { d: \"M9 17H7A5 5 0 0 1 7 7h2\" }],\n [\"path\", { d: \"M15 7h2a5 5 0 1 1 0 10h-2\" }],\n [\"line\", { x1: \"8\", x2: \"16\", y1: \"12\", y2: \"12\" }]\n];\n\nexport { Link2 as default };\n//# sourceMappingURL=link-2.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst MousePointer2 = [\n [\n \"path\",\n {\n d: \"M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z\"\n }\n ]\n];\n\nexport { MousePointer2 as default };\n//# sourceMappingURL=mouse-pointer-2.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Pencil = [\n [\n \"path\",\n {\n d: \"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z\"\n }\n ],\n [\"path\", { d: \"m15 5 4 4\" }]\n];\n\nexport { Pencil as default };\n//# sourceMappingURL=pencil.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Plus = [\n [\"path\", { d: \"M5 12h14\" }],\n [\"path\", { d: \"M12 5v14\" }]\n];\n\nexport { Plus as default };\n//# sourceMappingURL=plus.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Redo2 = [\n [\"path\", { d: \"m15 14 5-5-5-5\" }],\n [\"path\", { d: \"M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13\" }]\n];\n\nexport { Redo2 as default };\n//# sourceMappingURL=redo-2.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Settings = [\n [\n \"path\",\n {\n d: \"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915\"\n }\n ],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\" }]\n];\n\nexport { Settings as default };\n//# sourceMappingURL=settings.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Square = [[\"rect\", { width: \"18\", height: \"18\", x: \"3\", y: \"3\", rx: \"2\" }]];\n\nexport { Square as default };\n//# sourceMappingURL=square.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Trash2 = [\n [\"path\", { d: \"M10 11v6\" }],\n [\"path\", { d: \"M14 11v6\" }],\n [\"path\", { d: \"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\" }],\n [\"path\", { d: \"M3 6h18\" }],\n [\"path\", { d: \"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" }]\n];\n\nexport { Trash2 as default };\n//# sourceMappingURL=trash-2.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Type = [\n [\"path\", { d: \"M12 4v16\" }],\n [\"path\", { d: \"M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2\" }],\n [\"path\", { d: \"M9 20h6\" }]\n];\n\nexport { Type as default };\n//# sourceMappingURL=type.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Undo2 = [\n [\"path\", { d: \"M9 14 4 9l5-5\" }],\n [\"path\", { d: \"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11\" }]\n];\n\nexport { Undo2 as default };\n//# sourceMappingURL=undo-2.mjs.map\n","/**\n * @license lucide v1.14.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst X = [\n [\"path\", { d: \"M18 6 6 18\" }],\n [\"path\", { d: \"m6 6 12 12\" }]\n];\n\nexport { X as default };\n//# sourceMappingURL=x.mjs.map\n","/**\n * Icon library sourced from Lucide (ISC). Each icon is a named import so the\n * bundler tree-shakes unused glyphs. We stringify nodes ourselves rather than\n * use Lucide's `createElement` to avoid pulling its DOM runtime into the bundle.\n */\n\nimport {\n ArrowRight,\n Check as CheckIcon,\n ChevronDown,\n Circle as CircleIcon,\n X as CloseIcon,\n FlipHorizontal2,\n FlipVertical2,\n Highlighter,\n Keyboard as KeyboardIcon,\n Link2,\n Link2Off,\n MousePointer2,\n Pencil,\n Plus as PlusIcon,\n Redo2,\n Settings as SettingsIcon,\n Square,\n Trash2,\n Type as TypeIcon,\n Undo2,\n} from 'lucide';\n\ntype IconNode = ReadonlyArray<readonly [string, Record<string, string | number>]>;\n\nconst DEFAULT_SVG_ATTRS: Record<string, string | number> = {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 16,\n height: 16,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': 2,\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'aria-hidden': 'true',\n};\n\n/** Stringify a Lucide icon node to inline SVG markup. */\nexport function iconHtml(node: IconNode, attrs: Record<string, string | number> = {}): string {\n const merged = { ...DEFAULT_SVG_ATTRS, ...attrs };\n const svgAttrs = Object.entries(merged)\n .map(([k, v]) => `${k}=\"${v}\"`)\n .join(' ');\n const children = node\n .map(([tag, a]) => {\n const childAttrs = Object.entries(a)\n .map(([k, v]) => `${k}=\"${v}\"`)\n .join(' ');\n return `<${tag} ${childAttrs} />`;\n })\n .join('');\n return `<svg ${svgAttrs}>${children}</svg>`;\n}\n\nexport type IconName =\n | 'select'\n | 'text'\n | 'rect'\n | 'ellipse'\n | 'arrow'\n | 'freehand'\n | 'highlight'\n | 'undo'\n | 'redo'\n | 'close'\n | 'delete'\n | 'check'\n | 'lockClosed'\n | 'lockOpen'\n | 'plus'\n | 'flipHorizontal'\n | 'flipVertical'\n | 'chevronDown'\n | 'settings'\n | 'keyboard';\n\nfunction resolve(name: IconName): IconNode {\n switch (name) {\n case 'select':\n return MousePointer2 as IconNode;\n case 'text':\n return TypeIcon as IconNode;\n case 'rect':\n return Square as IconNode;\n case 'ellipse':\n return CircleIcon as IconNode;\n case 'arrow':\n return ArrowRight as IconNode;\n case 'freehand':\n return Pencil as IconNode;\n case 'highlight':\n return Highlighter as IconNode;\n case 'undo':\n return Undo2 as IconNode;\n case 'redo':\n return Redo2 as IconNode;\n case 'close':\n return CloseIcon as IconNode;\n case 'delete':\n return Trash2 as IconNode;\n case 'check':\n return CheckIcon as IconNode;\n case 'lockClosed':\n return Link2 as IconNode;\n case 'lockOpen':\n return Link2Off as IconNode;\n case 'plus':\n return PlusIcon as IconNode;\n case 'flipHorizontal':\n return FlipHorizontal2 as IconNode;\n case 'flipVertical':\n return FlipVertical2 as IconNode;\n case 'chevronDown':\n return ChevronDown as IconNode;\n case 'settings':\n return SettingsIcon as IconNode;\n case 'keyboard':\n return KeyboardIcon as IconNode;\n }\n}\n\n/** Render an icon by name. */\nexport function icon(name: IconName, attrs?: Record<string, string | number>): string {\n return iconHtml(resolve(name), attrs);\n}\n","import { icon } from '../icons.js';\n\nexport interface ShellDom {\n readonly editor: HTMLElement;\n readonly modal: HTMLElement;\n readonly root: HTMLElement;\n readonly main: HTMLElement;\n readonly stage: HTMLElement;\n readonly navTools: HTMLElement;\n readonly utilMain: HTMLElement;\n readonly utilFooter: HTMLElement;\n readonly closeButton: HTMLButtonElement;\n /** Gear icon — opens the Preferences modal. */\n readonly prefsButton: HTMLButtonElement;\n readonly exportButton: HTMLButtonElement;\n /** Caret next to Save — opens the output-format popover; the Save button itself bypasses it. */\n readonly outputSettingsButton: HTMLButtonElement;\n /** Visually-hidden polite live region for one-shot announcements. */\n readonly liveRegion: HTMLElement;\n readonly titleId: string;\n}\n\nexport interface BuildShellDomOptions {\n exportLabel: string;\n}\n\n// Monotonic id prefix so multiple editor instances on one page get distinct namespaces for aria-controls/labelledby.\nlet nextEditorId = 0;\n\n/**\n * Build the DOM skeleton for an editor session.\n *\n * The editor's own structure is namespaced `kalotyp-*`. Two class tokens are\n * the exception — `pintura-editor` on the host and `PinturaModal` on the modal\n * wrapper. Ghost's runtime looks those up by name (the host class scopes Ghost's\n * theme-variable overrides; the modal class is what Ghost's close-button click\n * handler selects on), so they exist purely for Ghost compatibility — not as\n * branding, and implying no affiliation with or endorsement by the editor Ghost\n * named them after. See `docs/ghost-contract.md`. The integration breaks if\n * those two tokens change.\n *\n * The util-main `aria-labelledby` is updated on tab switch by `setActiveUtilityButton`\n * to complete the tablist/tab/tabpanel triple.\n */\nexport function buildShellDom(options: BuildShellDomOptions): ShellDom {\n const editorId = `kalotyp-editor-${++nextEditorId}`;\n const titleId = `${editorId}-title`;\n\n const editor = document.createElement('div');\n // `pintura-editor`: Ghost-compatibility hook only (theme-variable scope), not\n // branding and no affiliation/endorsement implied. See the file docblock.\n editor.className = 'pintura-editor kalotyp-editor';\n editor.id = editorId;\n editor.setAttribute('role', 'dialog');\n editor.setAttribute('aria-modal', 'true');\n editor.setAttribute('aria-labelledby', titleId);\n // tabindex=-1 so the focus trap lands initial focus on the dialog root, not the close button.\n editor.tabIndex = -1;\n\n const title = document.createElement('h2');\n title.id = titleId;\n title.className = 'kalotyp-visually-hidden';\n title.textContent = 'Image editor';\n\n // `PinturaModal`: Ghost-compatibility hook only — Ghost's body-level click\n // handler selects on `.PinturaModal button[title=\"Close\"]` to detect explicit\n // closes. Not branding, no affiliation/endorsement implied. See the file\n // docblock and docs/ghost-contract.md (Dismissal).\n const modal = document.createElement('div');\n modal.className = 'PinturaModal kalotyp-modal';\n\n const closeButton = document.createElement('button');\n closeButton.type = 'button';\n closeButton.title = 'Close';\n closeButton.setAttribute('aria-label', 'Close image editor');\n closeButton.className = 'kalotyp-button-close';\n closeButton.innerHTML = icon('close');\n\n const prefsButton = document.createElement('button');\n prefsButton.type = 'button';\n prefsButton.title = 'Preferences';\n prefsButton.setAttribute('aria-label', 'Open editor preferences');\n prefsButton.setAttribute('aria-haspopup', 'dialog');\n prefsButton.className = 'kalotyp-button-prefs';\n prefsButton.innerHTML = icon('settings');\n\n const root = document.createElement('div');\n root.className = 'kalotyp-root';\n root.setAttribute('data-env', 'landscape has-navigation');\n\n const main = document.createElement('div');\n main.className = 'kalotyp-main';\n\n const stage = document.createElement('div');\n stage.className = 'kalotyp-stage';\n stage.setAttribute('role', 'region');\n stage.setAttribute('aria-label', 'Image preview');\n\n const utilMain = document.createElement('div');\n utilMain.id = `${editorId}-panel`;\n utilMain.className = 'kalotyp-util-main';\n utilMain.setAttribute('role', 'tabpanel');\n utilMain.setAttribute('tabindex', '0');\n\n const navTools = document.createElement('div');\n navTools.className = 'kalotyp-nav-tools';\n\n const utilFooter = document.createElement('div');\n utilFooter.className = 'kalotyp-util-footer';\n\n const exportGroup = document.createElement('div');\n exportGroup.className = 'kalotyp-export-group';\n\n const exportButton = document.createElement('button');\n exportButton.type = 'button';\n exportButton.className = 'kalotyp-button-export';\n const exportInner = document.createElement('span');\n exportInner.className = 'kalotyp-button-inner';\n exportInner.textContent = options.exportLabel;\n exportButton.appendChild(exportInner);\n\n const outputSettingsButton = document.createElement('button');\n outputSettingsButton.type = 'button';\n outputSettingsButton.className = 'kalotyp-button-output-settings';\n outputSettingsButton.title = 'Output settings';\n outputSettingsButton.setAttribute('aria-label', 'Output settings (format and quality)');\n outputSettingsButton.setAttribute('aria-haspopup', 'dialog');\n outputSettingsButton.setAttribute('aria-expanded', 'false');\n outputSettingsButton.innerHTML = icon('chevronDown');\n\n exportGroup.appendChild(exportButton);\n exportGroup.appendChild(outputSettingsButton);\n utilFooter.appendChild(exportGroup);\n\n // aria-atomic so screen readers read the whole message, not just diffed tokens.\n const liveRegion = document.createElement('div');\n liveRegion.className = 'kalotyp-visually-hidden';\n liveRegion.setAttribute('role', 'status');\n liveRegion.setAttribute('aria-live', 'polite');\n liveRegion.setAttribute('aria-atomic', 'true');\n\n main.appendChild(stage);\n main.appendChild(utilMain);\n\n root.appendChild(navTools);\n root.appendChild(main);\n root.appendChild(utilFooter);\n\n modal.appendChild(title);\n modal.appendChild(prefsButton);\n modal.appendChild(closeButton);\n modal.appendChild(root);\n modal.appendChild(liveRegion);\n\n editor.appendChild(modal);\n\n return {\n editor,\n modal,\n root,\n main,\n stage,\n navTools,\n utilMain,\n utilFooter,\n closeButton,\n prefsButton,\n exportButton,\n outputSettingsButton,\n liveRegion,\n titleId,\n };\n}\n","import { type ShellDom, buildShellDom } from './dom/build-shell-dom.js';\n\nexport interface ShellOptions {\n host: HTMLElement;\n exportLabel: string;\n onExportClick: () => void;\n onCloseClick: () => void;\n onOutputSettingsClick: () => void;\n onPrefsClick: () => void;\n}\n\nexport interface ShellHandle {\n readonly editor: HTMLElement;\n readonly modal: HTMLElement;\n readonly root: HTMLElement;\n readonly exportButton: HTMLButtonElement;\n /** Caret button next to Save — opens the output-format popover. */\n readonly outputSettingsButton: HTMLButtonElement;\n /** Gear button next to close — opens the Preferences modal. */\n readonly prefsButton: HTMLButtonElement;\n readonly closeButton: HTMLButtonElement;\n readonly stage: HTMLElement;\n readonly utilMain: HTMLElement;\n readonly navTools: HTMLElement;\n /** Polite live-region announcer; identical messages re-announce via a trailing-space flip (NVDA/JAWS suppress duplicate text). */\n announce(message: string): void;\n destroy(): void;\n}\n\nexport function mountShell(options: ShellOptions): ShellHandle {\n const dom: ShellDom = buildShellDom({ exportLabel: options.exportLabel });\n\n const onExport = () => options.onExportClick();\n const onClose = () => options.onCloseClick();\n const onOutputSettings = () => options.onOutputSettingsClick();\n const onPrefs = () => options.onPrefsClick();\n dom.exportButton.addEventListener('click', onExport);\n dom.closeButton.addEventListener('click', onClose);\n dom.outputSettingsButton.addEventListener('click', onOutputSettings);\n dom.prefsButton.addEventListener('click', onPrefs);\n\n options.host.appendChild(dom.editor);\n\n // NVDA/JAWS suppress identical-text updates in live regions; flip a trailing space to force re-announce.\n let announceFlip = false;\n function announce(message: string): void {\n announceFlip = !announceFlip;\n dom.liveRegion.textContent = announceFlip ? `${message} ` : message;\n }\n\n return {\n editor: dom.editor,\n modal: dom.modal,\n root: dom.root,\n exportButton: dom.exportButton,\n outputSettingsButton: dom.outputSettingsButton,\n prefsButton: dom.prefsButton,\n closeButton: dom.closeButton,\n stage: dom.stage,\n utilMain: dom.utilMain,\n navTools: dom.navTools,\n announce,\n destroy() {\n dom.exportButton.removeEventListener('click', onExport);\n dom.closeButton.removeEventListener('click', onClose);\n dom.outputSettingsButton.removeEventListener('click', onOutputSettings);\n dom.prefsButton.removeEventListener('click', onPrefs);\n dom.editor.remove();\n },\n };\n}\n","import type { Point, Rect, Size } from '../geometry/rect.js';\n\nexport interface StageDimensions {\n /** Stage width in CSS pixels. */\n readonly width: number;\n /** Stage height in CSS pixels. */\n readonly height: number;\n /** Padding on each side around the image, in CSS pixels. */\n readonly padding: number;\n}\n\n/**\n * User-driven zoom and pan applied on top of the fit-to-screen letterbox.\n * `computeViewport` folds the transform into `displayRect` and `scale` so\n * plugin-level draw calls stay zoom-agnostic. Pan is in stage CSS pixels\n * at the current zoom and is clamped at viewport emission.\n */\nexport interface ViewportTransform {\n /** 1 at fit; > 1 zoomed in; < 1 zoomed out. */\n readonly zoom: number;\n /** CSS pixels of pan offset, applied after the centered-fit baseline. */\n readonly panX: number;\n readonly panY: number;\n}\n\nexport const IDENTITY_VIEWPORT_TRANSFORM: ViewportTransform = Object.freeze({\n zoom: 1,\n panX: 0,\n panY: 0,\n});\n\nexport interface Viewport {\n /** Where the image is drawn in stage CSS pixels (post-zoom, post-pan). */\n readonly displayRect: Rect;\n /** Display CSS pixels per 1 image pixel (uniform on both axes, post-zoom). */\n readonly scale: number;\n}\n\n/**\n * Compute the post-zoom, post-pan display rect for an image inside the stage.\n *\n * At identity, the image is fit-scaled inside the stage minus padding and\n * centered. With a non-identity transform the fit scale is multiplied by\n * `zoom`, then the pan offset is added and clamped so at least 1 image\n * pixel remains inside the inner stage area on each axis.\n */\nexport function computeViewport(\n stage: StageDimensions,\n image: Size,\n transform: ViewportTransform = IDENTITY_VIEWPORT_TRANSFORM,\n): Viewport {\n const innerWidth = Math.max(0, stage.width - stage.padding * 2);\n const innerHeight = Math.max(0, stage.height - stage.padding * 2);\n\n if (image.width <= 0 || image.height <= 0 || innerWidth <= 0 || innerHeight <= 0) {\n return {\n displayRect: { x: stage.padding, y: stage.padding, width: 0, height: 0 },\n scale: 0,\n };\n }\n\n const fitScale = Math.min(innerWidth / image.width, innerHeight / image.height);\n const zoom = Math.max(0, transform.zoom || 0);\n const scale = fitScale * zoom;\n\n const width = image.width * scale;\n const height = image.height * scale;\n\n // Centered baseline inside the inner stage area, then add the pan.\n const baselineX = stage.padding + (innerWidth - width) / 2;\n const baselineY = stage.padding + (innerHeight - height) / 2;\n\n const x = baselineX + transform.panX;\n const y = baselineY + transform.panY;\n\n const clampedX = clampAxis(x, baselineX, width, innerWidth, stage.padding);\n const clampedY = clampAxis(y, baselineY, height, innerHeight, stage.padding);\n\n return {\n displayRect: { x: clampedX, y: clampedY, width, height },\n scale,\n };\n}\n\nfunction clampAxis(\n rawPos: number,\n baseline: number,\n size: number,\n innerSize: number,\n padding: number,\n): number {\n // Image narrower than the inner stage: no off-center pan, baseline is centered.\n if (size <= innerSize) return baseline;\n\n // At least 1 image pixel always remains inside the inner stage.\n const innerStart = padding;\n const innerEnd = padding + innerSize;\n const minLeft = innerStart + 1 - size;\n const maxLeft = innerEnd - 1;\n if (rawPos < minLeft) return minLeft;\n if (rawPos > maxLeft) return maxLeft;\n return rawPos;\n}\n\nexport function pointImageToDisplay(point: Point, viewport: Viewport): Point {\n return {\n x: viewport.displayRect.x + point.x * viewport.scale,\n y: viewport.displayRect.y + point.y * viewport.scale,\n };\n}\n\nexport function pointDisplayToImage(point: Point, viewport: Viewport): Point {\n if (viewport.scale === 0) return { x: 0, y: 0 };\n return {\n x: (point.x - viewport.displayRect.x) / viewport.scale,\n y: (point.y - viewport.displayRect.y) / viewport.scale,\n };\n}\n\nexport function rectImageToDisplay(rect: Rect, viewport: Viewport): Rect {\n return {\n x: viewport.displayRect.x + rect.x * viewport.scale,\n y: viewport.displayRect.y + rect.y * viewport.scale,\n width: rect.width * viewport.scale,\n height: rect.height * viewport.scale,\n };\n}\n\nexport function rectDisplayToImage(rect: Rect, viewport: Viewport): Rect {\n if (viewport.scale === 0) return { x: 0, y: 0, width: 0, height: 0 };\n return {\n x: (rect.x - viewport.displayRect.x) / viewport.scale,\n y: (rect.y - viewport.displayRect.y) / viewport.scale,\n width: rect.width / viewport.scale,\n height: rect.height / viewport.scale,\n };\n}\n","/**\n * Allocate a canvas suitable for an off-screen bake operation.\n *\n * The two return shapes are a discriminated union because the `toBlob`\n * vs `convertToBlob` signatures differ — callers pass through whichever\n * they got.\n */\nexport type BakeCanvas =\n | { readonly kind: 'offscreen'; readonly canvas: OffscreenCanvas }\n | { readonly kind: 'html'; readonly canvas: HTMLCanvasElement };\n\nexport function createBakeCanvas(width: number, height: number): BakeCanvas {\n if (canUseOffscreenForBlobs()) {\n const canvas = new OffscreenCanvas(width, height);\n return { kind: 'offscreen', canvas };\n }\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n return { kind: 'html', canvas };\n}\n\n/**\n * Get a 2D rendering context from either canvas shape with a single\n * narrowed type. Branching on `bake.kind` first lets TS narrow each\n * canvas's `getContext('2d')` correctly; calling it on the un-discriminated\n * union collapses the return to the broader `RenderingContext`.\n */\nexport function getBakeContext2D(\n bake: BakeCanvas,\n): CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D {\n if (bake.kind === 'offscreen') {\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n return ctx;\n }\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n return ctx;\n}\n\nexport async function bakeCanvasToBlob(\n bake: BakeCanvas,\n mimeType: string,\n quality: number,\n): Promise<Blob> {\n if (bake.kind === 'offscreen') {\n return bake.canvas.convertToBlob({ type: mimeType, quality });\n }\n return new Promise<Blob>((resolve, reject) => {\n bake.canvas.toBlob(\n (blob) => {\n if (blob) resolve(blob);\n else reject(new Error('toBlob produced null'));\n },\n mimeType,\n quality,\n );\n });\n}\n\nfunction canUseOffscreenForBlobs(): boolean {\n if (typeof OffscreenCanvas === 'undefined') return false;\n // WebKit historically shipped OffscreenCanvas without convertToBlob,\n // so test the actual capability rather than the constructor.\n return typeof OffscreenCanvas.prototype.convertToBlob === 'function';\n}\n\n/** Probe whether the runtime canvas can encode `mimeType` to a non-empty blob. Cached per-mime. */\nconst mimeSupportCache = new Map<string, Promise<boolean>>();\n\nexport function canEncodeMime(mimeType: string): Promise<boolean> {\n const cached = mimeSupportCache.get(mimeType);\n if (cached) return cached;\n const probe = (async () => {\n try {\n const bake = createBakeCanvas(1, 1);\n const blob = await bakeCanvasToBlob(bake, mimeType, 0.5);\n // `toBlob` will silently fall back to PNG on unsupported types,\n // so verify the result advertises the requested mime.\n return blob.type === mimeType && blob.size > 0;\n } catch {\n return false;\n }\n })();\n mimeSupportCache.set(mimeType, probe);\n return probe;\n}\n","export interface Point {\n readonly x: number;\n readonly y: number;\n}\n\nexport interface Rect {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\nexport interface Size {\n readonly width: number;\n readonly height: number;\n}\n\nexport function rectFromPoints(a: Point, b: Point): Rect {\n const x = Math.min(a.x, b.x);\n const y = Math.min(a.y, b.y);\n const width = Math.abs(a.x - b.x);\n const height = Math.abs(a.y - b.y);\n return { x, y, width, height };\n}\n\nexport function rectRight(rect: Rect): number {\n return rect.x + rect.width;\n}\n\nexport function rectBottom(rect: Rect): number {\n return rect.y + rect.height;\n}\n\nexport function rectCenter(rect: Rect): Point {\n return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };\n}\n\nexport function pointInRect(point: Point, rect: Rect): boolean {\n return (\n point.x >= rect.x &&\n point.x <= rect.x + rect.width &&\n point.y >= rect.y &&\n point.y <= rect.y + rect.height\n );\n}\n\nexport function rectsEqual(a: Rect, b: Rect): boolean {\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n}\n\n/**\n * Translate a rect by `(dx, dy)`, then clamp it inside `bounds` so that the\n * full rect remains inside (clamping translates further if needed; size is\n * preserved). If the rect is larger than the bounds in either axis, that axis\n * is left at the bounds origin.\n */\nexport function translateClampedRect(rect: Rect, dx: number, dy: number, bounds: Rect): Rect {\n const moved: Rect = { x: rect.x + dx, y: rect.y + dy, width: rect.width, height: rect.height };\n return clampRectInside(moved, bounds);\n}\n\n/**\n * Clamp a rect so it fits entirely inside `bounds`. If the rect is larger\n * than the bounds in either axis, the rect's extent in that axis is shrunk\n * to fit (preserving the upper-left anchor of `bounds`).\n */\nexport function clampRectInside(rect: Rect, bounds: Rect): Rect {\n let { x, y, width, height } = rect;\n\n if (width > bounds.width) width = bounds.width;\n if (height > bounds.height) height = bounds.height;\n\n if (x < bounds.x) x = bounds.x;\n if (y < bounds.y) y = bounds.y;\n if (x + width > bounds.x + bounds.width) x = bounds.x + bounds.width - width;\n if (y + height > bounds.y + bounds.height) y = bounds.y + bounds.height - height;\n\n return { x, y, width, height };\n}\n\nexport function roundRect(rect: Rect): Rect {\n return {\n x: Math.round(rect.x),\n y: Math.round(rect.y),\n width: Math.round(rect.width),\n height: Math.round(rect.height),\n };\n}\n","import { type Rect, clampRectInside } from '../../geometry/rect.js';\n\n/**\n * Compute the largest axis-aligned rectangle of `targetRatio` (= w/h) that\n * fits inside `bounds`, centered. Used to seed the crop rectangle when the\n * user picks an aspect-ratio preset before any drag.\n */\nexport function fitRectToBoundsWithRatio(bounds: Rect, targetRatio: number): Rect {\n if (targetRatio <= 0 || bounds.width <= 0 || bounds.height <= 0) {\n return { x: bounds.x, y: bounds.y, width: 0, height: 0 };\n }\n\n const boundsRatio = bounds.width / bounds.height;\n let width: number;\n let height: number;\n if (targetRatio >= boundsRatio) {\n width = bounds.width;\n height = width / targetRatio;\n } else {\n height = bounds.height;\n width = height * targetRatio;\n }\n\n return {\n x: bounds.x + (bounds.width - width) / 2,\n y: bounds.y + (bounds.height - height) / 2,\n width,\n height,\n };\n}\n\n/**\n * Reshape `rect` to `targetRatio`, anchored at `anchor`, clamped inside\n * `bounds`. If clamping breaks the ratio, falls back to the largest same-ratio\n * sub-rect that fits, anchored identically.\n */\nexport function applyAspectRatio(\n rect: Rect,\n targetRatio: number,\n anchor: AspectAnchor,\n bounds: Rect,\n): Rect {\n if (targetRatio <= 0) return rect;\n if (rect.width <= 0 || rect.height <= 0) return fitRectToBoundsWithRatio(bounds, targetRatio);\n\n const currentRatio = rect.width / rect.height;\n let width: number;\n let height: number;\n if (currentRatio > targetRatio) {\n height = rect.height;\n width = height * targetRatio;\n } else {\n width = rect.width;\n height = width / targetRatio;\n }\n\n const reshaped = anchorRect(rect, width, height, anchor);\n const clamped = clampRectInside(reshaped, bounds);\n\n const clampedRatio = clamped.height === 0 ? 0 : clamped.width / clamped.height;\n if (Math.abs(clampedRatio - targetRatio) <= RATIO_TOLERANCE) {\n return clamped;\n }\n return fitInsideAtAnchor(clamped, targetRatio, anchor);\n}\n\nconst RATIO_TOLERANCE = 1e-6;\n\nexport type AspectAnchor = 'tl' | 'tr' | 'bl' | 'br' | 'center';\n\nfunction anchorRect(rect: Rect, width: number, height: number, anchor: AspectAnchor): Rect {\n switch (anchor) {\n case 'tl':\n return { x: rect.x, y: rect.y, width, height };\n case 'tr':\n return { x: rect.x + rect.width - width, y: rect.y, width, height };\n case 'bl':\n return { x: rect.x, y: rect.y + rect.height - height, width, height };\n case 'br':\n return {\n x: rect.x + rect.width - width,\n y: rect.y + rect.height - height,\n width,\n height,\n };\n case 'center':\n return {\n x: rect.x + (rect.width - width) / 2,\n y: rect.y + (rect.height - height) / 2,\n width,\n height,\n };\n }\n}\n\nfunction fitInsideAtAnchor(bounds: Rect, targetRatio: number, anchor: AspectAnchor): Rect {\n const fitted = fitRectToBoundsWithRatio(bounds, targetRatio);\n return anchorRect(bounds, fitted.width, fitted.height, anchor);\n}\n","import { type Point, type Rect, clampRectInside } from '../../geometry/rect.js';\nimport { type AspectAnchor, applyAspectRatio } from './aspect-ratio.js';\n\nexport type CornerHandle = 'tl' | 'tr' | 'bl' | 'br';\nexport type EdgeHandle = 't' | 'r' | 'b' | 'l';\nexport type HandleDirection = CornerHandle | EdgeHandle;\n\nexport interface ResizeOptions {\n /** Image-space bounds the rect must stay inside. */\n readonly bounds: Rect;\n /** Aspect ratio to enforce, or `undefined` for free crop. */\n readonly aspectRatio?: number;\n /** Minimum size on either axis, in image-space units. Defaults to 1. */\n readonly minSize?: number;\n}\n\n/**\n * Resize a rect from one of its eight handles to `pointer`. Opposite\n * corner/edge anchors; result clamped to `bounds` and reshaped to\n * `aspectRatio` (anchored at the same opposite corner) when supplied.\n */\nexport function resizeRectFromHandle(\n rect: Rect,\n handle: HandleDirection,\n pointer: Point,\n options: ResizeOptions,\n): Rect {\n const minSize = options.minSize ?? 1;\n const left = rect.x;\n const top = rect.y;\n const right = rect.x + rect.width;\n const bottom = rect.y + rect.height;\n\n // Pointer may swap sides (drag through the anchor); recompute from anchor + live edge.\n let newLeft = left;\n let newTop = top;\n let newRight = right;\n let newBottom = bottom;\n\n if (handle === 'tl' || handle === 'l' || handle === 'bl') {\n newLeft = pointer.x;\n }\n if (handle === 'tr' || handle === 'r' || handle === 'br') {\n newRight = pointer.x;\n }\n if (handle === 'tl' || handle === 't' || handle === 'tr') {\n newTop = pointer.y;\n }\n if (handle === 'bl' || handle === 'b' || handle === 'br') {\n newBottom = pointer.y;\n }\n\n if (handle === 'l' || handle === 'r') {\n newTop = top;\n newBottom = bottom;\n }\n if (handle === 't' || handle === 'b') {\n newLeft = left;\n newRight = right;\n }\n\n let nx = Math.min(newLeft, newRight);\n let ny = Math.min(newTop, newBottom);\n let nw = Math.abs(newRight - newLeft);\n let nh = Math.abs(newBottom - newTop);\n\n if (nw < minSize) {\n nw = minSize;\n if (handle === 'tl' || handle === 'l' || handle === 'bl') {\n nx = right - minSize;\n } else if (handle === 'tr' || handle === 'r' || handle === 'br') {\n nx = left;\n }\n }\n if (nh < minSize) {\n nh = minSize;\n if (handle === 'tl' || handle === 't' || handle === 'tr') {\n ny = bottom - minSize;\n } else if (handle === 'bl' || handle === 'b' || handle === 'br') {\n ny = top;\n }\n }\n\n let resized: Rect = { x: nx, y: ny, width: nw, height: nh };\n resized = clampRectInside(resized, options.bounds);\n\n if (options.aspectRatio !== undefined && options.aspectRatio > 0) {\n resized = applyAspectRatio(resized, options.aspectRatio, anchorFor(handle), options.bounds);\n }\n\n return resized;\n}\n\nfunction anchorFor(handle: HandleDirection): AspectAnchor {\n switch (handle) {\n case 'tl':\n return 'br';\n case 'tr':\n return 'bl';\n case 'bl':\n return 'tr';\n case 'br':\n return 'tl';\n case 't':\n return 'bl';\n case 'b':\n return 'tl';\n case 'l':\n return 'tr';\n case 'r':\n return 'tl';\n }\n}\n","/**\n * Filters crop presets by aspect-ratio relative to 1. `landscape` keeps\n * ratio ≥ 1, `portrait` keeps ratio < 1; `undefined` ratios (Custom) and\n * unknown tokens stay visible.\n */\nexport type CropPresetFilter = 'landscape' | 'portrait';\nexport type CropPreset = readonly [number | undefined, string];\n\nexport function isPresetVisible(preset: CropPreset, filter: CropPresetFilter | undefined): boolean {\n const [ratio] = preset;\n if (ratio === undefined) return true;\n if (filter === undefined) return true;\n if (filter === 'landscape') return ratio >= 1;\n if (filter === 'portrait') return ratio < 1;\n return true;\n}\n\nexport function filterPresets(\n presets: readonly CropPreset[],\n filter: CropPresetFilter | undefined,\n): readonly CropPreset[] {\n return presets.filter((preset) => isPresetVisible(preset, filter));\n}\n","import type { Rect, Size } from '../../geometry/rect.js';\nimport { fitRectToBoundsWithRatio } from './aspect-ratio.js';\nimport type { CropPreset, CropPresetFilter } from './preset-filter.js';\n\nexport interface CropState {\n /** Crop rectangle in image-space pixels. */\n readonly rect: Rect;\n /** Active aspect-ratio constraint (image w/h), or `undefined` for free. */\n readonly aspectRatio: number | undefined;\n /** Index into `presets`, or `-1` if no preset is active. */\n readonly activePresetIndex: number;\n /** Visible presets after applying `cropSelectPresetFilter`. */\n readonly presets: readonly CropPreset[];\n /** Image dimensions, in pixels. The bounds the crop can move within. */\n readonly imageSize: Size;\n}\n\nexport interface InitialCropStateInput {\n readonly imageSize: Size;\n readonly presets: readonly CropPreset[];\n readonly filter: CropPresetFilter | undefined;\n}\n\n/** Full-frame crop, \"Custom\" preset active. */\nexport function initialCropState(input: InitialCropStateInput): CropState {\n const bounds: Rect = { x: 0, y: 0, width: input.imageSize.width, height: input.imageSize.height };\n return {\n rect: bounds,\n aspectRatio: undefined,\n activePresetIndex: findCustomIndex(input.presets),\n presets: input.presets,\n imageSize: input.imageSize,\n };\n}\n\nexport function applyPresetByIndex(state: CropState, presetIndex: number): CropState {\n const preset = state.presets[presetIndex];\n if (!preset) return state;\n const [ratio] = preset;\n if (ratio === undefined) {\n return { ...state, aspectRatio: undefined, activePresetIndex: presetIndex };\n }\n const bounds: Rect = {\n x: 0,\n y: 0,\n width: state.imageSize.width,\n height: state.imageSize.height,\n };\n const fitted = fitRectToBoundsWithRatio(bounds, ratio);\n return { ...state, rect: fitted, aspectRatio: ratio, activePresetIndex: presetIndex };\n}\n\nfunction findCustomIndex(presets: readonly CropPreset[]): number {\n return presets.findIndex(([ratio]) => ratio === undefined);\n}\n","import { createBakeCanvas } from '../../canvas/bake-canvas.js';\nimport { type Rect, roundRect } from '../../geometry/rect.js';\nimport type { SourceImage } from '../utility.js';\n\nexport interface CropBakeInput {\n /** The cropped region, in image-space pixels. */\n readonly rect: Rect;\n}\n\n/**\n * Apply a crop and return a SourceImage at the crop's pixel size. The\n * rect is rounded and clamped against the source so an oversized rect\n * doesn't crash — we draw what fits.\n */\nexport function bakeCrop(source: SourceImage, input: CropBakeInput): SourceImage {\n const rounded = roundRect(input.rect);\n const x = clamp(rounded.x, 0, source.width);\n const y = clamp(rounded.y, 0, source.height);\n const w = clamp(rounded.width, 1, source.width - x);\n const h = clamp(rounded.height, 1, source.height - y);\n\n const bake = createBakeCanvas(w, h);\n if (bake.kind === 'offscreen') {\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n ctx.drawImage(source.bitmap, x, y, w, h, 0, 0, w, h);\n return { bitmap: bake.canvas, width: w, height: h, mimeType: source.mimeType };\n }\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n ctx.drawImage(source.bitmap, x, y, w, h, 0, 0, w, h);\n return { bitmap: bake.canvas, width: w, height: h, mimeType: source.mimeType };\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, n));\n}\n","/**\n * `'auto'` resolves to the smallest format that preserves alpha on the\n * current runtime (WebP on evergreens, PNG fallback). AVIF never auto-\n * resolves; the user must pick it explicitly.\n */\nexport type OutputMimeChoice = 'auto' | 'image/png' | 'image/jpeg' | 'image/webp' | 'image/avif';\n\nexport interface OutputState {\n readonly mimeChoice: OutputMimeChoice;\n /** 0.0 – 1.0. Ignored for PNG (lossless). */\n readonly quality: number;\n /**\n * Strip EXIF / GPS / camera metadata on save. Canvas `convertToBlob`\n * already strips EXIF; when `false`, we attempt to preserve the source\n * EXIF segment, which only works for JPEG → JPEG. Other combinations\n * strip regardless — the toggle is a hint, not a guarantee.\n */\n readonly stripMetadata: boolean;\n}\n\nexport const DEFAULT_OUTPUT_STATE: OutputState = {\n mimeChoice: 'auto',\n quality: 0.85,\n stripMetadata: true,\n};\n\n/** The four concrete mime types the user can pick from in the popover. */\nexport const ENCODABLE_MIMES: ReadonlyArray<Exclude<OutputMimeChoice, 'auto'>> = [\n 'image/png',\n 'image/jpeg',\n 'image/webp',\n 'image/avif',\n];\n\n/** Clamp a quality value to [0, 1]; non-finite inputs return the default. */\nexport function clampQuality(value: number): number {\n if (!Number.isFinite(value)) return DEFAULT_OUTPUT_STATE.quality;\n if (value < 0) return 0;\n if (value > 1) return 1;\n return value;\n}\n\nexport function setOutputMime(state: OutputState, mimeChoice: OutputMimeChoice): OutputState {\n if (state.mimeChoice === mimeChoice) return state;\n return { ...state, mimeChoice };\n}\n\nexport function setOutputQuality(state: OutputState, quality: number): OutputState {\n const clamped = clampQuality(quality);\n if (state.quality === clamped) return state;\n return { ...state, quality: clamped };\n}\n\nexport function setStripMetadata(state: OutputState, stripMetadata: boolean): OutputState {\n if (state.stripMetadata === stripMetadata) return state;\n return { ...state, stripMetadata };\n}\n","/** Two independent axis flips; their composition equals a 180° rotation. */\nexport interface FlipState {\n readonly horizontal: boolean;\n readonly vertical: boolean;\n}\n\nexport function initialFlipState(): FlipState {\n return { horizontal: false, vertical: false };\n}\n\nexport function toggleFlip(state: FlipState, axis: 'horizontal' | 'vertical'): FlipState {\n return axis === 'horizontal'\n ? { ...state, horizontal: !state.horizontal }\n : { ...state, vertical: !state.vertical };\n}\n\nexport function isFlipNoOp(state: FlipState): boolean {\n return !state.horizontal && !state.vertical;\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { type FlipState, isFlipNoOp } from './state.js';\n\n/** Apply horizontal / vertical flips via a sign-flipped scale on `drawImage`. */\nexport async function bakeFlip(state: FlipState, source: SourceImage): Promise<SourceImage> {\n if (isFlipNoOp(state)) return source;\n\n const { width, height } = source;\n const bake = createBakeCanvas(width, height);\n const ctx = getBakeContext2D(bake);\n\n const sx = state.horizontal ? -1 : 1;\n const sy = state.vertical ? -1 : 1;\n const tx = state.horizontal ? width : 0;\n const ty = state.vertical ? height : 0;\n\n ctx.setTransform(sx, 0, 0, sy, tx, ty);\n ctx.drawImage(source.bitmap, 0, 0);\n\n return {\n bitmap: bake.canvas,\n width,\n height,\n mimeType: source.mimeType,\n };\n}\n","/**\n * Independent quarter-turns (lossless 90° CW) and free angle (±45°).\n * Bake applies `quarterTurns * 90° + freeAngle` so a press of \"rotate 90°\"\n * doesn't reset a straighten correction and vice versa.\n */\nexport interface RotateState {\n readonly quarterTurns: 0 | 1 | 2 | 3;\n /** Free-angle offset in degrees. Range: [-45, 45]. */\n readonly freeAngle: number;\n}\n\nexport const FREE_ANGLE_MIN = -45;\nexport const FREE_ANGLE_MAX = 45;\nexport const FREE_ANGLE_STEP = 0.1;\n\nexport function initialRotateState(): RotateState {\n return { quarterTurns: 0, freeAngle: 0 };\n}\n\nexport function rotateClockwise(state: RotateState): RotateState {\n return { ...state, quarterTurns: ((state.quarterTurns + 1) % 4) as 0 | 1 | 2 | 3 };\n}\n\nexport function rotateCounterClockwise(state: RotateState): RotateState {\n return { ...state, quarterTurns: ((state.quarterTurns + 3) % 4) as 0 | 1 | 2 | 3 };\n}\n\nexport function setFreeAngle(state: RotateState, angleDeg: number): RotateState {\n const clamped = clamp(angleDeg, FREE_ANGLE_MIN, FREE_ANGLE_MAX);\n // Snap to 0.1° to match the slider step and avoid sub-step float noise.\n const snapped = Math.round(clamped * 10) / 10;\n return { ...state, freeAngle: snapped };\n}\n\nexport function isRotateNoOp(state: RotateState): boolean {\n return state.quarterTurns === 0 && Math.abs(state.freeAngle) < 1e-6;\n}\n\n/** Effective rotation applied during bake, in degrees clockwise. */\nexport function effectiveAngleDeg(state: RotateState): number {\n return state.quarterTurns * 90 + state.freeAngle;\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, n));\n}\n","import type { Size } from '../../geometry/rect.js';\n\n/**\n * Largest axis-aligned rect of the source's aspect ratio that fits\n * inside the source rotated by `angleRad`. Used by free-angle rotation\n * to avoid transparent corners.\n *\n * The two binding constraints reduce to\n * `2x ≤ W² / (W|cosθ| + H|sinθ|)`\n * `2x ≤ W·H / (W|sinθ| + H|cosθ|)`\n * — the inscribed half-width is the smaller bound. Works on absolute\n * sin/cos so the result is symmetric in the sign of the angle.\n */\nexport function largestInscribedRect(source: Size, angleRad: number): Size {\n const width = source.width;\n const height = source.height;\n if (width <= 0 || height <= 0) return { width: 0, height: 0 };\n\n const c = Math.abs(Math.cos(angleRad));\n const s = Math.abs(Math.sin(angleRad));\n\n const denomA = width * c + height * s;\n const denomD = width * s + height * c;\n const capA = denomA > EPSILON ? (width * width) / denomA : Number.POSITIVE_INFINITY;\n const capD = denomD > EPSILON ? (width * height) / denomD : Number.POSITIVE_INFINITY;\n\n const outWidth = Math.min(capA, capD);\n const outHeight = (outWidth * height) / width;\n return { width: outWidth, height: outHeight };\n}\n\nconst EPSILON = 1e-9;\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { largestInscribedRect } from './inscribe.js';\nimport { type RotateState, effectiveAngleDeg, isRotateNoOp } from './state.js';\n\n/**\n * Apply rotation = quarter-turns + free-angle in one `drawImage`. Free\n * angles auto-crop to the largest same-aspect rect inside the rotated\n * bounding box so transparent corners stay out of the output.\n */\nexport async function bakeRotate(state: RotateState, source: SourceImage): Promise<SourceImage> {\n if (isRotateNoOp(state)) return source;\n\n const angleDeg = effectiveAngleDeg(state);\n const angleRad = (angleDeg * Math.PI) / 180;\n\n const sub90Deg = angleDeg - state.quarterTurns * 90; // ∈ [-45, 45]\n const isQuarterOnly = Math.abs(sub90Deg) < 1e-6;\n\n let outWidth: number;\n let outHeight: number;\n\n if (isQuarterOnly) {\n if (state.quarterTurns === 1 || state.quarterTurns === 3) {\n outWidth = source.height;\n outHeight = source.width;\n } else {\n outWidth = source.width;\n outHeight = source.height;\n }\n } else {\n const inscribed = largestInscribedRect(source, angleRad);\n outWidth = Math.max(1, Math.round(inscribed.width));\n outHeight = Math.max(1, Math.round(inscribed.height));\n }\n\n const bake = createBakeCanvas(outWidth, outHeight);\n const ctx = getBakeContext2D(bake);\n\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n\n ctx.translate(outWidth / 2, outHeight / 2);\n ctx.rotate(angleRad);\n ctx.drawImage(source.bitmap, -source.width / 2, -source.height / 2);\n\n return {\n bitmap: bake.canvas,\n width: outWidth,\n height: outHeight,\n mimeType: source.mimeType,\n };\n}\n","/**\n * Per-axis scale factors. `lockAspect` makes the editor keep `scaleX === scaleY`.\n * Output pixels are computed at bake time as `round(upstream * scale)`,\n * clamped to `[MIN_DIMENSION, MAX_DIMENSION]`.\n */\nexport interface ResizeState {\n readonly scaleX: number;\n readonly scaleY: number;\n readonly lockAspect: boolean;\n}\n\nexport const MAX_DIMENSION = 8000;\nexport const MIN_DIMENSION = 1;\n\nexport function initialResizeState(): ResizeState {\n return { scaleX: 1, scaleY: 1, lockAspect: true };\n}\n\nexport function isResizeNoOp(state: ResizeState): boolean {\n return Math.abs(state.scaleX - 1) < 1e-9 && Math.abs(state.scaleY - 1) < 1e-9;\n}\n\n/** Integer output dimensions for an upstream image, clamped per axis. */\nexport function resolveOutputSize(\n state: ResizeState,\n upstream: { readonly width: number; readonly height: number },\n): { width: number; height: number } {\n const width = clampInt(Math.round(upstream.width * state.scaleX));\n const height = clampInt(Math.round(upstream.height * state.scaleY));\n return { width, height };\n}\n\n/** Set width via a pixel value; with `lockAspect` the vertical scale follows. */\nexport function setWidthPx(\n state: ResizeState,\n widthPx: number,\n upstream: { readonly width: number; readonly height: number },\n): ResizeState {\n if (upstream.width <= 0) return state;\n const target = clampInt(Math.round(widthPx));\n const scaleX = target / upstream.width;\n const scaleY = state.lockAspect ? scaleX : state.scaleY;\n return { ...state, scaleX, scaleY };\n}\n\nexport function setHeightPx(\n state: ResizeState,\n heightPx: number,\n upstream: { readonly width: number; readonly height: number },\n): ResizeState {\n if (upstream.height <= 0) return state;\n const target = clampInt(Math.round(heightPx));\n const scaleY = target / upstream.height;\n const scaleX = state.lockAspect ? scaleY : state.scaleX;\n return { ...state, scaleX, scaleY };\n}\n\n/** Uniform percentage change; always touches both axes regardless of `lockAspect`. */\nexport function setPercent(state: ResizeState, percent: number): ResizeState {\n const scale = clampScaleForPercent(percent / 100);\n return { ...state, scaleX: scale, scaleY: scale };\n}\n\nexport function setLockAspect(state: ResizeState, locked: boolean): ResizeState {\n if (state.lockAspect === locked) return state;\n if (!locked) return { ...state, lockAspect: false };\n // Average the two scales so neither axis arbitrarily wins on lock.\n const merged = (state.scaleX + state.scaleY) / 2;\n return { scaleX: merged, scaleY: merged, lockAspect: true };\n}\n\n/** Percent display value; when axes differ, returns the larger scale. */\nexport function effectivePercent(state: ResizeState): number {\n const max = Math.max(state.scaleX, state.scaleY);\n return Math.round(max * 1000) / 10;\n}\n\nfunction clampInt(n: number): number {\n if (!Number.isFinite(n)) return MIN_DIMENSION;\n return Math.max(MIN_DIMENSION, Math.min(MAX_DIMENSION, Math.trunc(n)));\n}\n\nfunction clampScaleForPercent(scale: number): number {\n if (!Number.isFinite(scale) || scale <= 0) return 0.01;\n return Math.max(0.01, Math.min(scale, MAX_DIMENSION));\n}\n","import { type BakeCanvas, createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { type ResizeState, isResizeNoOp, resolveOutputSize } from './state.js';\n\n/**\n * Resize `source` to the dimensions implied by `state`. Downscales > 2×\n * run a halving pyramid first so the bilinear-ish `drawImage` scaler\n * doesn't alias.\n */\nexport async function bakeResize(state: ResizeState, source: SourceImage): Promise<SourceImage> {\n if (isResizeNoOp(state)) return source;\n const { width: targetW, height: targetH } = resolveOutputSize(state, source);\n if (targetW <= 0 || targetH <= 0) return source;\n\n const halvings = countHalvingsNeeded(source.width, source.height, targetW, targetH);\n\n let current: { bitmap: CanvasImageSource; width: number; height: number } = {\n bitmap: source.bitmap,\n width: source.width,\n height: source.height,\n };\n\n // Each halving writes into its own canvas so the source is never reused as destination.\n const intermediates: BakeCanvas[] = [];\n for (let i = 0; i < halvings; i++) {\n const stepW = Math.max(targetW, Math.floor(current.width / 2));\n const stepH = Math.max(targetH, Math.floor(current.height / 2));\n const step = drawScaled(current, stepW, stepH);\n intermediates.push(step);\n current = { bitmap: step.canvas, width: stepW, height: stepH };\n }\n\n const final = drawScaled(current, targetW, targetH);\n\n intermediates.length = 0;\n\n return {\n bitmap: final.canvas,\n width: targetW,\n height: targetH,\n mimeType: source.mimeType,\n };\n}\n\nfunction countHalvingsNeeded(srcW: number, srcH: number, targetW: number, targetH: number): number {\n let w = srcW;\n let h = srcH;\n let count = 0;\n // Defensive cap so a malformed input can't spin forever.\n while ((w / 2 > targetW || h / 2 > targetH) && count < 16) {\n w = Math.floor(w / 2);\n h = Math.floor(h / 2);\n count += 1;\n }\n return count;\n}\n\nfunction drawScaled(\n current: { bitmap: CanvasImageSource; width: number; height: number },\n outW: number,\n outH: number,\n): BakeCanvas {\n const bake = createBakeCanvas(outW, outH);\n const ctx = getBakeContext2D(bake);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(current.bitmap, 0, 0, current.width, current.height, 0, 0, outW, outH);\n return bake;\n}\n","/**\n * Six tone adjustments stored as slider values in [-100, +100]; math\n * constants (gamma exponent, contrast multiplier, etc.) are computed\n * from these in `math.ts` at LUT-build time.\n */\nexport interface FinetuneState {\n readonly brightness: number;\n readonly contrast: number;\n readonly saturation: number;\n readonly exposure: number;\n readonly clarity: number;\n readonly gamma: number;\n}\n\nexport const FINETUNE_MIN = -100;\nexport const FINETUNE_MAX = 100;\nexport const FINETUNE_STEP = 1;\n\nexport const DEFAULT_FINETUNE_STATE: FinetuneState = {\n brightness: 0,\n contrast: 0,\n saturation: 0,\n exposure: 0,\n clarity: 0,\n gamma: 0,\n};\n\nexport type FinetuneKey = keyof FinetuneState;\n\nexport const FINETUNE_ADJUSTMENTS: readonly {\n readonly key: FinetuneKey;\n readonly label: string;\n}[] = [\n { key: 'brightness', label: 'Brightness' },\n { key: 'contrast', label: 'Contrast' },\n { key: 'saturation', label: 'Saturation' },\n { key: 'exposure', label: 'Exposure' },\n { key: 'clarity', label: 'Clarity' },\n { key: 'gamma', label: 'Gamma' },\n];\n\nexport function initialFinetuneState(): FinetuneState {\n return DEFAULT_FINETUNE_STATE;\n}\n\nexport function isFinetuneNoOp(state: FinetuneState): boolean {\n return (\n state.brightness === 0 &&\n state.contrast === 0 &&\n state.saturation === 0 &&\n state.exposure === 0 &&\n state.clarity === 0 &&\n state.gamma === 0\n );\n}\n\n/** Update a single adjustment, clamped to the legal range. */\nexport function setFinetune(state: FinetuneState, key: FinetuneKey, value: number): FinetuneState {\n const next = clampSliderValue(value);\n if (state[key] === next) return state;\n return { ...state, [key]: next };\n}\n\n/** Reset a single adjustment to its default of 0. */\nexport function resetFinetune(state: FinetuneState, key: FinetuneKey): FinetuneState {\n if (state[key] === 0) return state;\n return { ...state, [key]: 0 };\n}\n\n/** Reset every adjustment to its default. */\nexport function resetAllFinetune(): FinetuneState {\n return DEFAULT_FINETUNE_STATE;\n}\n\nfunction clampSliderValue(value: number): number {\n if (Number.isNaN(value)) return 0;\n if (value <= FINETUNE_MIN) return FINETUNE_MIN;\n if (value >= FINETUNE_MAX) return FINETUNE_MAX;\n // Snap to slider step so programmatic setters round-trip through the input.\n return Math.round(value / FINETUNE_STEP) * FINETUNE_STEP;\n}\n","/**\n * Pure math for the finetune adjustments. Slider values arrive in\n * [-100, +100] from `state.ts`. Shared by the bake (full-resolution)\n * and the live preview (display-resolution).\n */\n\nimport type { FinetuneState } from './state.js';\n\n/**\n * 256-entry LUT collapsing brightness + contrast + exposure + gamma into\n * one per-byte mapping. Saturation and clarity run in separate passes.\n */\nexport function buildFinetuneLut(state: FinetuneState): Uint8ClampedArray {\n const lut = new Uint8ClampedArray(256);\n\n // brightness: -100 → -0.5, +100 → +0.5 on the normalised value\n const brightnessOffset = state.brightness / 200;\n // contrast multiplier around mid-gray: -100 → 0×, +100 → 2×\n const contrastFactor = 1 + state.contrast / 100;\n // exposure multiplier: -100 → 0.5×, +100 → 1.5×\n const exposureFactor = 1 + state.exposure / 200;\n // gamma exponent: -100 → 2.0, 0 → 1.0, +100 → 0.5\n const gammaExponent = gammaExponentFor(state.gamma);\n\n for (let v = 0; v < 256; v++) {\n let x = v / 255;\n x = x * exposureFactor;\n x = (x - 0.5) * contrastFactor + 0.5;\n x = x + brightnessOffset;\n if (x < 0) x = 0;\n else if (x > 1) x = 1;\n x = x ** gammaExponent;\n if (x < 0) x = 0;\n else if (x > 1) x = 1;\n lut[v] = Math.round(x * 255);\n }\n\n return lut;\n}\n\nfunction gammaExponentFor(slider: number): number {\n if (slider === 0) return 1;\n if (slider > 0) return 1 - 0.5 * (slider / 100);\n return 1 + 1.0 * (-slider / 100);\n}\n\n/**\n * Apply the LUT and saturation in one pass. `src`/`dst` may be the same\n * buffer. Saturation: -100 → grayscale (Rec. 709), 0 → identity, +100 → 2×.\n */\nexport function applyFinetuneLutAndSaturation(\n src: Uint8ClampedArray,\n dst: Uint8ClampedArray,\n lut: Uint8ClampedArray,\n state: FinetuneState,\n): void {\n const len = src.length;\n if (dst.length !== len) {\n throw new Error('applyFinetuneLutAndSaturation: src/dst length mismatch');\n }\n\n const saturation = 1 + state.saturation / 100;\n\n // Fast path for the common identity (saturation === 0).\n if (saturation === 1) {\n for (let i = 0; i < len; i += 4) {\n dst[i] = lut[src[i]];\n dst[i + 1] = lut[src[i + 1]];\n dst[i + 2] = lut[src[i + 2]];\n dst[i + 3] = src[i + 3];\n }\n return;\n }\n\n for (let i = 0; i < len; i += 4) {\n const r0 = lut[src[i]];\n const g0 = lut[src[i + 1]];\n const b0 = lut[src[i + 2]];\n // Rec. 709 luminance.\n const y = 0.2126 * r0 + 0.7152 * g0 + 0.0722 * b0;\n let r = y + (r0 - y) * saturation;\n let g = y + (g0 - y) * saturation;\n let b = y + (b0 - y) * saturation;\n if (r < 0) r = 0;\n else if (r > 255) r = 255;\n if (g < 0) g = 0;\n else if (g > 255) g = 255;\n if (b < 0) b = 0;\n else if (b > 255) b = 255;\n dst[i] = r;\n dst[i + 1] = g;\n dst[i + 2] = b;\n dst[i + 3] = src[i + 3];\n }\n}\n\n/**\n * Unsharp-mask local contrast: `result = dst + (clarity/100) * (dst - blurred)`.\n * `clarity === 0` is a no-op; caller is expected to skip the call.\n */\nexport function applyClarity(\n dst: Uint8ClampedArray,\n blurred: Uint8ClampedArray,\n clarity: number,\n): void {\n if (clarity === 0) return;\n const len = dst.length;\n if (blurred.length !== len) {\n throw new Error('applyClarity: dst/blurred length mismatch');\n }\n const amount = clarity / 100;\n for (let i = 0; i < len; i += 4) {\n const dr = dst[i];\n const dg = dst[i + 1];\n const db = dst[i + 2];\n let r = dr + amount * (dr - blurred[i]);\n let g = dg + amount * (dg - blurred[i + 1]);\n let b = db + amount * (db - blurred[i + 2]);\n if (r < 0) r = 0;\n else if (r > 255) r = 255;\n if (g < 0) g = 0;\n else if (g > 255) g = 255;\n if (b < 0) b = 0;\n else if (b > 255) b = 255;\n dst[i] = r;\n dst[i + 1] = g;\n dst[i + 2] = b;\n }\n}\n\n/** Separable 3×3 box blur (clamp at edges). Used as the unsharp-mask reference. */\nexport function boxBlur3x3(\n src: Uint8ClampedArray,\n tmp: Uint8ClampedArray,\n dst: Uint8ClampedArray,\n width: number,\n height: number,\n): void {\n if (src.length !== tmp.length || src.length !== dst.length) {\n throw new Error('boxBlur3x3: buffer length mismatch');\n }\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const xm = x === 0 ? 0 : x - 1;\n const xp = x === width - 1 ? width - 1 : x + 1;\n const i = (y * width + x) * 4;\n const im = (y * width + xm) * 4;\n const ip = (y * width + xp) * 4;\n tmp[i] = (src[im] + src[i] + src[ip]) / 3;\n tmp[i + 1] = (src[im + 1] + src[i + 1] + src[ip + 1]) / 3;\n tmp[i + 2] = (src[im + 2] + src[i + 2] + src[ip + 2]) / 3;\n tmp[i + 3] = src[i + 3];\n }\n }\n for (let y = 0; y < height; y++) {\n const ym = y === 0 ? 0 : y - 1;\n const yp = y === height - 1 ? height - 1 : y + 1;\n for (let x = 0; x < width; x++) {\n const i = (y * width + x) * 4;\n const im = (ym * width + x) * 4;\n const ip = (yp * width + x) * 4;\n dst[i] = (tmp[im] + tmp[i] + tmp[ip]) / 3;\n dst[i + 1] = (tmp[im + 1] + tmp[i + 1] + tmp[ip + 1]) / 3;\n dst[i + 2] = (tmp[im + 2] + tmp[i + 2] + tmp[ip + 2]) / 3;\n dst[i + 3] = tmp[i + 3];\n }\n }\n}\n\n/** Structural raster shape so tests can pass a plain object (jsdom 25 lacks `ImageData`). */\nexport interface RasterImage {\n readonly data: Uint8ClampedArray;\n readonly width: number;\n readonly height: number;\n}\n\n/** Apply the full pipeline (LUT + saturation + clarity); blur buffers allocate only when clarity ≠ 0. */\nexport function applyFinetuneToImageData(\n state: FinetuneState,\n baseline: RasterImage,\n dst: RasterImage,\n): void {\n if (\n baseline.width !== dst.width ||\n baseline.height !== dst.height ||\n baseline.data.length !== dst.data.length\n ) {\n throw new Error('applyFinetuneToImageData: baseline/dst dimensions mismatch');\n }\n\n const lut = buildFinetuneLut(state);\n applyFinetuneLutAndSaturation(baseline.data, dst.data, lut, state);\n\n if (state.clarity !== 0) {\n // Blur the pre-LUT baseline so the cached blur stays valid as long as\n // the baseline does; high-frequency is roughly order-independent here.\n const tmp = new Uint8ClampedArray(baseline.data.length);\n const blurred = new Uint8ClampedArray(baseline.data.length);\n boxBlur3x3(baseline.data, tmp, blurred, baseline.width, baseline.height);\n applyClarity(dst.data, blurred, state.clarity);\n }\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { applyFinetuneToImageData } from './math.js';\nimport { type FinetuneState, isFinetuneNoOp } from './state.js';\n\n/** Apply the six finetune adjustments at full resolution. Shares the math with the live preview. */\nexport async function bakeFinetune(\n state: FinetuneState,\n source: SourceImage,\n): Promise<SourceImage> {\n if (isFinetuneNoOp(state)) return source;\n\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n const baseline = ctx.getImageData(0, 0, source.width, source.height);\n // When clarity is non-zero the unsharp-mask step needs `dst` and `blurred`\n // simultaneously, so we can't reuse the baseline buffer in place.\n if (state.clarity === 0) {\n applyFinetuneToImageData(state, baseline, baseline);\n ctx.putImageData(baseline, 0, 0);\n } else {\n const dst = new ImageData(\n new Uint8ClampedArray(baseline.data.length),\n baseline.width,\n baseline.height,\n );\n applyFinetuneToImageData(state, baseline, dst);\n ctx.putImageData(dst, 0, 0);\n }\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n","/**\n * Curated `FinetuneState` shapes that one-click set the six tone numbers.\n * The filter tab is a UI view over the finetune store: clicking a preset\n * here is identical to dragging the matching sliders in finetune.\n */\nimport { DEFAULT_FINETUNE_STATE, type FinetuneState } from '../finetune/state.js';\n\nexport type FilterPresetId = 'none' | 'vivid' | 'mono' | 'soft' | 'punch' | 'mute' | 'bright';\n\nexport interface FilterPreset {\n readonly id: FilterPresetId;\n readonly label: string;\n readonly state: FinetuneState;\n}\n\n/** The seven presets in display order. `none` is the identity / off state. */\nexport const FILTER_PRESETS: readonly FilterPreset[] = [\n {\n id: 'none',\n label: 'None',\n state: DEFAULT_FINETUNE_STATE,\n },\n {\n id: 'vivid',\n label: 'Vivid',\n state: {\n brightness: 0,\n contrast: 10,\n saturation: 40,\n exposure: 0,\n clarity: 5,\n gamma: 0,\n },\n },\n {\n id: 'mono',\n label: 'Mono',\n // -100 saturation is bit-exact grayscale via Rec. 709 luminance.\n state: {\n brightness: 0,\n contrast: 15,\n saturation: -100,\n exposure: 0,\n clarity: 0,\n gamma: 0,\n },\n },\n {\n id: 'soft',\n label: 'Soft',\n state: {\n brightness: 5,\n contrast: -10,\n saturation: 0,\n exposure: 0,\n clarity: -25,\n gamma: 0,\n },\n },\n {\n id: 'punch',\n label: 'Punch',\n state: {\n brightness: 0,\n contrast: 30,\n saturation: 5,\n exposure: 0,\n clarity: 25,\n gamma: 0,\n },\n },\n {\n id: 'mute',\n label: 'Mute',\n state: {\n brightness: 0,\n contrast: 5,\n saturation: -50,\n exposure: 0,\n clarity: -5,\n gamma: 0,\n },\n },\n {\n id: 'bright',\n label: 'Bright',\n state: {\n brightness: 5,\n contrast: 5,\n saturation: 0,\n exposure: 15,\n clarity: 0,\n gamma: 0,\n },\n },\n];\n\n/** Structural equality across the six finetune fields. */\nexport function finetuneStatesEqual(a: FinetuneState, b: FinetuneState): boolean {\n return (\n a.brightness === b.brightness &&\n a.contrast === b.contrast &&\n a.saturation === b.saturation &&\n a.exposure === b.exposure &&\n a.clarity === b.clarity &&\n a.gamma === b.gamma\n );\n}\n\n/** Preset whose state matches `state` exactly, or `undefined` if between presets. */\nexport function findActivePreset(state: FinetuneState): FilterPreset | undefined {\n for (const preset of FILTER_PRESETS) {\n if (finetuneStatesEqual(preset.state, state)) return preset;\n }\n return undefined;\n}\n","/**\n * Annotation state and pure mutators. Shapes are a flat discriminated\n * union keyed on `kind`; all coordinates are image-space pixels.\n */\n\nimport type { Point } from '../../geometry/rect.js';\n\nexport type ShapeKind = 'text' | 'rect' | 'ellipse' | 'arrow' | 'freehand' | 'highlight';\n\n/** Tools the annotation plugin exposes. `select` is the picker. */\nexport type AnnotateTool = ShapeKind | 'select';\n\ninterface ShapeBase {\n /** Stable per-session id; survives undo/redo. */\n readonly id: string;\n readonly kind: ShapeKind;\n}\n\nexport interface TextShape extends ShapeBase {\n readonly kind: 'text';\n /** Top-left anchor in image-space pixels. */\n readonly x: number;\n readonly y: number;\n readonly text: string;\n /** Font size in image-space pixels. */\n readonly fontSize: number;\n /** CSS colour string. */\n readonly color: string;\n readonly textAlign: 'left' | 'center' | 'right';\n}\n\nexport interface RectShape extends ShapeBase {\n readonly kind: 'rect';\n readonly x: number;\n readonly y: number;\n /** Non-negative after gesture commit; may be negative mid-drag. */\n readonly width: number;\n readonly height: number;\n readonly strokeColor: string;\n readonly strokeWidth: number;\n /** `null` means \"no fill\". */\n readonly fillColor: string | null;\n}\n\nexport interface EllipseShape extends ShapeBase {\n readonly kind: 'ellipse';\n /** Bounding-box top-left; the ellipse fits inside the box. */\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n readonly strokeColor: string;\n readonly strokeWidth: number;\n readonly fillColor: string | null;\n}\n\nexport interface ArrowShape extends ShapeBase {\n readonly kind: 'arrow';\n readonly x1: number;\n readonly y1: number;\n /** Arrowhead is drawn at (x2, y2). */\n readonly x2: number;\n readonly y2: number;\n readonly color: string;\n readonly strokeWidth: number;\n}\n\nexport interface FreehandShape extends ShapeBase {\n readonly kind: 'freehand';\n /** Decimated raw points in image-space; smoothing happens at render time. */\n readonly points: ReadonlyArray<Point>;\n readonly color: string;\n readonly strokeWidth: number;\n}\n\nexport interface HighlightShape extends ShapeBase {\n readonly kind: 'highlight';\n readonly points: ReadonlyArray<Point>;\n /** Default semi-transparent yellow, drawn with `multiply` blend mode. */\n readonly color: string;\n readonly strokeWidth: number;\n}\n\nexport type Shape =\n | TextShape\n | RectShape\n | EllipseShape\n | ArrowShape\n | FreehandShape\n | HighlightShape;\n\nexport interface StylePalette {\n readonly color: string;\n readonly strokeWidth: number;\n /** Used for new rect/ellipse fills; `null` = unfilled. */\n readonly fillColor: string | null;\n /** Used for new text shapes. In image-space pixels. */\n readonly fontSize: number;\n}\n\nexport interface AnnotateState {\n readonly shapes: ReadonlyArray<Shape>;\n readonly selectedId: string | null;\n readonly activeTool: AnnotateTool;\n readonly currentStyle: StylePalette;\n /** Image-space dimensions of the upstream-baked source the plugin was mounted on. */\n readonly imageSize: { readonly width: number; readonly height: number };\n /** Monotonic counter used to mint shape ids. Never decreases. */\n readonly nextShapeNumber: number;\n}\n\n/** Yellow @ 35% alpha; the highlight bake uses `multiply` blending. */\nexport const HIGHLIGHT_DEFAULT_COLOR = 'rgba(255, 235, 59, 0.35)';\nexport const HIGHLIGHT_DEFAULT_STROKE = 18;\nexport const FREEHAND_DEFAULT_STROKE = 6;\nexport const TEXT_DEFAULT_FONT_SIZE = 32;\nexport const DEFAULT_PALETTE_COLOR = '#ff3b30';\nexport const DEFAULT_STROKE_WIDTH = 4;\n\nexport function defaultStylePalette(): StylePalette {\n return {\n color: DEFAULT_PALETTE_COLOR,\n strokeWidth: DEFAULT_STROKE_WIDTH,\n fillColor: null,\n fontSize: TEXT_DEFAULT_FONT_SIZE,\n };\n}\n\nexport interface InitialAnnotateStateInput {\n readonly imageSize: { readonly width: number; readonly height: number };\n}\n\nexport function initialAnnotateState(input: InitialAnnotateStateInput): AnnotateState {\n return {\n shapes: [],\n selectedId: null,\n activeTool: 'select',\n currentStyle: defaultStylePalette(),\n imageSize: input.imageSize,\n nextShapeNumber: 1,\n };\n}\n\n/** Allocate a new shape id from the monotonic counter; caller threads `nextShapeNumber` back into state. */\nexport function mintShapeId(state: AnnotateState): {\n id: string;\n nextShapeNumber: number;\n} {\n return {\n id: `s_${state.nextShapeNumber.toString(36)}`,\n nextShapeNumber: state.nextShapeNumber + 1,\n };\n}\n\nexport function setActiveTool(state: AnnotateState, tool: AnnotateTool): AnnotateState {\n if (state.activeTool === tool) return state;\n // Switching to a drawing tool deselects so the next drag starts a new shape.\n return { ...state, activeTool: tool, selectedId: tool === 'select' ? state.selectedId : null };\n}\n\nexport function setStyle(state: AnnotateState, partial: Partial<StylePalette>): AnnotateState {\n return { ...state, currentStyle: { ...state.currentStyle, ...partial } };\n}\n\nexport function selectShape(state: AnnotateState, id: string | null): AnnotateState {\n if (state.selectedId === id) return state;\n return { ...state, selectedId: id };\n}\n\nexport function addShape(state: AnnotateState, shape: Shape): AnnotateState {\n return { ...state, shapes: [...state.shapes, shape], selectedId: shape.id };\n}\n\nexport function replaceShape(state: AnnotateState, shape: Shape): AnnotateState {\n let changed = false;\n const next = state.shapes.map((existing) => {\n if (existing.id !== shape.id) return existing;\n changed = true;\n return shape;\n });\n if (!changed) return state;\n return { ...state, shapes: next };\n}\n\nexport function deleteShape(state: AnnotateState, id: string): AnnotateState {\n const next = state.shapes.filter((shape) => shape.id !== id);\n if (next.length === state.shapes.length) return state;\n return {\n ...state,\n shapes: next,\n selectedId: state.selectedId === id ? null : state.selectedId,\n };\n}\n\nexport function findShape(state: AnnotateState, id: string | null): Shape | undefined {\n if (id === null) return undefined;\n return state.shapes.find((shape) => shape.id === id);\n}\n\n/** Normalise a rect extent so `width`/`height` are non-negative after a sign-flip drag. */\nexport function normaliseRectExtent(extent: {\n x: number;\n y: number;\n width: number;\n height: number;\n}): { x: number; y: number; width: number; height: number } {\n let { x, y, width, height } = extent;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n return { x, y, width, height };\n}\n\nexport function translateShape(shape: Shape, dx: number, dy: number): Shape {\n switch (shape.kind) {\n case 'text':\n return { ...shape, x: shape.x + dx, y: shape.y + dy };\n case 'rect':\n case 'ellipse':\n return { ...shape, x: shape.x + dx, y: shape.y + dy };\n case 'arrow':\n return {\n ...shape,\n x1: shape.x1 + dx,\n y1: shape.y1 + dy,\n x2: shape.x2 + dx,\n y2: shape.y2 + dy,\n };\n case 'freehand':\n case 'highlight':\n return { ...shape, points: shape.points.map((p) => ({ x: p.x + dx, y: p.y + dy })) };\n default:\n return assertNever(shape);\n }\n}\n\n/** Type-narrowing helper for exhaustive switches over `Shape`. */\nexport function assertNever(value: never): never {\n throw new Error(`Unhandled annotation shape kind: ${JSON.stringify(value)}`);\n}\n\n/**\n * Mirror a shape across an axis of `dims`. Rect/ellipse top-left is\n * remapped so the visible rectangle straddles the same pixels; arrow\n * endpoints and freehand points mirror independently. Text uses its\n * anchor only — the glyph rect walks slightly relative to centre.\n */\nexport function mirrorShape(\n shape: Shape,\n axis: 'horizontal' | 'vertical',\n dims: { readonly width: number; readonly height: number },\n): Shape {\n if (axis === 'horizontal') {\n switch (shape.kind) {\n case 'rect':\n case 'ellipse':\n return { ...shape, x: dims.width - shape.x - shape.width };\n case 'text':\n return { ...shape, x: dims.width - shape.x };\n case 'arrow':\n return { ...shape, x1: dims.width - shape.x1, x2: dims.width - shape.x2 };\n case 'freehand':\n case 'highlight':\n return {\n ...shape,\n points: shape.points.map((p) => ({ x: dims.width - p.x, y: p.y })),\n };\n default:\n return assertNever(shape);\n }\n }\n switch (shape.kind) {\n case 'rect':\n case 'ellipse':\n return { ...shape, y: dims.height - shape.y - shape.height };\n case 'text':\n return { ...shape, y: dims.height - shape.y };\n case 'arrow':\n return { ...shape, y1: dims.height - shape.y1, y2: dims.height - shape.y2 };\n case 'freehand':\n case 'highlight':\n return {\n ...shape,\n points: shape.points.map((p) => ({ x: p.x, y: dims.height - p.y })),\n };\n default:\n return assertNever(shape);\n }\n}\n\n/**\n * Rotate a shape `turns × 90°` CW around the centre of `oldDims`. Returns\n * coordinates in the post-rotation image's coord space (dims swap on odd turns).\n */\nexport function rotateShape(\n shape: Shape,\n turns: 0 | 1 | 2 | 3,\n oldDims: { readonly width: number; readonly height: number },\n): Shape {\n if (turns === 0) return shape;\n const rotatePoint = (x: number, y: number): { x: number; y: number } => {\n if (turns === 1) return { x: oldDims.height - y, y: x };\n if (turns === 2) return { x: oldDims.width - x, y: oldDims.height - y };\n return { x: y, y: oldDims.width - x };\n };\n switch (shape.kind) {\n case 'rect':\n case 'ellipse': {\n // Rotated TL + BR become two corners of the new axis-aligned box.\n const corners = [\n rotatePoint(shape.x, shape.y),\n rotatePoint(shape.x + shape.width, shape.y + shape.height),\n ];\n const newX = Math.min(corners[0].x, corners[1].x);\n const newY = Math.min(corners[0].y, corners[1].y);\n const newW = Math.abs(corners[1].x - corners[0].x);\n const newH = Math.abs(corners[1].y - corners[0].y);\n return { ...shape, x: newX, y: newY, width: newW, height: newH };\n }\n case 'text': {\n const p = rotatePoint(shape.x, shape.y);\n return { ...shape, x: p.x, y: p.y };\n }\n case 'arrow': {\n const p1 = rotatePoint(shape.x1, shape.y1);\n const p2 = rotatePoint(shape.x2, shape.y2);\n return { ...shape, x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y };\n }\n case 'freehand':\n case 'highlight':\n return { ...shape, points: shape.points.map((p) => rotatePoint(p.x, p.y)) };\n default:\n return assertNever(shape);\n }\n}\n\n/** Apply a transformation to every shape in `state.shapes`. */\nexport function transformShapes(\n state: AnnotateState,\n transformer: (shape: Shape) => Shape,\n): AnnotateState {\n if (state.shapes.length === 0) return state;\n return { ...state, shapes: state.shapes.map(transformer) };\n}\n\n/**\n * Kinds placeable from the keyboard. Freehand / highlight are excluded;\n * a \"default at centre\" instance has no honest shape for those.\n */\nexport type KeyboardPlaceableKind = 'text' | 'rect' | 'ellipse' | 'arrow';\n\nexport const KEYBOARD_PLACEABLE_KINDS: ReadonlyArray<KeyboardPlaceableKind> = [\n 'text',\n 'rect',\n 'ellipse',\n 'arrow',\n];\n\nexport function isKeyboardPlaceableKind(kind: ShapeKind): kind is KeyboardPlaceableKind {\n return kind === 'text' || kind === 'rect' || kind === 'ellipse' || kind === 'arrow';\n}\n\nexport interface CreateCenteredShapeContext {\n readonly imageSize: { readonly width: number; readonly height: number };\n readonly style: StylePalette;\n readonly id: string;\n}\n\n/** A `Shape` whose kind is keyboard-placeable (rect / ellipse / arrow / text). */\nexport type KeyboardPlaceableShape = TextShape | RectShape | EllipseShape | ArrowShape;\n\nexport function createCenteredShape(\n kind: KeyboardPlaceableKind,\n ctx: CreateCenteredShapeContext,\n): KeyboardPlaceableShape {\n const { imageSize, style, id } = ctx;\n const shortEdge = Math.min(imageSize.width, imageSize.height);\n const cx = imageSize.width / 2;\n const cy = imageSize.height / 2;\n\n switch (kind) {\n case 'rect':\n case 'ellipse': {\n const size = Math.max(80, Math.round(shortEdge * 0.25));\n const x = Math.round(cx - size / 2);\n const y = Math.round(cy - size / 2);\n if (kind === 'rect') {\n return {\n id,\n kind: 'rect',\n x,\n y,\n width: size,\n height: size,\n strokeColor: style.color,\n strokeWidth: style.strokeWidth,\n fillColor: style.fillColor,\n };\n }\n return {\n id,\n kind: 'ellipse',\n x,\n y,\n width: size,\n height: size,\n strokeColor: style.color,\n strokeWidth: style.strokeWidth,\n fillColor: style.fillColor,\n };\n }\n case 'arrow': {\n const length = Math.max(100, Math.round(shortEdge * 0.3));\n const x1 = Math.round(cx - length / 2);\n const x2 = x1 + length;\n const y = Math.round(cy);\n return {\n id,\n kind: 'arrow',\n x1,\n y1: y,\n x2,\n y2: y,\n color: style.color,\n strokeWidth: style.strokeWidth,\n };\n }\n case 'text': {\n const x = Math.round(cx);\n const y = Math.round(cy - style.fontSize / 2);\n return {\n id,\n kind: 'text',\n x,\n y,\n text: '',\n fontSize: style.fontSize,\n color: style.color,\n textAlign: 'center',\n };\n }\n }\n}\n","import type { Rect } from '../../geometry/rect.js';\nimport { type Shape, assertNever } from './state.js';\n\n/**\n * Axis-aligned bounding box in image-space pixels. Text uses a font-metric\n * estimate because jsdom's `measureText` returns 0; the renderer measures\n * real text at paint time.\n */\nexport function boundingBoxOf(shape: Shape): Rect {\n switch (shape.kind) {\n case 'text': {\n const { width, height } = estimateTextSize(shape.text, shape.fontSize);\n const x = alignToOrigin(shape.x, width, shape.textAlign);\n return { x, y: shape.y, width, height };\n }\n case 'rect':\n case 'ellipse':\n return { x: shape.x, y: shape.y, width: shape.width, height: shape.height };\n case 'arrow': {\n const x = Math.min(shape.x1, shape.x2);\n const y = Math.min(shape.y1, shape.y2);\n return {\n x,\n y,\n width: Math.abs(shape.x2 - shape.x1),\n height: Math.abs(shape.y2 - shape.y1),\n };\n }\n case 'freehand':\n case 'highlight': {\n const head = shape.points[0];\n if (!head) return { x: 0, y: 0, width: 0, height: 0 };\n let minX = head.x;\n let minY = head.y;\n let maxX = head.x;\n let maxY = head.y;\n for (const p of shape.points) {\n if (p.x < minX) minX = p.x;\n if (p.x > maxX) maxX = p.x;\n if (p.y < minY) minY = p.y;\n if (p.y > maxY) maxY = p.y;\n }\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n }\n default:\n return assertNever(shape);\n }\n}\n\n/**\n * Eight-handle layout (corners + edges). Arrows reuse `tl`/`br` as\n * endpoint handles; callers detect arrow handles by shape kind.\n */\nexport type SelectionHandle = 'tl' | 'tr' | 'bl' | 'br' | 't' | 'r' | 'b' | 'l';\n\nexport const ALL_SELECTION_HANDLES: readonly SelectionHandle[] = [\n 'tl',\n 'tr',\n 'bl',\n 'br',\n 't',\n 'r',\n 'b',\n 'l',\n];\n\n/** Image-space coordinates for each handle; renderer projects to display. */\nexport function selectionHandlePositions(\n rect: Rect,\n): Record<SelectionHandle, { x: number; y: number }> {\n const left = rect.x;\n const right = rect.x + rect.width;\n const top = rect.y;\n const bottom = rect.y + rect.height;\n const cx = rect.x + rect.width / 2;\n const cy = rect.y + rect.height / 2;\n return {\n tl: { x: left, y: top },\n tr: { x: right, y: top },\n bl: { x: left, y: bottom },\n br: { x: right, y: bottom },\n t: { x: cx, y: top },\n r: { x: right, y: cy },\n b: { x: cx, y: bottom },\n l: { x: left, y: cy },\n };\n}\n\n/** Apply a handle drag to a rect. Returns the new rect; the caller normalises. */\nexport function rectFromHandleDrag(\n initial: Rect,\n handle: SelectionHandle,\n pointer: { x: number; y: number },\n): Rect {\n let x = initial.x;\n let y = initial.y;\n let right = initial.x + initial.width;\n let bottom = initial.y + initial.height;\n\n if (handle === 'tl' || handle === 'l' || handle === 'bl') x = pointer.x;\n if (handle === 'tr' || handle === 'r' || handle === 'br') right = pointer.x;\n if (handle === 'tl' || handle === 't' || handle === 'tr') y = pointer.y;\n if (handle === 'bl' || handle === 'b' || handle === 'br') bottom = pointer.y;\n\n return { x, y, width: right - x, height: bottom - y };\n}\n\n/**\n * Estimate painted text size without `measureText` (jsdom returns 0).\n * Uses 0.55em mean Latin char width and 1.2em line height — close enough\n * for selection-handle placement.\n */\nexport function estimateTextSize(\n text: string,\n fontSize: number,\n): { width: number; height: number } {\n const lines = text.length === 0 ? [''] : text.split('\\n');\n let maxLineLen = 0;\n for (const line of lines) {\n if (line.length > maxLineLen) maxLineLen = line.length;\n }\n const width = Math.max(fontSize * 0.6, maxLineLen * fontSize * 0.55);\n const height = lines.length * fontSize * 1.2;\n return { width, height };\n}\n\n/** Convert a text shape's anchor to the bounding box's top-left given `textAlign`. */\nexport function alignToOrigin(\n anchorX: number,\n width: number,\n align: 'left' | 'center' | 'right',\n): number {\n switch (align) {\n case 'left':\n return anchorX;\n case 'center':\n return anchorX - width / 2;\n case 'right':\n return anchorX - width;\n }\n}\n","import type { Point } from '../../geometry/rect.js';\nimport { boundingBoxOf } from './geometry.js';\nimport { type Shape, assertNever } from './state.js';\n\n/** Picking margin added to every stroked-shape hit-test, in image-space pixels. */\nexport const PICK_TOLERANCE = 4;\n\n/** Find the topmost shape under `point` (image-space). Iterates back-to-front. */\nexport function pickShape(shapes: ReadonlyArray<Shape>, point: Point): Shape | undefined {\n for (let i = shapes.length - 1; i >= 0; i--) {\n const shape = shapes[i];\n if (shape && hitTest(shape, point)) return shape;\n }\n return undefined;\n}\n\nexport function hitTest(shape: Shape, point: Point): boolean {\n switch (shape.kind) {\n case 'text':\n return pointInRect(point, boundingBoxOf(shape));\n case 'rect': {\n const inside = pointInRect(point, normaliseBox(shape));\n // Filled rects pick anywhere inside; outline-only picks on the stroke (with tolerance).\n if (shape.fillColor !== null) return inside;\n const outer = expandRect(normaliseBox(shape), shape.strokeWidth / 2 + PICK_TOLERANCE);\n const inner = expandRect(normaliseBox(shape), -(shape.strokeWidth / 2 + PICK_TOLERANCE));\n return pointInRect(point, outer) && !pointInRect(point, inner);\n }\n case 'ellipse': {\n const box = normaliseBox(shape);\n const rx = box.width / 2;\n const ry = box.height / 2;\n const cx = box.x + rx;\n const cy = box.y + ry;\n if (rx <= 0 || ry <= 0) return false;\n const nx = (point.x - cx) / rx;\n const ny = (point.y - cy) / ry;\n const r2 = nx * nx + ny * ny;\n const tolerance = (shape.strokeWidth / 2 + PICK_TOLERANCE) / Math.min(rx, ry);\n if (shape.fillColor !== null) return r2 <= (1 + tolerance) ** 2;\n return r2 <= (1 + tolerance) ** 2 && r2 >= (1 - tolerance) ** 2;\n }\n case 'arrow':\n return pointNearSegment(\n point,\n { x: shape.x1, y: shape.y1 },\n { x: shape.x2, y: shape.y2 },\n shape.strokeWidth / 2 + PICK_TOLERANCE,\n );\n case 'freehand':\n case 'highlight': {\n const box = boundingBoxOf(shape);\n const expanded = expandRect(box, shape.strokeWidth / 2 + PICK_TOLERANCE);\n if (!pointInRect(point, expanded)) return false;\n const tolerance = shape.strokeWidth / 2 + PICK_TOLERANCE;\n for (let i = 1; i < shape.points.length; i++) {\n const a = shape.points[i - 1];\n const b = shape.points[i];\n if (a && b && pointNearSegment(point, a, b, tolerance)) return true;\n }\n if (shape.points.length === 1) {\n const p = shape.points[0];\n if (!p) return false;\n const dx = p.x - point.x;\n const dy = p.y - point.y;\n return dx * dx + dy * dy <= tolerance * tolerance;\n }\n return false;\n }\n default:\n return assertNever(shape);\n }\n}\n\nfunction pointInRect(\n point: Point,\n rect: { x: number; y: number; width: number; height: number },\n): boolean {\n return (\n point.x >= rect.x &&\n point.y >= rect.y &&\n point.x <= rect.x + rect.width &&\n point.y <= rect.y + rect.height\n );\n}\n\nfunction expandRect(\n rect: { x: number; y: number; width: number; height: number },\n amount: number,\n): { x: number; y: number; width: number; height: number } {\n return {\n x: rect.x - amount,\n y: rect.y - amount,\n width: rect.width + amount * 2,\n height: rect.height + amount * 2,\n };\n}\n\nfunction normaliseBox(shape: { x: number; y: number; width: number; height: number }): {\n x: number;\n y: number;\n width: number;\n height: number;\n} {\n let { x, y, width, height } = shape;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n return { x, y, width, height };\n}\n\nfunction pointNearSegment(point: Point, a: Point, b: Point, tolerance: number): boolean {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const len2 = dx * dx + dy * dy;\n if (len2 === 0) {\n const ex = point.x - a.x;\n const ey = point.y - a.y;\n return ex * ex + ey * ey <= tolerance * tolerance;\n }\n let t = ((point.x - a.x) * dx + (point.y - a.y) * dy) / len2;\n if (t < 0) t = 0;\n else if (t > 1) t = 1;\n const projX = a.x + t * dx;\n const projY = a.y + t * dy;\n const ex = point.x - projX;\n const ey = point.y - projY;\n return ex * ex + ey * ey <= tolerance * tolerance;\n}\n","/**\n * Freehand stroke decimation + midpoint-curve smoothing. Decimation drops\n * sub-pixel samples from the raw pointer stream; the curve smoothing\n * happens at draw time so stored points stay unchanged across edits.\n */\n\nimport type { Point } from '../../geometry/rect.js';\n\nexport const MIN_SAMPLE_DISTANCE = 2;\n\n/** Drop interior points closer than `MIN_SAMPLE_DISTANCE` to the previous kept point. */\nexport function decimatePoints(points: ReadonlyArray<Point>): Point[] {\n if (points.length <= 1) return [...points];\n const head = points[0];\n if (!head) return [];\n const out: Point[] = [head];\n let last = head;\n const minSq = MIN_SAMPLE_DISTANCE * MIN_SAMPLE_DISTANCE;\n for (let i = 1; i < points.length - 1; i++) {\n const p = points[i];\n if (!p) continue;\n const dx = p.x - last.x;\n const dy = p.y - last.y;\n if (dx * dx + dy * dy < minSq) continue;\n out.push(p);\n last = p;\n }\n // Always keep the final sample so the stroke ends where the pen lifted.\n const tail = points[points.length - 1];\n if (tail && tail !== last) out.push(tail);\n return out;\n}\n\n/**\n * Trace a smoothed path through `points` using the midpoint-curve technique.\n * Caller owns `beginPath`, stroke style, and `stroke()` so blend modes\n * (highlight) can wrap this without re-implementing the curve math.\n */\nexport function tracePath(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n points: ReadonlyArray<Point>,\n): void {\n if (points.length === 0) return;\n const head = points[0];\n if (!head) return;\n if (points.length === 1) {\n // Single tap: zero-length segment relies on round lineCap to render a dot.\n ctx.moveTo(head.x, head.y);\n ctx.lineTo(head.x, head.y);\n return;\n }\n ctx.moveTo(head.x, head.y);\n for (let i = 1; i < points.length - 1; i++) {\n const a = points[i];\n const b = points[i + 1];\n if (!a || !b) continue;\n const midX = (a.x + b.x) / 2;\n const midY = (a.y + b.y) / 2;\n ctx.quadraticCurveTo(a.x, a.y, midX, midY);\n }\n const last = points[points.length - 1];\n if (last) ctx.lineTo(last.x, last.y);\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { tracePath } from './smooth.js';\nimport { type Shape, assertNever } from './state.js';\n\nexport interface AnnotateBakeInput {\n readonly shapes: ReadonlyArray<Shape>;\n}\n\n/**\n * System font stack used at bake; matches what the preview canvas renders.\n * No web font is loaded — the bundle budget rules it out and the bake\n * pipeline has no \"wait for font\" affordance.\n */\nexport const SYSTEM_FONT_STACK =\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif';\n\n/** Paint every shape onto a fresh canvas at the source's dimensions. */\nexport async function bakeAnnotate(\n state: AnnotateBakeInput,\n source: SourceImage,\n): Promise<SourceImage> {\n if (state.shapes.length === 0) return source;\n\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n for (const shape of state.shapes) {\n paintShape(ctx, shape);\n }\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n\n/** Paint one shape; caller positions the context for image-space coordinates. Shared by preview and bake. */\nexport function paintShape(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape,\n): void {\n switch (shape.kind) {\n case 'text':\n paintText(ctx, shape);\n return;\n case 'rect':\n paintRect(ctx, shape);\n return;\n case 'ellipse':\n paintEllipse(ctx, shape);\n return;\n case 'arrow':\n paintArrow(ctx, shape);\n return;\n case 'freehand':\n paintFreehand(ctx, shape);\n return;\n case 'highlight':\n paintHighlight(ctx, shape);\n return;\n default:\n assertNever(shape);\n }\n}\n\nfunction paintText(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'text' },\n): void {\n ctx.save();\n ctx.fillStyle = shape.color;\n ctx.font = `${shape.fontSize}px ${SYSTEM_FONT_STACK}`;\n ctx.textAlign = shape.textAlign;\n ctx.textBaseline = 'top';\n // Paint each line on its own; explicit `\\n` only (no auto-wrap).\n const lines = shape.text.length === 0 ? [''] : shape.text.split('\\n');\n const lineHeight = shape.fontSize * 1.2;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line === undefined) continue;\n ctx.fillText(line, shape.x, shape.y + i * lineHeight);\n }\n ctx.restore();\n}\n\nfunction paintRect(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'rect' },\n): void {\n ctx.save();\n if (shape.fillColor !== null) {\n ctx.fillStyle = shape.fillColor;\n ctx.fillRect(shape.x, shape.y, shape.width, shape.height);\n }\n if (shape.strokeWidth > 0) {\n ctx.strokeStyle = shape.strokeColor;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineJoin = 'miter';\n ctx.strokeRect(shape.x, shape.y, shape.width, shape.height);\n }\n ctx.restore();\n}\n\nfunction paintEllipse(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'ellipse' },\n): void {\n ctx.save();\n const rx = shape.width / 2;\n const ry = shape.height / 2;\n const cx = shape.x + rx;\n const cy = shape.y + ry;\n ctx.beginPath();\n ctx.ellipse(cx, cy, Math.max(0, rx), Math.max(0, ry), 0, 0, Math.PI * 2);\n if (shape.fillColor !== null) {\n ctx.fillStyle = shape.fillColor;\n ctx.fill();\n }\n if (shape.strokeWidth > 0) {\n ctx.strokeStyle = shape.strokeColor;\n ctx.lineWidth = shape.strokeWidth;\n ctx.stroke();\n }\n ctx.restore();\n}\n\nfunction paintArrow(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'arrow' },\n): void {\n const dx = shape.x2 - shape.x1;\n const dy = shape.y2 - shape.y1;\n const length = Math.sqrt(dx * dx + dy * dy);\n if (length < 0.5) return;\n\n ctx.save();\n ctx.strokeStyle = shape.color;\n ctx.fillStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n\n // Floor on head size so thin strokes still get a readable head.\n const headLength = Math.min(Math.max(shape.strokeWidth * 5, 28), length * 0.6);\n const headWidth = Math.max(shape.strokeWidth * 4, 18);\n const ux = dx / length;\n const uy = dy / length;\n // Shaft stops short of the tip so the head's base sits flush with the cap.\n const shaftEndX = shape.x2 - ux * headLength * 0.6;\n const shaftEndY = shape.y2 - uy * headLength * 0.6;\n\n ctx.beginPath();\n ctx.moveTo(shape.x1, shape.y1);\n ctx.lineTo(shaftEndX, shaftEndY);\n ctx.stroke();\n\n const baseX = shape.x2 - ux * headLength;\n const baseY = shape.y2 - uy * headLength;\n const px = -uy;\n const py = ux;\n ctx.beginPath();\n ctx.moveTo(shape.x2, shape.y2);\n ctx.lineTo(baseX + (px * headWidth) / 2, baseY + (py * headWidth) / 2);\n ctx.lineTo(baseX - (px * headWidth) / 2, baseY - (py * headWidth) / 2);\n ctx.closePath();\n ctx.fill();\n ctx.restore();\n}\n\nfunction paintFreehand(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'freehand' },\n): void {\n if (shape.points.length === 0) return;\n ctx.save();\n ctx.strokeStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.beginPath();\n tracePath(ctx, shape.points);\n ctx.stroke();\n ctx.restore();\n}\n\nfunction paintHighlight(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'highlight' },\n): void {\n if (shape.points.length === 0) return;\n ctx.save();\n // `multiply` tints pixels like a highlighter pen; engines without it fall back to alpha-blend.\n ctx.globalCompositeOperation = 'multiply';\n ctx.strokeStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.beginPath();\n tracePath(ctx, shape.points);\n ctx.stroke();\n ctx.restore();\n}\n","/**\n * Redact state and mutators. Mirrors the annotate plugin's vocabulary\n * (id + kind, monotonic mint, replace/delete) so the selection layer\n * can be reused.\n */\n\nimport type { Rect } from '../../geometry/rect.js';\n\n/** `pixelate`, `blur`, or `solid` (flat fill). */\nexport type RedactMode = 'pixelate' | 'blur' | 'solid';\n\nexport const REDACT_MODES: readonly RedactMode[] = ['pixelate', 'blur', 'solid'];\n\nexport interface RedactRegion {\n /** Stable per-session id; survives undo/redo. */\n readonly id: string;\n /** Image-space rectangle. May be temporarily negative-extent mid-drag. */\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n readonly mode: RedactMode;\n /** Used only when `mode === 'solid'`. CSS hex string. */\n readonly color: string;\n}\n\nexport interface RedactState {\n readonly regions: ReadonlyArray<RedactRegion>;\n /** Monotonic id source for new regions. Never decreases. */\n readonly nextRegionNumber: number;\n /** The currently-selected region id, or `null` when none. */\n readonly selectedId: string | null;\n /** The mode new regions are created with. Persists across selections. */\n readonly currentMode: RedactMode;\n /** Default fill colour for the `solid` mode. */\n readonly currentColor: string;\n /** Image-space dimensions of the upstream-baked source the plugin was mounted on. */\n readonly imageSize: { readonly width: number; readonly height: number };\n}\n\nexport const DEFAULT_REDACT_COLOR = '#000000';\nexport const DEFAULT_REDACT_MODE: RedactMode = 'pixelate';\n\nexport interface InitialRedactStateInput {\n readonly imageSize: { readonly width: number; readonly height: number };\n}\n\nexport function initialRedactState(input: InitialRedactStateInput): RedactState {\n return {\n regions: [],\n nextRegionNumber: 1,\n selectedId: null,\n currentMode: DEFAULT_REDACT_MODE,\n currentColor: DEFAULT_REDACT_COLOR,\n imageSize: input.imageSize,\n };\n}\n\n/** Allocate a new region id; caller threads `nextRegionNumber` back into state. */\nexport function mintRegionId(state: RedactState): {\n id: string;\n nextRegionNumber: number;\n} {\n return {\n id: `r_${state.nextRegionNumber.toString(36)}`,\n nextRegionNumber: state.nextRegionNumber + 1,\n };\n}\n\nexport function addRegion(state: RedactState, region: RedactRegion): RedactState {\n return {\n ...state,\n regions: [...state.regions, region],\n selectedId: region.id,\n };\n}\n\nexport function replaceRegion(state: RedactState, region: RedactRegion): RedactState {\n let changed = false;\n const next = state.regions.map((existing) => {\n if (existing.id !== region.id) return existing;\n changed = true;\n return region;\n });\n if (!changed) return state;\n return { ...state, regions: next };\n}\n\nexport function deleteRegion(state: RedactState, id: string): RedactState {\n const next = state.regions.filter((region) => region.id !== id);\n if (next.length === state.regions.length) return state;\n return {\n ...state,\n regions: next,\n selectedId: state.selectedId === id ? null : state.selectedId,\n };\n}\n\n/** Mirror every region across an axis of `dims`. */\nexport function mirrorRegions(\n state: RedactState,\n axis: 'horizontal' | 'vertical',\n dims: { readonly width: number; readonly height: number },\n): RedactState {\n if (state.regions.length === 0) return state;\n const next = state.regions.map((region) => {\n if (axis === 'horizontal') {\n return { ...region, x: dims.width - region.x - region.width };\n }\n return { ...region, y: dims.height - region.y - region.height };\n });\n return { ...state, regions: next, imageSize: dims };\n}\n\n/** Translate every region by `(dx, dy)`. Out-of-bounds regions are kept (bake clips on Save). */\nexport function translateRegions(\n state: RedactState,\n dx: number,\n dy: number,\n dims: { readonly width: number; readonly height: number },\n): RedactState {\n if (state.regions.length === 0) return { ...state, imageSize: dims };\n if (dx === 0 && dy === 0 && state.imageSize === dims) return state;\n const next = state.regions.map((region) => ({\n ...region,\n x: region.x + dx,\n y: region.y + dy,\n }));\n return { ...state, regions: next, imageSize: dims };\n}\n\n/**\n * Rotate every region `turns × 90°` CW around the image centre. Caller\n * passes post-rotation dims as `newDims`; pre-rotation dims come from\n * `state.imageSize`.\n */\nexport function rotateRegions(\n state: RedactState,\n turns: 0 | 1 | 2 | 3,\n newDims: { readonly width: number; readonly height: number },\n): RedactState {\n if (turns === 0) return { ...state, imageSize: newDims };\n if (state.regions.length === 0) return { ...state, imageSize: newDims };\n const oldW = state.imageSize.width;\n const oldH = state.imageSize.height;\n const next = state.regions.map((region) => {\n const { x, y, width, height } = region;\n if (turns === 1) {\n return { ...region, x: oldH - y - height, y: x, width: height, height: width };\n }\n if (turns === 2) {\n return {\n ...region,\n x: oldW - x - width,\n y: oldH - y - height,\n };\n }\n return { ...region, x: y, y: oldW - x - width, width: height, height: width };\n });\n return { ...state, regions: next, imageSize: newDims };\n}\n\nexport function selectRegion(state: RedactState, id: string | null): RedactState {\n if (state.selectedId === id) return state;\n return { ...state, selectedId: id };\n}\n\nexport function setCurrentMode(state: RedactState, mode: RedactMode): RedactState {\n if (state.currentMode === mode) return state;\n return { ...state, currentMode: mode };\n}\n\nexport function setCurrentColor(state: RedactState, color: string): RedactState {\n if (state.currentColor === color) return state;\n return { ...state, currentColor: color };\n}\n\n/** Update the mode of a region; `color` is preserved across mode flips. */\nexport function setRegionMode(state: RedactState, id: string, mode: RedactMode): RedactState {\n const region = state.regions.find((r) => r.id === id);\n if (!region) return state;\n if (region.mode === mode) return state;\n return replaceRegion(state, { ...region, mode });\n}\n\nexport function setRegionColor(state: RedactState, id: string, color: string): RedactState {\n const region = state.regions.find((r) => r.id === id);\n if (!region) return state;\n if (region.color === color) return state;\n return replaceRegion(state, { ...region, color });\n}\n\nexport function findRegion(state: RedactState, id: string | null): RedactRegion | undefined {\n if (id === null) return undefined;\n return state.regions.find((r) => r.id === id);\n}\n\nexport function selectedRegionOf(state: RedactState): RedactRegion | null {\n if (state.selectedId === null) return null;\n return state.regions.find((r) => r.id === state.selectedId) ?? null;\n}\n\n/** Normalise a region's rect so `width` / `height` are non-negative. */\nexport function normaliseRegionExtent(extent: {\n x: number;\n y: number;\n width: number;\n height: number;\n}): { x: number; y: number; width: number; height: number } {\n let { x, y, width, height } = extent;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n return { x, y, width, height };\n}\n\n/** Default-sized region centred on the image. Used by the keyboard \"Insert\" path. */\nexport interface CreateCenteredRegionContext {\n readonly imageSize: { readonly width: number; readonly height: number };\n readonly mode: RedactMode;\n readonly color: string;\n readonly id: string;\n}\n\nexport function createCenteredRegion(ctx: CreateCenteredRegionContext): RedactRegion {\n const { imageSize, mode, color, id } = ctx;\n const shortEdge = Math.min(imageSize.width, imageSize.height);\n const size = Math.max(80, Math.round(shortEdge * 0.25));\n const cx = imageSize.width / 2;\n const cy = imageSize.height / 2;\n const x = Math.round(cx - size / 2);\n const y = Math.round(cy - size / 2);\n return {\n id,\n x,\n y,\n width: size,\n height: size,\n mode,\n color,\n };\n}\n\n/** Clamp regions against new bounds; drop regions fully outside. \"Clamp, don't reset\". */\nexport function revalidateAgainstBounds(\n state: RedactState,\n bounds: { width: number; height: number },\n): RedactState {\n if (state.imageSize.width === bounds.width && state.imageSize.height === bounds.height) {\n return state;\n }\n const kept: RedactRegion[] = [];\n for (const region of state.regions) {\n if (\n region.x + region.width <= 0 ||\n region.y + region.height <= 0 ||\n region.x >= bounds.width ||\n region.y >= bounds.height\n ) {\n continue;\n }\n kept.push(clampRegion(region, bounds));\n }\n const selectedDropped = state.selectedId !== null && !kept.some((r) => r.id === state.selectedId);\n return {\n ...state,\n regions: kept,\n imageSize: { width: bounds.width, height: bounds.height },\n selectedId: selectedDropped ? null : state.selectedId,\n };\n}\n\nfunction clampRegion(\n region: RedactRegion,\n bounds: { width: number; height: number },\n): RedactRegion {\n const x = Math.max(0, region.x);\n const y = Math.max(0, region.y);\n const right = Math.min(bounds.width, region.x + region.width);\n const bottom = Math.min(bounds.height, region.y + region.height);\n return {\n ...region,\n x,\n y,\n width: Math.max(0, right - x),\n height: Math.max(0, bottom - y),\n };\n}\n\n/** Bounding-box shape used by the selection layer; matches `Rect`. */\nexport function regionBoundingBox(region: RedactRegion): Rect {\n return { x: region.x, y: region.y, width: region.width, height: region.height };\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport type { RedactRegion } from './state.js';\n\nexport interface RedactBakeInput {\n readonly regions: ReadonlyArray<RedactRegion>;\n}\n\n/** Paint every redaction region onto a copy of `source` in creation order. */\nexport async function bakeRedact(\n state: RedactBakeInput,\n source: SourceImage,\n): Promise<SourceImage> {\n if (state.regions.length === 0) return source;\n\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n // Pixelate/blur read from the post-source canvas, so overlapping\n // earlier regions are redacted again — \"redact wins\".\n for (const region of state.regions) {\n paintRegion(ctx, bake.canvas, region, source);\n }\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n\n/**\n * Paint a single redaction region. `canvas` is needed because pixelate\n * and blur read from it and redraw a transformed copy in place.\n */\nexport function paintRegion(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n canvas: HTMLCanvasElement | OffscreenCanvas,\n region: RedactRegion,\n source: SourceImage,\n): void {\n // Degenerate rects would crash `getImageData` on some engines.\n const w = Math.round(region.width);\n const h = Math.round(region.height);\n if (w < 1 || h < 1) return;\n const x = Math.round(region.x);\n const y = Math.round(region.y);\n\n switch (region.mode) {\n case 'solid':\n paintSolid(ctx, region, x, y, w, h);\n return;\n case 'pixelate':\n paintPixelate(ctx, canvas, source, x, y, w, h);\n return;\n case 'blur':\n paintBlur(ctx, canvas, source, x, y, w, h);\n return;\n }\n}\n\nfunction paintSolid(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n region: RedactRegion,\n x: number,\n y: number,\n w: number,\n h: number,\n): void {\n ctx.save();\n ctx.fillStyle = region.color;\n ctx.fillRect(x, y, w, h);\n ctx.restore();\n}\n\n/**\n * Pixelate by downsampling to a small grid then upsampling with\n * nearest-neighbour. Capped at 8 cells on the longer side; floored at\n * 4 so tiny regions still read as chunky rather than as anti-aliasing.\n */\nfunction paintPixelate(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n canvas: HTMLCanvasElement | OffscreenCanvas,\n _source: SourceImage,\n x: number,\n y: number,\n w: number,\n h: number,\n): void {\n const longer = Math.max(w, h);\n const cells = Math.max(4, Math.round(8 * Math.min(1, longer / 240)));\n const gridW = Math.max(1, Math.round((w / longer) * cells));\n const gridH = Math.max(1, Math.round((h / longer) * cells));\n\n ctx.save();\n const small = createSmallCanvas(gridW, gridH);\n if (!small) {\n ctx.restore();\n return;\n }\n small.ctx.imageSmoothingEnabled = true;\n small.ctx.imageSmoothingQuality = 'low';\n small.ctx.drawImage(canvas, x, y, w, h, 0, 0, gridW, gridH);\n\n ctx.imageSmoothingEnabled = false;\n ctx.drawImage(small.canvas, 0, 0, gridW, gridH, x, y, w, h);\n ctx.restore();\n}\n\n/** Downscale-and-back blur. Two passes (1/8 then 1/2) approximate a Gaussian. */\nfunction paintBlur(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n canvas: HTMLCanvasElement | OffscreenCanvas,\n _source: SourceImage,\n x: number,\n y: number,\n w: number,\n h: number,\n): void {\n const downscale = 1 / 8;\n const smallW = Math.max(1, Math.round(w * downscale));\n const smallH = Math.max(1, Math.round(h * downscale));\n\n const small = createSmallCanvas(smallW, smallH);\n if (!small) return;\n small.ctx.imageSmoothingEnabled = true;\n small.ctx.imageSmoothingQuality = 'high';\n small.ctx.drawImage(canvas, x, y, w, h, 0, 0, smallW, smallH);\n\n const tinyW = Math.max(1, Math.round(smallW * 0.5));\n const tinyH = Math.max(1, Math.round(smallH * 0.5));\n const tiny = createSmallCanvas(tinyW, tinyH);\n if (!tiny) {\n ctx.save();\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(small.canvas, 0, 0, smallW, smallH, x, y, w, h);\n ctx.restore();\n return;\n }\n tiny.ctx.imageSmoothingEnabled = true;\n tiny.ctx.imageSmoothingQuality = 'high';\n tiny.ctx.drawImage(small.canvas, 0, 0, smallW, smallH, 0, 0, tinyW, tinyH);\n\n ctx.save();\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(tiny.canvas, 0, 0, tinyW, tinyH, x, y, w, h);\n ctx.restore();\n}\n\ninterface SmallCanvas {\n readonly canvas: HTMLCanvasElement | OffscreenCanvas;\n readonly ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n}\n\n/** Small intermediate canvas; prefers OffscreenCanvas, falls back to detached `<canvas>`. */\nfunction createSmallCanvas(width: number, height: number): SmallCanvas | null {\n if (typeof OffscreenCanvas !== 'undefined') {\n try {\n const offscreen = new OffscreenCanvas(width, height);\n const ctx = offscreen.getContext('2d');\n if (ctx) return { canvas: offscreen, ctx };\n } catch {\n // Some engines throw on zero-size construction.\n }\n }\n if (typeof document === 'undefined') return null;\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n return { canvas, ctx };\n}\n","/**\n * Frame preset ids match Ghost's `frameOptions` identifiers directly so\n * a contract change is a state rename, not a translation table.\n */\nexport type FramePresetId =\n | 'none'\n | 'solidSharp'\n | 'solidRound'\n | 'lineSingle'\n | 'hook'\n | 'polaroid';\n\nexport const FRAME_PRESET_IDS: readonly FramePresetId[] = [\n 'none',\n 'solidSharp',\n 'solidRound',\n 'lineSingle',\n 'hook',\n 'polaroid',\n];\n\nexport interface FramePreset {\n readonly id: FramePresetId;\n /** UI label. The Ghost adapter overrides these from `frameOptions[i][1]`. */\n readonly label: string;\n /** Whether the preset's colour can be customised by the user. */\n readonly acceptsColor: boolean;\n /** Default colour when `acceptsColor`. Polaroid defaults to white. */\n readonly defaultColor: string;\n}\n\n/** Six presets in Ghost's `frameOptions` order. Labels are English defaults; adapter localises. */\nexport const FRAME_PRESETS: readonly FramePreset[] = [\n {\n id: 'none',\n label: 'None',\n acceptsColor: false,\n defaultColor: '#000000',\n },\n {\n id: 'solidSharp',\n label: 'Mat Sharp',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'solidRound',\n label: 'Mat Round',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'lineSingle',\n label: 'Line Single',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'hook',\n label: 'Corner Hooks',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'polaroid',\n label: 'Polaroid',\n acceptsColor: true,\n defaultColor: '#ffffff',\n },\n];\n\nexport interface FrameState {\n readonly presetId: FramePresetId;\n /** CSS hex colour. Used by every preset whose `acceptsColor` is true. */\n readonly color: string;\n}\n\nexport const DEFAULT_FRAME_STATE: FrameState = {\n presetId: 'none',\n color: '#000000',\n};\n\nexport function initialFrameState(): FrameState {\n return DEFAULT_FRAME_STATE;\n}\n\nexport function isFrameNoOp(state: FrameState): boolean {\n return state.presetId === 'none';\n}\n\nexport function setFramePreset(state: FrameState, presetId: FramePresetId): FrameState {\n if (state.presetId === presetId) return state;\n // Reset colour to the new preset's default iff the current colour\n // matches the previous preset's default (i.e. user hadn't customised it).\n const currentPreset = findFramePreset(state.presetId);\n const nextPreset = findFramePreset(presetId);\n if (!nextPreset) return { ...state, presetId };\n const wasOnDefault = currentPreset !== undefined && state.color === currentPreset.defaultColor;\n const nextColor = wasOnDefault ? nextPreset.defaultColor : state.color;\n return { presetId, color: nextColor };\n}\n\nexport function setFrameColor(state: FrameState, color: string): FrameState {\n if (state.color === color) return state;\n return { ...state, color };\n}\n\nexport function findFramePreset(id: FramePresetId): FramePreset | undefined {\n return FRAME_PRESETS.find((p) => p.id === id);\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { type FramePresetId, type FrameState, isFrameNoOp } from './state.js';\n\n/**\n * Apply the active frame preset. Frame thickness scales with `source`\n * so it stays consistent across resize choices. Output dimensions match\n * the input except for Polaroid, which extends the canvas.\n */\nexport async function bakeFrame(state: FrameState, source: SourceImage): Promise<SourceImage> {\n if (isFrameNoOp(state)) return source;\n\n if (state.presetId === 'polaroid') {\n return bakePolaroid(state.color, source);\n }\n if (state.presetId === 'none') return source;\n return bakeInsideFrame(state.presetId, state.color, source);\n}\n\nfunction bakeInsideFrame(\n presetId: Exclude<FramePresetId, 'none' | 'polaroid'>,\n color: string,\n source: SourceImage,\n): SourceImage {\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n paintInsideFrame(ctx, presetId, color, source.width, source.height);\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n\n/** Paint a non-extending frame; caller has already drawn the source image. */\nexport function paintInsideFrame(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n presetId: Exclude<FramePresetId, 'none' | 'polaroid'>,\n color: string,\n width: number,\n height: number,\n): void {\n switch (presetId) {\n case 'solidSharp':\n paintMatSharp(ctx, color, width, height);\n return;\n case 'solidRound':\n paintMatRound(ctx, color, width, height);\n return;\n case 'lineSingle':\n paintLineSingle(ctx, color, width, height);\n return;\n case 'hook':\n paintCornerHooks(ctx, color, width, height);\n return;\n }\n}\n\n/** Mat Sharp: 4%-of-shorter-edge solid border with sharp corners. */\nfunction paintMatSharp(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const t = matThickness(width, height);\n ctx.save();\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, width, t);\n ctx.fillRect(0, height - t, width, t);\n ctx.fillRect(0, t, t, height - 2 * t);\n ctx.fillRect(width - t, t, t, height - 2 * t);\n ctx.restore();\n}\n\n/** Mat Round: Mat Sharp with the four outer corners knocked out via destination-out. */\nfunction paintMatRound(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const t = matThickness(width, height);\n const r = t;\n paintMatSharp(ctx, color, width, height);\n\n ctx.save();\n ctx.globalCompositeOperation = 'destination-out';\n ctx.fillStyle = '#000';\n drawCornerCutout(ctx, 0, 0, r, 'tl');\n drawCornerCutout(ctx, width, 0, r, 'tr');\n drawCornerCutout(ctx, 0, height, r, 'bl');\n drawCornerCutout(ctx, width, height, r, 'br');\n ctx.restore();\n}\n\nfunction drawCornerCutout(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n cx: number,\n cy: number,\n r: number,\n corner: 'tl' | 'tr' | 'bl' | 'br',\n): void {\n ctx.beginPath();\n ctx.moveTo(cx, cy);\n switch (corner) {\n case 'tl':\n ctx.lineTo(cx + r, cy);\n ctx.arc(cx + r, cy + r, r, -Math.PI / 2, Math.PI, true);\n ctx.lineTo(cx, cy);\n break;\n case 'tr':\n ctx.lineTo(cx, cy + r);\n ctx.arc(cx - r, cy + r, r, 0, -Math.PI / 2, true);\n ctx.lineTo(cx, cy);\n break;\n case 'bl':\n ctx.lineTo(cx + r, cy);\n ctx.arc(cx + r, cy - r, r, Math.PI / 2, Math.PI, false);\n ctx.lineTo(cx, cy);\n break;\n case 'br':\n ctx.lineTo(cx - r, cy);\n ctx.arc(cx - r, cy - r, r, Math.PI / 2, 0, true);\n ctx.lineTo(cx, cy);\n break;\n }\n ctx.closePath();\n ctx.fill();\n}\n\n/** Line Single: thin stroked rect inset 5% from the edge. */\nfunction paintLineSingle(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const inset = Math.round(Math.min(width, height) * 0.05);\n const stroke = Math.max(2, Math.round(Math.min(width, height) * 0.01));\n ctx.save();\n ctx.strokeStyle = color;\n ctx.lineWidth = stroke;\n // strokeRect centres on the path; offset by half-stroke to fit inside the inset.\n const half = stroke / 2;\n ctx.strokeRect(\n inset + half,\n inset + half,\n width - 2 * inset - stroke,\n height - 2 * inset - stroke,\n );\n ctx.restore();\n}\n\n/** Corner Hooks: four L-shapes at the corners. */\nfunction paintCornerHooks(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const arm = Math.round(Math.min(width, height) * 0.08);\n const stroke = Math.max(2, Math.round(Math.min(width, height) * 0.01));\n const inset = Math.round(Math.min(width, height) * 0.05);\n\n ctx.save();\n ctx.strokeStyle = color;\n ctx.lineWidth = stroke;\n ctx.lineCap = 'square';\n\n const half = stroke / 2;\n const drawHook = (vx: number, vy: number, horizDir: -1 | 1, vertDir: -1 | 1): void => {\n ctx.beginPath();\n ctx.moveTo(vx + horizDir * arm, vy);\n ctx.lineTo(vx, vy);\n ctx.lineTo(vx, vy + vertDir * arm);\n ctx.stroke();\n };\n\n drawHook(inset + half, inset + half, 1, 1);\n drawHook(width - inset - half, inset + half, -1, 1);\n drawHook(inset + half, height - inset - half, 1, -1);\n drawHook(width - inset - half, height - inset - half, -1, -1);\n ctx.restore();\n}\n\n/** Polaroid: 5% top/left/right + 18% bottom border; output canvas is larger than input. */\nfunction bakePolaroid(color: string, source: SourceImage): SourceImage {\n const shorter = Math.min(source.width, source.height);\n const top = Math.round(shorter * 0.05);\n const left = top;\n const right = top;\n const bottom = Math.round(shorter * 0.18);\n\n const outW = source.width + left + right;\n const outH = source.height + top + bottom;\n\n const bake = createBakeCanvas(outW, outH);\n const ctx = getBakeContext2D(bake);\n\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, outW, outH);\n ctx.drawImage(source.bitmap, left, top, source.width, source.height);\n\n return {\n bitmap: bake.canvas,\n width: outW,\n height: outH,\n mimeType: source.mimeType,\n };\n}\n\n/** 4% of the shorter dimension, floored at 4px. */\nfunction matThickness(width: number, height: number): number {\n return Math.max(4, Math.round(Math.min(width, height) * 0.04));\n}\n\n/** Output dimensions for a preset; equals input except for Polaroid. */\nexport function frameOutputSize(\n presetId: FramePresetId,\n inputWidth: number,\n inputHeight: number,\n): { width: number; height: number } {\n if (presetId !== 'polaroid') {\n return { width: inputWidth, height: inputHeight };\n }\n const shorter = Math.min(inputWidth, inputHeight);\n const top = Math.round(shorter * 0.05);\n const bottom = Math.round(shorter * 0.18);\n return {\n width: inputWidth + 2 * top,\n height: inputHeight + top + bottom,\n };\n}\n","import {\n type CornerHandle,\n type EdgeHandle,\n type Rect,\n type Viewport,\n rectImageToDisplay,\n} from '@magicpages/kalotyp-core';\n\n/**\n * Position corner anchors, edge handles, and body hit-area to match the crop rect.\n *\n * Corner buttons aren't positioned directly: they live inside an anchor that sits at the\n * corner point, and host CSS (Ghost's `pintura.css` or our `crop.css`) shifts the button\n * by `-20px` so its 20×20 bracket pseudo-element aligns with the corner. Edges are inset\n * by 12px on each end so they don't overlap the corner brackets.\n */\nexport interface PositionHandlesInput {\n readonly cropRectImage: Rect;\n readonly viewport: Viewport;\n readonly cornerAnchors: Readonly<Record<CornerHandle, HTMLElement>>;\n readonly edgeHandles: Readonly<Record<EdgeHandle, HTMLElement>>;\n readonly bodyHitArea: HTMLElement;\n}\n\nconst CORNER_INSET = 12;\n\nexport function positionHandles(input: PositionHandlesInput): void {\n const display = rectImageToDisplay(input.cropRectImage, input.viewport);\n const { cornerAnchors, edgeHandles, bodyHitArea } = input;\n\n bodyHitArea.style.left = `${display.x}px`;\n bodyHitArea.style.top = `${display.y}px`;\n bodyHitArea.style.width = `${display.width}px`;\n bodyHitArea.style.height = `${display.height}px`;\n\n setAnchor(cornerAnchors.tl, display.x, display.y);\n setAnchor(cornerAnchors.tr, display.x + display.width, display.y);\n setAnchor(cornerAnchors.bl, display.x, display.y + display.height);\n setAnchor(cornerAnchors.br, display.x + display.width, display.y + display.height);\n\n const horizontalLength = Math.max(0, display.width - CORNER_INSET * 2);\n const verticalLength = Math.max(0, display.height - CORNER_INSET * 2);\n\n setHorizontalEdge(edgeHandles.t, display.x + CORNER_INSET, display.y, horizontalLength);\n setHorizontalEdge(\n edgeHandles.b,\n display.x + CORNER_INSET,\n display.y + display.height,\n horizontalLength,\n );\n setVerticalEdge(edgeHandles.l, display.x, display.y + CORNER_INSET, verticalLength);\n setVerticalEdge(\n edgeHandles.r,\n display.x + display.width,\n display.y + CORNER_INSET,\n verticalLength,\n );\n}\n\nfunction setAnchor(anchor: HTMLElement, x: number, y: number): void {\n anchor.style.left = `${x}px`;\n anchor.style.top = `${y}px`;\n}\n\nfunction setHorizontalEdge(handle: HTMLElement, x: number, y: number, length: number): void {\n handle.style.left = `${x}px`;\n handle.style.top = `${y}px`;\n handle.style.width = `${length}px`;\n}\n\nfunction setVerticalEdge(handle: HTMLElement, x: number, y: number, length: number): void {\n handle.style.left = `${x}px`;\n handle.style.top = `${y}px`;\n handle.style.height = `${length}px`;\n}\n","import type { Viewport } from '@magicpages/kalotyp-core';\n\n/** Paint the source image onto the canvas at the viewport's display size, DPR-scaled. */\nexport function renderImageCanvas(\n canvas: HTMLCanvasElement,\n source: CanvasImageSource,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const targetW = Math.max(1, Math.round(stageWidth * dpr));\n const targetH = Math.max(1, Math.round(stageHeight * dpr));\n if (canvas.width !== targetW) canvas.width = targetW;\n if (canvas.height !== targetH) canvas.height = targetH;\n canvas.style.width = `${stageWidth}px`;\n canvas.style.height = `${stageHeight}px`;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, stageWidth, stageHeight);\n ctx.drawImage(\n source,\n viewport.displayRect.x,\n viewport.displayRect.y,\n viewport.displayRect.width,\n viewport.displayRect.height,\n );\n}\n","import { type Rect, type Viewport, rectImageToDisplay } from '@magicpages/kalotyp-core';\n\nconst MASK_FILL = 'rgba(0, 0, 0, 0.4)';\n// Halo pattern (wide soft-black under, 1px white over) stays readable on any background\n// without `mix-blend-mode: difference`, which has known Safari bugs over canvas.\nconst OUTLINE_HALO = 'rgba(0, 0, 0, 0.45)';\nconst OUTLINE_HALO_WIDTH = 3;\nconst OUTLINE_STROKE = 'rgba(255, 255, 255, 0.95)';\nconst OUTLINE_WIDTH = 1;\nconst GRID_HALO = 'rgba(0, 0, 0, 0.25)';\nconst GRID_HALO_WIDTH = 2;\nconst GRID_STROKE = 'rgba(255, 255, 255, 0.55)';\nconst GRID_WIDTH = 1;\n\n/** Repaint the overlay: dim mask outside the crop rect, outline, and rule-of-thirds grid. */\nexport function renderOverlayCanvas(\n canvas: HTMLCanvasElement,\n cropRectImage: Rect,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const targetW = Math.max(1, Math.round(stageWidth * dpr));\n const targetH = Math.max(1, Math.round(stageHeight * dpr));\n if (canvas.width !== targetW) canvas.width = targetW;\n if (canvas.height !== targetH) canvas.height = targetH;\n canvas.style.width = `${stageWidth}px`;\n canvas.style.height = `${stageHeight}px`;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, stageWidth, stageHeight);\n\n const display = rectImageToDisplay(cropRectImage, viewport);\n const imageRect = viewport.displayRect;\n\n // Mask is scoped to the image's display rect, not the whole stage — the editor mat\n // outside the image stays clean.\n ctx.save();\n ctx.fillStyle = MASK_FILL;\n ctx.fillRect(imageRect.x, imageRect.y, imageRect.width, imageRect.height);\n ctx.globalCompositeOperation = 'destination-out';\n ctx.fillRect(display.x, display.y, display.width, display.height);\n ctx.restore();\n\n const x = display.x + 0.5;\n const y = display.y + 0.5;\n const w = display.width - 1;\n const h = display.height - 1;\n ctx.strokeStyle = OUTLINE_HALO;\n ctx.lineWidth = OUTLINE_HALO_WIDTH;\n ctx.strokeRect(x, y, w, h);\n ctx.strokeStyle = OUTLINE_STROKE;\n ctx.lineWidth = OUTLINE_WIDTH;\n ctx.strokeRect(x, y, w, h);\n\n drawGridLines(ctx, display, GRID_HALO, GRID_HALO_WIDTH);\n drawGridLines(ctx, display, GRID_STROKE, GRID_WIDTH);\n // Corner visuals are owned by DOM buttons (see build-stage.ts); don't double-draw here.\n}\n\nfunction drawGridLines(\n ctx: CanvasRenderingContext2D,\n display: Rect,\n stroke: string,\n width: number,\n): void {\n ctx.strokeStyle = stroke;\n ctx.lineWidth = width;\n ctx.beginPath();\n for (let i = 1; i < 3; i++) {\n const x = display.x + (display.width * i) / 3;\n const y = display.y + (display.height * i) / 3;\n ctx.moveTo(x, display.y);\n ctx.lineTo(x, display.y + display.height);\n ctx.moveTo(display.x, y);\n ctx.lineTo(display.x + display.width, y);\n }\n ctx.stroke();\n}\n","import type { CropPreset } from '@magicpages/kalotyp-core';\n\nexport interface PresetRowElements {\n readonly container: HTMLDivElement;\n readonly buttons: readonly HTMLButtonElement[];\n}\n\n/** Build the aspect-ratio preset row. `activeIndex` indexes into the visible presets passed in. */\nexport function buildPresetRow(\n visiblePresets: readonly CropPreset[],\n activeIndex: number,\n onSelect: (visibleIndex: number, preset: CropPreset) => void,\n): PresetRowElements {\n const container = document.createElement('div');\n container.className = 'kalotyp-preset-row';\n container.setAttribute('role', 'radiogroup');\n container.setAttribute('aria-label', 'Crop aspect ratio');\n\n const buttons: HTMLButtonElement[] = [];\n visiblePresets.forEach((preset, index) => {\n const [, label] = preset;\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-preset-button';\n button.textContent = label;\n button.setAttribute('role', 'radio');\n button.setAttribute('aria-checked', index === activeIndex ? 'true' : 'false');\n button.dataset.presetIndex = String(index);\n button.addEventListener('click', () => onSelect(index, preset));\n container.appendChild(button);\n buttons.push(button);\n });\n\n return { container, buttons };\n}\n\n/** Update the active state of an existing preset row in place. */\nexport function setActivePresetButton(\n buttons: readonly HTMLButtonElement[],\n activeIndex: number,\n): void {\n buttons.forEach((button, index) => {\n button.setAttribute('aria-checked', index === activeIndex ? 'true' : 'false');\n });\n}\n","import type { CornerHandle, EdgeHandle, HandleDirection } from '@magicpages/kalotyp-core';\n\nexport interface StageElements {\n readonly container: HTMLDivElement;\n readonly imageCanvas: HTMLCanvasElement;\n readonly overlayCanvas: HTMLCanvasElement;\n readonly handlesLayer: HTMLDivElement;\n readonly handles: Readonly<Record<HandleDirection, HTMLButtonElement>>;\n /**\n * Per-corner wrapper providing the positioning context Ghost's pintura.css expects:\n * `[data-direction=tr|bl|br] { left/top: -20px !important }` resolves against this\n * anchor, not the stage. Without it three corner buttons end up off-screen.\n */\n readonly cornerAnchors: Readonly<Record<CornerHandle, HTMLDivElement>>;\n /** Body drag surface — under handles, above canvases in z-order. */\n readonly bodyHitArea: HTMLDivElement;\n}\n\nconst CORNERS: readonly CornerHandle[] = ['tl', 'tr', 'bl', 'br'];\nconst EDGES: readonly EdgeHandle[] = ['t', 'r', 'b', 'l'];\n\n/** Build the interactive crop UI inside the stage. Class names and data-* come from the Ghost contract. */\nexport function buildStageElements(): StageElements {\n const container = document.createElement('div');\n container.className = 'kalotyp-stage-container';\n\n const imageCanvas = document.createElement('canvas');\n imageCanvas.className = 'kalotyp-stage-image';\n imageCanvas.setAttribute('aria-hidden', 'true');\n\n const overlayCanvas = document.createElement('canvas');\n overlayCanvas.className = 'kalotyp-stage-overlay';\n overlayCanvas.setAttribute('aria-hidden', 'true');\n\n const bodyHitArea = document.createElement('div');\n bodyHitArea.className = 'kalotyp-stage-body';\n\n const handlesLayer = document.createElement('div');\n handlesLayer.className = 'kalotyp-handles';\n handlesLayer.setAttribute('role', 'group');\n handlesLayer.setAttribute('aria-label', 'Crop region');\n\n const handles = {} as Record<HandleDirection, HTMLButtonElement>;\n for (const direction of EDGES) {\n const button = createEdgeButton(direction);\n handles[direction] = button;\n handlesLayer.appendChild(button);\n }\n\n const cornerAnchors = {} as Record<CornerHandle, HTMLDivElement>;\n for (const direction of CORNERS) {\n const anchor = document.createElement('div');\n anchor.className = 'kalotyp-corner-anchor';\n anchor.dataset.direction = direction;\n const button = createCornerButton(direction);\n anchor.appendChild(button);\n handles[direction] = button;\n cornerAnchors[direction] = anchor;\n handlesLayer.appendChild(anchor);\n }\n\n container.appendChild(imageCanvas);\n container.appendChild(overlayCanvas);\n container.appendChild(bodyHitArea);\n container.appendChild(handlesLayer);\n\n return {\n container,\n imageCanvas,\n overlayCanvas,\n handlesLayer,\n handles,\n cornerAnchors,\n bodyHitArea,\n };\n}\n\nfunction createCornerButton(direction: CornerHandle): HTMLButtonElement {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-handle';\n button.dataset.shape = 'circle';\n button.dataset.direction = direction;\n button.setAttribute('aria-label', labelFor(direction));\n button.tabIndex = 0;\n return button;\n}\n\nfunction createEdgeButton(direction: EdgeHandle): HTMLButtonElement {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-handle';\n button.dataset.shape = 'edge';\n button.dataset.direction = direction;\n button.setAttribute('aria-label', labelFor(direction));\n button.tabIndex = 0;\n return button;\n}\n\nfunction labelFor(direction: HandleDirection): string {\n switch (direction) {\n case 'tl':\n return 'Top-left crop handle';\n case 'tr':\n return 'Top-right crop handle';\n case 'bl':\n return 'Bottom-left crop handle';\n case 'br':\n return 'Bottom-right crop handle';\n case 't':\n return 'Top crop handle';\n case 'r':\n return 'Right crop handle';\n case 'b':\n return 'Bottom crop handle';\n case 'l':\n return 'Left crop handle';\n }\n}\n","import {\n type CropState,\n type HandleDirection,\n type Rect,\n type Store,\n type Viewport,\n pointDisplayToImage,\n resizeRectFromHandle,\n translateClampedRect,\n} from '@magicpages/kalotyp-core';\n\nexport interface CropInteractionElements {\n readonly stageElement: HTMLElement;\n readonly handles: Readonly<Record<HandleDirection, HTMLElement>>;\n readonly bodyHitArea: HTMLElement;\n}\n\nexport interface CropInteractionContext {\n getViewport(): Viewport;\n /** Called once on pointerup so editor history can snapshot. Optional for tests. */\n onCommit?: () => void;\n}\n\nexport interface CropInteractionHandle {\n destroy(): void;\n}\n\n/** Wire eight-handle resize + body translate onto the crop rect. Per-frame rAF-coalesced. */\nexport function bindCropInteractions(\n elements: CropInteractionElements,\n store: Store<CropState>,\n ctx: CropInteractionContext,\n): CropInteractionHandle {\n const cleanups: Array<() => void> = [];\n\n for (const direction of Object.keys(elements.handles) as HandleDirection[]) {\n const handle = elements.handles[direction];\n cleanups.push(attachResizeGesture(handle, direction, store, ctx));\n }\n cleanups.push(attachTranslateGesture(elements.bodyHitArea, store, ctx));\n\n return {\n destroy() {\n for (const cleanup of cleanups) cleanup();\n },\n };\n}\n\nfunction attachResizeGesture(\n element: HTMLElement,\n direction: HandleDirection,\n store: Store<CropState>,\n ctx: CropInteractionContext,\n): () => void {\n return attachPointerDrag(element, () => {\n const viewport = ctx.getViewport();\n const initial = store.get();\n const bounds: Rect = {\n x: 0,\n y: 0,\n width: initial.imageSize.width,\n height: initial.imageSize.height,\n };\n const aspectRatio = initial.aspectRatio;\n\n return {\n onMove(point) {\n const stagePoint = clientToStage(element, point.clientX, point.clientY);\n const imagePoint = pointDisplayToImage(stagePoint, viewport);\n const next = resizeRectFromHandle(initial.rect, direction, imagePoint, {\n bounds,\n ...(aspectRatio !== undefined ? { aspectRatio } : {}),\n });\n store.set({ rect: next });\n },\n onCommit() {\n ctx.onCommit?.();\n },\n onCancel() {\n store.set({ rect: initial.rect });\n },\n };\n });\n}\n\nfunction attachTranslateGesture(\n element: HTMLElement,\n store: Store<CropState>,\n ctx: CropInteractionContext,\n): () => void {\n return attachPointerDrag(element, (event) => {\n const viewport = ctx.getViewport();\n const initial = store.get();\n const bounds: Rect = {\n x: 0,\n y: 0,\n width: initial.imageSize.width,\n height: initial.imageSize.height,\n };\n const originStage = clientToStage(element, event.clientX, event.clientY);\n\n return {\n onMove(point) {\n const here = clientToStage(element, point.clientX, point.clientY);\n const dxImage = (here.x - originStage.x) / viewport.scale;\n const dyImage = (here.y - originStage.y) / viewport.scale;\n const next = translateClampedRect(initial.rect, dxImage, dyImage, bounds);\n store.set({ rect: next });\n },\n onCommit() {\n ctx.onCommit?.();\n },\n onCancel() {\n store.set({ rect: initial.rect });\n },\n };\n });\n}\n\ninterface DragHandlers {\n onMove(point: { clientX: number; clientY: number }): void;\n onCommit(): void;\n onCancel(): void;\n}\n\n/** rAF-coalesced pointer-drag attach. Factory runs on pointerdown. */\nfunction attachPointerDrag(\n element: HTMLElement,\n factory: (start: PointerEvent) => DragHandlers,\n): () => void {\n const onPointerDown = (event: PointerEvent): void => {\n if (event.button !== 0) return;\n event.preventDefault();\n event.stopPropagation();\n\n const handlers = factory(event);\n element.setPointerCapture(event.pointerId);\n\n let pendingPoint: { clientX: number; clientY: number } | undefined;\n let rafScheduled = false;\n\n const flush = (): void => {\n rafScheduled = false;\n if (!pendingPoint) return;\n const point = pendingPoint;\n pendingPoint = undefined;\n handlers.onMove(point);\n };\n\n const onPointerMove = (moveEvent: PointerEvent): void => {\n if (moveEvent.pointerId !== event.pointerId) return;\n pendingPoint = { clientX: moveEvent.clientX, clientY: moveEvent.clientY };\n if (!rafScheduled) {\n rafScheduled = true;\n requestAnimationFrame(flush);\n }\n };\n\n const finish = (committed: boolean): void => {\n element.removeEventListener('pointermove', onPointerMove);\n element.removeEventListener('pointerup', onPointerUp);\n element.removeEventListener('pointercancel', onPointerCancel);\n try {\n element.releasePointerCapture(event.pointerId);\n } catch {\n // already released or never captured\n }\n // Drain pending frame so the final point lands.\n if (pendingPoint) {\n const final = pendingPoint;\n pendingPoint = undefined;\n handlers.onMove(final);\n }\n if (committed) handlers.onCommit();\n else handlers.onCancel();\n };\n\n const onPointerUp = (upEvent: PointerEvent): void => {\n if (upEvent.pointerId !== event.pointerId) return;\n finish(true);\n };\n const onPointerCancel = (cancelEvent: PointerEvent): void => {\n if (cancelEvent.pointerId !== event.pointerId) return;\n finish(false);\n };\n\n element.addEventListener('pointermove', onPointerMove);\n element.addEventListener('pointerup', onPointerUp);\n element.addEventListener('pointercancel', onPointerCancel);\n };\n\n element.addEventListener('pointerdown', onPointerDown);\n return () => element.removeEventListener('pointerdown', onPointerDown);\n}\n\nfunction clientToStage(\n element: HTMLElement,\n clientX: number,\n clientY: number,\n): { x: number; y: number } {\n // Walk up to the stage container so coords are stage-relative regardless of handle nesting.\n const stage = element.closest<HTMLElement>('.kalotyp-stage-container') ?? element;\n const rect = stage.getBoundingClientRect();\n return { x: clientX - rect.left, y: clientY - rect.top };\n}\n","import {\n type CropPreset,\n type CropPresetFilter,\n type CropState,\n type Rect,\n type SourceImage,\n type Store,\n type Viewport,\n type ViewportController,\n applyPresetByIndex,\n clampRectInside,\n computeViewport,\n filterPresets,\n initialCropState,\n} from '@magicpages/kalotyp-core';\nimport { positionHandles } from '../../canvas/position-handles.js';\nimport { renderImageCanvas } from '../../canvas/render-image.js';\nimport { renderOverlayCanvas } from '../../canvas/render-overlay.js';\nimport { buildPresetRow, setActivePresetButton } from '../../dom/build-preset-row.js';\nimport { buildStageElements } from '../../dom/build-stage.js';\nimport { bindCropInteractions } from './interaction.js';\n\nexport interface MountCropOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly presets: readonly CropPreset[];\n readonly presetFilter: CropPresetFilter | undefined;\n readonly store: Store<CropState>;\n /** Editor-level zoom + pan. Optional — falls back to fit-only viewports when absent. */\n readonly viewport?: ViewportController;\n readonly onCommit?: () => void;\n}\n\nexport interface MountCropHandle {\n destroy(): void;\n}\n\nconst STAGE_PADDING_PX = 32;\n\n/** Mount the crop UI. Expects the store pre-initialised via `initialCropState`. */\nexport function mountCropUtility(options: MountCropOptions): MountCropHandle {\n const {\n stageHost,\n utilHost,\n source,\n presets,\n presetFilter,\n store,\n viewport: controller,\n } = options;\n const commit = options.onCommit ?? noop;\n\n const stage = buildStageElements();\n stageHost.appendChild(stage.container);\n\n const panelContainer = document.createElement('div');\n panelContainer.className = 'kalotyp-crop-panel';\n\n const visiblePresets = filterPresets(presets, presetFilter);\n const initialActive = mapToVisibleIndex(store.get(), presets, visiblePresets);\n const presetRow = buildPresetRow(visiblePresets, initialActive, (visibleIndex, preset) => {\n const fullIndex = presets.indexOf(preset);\n if (fullIndex === -1) return;\n const next = applyPresetByIndex(store.get(), fullIndex);\n store.set({\n rect: next.rect,\n aspectRatio: next.aspectRatio,\n activePresetIndex: fullIndex,\n });\n setActivePresetButton(presetRow.buttons, visibleIndex);\n commit();\n });\n panelContainer.appendChild(presetRow.container);\n\n const dimensions = buildCropDimensionsRow({\n initial: store.get().rect,\n bounds: { width: source.width, height: source.height },\n onCommit(next) {\n const current = store.get();\n const bounds: Rect = {\n x: 0,\n y: 0,\n width: current.imageSize.width,\n height: current.imageSize.height,\n };\n const clamped = clampRectInside(next, bounds);\n store.set({ rect: clamped });\n commit();\n },\n });\n panelContainer.appendChild(dimensions.container);\n\n utilHost.appendChild(panelContainer);\n\n let viewport: Viewport = computeViewport(\n { width: 1, height: 1, padding: STAGE_PADDING_PX },\n { width: source.width, height: source.height },\n );\n\n function recomputeViewport(): void {\n const rect = stage.container.getBoundingClientRect();\n const stageDims = { width: rect.width, height: rect.height, padding: STAGE_PADDING_PX };\n const imageSize = { width: source.width, height: source.height };\n viewport = controller\n ? controller.computeViewport(stageDims, imageSize)\n : computeViewport(stageDims, imageSize);\n }\n\n function paintAll(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n renderImageCanvas(stage.imageCanvas, source.bitmap, rect.width, rect.height, viewport);\n paintOverlay();\n }\n\n function paintOverlay(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n renderOverlayCanvas(stage.overlayCanvas, store.get().rect, rect.width, rect.height, viewport);\n positionHandles({\n cropRectImage: store.get().rect,\n viewport,\n cornerAnchors: stage.cornerAnchors,\n edgeHandles: {\n t: stage.handles.t,\n r: stage.handles.r,\n b: stage.handles.b,\n l: stage.handles.l,\n },\n bodyHitArea: stage.bodyHitArea,\n });\n }\n\n recomputeViewport();\n paintAll();\n\n const resizeObserver = new ResizeObserver(() => {\n recomputeViewport();\n paintAll();\n });\n resizeObserver.observe(stage.container);\n\n // Viewport changes flow outside the store — schedule our own rAF to coalesce wheel/pinch bursts.\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n recomputeViewport();\n paintAll();\n });\n });\n\n let overlayRafScheduled = false;\n const unsubscribe = store.subscribe((next, previous) => {\n syncPresetButtons(next, previous, presets, visiblePresets, presetRow.buttons);\n if (rectsEqual(next.rect, previous.rect)) return;\n dimensions.sync(next.rect);\n if (overlayRafScheduled) return;\n overlayRafScheduled = true;\n requestAnimationFrame(() => {\n overlayRafScheduled = false;\n paintOverlay();\n });\n });\n\n const interactions = bindCropInteractions(\n {\n stageElement: stage.container,\n handles: stage.handles,\n bodyHitArea: stage.bodyHitArea,\n },\n store,\n { getViewport: () => viewport, onCommit: commit },\n );\n\n return {\n destroy() {\n interactions.destroy();\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n stage.container.remove();\n panelContainer.remove();\n },\n };\n}\n\ninterface CropDimensionsRow {\n readonly container: HTMLDivElement;\n sync(rect: Rect): void;\n}\n\ninterface BuildCropDimensionsRowOptions {\n readonly initial: Rect;\n readonly bounds: { readonly width: number; readonly height: number };\n onCommit(rect: Rect): void;\n}\n\n/** Four numeric inputs (x/y/w/h) committing on blur or Enter. Caller clamps to image bounds. */\nfunction buildCropDimensionsRow(options: BuildCropDimensionsRowOptions): CropDimensionsRow {\n const container = document.createElement('div');\n container.className = 'kalotyp-crop-dims';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Crop region dimensions');\n\n const xInput = makeNumericInput('Left', options.initial.x, 0, options.bounds.width);\n const yInput = makeNumericInput('Top', options.initial.y, 0, options.bounds.height);\n const wInput = makeNumericInput('Width', options.initial.width, 1, options.bounds.width);\n const hInput = makeNumericInput('Height', options.initial.height, 1, options.bounds.height);\n\n function readRect(): Rect {\n return {\n x: Math.round(xInput.input.valueAsNumber),\n y: Math.round(yInput.input.valueAsNumber),\n width: Math.round(wInput.input.valueAsNumber),\n height: Math.round(hInput.input.valueAsNumber),\n };\n }\n\n for (const field of [xInput, yInput, wInput, hInput]) {\n field.input.addEventListener('change', () => {\n const next = readRect();\n if (!Number.isFinite(next.x + next.y + next.width + next.height)) return;\n options.onCommit(next);\n });\n }\n\n container.appendChild(xInput.wrapper);\n container.appendChild(yInput.wrapper);\n container.appendChild(wInput.wrapper);\n container.appendChild(hInput.wrapper);\n\n function sync(rect: Rect): void {\n if (xInput.input.valueAsNumber !== rect.x) xInput.input.value = String(Math.round(rect.x));\n if (yInput.input.valueAsNumber !== rect.y) yInput.input.value = String(Math.round(rect.y));\n if (wInput.input.valueAsNumber !== rect.width)\n wInput.input.value = String(Math.round(rect.width));\n if (hInput.input.valueAsNumber !== rect.height)\n hInput.input.value = String(Math.round(rect.height));\n }\n\n return { container, sync };\n}\n\nfunction makeNumericInput(\n label: string,\n value: number,\n min: number,\n max: number,\n): { wrapper: HTMLLabelElement; input: HTMLInputElement } {\n const wrapper = document.createElement('label');\n wrapper.className = 'kalotyp-crop-dims-field';\n\n const labelSpan = document.createElement('span');\n labelSpan.className = 'kalotyp-crop-dims-label';\n labelSpan.textContent = label;\n\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'kalotyp-crop-dims-input';\n input.min = String(min);\n input.max = String(max);\n input.step = '1';\n input.value = String(Math.round(value));\n input.inputMode = 'numeric';\n input.setAttribute('aria-label', `${label} (pixels)`);\n\n wrapper.appendChild(labelSpan);\n wrapper.appendChild(input);\n return { wrapper, input };\n}\n\nexport { initialCropState };\n\nfunction noop(): void {}\n\nfunction rectsEqual(a: Rect, b: Rect): boolean {\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n}\n\nfunction mapToVisibleIndex(\n state: CropState,\n fullPresets: readonly CropPreset[],\n visiblePresets: readonly CropPreset[],\n): number {\n if (state.activePresetIndex === -1) return -1;\n const active = fullPresets[state.activePresetIndex];\n if (!active) return -1;\n return visiblePresets.indexOf(active);\n}\n\nfunction syncPresetButtons(\n next: CropState,\n previous: CropState,\n fullPresets: readonly CropPreset[],\n visiblePresets: readonly CropPreset[],\n buttons: readonly HTMLButtonElement[],\n): void {\n if (next.activePresetIndex === previous.activePresetIndex) return;\n const visibleIndex = mapToVisibleIndex(next, fullPresets, visiblePresets);\n setActivePresetButton(buttons, visibleIndex);\n}\n","import {\n type CropPreset,\n type CropPresetFilter,\n type CropState,\n type UtilityPlugin,\n bakeCrop,\n initialCropState,\n} from '@magicpages/kalotyp-core';\nimport { mountCropUtility } from './mount.js';\n\nconst UTILITY_ID = 'crop';\n\nexport interface CropPluginOptions {\n /** The 12 presets Ghost passes (or any caller-supplied subset). */\n readonly presets: readonly CropPreset[];\n /** Orientation filter (Ghost always passes 'landscape'). */\n readonly presetFilter: CropPresetFilter | undefined;\n /** Panel host (closed-over); stage host is the `mount()` argument. */\n readonly panelHost: HTMLElement;\n}\n\nexport function createCropPlugin(options: CropPluginOptions): UtilityPlugin<CropState> {\n return {\n id: 'crop',\n init(ctx) {\n return initialCropState({\n imageSize: { width: ctx.source.width, height: ctx.source.height },\n presets: options.presets,\n filter: options.presetFilter,\n });\n },\n mount(stageHost, ctx, store) {\n const handle = mountCropUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n presets: options.presets,\n presetFilter: options.presetFilter,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: UTILITY_ID }),\n });\n return { destroy: () => handle.destroy() };\n },\n async bake(state, source) {\n return bakeCrop(source, { rect: state.rect });\n },\n };\n}\n","import {\n type SourceImage,\n type Viewport,\n type ViewportController,\n computeViewport,\n} from '@magicpages/kalotyp-core';\n\nconst STAGE_PADDING_PX = 32;\n\nexport interface PreviewCanvas {\n readonly container: HTMLDivElement;\n readonly canvas: HTMLCanvasElement;\n}\n\nexport function buildPreviewCanvas(): PreviewCanvas {\n const container = document.createElement('div');\n container.className = 'kalotyp-stage-container kalotyp-preview-container';\n\n const canvas = document.createElement('canvas');\n canvas.className = 'kalotyp-stage-image kalotyp-preview-canvas';\n canvas.setAttribute('aria-hidden', 'true');\n\n container.appendChild(canvas);\n return { container, canvas };\n}\n\n/** Compute the letterboxed viewport for a preview. Returns `undefined` if the container has no laid-out size yet. */\nexport function previewViewportFor(\n container: HTMLElement,\n intrinsic: { width: number; height: number },\n controller?: ViewportController,\n): { viewport: Viewport; stageWidth: number; stageHeight: number } | undefined {\n const rect = container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return undefined;\n const stageDims = { width: rect.width, height: rect.height, padding: STAGE_PADDING_PX };\n const viewport = controller\n ? controller.computeViewport(stageDims, intrinsic)\n : computeViewport(stageDims, intrinsic);\n return { viewport, stageWidth: rect.width, stageHeight: rect.height };\n}\n\n/** Paint into a preview canvas. The callback receives a DPR-scaled context positioned at (0,0) in stage CSS pixels. */\nexport function paintPreview(\n canvas: HTMLCanvasElement,\n stageWidth: number,\n stageHeight: number,\n draw: (ctx: CanvasRenderingContext2D) => void,\n): void {\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const targetW = Math.max(1, Math.round(stageWidth * dpr));\n const targetH = Math.max(1, Math.round(stageHeight * dpr));\n if (canvas.width !== targetW) canvas.width = targetW;\n if (canvas.height !== targetH) canvas.height = targetH;\n canvas.style.width = `${stageWidth}px`;\n canvas.style.height = `${stageHeight}px`;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, stageWidth, stageHeight);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n draw(ctx);\n}\n\nexport { STAGE_PADDING_PX };\nexport type { SourceImage };\n","import {\n type FlipState,\n type SourceImage,\n type Store,\n type ViewportController,\n toggleFlip,\n} from '@magicpages/kalotyp-core';\nimport {\n buildPreviewCanvas,\n paintPreview,\n previewViewportFor,\n} from '../../canvas/preview-canvas.js';\nimport { icon } from '../../icons.js';\n\nexport interface MountFlipOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly store: Store<FlipState>;\n readonly viewport?: ViewportController;\n readonly onCommit?: () => void;\n}\n\nexport interface MountFlipHandle {\n destroy(): void;\n}\n\nexport function mountFlipUtility(options: MountFlipOptions): MountFlipHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? (() => {});\n\n const preview = buildPreviewCanvas();\n stageHost.appendChild(preview.container);\n\n const panel = buildFlipPanel({\n onToggleHorizontal: () => {\n store.set(toggleFlip(store.get(), 'horizontal'));\n commit();\n },\n onToggleVertical: () => {\n store.set(toggleFlip(store.get(), 'vertical'));\n commit();\n },\n });\n utilHost.appendChild(panel.container);\n\n function paint(): void {\n const v = previewViewportFor(\n preview.container,\n { width: source.width, height: source.height },\n controller,\n );\n if (!v) return;\n const state = store.get();\n paintPreview(preview.canvas, v.stageWidth, v.stageHeight, (ctx) => {\n const display = v.viewport.displayRect;\n const sx = state.horizontal ? -1 : 1;\n const sy = state.vertical ? -1 : 1;\n // Anchor on display centre so the image stays letterboxed in place, just mirrored.\n const cx = display.x + display.width / 2;\n const cy = display.y + display.height / 2;\n ctx.translate(cx, cy);\n ctx.scale(sx, sy);\n ctx.drawImage(\n source.bitmap,\n -display.width / 2,\n -display.height / 2,\n display.width,\n display.height,\n );\n });\n }\n\n function syncPanel(state: FlipState): void {\n panel.horizontalButton.setAttribute('aria-pressed', state.horizontal ? 'true' : 'false');\n panel.verticalButton.setAttribute('aria-pressed', state.vertical ? 'true' : 'false');\n }\n\n syncPanel(store.get());\n paint();\n\n const resizeObserver = new ResizeObserver(() => paint());\n resizeObserver.observe(preview.container);\n\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n paint();\n });\n });\n\n let rafScheduled = false;\n const unsubscribe = store.subscribe((next) => {\n syncPanel(next);\n if (rafScheduled) return;\n rafScheduled = true;\n requestAnimationFrame(() => {\n rafScheduled = false;\n paint();\n });\n });\n\n return {\n destroy() {\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n preview.container.remove();\n panel.container.remove();\n },\n };\n}\n\ninterface FlipPanelOptions {\n onToggleHorizontal(): void;\n onToggleVertical(): void;\n}\n\ninterface FlipPanel {\n container: HTMLDivElement;\n horizontalButton: HTMLButtonElement;\n verticalButton: HTMLButtonElement;\n}\n\nfunction buildFlipPanel(options: FlipPanelOptions): FlipPanel {\n const container = document.createElement('div');\n container.className = 'kalotyp-flip-panel';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Flip');\n\n const horizontalButton = createToggleButton(\n 'Flip horizontal',\n icon('flipHorizontal'),\n options.onToggleHorizontal,\n );\n horizontalButton.classList.add('kalotyp-flip-button-h');\n const verticalButton = createToggleButton(\n 'Flip vertical',\n icon('flipVertical'),\n options.onToggleVertical,\n );\n verticalButton.classList.add('kalotyp-flip-button-v');\n\n container.appendChild(horizontalButton);\n container.appendChild(verticalButton);\n\n return { container, horizontalButton, verticalButton };\n}\n\nfunction createToggleButton(\n label: string,\n iconHtml: string,\n onClick: () => void,\n): HTMLButtonElement {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-toggle-button';\n button.innerHTML = `${iconHtml}<span>${label}</span>`;\n button.setAttribute('aria-pressed', 'false');\n button.setAttribute('aria-label', label);\n button.addEventListener('click', onClick);\n return button;\n}\n","import {\n type FlipState,\n type UtilityPlugin,\n bakeFlip,\n initialFlipState,\n} from '@magicpages/kalotyp-core';\nimport { mountFlipUtility } from './mount.js';\n\nexport interface FlipPluginOptions {\n readonly panelHost: HTMLElement;\n}\n\nexport function createFlipPlugin(options: FlipPluginOptions): UtilityPlugin<FlipState> {\n return {\n id: 'flip',\n init: () => initialFlipState(),\n mount(stageHost, ctx, store) {\n const handle = mountFlipUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: 'flip' }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: bakeFlip,\n };\n}\n","import {\n FREE_ANGLE_MAX,\n FREE_ANGLE_MIN,\n FREE_ANGLE_STEP,\n type RotateState,\n type SourceImage,\n type Store,\n type ViewportController,\n computeViewport,\n effectiveAngleDeg,\n largestInscribedRect,\n rotateClockwise,\n rotateCounterClockwise,\n setFreeAngle,\n} from '@magicpages/kalotyp-core';\nimport { STAGE_PADDING_PX, buildPreviewCanvas, paintPreview } from '../../canvas/preview-canvas.js';\n\nexport interface MountRotateOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly store: Store<RotateState>;\n /** Editor-level zoom + pan controller. Optional in jsdom tests. */\n readonly viewport?: ViewportController;\n /** Fires on quarter-turn / slider `change` / number `change` / reset — one undo per drag, not per tick. */\n readonly onCommit?: () => void;\n}\n\nexport interface MountRotateHandle {\n destroy(): void;\n}\n\n// Same mask alpha as crop overlay so the \"excluded region\" cue reads consistently across plugins.\nconst MASK_FILL = 'rgba(0, 0, 0, 0.4)';\nconst INSCRIBED_OUTLINE = 'rgba(255, 255, 255, 0.95)';\nconst INSCRIBED_HALO = 'rgba(0, 0, 0, 0.45)';\n\nexport function mountRotateUtility(options: MountRotateOptions): MountRotateHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? noop;\n\n const preview = buildPreviewCanvas();\n stageHost.appendChild(preview.container);\n\n const panel = buildRotatePanel({\n onCounterClockwise: () => {\n store.set(rotateCounterClockwise(store.get()));\n commit();\n },\n onClockwise: () => {\n store.set(rotateClockwise(store.get()));\n commit();\n },\n onAngleInput: (deg) => store.set(setFreeAngle(store.get(), deg)),\n onAngleCommit: () => commit(),\n onAngleReset: () => {\n store.set(setFreeAngle(store.get(), 0));\n commit();\n },\n });\n utilHost.appendChild(panel.container);\n\n function paint(): void {\n const rect = preview.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n const state = store.get();\n const angleRad = (effectiveAngleDeg(state) * Math.PI) / 180;\n const sub90Deg = effectiveAngleDeg(state) - state.quarterTurns * 90;\n const isQuarterOnly = Math.abs(sub90Deg) < 1e-6;\n\n // Letterbox the rotated bounding box so the user sees the whole rotated source; the bake's crop\n // shows as the inscribed rect inside it.\n const c = Math.abs(Math.cos(angleRad));\n const s = Math.abs(Math.sin(angleRad));\n const boundsW = source.width * c + source.height * s;\n const boundsH = source.width * s + source.height * c;\n\n // Pass rotated bounds to computeViewport so fit-to-screen treats the rotated frame as intrinsic.\n const stageDims = { width: rect.width, height: rect.height, padding: STAGE_PADDING_PX };\n const rotatedBounds = { width: boundsW, height: boundsH };\n const viewport = controller\n ? controller.computeViewport(stageDims, rotatedBounds)\n : computeViewport(stageDims, rotatedBounds);\n const display = viewport.displayRect;\n const cx = display.x + display.width / 2;\n const cy = display.y + display.height / 2;\n\n // Quarter-only turns: inscribed equals the (axis-swapped) source.\n const inscribed = isQuarterOnly\n ? state.quarterTurns % 2 === 0\n ? { width: source.width, height: source.height }\n : { width: source.height, height: source.width }\n : largestInscribedRect(source, angleRad);\n\n paintPreview(preview.canvas, rect.width, rect.height, (ctx) => {\n // Rotated source centred on the displayRect, scaled to display px.\n const drawW = source.width * viewport.scale;\n const drawH = source.height * viewport.scale;\n ctx.save();\n ctx.translate(cx, cy);\n ctx.rotate(angleRad);\n ctx.drawImage(source.bitmap, -drawW / 2, -drawH / 2, drawW, drawH);\n ctx.restore();\n\n const iw = inscribed.width * viewport.scale;\n const ih = inscribed.height * viewport.scale;\n const ix = cx - iw / 2;\n const iy = cy - ih / 2;\n\n // Mask outside the inscribed rect via even-odd clip. Skip on quarter-only turns (no crop).\n if (!isQuarterOnly) {\n ctx.save();\n ctx.beginPath();\n ctx.rect(0, 0, rect.width, rect.height);\n ctx.rect(ix, iy, iw, ih);\n ctx.clip('evenodd');\n ctx.fillStyle = MASK_FILL;\n ctx.fillRect(0, 0, rect.width, rect.height);\n ctx.restore();\n }\n\n ctx.save();\n ctx.lineWidth = 3;\n ctx.strokeStyle = INSCRIBED_HALO;\n ctx.strokeRect(ix + 0.5, iy + 0.5, iw - 1, ih - 1);\n ctx.lineWidth = 1;\n ctx.strokeStyle = INSCRIBED_OUTLINE;\n ctx.strokeRect(ix + 0.5, iy + 0.5, iw - 1, ih - 1);\n ctx.restore();\n });\n }\n\n function syncPanel(state: RotateState): void {\n if (panel.angleSlider.valueAsNumber !== state.freeAngle) {\n panel.angleSlider.valueAsNumber = state.freeAngle;\n }\n const formatted = formatAngleValue(state.freeAngle);\n if (panel.angleInput.value !== formatted) {\n panel.angleInput.value = formatted;\n }\n }\n\n syncPanel(store.get());\n paint();\n\n const resizeObserver = new ResizeObserver(() => paint());\n resizeObserver.observe(preview.container);\n\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n paint();\n });\n });\n\n let rafScheduled = false;\n const unsubscribe = store.subscribe((next) => {\n syncPanel(next);\n if (rafScheduled) return;\n rafScheduled = true;\n requestAnimationFrame(() => {\n rafScheduled = false;\n paint();\n });\n });\n\n return {\n destroy() {\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n preview.container.remove();\n panel.container.remove();\n },\n };\n}\n\ninterface RotatePanelOptions {\n onCounterClockwise(): void;\n onClockwise(): void;\n /** Per-tick state mutation; no commit. */\n onAngleInput(deg: number): void;\n /** Drag-end commit boundary. */\n onAngleCommit(): void;\n onAngleReset(): void;\n}\n\ninterface RotatePanel {\n container: HTMLDivElement;\n angleSlider: HTMLInputElement;\n angleInput: HTMLInputElement;\n}\n\nfunction buildRotatePanel(options: RotatePanelOptions): RotatePanel {\n const container = document.createElement('div');\n container.className = 'kalotyp-rotate-panel';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Rotate');\n\n const ccwButton = makeQuarterButton(\n 'Rotate 90° counter-clockwise',\n '↺',\n options.onCounterClockwise,\n );\n const cwButton = makeQuarterButton('Rotate 90° clockwise', '↻', options.onClockwise);\n\n const angleSliderLabel = document.createElement('label');\n angleSliderLabel.className = 'kalotyp-rotate-slider-label';\n angleSliderLabel.textContent = 'Straighten';\n\n const angleSlider = document.createElement('input');\n angleSlider.type = 'range';\n angleSlider.className = 'kalotyp-rotate-slider';\n angleSlider.min = String(FREE_ANGLE_MIN);\n angleSlider.max = String(FREE_ANGLE_MAX);\n angleSlider.step = String(FREE_ANGLE_STEP);\n angleSlider.value = '0';\n angleSlider.setAttribute('aria-label', 'Straighten angle');\n angleSlider.addEventListener('input', () => options.onAngleInput(angleSlider.valueAsNumber));\n angleSlider.addEventListener('change', () => options.onAngleCommit());\n\n const angleInput = document.createElement('input');\n angleInput.type = 'number';\n angleInput.className = 'kalotyp-rotate-input';\n angleInput.min = String(FREE_ANGLE_MIN);\n angleInput.max = String(FREE_ANGLE_MAX);\n angleInput.step = String(FREE_ANGLE_STEP);\n angleInput.value = '0';\n angleInput.setAttribute('aria-label', 'Straighten angle in degrees');\n angleInput.addEventListener('change', () => {\n const value = angleInput.valueAsNumber;\n if (Number.isFinite(value)) {\n options.onAngleInput(value);\n options.onAngleCommit();\n }\n });\n\n const angleSuffix = document.createElement('span');\n angleSuffix.className = 'kalotyp-rotate-suffix';\n angleSuffix.setAttribute('aria-hidden', 'true');\n angleSuffix.textContent = '°';\n\n const resetButton = document.createElement('button');\n resetButton.type = 'button';\n resetButton.className = 'kalotyp-rotate-reset';\n resetButton.textContent = 'Reset';\n resetButton.addEventListener('click', options.onAngleReset);\n\n const quarterGroup = document.createElement('div');\n quarterGroup.className = 'kalotyp-rotate-row';\n quarterGroup.appendChild(ccwButton);\n quarterGroup.appendChild(cwButton);\n\n const angleInputGroup = document.createElement('span');\n angleInputGroup.className = 'kalotyp-rotate-input-group';\n angleInputGroup.appendChild(angleInput);\n angleInputGroup.appendChild(angleSuffix);\n\n const straightenGroup = document.createElement('div');\n straightenGroup.className = 'kalotyp-rotate-row kalotyp-rotate-slider-row';\n straightenGroup.appendChild(angleSliderLabel);\n straightenGroup.appendChild(angleSlider);\n straightenGroup.appendChild(angleInputGroup);\n straightenGroup.appendChild(resetButton);\n\n container.appendChild(quarterGroup);\n container.appendChild(straightenGroup);\n\n return { container, angleSlider, angleInput };\n}\n\n/** Whole numbers render as `15`; sub-unit values keep one decimal (`15.5`). */\nfunction formatAngleValue(value: number): string {\n const rounded = Math.round(value * 10) / 10;\n if (Number.isInteger(rounded)) return String(rounded);\n return rounded.toFixed(1);\n}\n\nfunction noop(): void {}\n\nfunction makeQuarterButton(label: string, glyph: string, onClick: () => void): HTMLButtonElement {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-quarter-button';\n button.setAttribute('aria-label', label);\n button.title = label;\n button.textContent = glyph;\n button.addEventListener('click', onClick);\n return button;\n}\n","import {\n type RotateState,\n type UtilityPlugin,\n bakeRotate,\n initialRotateState,\n} from '@magicpages/kalotyp-core';\nimport { mountRotateUtility } from './mount.js';\n\nexport interface RotatePluginOptions {\n readonly panelHost: HTMLElement;\n}\n\nexport function createRotatePlugin(options: RotatePluginOptions): UtilityPlugin<RotateState> {\n return {\n id: 'rotate',\n init: () => initialRotateState(),\n mount(stageHost, ctx, store) {\n const handle = mountRotateUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: 'rotate' }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: bakeRotate,\n };\n}\n","import {\n RESIZE_MAX_DIMENSION,\n RESIZE_MIN_DIMENSION,\n type ResizeState,\n type SourceImage,\n type Store,\n type ViewportController,\n resolveOutputSize,\n setHeightPx,\n setLockAspect,\n setPercent,\n setWidthPx,\n} from '@magicpages/kalotyp-core';\nimport {\n buildPreviewCanvas,\n paintPreview,\n previewViewportFor,\n} from '../../canvas/preview-canvas.js';\nimport { icon } from '../../icons.js';\n\nexport interface MountResizeOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly store: Store<ResizeState>;\n /** Editor-level zoom + pan controller. Optional in jsdom tests. */\n readonly viewport?: ViewportController;\n /**\n * Called after each width/height/percent/lock change so the editor\n * can capture an undo snapshot. Inputs already commit on\n * `change` (blur/Enter), not `input`, so a single typed value is one\n * undo step.\n */\n readonly onCommit?: () => void;\n}\n\nexport interface MountResizeHandle {\n destroy(): void;\n}\n\nexport function mountResizeUtility(options: MountResizeOptions): MountResizeHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? (() => {});\n\n const preview = buildPreviewCanvas();\n stageHost.appendChild(preview.container);\n\n const upstream = { width: source.width, height: source.height };\n const panel = buildResizePanel({\n upstream,\n onWidthChange: (px) => {\n store.set(setWidthPx(store.get(), px, upstream));\n commit();\n },\n onHeightChange: (px) => {\n store.set(setHeightPx(store.get(), px, upstream));\n commit();\n },\n onPercentChange: (pct) => {\n store.set(setPercent(store.get(), pct));\n commit();\n },\n onLockChange: (locked) => {\n store.set(setLockAspect(store.get(), locked));\n commit();\n },\n });\n utilHost.appendChild(panel.container);\n\n function paint(): void {\n const v = previewViewportFor(preview.container, upstream, controller);\n if (!v) return;\n paintPreview(preview.canvas, v.stageWidth, v.stageHeight, (ctx) => {\n const display = v.viewport.displayRect;\n ctx.drawImage(source.bitmap, display.x, display.y, display.width, display.height);\n });\n }\n\n function syncPanel(state: ResizeState): void {\n const out = resolveOutputSize(state, upstream);\n if (panel.widthInput.valueAsNumber !== out.width) panel.widthInput.value = String(out.width);\n if (panel.heightInput.valueAsNumber !== out.height)\n panel.heightInput.value = String(out.height);\n const percent = (state.scaleX + state.scaleY) / 2;\n const percentDisplay = Math.round(percent * 1000) / 10;\n if (Number.parseFloat(panel.percentInput.value || '0') !== percentDisplay) {\n panel.percentInput.value = String(percentDisplay);\n }\n panel.lockButton.setAttribute('aria-pressed', state.lockAspect ? 'true' : 'false');\n panel.lockButton.setAttribute(\n 'aria-label',\n state.lockAspect\n ? 'Aspect ratio locked — click to unlock'\n : 'Aspect ratio unlocked — click to lock',\n );\n panel.lockButton.title = state.lockAspect ? 'Aspect ratio locked' : 'Aspect ratio unlocked';\n panel.lockButton.innerHTML = chainIconSvg(state.lockAspect);\n panel.summary.textContent = `${out.width} × ${out.height}px (from ${upstream.width} × ${upstream.height}px)`;\n }\n\n syncPanel(store.get());\n paint();\n\n const resizeObserver = new ResizeObserver(() => paint());\n resizeObserver.observe(preview.container);\n\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n paint();\n });\n });\n\n const unsubscribe = store.subscribe((next) => {\n syncPanel(next);\n });\n\n return {\n destroy() {\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n preview.container.remove();\n panel.container.remove();\n },\n };\n}\n\ninterface ResizePanelOptions {\n readonly upstream: { width: number; height: number };\n onWidthChange(px: number): void;\n onHeightChange(px: number): void;\n onPercentChange(pct: number): void;\n onLockChange(locked: boolean): void;\n}\n\ninterface ResizePanel {\n container: HTMLDivElement;\n widthInput: HTMLInputElement;\n heightInput: HTMLInputElement;\n percentInput: HTMLInputElement;\n lockButton: HTMLButtonElement;\n summary: HTMLSpanElement;\n}\n\nfunction buildResizePanel(options: ResizePanelOptions): ResizePanel {\n const container = document.createElement('div');\n container.className = 'kalotyp-resize-panel';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Resize');\n\n const widthInput = makeNumberInput({\n label: 'Width (px)',\n min: RESIZE_MIN_DIMENSION,\n max: RESIZE_MAX_DIMENSION,\n step: 1,\n value: options.upstream.width,\n onChange: options.onWidthChange,\n });\n const heightInput = makeNumberInput({\n label: 'Height (px)',\n min: RESIZE_MIN_DIMENSION,\n max: RESIZE_MAX_DIMENSION,\n step: 1,\n value: options.upstream.height,\n onChange: options.onHeightChange,\n });\n const percentInput = makeNumberInput({\n label: 'Scale (%)',\n min: 1,\n max: 1000,\n step: 0.1,\n value: 100,\n onChange: options.onPercentChange,\n });\n\n // Small icon-only button between Width and Height — the convention\n // every desktop image-editor uses (GIMP / Photoshop / Photopea /\n // Figma / filerobot's Image Size dialog: a chain-link icon that\n // visually links W to H, closed when ratio is locked and broken\n // when not). The button itself is a 32×32 square with the chain\n // icon centred; aria-pressed carries the state.\n const lockButton = document.createElement('button');\n lockButton.type = 'button';\n lockButton.className = 'kalotyp-resize-lock';\n lockButton.setAttribute('aria-pressed', 'true');\n lockButton.setAttribute('aria-label', 'Lock aspect ratio');\n lockButton.title = 'Lock aspect ratio';\n lockButton.innerHTML = chainIconSvg(true);\n lockButton.addEventListener('click', () => {\n const next = lockButton.getAttribute('aria-pressed') !== 'true';\n options.onLockChange(next);\n });\n\n const summary = document.createElement('span');\n summary.className = 'kalotyp-resize-summary';\n summary.setAttribute('aria-live', 'polite');\n // Helper text moved to a `title` on the panel — surfacing the\n // 8000px cap via tooltip rather than a permanent caption keeps the\n // panel a single tidy row in line with Crop / Flip / Rotate.\n summary.textContent = `${options.upstream.width} × ${options.upstream.height}px`;\n\n container.title = `Maximum ${RESIZE_MAX_DIMENSION}px on either axis`;\n\n // Single flat row — the panel container is flex-row+wrap+centred\n // (transform.css). Width / [lock] / Height puts the chain-link\n // button between the two inputs it relates, matching every\n // desktop editor's Image Size dialog.\n const dimsRow = document.createElement('div');\n dimsRow.className = 'kalotyp-resize-row kalotyp-resize-dims';\n dimsRow.appendChild(widthInput.wrapper);\n dimsRow.appendChild(lockButton);\n dimsRow.appendChild(heightInput.wrapper);\n\n const scaleRow = document.createElement('div');\n scaleRow.className = 'kalotyp-resize-row';\n scaleRow.appendChild(percentInput.wrapper);\n scaleRow.appendChild(summary);\n\n container.appendChild(dimsRow);\n container.appendChild(scaleRow);\n\n return {\n container,\n widthInput: widthInput.input,\n heightInput: heightInput.input,\n percentInput: percentInput.input,\n lockButton,\n summary,\n };\n}\n\ninterface NumberInputOptions {\n readonly label: string;\n readonly min: number;\n readonly max: number;\n readonly step: number;\n readonly value: number;\n onChange(value: number): void;\n}\n\nfunction makeNumberInput(options: NumberInputOptions): {\n wrapper: HTMLLabelElement;\n input: HTMLInputElement;\n} {\n const wrapper = document.createElement('label');\n wrapper.className = 'kalotyp-resize-field';\n\n const labelSpan = document.createElement('span');\n labelSpan.className = 'kalotyp-resize-field-label';\n labelSpan.textContent = options.label;\n\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'kalotyp-resize-input';\n input.min = String(options.min);\n input.max = String(options.max);\n input.step = String(options.step);\n input.value = String(options.value);\n input.inputMode = 'numeric';\n input.setAttribute('aria-label', options.label);\n\n // Commit on `change` (blur/Enter) so the user can type intermediate\n // values without firing a state update on every keystroke.\n input.addEventListener('change', () => {\n const value = input.valueAsNumber;\n if (Number.isFinite(value)) options.onChange(value);\n });\n\n wrapper.appendChild(labelSpan);\n wrapper.appendChild(input);\n return { wrapper, input };\n}\n\n/**\n * Inline SVG for the aspect-lock toggle. Sourced from the shared\n * Lucide-backed icon module (`link-2` for locked, `link-2-off` for\n * unlocked). `currentColor` on the strokes lets the icon inherit the\n * button's text colour for active/hover states.\n */\nfunction chainIconSvg(locked: boolean): string {\n return locked ? icon('lockClosed') : icon('lockOpen');\n}\n","import {\n type ResizeState,\n type UtilityPlugin,\n bakeResize,\n initialResizeState,\n} from '@magicpages/kalotyp-core';\nimport { mountResizeUtility } from './mount.js';\n\nexport interface ResizePluginOptions {\n readonly panelHost: HTMLElement;\n}\n\nexport function createResizePlugin(options: ResizePluginOptions): UtilityPlugin<ResizeState> {\n return {\n id: 'resize',\n init: () => initialResizeState(),\n mount(stageHost, ctx, store) {\n const handle = mountResizeUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: 'resize' }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: bakeResize,\n };\n}\n","import {\n type FinetuneState,\n applyClarity,\n applyFinetuneLutAndSaturation,\n boxBlur3x3,\n buildFinetuneLut,\n isFinetuneNoOp,\n} from '@magicpages/kalotyp-core';\n\n/**\n * Finetune preview pipeline. Operates on display-res pixels (a 4000×3000 photo letterboxed to\n * ~720×480 at DPR 2 is ~1.4 MP — the six-adjust composed pass measures ~5 ms there).\n */\nexport interface FinetunePreviewPipeline {\n paint(state: FinetuneState): void;\n rebuild(width: number, height: number): void;\n dispose(): void;\n}\n\ninterface PreviewBuffers {\n baseline: Uint8ClampedArray;\n scratch: Uint8ClampedArray;\n /** Pre-blurred baseline for clarity; rebuilt with baseline. */\n blurred: Uint8ClampedArray | undefined;\n width: number;\n height: number;\n}\n\nexport interface BuildPreviewPipelineOptions {\n readonly canvas: HTMLCanvasElement;\n readonly sourceBitmap: CanvasImageSource;\n}\n\nexport function buildFinetunePreviewPipeline(\n options: BuildPreviewPipelineOptions,\n): FinetunePreviewPipeline {\n const { canvas, sourceBitmap } = options;\n let buffers: PreviewBuffers | undefined;\n\n function rebuild(width: number, height: number): void {\n if (width <= 0 || height <= 0) return;\n // willReadFrequently keeps the backing store CPU-side, avoiding a GPU sync per getImageData — load-bearing here.\n const ctx = getReadFrequentContext(canvas);\n if (!ctx) return;\n canvas.width = width;\n canvas.height = height;\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.clearRect(0, 0, width, height);\n ctx.drawImage(sourceBitmap, 0, 0, width, height);\n const imageData = ctx.getImageData(0, 0, width, height);\n buffers = {\n baseline: new Uint8ClampedArray(imageData.data),\n scratch: imageData.data,\n blurred: undefined,\n width,\n height,\n };\n }\n\n function paint(state: FinetuneState): void {\n if (!buffers) return;\n const ctx = getReadFrequentContext(canvas);\n if (!ctx) return;\n\n if (isFinetuneNoOp(state)) {\n // Paint a fresh copy of baseline (not scratch) so releasing every slider is bit-exact \"this is the source\".\n const imageData = new ImageData(\n new Uint8ClampedArray(buffers.baseline),\n buffers.width,\n buffers.height,\n );\n ctx.putImageData(imageData, 0, 0);\n return;\n }\n\n const lut = buildFinetuneLut(state);\n applyFinetuneLutAndSaturation(buffers.baseline, buffers.scratch, lut, state);\n\n if (state.clarity !== 0) {\n // Lazy-cached blurred baseline; invalidated on every rebuild.\n if (!buffers.blurred) {\n const tmp = new Uint8ClampedArray(buffers.baseline.length);\n const blurred = new Uint8ClampedArray(buffers.baseline.length);\n boxBlur3x3(buffers.baseline, tmp, blurred, buffers.width, buffers.height);\n buffers.blurred = blurred;\n }\n applyClarity(buffers.scratch, buffers.blurred, state.clarity);\n }\n\n const imageData = new ImageData(buffers.scratch, buffers.width, buffers.height);\n ctx.putImageData(imageData, 0, 0);\n }\n\n function dispose(): void {\n buffers = undefined;\n }\n\n return { paint, rebuild, dispose };\n}\n\n// getContext returns the first-acquired context; subsequent attribute args are ignored, so this helper is safe to call from both rebuild and paint.\nfunction getReadFrequentContext(canvas: HTMLCanvasElement): CanvasRenderingContext2D | null {\n return canvas.getContext('2d', { willReadFrequently: true });\n}\n","import {\n FINETUNE_ADJUSTMENTS,\n FINETUNE_MAX,\n FINETUNE_MIN,\n FINETUNE_STEP,\n type FinetuneKey,\n type FinetuneState,\n type SourceImage,\n type Store,\n type ViewportController,\n resetAllFinetune,\n resetFinetune,\n setFinetune,\n} from '@magicpages/kalotyp-core';\nimport { buildPreviewCanvas, previewViewportFor } from '../../canvas/preview-canvas.js';\nimport { buildFinetunePreviewPipeline } from './preview.js';\n\nexport interface MountFinetuneOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly store: Store<FinetuneState>;\n /** Editor-level zoom + pan controller. Optional in jsdom tests. */\n readonly viewport?: ViewportController;\n /** Fires on slider `change` / number `change` / reset (one undo entry per drag, not per `input`). */\n readonly onCommit?: () => void;\n}\n\nexport interface MountFinetuneHandle {\n destroy(): void;\n}\n\nexport function mountFinetuneUtility(options: MountFinetuneOptions): MountFinetuneHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? noop;\n\n const preview = buildPreviewCanvas();\n stageHost.appendChild(preview.container);\n\n const pipeline = buildFinetunePreviewPipeline({\n canvas: preview.canvas,\n sourceBitmap: source.bitmap,\n });\n\n const panel = buildFinetunePanel({\n onSliderInput: (key, value) => store.set(setFinetune(store.get(), key, value)),\n onSliderCommit: () => commit(),\n onNumberCommit: (key, value) => {\n store.set(setFinetune(store.get(), key, value));\n commit();\n },\n onRowReset: (key) => {\n store.set(resetFinetune(store.get(), key));\n commit();\n },\n onResetAll: () => {\n store.update(() => resetAllFinetune());\n commit();\n },\n });\n utilHost.appendChild(panel.container);\n\n let upstreamSize = { width: 0, height: 0 };\n\n function repaint(): void {\n const v = previewViewportFor(\n preview.container,\n { width: source.width, height: source.height },\n controller,\n );\n if (!v) return;\n const display = v.viewport.displayRect;\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const pxW = Math.max(1, Math.round(display.width * dpr));\n const pxH = Math.max(1, Math.round(display.height * dpr));\n\n // Canvas is the math's surface (pipeline does getImageData/putImageData) — no transformed ctx, so paintPreview's DPR dance doesn't apply.\n preview.canvas.style.width = `${display.width}px`;\n preview.canvas.style.height = `${display.height}px`;\n preview.canvas.style.position = 'absolute';\n preview.canvas.style.left = `${display.x}px`;\n preview.canvas.style.top = `${display.y}px`;\n\n // Skip the CPU rebuild during pinch — CSS interp on the existing buffer is good enough; post-gesture repaint sharpens.\n const pinching = controller?.getPinching() ?? false;\n if (!pinching && (upstreamSize.width !== pxW || upstreamSize.height !== pxH)) {\n upstreamSize = { width: pxW, height: pxH };\n pipeline.rebuild(pxW, pxH);\n }\n\n pipeline.paint(store.get());\n }\n\n function syncPanel(state: FinetuneState): void {\n for (const adj of FINETUNE_ADJUSTMENTS) {\n const row = panel.rows.get(adj.key);\n if (!row) continue;\n const value = state[adj.key];\n if (row.slider.valueAsNumber !== value) row.slider.valueAsNumber = value;\n if (Number.parseFloat(row.input.value || '0') !== value) row.input.value = String(value);\n }\n }\n\n syncPanel(store.get());\n repaint();\n\n const resizeObserver = new ResizeObserver(() => repaint());\n resizeObserver.observe(preview.container);\n\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n repaint();\n });\n });\n\n let rafScheduled = false;\n const unsubscribe = store.subscribe((next) => {\n syncPanel(next);\n if (rafScheduled) return;\n rafScheduled = true;\n requestAnimationFrame(() => {\n rafScheduled = false;\n pipeline.paint(store.get());\n });\n });\n\n return {\n destroy() {\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n pipeline.dispose();\n preview.container.remove();\n panel.container.remove();\n },\n };\n}\n\ninterface FinetunePanelOptions {\n onSliderInput(key: FinetuneKey, value: number): void;\n onSliderCommit(): void;\n onNumberCommit(key: FinetuneKey, value: number): void;\n onRowReset(key: FinetuneKey): void;\n onResetAll(): void;\n}\n\ninterface FinetuneRowEls {\n readonly row: HTMLDivElement;\n readonly slider: HTMLInputElement;\n readonly input: HTMLInputElement;\n readonly resetButton: HTMLButtonElement;\n}\n\ninterface FinetunePanel {\n container: HTMLDivElement;\n rows: ReadonlyMap<FinetuneKey, FinetuneRowEls>;\n resetAllButton: HTMLButtonElement;\n}\n\nfunction buildFinetunePanel(options: FinetunePanelOptions): FinetunePanel {\n const container = document.createElement('div');\n container.className = 'kalotyp-finetune-panel';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Finetune adjustments');\n\n const rows = new Map<FinetuneKey, FinetuneRowEls>();\n for (const adj of FINETUNE_ADJUSTMENTS) {\n const row = buildAdjustmentRow(adj.key, adj.label, options);\n rows.set(adj.key, row);\n container.appendChild(row.row);\n }\n\n const resetAllButton = document.createElement('button');\n resetAllButton.type = 'button';\n resetAllButton.className = 'kalotyp-finetune-reset-all';\n resetAllButton.textContent = 'Reset all';\n resetAllButton.title = 'Reset every adjustment to 0';\n resetAllButton.addEventListener('click', options.onResetAll);\n container.appendChild(resetAllButton);\n\n return { container, rows, resetAllButton };\n}\n\nfunction buildAdjustmentRow(\n key: FinetuneKey,\n label: string,\n options: FinetunePanelOptions,\n): FinetuneRowEls {\n const row = document.createElement('div');\n row.className = 'kalotyp-finetune-row';\n row.dataset.adjustment = key;\n\n const labelEl = document.createElement('label');\n labelEl.className = 'kalotyp-finetune-label';\n labelEl.textContent = label;\n\n const slider = document.createElement('input');\n slider.type = 'range';\n slider.className = 'kalotyp-finetune-slider';\n slider.min = String(FINETUNE_MIN);\n slider.max = String(FINETUNE_MAX);\n slider.step = String(FINETUNE_STEP);\n slider.value = '0';\n slider.setAttribute('aria-label', `${label} adjustment`);\n slider.addEventListener('input', () => options.onSliderInput(key, slider.valueAsNumber));\n slider.addEventListener('change', () => options.onSliderCommit());\n\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'kalotyp-finetune-input';\n input.min = String(FINETUNE_MIN);\n input.max = String(FINETUNE_MAX);\n input.step = String(FINETUNE_STEP);\n input.value = '0';\n input.inputMode = 'numeric';\n input.setAttribute('aria-label', `${label} value`);\n input.addEventListener('change', () => {\n const v = input.valueAsNumber;\n if (Number.isFinite(v)) options.onNumberCommit(key, v);\n });\n\n const resetButton = document.createElement('button');\n resetButton.type = 'button';\n resetButton.className = 'kalotyp-finetune-row-reset';\n resetButton.setAttribute('aria-label', `Reset ${label}`);\n resetButton.title = `Reset ${label}`;\n resetButton.textContent = '↺';\n resetButton.addEventListener('click', () => options.onRowReset(key));\n\n row.appendChild(labelEl);\n row.appendChild(slider);\n row.appendChild(input);\n row.appendChild(resetButton);\n\n return { row, slider, input, resetButton };\n}\n\nfunction noop(): void {}\n","import {\n type FinetuneState,\n type UtilityPlugin,\n bakeFinetune,\n initialFinetuneState,\n} from '@magicpages/kalotyp-core';\nimport { mountFinetuneUtility } from './mount.js';\n\nexport interface FinetunePluginOptions {\n readonly panelHost: HTMLElement;\n}\n\nexport function createFinetunePlugin(options: FinetunePluginOptions): UtilityPlugin<FinetuneState> {\n return {\n id: 'finetune',\n init: () => initialFinetuneState(),\n mount(stageHost, ctx, store) {\n const handle = mountFinetuneUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: 'finetune' }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: bakeFinetune,\n };\n}\n","import {\n type FilterPreset,\n type FilterPresetId,\n type RasterImage,\n applyFinetuneToImageData,\n} from '@magicpages/kalotyp-core';\n\n/**\n * Filter-strip thumbnails. Uses the same `applyFinetuneToImageData` as bake+preview, so\n * the thumbnail honestly previews the saved result (modulo source→thumbnail resampling).\n */\n\nexport const THUMBNAIL_MAX_WIDTH = 80;\nexport const THUMBNAIL_MAX_HEIGHT = 60;\n\nexport interface ThumbnailDims {\n readonly width: number;\n readonly height: number;\n}\n\n/** Largest aspect-preserving fit inside the THUMBNAIL_MAX_* box. */\nexport function computeThumbnailDims(source: { width: number; height: number }): ThumbnailDims {\n if (source.width <= 0 || source.height <= 0) {\n return { width: THUMBNAIL_MAX_WIDTH, height: THUMBNAIL_MAX_HEIGHT };\n }\n const widthRatio = THUMBNAIL_MAX_WIDTH / source.width;\n const heightRatio = THUMBNAIL_MAX_HEIGHT / source.height;\n const ratio = Math.min(widthRatio, heightRatio);\n const width = Math.max(1, Math.floor(source.width * ratio));\n const height = Math.max(1, Math.floor(source.height * ratio));\n return { width, height };\n}\n\nexport interface ThumbnailCache {\n get(preset: FilterPreset): HTMLCanvasElement;\n dispose(): void;\n}\n\nexport interface BuildThumbnailCacheOptions {\n readonly source: CanvasImageSource & { readonly width?: number; readonly height?: number };\n readonly dims: ThumbnailDims;\n readonly dpr: number;\n readonly presets?: readonly FilterPreset[];\n}\n\n/** Lazy-rendered preset cache. Baseline ImageData is captured once so per-preset cost is math + putImageData. */\nexport function buildThumbnailCache(options: BuildThumbnailCacheOptions): ThumbnailCache {\n const { source, dims, dpr } = options;\n const pxW = Math.max(1, Math.round(dims.width * dpr));\n const pxH = Math.max(1, Math.round(dims.height * dpr));\n\n // Render source at thumbnail pixel-grid once; this baseline ImageData feeds every preset's math.\n const baselineCanvas = document.createElement('canvas');\n baselineCanvas.width = pxW;\n baselineCanvas.height = pxH;\n const baselineCtx = baselineCanvas.getContext('2d', { willReadFrequently: true });\n if (!baselineCtx) {\n return {\n get: () => makeBlankCanvas(dims, dpr),\n dispose: () => {},\n };\n }\n baselineCtx.imageSmoothingEnabled = true;\n baselineCtx.imageSmoothingQuality = 'high';\n baselineCtx.drawImage(source, 0, 0, pxW, pxH);\n const baselineImageData = baselineCtx.getImageData(0, 0, pxW, pxH);\n const baseline: RasterImage = {\n data: new Uint8ClampedArray(baselineImageData.data),\n width: pxW,\n height: pxH,\n };\n\n const cache = new Map<FilterPresetId, HTMLCanvasElement>();\n\n function renderPreset(preset: FilterPreset): HTMLCanvasElement {\n const canvas = document.createElement('canvas');\n canvas.width = pxW;\n canvas.height = pxH;\n canvas.style.width = `${dims.width}px`;\n canvas.style.height = `${dims.height}px`;\n const ctx = canvas.getContext('2d');\n if (!ctx) return canvas;\n\n const dst: RasterImage = {\n data: new Uint8ClampedArray(baseline.data.length),\n width: pxW,\n height: pxH,\n };\n applyFinetuneToImageData(preset.state, baseline, dst);\n ctx.putImageData(new ImageData(dst.data, pxW, pxH), 0, 0);\n return canvas;\n }\n\n return {\n get(preset: FilterPreset): HTMLCanvasElement {\n const existing = cache.get(preset.id);\n if (existing) return existing;\n const canvas = renderPreset(preset);\n cache.set(preset.id, canvas);\n return canvas;\n },\n dispose(): void {\n cache.clear();\n },\n };\n}\n\nfunction makeBlankCanvas(dims: ThumbnailDims, dpr: number): HTMLCanvasElement {\n const canvas = document.createElement('canvas');\n canvas.width = Math.max(1, Math.round(dims.width * dpr));\n canvas.height = Math.max(1, Math.round(dims.height * dpr));\n canvas.style.width = `${dims.width}px`;\n canvas.style.height = `${dims.height}px`;\n return canvas;\n}\n","import {\n DEFAULT_FINETUNE_STATE,\n FILTER_PRESETS,\n type FilterPreset,\n type FilterPresetId,\n type FinetuneState,\n type SourceImage,\n type Store,\n type ViewportController,\n findActivePreset,\n} from '@magicpages/kalotyp-core';\nimport { buildPreviewCanvas, previewViewportFor } from '../../canvas/preview-canvas.js';\nimport { buildFinetunePreviewPipeline } from '../finetune/preview.js';\nimport { type ThumbnailCache, buildThumbnailCache, computeThumbnailDims } from './thumbnails.js';\n\nexport interface MountFilterOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n /** Upstream-baked source (same input as the finetune tab — filter shares finetune's slot). */\n readonly source: SourceImage;\n /** Shared store with finetune; clicking a thumbnail writes here. */\n readonly store: Store<FinetuneState>;\n readonly viewport?: ViewportController;\n readonly onCommit?: () => void;\n}\n\nexport interface MountFilterHandle {\n destroy(): void;\n}\n\nexport function mountFilterUtility(options: MountFilterOptions): MountFilterHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? noop;\n\n // Mirror of the finetune-tab preview so switching tabs doesn't change what's on screen.\n const preview = buildPreviewCanvas();\n stageHost.appendChild(preview.container);\n const pipeline = buildFinetunePreviewPipeline({\n canvas: preview.canvas,\n sourceBitmap: source.bitmap,\n });\n\n // Upstream-baked source is fixed for this mount, so the seven thumbnails generate once.\n const dims = computeThumbnailDims({ width: source.width, height: source.height });\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const thumbnailCache = buildThumbnailCache({\n source: source.bitmap,\n dims,\n dpr,\n });\n\n const strip = buildFilterStrip({\n presets: FILTER_PRESETS,\n thumbnailCache,\n dims,\n onPresetClick: (preset) => {\n const current = store.get();\n const isActiveNow = findActivePreset(current)?.id === preset.id;\n if (isActiveNow && preset.id !== 'none') {\n // Click-to-deselect → all-zeros. Without it, clearing a filter requires dragging sliders back.\n store.update(() => DEFAULT_FINETUNE_STATE);\n commit();\n return;\n }\n store.update(() => preset.state);\n commit();\n },\n });\n utilHost.appendChild(strip.container);\n\n let upstreamSize = { width: 0, height: 0 };\n\n function repaint(): void {\n const v = previewViewportFor(\n preview.container,\n { width: source.width, height: source.height },\n controller,\n );\n if (!v) return;\n const display = v.viewport.displayRect;\n const pxW = Math.max(1, Math.round(display.width * dpr));\n const pxH = Math.max(1, Math.round(display.height * dpr));\n\n preview.canvas.style.width = `${display.width}px`;\n preview.canvas.style.height = `${display.height}px`;\n preview.canvas.style.position = 'absolute';\n preview.canvas.style.left = `${display.x}px`;\n preview.canvas.style.top = `${display.y}px`;\n\n // Skip the heavy pixel-grid rebuild during a pinch; CSS interp covers it, post-gesture repaint sharpens.\n const pinching = controller?.getPinching() ?? false;\n if (!pinching && (upstreamSize.width !== pxW || upstreamSize.height !== pxH)) {\n upstreamSize = { width: pxW, height: pxH };\n pipeline.rebuild(pxW, pxH);\n }\n\n pipeline.paint(store.get());\n }\n\n function syncStripActive(state: FinetuneState): void {\n const activeId = findActivePreset(state)?.id;\n strip.setActive(activeId);\n }\n\n syncStripActive(store.get());\n repaint();\n\n const resizeObserver = new ResizeObserver(() => repaint());\n resizeObserver.observe(preview.container);\n\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n repaint();\n });\n });\n\n let rafScheduled = false;\n const unsubscribe = store.subscribe((next) => {\n syncStripActive(next);\n if (rafScheduled) return;\n rafScheduled = true;\n requestAnimationFrame(() => {\n rafScheduled = false;\n pipeline.paint(store.get());\n });\n });\n\n return {\n destroy() {\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n pipeline.dispose();\n thumbnailCache.dispose();\n preview.container.remove();\n strip.container.remove();\n },\n };\n}\n\ninterface FilterStripOptions {\n readonly presets: readonly FilterPreset[];\n readonly thumbnailCache: ThumbnailCache;\n readonly dims: { width: number; height: number };\n onPresetClick(preset: FilterPreset): void;\n}\n\ninterface FilterStrip {\n readonly container: HTMLDivElement;\n setActive(id: FilterPresetId | undefined): void;\n}\n\nfunction buildFilterStrip(options: FilterStripOptions): FilterStrip {\n const container = document.createElement('div');\n container.className = 'kalotyp-filter-panel';\n // Radiogroup, not tablist: presets are mutually-exclusive state, not panel switchers.\n container.setAttribute('role', 'radiogroup');\n container.setAttribute('aria-label', 'Filter presets');\n\n const list = document.createElement('div');\n list.className = 'kalotyp-filter-strip';\n container.appendChild(list);\n\n const buttons = new Map<FilterPresetId, HTMLButtonElement>();\n\n for (const preset of options.presets) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-filter-thumb';\n button.dataset.presetId = preset.id;\n button.setAttribute('role', 'radio');\n button.setAttribute('aria-checked', 'false');\n button.setAttribute('aria-label', `${preset.label} filter`);\n button.title = preset.label;\n\n const thumbWrap = document.createElement('span');\n thumbWrap.className = 'kalotyp-filter-thumb-image';\n thumbWrap.style.width = `${options.dims.width}px`;\n thumbWrap.style.height = `${options.dims.height}px`;\n const canvas = options.thumbnailCache.get(preset);\n canvas.classList.add('kalotyp-filter-thumb-canvas');\n thumbWrap.appendChild(canvas);\n\n // Active-state checkmark (avoids color-only signalling). aria-hidden: aria-checked is the state carrier.\n const activeBadge = document.createElement('span');\n activeBadge.className = 'kalotyp-filter-thumb-check';\n activeBadge.setAttribute('aria-hidden', 'true');\n activeBadge.innerHTML = '✓';\n thumbWrap.appendChild(activeBadge);\n\n const labelEl = document.createElement('span');\n labelEl.className = 'kalotyp-filter-thumb-label';\n labelEl.textContent = preset.label;\n\n button.appendChild(thumbWrap);\n button.appendChild(labelEl);\n button.addEventListener('click', () => options.onPresetClick(preset));\n\n list.appendChild(button);\n buttons.set(preset.id, button);\n }\n\n return {\n container,\n setActive(id) {\n for (const [presetId, button] of buttons) {\n const isActive = presetId === id;\n button.setAttribute('aria-checked', isActive ? 'true' : 'false');\n button.classList.toggle('kalotyp-filter-thumb--active', isActive);\n }\n },\n };\n}\n\nfunction noop(): void {}\n","import {\n DEFAULT_FINETUNE_STATE,\n type FinetuneState,\n type SourceImage,\n type UtilityPlugin,\n} from '@magicpages/kalotyp-core';\nimport { mountFilterUtility } from './mount.js';\n\nexport interface FilterPluginOptions {\n readonly panelHost: HTMLElement;\n}\n\n/**\n * Filter plugin. Shares the finetune slot's store; `init`/`bake` here are synthetic\n * (filter isn't in CHAIN_ORDER and the editor registers the slot manually with the shared store).\n */\nexport function createFilterPlugin(options: FilterPluginOptions): UtilityPlugin<FinetuneState> {\n return {\n id: 'filter',\n init: () => DEFAULT_FINETUNE_STATE,\n mount(stageHost, ctx, store) {\n const handle = mountFilterUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: 'filter' }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: filterBakeIdentity,\n };\n}\n\n// Identity bake — never called, exists only to satisfy `UtilityPlugin<TState>`.\nasync function filterBakeIdentity(\n _state: FinetuneState,\n source: SourceImage,\n): Promise<SourceImage> {\n return source;\n}\n","/**\n * Per-shape coordinate-input rows for the keyboard placement path\n *. The inputs are the keyboard-only equivalent\n * of dragging selection handles: a user types image-space pixel\n * coordinates, the shape's geometry updates on `change` (blur or\n * Enter), and the live canvas + selection handles re-paint via the\n * existing store subscription path.\n *\n * Each shape kind exposes a different shape of fields:\n *\n * - rect / ellipse → Left, Top, Width, Height (matches crop's\n * dimension-input pattern from Phase 6.2; same semantics).\n * - arrow → Start X, Start Y, End X, End Y (the two endpoints).\n * - text → X, Y (anchor only; size is driven by the font size\n * control, and the inline editor handles the text content).\n *\n * One row instance is reused across selections; it rebuilds its\n * fields when the selected shape's kind changes. The mount layer\n * subscribes to the selection and calls `updateForShape(shape)` on\n * each change so the inputs reflect the live geometry.\n */\n\nimport type {\n ArrowShape,\n EllipseShape,\n RectShape,\n Shape,\n TextShape,\n} from '@magicpages/kalotyp-core';\n\nexport interface CoordInputsOptions {\n /**\n * Called when a typed value commits (blur or Enter). The handler\n * receives the new shape; the mount layer writes it to the store\n * via `replaceShape` and emits a commit. Keeping the helper\n * store-free means the row's tests don't need to thread a store.\n */\n onShapeChanged(shape: Shape): void;\n}\n\n/**\n * A single coordinate edit. Discriminated on shape kind so the apply\n * step can narrow without re-reading the input ids.\n */\nexport type ShapeCoordEdit =\n | {\n readonly kind: 'rect' | 'ellipse';\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n }\n | {\n readonly kind: 'arrow';\n readonly x1: number;\n readonly y1: number;\n readonly x2: number;\n readonly y2: number;\n }\n | { readonly kind: 'text'; readonly x: number; readonly y: number };\n\nexport interface CoordInputsHandle {\n readonly container: HTMLDivElement;\n /** Show inputs for the given shape and prefill values. Hides when shape is null. */\n updateForShape(shape: Shape | null): void;\n destroy(): void;\n}\n\ninterface FieldSpec {\n readonly id: string;\n readonly label: string;\n readonly min?: number;\n readonly max?: number;\n}\n\nconst RECT_FIELDS: ReadonlyArray<FieldSpec> = [\n { id: 'x', label: 'Left' },\n { id: 'y', label: 'Top' },\n { id: 'width', label: 'Width', min: 1 },\n { id: 'height', label: 'Height', min: 1 },\n];\n\nconst ARROW_FIELDS: ReadonlyArray<FieldSpec> = [\n { id: 'x1', label: 'Start X' },\n { id: 'y1', label: 'Start Y' },\n { id: 'x2', label: 'End X' },\n { id: 'y2', label: 'End Y' },\n];\n\nconst TEXT_FIELDS: ReadonlyArray<FieldSpec> = [\n { id: 'x', label: 'X' },\n { id: 'y', label: 'Y' },\n];\n\n/**\n * Build a single per-selection coordinate-input row. The row reuses\n * the same `kalotyp-annotate-coords-*` class set across shape kinds\n * so styling is one rule set; only the field set changes per shape.\n */\nexport function buildCoordInputs(options: CoordInputsOptions): CoordInputsHandle {\n const container = document.createElement('div');\n container.className = 'kalotyp-annotate-coords';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Selected annotation position');\n container.hidden = true;\n\n let activeShape: Shape | null = null;\n let activeKind: 'rect' | 'ellipse' | 'arrow' | 'text' | null = null;\n const inputs = new Map<string, HTMLInputElement>();\n\n function rebuildFor(kind: 'rect' | 'ellipse' | 'arrow' | 'text'): void {\n container.replaceChildren();\n inputs.clear();\n const fields = fieldsFor(kind);\n for (const spec of fields) {\n const wrapper = document.createElement('label');\n wrapper.className = 'kalotyp-annotate-coords-field';\n\n const labelSpan = document.createElement('span');\n labelSpan.className = 'kalotyp-annotate-coords-label';\n labelSpan.textContent = spec.label;\n\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'kalotyp-annotate-coords-input';\n input.dataset.field = spec.id;\n input.step = '1';\n input.inputMode = 'numeric';\n if (spec.min !== undefined) input.min = String(spec.min);\n if (spec.max !== undefined) input.max = String(spec.max);\n input.setAttribute('aria-label', `${spec.label} (pixels)`);\n input.addEventListener('change', onAnyInputChange);\n\n wrapper.appendChild(labelSpan);\n wrapper.appendChild(input);\n container.appendChild(wrapper);\n inputs.set(spec.id, input);\n }\n activeKind = kind;\n }\n\n function syncValuesFromShape(shape: Shape): void {\n const setVal = (id: string, value: number): void => {\n const el = inputs.get(id);\n if (!el) return;\n const next = String(Math.round(value));\n // Skip the assignment if the user is mid-edit on this very\n // input — overwriting a focused, partially-typed value is the\n // most surprising thing an a11y helper can do. The store-driven\n // sync still catches up after blur via the next `update`.\n if (document.activeElement === el) return;\n if (el.value !== next) el.value = next;\n };\n\n switch (shape.kind) {\n case 'rect':\n case 'ellipse': {\n setVal('x', shape.x);\n setVal('y', shape.y);\n setVal('width', shape.width);\n setVal('height', shape.height);\n return;\n }\n case 'arrow': {\n setVal('x1', shape.x1);\n setVal('y1', shape.y1);\n setVal('x2', shape.x2);\n setVal('y2', shape.y2);\n return;\n }\n case 'text': {\n setVal('x', shape.x);\n setVal('y', shape.y);\n return;\n }\n default:\n // freehand / highlight don't expose coordinate inputs (they're\n // pointer-only per ). The mount layer hides the row\n // before reaching this branch; keeping the case exhaustive\n // means adding a new keyboard-placeable kind shows up as a\n // type error here.\n return;\n }\n }\n\n function onAnyInputChange(): void {\n if (!activeShape || !activeKind) return;\n const edit = readCurrentEdit(activeShape);\n if (!edit) return;\n const updated = applyCoordEdit(activeShape, edit);\n if (updated === activeShape) return;\n activeShape = updated;\n options.onShapeChanged(updated);\n }\n\n function readCurrentEdit(shape: Shape): ShapeCoordEdit | null {\n const num = (id: string): number => {\n const el = inputs.get(id);\n if (!el) return Number.NaN;\n return el.valueAsNumber;\n };\n switch (shape.kind) {\n case 'rect':\n case 'ellipse': {\n const x = Math.round(num('x'));\n const y = Math.round(num('y'));\n const width = Math.round(num('width'));\n const height = Math.round(num('height'));\n if (![x, y, width, height].every(Number.isFinite)) return null;\n return { kind: shape.kind, x, y, width, height };\n }\n case 'arrow': {\n const x1 = Math.round(num('x1'));\n const y1 = Math.round(num('y1'));\n const x2 = Math.round(num('x2'));\n const y2 = Math.round(num('y2'));\n if (![x1, y1, x2, y2].every(Number.isFinite)) return null;\n return { kind: 'arrow', x1, y1, x2, y2 };\n }\n case 'text': {\n const x = Math.round(num('x'));\n const y = Math.round(num('y'));\n if (![x, y].every(Number.isFinite)) return null;\n return { kind: 'text', x, y };\n }\n default:\n return null;\n }\n }\n\n return {\n container,\n updateForShape(shape): void {\n if (!shape) {\n activeShape = null;\n activeKind = null;\n container.hidden = true;\n container.replaceChildren();\n inputs.clear();\n return;\n }\n // Freehand / highlight aren't keyboard-placeable; their\n // selection still works (Delete to remove; arrow keys to\n // nudge), but no coordinate inputs are shown.\n if (shape.kind === 'freehand' || shape.kind === 'highlight') {\n activeShape = shape;\n activeKind = null;\n container.hidden = true;\n container.replaceChildren();\n inputs.clear();\n return;\n }\n activeShape = shape;\n if (activeKind !== shape.kind) {\n rebuildFor(shape.kind);\n }\n syncValuesFromShape(shape);\n container.hidden = false;\n },\n destroy(): void {\n container.replaceChildren();\n inputs.clear();\n container.remove();\n },\n };\n}\n\nfunction fieldsFor(kind: 'rect' | 'ellipse' | 'arrow' | 'text'): ReadonlyArray<FieldSpec> {\n switch (kind) {\n case 'rect':\n case 'ellipse':\n return RECT_FIELDS;\n case 'arrow':\n return ARROW_FIELDS;\n case 'text':\n return TEXT_FIELDS;\n }\n}\n\n/**\n * Apply an edit produced by the inputs to the shape it came from.\n * Exported so the mount layer can compose it with its own clamping\n * before writing to the store. The function is total over the shape\n * union so the caller doesn't need to re-narrow.\n */\nexport function applyCoordEdit(shape: Shape, edit: ShapeCoordEdit): Shape {\n switch (shape.kind) {\n case 'rect': {\n if (edit.kind !== 'rect') return shape;\n const next: RectShape = {\n ...shape,\n x: edit.x,\n y: edit.y,\n width: edit.width,\n height: edit.height,\n };\n return next;\n }\n case 'ellipse': {\n if (edit.kind !== 'ellipse') return shape;\n const next: EllipseShape = {\n ...shape,\n x: edit.x,\n y: edit.y,\n width: edit.width,\n height: edit.height,\n };\n return next;\n }\n case 'arrow': {\n if (edit.kind !== 'arrow') return shape;\n const next: ArrowShape = { ...shape, x1: edit.x1, y1: edit.y1, x2: edit.x2, y2: edit.y2 };\n return next;\n }\n case 'text': {\n if (edit.kind !== 'text') return shape;\n const next: TextShape = { ...shape, x: edit.x, y: edit.y };\n return next;\n }\n default:\n return shape;\n }\n}\n","/**\n * Build the annotation panel: tool toolbar on the left, style\n * controls on the right. Mirrors the layout-rhythm conventions the\n * other Phase 2 panels (rotate, flip, resize) established — flat\n * row(s) of controls under `.kalotyp-util-main`.\n */\n\nimport {\n type AnnotateTool,\n type StylePalette,\n isKeyboardPlaceableKind,\n} from '@magicpages/kalotyp-core';\nimport { icon } from '../../icons.js';\n\nexport interface AnnotatePanelOptions {\n readonly initialTool: AnnotateTool;\n readonly initialStyle: StylePalette;\n readonly canDelete: boolean;\n /**\n * Where the per-selection coordinate-input row mounts. The mount layer owns the row's lifecycle and just\n * needs the panel to expose the slot so the inputs render in the\n * panel rhythm rather than as a free-floating bar.\n */\n readonly coordInputs: HTMLElement;\n onSelectTool(tool: AnnotateTool): void;\n onColorChange(color: string): void;\n onStrokeWidthChange(width: number): void;\n onDeleteSelected(): void;\n /**\n * Insert the active drawing tool's default shape at image centre\n * (Phase 6.3). The panel disables the button when the active tool\n * is `select`, `freehand`, or `highlight` — see\n * `isKeyboardPlaceableKind` in core.\n */\n onInsertAtCenter(): void;\n}\n\nexport interface AnnotatePanel {\n readonly container: HTMLDivElement;\n readonly toolButtons: ReadonlyMap<AnnotateTool, HTMLButtonElement>;\n readonly hexInput: HTMLInputElement;\n readonly colorSwatches: ReadonlyArray<HTMLButtonElement>;\n readonly strokeRange: HTMLInputElement;\n readonly deleteButton: HTMLButtonElement;\n readonly insertButton: HTMLButtonElement;\n setActiveTool(tool: AnnotateTool): void;\n setStyle(style: StylePalette): void;\n setCanDelete(canDelete: boolean): void;\n}\n\n/**\n * Tool icons. Sourced from Lucide via the shared icons module so the\n * whole editor reads as one icon family (history controls, lock\n * toggle, annotation tools). The select cursor is filled to match\n * the conventional pointer-arrow visual; the others are stroked\n * outlines.\n */\nconst TOOL_DEFS: ReadonlyArray<{ id: AnnotateTool; label: string; icon: string }> = [\n {\n id: 'select',\n label: 'Select',\n icon: icon('select', { fill: 'currentColor', 'stroke-width': 1 }),\n },\n { id: 'text', label: 'Text', icon: icon('text') },\n { id: 'rect', label: 'Rectangle', icon: icon('rect') },\n { id: 'ellipse', label: 'Ellipse', icon: icon('ellipse') },\n { id: 'arrow', label: 'Arrow', icon: icon('arrow') },\n { id: 'freehand', label: 'Freehand', icon: icon('freehand') },\n { id: 'highlight', label: 'Highlight', icon: icon('highlight') },\n];\n\n/**\n * Compact preset swatch palette. Six colours covering bright (red,\n * yellow, green, blue) plus white and black for outlines on either\n * background. The custom-colour `<input type=\"color\">` lets the user\n * override; the swatches just save them a click for the common cases.\n */\nconst PRESET_COLORS: readonly string[] = [\n '#ff3b30',\n '#ffcc00',\n '#34c759',\n '#007aff',\n '#ffffff',\n '#000000',\n];\n\nexport function buildAnnotatePanel(options: AnnotatePanelOptions): AnnotatePanel {\n const container = document.createElement('div');\n container.className = 'kalotyp-annotate-panel';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Annotate');\n\n // ----- Tool toolbar -----\n const toolbar = document.createElement('div');\n toolbar.className = 'kalotyp-annotate-toolbar';\n toolbar.setAttribute('role', 'toolbar');\n toolbar.setAttribute('aria-label', 'Annotation tools');\n\n const toolButtons = new Map<AnnotateTool, HTMLButtonElement>();\n for (const def of TOOL_DEFS) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-annotate-tool';\n button.dataset.tool = def.id;\n button.setAttribute('aria-label', def.label);\n button.title = def.label;\n button.setAttribute('aria-pressed', def.id === options.initialTool ? 'true' : 'false');\n // The icon string is inline SVG markup (from Lucide via the\n // shared icons module). Use innerHTML so the SVG actually\n // parses and renders — `textContent` would print the markup as\n // literal text inside the button.\n button.innerHTML = def.icon;\n button.addEventListener('click', () => options.onSelectTool(def.id));\n toolbar.appendChild(button);\n toolButtons.set(def.id, button);\n }\n\n // ----- Style controls (color + stroke) -----\n const styleRow = document.createElement('div');\n styleRow.className = 'kalotyp-annotate-style-row';\n\n const swatches: HTMLButtonElement[] = [];\n const swatchGroup = document.createElement('div');\n swatchGroup.className = 'kalotyp-annotate-swatches';\n swatchGroup.setAttribute('role', 'radiogroup');\n swatchGroup.setAttribute('aria-label', 'Color');\n for (const color of PRESET_COLORS) {\n const swatch = document.createElement('button');\n swatch.type = 'button';\n swatch.className = 'kalotyp-annotate-swatch';\n swatch.setAttribute('role', 'radio');\n swatch.setAttribute('aria-label', `Use color ${color}`);\n swatch.dataset.color = color;\n swatch.style.setProperty('--kalotyp-swatch', color);\n swatch.addEventListener('click', () => options.onColorChange(color));\n swatchGroup.appendChild(swatch);\n swatches.push(swatch);\n }\n\n // Single hex code input. The native `<input type=\"color\">` was\n // dropped (Phase 6.6 polish) — it duplicated the swatches' role\n // and crowded the row. The hex input is the keyboard-accessible\n // path; swatches cover the common-colour quick-pick path. Power\n // users with a custom palette type the hex.\n let lastValidHex = normaliseColorForInput(options.initialStyle.color);\n const hexInput = document.createElement('input');\n hexInput.type = 'text';\n hexInput.className = 'kalotyp-annotate-hex';\n hexInput.value = lastValidHex;\n hexInput.maxLength = 7;\n hexInput.spellcheck = false;\n hexInput.autocomplete = 'off';\n hexInput.setAttribute('aria-label', 'Color hex code');\n hexInput.setAttribute('placeholder', '#000000');\n hexInput.style.setProperty('--kalotyp-hex-swatch', lastValidHex);\n hexInput.addEventListener('change', () => {\n const value = hexInput.value.trim();\n const normalised = normaliseHexInput(value);\n if (normalised) {\n hexInput.value = normalised;\n lastValidHex = normalised;\n hexInput.style.setProperty('--kalotyp-hex-swatch', normalised);\n options.onColorChange(normalised);\n } else {\n // Restore to the last valid colour. The colour swatches and\n // state still reflect the last accepted value; the input\n // visibly resets so the user sees their bad input cleared.\n hexInput.value = lastValidHex;\n }\n });\n\n const strokeLabel = document.createElement('label');\n strokeLabel.className = 'kalotyp-annotate-stroke-label';\n strokeLabel.textContent = 'Width';\n\n const strokeRange = document.createElement('input');\n strokeRange.type = 'range';\n strokeRange.className = 'kalotyp-annotate-stroke';\n strokeRange.min = '1';\n strokeRange.max = '40';\n strokeRange.step = '1';\n strokeRange.value = String(options.initialStyle.strokeWidth);\n strokeRange.setAttribute('aria-label', 'Stroke width');\n strokeRange.addEventListener('change', () =>\n options.onStrokeWidthChange(strokeRange.valueAsNumber),\n );\n\n const deleteButton = document.createElement('button');\n deleteButton.type = 'button';\n deleteButton.className = 'kalotyp-annotate-delete';\n deleteButton.innerHTML = `${icon('delete')}<span>Delete</span>`;\n deleteButton.setAttribute('aria-label', 'Delete selected annotation');\n deleteButton.title = 'Delete (Del)';\n deleteButton.disabled = !options.canDelete;\n deleteButton.addEventListener('click', () => options.onDeleteSelected());\n\n // Insert-at-centre button. The keyboard-only\n // path for placing a shape: pick a tool, press this, get a default\n // shape at image centre, then position via the coordinate inputs.\n // Pointer users can ignore it — the canvas drag still works for\n // them. The button is disabled for tools that can't be placed\n // without a path (freehand, highlight) and for `select`.\n const insertButton = document.createElement('button');\n insertButton.type = 'button';\n insertButton.className = 'kalotyp-annotate-insert';\n insertButton.innerHTML = `${icon('plus')}<span>Insert at centre</span>`;\n insertButton.setAttribute('aria-label', 'Insert annotation at image centre');\n insertButton.title = 'Insert at centre';\n insertButton.disabled = !canInsertForTool(options.initialTool);\n insertButton.addEventListener('click', () => options.onInsertAtCenter());\n\n styleRow.appendChild(swatchGroup);\n styleRow.appendChild(hexInput);\n styleRow.appendChild(strokeLabel);\n styleRow.appendChild(strokeRange);\n styleRow.appendChild(insertButton);\n styleRow.appendChild(deleteButton);\n\n container.appendChild(toolbar);\n container.appendChild(styleRow);\n // Coordinate-input slot. The mount layer\n // appends the inputs into this slot after constructing the panel,\n // so the panel doesn't need to know per-shape geometry. The slot\n // sits below the style row inside the panel container so a\n // keyboard user finds it via natural Tab order.\n container.appendChild(options.coordInputs);\n\n function setActiveTool(tool: AnnotateTool): void {\n for (const [id, button] of toolButtons) {\n button.setAttribute('aria-pressed', id === tool ? 'true' : 'false');\n }\n insertButton.disabled = !canInsertForTool(tool);\n }\n\n function setStyle(style: StylePalette): void {\n const targetColor = normaliseColorForInput(style.color);\n if (hexInput.value.toLowerCase() !== targetColor.toLowerCase()) hexInput.value = targetColor;\n lastValidHex = targetColor;\n hexInput.style.setProperty('--kalotyp-hex-swatch', targetColor);\n if (strokeRange.valueAsNumber !== style.strokeWidth) {\n strokeRange.value = String(style.strokeWidth);\n }\n for (const swatch of swatches) {\n const matches = swatch.dataset.color?.toLowerCase() === style.color.toLowerCase();\n swatch.setAttribute('aria-checked', matches ? 'true' : 'false');\n }\n }\n\n function setCanDelete(canDelete: boolean): void {\n deleteButton.disabled = !canDelete;\n }\n\n setStyle(options.initialStyle);\n\n return {\n container,\n toolButtons,\n hexInput,\n colorSwatches: swatches,\n strokeRange,\n deleteButton,\n insertButton,\n setActiveTool,\n setStyle,\n setCanDelete,\n };\n}\n\n/**\n * Whether the active tool exposes a default-at-centre keyboard\n * placement path. The toolbar's `select` button isn't a drawing\n * tool, and `freehand` / `highlight` are gestural — neither has a\n * meaningful \"default shape\" to place., Phase 6.3.\n */\nfunction canInsertForTool(tool: AnnotateTool): boolean {\n if (tool === 'select') return false;\n return isKeyboardPlaceableKind(tool);\n}\n\n/**\n * `<input type=\"color\">` only accepts `#rrggbb`. Our default palette\n * stores colours in the same form, but state writes from elsewhere\n * (e.g. the highlight default) might use rgba — those just leave the\n * native picker showing the previous valid colour, which is fine.\n */\nfunction normaliseColorForInput(color: string): string {\n if (/^#[0-9a-fA-F]{6}$/.test(color)) return color;\n if (/^#[0-9a-fA-F]{3}$/.test(color)) {\n // Expand short hex to full hex so the input accepts it.\n const r = color[1];\n const g = color[2];\n const b = color[3];\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return '#000000';\n}\n\n/**\n * Accept hex codes the user types into the keyboard-accessible\n * colour input. Tolerant of upper/lower case and\n * missing `#` prefix; returns the canonical `#rrggbb` form for\n * accepted input or `null` if the input isn't a valid hex code.\n */\nfunction normaliseHexInput(value: string): string | null {\n const cleaned = value.startsWith('#') ? value.slice(1) : value;\n if (/^[0-9a-fA-F]{6}$/.test(cleaned)) return `#${cleaned.toLowerCase()}`;\n if (/^[0-9a-fA-F]{3}$/.test(cleaned)) {\n const r = cleaned[0];\n const g = cleaned[1];\n const b = cleaned[2];\n return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();\n }\n return null;\n}\n","/**\n * Pointer-drag helper. Same shape as the crop plugin's\n * `attachPointerDrag` (interaction.ts), kept local here so the\n * annotation tool gestures can layer their own per-gesture state on\n * top without coupling to the crop module.\n *\n * The factory is invoked on `pointerdown`. It must return a\n * `DragHandlers` object whose three callbacks describe the gesture's\n * lifecycle: `onMove(point)` per coalesced animation frame,\n * `onCommit()` once on `pointerup` (after a final drained move), and\n * `onCancel()` once on `pointercancel`.\n *\n * The factory may return `null` to refuse the gesture — useful when\n * a hit-area pointerdown only sometimes starts a drag (e.g. the\n * select tool doesn't drag if no shape is under the pointer).\n */\n\n/**\n * Per-frame point payload. Includes `shiftKey` so tool gestures can\n * apply axis / square / circle constraints while the modifier is\n * held — the standard image-editor convention (Figma, Sketch,\n * Photoshop). The flag reflects shift-state at the most\n * recent pointer event, so releasing/re-pressing shift mid-drag\n * toggles the constraint live.\n */\nexport interface DragPoint {\n readonly clientX: number;\n readonly clientY: number;\n readonly shiftKey: boolean;\n}\n\nexport interface DragHandlers {\n onMove(point: DragPoint): void;\n onCommit(): void;\n onCancel(): void;\n}\n\nexport function attachPointerDrag(\n element: HTMLElement,\n factory: (start: PointerEvent) => DragHandlers | null,\n): () => void {\n const onPointerDown = (event: PointerEvent): void => {\n if (event.button !== 0) return;\n const handlers = factory(event);\n if (!handlers) return;\n event.preventDefault();\n event.stopPropagation();\n\n try {\n element.setPointerCapture(event.pointerId);\n } catch {\n // Synthetic events (test runners, accessibility tools) sometimes\n // present a pointer id the browser can't resolve. Move/up events\n // still flow through the listeners we attach below; capture is a\n // best-effort affordance for OS-level cross-element drag.\n }\n\n let pendingPoint: DragPoint | undefined;\n let rafScheduled = false;\n // Track the latest shift state so a key-up/key-down between\n // pointer moves still flushes a frame with the new constraint.\n let lastShiftKey = event.shiftKey;\n\n const flush = (): void => {\n rafScheduled = false;\n if (!pendingPoint) return;\n const point = pendingPoint;\n pendingPoint = undefined;\n handlers.onMove(point);\n };\n\n const schedule = (point: DragPoint): void => {\n pendingPoint = point;\n if (!rafScheduled) {\n rafScheduled = true;\n requestAnimationFrame(flush);\n }\n };\n\n const onPointerMove = (moveEvent: PointerEvent): void => {\n if (moveEvent.pointerId !== event.pointerId) return;\n lastShiftKey = moveEvent.shiftKey;\n schedule({\n clientX: moveEvent.clientX,\n clientY: moveEvent.clientY,\n shiftKey: moveEvent.shiftKey,\n });\n };\n\n // Re-apply the gesture with the new constraint when the user\n // toggles shift mid-drag without moving the pointer.\n const onKeyToggle = (keyEvent: KeyboardEvent): void => {\n if (keyEvent.key !== 'Shift') return;\n if (keyEvent.shiftKey === lastShiftKey) return;\n lastShiftKey = keyEvent.shiftKey;\n // Use the last committed/pending pointer position; if neither\n // exists yet (no pointermove since pointerdown), use the\n // pointerdown coords.\n const last = pendingPoint;\n const x = last?.clientX ?? event.clientX;\n const y = last?.clientY ?? event.clientY;\n schedule({ clientX: x, clientY: y, shiftKey: keyEvent.shiftKey });\n };\n\n const finish = (committed: boolean): void => {\n element.removeEventListener('pointermove', onPointerMove);\n element.removeEventListener('pointerup', onPointerUp);\n element.removeEventListener('pointercancel', onPointerCancel);\n window.removeEventListener('keydown', onKeyToggle);\n window.removeEventListener('keyup', onKeyToggle);\n try {\n element.releasePointerCapture(event.pointerId);\n } catch {\n // Already released or never captured; nothing to do.\n }\n if (pendingPoint) {\n const final = pendingPoint;\n pendingPoint = undefined;\n handlers.onMove(final);\n }\n if (committed) handlers.onCommit();\n else handlers.onCancel();\n };\n\n const onPointerUp = (upEvent: PointerEvent): void => {\n if (upEvent.pointerId !== event.pointerId) return;\n finish(true);\n };\n const onPointerCancel = (cancelEvent: PointerEvent): void => {\n if (cancelEvent.pointerId !== event.pointerId) return;\n finish(false);\n };\n\n element.addEventListener('pointermove', onPointerMove);\n element.addEventListener('pointerup', onPointerUp);\n element.addEventListener('pointercancel', onPointerCancel);\n window.addEventListener('keydown', onKeyToggle);\n window.addEventListener('keyup', onKeyToggle);\n };\n\n element.addEventListener('pointerdown', onPointerDown);\n return () => element.removeEventListener('pointerdown', onPointerDown);\n}\n\n/** Convert a client-space point to the bounding-rect-local coords of `element`. */\nexport function clientToElement(\n element: HTMLElement,\n clientX: number,\n clientY: number,\n): { x: number; y: number } {\n const rect = element.getBoundingClientRect();\n return { x: clientX - rect.left, y: clientY - rect.top };\n}\n","/**\n * Render helpers for the annotation plugin's three canvas layers.\n *\n * - `paintImageLayer`: paints the upstream-baked source onto the\n * bottom canvas. Called once on mount and on stage resize.\n * - `paintShapesLayer`: paints every committed shape onto the middle\n * canvas. Called when the shape list, selection, or viewport\n * changes.\n * - `paintLiveLayer`: paints whatever the in-progress gesture wants\n * on top — a draft shape during a drag, a marquee, etc. Called\n * per frame during a gesture.\n *\n * All three reuse the same DPR-aware canvas-sizing helper to keep the\n * pixel grids consistent. Shapes are rendered through `paintShape`\n * from the core `bake.ts` module so the live preview is byte-equal to\n * the bake output (per shape).\n */\n\nimport { type Shape, type SourceImage, type Viewport, paintShape } from '@magicpages/kalotyp-core';\n\n/**\n * Resize the canvas's backing store to the stage CSS pixels × DPR\n * and return its 2D context already scaled into CSS-pixel space.\n * Returns `null` if the context is unavailable (jsdom path).\n */\nfunction prepareCanvas(\n canvas: HTMLCanvasElement,\n stageWidth: number,\n stageHeight: number,\n): CanvasRenderingContext2D | null {\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const targetW = Math.max(1, Math.round(stageWidth * dpr));\n const targetH = Math.max(1, Math.round(stageHeight * dpr));\n if (canvas.width !== targetW) canvas.width = targetW;\n if (canvas.height !== targetH) canvas.height = targetH;\n canvas.style.width = `${stageWidth}px`;\n canvas.style.height = `${stageHeight}px`;\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, stageWidth, stageHeight);\n return ctx;\n}\n\nexport function paintImageLayer(\n canvas: HTMLCanvasElement,\n source: SourceImage,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const ctx = prepareCanvas(canvas, stageWidth, stageHeight);\n if (!ctx) return;\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(\n source.bitmap,\n viewport.displayRect.x,\n viewport.displayRect.y,\n viewport.displayRect.width,\n viewport.displayRect.height,\n );\n}\n\n/**\n * Paint a list of shapes by setting up an image-space coordinate\n * frame and calling the shared `paintShape` for each. The image-space\n * frame is established by translating to the displayRect's origin and\n * scaling by the viewport scale; the shapes' image-space coordinates\n * then land at the right display pixels for free.\n */\nexport function paintShapesLayer(\n canvas: HTMLCanvasElement,\n shapes: ReadonlyArray<Shape>,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const ctx = prepareCanvas(canvas, stageWidth, stageHeight);\n if (!ctx) return;\n if (shapes.length === 0) return;\n ctx.save();\n ctx.translate(viewport.displayRect.x, viewport.displayRect.y);\n ctx.scale(viewport.scale, viewport.scale);\n for (const shape of shapes) {\n paintShape(ctx, shape);\n }\n ctx.restore();\n}\n\n/**\n * Paint a single in-progress shape on the live canvas. Same\n * coordinate setup as the shapes layer; passing `null` clears the\n * canvas without drawing — useful when a gesture ends.\n */\nexport function paintLiveLayer(\n canvas: HTMLCanvasElement,\n shape: Shape | null,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const ctx = prepareCanvas(canvas, stageWidth, stageHeight);\n if (!ctx) return;\n if (shape === null) return;\n ctx.save();\n ctx.translate(viewport.displayRect.x, viewport.displayRect.y);\n ctx.scale(viewport.scale, viewport.scale);\n paintShape(ctx, shape);\n ctx.restore();\n}\n\n/**\n * Paint a selection-marquee rectangle (image-space) on the live\n * canvas. Distinct from `paintLiveLayer` because the marquee uses\n * dashed strokes + a faint fill — visual conventions for \"I am\n * marquee-selecting\" — that don't belong to any committed shape.\n */\nexport function paintMarqueeLayer(\n canvas: HTMLCanvasElement,\n marqueeImage: { x: number; y: number; width: number; height: number } | null,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const ctx = prepareCanvas(canvas, stageWidth, stageHeight);\n if (!ctx) return;\n if (!marqueeImage) return;\n // Translate to display space (no scale — drawing in display pixels\n // so the dash pattern stays a constant visual width regardless of\n // the upstream image's pixel density).\n const dx = viewport.displayRect.x + marqueeImage.x * viewport.scale;\n const dy = viewport.displayRect.y + marqueeImage.y * viewport.scale;\n let dw = marqueeImage.width * viewport.scale;\n let dh = marqueeImage.height * viewport.scale;\n // Negative-extent drag: flip the box for rendering so we always\n // pass non-negative dimensions to fillRect/strokeRect.\n let drawX = dx;\n let drawY = dy;\n if (dw < 0) {\n drawX = dx + dw;\n dw = -dw;\n }\n if (dh < 0) {\n drawY = dy + dh;\n dh = -dh;\n }\n ctx.save();\n ctx.fillStyle = 'rgba(99, 102, 241, 0.12)';\n ctx.fillRect(drawX, drawY, dw, dh);\n ctx.strokeStyle = 'rgba(99, 102, 241, 0.95)';\n ctx.lineWidth = 1;\n ctx.setLineDash([4, 3]);\n ctx.strokeRect(drawX + 0.5, drawY + 0.5, dw - 1, dh - 1);\n ctx.restore();\n}\n","/**\n * Selection rendering and per-handle resize gesture for annotation\n * shapes. The selection layer is responsible for:\n *\n * - Rendering the bounding-box outline + 8 corner/edge handles when\n * a shape is selected. Arrows render with two endpoint handles\n * instead (no orthogonal extent to resize).\n * - Wiring each handle's pointerdown to a per-handle resize gesture\n * that mutates the shape's geometry as the user drags.\n * - Cleaning up handles when the selection clears.\n *\n * Handles are positioned in stage CSS pixels using the shared\n * `position-handles` helper that crop already uses for its 8\n * manipulators. Reusing that helper keeps the handle DOM consistent\n * with crop's so future styling can target both with a single rule.\n */\n\nimport {\n ALL_SELECTION_HANDLES,\n type AnnotateState,\n type ArrowShape,\n type Rect,\n type SelectionHandle,\n type Shape,\n type Viewport,\n boundingBoxOf,\n rectFromHandleDrag,\n replaceShape,\n selectionHandlePositions,\n} from '@magicpages/kalotyp-core';\nimport type { DragHandlers } from './pointer-drag.js';\nimport { attachPointerDrag } from './pointer-drag.js';\nimport type { ToolGestureContext } from './tools.js';\n\n/**\n * Build and own the selection-handle DOM. The returned object exposes\n * `update(shape, viewport)` so the caller can re-render handles after\n * any state change without rebuilding the DOM.\n */\nexport interface SelectionLayerOptions {\n readonly host: HTMLDivElement;\n readonly stageElement: HTMLElement;\n readonly toolContext: ToolGestureContext;\n /** Read the current letterbox viewport at gesture-start time. */\n getViewport(): Viewport;\n}\n\nexport interface SelectionLayer {\n /** Update the rendered handles to reflect the selected shape (or clear). */\n update(shape: Shape | null, viewport: Viewport): void;\n destroy(): void;\n}\n\nexport function buildSelectionLayer(options: SelectionLayerOptions): SelectionLayer {\n const { host, toolContext } = options;\n const handleEls = new Map<SelectionHandle, HTMLButtonElement>();\n const cleanups: Array<() => void> = [];\n\n // Eight box handles: only some are shown for non-rectangular shapes\n // (arrow uses just two endpoint handles).\n for (const direction of ALL_SELECTION_HANDLES) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-annotate-handle';\n button.dataset.direction = direction;\n button.setAttribute('aria-label', handleLabel(direction));\n // The handles are pointer-driven affordances. Keyboard users\n // resize via the coordinate inputs (Phase 6.3), which\n // expose every dimension the eight handles do without forcing\n // the user to Tab through eight stops per selection. Removing\n // the handles from the keyboard tab order keeps the Tab-walk\n // through the editor short and meaningful — every stop the user\n // lands on does something via keyboard.\n //\n // We keep `aria-label` so the accessibility tree carries a\n // meaningful name (axe rule \"button-name\") and assistive tools\n // that navigate by something other than Tab still see a label.\n // We don't add `aria-hidden` here because a focusable element\n // (even with tabIndex=-1, since it's still programmatically\n // focusable) inside an aria-hidden subtree trips axe rule\n // \"aria-hidden-focus\".\n button.tabIndex = -1;\n button.style.display = 'none';\n handleEls.set(direction, button);\n host.appendChild(button);\n\n cleanups.push(\n attachPointerDrag(button, (event) => startHandleResizeGesture(toolContext, direction, event)),\n );\n }\n\n function update(shape: Shape | null, viewport: Viewport): void {\n if (!shape) {\n hideAll(handleEls);\n return;\n }\n if (shape.kind === 'arrow') {\n // Arrows only get two handles, mapped to their endpoints.\n hideAll(handleEls);\n const tl = handleEls.get('tl');\n const br = handleEls.get('br');\n if (tl) {\n positionHandle(tl, imageToDisplay({ x: shape.x1, y: shape.y1 }, viewport));\n }\n if (br) {\n positionHandle(br, imageToDisplay({ x: shape.x2, y: shape.y2 }, viewport));\n }\n return;\n }\n const box = boundingBoxOf(shape);\n const handlePositions = selectionHandlePositions(box);\n for (const direction of ALL_SELECTION_HANDLES) {\n const handle = handleEls.get(direction);\n if (!handle) continue;\n positionHandle(handle, imageToDisplay(handlePositions[direction], viewport));\n }\n }\n\n function destroy(): void {\n for (const cleanup of cleanups) cleanup();\n for (const [, button] of handleEls) button.remove();\n handleEls.clear();\n }\n\n return { update, destroy };\n}\n\nfunction imageToDisplay(\n point: { x: number; y: number },\n viewport: Viewport,\n): { x: number; y: number } {\n return {\n x: viewport.displayRect.x + point.x * viewport.scale,\n y: viewport.displayRect.y + point.y * viewport.scale,\n };\n}\n\nfunction positionHandle(handle: HTMLButtonElement, displayPoint: { x: number; y: number }): void {\n handle.style.display = '';\n handle.style.left = `${displayPoint.x}px`;\n handle.style.top = `${displayPoint.y}px`;\n}\n\nfunction hideAll(handles: Map<SelectionHandle, HTMLButtonElement>): void {\n for (const [, button] of handles) {\n button.style.display = 'none';\n }\n}\n\n/**\n * Build the per-handle resize gesture. Snapshots the selected shape\n * at gesture start; each move computes the new image-space pointer\n * position and applies it via the appropriate per-shape mutator.\n *\n * Returns `null` if no shape is selected when the handle is pressed\n * (defensive — UI should hide handles in that case).\n */\nfunction startHandleResizeGesture(\n ctx: ToolGestureContext,\n direction: SelectionHandle,\n origin: PointerEvent,\n): DragHandlers | null {\n const state = ctx.store.get();\n const selected = state.shapes.find((shape) => shape.id === state.selectedId);\n if (!selected) return null;\n const initial = selected;\n\n // `origin` is part of the gesture signature even though we read all\n // needed state from the store at gesture start. It carries pointerId\n // / button info the pointer-drag helper consumes.\n void origin;\n return {\n onMove(point) {\n const image = ctx.toImageSpace(point);\n const next = applyHandleDrag(initial, direction, image);\n if (next) ctx.store.update((cur) => replaceShape(cur, next));\n },\n onCommit() {\n ctx.commit();\n },\n onCancel() {\n ctx.store.update((cur) => replaceShape(cur, initial));\n },\n };\n}\n\nfunction applyHandleDrag(\n shape: Shape,\n direction: SelectionHandle,\n image: { x: number; y: number },\n): Shape | null {\n switch (shape.kind) {\n case 'rect':\n case 'ellipse': {\n const box: Rect = {\n x: shape.x,\n y: shape.y,\n width: shape.width,\n height: shape.height,\n };\n const next = rectFromHandleDrag(box, direction, image);\n // We allow negative width/height during the drag; the next\n // commit normalises. Keeping it un-normalised mid-drag makes\n // the live preview match what the user is doing on screen.\n return { ...shape, x: next.x, y: next.y, width: next.width, height: next.height };\n }\n case 'arrow': {\n const arrow = shape as ArrowShape;\n // 'tl' handle maps to (x1,y1); 'br' maps to (x2,y2). The\n // selection layer hides the other six handles for arrows.\n if (direction === 'tl') return { ...arrow, x1: image.x, y1: image.y };\n if (direction === 'br') return { ...arrow, x2: image.x, y2: image.y };\n return shape;\n }\n case 'text': {\n // Text shapes resize by font size. The bottom-right handle\n // scales the font; other handles aren't shown for text in v1.\n if (direction !== 'br') return shape;\n const dx = image.x - shape.x;\n const dy = image.y - shape.y;\n // Scale so the dragged distance roughly matches the typed\n // box's diagonal. A min font size of 8 px stops the user from\n // making text invisible by accident.\n const newSize = Math.max(8, Math.round(Math.max(dx, dy) * 0.6));\n return { ...shape, fontSize: newSize };\n }\n case 'freehand':\n case 'highlight': {\n // Path shapes scale around their bounding-box centre by the\n // ratio between the initial and dragged box. Move the dragged\n // corner to the pointer; the other corners scale accordingly.\n const box = boundingBoxOf(shape);\n if (box.width === 0 || box.height === 0) return shape;\n const next = rectFromHandleDrag(box, direction, image);\n const scaleX = next.width / box.width;\n const scaleY = next.height / box.height;\n if (!Number.isFinite(scaleX) || !Number.isFinite(scaleY)) return shape;\n const points = shape.points.map((p) => ({\n x: next.x + (p.x - box.x) * scaleX,\n y: next.y + (p.y - box.y) * scaleY,\n }));\n return { ...shape, points };\n }\n }\n}\n\nfunction handleLabel(direction: SelectionHandle): string {\n switch (direction) {\n case 'tl':\n return 'Resize from top-left';\n case 'tr':\n return 'Resize from top-right';\n case 'bl':\n return 'Resize from bottom-left';\n case 'br':\n return 'Resize from bottom-right';\n case 't':\n return 'Resize from top';\n case 'r':\n return 'Resize from right';\n case 'b':\n return 'Resize from bottom';\n case 'l':\n return 'Resize from left';\n }\n}\n\n/**\n * Helper for the mount layer: which shape (if any) is selected, given\n * the current state? Keeps the lookup logic in one place — most\n * callers in `mount.ts` need it via the store subscription.\n */\nexport function selectedShapeOf(state: AnnotateState): Shape | null {\n if (state.selectedId === null) return null;\n return state.shapes.find((shape) => shape.id === state.selectedId) ?? null;\n}\n","/**\n * Build the layered DOM for the annotation plugin's stage. Three\n * canvases (image, committed shapes, live in-progress) plus three DOM\n * layers (a hit-area for pointer input, a handles layer for selection\n * resize handles, and a text-edit overlay) — / Phase 3\n * brief on stacked canvases.\n *\n * The element ordering follows the z-stack: image at the bottom,\n * shapes and live canvases above it, then the pointer hit-area, then\n * the handles layer (so handles can intercept pointerdowns before the\n * hit-area sees them), then the text overlay on top.\n *\n * The hit-area is the surface the tool / selection layer attaches its\n * pointerdown handlers to. It carries `touch-action: none` so the\n * browser doesn't hijack drags for scroll/pinch on touch devices.\n */\n\nexport interface AnnotateStageElements {\n readonly container: HTMLDivElement;\n readonly imageCanvas: HTMLCanvasElement;\n readonly shapesCanvas: HTMLCanvasElement;\n readonly liveCanvas: HTMLCanvasElement;\n readonly hitArea: HTMLDivElement;\n /** Holds selection handles (DOM buttons) when a shape is selected. */\n readonly handlesLayer: HTMLDivElement;\n /** Holds the inline text editor when text is being edited. */\n readonly textOverlay: HTMLDivElement;\n}\n\nexport function buildAnnotateStage(): AnnotateStageElements {\n const container = document.createElement('div');\n container.className = 'kalotyp-annotate-stage';\n\n const imageCanvas = document.createElement('canvas');\n imageCanvas.className = 'kalotyp-annotate-image';\n imageCanvas.setAttribute('aria-hidden', 'true');\n\n const shapesCanvas = document.createElement('canvas');\n shapesCanvas.className = 'kalotyp-annotate-shapes';\n shapesCanvas.setAttribute('aria-hidden', 'true');\n\n const liveCanvas = document.createElement('canvas');\n liveCanvas.className = 'kalotyp-annotate-live';\n liveCanvas.setAttribute('aria-hidden', 'true');\n\n const hitArea = document.createElement('div');\n hitArea.className = 'kalotyp-annotate-hit';\n hitArea.setAttribute('role', 'presentation');\n\n const handlesLayer = document.createElement('div');\n handlesLayer.className = 'kalotyp-annotate-handles';\n handlesLayer.setAttribute('role', 'group');\n handlesLayer.setAttribute('aria-label', 'Selected annotation');\n\n const textOverlay = document.createElement('div');\n textOverlay.className = 'kalotyp-annotate-text-overlay';\n\n container.appendChild(imageCanvas);\n container.appendChild(shapesCanvas);\n container.appendChild(liveCanvas);\n container.appendChild(hitArea);\n container.appendChild(handlesLayer);\n container.appendChild(textOverlay);\n\n return {\n container,\n imageCanvas,\n shapesCanvas,\n liveCanvas,\n hitArea,\n handlesLayer,\n textOverlay,\n };\n}\n","/**\n * Inline text editor for the text annotation tool.\n *\n * The editor renders a contenteditable `<div>` overlaid on the\n * annotation's image-space anchor. Using a `<div>` instead of a\n * `<textarea>` lets us match the canvas-side font and size precisely\n * (textareas restrict the visible padding/size combination on some\n * browsers). The element keeps a fixed width by default and grows\n * vertically with line breaks.\n *\n * Lifecycle:\n * - `open(shape, viewport)`: position the editor over the shape,\n * prefill its text, focus it. Each input event reports the new\n * text via `onInput`. Pressing Enter (without Shift) commits;\n * pressing Escape cancels.\n * - `close()`: hide the editor and blur it.\n *\n * The caller is responsible for committing the shape into the store\n * when the editor closes; the editor is presentational.\n */\n\nimport {\n SYSTEM_FONT_STACK,\n type SourceImage,\n type TextShape,\n type Viewport,\n} from '@magicpages/kalotyp-core';\n\nexport interface TextEditorOptions {\n readonly host: HTMLDivElement;\n onInput(text: string): void;\n onCommit(): void;\n onCancel(): void;\n}\n\nexport interface TextEditorHandle {\n open(shape: TextShape, viewport: Viewport, source: SourceImage): void;\n close(): void;\n destroy(): void;\n}\n\nexport function buildTextEditor(options: TextEditorOptions): TextEditorHandle {\n const editor = document.createElement('div');\n editor.className = 'kalotyp-annotate-text-editor';\n editor.setAttribute('contenteditable', 'true');\n editor.setAttribute('role', 'textbox');\n editor.setAttribute('aria-label', 'Annotation text');\n editor.spellcheck = false;\n editor.style.display = 'none';\n options.host.appendChild(editor);\n\n let activeShape: TextShape | null = null;\n\n const onInput = (): void => {\n options.onInput(editor.innerText);\n };\n\n const onKeyDown = (event: KeyboardEvent): void => {\n // Enter without modifiers commits; Shift+Enter inserts a newline.\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n event.stopPropagation();\n options.onCommit();\n return;\n }\n if (event.key === 'Escape') {\n // Stop propagation so the editor-level Esc-to-close handler\n // doesn't fire while the user is actively editing text.\n event.preventDefault();\n event.stopPropagation();\n options.onCancel();\n }\n };\n\n // Click outside the editor element commits the edit. Listening on\n // the host (the text-overlay div) means anywhere outside the editor\n // bubble closes it; we filter clicks on the editor itself to let\n // them pass through normally.\n const onPointerDownOutside = (event: PointerEvent): void => {\n if (activeShape === null) return;\n if (editor.contains(event.target as Node)) return;\n options.onCommit();\n };\n\n editor.addEventListener('input', onInput);\n editor.addEventListener('keydown', onKeyDown);\n document.addEventListener('pointerdown', onPointerDownOutside, true);\n\n return {\n open(shape, viewport, source): void {\n activeShape = shape;\n // Position in stage CSS pixels: image origin + image-space\n // anchor scaled by viewport.\n const left = viewport.displayRect.x + shape.x * viewport.scale;\n const top = viewport.displayRect.y + shape.y * viewport.scale;\n editor.style.display = '';\n editor.style.left = `${left}px`;\n editor.style.top = `${top}px`;\n editor.style.color = shape.color;\n editor.style.font = `${shape.fontSize * viewport.scale}px ${SYSTEM_FONT_STACK}`;\n editor.style.textAlign = shape.textAlign;\n // Align the editor's anchor to the shape's anchor for the\n // current `textAlign` so typing grows the box outward in the\n // same direction the rendered text would.\n editor.style.transformOrigin = transformOriginFor(shape.textAlign);\n // Constrain width so the user has room to type without the\n // editor sliding off the stage. The displayRect.width is the\n // image's painted width in CSS pixels.\n const maxWidth = Math.max(\n 100,\n viewport.displayRect.x + viewport.displayRect.width - left - 8,\n );\n editor.style.maxWidth = `${maxWidth}px`;\n editor.innerText = shape.text;\n // Defer focus so the layout pass settles before we move the\n // caret. Without this, Safari occasionally focuses but doesn't\n // place the caret.\n requestAnimationFrame(() => {\n editor.focus();\n // Place caret at end.\n const range = document.createRange();\n range.selectNodeContents(editor);\n range.collapse(false);\n const sel = window.getSelection();\n sel?.removeAllRanges();\n sel?.addRange(range);\n });\n // `source` is part of the API surface so the caller can pass it\n // through unconditionally; the position math doesn't need it\n // today but a future per-image-bound clamp would.\n void source;\n },\n close(): void {\n activeShape = null;\n editor.style.display = 'none';\n editor.blur();\n },\n destroy(): void {\n editor.removeEventListener('input', onInput);\n editor.removeEventListener('keydown', onKeyDown);\n document.removeEventListener('pointerdown', onPointerDownOutside, true);\n editor.remove();\n },\n };\n}\n\nfunction transformOriginFor(align: 'left' | 'center' | 'right'): string {\n switch (align) {\n case 'left':\n return 'top left';\n case 'center':\n return 'top center';\n case 'right':\n return 'top right';\n }\n}\n","/**\n * Drawing-tool gesture factories. Each function builds the\n * pointer-drag handlers for one tool (rect, ellipse, arrow, freehand,\n * highlight, plus body-move for the select tool). Text and select\n * dispatch are handled in `mount.ts` because they don't fit the\n * \"drag-to-create\" shape these factories codify.\n *\n * Each gesture:\n * 1. Mints a fresh shape id and creates an in-progress shape.\n * 2. On every coalesced pointermove, the shape's geometry is\n * updated and rendered into the live canvas (caller-supplied).\n * 3. On commit (`pointerup`), the shape is added to the store and\n * a `commit` event is emitted so the editor history snapshots.\n * A degenerate shape (zero-extent rect, single-tap freehand) is\n * dropped instead of committed — the user obviously didn't mean\n * to draw anything.\n */\n\nimport {\n type AnnotateState,\n type ArrowShape,\n type EllipseShape,\n type FreehandShape,\n HIGHLIGHT_DEFAULT_COLOR,\n HIGHLIGHT_DEFAULT_STROKE,\n type HighlightShape,\n type Point,\n type RectShape,\n type Shape,\n type Store,\n addShape,\n decimatePoints,\n mintShapeId,\n normaliseRectExtent,\n selectShape,\n} from '@magicpages/kalotyp-core';\nimport type { DragHandlers } from './pointer-drag.js';\n\n/**\n * Shift-modifier constraint helpers. Three flavours, all returning\n * the constrained image-space end-point:\n *\n * - `constrainSquare`: rect/ellipse drag uses the larger absolute\n * delta on both axes so the resulting box is a square (and thus\n * the inscribed ellipse is a circle).\n * - `constrainAxisOrDiagonal`: arrow drag snaps to the nearest of\n * eight directions (4 cardinal + 4 diagonal). Length matches the\n * user's pointer distance projected onto the chosen axis.\n * - `constrainStroke`: freehand/highlight strokes lock to whichever\n * axis the cursor moved further along, so a quick shift-drag\n * draws a straight horizontal or vertical line.\n *\n * Industry convention across Figma / Sketch / Photoshop / Pintura.\n */\nfunction constrainSquare(start: Point, end: Point): Point {\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n const size = Math.max(Math.abs(dx), Math.abs(dy));\n const sx = dx === 0 ? 1 : Math.sign(dx);\n const sy = dy === 0 ? 1 : Math.sign(dy);\n return { x: start.x + sx * size, y: start.y + sy * size };\n}\n\nfunction constrainAxisOrDiagonal(start: Point, end: Point): Point {\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n const len = Math.sqrt(dx * dx + dy * dy);\n if (len === 0) return start;\n // Snap angle to nearest 45° increment (8 compass directions).\n const angle = Math.atan2(dy, dx);\n const snapped = Math.round(angle / (Math.PI / 4)) * (Math.PI / 4);\n return { x: start.x + Math.cos(snapped) * len, y: start.y + Math.sin(snapped) * len };\n}\n\nfunction constrainStroke(start: Point, end: Point): Point {\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n if (Math.abs(dx) >= Math.abs(dy)) return { x: end.x, y: start.y };\n return { x: start.x, y: end.y };\n}\n\nexport interface ToolGestureContext {\n readonly store: Store<AnnotateState>;\n /** Project a raw client-space pointer to image-space pixels. */\n toImageSpace(clientPoint: { clientX: number; clientY: number }): Point;\n /** Update the live canvas with the in-progress shape. */\n setLiveShape(shape: Shape | null): void;\n /** Emit the editor's history-commit signal. */\n commit(): void;\n}\n\nexport function startRectGesture(ctx: ToolGestureContext, origin: PointerEvent): DragHandlers {\n const startImage = ctx.toImageSpace(origin);\n const state = ctx.store.get();\n const { id, nextShapeNumber } = mintShapeId(state);\n let lastImage = startImage;\n return {\n onMove(point) {\n const raw = ctx.toImageSpace(point);\n // Shift constrains to a square (and thus the inscribed ellipse\n // would be a circle for the ellipse tool).\n lastImage = point.shiftKey ? constrainSquare(startImage, raw) : raw;\n const draft: RectShape = {\n id,\n kind: 'rect',\n x: startImage.x,\n y: startImage.y,\n width: lastImage.x - startImage.x,\n height: lastImage.y - startImage.y,\n strokeColor: state.currentStyle.color,\n strokeWidth: state.currentStyle.strokeWidth,\n fillColor: state.currentStyle.fillColor,\n };\n ctx.setLiveShape(draft);\n },\n onCommit() {\n const extent = normaliseRectExtent({\n x: startImage.x,\n y: startImage.y,\n width: lastImage.x - startImage.x,\n height: lastImage.y - startImage.y,\n });\n ctx.setLiveShape(null);\n // Drop zero-extent gestures (a click that didn't drag).\n if (extent.width < 2 || extent.height < 2) return;\n const shape: RectShape = {\n id,\n kind: 'rect',\n ...extent,\n strokeColor: state.currentStyle.color,\n strokeWidth: state.currentStyle.strokeWidth,\n fillColor: state.currentStyle.fillColor,\n };\n ctx.store.update((current) => ({ ...addShape(current, shape), nextShapeNumber }));\n ctx.commit();\n },\n onCancel() {\n ctx.setLiveShape(null);\n },\n };\n}\n\nexport function startEllipseGesture(ctx: ToolGestureContext, origin: PointerEvent): DragHandlers {\n const startImage = ctx.toImageSpace(origin);\n const state = ctx.store.get();\n const { id, nextShapeNumber } = mintShapeId(state);\n let lastImage = startImage;\n return {\n onMove(point) {\n const raw = ctx.toImageSpace(point);\n // Shift constrains the bounding box to a square so the\n // inscribed ellipse becomes a circle.\n lastImage = point.shiftKey ? constrainSquare(startImage, raw) : raw;\n const draft: EllipseShape = {\n id,\n kind: 'ellipse',\n x: startImage.x,\n y: startImage.y,\n width: lastImage.x - startImage.x,\n height: lastImage.y - startImage.y,\n strokeColor: state.currentStyle.color,\n strokeWidth: state.currentStyle.strokeWidth,\n fillColor: state.currentStyle.fillColor,\n };\n ctx.setLiveShape(draft);\n },\n onCommit() {\n const extent = normaliseRectExtent({\n x: startImage.x,\n y: startImage.y,\n width: lastImage.x - startImage.x,\n height: lastImage.y - startImage.y,\n });\n ctx.setLiveShape(null);\n if (extent.width < 2 || extent.height < 2) return;\n const shape: EllipseShape = {\n id,\n kind: 'ellipse',\n ...extent,\n strokeColor: state.currentStyle.color,\n strokeWidth: state.currentStyle.strokeWidth,\n fillColor: state.currentStyle.fillColor,\n };\n ctx.store.update((current) => ({ ...addShape(current, shape), nextShapeNumber }));\n ctx.commit();\n },\n onCancel() {\n ctx.setLiveShape(null);\n },\n };\n}\n\nexport function startArrowGesture(ctx: ToolGestureContext, origin: PointerEvent): DragHandlers {\n const startImage = ctx.toImageSpace(origin);\n const state = ctx.store.get();\n const { id, nextShapeNumber } = mintShapeId(state);\n let lastImage = startImage;\n return {\n onMove(point) {\n const raw = ctx.toImageSpace(point);\n // Shift snaps arrow direction to the nearest 45° increment\n // (4 cardinal + 4 diagonal). Length follows the projection.\n lastImage = point.shiftKey ? constrainAxisOrDiagonal(startImage, raw) : raw;\n const draft: ArrowShape = {\n id,\n kind: 'arrow',\n x1: startImage.x,\n y1: startImage.y,\n x2: lastImage.x,\n y2: lastImage.y,\n color: state.currentStyle.color,\n strokeWidth: state.currentStyle.strokeWidth,\n };\n ctx.setLiveShape(draft);\n },\n onCommit() {\n ctx.setLiveShape(null);\n const dx = lastImage.x - startImage.x;\n const dy = lastImage.y - startImage.y;\n // Arrow needs at least a few image-space pixels of length to\n // be meaningful; otherwise it's a click.\n if (dx * dx + dy * dy < 16) return;\n const shape: ArrowShape = {\n id,\n kind: 'arrow',\n x1: startImage.x,\n y1: startImage.y,\n x2: lastImage.x,\n y2: lastImage.y,\n color: state.currentStyle.color,\n strokeWidth: state.currentStyle.strokeWidth,\n };\n ctx.store.update((current) => ({ ...addShape(current, shape), nextShapeNumber }));\n ctx.commit();\n },\n onCancel() {\n ctx.setLiveShape(null);\n },\n };\n}\n\nexport function startFreehandGesture(\n ctx: ToolGestureContext,\n origin: PointerEvent,\n options: { kind: 'freehand' | 'highlight' },\n): DragHandlers {\n const startImage = ctx.toImageSpace(origin);\n const state = ctx.store.get();\n const { id, nextShapeNumber } = mintShapeId(state);\n /**\n * Two parallel point streams:\n *\n * - `freePoints` — the natural freehand path, recorded every move\n * regardless of shift state. Used when shift is released so a\n * user can press-and-release shift mid-stroke without losing\n * earlier curvy segments.\n * - The render path is computed per-frame: while shift is held\n * we render a straight axis-locked line from `startImage` to\n * the projected end-point; otherwise we render the full\n * freehand path.\n *\n * On commit we choose: if shift was held at release, the persisted\n * shape is just the two endpoints; otherwise the decimated free path.\n */\n const freePoints: Point[] = [startImage];\n let lastWasShift = false;\n let lastConstrainedEnd: Point = startImage;\n const isHighlight = options.kind === 'highlight';\n const color = isHighlight ? HIGHLIGHT_DEFAULT_COLOR : state.currentStyle.color;\n const strokeWidth = isHighlight ? HIGHLIGHT_DEFAULT_STROKE : state.currentStyle.strokeWidth;\n\n function paint(points: ReadonlyArray<Point>): void {\n const draft: FreehandShape | HighlightShape = {\n id,\n kind: options.kind,\n points,\n color,\n strokeWidth,\n };\n ctx.setLiveShape(draft);\n }\n\n return {\n onMove(point) {\n const raw = ctx.toImageSpace(point);\n lastWasShift = point.shiftKey;\n if (point.shiftKey) {\n // Lock the stroke to a horizontal/vertical line from the\n // gesture's start to the current pointer projection.\n lastConstrainedEnd = constrainStroke(startImage, raw);\n paint([startImage, lastConstrainedEnd]);\n } else {\n freePoints.push(raw);\n paint(freePoints);\n }\n },\n onCommit() {\n ctx.setLiveShape(null);\n const finalPoints: ReadonlyArray<Point> = lastWasShift\n ? [startImage, lastConstrainedEnd]\n : decimatePoints(freePoints);\n if (finalPoints.length < 2) return;\n // Reject zero-length shift-clicks.\n if (finalPoints.length === 2) {\n const a = finalPoints[0];\n const b = finalPoints[1];\n if (a && b) {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n if (dx * dx + dy * dy < 4) return;\n }\n }\n const shape: FreehandShape | HighlightShape = {\n id,\n kind: options.kind,\n points: finalPoints,\n color,\n strokeWidth,\n };\n ctx.store.update((current) => ({ ...addShape(current, shape), nextShapeNumber }));\n ctx.commit();\n },\n onCancel() {\n ctx.setLiveShape(null);\n },\n };\n}\n\n/**\n * Body-drag (move) gesture used by the select tool when the user\n * presses on an already-selected shape and drags. Translates the\n * shape by the per-frame delta; commits on pointerup. The caller\n * supplies the `translate` and `replace` closures so this stays\n * decoupled from the store-write specifics.\n */\nexport function startBodyMoveGesture(\n ctx: ToolGestureContext,\n origin: PointerEvent,\n shapeId: string,\n initialShape: Shape,\n translate: (shape: Shape, dx: number, dy: number) => Shape,\n replace: (shape: Shape) => void,\n): DragHandlers {\n const startImage = ctx.toImageSpace(origin);\n ctx.store.update((current) => selectShape(current, shapeId));\n return {\n onMove(point) {\n const here = ctx.toImageSpace(point);\n const moved = translate(initialShape, here.x - startImage.x, here.y - startImage.y);\n replace(moved);\n },\n onCommit() {\n ctx.commit();\n },\n onCancel() {\n replace(initialShape);\n },\n };\n}\n","/**\n * Mount the annotation plugin's stage UI and wire up:\n * - the three layered canvases (image / shapes / live);\n * - the bottom panel (tool toolbar + style controls);\n * - pointer dispatch (drawing tools vs select tool);\n * - selection handles + per-handle resize gestures;\n * - the inline text editor;\n * - keyboard shortcuts (Delete/Backspace to remove the selected\n * shape; Esc to deselect).\n *\n * The mount keeps two pieces of derived state at module scope:\n *\n * - `viewport` — the current letterbox of the upstream-baked source\n * into the stage. Recomputed on every stage resize.\n * - `liveShape` — the in-progress shape during a draw or move\n * gesture. `null` when no gesture is active.\n */\n\nimport {\n type AnnotateState,\n type AnnotateTool,\n type Point,\n type Shape,\n type SourceImage,\n type Store,\n TEXT_DEFAULT_FONT_SIZE,\n type TextShape,\n type Viewport,\n type ViewportController,\n addShape,\n boundingBoxOf,\n computeViewport,\n createCenteredShape,\n deleteShape,\n isKeyboardPlaceableKind,\n mintShapeId,\n pickShape,\n pointDisplayToImage,\n replaceShape,\n selectShape,\n setActiveTool,\n setStyle,\n translateShape,\n} from '@magicpages/kalotyp-core';\nimport { buildCoordInputs } from './coord-inputs.js';\nimport { type AnnotatePanel, buildAnnotatePanel } from './panel.js';\nimport { type DragHandlers, attachPointerDrag, clientToElement } from './pointer-drag.js';\nimport { paintImageLayer, paintLiveLayer, paintMarqueeLayer, paintShapesLayer } from './render.js';\nimport { buildSelectionLayer, selectedShapeOf } from './selection.js';\nimport { buildAnnotateStage } from './stage.js';\nimport { buildTextEditor } from './text-editor.js';\nimport {\n type ToolGestureContext,\n startArrowGesture,\n startBodyMoveGesture,\n startEllipseGesture,\n startFreehandGesture,\n startRectGesture,\n} from './tools.js';\n\nconst STAGE_PADDING_PX = 32;\n\nexport interface MountAnnotateOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly store: Store<AnnotateState>;\n /** Editor-level zoom + pan controller. Optional in jsdom tests. */\n readonly viewport?: ViewportController;\n /** Called after each user-meaningful annotation mutation. */\n readonly onCommit?: () => void;\n /**\n * Optional live-region announcer. The annotate plugin\n * uses it for state changes that don't move keyboard focus —\n * notably Esc-deselect, where the screen reader would otherwise\n * have no cue that the selection went away.\n */\n readonly onAnnounce?: (message: string) => void;\n}\n\nexport interface MountAnnotateHandle {\n destroy(): void;\n}\n\nexport function mountAnnotateUtility(options: MountAnnotateOptions): MountAnnotateHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? (() => {});\n const announce = options.onAnnounce ?? (() => {});\n\n const stage = buildAnnotateStage();\n stageHost.appendChild(stage.container);\n\n let viewport: Viewport = computeViewport(\n { width: 1, height: 1, padding: STAGE_PADDING_PX },\n { width: source.width, height: source.height },\n );\n let liveShape: Shape | null = null;\n let liveMarquee: { x: number; y: number; width: number; height: number } | null = null;\n\n function recomputeViewport(): void {\n const rect = stage.container.getBoundingClientRect();\n const stageDims = { width: rect.width, height: rect.height, padding: STAGE_PADDING_PX };\n const imageSize = { width: source.width, height: source.height };\n viewport = controller\n ? controller.computeViewport(stageDims, imageSize)\n : computeViewport(stageDims, imageSize);\n }\n\n function paintAll(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n paintImageLayer(stage.imageCanvas, source, rect.width, rect.height, viewport);\n paintShapesLayer(stage.shapesCanvas, store.get().shapes, rect.width, rect.height, viewport);\n paintLiveLayer(stage.liveCanvas, liveShape, rect.width, rect.height, viewport);\n selectionLayer.update(selectedShapeOf(store.get()), viewport);\n }\n\n function paintShapes(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n paintShapesLayer(stage.shapesCanvas, store.get().shapes, rect.width, rect.height, viewport);\n }\n\n function paintLive(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n if (liveMarquee !== null) {\n paintMarqueeLayer(stage.liveCanvas, liveMarquee, rect.width, rect.height, viewport);\n } else {\n paintLiveLayer(stage.liveCanvas, liveShape, rect.width, rect.height, viewport);\n }\n }\n\n function setLiveShape(shape: Shape | null): void {\n liveShape = shape;\n liveMarquee = null;\n paintLive();\n }\n\n function setLiveMarquee(\n rect: { x: number; y: number; width: number; height: number } | null,\n ): void {\n liveMarquee = rect;\n liveShape = null;\n paintLive();\n }\n\n // Project a raw client-space pointer to the upstream-baked source's\n // image-space pixels. Used by every gesture factory.\n function toImageSpace(point: { clientX: number; clientY: number }): Point {\n const stagePoint = clientToElement(stage.container, point.clientX, point.clientY);\n return pointDisplayToImage(stagePoint, viewport);\n }\n\n const toolContext: ToolGestureContext = {\n store,\n toImageSpace,\n setLiveShape,\n commit,\n };\n\n // ----- Selection layer (handles + per-handle resize) -----\n const selectionLayer = buildSelectionLayer({\n host: stage.handlesLayer,\n stageElement: stage.container,\n toolContext,\n getViewport: () => viewport,\n });\n\n // ----- Inline text editor -----\n const textEditor = buildTextEditor({\n host: stage.textOverlay,\n onInput: (text) => {\n const selected = selectedShapeOf(store.get());\n if (!selected || selected.kind !== 'text') return;\n store.update((current) => replaceShape(current, { ...selected, text }));\n },\n onCommit: () => {\n const selected = selectedShapeOf(store.get());\n textEditor.close();\n // Drop the text shape entirely if the user committed an empty\n // string — a blank text shape has no representation.\n if (selected?.kind === 'text' && selected.text.trim().length === 0) {\n store.update((current) => deleteShape(current, selected.id));\n }\n commit();\n // Switch back to select so subsequent clicks pick existing\n // shapes rather than spawning a fresh empty text.\n store.update((current) => setActiveTool(current, 'select'));\n },\n onCancel: () => {\n const selected = selectedShapeOf(store.get());\n textEditor.close();\n // If the user cancelled an empty text (the click that created\n // it), drop the shape so we don't pollute the list.\n if (selected?.kind === 'text' && selected.text.length === 0) {\n store.update((current) => deleteShape(current, selected.id));\n }\n store.update((current) => setActiveTool(current, 'select'));\n },\n });\n\n // ----- Pointer dispatch on the hit area -----\n const removeHitDrag = attachPointerDrag(stage.hitArea, (event) => {\n const state = store.get();\n switch (state.activeTool) {\n case 'select':\n return startSelectGesture(state, event);\n case 'rect':\n return startRectGesture(toolContext, event);\n case 'ellipse':\n return startEllipseGesture(toolContext, event);\n case 'arrow':\n return startArrowGesture(toolContext, event);\n case 'freehand':\n return startFreehandGesture(toolContext, event, { kind: 'freehand' });\n case 'highlight':\n return startFreehandGesture(toolContext, event, { kind: 'highlight' });\n case 'text': {\n // Text doesn't use the drag pipeline — handle it inline.\n startTextGesture(event);\n return null;\n }\n default:\n return null;\n }\n });\n\n function startSelectGesture(state: AnnotateState, event: PointerEvent): DragHandlers | null {\n const image = toImageSpace(event);\n const picked = pickShape(state.shapes, image);\n if (!picked) {\n // Empty space → start a marquee. The marquee renders as a\n // dashed rectangle on the live canvas; on commit, it picks\n // the topmost shape whose bounding box intersects the\n // marquee. A no-move tap (start === end) deselects.\n return startMarqueeGesture(event);\n }\n // If the picked shape is already selected, drag it. Otherwise\n // select it first; the same drag continues through the move.\n if (state.selectedId !== picked.id) {\n store.update((current) => selectShape(current, picked.id));\n }\n return startBodyMoveGesture(toolContext, event, picked.id, picked, translateShape, (next) =>\n store.update((current) => replaceShape(current, next)),\n );\n }\n\n function startMarqueeGesture(event: PointerEvent): DragHandlers {\n const startImage = toImageSpace(event);\n let lastImage = startImage;\n return {\n onMove(point) {\n lastImage = toImageSpace(point);\n setLiveMarquee({\n x: startImage.x,\n y: startImage.y,\n width: lastImage.x - startImage.x,\n height: lastImage.y - startImage.y,\n });\n },\n onCommit() {\n setLiveMarquee(null);\n // No-move click: just deselect.\n const dx = lastImage.x - startImage.x;\n const dy = lastImage.y - startImage.y;\n if (Math.abs(dx) < 2 && Math.abs(dy) < 2) {\n store.update((current) => selectShape(current, null));\n return;\n }\n const marquee = normaliseExtent({\n x: startImage.x,\n y: startImage.y,\n width: dx,\n height: dy,\n });\n const hit = topmostShapeIntersectingMarquee(store.get().shapes, marquee);\n store.update((current) => selectShape(current, hit?.id ?? null));\n },\n onCancel() {\n setLiveMarquee(null);\n },\n };\n }\n\n function startTextGesture(event: PointerEvent): void {\n const state = store.get();\n const image = toImageSpace(event);\n const { id, nextShapeNumber } = mintShapeId(state);\n const shape: TextShape = {\n id,\n kind: 'text',\n x: image.x,\n y: image.y,\n text: '',\n fontSize: state.currentStyle.fontSize ?? TEXT_DEFAULT_FONT_SIZE,\n color: state.currentStyle.color,\n textAlign: 'left',\n };\n store.update((current) => ({ ...addShape(current, shape), nextShapeNumber }));\n // The shape is selected by `addShape`; open the editor on it.\n textEditor.open(shape, viewport, source);\n }\n\n /**\n * Place the active drawing tool's default-sized shape at image\n * centre and select it. The keyboard-only equivalent of dragging a\n * shape onto the canvas. For the text tool\n * the inline editor opens immediately so the keyboard user can\n * type without further navigation; the text-overlay div is\n * already in the focus trap, so the editor receives focus\n * naturally. For rect / ellipse / arrow the shape is selected and\n * the coordinate inputs become available below the style row.\n */\n function insertDefaultAtCenter(): void {\n const state = store.get();\n const tool: AnnotateTool = state.activeTool;\n if (tool === 'select' || !isKeyboardPlaceableKind(tool)) return;\n const { id, nextShapeNumber } = mintShapeId(state);\n const shape = createCenteredShape(tool, {\n imageSize: { width: source.width, height: source.height },\n style: state.currentStyle,\n id,\n });\n store.update((current) => ({ ...addShape(current, shape), nextShapeNumber }));\n if (shape.kind === 'text') {\n // Open the inline editor immediately so the user can start\n // typing. Without this the user would have to find another\n // affordance to enter the text — defeats the point.\n textEditor.open(shape, viewport, source);\n announce('Text annotation placed at centre. Type to enter text.');\n return;\n }\n announce(\n `${labelForKind(shape.kind)} placed at centre. Use arrow keys to nudge, or edit coordinates below.`,\n );\n // Move keyboard focus straight into the first coordinate input\n // so a keyboard-only user doesn't have to hunt for the next\n // affordance after Insert. The store-subscription update has\n // already painted the inputs by the time the click handler\n // returns, but the layout pass may not be settled — defer one\n // animation frame so the input is hit-testable before we focus.\n requestAnimationFrame(() => {\n const firstInput = coordInputs.container.querySelector<HTMLInputElement>(\n '.kalotyp-annotate-coords-input',\n );\n firstInput?.focus();\n firstInput?.select();\n });\n }\n\n // ----- Coordinate inputs (keyboard-only positioning) -----\n // Built first so the panel can host the row in its DOM rhythm. The\n // row is store-free; each typed value commit hands the new shape\n // back to the mount layer, which writes it via replaceShape and\n // emits a history-commit.\n const coordInputs = buildCoordInputs({\n onShapeChanged: (shape) => {\n store.update((current) => replaceShape(current, shape));\n commit();\n },\n });\n\n // ----- Panel -----\n const initialState = store.get();\n const panel: AnnotatePanel = buildAnnotatePanel({\n initialTool: initialState.activeTool,\n initialStyle: initialState.currentStyle,\n canDelete: initialState.selectedId !== null,\n coordInputs: coordInputs.container,\n onSelectTool: (tool) => store.update((current) => setActiveTool(current, tool)),\n onColorChange: (color) => {\n store.update((current) => {\n let next = setStyle(current, { color });\n const selected = selectedShapeOf(current);\n if (selected) next = replaceShape(next, applyColorToShape(selected, color));\n return next;\n });\n commit();\n },\n onStrokeWidthChange: (width) => {\n store.update((current) => {\n let next = setStyle(current, { strokeWidth: width });\n const selected = selectedShapeOf(current);\n if (selected) next = replaceShape(next, applyStrokeWidthToShape(selected, width));\n return next;\n });\n commit();\n },\n onDeleteSelected: () => {\n const id = store.get().selectedId;\n if (!id) return;\n store.update((current) => deleteShape(current, id));\n commit();\n },\n onInsertAtCenter: () => insertDefaultAtCenter(),\n });\n utilHost.appendChild(panel.container);\n\n // ----- Initial paint + observers -----\n recomputeViewport();\n paintAll();\n\n const resizeObserver = new ResizeObserver(() => {\n recomputeViewport();\n paintAll();\n });\n resizeObserver.observe(stage.container);\n\n // Repaint everything on viewport change. Selection handles +\n // text-editor position read from the same viewport so they pick up\n // zoom/pan automatically. RAF-coalesce a burst of emissions into one\n // paint per frame.\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n recomputeViewport();\n paintAll();\n });\n });\n\n // Repaint shapes + handles on store changes; keep panel in sync;\n // open/close the text editor when the selected text shape changes.\n let lastShapes = store.get().shapes;\n let lastSelected = store.get().selectedId;\n let lastTool = store.get().activeTool;\n let lastStyle = store.get().currentStyle;\n\n const unsubscribe = store.subscribe((next) => {\n const shapesChanged = next.shapes !== lastShapes;\n const selectionChanged = next.selectedId !== lastSelected;\n if (shapesChanged) {\n lastShapes = next.shapes;\n paintShapes();\n }\n if (selectionChanged) {\n lastSelected = next.selectedId;\n panel.setCanDelete(next.selectedId !== null);\n }\n if (next.activeTool !== lastTool) {\n lastTool = next.activeTool;\n panel.setActiveTool(next.activeTool);\n }\n if (next.currentStyle !== lastStyle) {\n lastStyle = next.currentStyle;\n panel.setStyle(next.currentStyle);\n }\n selectionLayer.update(selectedShapeOf(next), viewport);\n // Keep the per-selection coordinate inputs in sync.\n // We update on either selection or geometry change so a pointer\n // drag updates the typed values too — the keyboard and pointer\n // paths see the same source of truth, in both directions.\n if (selectionChanged || shapesChanged) {\n coordInputs.updateForShape(selectedShapeOf(next));\n }\n });\n\n // Delete + Esc + arrow-key keyboard handling. Lives on `document`\n // while the plugin is mounted; the editor's broader undo/redo\n // handler already filters out editable targets, so we follow the\n // same rule.\n const onKeyDown = (event: KeyboardEvent): void => {\n const target = event.target as Element | null;\n if (isEditableTarget(target)) return;\n const state = store.get();\n if (event.key === 'Escape') {\n // While an annotation is selected, Esc deselects (and stops the\n // editor-level Esc-to-close handler from firing on top).\n // Without a selection Esc falls through to the editor.\n if (state.selectedId !== null) {\n event.preventDefault();\n event.stopPropagation();\n store.update((current) => selectShape(current, null));\n // Announce the deselection — focus doesn't move,\n // so without this a screen reader user has no cue that\n // anything changed.\n announce('Selection cleared.');\n }\n return;\n }\n if (event.key === 'Delete' || event.key === 'Backspace') {\n if (state.selectedId === null) return;\n event.preventDefault();\n const id = state.selectedId;\n store.update((current) => deleteShape(current, id));\n commit();\n return;\n }\n // Arrow-key shape nudging. With a shape\n // selected, the four arrow keys translate it by 1px in image\n // space. Holding Shift snaps to a 10× step the way professional\n // editors handle nudge, so a keyboard-only user can travel\n // distance quickly without losing precision. The keys only fire\n // when no input/textarea/contenteditable is focused, so the\n // user is free to step through coordinate inputs without the\n // arrow keys hijacking input-internal cursor movement.\n if (\n event.key === 'ArrowUp' ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowLeft' ||\n event.key === 'ArrowRight'\n ) {\n const selected = selectedShapeOf(state);\n if (!selected) return;\n // Don't nudge when modifier keys other than Shift are held —\n // OS-level shortcuts (Ctrl/Alt/Meta + Arrow) shouldn't be\n // consumed by the editor.\n if (event.ctrlKey || event.altKey || event.metaKey) return;\n const step = event.shiftKey ? 10 : 1;\n const dx = event.key === 'ArrowLeft' ? -step : event.key === 'ArrowRight' ? step : 0;\n const dy = event.key === 'ArrowUp' ? -step : event.key === 'ArrowDown' ? step : 0;\n event.preventDefault();\n const moved = translateShape(selected, dx, dy);\n store.update((current) => replaceShape(current, moved));\n commit();\n }\n };\n // Capture-phase so the plugin sees Esc / Delete before the editor's\n // document-level shortcut handler. The plugin only consumes the\n // event (with stopPropagation) when it actually acts on it; events\n // it ignores fall through to the editor.\n document.addEventListener('keydown', onKeyDown, true);\n\n return {\n destroy() {\n document.removeEventListener('keydown', onKeyDown, true);\n removeHitDrag();\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n textEditor.destroy();\n selectionLayer.destroy();\n stage.container.remove();\n panel.container.remove();\n },\n };\n}\n\n/**\n * Apply a colour change to an existing shape. Each shape kind has its\n * own colour-bearing field — text uses `color`, rect/ellipse use\n * `strokeColor` (and never overwrite `fillColor` from the style row),\n * arrow/freehand/highlight use `color`. This is the small price the\n * discriminated union charges us for cross-cutting style edits.\n */\nfunction applyColorToShape(shape: Shape, color: string): Shape {\n switch (shape.kind) {\n case 'text':\n return { ...shape, color };\n case 'rect':\n case 'ellipse':\n return { ...shape, strokeColor: color };\n case 'arrow':\n case 'freehand':\n case 'highlight':\n return { ...shape, color };\n }\n}\n\nfunction applyStrokeWidthToShape(shape: Shape, strokeWidth: number): Shape {\n switch (shape.kind) {\n case 'text':\n // Text doesn't have a stroke width; treat the slider as a\n // \"size scale\" that nudges fontSize in 8 px increments.\n return { ...shape, fontSize: Math.max(8, Math.round(strokeWidth * 4)) };\n case 'rect':\n case 'ellipse':\n case 'arrow':\n case 'freehand':\n case 'highlight':\n return { ...shape, strokeWidth };\n }\n}\n\nfunction normaliseExtent(extent: {\n x: number;\n y: number;\n width: number;\n height: number;\n}): { x: number; y: number; width: number; height: number } {\n let { x, y, width, height } = extent;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n return { x, y, width, height };\n}\n\n/**\n * Topmost (last drawn) shape whose bounding box intersects the\n * marquee rectangle. Returns `undefined` when nothing intersects.\n * Multi-select is out of scope for v1 (per Phase 3 brief), so\n * marquee resolves to a single selection.\n */\nfunction topmostShapeIntersectingMarquee(\n shapes: ReadonlyArray<Shape>,\n marquee: { x: number; y: number; width: number; height: number },\n): Shape | undefined {\n for (let i = shapes.length - 1; i >= 0; i--) {\n const shape = shapes[i];\n if (!shape) continue;\n const bbox = boundingBoxOf(shape);\n if (rectsIntersect(bbox, marquee)) return shape;\n }\n return undefined;\n}\n\nfunction rectsIntersect(\n a: { x: number; y: number; width: number; height: number },\n b: { x: number; y: number; width: number; height: number },\n): boolean {\n return !(\n a.x + a.width < b.x ||\n b.x + b.width < a.x ||\n a.y + a.height < b.y ||\n b.y + b.height < a.y\n );\n}\n\nfunction isEditableTarget(target: Element | null): boolean {\n if (!target) return false;\n const tag = target.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true;\n return (target as HTMLElement).isContentEditable === true;\n}\n\n/**\n * Human-readable label for the keyboard-placement live-region\n * announcement. Each kind reads as a noun in the announcement\n * sentence — \"Rectangle placed at centre. Use arrow keys…\".\n */\nfunction labelForKind(kind: 'rect' | 'ellipse' | 'arrow' | 'text'): string {\n switch (kind) {\n case 'rect':\n return 'Rectangle';\n case 'ellipse':\n return 'Ellipse';\n case 'arrow':\n return 'Arrow';\n case 'text':\n return 'Text annotation';\n }\n}\n","import {\n type AnnotateState,\n type UtilityPlugin,\n bakeAnnotate,\n initialAnnotateState,\n} from '@magicpages/kalotyp-core';\nimport { mountAnnotateUtility } from './mount.js';\n\nexport interface AnnotatePluginOptions {\n /**\n * Where the plugin's panel UI mounts. The plugin's stage UI is\n * mounted into the `host` argument of the standard plugin `mount()`\n * call. Same panel-host-as-closure pattern the other plugins use\n * (see `crop/plugin.ts`).\n */\n readonly panelHost: HTMLElement;\n}\n\n/**\n * Build the annotation `UtilityPlugin` instance for one editor\n * session. Each `openDefaultEditor` call gets a fresh plugin closing\n * over its own panel host.\n *\n * The chain position (annotate before resize) is the editor's\n * concern; this factory just supplies the bake.\n */\nexport function createAnnotatePlugin(options: AnnotatePluginOptions): UtilityPlugin<AnnotateState> {\n return {\n id: 'annotate',\n init: (ctx) =>\n initialAnnotateState({ imageSize: { width: ctx.source.width, height: ctx.source.height } }),\n mount(stageHost, ctx, store) {\n const handle = mountAnnotateUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: 'annotate' }),\n onAnnounce: (message) => ctx.bus.emit('announce', { message }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: (state, source) => bakeAnnotate({ shapes: state.shapes }, source),\n };\n}\n","/**\n * Per-region coordinate-input row for the redact plugin's keyboard\n * placement path. Same shape as the annotate\n * plugin's coord-inputs.ts but with one shape kind (rect) and four\n * fields (Left, Top, Width, Height).\n */\n\nimport type { RedactRegion } from '@magicpages/kalotyp-core';\n\nexport interface RedactCoordInputsOptions {\n onRegionChanged(region: RedactRegion): void;\n}\n\nexport interface RedactCoordInputsHandle {\n readonly container: HTMLDivElement;\n /** Show inputs for the given region, or hide when null. */\n updateForRegion(region: RedactRegion | null): void;\n destroy(): void;\n}\n\ninterface FieldSpec {\n readonly id: 'x' | 'y' | 'width' | 'height';\n readonly label: string;\n readonly min?: number;\n}\n\nconst FIELDS: ReadonlyArray<FieldSpec> = [\n { id: 'x', label: 'Left' },\n { id: 'y', label: 'Top' },\n { id: 'width', label: 'Width', min: 1 },\n { id: 'height', label: 'Height', min: 1 },\n];\n\nexport function buildRedactCoordInputs(options: RedactCoordInputsOptions): RedactCoordInputsHandle {\n const container = document.createElement('div');\n container.className = 'kalotyp-redact-coords';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Selected redaction position');\n container.hidden = true;\n\n let activeRegion: RedactRegion | null = null;\n const inputs = new Map<FieldSpec['id'], HTMLInputElement>();\n\n for (const spec of FIELDS) {\n const wrapper = document.createElement('label');\n wrapper.className = 'kalotyp-redact-coords-field';\n\n const labelSpan = document.createElement('span');\n labelSpan.className = 'kalotyp-redact-coords-label';\n labelSpan.textContent = spec.label;\n\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'kalotyp-redact-coords-input';\n input.dataset.field = spec.id;\n input.step = '1';\n input.inputMode = 'numeric';\n if (spec.min !== undefined) input.min = String(spec.min);\n input.setAttribute('aria-label', `${spec.label} (pixels)`);\n input.addEventListener('change', onAnyInputChange);\n\n wrapper.appendChild(labelSpan);\n wrapper.appendChild(input);\n container.appendChild(wrapper);\n inputs.set(spec.id, input);\n }\n\n function syncValuesFromRegion(region: RedactRegion): void {\n const setVal = (id: FieldSpec['id'], value: number): void => {\n const el = inputs.get(id);\n if (!el) return;\n const next = String(Math.round(value));\n // Skip the assignment if the user is mid-edit on the input —\n // overwriting a focused, partially-typed value is the most\n // surprising thing an a11y helper can do.\n if (document.activeElement === el) return;\n if (el.value !== next) el.value = next;\n };\n setVal('x', region.x);\n setVal('y', region.y);\n setVal('width', region.width);\n setVal('height', region.height);\n }\n\n function onAnyInputChange(): void {\n if (!activeRegion) return;\n const x = readNumber('x');\n const y = readNumber('y');\n const width = readNumber('width');\n const height = readNumber('height');\n if (![x, y, width, height].every(Number.isFinite)) return;\n const next: RedactRegion = {\n ...activeRegion,\n x,\n y,\n width,\n height,\n };\n if (\n next.x === activeRegion.x &&\n next.y === activeRegion.y &&\n next.width === activeRegion.width &&\n next.height === activeRegion.height\n ) {\n return;\n }\n activeRegion = next;\n options.onRegionChanged(next);\n }\n\n function readNumber(id: FieldSpec['id']): number {\n const el = inputs.get(id);\n if (!el) return Number.NaN;\n return Math.round(el.valueAsNumber);\n }\n\n return {\n container,\n updateForRegion(region): void {\n if (!region) {\n activeRegion = null;\n container.hidden = true;\n return;\n }\n activeRegion = region;\n syncValuesFromRegion(region);\n container.hidden = false;\n },\n destroy(): void {\n container.replaceChildren();\n inputs.clear();\n container.remove();\n },\n };\n}\n","/**\n * Build the redact panel: mode toggle, optional colour picker, the\n * per-selection coordinate inputs row, and Insert/Delete actions.\n *\n * Same panel-rhythm conventions as the annotate plugin (Phase 6.3) —\n * a toolbar of mode buttons, then a style row with colour + Insert +\n * Delete, then a slot for the coordinate inputs row mounted by the\n * mount layer.\n */\n\nimport type { RedactMode } from '@magicpages/kalotyp-core';\nimport { icon } from '../../icons.js';\n\nexport interface RedactPanelOptions {\n readonly initialMode: RedactMode;\n readonly initialColor: string;\n readonly canDelete: boolean;\n /** Where the per-selection coordinate-input row mounts. */\n readonly coordInputs: HTMLElement;\n onSelectMode(mode: RedactMode): void;\n onColorChange(color: string): void;\n onDeleteSelected(): void;\n onInsertAtCenter(): void;\n}\n\nexport interface RedactPanel {\n readonly container: HTMLDivElement;\n readonly modeButtons: ReadonlyMap<RedactMode, HTMLButtonElement>;\n readonly colorInput: HTMLInputElement;\n readonly hexInput: HTMLInputElement;\n readonly insertButton: HTMLButtonElement;\n readonly deleteButton: HTMLButtonElement;\n setActiveMode(mode: RedactMode): void;\n setColor(color: string): void;\n setCanDelete(canDelete: boolean): void;\n}\n\nconst MODE_DEFS: ReadonlyArray<{ id: RedactMode; label: string }> = [\n { id: 'pixelate', label: 'Pixelate' },\n { id: 'blur', label: 'Blur' },\n { id: 'solid', label: 'Solid fill' },\n];\n\nexport function buildRedactPanel(options: RedactPanelOptions): RedactPanel {\n const container = document.createElement('div');\n container.className = 'kalotyp-redact-panel';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Redact');\n\n // ----- Mode toolbar -----\n const toolbar = document.createElement('div');\n toolbar.className = 'kalotyp-redact-toolbar';\n toolbar.setAttribute('role', 'radiogroup');\n toolbar.setAttribute('aria-label', 'Redaction mode');\n\n const modeButtons = new Map<RedactMode, HTMLButtonElement>();\n for (const def of MODE_DEFS) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-redact-mode';\n button.dataset.mode = def.id;\n button.setAttribute('role', 'radio');\n button.setAttribute('aria-checked', def.id === options.initialMode ? 'true' : 'false');\n button.setAttribute('aria-label', `${def.label} redaction`);\n button.title = def.label;\n button.textContent = def.label;\n button.addEventListener('click', () => options.onSelectMode(def.id));\n toolbar.appendChild(button);\n modeButtons.set(def.id, button);\n }\n\n // ----- Style row -----\n const styleRow = document.createElement('div');\n styleRow.className = 'kalotyp-redact-style-row';\n\n // Colour picker for the `solid` mode. We always render the controls\n // and disable them when the mode isn't `solid`, instead of hiding,\n // so the panel layout doesn't reflow as the user switches modes.\n const colorInput = document.createElement('input');\n colorInput.type = 'color';\n colorInput.className = 'kalotyp-redact-color';\n colorInput.value = normaliseColorForInput(options.initialColor);\n colorInput.setAttribute('aria-label', 'Solid fill colour (visual picker)');\n colorInput.addEventListener('change', () => options.onColorChange(colorInput.value));\n\n const hexInput = document.createElement('input');\n hexInput.type = 'text';\n hexInput.className = 'kalotyp-redact-hex';\n hexInput.value = normaliseColorForInput(options.initialColor);\n hexInput.maxLength = 7;\n hexInput.spellcheck = false;\n hexInput.autocomplete = 'off';\n hexInput.setAttribute('aria-label', 'Solid fill hex code');\n hexInput.setAttribute('placeholder', '#000000');\n hexInput.addEventListener('change', () => {\n const value = hexInput.value.trim();\n const normalised = normaliseHexInput(value);\n if (normalised) {\n hexInput.value = normalised;\n options.onColorChange(normalised);\n } else {\n hexInput.value = colorInput.value;\n }\n });\n\n const insertButton = document.createElement('button');\n insertButton.type = 'button';\n insertButton.className = 'kalotyp-redact-insert';\n insertButton.innerHTML = `${icon('plus')}<span>Insert at centre</span>`;\n insertButton.setAttribute('aria-label', 'Insert redaction region at image centre');\n insertButton.title = 'Insert at centre';\n insertButton.addEventListener('click', () => options.onInsertAtCenter());\n\n const deleteButton = document.createElement('button');\n deleteButton.type = 'button';\n deleteButton.className = 'kalotyp-redact-delete';\n deleteButton.innerHTML = `${icon('delete')}<span>Delete</span>`;\n deleteButton.setAttribute('aria-label', 'Delete selected redaction region');\n deleteButton.title = 'Delete (Del)';\n deleteButton.disabled = !options.canDelete;\n deleteButton.addEventListener('click', () => options.onDeleteSelected());\n\n // Group colour controls so they wrap together as a unit (Phase 6.6\n // mobile-layout fix: previously the row could split with the hint\n // floating alone). The Insert/Delete pair is its own group so they\n // also stay together.\n const colorGroup = document.createElement('div');\n colorGroup.className = 'kalotyp-redact-color-group';\n colorGroup.appendChild(colorInput);\n colorGroup.appendChild(hexInput);\n\n // Helper text that explains why the colour controls are disabled\n // outside Solid fill mode. Hidden when the mode is `solid` (the\n // controls are usable). `aria-live` so screen-reader users hear\n // the explanation when they switch modes.\n const colorHint = document.createElement('span');\n colorHint.className = 'kalotyp-redact-color-hint';\n colorHint.textContent = 'Colour applies to Solid fill only.';\n colorHint.setAttribute('aria-live', 'polite');\n\n const buttonGroup = document.createElement('div');\n buttonGroup.className = 'kalotyp-redact-button-group';\n buttonGroup.appendChild(insertButton);\n buttonGroup.appendChild(deleteButton);\n\n styleRow.appendChild(colorGroup);\n styleRow.appendChild(colorHint);\n styleRow.appendChild(buttonGroup);\n\n container.appendChild(toolbar);\n container.appendChild(styleRow);\n container.appendChild(options.coordInputs);\n\n function setActiveMode(mode: RedactMode): void {\n for (const [id, button] of modeButtons) {\n button.setAttribute('aria-checked', id === mode ? 'true' : 'false');\n button.classList.toggle('kalotyp-redact-mode--active', id === mode);\n }\n const isSolid = mode === 'solid';\n colorInput.disabled = !isSolid;\n hexInput.disabled = !isSolid;\n colorHint.hidden = isSolid;\n }\n\n function setColor(color: string): void {\n const target = normaliseColorForInput(color);\n if (colorInput.value !== target) colorInput.value = target;\n if (hexInput.value.toLowerCase() !== target.toLowerCase()) hexInput.value = target;\n }\n\n function setCanDelete(canDelete: boolean): void {\n deleteButton.disabled = !canDelete;\n }\n\n setActiveMode(options.initialMode);\n\n return {\n container,\n modeButtons,\n colorInput,\n hexInput,\n insertButton,\n deleteButton,\n setActiveMode,\n setColor,\n setCanDelete,\n };\n}\n\nfunction normaliseColorForInput(color: string): string {\n if (/^#[0-9a-fA-F]{6}$/.test(color)) return color;\n if (/^#[0-9a-fA-F]{3}$/.test(color)) {\n const r = color[1];\n const g = color[2];\n const b = color[3];\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return '#000000';\n}\n\nfunction normaliseHexInput(value: string): string | null {\n const cleaned = value.startsWith('#') ? value.slice(1) : value;\n if (/^[0-9a-fA-F]{6}$/.test(cleaned)) return `#${cleaned.toLowerCase()}`;\n if (/^[0-9a-fA-F]{3}$/.test(cleaned)) {\n const r = cleaned[0];\n const g = cleaned[1];\n const b = cleaned[2];\n return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();\n }\n return null;\n}\n","/**\n * Render helpers for the redact plugin's three canvas layers. The\n * approach mirrors the annotate plugin's stacked layers exactly:\n *\n * - `paintRedactImageLayer` draws the upstream-baked source onto the\n * bottom canvas. Called once on mount and on stage resize.\n * - `paintRedactRegionsLayer` draws every committed region's preview\n * representation on top of the image. Pixelate/blur are baked\n * into the layer here so the live stage matches the export. Solid\n * regions are simple coloured rectangles.\n * - `paintRedactLiveLayer` draws the in-progress region (during a\n * drag) in display space — a dashed marquee with the current mode\n * rendered as a translucent fill so the user gets a feel for what\n * they'll get on commit.\n *\n * The regions layer reuses the core `paintRegion` so the visible\n * preview is byte-equal to the bake output (per region). The image\n * argument is only needed for pixelate/blur — solid doesn't read it.\n */\n\nimport {\n type RedactRegion,\n type SourceImage,\n type Viewport,\n paintRedactRegion,\n} from '@magicpages/kalotyp-core';\n\n/**\n * Resize the canvas's backing store to stage CSS pixels × DPR and\n * return its 2D context already scaled into CSS-pixel space. Returns\n * `null` if the context is unavailable (jsdom path).\n */\nfunction prepareCanvas(\n canvas: HTMLCanvasElement,\n stageWidth: number,\n stageHeight: number,\n): CanvasRenderingContext2D | null {\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const targetW = Math.max(1, Math.round(stageWidth * dpr));\n const targetH = Math.max(1, Math.round(stageHeight * dpr));\n if (canvas.width !== targetW) canvas.width = targetW;\n if (canvas.height !== targetH) canvas.height = targetH;\n canvas.style.width = `${stageWidth}px`;\n canvas.style.height = `${stageHeight}px`;\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, stageWidth, stageHeight);\n return ctx;\n}\n\nexport function paintRedactImageLayer(\n canvas: HTMLCanvasElement,\n source: SourceImage,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const ctx = prepareCanvas(canvas, stageWidth, stageHeight);\n if (!ctx) return;\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(\n source.bitmap,\n viewport.displayRect.x,\n viewport.displayRect.y,\n viewport.displayRect.width,\n viewport.displayRect.height,\n );\n}\n\n/**\n * Paint every region onto the regions canvas. Pixelate and blur read\n * from the supplied image-source bitmap (drawn at viewport scale) so\n * the preview is the same composition the bake produces. We draw the\n * image into an off-screen canvas at the viewport scale, then ask\n * the core `paintRegion` to redact each region's pixels in turn.\n *\n * The trade-off here: regions drawn during the live preview composite\n * onto the image at *display* pixels (not source pixels), so the\n * pixelate grid size in the preview differs slightly from the export.\n * The bake re-runs at source resolution; the user sees the right\n * thing on the way out.\n */\nexport function paintRedactRegionsLayer(\n canvas: HTMLCanvasElement,\n source: SourceImage,\n regions: ReadonlyArray<RedactRegion>,\n selectedId: string | null,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const ctx = prepareCanvas(canvas, stageWidth, stageHeight);\n if (!ctx) return;\n if (regions.length === 0) return;\n\n // Build a temporary canvas the size of the visible image rect that\n // mirrors the source image; `paintRegion` will read pixels from it\n // for pixelate/blur. Drawing into this buffer first means the\n // composite-so-far is the previous regions baked in, which matches\n // the export ordering.\n const tempCanvas = document.createElement('canvas');\n const dispW = Math.max(1, Math.round(viewport.displayRect.width));\n const dispH = Math.max(1, Math.round(viewport.displayRect.height));\n tempCanvas.width = dispW;\n tempCanvas.height = dispH;\n const tempCtx = tempCanvas.getContext('2d');\n if (!tempCtx) return;\n tempCtx.imageSmoothingEnabled = true;\n tempCtx.imageSmoothingQuality = 'high';\n tempCtx.drawImage(source.bitmap, 0, 0, dispW, dispH);\n\n // Project each region's image-space rect into the temp canvas's\n // display-pixel space and ask the core renderer to paint it.\n for (const region of regions) {\n const projected: RedactRegion = {\n ...region,\n x: region.x * viewport.scale,\n y: region.y * viewport.scale,\n width: region.width * viewport.scale,\n height: region.height * viewport.scale,\n };\n paintRedactRegion(tempCtx, tempCanvas, projected, {\n bitmap: tempCanvas,\n width: dispW,\n height: dispH,\n mimeType: 'image/png',\n });\n }\n\n // Draw the redacted image preview into the regions canvas at the\n // viewport's display position so it sits exactly over the image\n // layer. Use destination-over composite so a future overlay on\n // the same canvas wouldn't swallow earlier paints, though we\n // currently only paint regions here.\n ctx.drawImage(tempCanvas, viewport.displayRect.x, viewport.displayRect.y, dispW, dispH);\n\n // Draw a thin outline around every region so the user can see\n // where the redaction sits. The selected region gets a brighter\n // accent stroke (the selection-handle layer adds the corner\n // handles on top of this).\n for (const region of regions) {\n const x = viewport.displayRect.x + region.x * viewport.scale;\n const y = viewport.displayRect.y + region.y * viewport.scale;\n const w = region.width * viewport.scale;\n const h = region.height * viewport.scale;\n ctx.save();\n if (region.id === selectedId) {\n ctx.strokeStyle = 'rgba(99, 102, 241, 0.9)';\n ctx.lineWidth = 1.5;\n } else {\n ctx.strokeStyle = 'rgba(255, 255, 255, 0.6)';\n ctx.lineWidth = 1;\n }\n ctx.setLineDash([4, 3]);\n ctx.strokeRect(x + 0.5, y + 0.5, Math.max(0, w - 1), Math.max(0, h - 1));\n ctx.restore();\n }\n}\n\n/**\n * Draw a marquee for the in-progress drag. We don't bake pixelate /\n * blur here — pixelate at the live drag's pixel grid is jittery and\n * doesn't help the user understand the result. Instead we paint a\n * translucent fill in the mode's signature colour:\n *\n * - `solid` → user-chosen colour at 70% alpha\n * - `pixelate` → neutral grey at 60% alpha + a \"pixelate\" texture\n * (a small grid of squares) so the user knows what they're getting\n * - `blur` → low-alpha blue-grey, signalling \"softening\" without\n * actually blurring (we'd have to repeatedly re-read the canvas;\n * the export bake handles the real blur).\n */\nexport function paintRedactLiveLayer(\n canvas: HTMLCanvasElement,\n marquee: {\n x: number;\n y: number;\n width: number;\n height: number;\n mode: RedactRegion['mode'];\n color: string;\n } | null,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const ctx = prepareCanvas(canvas, stageWidth, stageHeight);\n if (!ctx) return;\n if (!marquee) return;\n\n const dx = viewport.displayRect.x + marquee.x * viewport.scale;\n const dy = viewport.displayRect.y + marquee.y * viewport.scale;\n let dw = marquee.width * viewport.scale;\n let dh = marquee.height * viewport.scale;\n let drawX = dx;\n let drawY = dy;\n if (dw < 0) {\n drawX = dx + dw;\n dw = -dw;\n }\n if (dh < 0) {\n drawY = dy + dh;\n dh = -dh;\n }\n ctx.save();\n ctx.fillStyle = marqueeFillFor(marquee.mode, marquee.color);\n ctx.fillRect(drawX, drawY, dw, dh);\n ctx.strokeStyle = 'rgba(99, 102, 241, 0.95)';\n ctx.lineWidth = 1.5;\n ctx.setLineDash([4, 3]);\n ctx.strokeRect(drawX + 0.75, drawY + 0.75, Math.max(0, dw - 1.5), Math.max(0, dh - 1.5));\n ctx.restore();\n}\n\nfunction marqueeFillFor(mode: RedactRegion['mode'], color: string): string {\n switch (mode) {\n case 'solid':\n return hexWithAlpha(color, 0.7);\n case 'pixelate':\n return 'rgba(120, 120, 120, 0.6)';\n case 'blur':\n return 'rgba(180, 200, 220, 0.45)';\n }\n}\n\n/**\n * Build an `rgba(...)` string from a `#rrggbb` hex and an alpha. Falls\n * back to the input string if the hex doesn't parse (e.g. a CSS\n * keyword) so the live preview still draws something.\n */\nfunction hexWithAlpha(hex: string, alpha: number): string {\n const match = /^#([0-9a-fA-F]{6})$/.exec(hex);\n if (!match) return hex;\n const value = match[1];\n if (!value) return hex;\n const r = Number.parseInt(value.slice(0, 2), 16);\n const g = Number.parseInt(value.slice(2, 4), 16);\n const b = Number.parseInt(value.slice(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n","/**\n * Selection rendering and per-handle resize gesture for redaction\n * regions. The annotate plugin's `selection.ts` does the same job for\n * shapes; the redact version is simpler because there's only one\n * shape kind (rectangle), so we can wire the corner/edge handles\n * directly to a rect-resize without a per-kind dispatch.\n */\n\nimport {\n ALL_SELECTION_HANDLES,\n type Rect,\n type RedactRegion,\n type RedactState,\n type SelectionHandle,\n type Store,\n type Viewport,\n rectFromHandleDrag,\n replaceRedactRegion,\n selectedRedactRegionOf,\n} from '@magicpages/kalotyp-core';\nimport type { DragHandlers } from '../annotate/pointer-drag.js';\nimport { attachPointerDrag } from '../annotate/pointer-drag.js';\n\nexport interface RedactSelectionLayerOptions {\n readonly host: HTMLDivElement;\n readonly store: Store<RedactState>;\n /** Project a raw client-space pointer to image-space pixels. */\n toImageSpace(point: { clientX: number; clientY: number }): { x: number; y: number };\n /** Read the current viewport at gesture start. */\n getViewport(): Viewport;\n /** Emit a history-commit at gesture end. */\n commit(): void;\n}\n\nexport interface RedactSelectionLayer {\n update(region: RedactRegion | null, viewport: Viewport): void;\n destroy(): void;\n}\n\nexport function buildRedactSelectionLayer(\n options: RedactSelectionLayerOptions,\n): RedactSelectionLayer {\n const { host, store } = options;\n const handleEls = new Map<SelectionHandle, HTMLButtonElement>();\n const cleanups: Array<() => void> = [];\n\n for (const direction of ALL_SELECTION_HANDLES) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-redact-handle';\n button.dataset.direction = direction;\n button.setAttribute('aria-label', handleLabel(direction));\n // Same a11y rationale as the annotate plugin: handles are\n // pointer-driven; keyboard users use the coordinate inputs.\n // Exclude from tab order to keep the Tab walk meaningful.\n button.tabIndex = -1;\n button.style.display = 'none';\n handleEls.set(direction, button);\n host.appendChild(button);\n\n cleanups.push(attachPointerDrag(button, (event) => startHandleResizeGesture(direction, event)));\n }\n\n function update(region: RedactRegion | null, viewport: Viewport): void {\n if (!region) {\n for (const [, button] of handleEls) button.style.display = 'none';\n return;\n }\n const box: Rect = {\n x: region.x,\n y: region.y,\n width: region.width,\n height: region.height,\n };\n const positions = handlePositionsFor(box);\n for (const direction of ALL_SELECTION_HANDLES) {\n const handle = handleEls.get(direction);\n if (!handle) continue;\n const display = imageToDisplay(positions[direction], viewport);\n handle.style.display = '';\n handle.style.left = `${display.x}px`;\n handle.style.top = `${display.y}px`;\n }\n }\n\n function destroy(): void {\n for (const cleanup of cleanups) cleanup();\n for (const [, button] of handleEls) button.remove();\n handleEls.clear();\n }\n\n function startHandleResizeGesture(\n direction: SelectionHandle,\n origin: PointerEvent,\n ): DragHandlers | null {\n const initial = selectedRedactRegionOf(store.get());\n if (!initial) return null;\n void origin;\n return {\n onMove(point) {\n const image = options.toImageSpace(point);\n const box: Rect = {\n x: initial.x,\n y: initial.y,\n width: initial.width,\n height: initial.height,\n };\n const next = rectFromHandleDrag(box, direction, image);\n // Allow negative width/height during the drag (the live\n // preview matches the user's gesture); the commit step\n // normalises by sign-flipping. Rect coords go straight onto\n // the region so the regions canvas re-renders the new shape.\n store.update((current) =>\n replaceRedactRegion(current, {\n ...initial,\n x: next.x,\n y: next.y,\n width: next.width,\n height: next.height,\n }),\n );\n },\n onCommit() {\n // Normalise the rect's sign on commit. We re-read the store\n // because the active gesture may have left negative dims;\n // the bake assumes non-negative.\n const region = selectedRedactRegionOf(store.get());\n if (region && (region.width < 0 || region.height < 0)) {\n let { x, y, width, height } = region;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n store.update((current) =>\n replaceRedactRegion(current, { ...region, x, y, width, height }),\n );\n }\n options.commit();\n },\n onCancel() {\n store.update((current) => replaceRedactRegion(current, initial));\n },\n };\n }\n\n return { update, destroy };\n}\n\nfunction handlePositionsFor(rect: Rect): Record<SelectionHandle, { x: number; y: number }> {\n const left = rect.x;\n const right = rect.x + rect.width;\n const top = rect.y;\n const bottom = rect.y + rect.height;\n const cx = rect.x + rect.width / 2;\n const cy = rect.y + rect.height / 2;\n return {\n tl: { x: left, y: top },\n tr: { x: right, y: top },\n bl: { x: left, y: bottom },\n br: { x: right, y: bottom },\n t: { x: cx, y: top },\n r: { x: right, y: cy },\n b: { x: cx, y: bottom },\n l: { x: left, y: cy },\n };\n}\n\nfunction imageToDisplay(\n point: { x: number; y: number },\n viewport: Viewport,\n): { x: number; y: number } {\n return {\n x: viewport.displayRect.x + point.x * viewport.scale,\n y: viewport.displayRect.y + point.y * viewport.scale,\n };\n}\n\nfunction handleLabel(direction: SelectionHandle): string {\n switch (direction) {\n case 'tl':\n return 'Resize from top-left';\n case 'tr':\n return 'Resize from top-right';\n case 'bl':\n return 'Resize from bottom-left';\n case 'br':\n return 'Resize from bottom-right';\n case 't':\n return 'Resize from top';\n case 'r':\n return 'Resize from right';\n case 'b':\n return 'Resize from bottom';\n case 'l':\n return 'Resize from left';\n }\n}\n","/**\n * Build the layered DOM for the redact plugin's stage. Same structural\n * shape as the annotate plugin (image canvas, regions canvas, live\n * canvas, hit-area, handles layer) — redact reuses the same selection\n * + drag-to-define interaction model with one shape kind, so keeping\n * the DOM rhythm aligned across plugins is the cleanest choice.\n *\n * No text overlay: redactions don't carry typed content.\n */\n\nexport interface RedactStageElements {\n readonly container: HTMLDivElement;\n readonly imageCanvas: HTMLCanvasElement;\n readonly regionsCanvas: HTMLCanvasElement;\n readonly liveCanvas: HTMLCanvasElement;\n readonly hitArea: HTMLDivElement;\n readonly handlesLayer: HTMLDivElement;\n}\n\nexport function buildRedactStage(): RedactStageElements {\n const container = document.createElement('div');\n container.className = 'kalotyp-redact-stage';\n\n const imageCanvas = document.createElement('canvas');\n imageCanvas.className = 'kalotyp-redact-image';\n imageCanvas.setAttribute('aria-hidden', 'true');\n\n const regionsCanvas = document.createElement('canvas');\n regionsCanvas.className = 'kalotyp-redact-regions';\n regionsCanvas.setAttribute('aria-hidden', 'true');\n\n const liveCanvas = document.createElement('canvas');\n liveCanvas.className = 'kalotyp-redact-live';\n liveCanvas.setAttribute('aria-hidden', 'true');\n\n const hitArea = document.createElement('div');\n hitArea.className = 'kalotyp-redact-hit';\n hitArea.setAttribute('role', 'presentation');\n\n const handlesLayer = document.createElement('div');\n handlesLayer.className = 'kalotyp-redact-handles';\n handlesLayer.setAttribute('role', 'group');\n handlesLayer.setAttribute('aria-label', 'Selected redaction region');\n\n container.appendChild(imageCanvas);\n container.appendChild(regionsCanvas);\n container.appendChild(liveCanvas);\n container.appendChild(hitArea);\n container.appendChild(handlesLayer);\n\n return { container, imageCanvas, regionsCanvas, liveCanvas, hitArea, handlesLayer };\n}\n","/**\n * Mount the redact plugin's stage UI and wire up:\n * - the layered canvases (image / regions / live);\n * - the bottom panel (mode toolbar, colour picker, insert/delete);\n * - pointer drag-to-define for new regions;\n * - the selection layer (corner handles + per-handle resize);\n * - the per-region coordinate inputs (Phase 6.4 keyboard a11y);\n * - keyboard shortcuts (Delete/Backspace removes selection; Esc\n * deselects; arrow keys nudge the selected region);\n * - revalidation against upstream-bounds changes.\n */\n\nimport {\n type Point,\n type RedactMode,\n type RedactRegion,\n type RedactState,\n type SourceImage,\n type Store,\n type Viewport,\n type ViewportController,\n addRegion,\n computeViewport,\n createCenteredRedactRegion,\n deleteRedactRegion,\n mintRegionId,\n normaliseRedactExtent,\n pointDisplayToImage,\n replaceRedactRegion,\n revalidateRedactAgainstBounds,\n selectRedactRegion,\n selectedRedactRegionOf,\n setRedactCurrentColor,\n setRedactCurrentMode,\n setRedactRegionColor,\n setRedactRegionMode,\n} from '@magicpages/kalotyp-core';\nimport { type DragHandlers, attachPointerDrag, clientToElement } from '../annotate/pointer-drag.js';\nimport { buildRedactCoordInputs } from './coord-inputs.js';\nimport { type RedactPanel, buildRedactPanel } from './panel.js';\nimport { paintRedactImageLayer, paintRedactLiveLayer, paintRedactRegionsLayer } from './render.js';\nimport { buildRedactSelectionLayer } from './selection.js';\nimport { buildRedactStage } from './stage.js';\n\nconst STAGE_PADDING_PX = 32;\n\nexport interface MountRedactOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly store: Store<RedactState>;\n readonly viewport?: ViewportController;\n readonly onCommit?: () => void;\n readonly onAnnounce?: (message: string) => void;\n}\n\nexport interface MountRedactHandle {\n destroy(): void;\n}\n\nexport function mountRedactUtility(options: MountRedactOptions): MountRedactHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? (() => {});\n const announce = options.onAnnounce ?? (() => {});\n\n // First, revalidate against the upstream source's dimensions —\n // entering redact after a re-crop may bring outdated regions.\n const revalidated = revalidateRedactAgainstBounds(store.get(), {\n width: source.width,\n height: source.height,\n });\n if (revalidated !== store.get()) {\n store.update(() => revalidated);\n }\n\n const stage = buildRedactStage();\n stageHost.appendChild(stage.container);\n\n let viewport: Viewport = computeViewport(\n { width: 1, height: 1, padding: STAGE_PADDING_PX },\n { width: source.width, height: source.height },\n );\n let liveMarquee: {\n x: number;\n y: number;\n width: number;\n height: number;\n mode: RedactMode;\n color: string;\n } | null = null;\n\n function recomputeViewport(): void {\n const rect = stage.container.getBoundingClientRect();\n const stageDims = { width: rect.width, height: rect.height, padding: STAGE_PADDING_PX };\n const imageSize = { width: source.width, height: source.height };\n viewport = controller\n ? controller.computeViewport(stageDims, imageSize)\n : computeViewport(stageDims, imageSize);\n }\n\n function paintImage(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n paintRedactImageLayer(stage.imageCanvas, source, rect.width, rect.height, viewport);\n }\n\n function paintRegions(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n const state = store.get();\n paintRedactRegionsLayer(\n stage.regionsCanvas,\n source,\n state.regions,\n state.selectedId,\n rect.width,\n rect.height,\n viewport,\n );\n }\n\n function paintLive(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n paintRedactLiveLayer(stage.liveCanvas, liveMarquee, rect.width, rect.height, viewport);\n }\n\n function paintAll(): void {\n paintImage();\n paintRegions();\n paintLive();\n selectionLayer.update(selectedRedactRegionOf(store.get()), viewport);\n }\n\n function setLiveMarquee(\n next: {\n x: number;\n y: number;\n width: number;\n height: number;\n mode: RedactMode;\n color: string;\n } | null,\n ): void {\n liveMarquee = next;\n paintLive();\n }\n\n function toImageSpace(point: { clientX: number; clientY: number }): Point {\n const stagePoint = clientToElement(stage.container, point.clientX, point.clientY);\n return pointDisplayToImage(stagePoint, viewport);\n }\n\n // ----- Selection layer (handles + per-handle resize) -----\n const selectionLayer = buildRedactSelectionLayer({\n host: stage.handlesLayer,\n store,\n toImageSpace,\n getViewport: () => viewport,\n commit,\n });\n\n // ----- Pointer dispatch on the hit area -----\n const removeHitDrag = attachPointerDrag(stage.hitArea, (event) => {\n const state = store.get();\n // First: did the click land on an existing region? If so, select\n // it and start a body-move drag. Otherwise, drag-to-create.\n const image = toImageSpace(event);\n const hit = pickRegion(state.regions, image);\n if (hit) {\n if (state.selectedId !== hit.id) {\n store.update((current) => selectRedactRegion(current, hit.id));\n }\n return startBodyMoveGesture(hit, event);\n }\n return startCreateGesture(state, event);\n });\n\n function startBodyMoveGesture(initial: RedactRegion, event: PointerEvent): DragHandlers {\n const startImage = toImageSpace(event);\n return {\n onMove(point) {\n const here = toImageSpace(point);\n const dx = here.x - startImage.x;\n const dy = here.y - startImage.y;\n store.update((current) =>\n replaceRedactRegion(current, {\n ...initial,\n x: initial.x + dx,\n y: initial.y + dy,\n }),\n );\n },\n onCommit() {\n commit();\n },\n onCancel() {\n store.update((current) => replaceRedactRegion(current, initial));\n },\n };\n }\n\n function startCreateGesture(state: RedactState, event: PointerEvent): DragHandlers {\n const startImage = toImageSpace(event);\n let lastImage = startImage;\n return {\n onMove(point) {\n const raw = toImageSpace(point);\n // Shift constrains to a square — same convention the annotate\n // plugin uses for rect/ellipse drag-to-create.\n if (point.shiftKey) {\n const dx = raw.x - startImage.x;\n const dy = raw.y - startImage.y;\n const size = Math.max(Math.abs(dx), Math.abs(dy));\n const sx = dx === 0 ? 1 : Math.sign(dx);\n const sy = dy === 0 ? 1 : Math.sign(dy);\n lastImage = { x: startImage.x + sx * size, y: startImage.y + sy * size };\n } else {\n lastImage = raw;\n }\n setLiveMarquee({\n x: startImage.x,\n y: startImage.y,\n width: lastImage.x - startImage.x,\n height: lastImage.y - startImage.y,\n mode: state.currentMode,\n color: state.currentColor,\n });\n },\n onCommit() {\n setLiveMarquee(null);\n const extent = normaliseRedactExtent({\n x: startImage.x,\n y: startImage.y,\n width: lastImage.x - startImage.x,\n height: lastImage.y - startImage.y,\n });\n // Drop zero-extent gestures (a click that didn't drag).\n if (extent.width < 4 || extent.height < 4) return;\n const { id, nextRegionNumber } = mintRegionId(state);\n const region: RedactRegion = {\n id,\n x: extent.x,\n y: extent.y,\n width: extent.width,\n height: extent.height,\n mode: state.currentMode,\n color: state.currentColor,\n };\n store.update((current) => ({ ...addRegion(current, region), nextRegionNumber }));\n commit();\n },\n onCancel() {\n setLiveMarquee(null);\n },\n };\n }\n\n function insertDefaultAtCenter(): void {\n const state = store.get();\n const { id, nextRegionNumber } = mintRegionId(state);\n const region = createCenteredRedactRegion({\n imageSize: { width: source.width, height: source.height },\n mode: state.currentMode,\n color: state.currentColor,\n id,\n });\n store.update((current) => ({ ...addRegion(current, region), nextRegionNumber }));\n announce(\n 'Redaction region placed at centre. Use arrow keys to nudge, or edit coordinates below.',\n );\n requestAnimationFrame(() => {\n const firstInput = coordInputs.container.querySelector<HTMLInputElement>(\n '.kalotyp-redact-coords-input',\n );\n firstInput?.focus();\n firstInput?.select();\n });\n commit();\n }\n\n // ----- Coordinate inputs (keyboard a11y) -----\n const coordInputs = buildRedactCoordInputs({\n onRegionChanged: (region) => {\n store.update((current) => replaceRedactRegion(current, region));\n commit();\n },\n });\n\n // ----- Panel -----\n const initialState = store.get();\n const panel: RedactPanel = buildRedactPanel({\n initialMode: initialState.currentMode,\n initialColor: initialState.currentColor,\n canDelete: initialState.selectedId !== null,\n coordInputs: coordInputs.container,\n onSelectMode: (mode) => {\n // Mode toggle does double duty: set the mode for new regions\n // *and* update the selected region's mode if one is selected\n // (Phase 6.4 brief — \"mode change on selected region\").\n store.update((current) => {\n const next = setRedactCurrentMode(current, mode);\n if (next.selectedId === null) return next;\n return setRedactRegionMode(next, next.selectedId, mode);\n });\n commit();\n },\n onColorChange: (color) => {\n store.update((current) => {\n const next = setRedactCurrentColor(current, color);\n if (next.selectedId === null) return next;\n return setRedactRegionColor(next, next.selectedId, color);\n });\n commit();\n },\n onInsertAtCenter: () => insertDefaultAtCenter(),\n onDeleteSelected: () => {\n const id = store.get().selectedId;\n if (!id) return;\n store.update((current) => deleteRedactRegion(current, id));\n commit();\n },\n });\n utilHost.appendChild(panel.container);\n\n // ----- Initial paint + observers -----\n recomputeViewport();\n paintAll();\n\n const resizeObserver = new ResizeObserver(() => {\n recomputeViewport();\n paintAll();\n });\n resizeObserver.observe(stage.container);\n\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n recomputeViewport();\n paintAll();\n });\n });\n\n // Repaint regions and panel state on every store change. This is\n // identical to the annotate plugin's subscription rhythm; keeps a\n // pointer drag, a coordinate edit, an undo, and a panel toggle on\n // one consistent re-render path.\n let lastRegions = store.get().regions;\n let lastSelected = store.get().selectedId;\n let lastMode = store.get().currentMode;\n let lastColor = store.get().currentColor;\n\n const unsubscribe = store.subscribe((next) => {\n const regionsChanged = next.regions !== lastRegions;\n const selectionChanged = next.selectedId !== lastSelected;\n if (regionsChanged) {\n lastRegions = next.regions;\n paintRegions();\n }\n if (selectionChanged) {\n lastSelected = next.selectedId;\n panel.setCanDelete(next.selectedId !== null);\n }\n if (next.currentMode !== lastMode) {\n lastMode = next.currentMode;\n panel.setActiveMode(next.currentMode);\n }\n if (next.currentColor !== lastColor) {\n lastColor = next.currentColor;\n panel.setColor(next.currentColor);\n }\n selectionLayer.update(selectedRedactRegionOf(next), viewport);\n if (selectionChanged || regionsChanged) {\n // Sync the coordinate inputs to whichever region is now\n // selected; the inputs are a render of the selected region's\n // image-space coordinates.\n coordInputs.updateForRegion(selectedRedactRegionOf(next));\n }\n });\n\n // ----- Keyboard handlers -----\n const onKeyDown = (event: KeyboardEvent): void => {\n const target = event.target as Element | null;\n if (isEditableTarget(target)) return;\n const state = store.get();\n if (event.key === 'Escape') {\n if (state.selectedId !== null) {\n event.preventDefault();\n event.stopPropagation();\n store.update((current) => selectRedactRegion(current, null));\n announce('Selection cleared.');\n }\n return;\n }\n if (event.key === 'Delete' || event.key === 'Backspace') {\n if (state.selectedId === null) return;\n event.preventDefault();\n const id = state.selectedId;\n store.update((current) => deleteRedactRegion(current, id));\n commit();\n return;\n }\n if (\n event.key === 'ArrowUp' ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowLeft' ||\n event.key === 'ArrowRight'\n ) {\n const selected = selectedRedactRegionOf(state);\n if (!selected) return;\n if (event.ctrlKey || event.altKey || event.metaKey) return;\n const step = event.shiftKey ? 10 : 1;\n const dx = event.key === 'ArrowLeft' ? -step : event.key === 'ArrowRight' ? step : 0;\n const dy = event.key === 'ArrowUp' ? -step : event.key === 'ArrowDown' ? step : 0;\n event.preventDefault();\n store.update((current) =>\n replaceRedactRegion(current, {\n ...selected,\n x: selected.x + dx,\n y: selected.y + dy,\n }),\n );\n commit();\n }\n };\n document.addEventListener('keydown', onKeyDown, true);\n\n return {\n destroy() {\n document.removeEventListener('keydown', onKeyDown, true);\n removeHitDrag();\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n coordInputs.destroy();\n selectionLayer.destroy();\n stage.container.remove();\n panel.container.remove();\n },\n };\n}\n\n/**\n * Topmost (last-drawn) region containing the supplied image-space\n * point. Returns `undefined` when no region matches. Same pattern\n * as the annotate plugin's `pickShape`.\n */\nfunction pickRegion(\n regions: ReadonlyArray<RedactRegion>,\n point: { x: number; y: number },\n): RedactRegion | undefined {\n for (let i = regions.length - 1; i >= 0; i--) {\n const region = regions[i];\n if (!region) continue;\n if (\n point.x >= region.x &&\n point.x <= region.x + region.width &&\n point.y >= region.y &&\n point.y <= region.y + region.height\n ) {\n return region;\n }\n }\n return undefined;\n}\n\nfunction isEditableTarget(target: Element | null): boolean {\n if (!target) return false;\n const tag = target.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true;\n return (target as HTMLElement).isContentEditable === true;\n}\n","import {\n type RedactState,\n type UtilityPlugin,\n bakeRedact,\n initialRedactState,\n} from '@magicpages/kalotyp-core';\nimport { mountRedactUtility } from './mount.js';\n\nexport interface RedactPluginOptions {\n /** Where the plugin's panel UI mounts. */\n readonly panelHost: HTMLElement;\n}\n\n/**\n * Build the redact `UtilityPlugin` instance for one editor session.\n * redact is the chain link between annotate and resize;\n * `bake` is called by the chain runner with the post-annotate\n * composite as input and returns a fresh `SourceImage` with each\n * region redacted.\n */\nexport function createRedactPlugin(options: RedactPluginOptions): UtilityPlugin<RedactState> {\n return {\n id: 'redact',\n init: (ctx) =>\n initialRedactState({ imageSize: { width: ctx.source.width, height: ctx.source.height } }),\n mount(stageHost, ctx, store) {\n const handle = mountRedactUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: 'redact' }),\n onAnnounce: (message) => ctx.bus.emit('announce', { message }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: (state, source) => bakeRedact({ regions: state.regions }, source),\n };\n}\n","/**\n * Mount the frame plugin: a preset thumbnail strip (six entries\n * matching Ghost's `frameOptions` order) plus a colour picker for the\n * presets that accept colour customisation. The stage shows the\n * upstream-baked source with the active frame composited on top.\n *\n * frame is the chain's tail link, so the live preview\n * is conceptually \"what the user gets on Save.\" We render the source\n * + frame composite into a single preview canvas; clicking a\n * thumbnail updates the store, the preview re-renders, and the\n * editor's history captures the commit.\n */\n\nimport {\n FRAME_PRESETS,\n type FramePreset,\n type FramePresetId,\n type FrameState,\n type SourceImage,\n type Store,\n type Viewport,\n type ViewportController,\n computeViewport,\n frameOutputSize,\n paintInsideFrame,\n setFrameColor,\n setFramePreset,\n} from '@magicpages/kalotyp-core';\n\nconst STAGE_PADDING_PX = 32;\n\nexport interface MountFrameOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly store: Store<FrameState>;\n readonly viewport?: ViewportController | undefined;\n /**\n * Optional locale callbacks Ghost passes via `frameOptions[i][1]`.\n * The factory wires this from the Ghost adapter; the playground\n * passes nothing and the strip falls back to the preset's default\n * label.\n */\n readonly labels?: Partial<Record<FramePresetId, string>> | undefined;\n readonly onCommit?: (() => void) | undefined;\n}\n\nexport interface MountFrameHandle {\n destroy(): void;\n}\n\nexport function mountFrameUtility(options: MountFrameOptions): MountFrameHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? (() => {});\n\n // Stage: a preview canvas the same shape the finetune/filter\n // plugins use. We render the upstream image + frame composite at\n // every paint.\n const previewContainer = document.createElement('div');\n previewContainer.className = 'kalotyp-stage-container kalotyp-frame-preview-container';\n const previewCanvas = document.createElement('canvas');\n previewCanvas.className = 'kalotyp-stage-image kalotyp-frame-preview-canvas';\n previewCanvas.setAttribute('aria-hidden', 'true');\n previewContainer.appendChild(previewCanvas);\n stageHost.appendChild(previewContainer);\n\n // Build the strip + colour picker.\n const initialState = store.get();\n const stripContainer = document.createElement('div');\n stripContainer.className = 'kalotyp-frame-panel';\n\n const strip = buildFrameStrip({\n presets: FRAME_PRESETS,\n initialActiveId: initialState.presetId,\n labels: options.labels,\n source,\n initialState,\n onPresetClick: (preset) => {\n store.update((current) => setFramePreset(current, preset.id));\n commit();\n },\n });\n stripContainer.appendChild(strip.container);\n\n const colorRow = buildColourRow({\n initialColor: initialState.color,\n initialPresetId: initialState.presetId,\n onColorChange: (color) => {\n store.update((current) => setFrameColor(current, color));\n commit();\n },\n });\n stripContainer.appendChild(colorRow.container);\n\n utilHost.appendChild(stripContainer);\n\n let viewport: Viewport = computeViewport(\n { width: 1, height: 1, padding: STAGE_PADDING_PX },\n { width: source.width, height: source.height },\n );\n\n function recomputeViewport(): void {\n const rect = previewContainer.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n const stageDims = { width: rect.width, height: rect.height, padding: STAGE_PADDING_PX };\n // The frame plugin's preview accounts for the *output* dimensions\n // (which differ from the source for Polaroid). Compute the\n // viewport against the frame's output dims so the framed\n // composite letterboxes correctly inside the stage.\n const state = store.get();\n const out = frameOutputSize(state.presetId, source.width, source.height);\n viewport = controller\n ? controller.computeViewport(stageDims, out)\n : computeViewport(stageDims, out);\n }\n\n function repaint(): void {\n const rect = previewContainer.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const targetW = Math.max(1, Math.round(rect.width * dpr));\n const targetH = Math.max(1, Math.round(rect.height * dpr));\n if (previewCanvas.width !== targetW) previewCanvas.width = targetW;\n if (previewCanvas.height !== targetH) previewCanvas.height = targetH;\n previewCanvas.style.width = `${rect.width}px`;\n previewCanvas.style.height = `${rect.height}px`;\n const ctx = previewCanvas.getContext('2d');\n if (!ctx) return;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, rect.width, rect.height);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n\n const state = store.get();\n const out = frameOutputSize(state.presetId, source.width, source.height);\n const dx = viewport.displayRect.x;\n const dy = viewport.displayRect.y;\n const dw = viewport.displayRect.width;\n const dh = viewport.displayRect.height;\n\n // For polaroid we need to draw the polaroid border first then\n // the source image inset by its inner-edge thickness. For the\n // other presets we draw the source full-size then paint the\n // frame on top.\n if (state.presetId === 'polaroid') {\n // Border colour fills the entire output rect.\n ctx.fillStyle = state.color;\n ctx.fillRect(dx, dy, dw, dh);\n // Compute the inset proportions (matching bake.ts). The inner\n // image lives at (top/left, top/top) of the output, sized so\n // its aspect ratio matches the source.\n const innerW = source.width * (dw / out.width);\n const innerH = source.height * (dh / out.height);\n const shorter = Math.min(source.width, source.height);\n const inset = Math.round(shorter * 0.05);\n const innerOffsetX = dx + (inset * dw) / out.width;\n const innerOffsetY = dy + (inset * dh) / out.height;\n ctx.drawImage(source.bitmap, innerOffsetX, innerOffsetY, innerW, innerH);\n } else {\n // Draw the source filling the displayRect.\n ctx.drawImage(source.bitmap, dx, dy, dw, dh);\n if (state.presetId !== 'none') {\n // Paint the frame in display-pixel space at the displayRect's\n // dimensions. Translate so frame coordinates start at (dx, dy).\n ctx.save();\n ctx.translate(dx, dy);\n paintInsideFrame(ctx, state.presetId, state.color, dw, dh);\n ctx.restore();\n }\n }\n }\n\n recomputeViewport();\n repaint();\n\n const resizeObserver = new ResizeObserver(() => {\n recomputeViewport();\n repaint();\n });\n resizeObserver.observe(previewContainer);\n\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n recomputeViewport();\n repaint();\n });\n });\n\n let rafScheduled = false;\n const unsubscribe = store.subscribe((next) => {\n strip.setActive(next.presetId);\n colorRow.setColor(next.color);\n colorRow.setEnabled(next.presetId !== 'none');\n if (rafScheduled) return;\n rafScheduled = true;\n requestAnimationFrame(() => {\n rafScheduled = false;\n recomputeViewport();\n repaint();\n });\n });\n\n return {\n destroy() {\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n previewContainer.remove();\n stripContainer.remove();\n },\n };\n}\n\ninterface FrameStripOptions {\n readonly presets: readonly FramePreset[];\n readonly initialActiveId: FramePresetId;\n readonly labels?: Partial<Record<FramePresetId, string>> | undefined;\n readonly source: SourceImage;\n readonly initialState: FrameState;\n onPresetClick(preset: FramePreset): void;\n}\n\ninterface FrameStrip {\n readonly container: HTMLDivElement;\n setActive(id: FramePresetId): void;\n}\n\nfunction buildFrameStrip(options: FrameStripOptions): FrameStrip {\n const container = document.createElement('div');\n container.className = 'kalotyp-frame-strip-wrap';\n container.setAttribute('role', 'radiogroup');\n container.setAttribute('aria-label', 'Frame presets');\n\n const list = document.createElement('div');\n list.className = 'kalotyp-frame-strip';\n container.appendChild(list);\n\n const buttons = new Map<FramePresetId, HTMLButtonElement>();\n for (const preset of options.presets) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-frame-thumb';\n button.dataset.presetId = preset.id;\n button.setAttribute('role', 'radio');\n button.setAttribute('aria-checked', 'false');\n const label = options.labels?.[preset.id] ?? preset.label;\n button.setAttribute('aria-label', `${label} frame`);\n button.title = label;\n\n const thumbWrap = document.createElement('span');\n thumbWrap.className = 'kalotyp-frame-thumb-image';\n const canvas = renderFrameThumbnail(preset, options.source, options.initialState.color);\n canvas.classList.add('kalotyp-frame-thumb-canvas');\n thumbWrap.appendChild(canvas);\n\n const activeBadge = document.createElement('span');\n activeBadge.className = 'kalotyp-frame-thumb-check';\n activeBadge.setAttribute('aria-hidden', 'true');\n activeBadge.innerHTML = '✓';\n thumbWrap.appendChild(activeBadge);\n\n const labelEl = document.createElement('span');\n labelEl.className = 'kalotyp-frame-thumb-label';\n labelEl.textContent = label;\n\n button.appendChild(thumbWrap);\n button.appendChild(labelEl);\n button.addEventListener('click', () => options.onPresetClick(preset));\n\n list.appendChild(button);\n buttons.set(preset.id, button);\n }\n\n function setActive(id: FramePresetId): void {\n for (const [presetId, button] of buttons) {\n const isActive = presetId === id;\n button.setAttribute('aria-checked', isActive ? 'true' : 'false');\n button.classList.toggle('kalotyp-frame-thumb--active', isActive);\n }\n }\n\n setActive(options.initialActiveId);\n\n return { container, setActive };\n}\n\ninterface ColourRowOptions {\n readonly initialColor: string;\n readonly initialPresetId: FramePresetId;\n onColorChange(color: string): void;\n}\n\ninterface ColourRow {\n readonly container: HTMLDivElement;\n setColor(color: string): void;\n setEnabled(enabled: boolean): void;\n}\n\nfunction buildColourRow(options: ColourRowOptions): ColourRow {\n const container = document.createElement('div');\n container.className = 'kalotyp-frame-color-row';\n\n const label = document.createElement('span');\n label.className = 'kalotyp-frame-color-label';\n label.textContent = 'Colour';\n\n const colorInput = document.createElement('input');\n colorInput.type = 'color';\n colorInput.className = 'kalotyp-frame-color';\n colorInput.value = normaliseColorForInput(options.initialColor);\n colorInput.setAttribute('aria-label', 'Frame colour (visual picker)');\n colorInput.addEventListener('change', () => options.onColorChange(colorInput.value));\n\n const hexInput = document.createElement('input');\n hexInput.type = 'text';\n hexInput.className = 'kalotyp-frame-hex';\n hexInput.value = normaliseColorForInput(options.initialColor);\n hexInput.maxLength = 7;\n hexInput.spellcheck = false;\n hexInput.autocomplete = 'off';\n hexInput.setAttribute('aria-label', 'Frame colour hex code');\n hexInput.setAttribute('placeholder', '#000000');\n hexInput.addEventListener('change', () => {\n const value = hexInput.value.trim();\n const normalised = normaliseHexInput(value);\n if (normalised) {\n hexInput.value = normalised;\n options.onColorChange(normalised);\n } else {\n hexInput.value = colorInput.value;\n }\n });\n\n // Helper text that explains the disabled state under the None\n // preset (Phase 6.6 polish). Hidden when colour customisation is\n // available. `aria-live` so screen-reader users hear the\n // explanation when they switch presets.\n const hint = document.createElement('span');\n hint.className = 'kalotyp-frame-color-hint';\n hint.textContent = 'Pick a frame preset to choose a colour.';\n hint.setAttribute('aria-live', 'polite');\n\n container.appendChild(label);\n container.appendChild(colorInput);\n container.appendChild(hexInput);\n container.appendChild(hint);\n\n // None doesn't accept colour customisation; disable the inputs to\n // avoid the user wasting time on a control that has no effect.\n function setEnabled(enabled: boolean): void {\n colorInput.disabled = !enabled;\n hexInput.disabled = !enabled;\n hint.hidden = enabled;\n }\n function setColor(color: string): void {\n const target = normaliseColorForInput(color);\n if (colorInput.value !== target) colorInput.value = target;\n if (hexInput.value.toLowerCase() !== target.toLowerCase()) hexInput.value = target;\n }\n setEnabled(options.initialPresetId !== 'none');\n\n return { container, setColor, setEnabled };\n}\n\n/**\n * Render a small preview canvas of the source under a given frame\n * preset. The thumbnails fit inside an 80×60 box (matching the filter\n * strip's max), and the source is downscaled to fit. The bake we\n * use here is the same path the chain uses on Save — `bakeFrame` is\n * synchronous-shaped (Promise-returning but resolves immediately),\n * so we render synchronously by reading the resulting canvas\n * bitmap into the thumbnail.\n *\n * For Polaroid the bake output is larger than the input, so the\n * thumbnail's aspect ratio differs from the other presets. We\n * re-letterbox the polaroid output into the same 80×60 box so the\n * strip's row height stays consistent.\n */\nfunction renderFrameThumbnail(\n preset: FramePreset,\n source: SourceImage,\n color: string,\n): HTMLCanvasElement {\n const max = { width: 80, height: 60 };\n const out = frameOutputSize(preset.id, source.width, source.height);\n const ratio = Math.min(max.width / out.width, max.height / out.height);\n const w = Math.max(1, Math.floor(out.width * ratio));\n const h = Math.max(1, Math.floor(out.height * ratio));\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n\n const canvas = document.createElement('canvas');\n canvas.width = Math.max(1, Math.round(w * dpr));\n canvas.height = Math.max(1, Math.round(h * dpr));\n canvas.style.width = `${w}px`;\n canvas.style.height = `${h}px`;\n const ctx = canvas.getContext('2d');\n if (!ctx) return canvas;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n\n // Render the source + frame at the thumbnail's display size. We\n // can't reuse `bakeFrame` directly because the bake produces an\n // output at the frame's full output dimensions; the thumbnail is\n // a downscale of that. So we approximate by drawing inline.\n if (preset.id === 'polaroid') {\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, w, h);\n const shorter = Math.min(source.width, source.height);\n const innerOffsetX = Math.round(((shorter * 0.05) / out.width) * w);\n const innerOffsetY = Math.round(((shorter * 0.05) / out.height) * h);\n const innerW = Math.round((source.width / out.width) * w);\n const innerH = Math.round((source.height / out.height) * h);\n ctx.drawImage(source.bitmap, innerOffsetX, innerOffsetY, innerW, innerH);\n } else {\n ctx.drawImage(source.bitmap, 0, 0, w, h);\n if (preset.id !== 'none') {\n paintInsideFrame(ctx, preset.id, color, w, h);\n }\n }\n\n return canvas;\n}\n\nfunction normaliseColorForInput(color: string): string {\n if (/^#[0-9a-fA-F]{6}$/.test(color)) return color;\n if (/^#[0-9a-fA-F]{3}$/.test(color)) {\n const r = color[1];\n const g = color[2];\n const b = color[3];\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return '#000000';\n}\n\nfunction normaliseHexInput(value: string): string | null {\n const cleaned = value.startsWith('#') ? value.slice(1) : value;\n if (/^[0-9a-fA-F]{6}$/.test(cleaned)) return `#${cleaned.toLowerCase()}`;\n if (/^[0-9a-fA-F]{3}$/.test(cleaned)) {\n const r = cleaned[0];\n const g = cleaned[1];\n const b = cleaned[2];\n return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();\n }\n return null;\n}\n","import {\n type FramePresetId,\n type FrameState,\n type UtilityPlugin,\n bakeFrame,\n initialFrameState,\n} from '@magicpages/kalotyp-core';\nimport { mountFrameUtility } from './mount.js';\n\nexport interface FramePluginOptions {\n /** Where the plugin's panel UI mounts. */\n readonly panelHost: HTMLElement;\n /**\n * Optional locale labels for the six presets. The Ghost adapter\n * passes Ghost's resolved `frameOptions[i][1](locale)` strings here\n * so the strip uses the user's localised labels; the playground\n * passes nothing and the strip falls back to the defaults from the\n * core preset list.\n */\n readonly labels?: Partial<Record<FramePresetId, string>> | undefined;\n}\n\nexport function createFramePlugin(options: FramePluginOptions): UtilityPlugin<FrameState> {\n return {\n id: 'frame',\n init: () => initialFrameState(),\n mount(stageHost, ctx, store) {\n const handle = mountFrameUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n labels: options.labels,\n onCommit: () => ctx.bus.emit('commit', { utility: 'frame' }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: (state, source) => bakeFrame(state, source),\n };\n}\n","import type { UtilityId } from '@magicpages/kalotyp-core';\n\nexport interface UtilityNavEntry {\n readonly id: UtilityId;\n readonly label: string;\n}\n\nexport interface UtilityNavElements {\n readonly container: HTMLDivElement;\n readonly buttons: ReadonlyMap<UtilityId, HTMLButtonElement>;\n}\n\nexport interface BuildUtilityNavOptions {\n /** Tabpanel id wired into each tab's `aria-controls` to complete the tablist/tab/tabpanel triple. */\n panelId: string;\n}\n\n/** Build the utility nav. Roving-tabindex per WAI-ARIA APG: active tab `tabindex=0`, others `-1`; Left/Right/Home/End move active state. */\nexport function buildUtilityNav(\n entries: readonly UtilityNavEntry[],\n initialActive: UtilityId,\n onSelect: (id: UtilityId) => void,\n options: BuildUtilityNavOptions,\n): UtilityNavElements {\n const container = document.createElement('div');\n container.className = 'kalotyp-util-nav';\n container.setAttribute('role', 'tablist');\n container.setAttribute('aria-label', 'Editor tools');\n\n const buttons = new Map<UtilityId, HTMLButtonElement>();\n for (const entry of entries) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-util-nav-button';\n button.dataset.utilityId = entry.id;\n button.id = `${options.panelId}-tab-${entry.id}`;\n button.setAttribute('role', 'tab');\n button.setAttribute('aria-selected', entry.id === initialActive ? 'true' : 'false');\n button.setAttribute('aria-controls', options.panelId);\n button.tabIndex = entry.id === initialActive ? 0 : -1;\n button.textContent = entry.label;\n button.addEventListener('click', () => onSelect(entry.id));\n container.appendChild(button);\n buttons.set(entry.id, button);\n }\n\n // WAI-ARIA APG \"automatic activation\" pattern: Left/Right/Home/End change selection + focus.\n container.addEventListener('keydown', (event) => {\n if (\n event.key !== 'ArrowLeft' &&\n event.key !== 'ArrowRight' &&\n event.key !== 'Home' &&\n event.key !== 'End'\n ) {\n return;\n }\n const ids = entries.map((e) => e.id);\n const currentEl = event.target as HTMLElement | null;\n const currentId = currentEl?.dataset?.utilityId as UtilityId | undefined;\n const currentIdx = currentId ? ids.indexOf(currentId) : -1;\n if (currentIdx === -1) return;\n\n let nextIdx = currentIdx;\n if (event.key === 'ArrowLeft') nextIdx = (currentIdx - 1 + ids.length) % ids.length;\n else if (event.key === 'ArrowRight') nextIdx = (currentIdx + 1) % ids.length;\n else if (event.key === 'Home') nextIdx = 0;\n else if (event.key === 'End') nextIdx = ids.length - 1;\n\n const nextId = ids[nextIdx];\n if (!nextId || nextId === currentId) return;\n event.preventDefault();\n onSelect(nextId);\n buttons.get(nextId)?.focus();\n });\n\n return { container, buttons };\n}\n\n/** Update active tab state and scroll the newly-active tab into view (no-op on non-overflowing desktop strips). */\nexport function setActiveUtilityButton(\n nav: UtilityNavElements,\n active: UtilityId,\n panel?: HTMLElement,\n): void {\n for (const [id, button] of nav.buttons.entries()) {\n const isActive = id === active;\n button.setAttribute('aria-selected', isActive ? 'true' : 'false');\n button.tabIndex = isActive ? 0 : -1;\n if (isActive) {\n // jsdom's scrollIntoView stub throws on options args — swallow to keep tests green.\n try {\n button.scrollIntoView({ block: 'nearest', inline: 'nearest', behavior: 'smooth' });\n } catch {\n // ignore\n }\n if (panel) panel.setAttribute('aria-labelledby', button.id);\n }\n }\n}\n","/**\n * Focus trap + initial-focus + restore-on-release for the editor dialog.\n *\n * The Tab keydown path doesn't trap screen readers' virtual cursors — `aria-modal=true`\n * is what makes content outside the dialog inert for assistive tech. On release, focus\n * returns to the element that was active before the trap installed (usually the trigger).\n */\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'details',\n 'summary',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]',\n].join(', ');\n\nexport interface InstallFocusTrapOptions {\n readonly host: HTMLElement;\n /** Element to focus on mount. Defaults to the first focusable descendant of `host`. */\n readonly initialFocus?: HTMLElement;\n}\n\nexport interface FocusTrapHandle {\n /** Hook for future use; currently a no-op because focusables are re-queried on every Tab. */\n refresh(): void;\n /** Tear down listeners and restore focus to the trigger element. */\n release(): void;\n}\n\n/** Install the focus trap and seed initial focus. */\nexport function installFocusTrap(options: InstallFocusTrapOptions): FocusTrapHandle {\n const { host } = options;\n const trigger =\n document.activeElement instanceof HTMLElement && document.activeElement !== document.body\n ? document.activeElement\n : null;\n\n function getFocusable(): HTMLElement[] {\n // No offsetParent/visibility filtering: the selector handles `disabled`, jsdom\n // reports `offsetParent === null` for every element regardless of layout, and\n // we never display-hide focusable controls mid-interaction.\n return Array.from(host.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));\n }\n\n // rAF so the DOM is laid out before focus — initial tabIndex may have been set in this same tick.\n const seedTarget = options.initialFocus ?? getFocusable()[0];\n if (seedTarget) {\n requestAnimationFrame(() => seedTarget.focus());\n }\n\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key !== 'Tab') return;\n const focusable = getFocusable();\n if (focusable.length === 0) {\n event.preventDefault();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (!first || !last) return;\n const active = document.activeElement as HTMLElement | null;\n if (event.shiftKey) {\n if (active === first || !host.contains(active)) {\n event.preventDefault();\n last.focus();\n }\n } else {\n if (active === last || !host.contains(active)) {\n event.preventDefault();\n first.focus();\n }\n }\n };\n\n document.addEventListener('keydown', onKeyDown, true);\n\n return {\n refresh: () => {},\n release: () => {\n document.removeEventListener('keydown', onKeyDown, true);\n if (trigger?.isConnected) {\n try {\n trigger.focus();\n } catch {\n // ignore\n }\n }\n },\n };\n}\n","import type { ViewportController } from '@magicpages/kalotyp-core';\n\n/**\n * Editor-level wheel + multi-pointer (pinch / two-finger pan) handler driving a `ViewportController`.\n *\n * - Wheel: zoom anchored at the cursor (1.0015 per `deltaY` unit).\n * - Two pointers: pinch + pan applied together each frame (no pinch-vs-pan state machine —\n * applying both feels more natural and matches Snapseed / Apple Photos).\n * - Single pointer: pass-through to plugin gestures.\n */\nexport type StageGestureHandle = () => void;\n\nconst WHEEL_ZOOM_PER_DELTA = 1.0015;\n\ninterface PointerSample {\n readonly id: number;\n x: number;\n y: number;\n}\n\ninterface ActiveGesture {\n lastDistance: number;\n lastMidpoint: { x: number; y: number };\n}\n\nexport function attachStageGestures(\n stage: HTMLElement,\n controller: ViewportController,\n): StageGestureHandle {\n const pointers = new Map<number, PointerSample>();\n let gesture: ActiveGesture | null = null;\n\n function stageRect(): DOMRect {\n return stage.getBoundingClientRect();\n }\n\n function clientToStage(clientX: number, clientY: number): { x: number; y: number } {\n const rect = stageRect();\n return { x: clientX - rect.left, y: clientY - rect.top };\n }\n\n function stageCenter(): { x: number; y: number } {\n const rect = stageRect();\n return { x: rect.width / 2, y: rect.height / 2 };\n }\n\n function snapshotMidpointAndDistance(): {\n midpoint: { x: number; y: number };\n distance: number;\n } | null {\n if (pointers.size < 2) return null;\n // With 3+ pointers (rare on phones, common on multi-touch trackpads), use the first two.\n const iter = pointers.values();\n const first = iter.next().value as PointerSample;\n const second = iter.next().value as PointerSample;\n const midpoint = clientToStage((first.x + second.x) / 2, (first.y + second.y) / 2);\n const dx = second.x - first.x;\n const dy = second.y - first.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n return { midpoint, distance };\n }\n\n function startGestureIfPaired(): void {\n if (pointers.size < 2 || gesture !== null) return;\n const snap = snapshotMidpointAndDistance();\n if (!snap || snap.distance <= 0) return;\n gesture = { lastDistance: snap.distance, lastMidpoint: snap.midpoint };\n controller.setPinching(true);\n }\n\n function endGesture(): void {\n if (gesture === null) return;\n gesture = null;\n controller.setPinching(false);\n }\n\n function onPointerDown(event: PointerEvent): void {\n // No pointer-capture here — plugin handlers may own single-pointer drags. A second pointer\n // opportunistically promotes to a pinch/pan gesture, interrupting any in-flight plugin drag.\n if (event.pointerType === 'mouse' && event.button !== 0) return;\n pointers.set(event.pointerId, {\n id: event.pointerId,\n x: event.clientX,\n y: event.clientY,\n });\n startGestureIfPaired();\n }\n\n function onPointerMove(event: PointerEvent): void {\n const tracked = pointers.get(event.pointerId);\n if (!tracked) return;\n tracked.x = event.clientX;\n tracked.y = event.clientY;\n if (gesture === null) return;\n const snap = snapshotMidpointAndDistance();\n if (!snap || snap.distance <= 0) return;\n\n // Pinch anchored at the midpoint so the gesture's center stays fixed.\n const zoomDelta = snap.distance / gesture.lastDistance;\n if (zoomDelta !== 1) {\n controller.zoomAt(zoomDelta, snap.midpoint, stageCenter());\n }\n\n const dx = snap.midpoint.x - gesture.lastMidpoint.x;\n const dy = snap.midpoint.y - gesture.lastMidpoint.y;\n if (dx !== 0 || dy !== 0) {\n controller.panBy(dx, dy);\n }\n\n gesture.lastDistance = snap.distance;\n gesture.lastMidpoint = snap.midpoint;\n\n event.preventDefault();\n }\n\n function onPointerUpOrCancel(event: PointerEvent): void {\n if (!pointers.has(event.pointerId)) return;\n pointers.delete(event.pointerId);\n if (pointers.size < 2) endGesture();\n }\n\n function onWheel(event: WheelEvent): void {\n // ctrlKey + wheel on macOS trackpads = pinch; we want our zoom for both that and plain wheel.\n const factor = WHEEL_ZOOM_PER_DELTA ** -event.deltaY;\n if (factor === 1) return;\n const anchor = clientToStage(event.clientX, event.clientY);\n controller.zoomAt(factor, anchor, stageCenter());\n event.preventDefault();\n }\n\n // Listeners on bubble phase so plugin handlers see pointerdown first; we only intervene on the 2nd pointer.\n stage.addEventListener('pointerdown', onPointerDown);\n stage.addEventListener('pointermove', onPointerMove);\n stage.addEventListener('pointerup', onPointerUpOrCancel);\n stage.addEventListener('pointercancel', onPointerUpOrCancel);\n stage.addEventListener('pointerleave', onPointerUpOrCancel);\n // { passive: false } required: Chromium/WebKit treat wheel as passive by default, blocking preventDefault.\n stage.addEventListener('wheel', onWheel, { passive: false });\n\n return () => {\n stage.removeEventListener('pointerdown', onPointerDown);\n stage.removeEventListener('pointermove', onPointerMove);\n stage.removeEventListener('pointerup', onPointerUpOrCancel);\n stage.removeEventListener('pointercancel', onPointerUpOrCancel);\n stage.removeEventListener('pointerleave', onPointerUpOrCancel);\n stage.removeEventListener('wheel', onWheel);\n pointers.clear();\n if (gesture !== null) controller.setPinching(false);\n gesture = null;\n };\n}\n","/**\n * Nested overlay (modal or anchored popover) used for the Output popover, Preferences modal,\n * and Keyboard cheatsheet. Lives inside the editor host so the editor's click-capture scope\n * and Ghost's modal-service allowlist still cover it. Traps Tab in capture phase so it wins\n * over the editor's outer trap.\n */\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]',\n].join(', ');\n\nexport interface NestedModalOptions {\n readonly host: HTMLElement;\n /** When supplied, the overlay positions as a popover above this element instead of centred. */\n readonly anchor?: HTMLElement;\n readonly title: string;\n readonly body: HTMLElement;\n /** CSS class added to the overlay container. */\n readonly variant?: string;\n /** Default true; the popover variant typically sets false (Esc / click-outside dismiss). */\n readonly showCloseButton?: boolean;\n readonly onClose: () => void;\n}\n\nexport interface NestedModalHandle {\n readonly element: HTMLElement;\n close(): void;\n}\n\n/** Open a nested modal or anchored popover. Caller owns the body content. */\nexport function openNestedModal(options: NestedModalOptions): NestedModalHandle {\n const previouslyFocused =\n document.activeElement instanceof HTMLElement ? document.activeElement : null;\n\n const overlay = document.createElement('div');\n overlay.className = 'kalotyp-nested-overlay';\n if (options.variant) overlay.classList.add(options.variant);\n\n const surface = document.createElement('div');\n surface.className = 'kalotyp-nested-surface';\n surface.setAttribute('role', 'dialog');\n surface.setAttribute('aria-modal', 'true');\n surface.tabIndex = -1;\n\n const titleId = `kalotyp-nested-title-${Math.random().toString(36).slice(2, 8)}`;\n surface.setAttribute('aria-labelledby', titleId);\n\n const header = document.createElement('div');\n header.className = 'kalotyp-nested-header';\n\n const heading = document.createElement('h3');\n heading.id = titleId;\n heading.className = 'kalotyp-nested-title';\n heading.textContent = options.title;\n header.appendChild(heading);\n\n let closeButton: HTMLButtonElement | undefined;\n if (options.showCloseButton !== false) {\n closeButton = document.createElement('button');\n closeButton.type = 'button';\n closeButton.className = 'kalotyp-nested-close';\n closeButton.setAttribute('aria-label', `Close ${options.title}`);\n closeButton.textContent = '×';\n closeButton.addEventListener('click', () => requestClose());\n header.appendChild(closeButton);\n }\n\n surface.appendChild(header);\n\n const bodyWrap = document.createElement('div');\n bodyWrap.className = 'kalotyp-nested-body';\n bodyWrap.appendChild(options.body);\n surface.appendChild(bodyWrap);\n\n overlay.appendChild(surface);\n options.host.appendChild(overlay);\n\n if (options.anchor) {\n overlay.classList.add('kalotyp-nested-overlay--popover');\n positionAnchored(overlay, surface, options.anchor);\n const reposition = (): void =>\n positionAnchored(overlay, surface, options.anchor as HTMLElement);\n window.addEventListener('resize', reposition);\n overlay.dataset.resizeListenerAttached = '1';\n overlay.addEventListener('kalotyp-nested-cleanup', () => {\n window.removeEventListener('resize', reposition);\n });\n } else {\n overlay.classList.add('kalotyp-nested-overlay--modal');\n }\n\n requestAnimationFrame(() => surface.focus());\n\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n requestClose();\n return;\n }\n if (event.key !== 'Tab') return;\n const focusable = Array.from(surface.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));\n if (focusable.length === 0) {\n event.preventDefault();\n surface.focus();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (!first || !last) return;\n const active = document.activeElement as HTMLElement | null;\n if (event.shiftKey) {\n if (active === first || !surface.contains(active)) {\n event.preventDefault();\n last.focus();\n }\n } else {\n if (active === last || !surface.contains(active)) {\n event.preventDefault();\n first.focus();\n }\n }\n };\n\n // Capture phase so we win over the editor's outer Tab trap.\n document.addEventListener('keydown', onKeyDown, true);\n\n const onClickOutside = (event: MouseEvent): void => {\n const target = event.target as Node | null;\n if (!target) return;\n if (!surface.contains(target)) {\n requestClose();\n }\n };\n // Listen on overlay (dimming layer) not document, so other editor clicks pass through normally.\n overlay.addEventListener('mousedown', onClickOutside);\n\n let closing = false;\n function requestClose(): void {\n if (closing) return;\n closing = true;\n document.removeEventListener('keydown', onKeyDown, true);\n overlay.removeEventListener('mousedown', onClickOutside);\n overlay.dispatchEvent(new Event('kalotyp-nested-cleanup'));\n overlay.remove();\n if (previouslyFocused?.isConnected) {\n try {\n previouslyFocused.focus();\n } catch {\n /* trigger may have been removed; ignore */\n }\n }\n options.onClose();\n }\n\n return {\n element: overlay,\n close: requestClose,\n };\n}\n\nfunction positionAnchored(overlay: HTMLElement, surface: HTMLElement, anchor: HTMLElement): void {\n const anchorRect = anchor.getBoundingClientRect();\n const hostRect = overlay.parentElement?.getBoundingClientRect() ?? {\n left: 0,\n top: 0,\n right: window.innerWidth,\n bottom: window.innerHeight,\n };\n // Bottom edge 8px above anchor's top; right edge aligned to anchor's right (keeps it in the gutter).\n const surfaceRect = surface.getBoundingClientRect();\n const top = anchorRect.top - hostRect.top - surfaceRect.height - 8;\n const right = hostRect.right - anchorRect.right;\n surface.style.position = 'absolute';\n surface.style.top = `${Math.max(8, top)}px`;\n surface.style.right = `${Math.max(8, right)}px`;\n}\n","/**\n * Output popover anchored to the Save-button caret: format, quality, strip-metadata.\n * State commits immediately to the Store; no apply button. Unsupported formats\n * (per async runtime probe) are disabled in-place when the probe resolves.\n */\n\nimport {\n type OutputMimeChoice,\n type OutputState,\n type Store,\n canEncodeMime,\n setOutputMime,\n setOutputQuality,\n setStripMetadata,\n} from '@magicpages/kalotyp-core';\nimport { openNestedModal } from '../dom/nested-modal.js';\n\nexport interface OpenOutputPopoverOptions {\n readonly host: HTMLElement;\n readonly anchor: HTMLElement;\n readonly store: Store<OutputState>;\n /** Click of \"Save and close\" inside the popover. */\n readonly onSaveAndClose: () => void;\n /** Mirrors the editor's Save-enabled state so the in-popover Save button stays in sync. */\n readonly canSave: () => boolean;\n readonly onClose: () => void;\n}\n\nexport interface OutputPopoverHandle {\n close(): void;\n}\n\ninterface FormatChoice {\n readonly value: OutputMimeChoice;\n readonly label: string;\n readonly description: string;\n /** Mime types the runtime must be able to encode for this option to be usable. */\n readonly requires: ReadonlyArray<string>;\n}\n\nconst FORMAT_CHOICES: ReadonlyArray<FormatChoice> = [\n {\n value: 'auto',\n label: 'Auto',\n description: 'WebP when supported, PNG fallback.',\n requires: [],\n },\n {\n value: 'image/webp',\n label: 'WebP',\n description: 'Best compression for most images.',\n requires: ['image/webp'],\n },\n {\n value: 'image/avif',\n label: 'AVIF',\n description: 'Smallest size; slower encode.',\n requires: ['image/avif'],\n },\n {\n value: 'image/jpeg',\n label: 'JPEG',\n description: 'Universal; no transparency.',\n requires: ['image/jpeg'],\n },\n {\n value: 'image/png',\n label: 'PNG',\n description: 'Lossless; preserves transparency.',\n requires: ['image/png'],\n },\n];\n\nexport function openOutputPopover(options: OpenOutputPopoverOptions): OutputPopoverHandle {\n const { host, anchor, store } = options;\n\n const body = document.createElement('div');\n body.className = 'kalotyp-output-popover-body';\n\n const formatLabel = document.createElement('label');\n formatLabel.className = 'kalotyp-output-row';\n const formatLabelText = document.createElement('span');\n formatLabelText.className = 'kalotyp-output-row-label';\n formatLabelText.textContent = 'Format';\n formatLabel.appendChild(formatLabelText);\n\n const formatSelect = document.createElement('select');\n formatSelect.className = 'kalotyp-output-format';\n formatSelect.setAttribute('aria-label', 'Output format');\n for (const choice of FORMAT_CHOICES) {\n const option = document.createElement('option');\n option.value = choice.value;\n option.textContent = choice.label;\n formatSelect.appendChild(option);\n }\n formatSelect.value = store.get().mimeChoice;\n formatSelect.addEventListener('change', () => {\n const value = formatSelect.value as OutputMimeChoice;\n store.update((current) => setOutputMime(current, value));\n });\n formatLabel.appendChild(formatSelect);\n\n const formatHint = document.createElement('p');\n formatHint.className = 'kalotyp-output-hint';\n formatHint.setAttribute('aria-live', 'polite');\n\n const qualityLabel = document.createElement('label');\n qualityLabel.className = 'kalotyp-output-row';\n const qualityLabelText = document.createElement('span');\n qualityLabelText.className = 'kalotyp-output-row-label';\n qualityLabelText.textContent = 'Quality';\n qualityLabel.appendChild(qualityLabelText);\n\n const qualitySlider = document.createElement('input');\n qualitySlider.type = 'range';\n qualitySlider.className = 'kalotyp-output-quality';\n qualitySlider.min = '50';\n qualitySlider.max = '100';\n qualitySlider.step = '1';\n qualitySlider.value = String(Math.round(store.get().quality * 100));\n qualitySlider.setAttribute('aria-label', 'Output quality');\n qualitySlider.addEventListener('input', () => {\n store.update((current) => setOutputQuality(current, qualitySlider.valueAsNumber / 100));\n });\n qualityLabel.appendChild(qualitySlider);\n\n const qualityReadout = document.createElement('span');\n qualityReadout.className = 'kalotyp-output-quality-readout';\n qualityReadout.setAttribute('aria-hidden', 'true');\n qualityReadout.textContent = `${Math.round(store.get().quality * 100)}`;\n qualityLabel.appendChild(qualityReadout);\n\n const summary = document.createElement('p');\n summary.className = 'kalotyp-output-summary';\n summary.setAttribute('aria-live', 'polite');\n\n // Toggle stores intent; the encoder honours it only when source and output are both JPEG\n // (canvas re-encoding strips metadata otherwise). The hint below surfaces that constraint.\n const metadataRow = document.createElement('label');\n metadataRow.className = 'kalotyp-output-metadata-row';\n const metadataCheckbox = document.createElement('input');\n metadataCheckbox.type = 'checkbox';\n metadataCheckbox.className = 'kalotyp-output-metadata-checkbox';\n metadataCheckbox.checked = store.get().stripMetadata;\n metadataCheckbox.addEventListener('change', () => {\n store.update((current) => setStripMetadata(current, metadataCheckbox.checked));\n });\n const metadataText = document.createElement('span');\n metadataText.className = 'kalotyp-output-metadata-text';\n metadataText.textContent = 'Strip EXIF, GPS, and camera metadata on save';\n metadataRow.appendChild(metadataCheckbox);\n metadataRow.appendChild(metadataText);\n\n const metadataHint = document.createElement('p');\n metadataHint.className = 'kalotyp-output-metadata-hint';\n metadataHint.setAttribute('aria-live', 'polite');\n\n const footer = document.createElement('footer');\n footer.className = 'kalotyp-output-footer';\n\n const doneButton = document.createElement('button');\n doneButton.type = 'button';\n doneButton.className = 'kalotyp-output-done';\n doneButton.textContent = 'Done';\n doneButton.setAttribute('aria-label', 'Close output settings');\n doneButton.addEventListener('click', () => handle.close());\n\n const saveButton = document.createElement('button');\n saveButton.type = 'button';\n saveButton.className = 'kalotyp-output-save';\n saveButton.textContent = 'Save and close';\n saveButton.addEventListener('click', () => {\n if (!options.canSave()) return;\n options.onSaveAndClose();\n });\n\n footer.appendChild(doneButton);\n footer.appendChild(saveButton);\n\n body.appendChild(formatLabel);\n body.appendChild(formatHint);\n body.appendChild(qualityLabel);\n body.appendChild(summary);\n body.appendChild(metadataRow);\n body.appendChild(metadataHint);\n body.appendChild(footer);\n\n anchor.setAttribute('aria-expanded', 'true');\n\n const handle = openNestedModal({\n host,\n anchor,\n title: 'Output settings',\n body,\n variant: 'kalotyp-output-popover',\n showCloseButton: true,\n onClose: () => {\n anchor.setAttribute('aria-expanded', 'false');\n unsubscribe();\n options.onClose();\n },\n });\n\n function renderState(state: OutputState): void {\n if (formatSelect.value !== state.mimeChoice) formatSelect.value = state.mimeChoice;\n const percent = Math.round(state.quality * 100);\n if (qualitySlider.valueAsNumber !== percent) qualitySlider.value = String(percent);\n qualityReadout.textContent = String(percent);\n const choice = FORMAT_CHOICES.find((c) => c.value === state.mimeChoice);\n formatHint.textContent = choice?.description ?? '';\n summary.textContent = describeSelection(state);\n // PNG ignores quality — visually disable the slider for that format.\n const qualityActive = state.mimeChoice !== 'image/png';\n qualitySlider.disabled = !qualityActive;\n qualityReadout.style.opacity = qualityActive ? '1' : '0.4';\n if (metadataCheckbox.checked !== state.stripMetadata) {\n metadataCheckbox.checked = state.stripMetadata;\n }\n metadataHint.textContent = describeMetadataHint(state);\n saveButton.disabled = !options.canSave();\n }\n\n renderState(store.get());\n const unsubscribe = store.subscribe(renderState);\n\n // 'auto' is always enabled because its resolver falls back to PNG internally.\n void (async () => {\n for (const choice of FORMAT_CHOICES) {\n if (choice.requires.length === 0) continue;\n const supported = (\n await Promise.all(choice.requires.map((mime) => canEncodeMime(mime)))\n ).every(Boolean);\n if (!supported) {\n const option = formatSelect.querySelector<HTMLOptionElement>(\n `option[value=\"${choice.value}\"]`,\n );\n if (option) {\n option.disabled = true;\n option.textContent = `${choice.label} (unsupported)`;\n }\n }\n }\n })();\n\n return {\n close: () => handle.close(),\n };\n}\n\nfunction describeSelection(state: OutputState): string {\n if (state.mimeChoice === 'image/png') return 'PNG · lossless';\n const percent = Math.round(state.quality * 100);\n switch (state.mimeChoice) {\n case 'auto':\n return `Auto · ${percent}% quality`;\n case 'image/webp':\n return `WebP · ${percent}% quality`;\n case 'image/avif':\n return `AVIF · ${percent}% quality`;\n case 'image/jpeg':\n return `JPEG · ${percent}% quality`;\n default:\n return `${state.mimeChoice} · ${percent}% quality`;\n }\n}\n\n/** When strip is off, surface the JPEG→JPEG constraint so EXIF loss doesn't surprise the user. */\nfunction describeMetadataHint(state: OutputState): string {\n if (state.stripMetadata) return '';\n if (state.mimeChoice === 'image/jpeg') {\n return 'EXIF preserved when the source is also JPEG.';\n }\n if (state.mimeChoice === 'auto') {\n return 'Metadata is preserved only when the resolved output is JPEG.';\n }\n return 'Metadata can only be preserved when the output is JPEG.';\n}\n","/**\n * Per-site preferences for the editor. Persisted to `localStorage`\n * (browser-origin-scoped automatically); the `siteScope` derived from\n * the Ghost-passed `src` URL further namespaces same-origin-multi-site\n * installs by Ghost-content-root.\n *\n * What's persisted:\n * - Output format and quality (always, no opt-out — saving the\n * user's preferred output format is universally useful).\n * - Annotation default style (colour + stroke width) — opt-in via\n * `rememberAnnotationStyle`.\n * - Last-used filter preset id — opt-in via `rememberFilter`.\n * - Last-used frame preset id and colour — opt-in via\n * `rememberFrame`.\n *\n * What's *not* persisted:\n * - The current edit (transient session state — undo/redo handles\n * in-session reverts).\n * - The image source (Ghost passes a fresh `src` each open).\n * - Anything that could leak identity across sessions (no telemetry,\n * no usage counts).\n *\n * The total payload sits well under 1 KB; quota is not a concern, and\n * even repeated writes-on-commit don't approach `localStorage`'s 5–10 MB\n * per-origin budget.\n */\n\nimport type { OutputMimeChoice } from '@magicpages/kalotyp-core';\n\nexport interface KalotypPreferences {\n readonly outputMimeChoice: OutputMimeChoice;\n readonly outputQuality: number;\n /**\n * Whether to strip EXIF / GPS / camera metadata on save. Stored\n * alongside the output format so the user's privacy choice\n * survives across sessions like the format and quality do.\n */\n readonly outputStripMetadata: boolean;\n readonly rememberAnnotationStyle: boolean;\n readonly rememberFilter: boolean;\n readonly rememberFrame: boolean;\n readonly lastAnnotationColor: string;\n readonly lastAnnotationStrokeWidth: number;\n readonly lastFilterPresetId: string | null;\n readonly lastFramePresetId: string | null;\n readonly lastFrameColor: string;\n}\n\nexport const DEFAULT_PREFERENCES: KalotypPreferences = {\n outputMimeChoice: 'auto',\n outputQuality: 0.85,\n outputStripMetadata: true,\n rememberAnnotationStyle: true,\n rememberFilter: true,\n rememberFrame: true,\n lastAnnotationColor: '#ff3b30',\n lastAnnotationStrokeWidth: 4,\n lastFilterPresetId: null,\n lastFramePresetId: null,\n lastFrameColor: '#000000',\n};\n\n/**\n * Schema version baked into the storage key. Bumped if the shape\n * changes incompatibly — older saved payloads then fail their JSON\n * parse and fall back to defaults rather than producing an\n * ambiguous mix of fields.\n */\nconst STORAGE_KEY_PREFIX = 'kalotyp:prefs:v1';\n\n/**\n * Derive a stable per-site scope identifier from the `src` option\n * Ghost passes. Used as the suffix on the `localStorage` key so two\n * Ghost installs on the same origin (rare but possible, e.g. a host\n * serving `/site-a/` and `/site-b/` Ghost instances) get separate\n * preferences.\n *\n * Strategy:\n * - URL `src`: use `origin + the leading path up to (and excluding)\n * `/content/`. Ghost serves images at `<origin>/content/images/...`,\n * so the prefix uniquely identifies a Ghost root.\n * - Blob/File `src` (test environments, future direct-feed\n * scenarios): fall back to `window.location.origin`.\n * - Anything else falls to `'default'` so the read still succeeds.\n */\nexport function getSiteScope(srcOption: unknown): string {\n if (typeof srcOption === 'string') {\n try {\n const url = new URL(srcOption);\n const path = url.pathname;\n const contentIdx = path.indexOf('/content/');\n const root = contentIdx === -1 ? '' : path.slice(0, contentIdx);\n return `${url.origin}${root}`;\n } catch {\n // Fall through.\n }\n }\n if (typeof window !== 'undefined') {\n return window.location.origin;\n }\n return 'default';\n}\n\nfunction storageKey(siteScope: string): string {\n return `${STORAGE_KEY_PREFIX}:${siteScope}`;\n}\n\n/**\n * Load preferences for `siteScope`. Missing keys fill from defaults\n * (sparse merge) so a partial historical payload doesn't break the\n * editor. Any read error (no storage, quota, parse failure) returns\n * the defaults — preferences are best-effort.\n */\nexport function loadPreferences(siteScope: string): KalotypPreferences {\n try {\n if (typeof localStorage === 'undefined') return DEFAULT_PREFERENCES;\n const raw = localStorage.getItem(storageKey(siteScope));\n if (!raw) return DEFAULT_PREFERENCES;\n const parsed = JSON.parse(raw) as Partial<KalotypPreferences>;\n return mergeWithDefaults(parsed);\n } catch {\n return DEFAULT_PREFERENCES;\n }\n}\n\n/**\n * Save preferences for `siteScope`. Writes are best-effort: quota\n * errors and missing storage are swallowed so a failed save never\n * crashes the editor. Validation happens here too — any non-finite\n * or out-of-range value is replaced with the default before write.\n */\nexport function savePreferences(siteScope: string, prefs: KalotypPreferences): void {\n try {\n if (typeof localStorage === 'undefined') return;\n const sanitised = mergeWithDefaults(prefs);\n localStorage.setItem(storageKey(siteScope), JSON.stringify(sanitised));\n } catch {\n // Quota exceeded, storage disabled, etc. Best-effort: ignore.\n }\n}\n\nfunction mergeWithDefaults(partial: Partial<KalotypPreferences>): KalotypPreferences {\n const outputMimeChoice = isOutputMimeChoice(partial.outputMimeChoice)\n ? partial.outputMimeChoice\n : DEFAULT_PREFERENCES.outputMimeChoice;\n const outputQuality = clampToRange(\n partial.outputQuality,\n 0,\n 1,\n DEFAULT_PREFERENCES.outputQuality,\n );\n const outputStripMetadata =\n typeof partial.outputStripMetadata === 'boolean'\n ? partial.outputStripMetadata\n : DEFAULT_PREFERENCES.outputStripMetadata;\n const rememberAnnotationStyle =\n typeof partial.rememberAnnotationStyle === 'boolean'\n ? partial.rememberAnnotationStyle\n : DEFAULT_PREFERENCES.rememberAnnotationStyle;\n const rememberFilter =\n typeof partial.rememberFilter === 'boolean'\n ? partial.rememberFilter\n : DEFAULT_PREFERENCES.rememberFilter;\n const rememberFrame =\n typeof partial.rememberFrame === 'boolean'\n ? partial.rememberFrame\n : DEFAULT_PREFERENCES.rememberFrame;\n const lastAnnotationColor =\n typeof partial.lastAnnotationColor === 'string'\n ? partial.lastAnnotationColor\n : DEFAULT_PREFERENCES.lastAnnotationColor;\n const lastAnnotationStrokeWidth = clampToRange(\n partial.lastAnnotationStrokeWidth,\n 1,\n 40,\n DEFAULT_PREFERENCES.lastAnnotationStrokeWidth,\n );\n const lastFilterPresetId =\n partial.lastFilterPresetId === null || typeof partial.lastFilterPresetId === 'string'\n ? partial.lastFilterPresetId\n : DEFAULT_PREFERENCES.lastFilterPresetId;\n const lastFramePresetId =\n partial.lastFramePresetId === null || typeof partial.lastFramePresetId === 'string'\n ? partial.lastFramePresetId\n : DEFAULT_PREFERENCES.lastFramePresetId;\n const lastFrameColor =\n typeof partial.lastFrameColor === 'string'\n ? partial.lastFrameColor\n : DEFAULT_PREFERENCES.lastFrameColor;\n\n return {\n outputMimeChoice,\n outputQuality,\n outputStripMetadata,\n rememberAnnotationStyle,\n rememberFilter,\n rememberFrame,\n lastAnnotationColor,\n lastAnnotationStrokeWidth,\n lastFilterPresetId,\n lastFramePresetId,\n lastFrameColor,\n };\n}\n\nfunction isOutputMimeChoice(value: unknown): value is OutputMimeChoice {\n return (\n value === 'auto' ||\n value === 'image/png' ||\n value === 'image/jpeg' ||\n value === 'image/webp' ||\n value === 'image/avif'\n );\n}\n\nfunction clampToRange(value: unknown, lo: number, hi: number, fallback: number): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return fallback;\n if (value < lo) return lo;\n if (value > hi) return hi;\n return value;\n}\n","/**\n * Preferences modal. Lets the user adjust persisted defaults across\n * editor sessions: output format/quality, plus per-utility \"remember\n * what I picked last time\" toggles.\n *\n * Lifecycle: the caller supplies the current preferences and an\n * `onChange` handler that fires on every internal interaction. The\n * caller is responsible for persisting via `savePreferences` —\n * keeping this modal storage-free means it can be tested without\n * a `localStorage` shim and lets the editor coordinate batched\n * writes with its own debounce.\n */\n\nimport type { OutputMimeChoice } from '@magicpages/kalotyp-core';\nimport { openNestedModal } from '../dom/nested-modal.js';\nimport { DEFAULT_PREFERENCES, type KalotypPreferences } from './storage.js';\n\nexport interface OpenPreferencesModalOptions {\n readonly host: HTMLElement;\n readonly initial: KalotypPreferences;\n onChange(next: KalotypPreferences): void;\n onClose(): void;\n}\n\nexport interface PreferencesModalHandle {\n close(): void;\n}\n\ninterface FormatChoice {\n readonly value: OutputMimeChoice;\n readonly label: string;\n}\n\nconst FORMAT_CHOICES: ReadonlyArray<FormatChoice> = [\n { value: 'auto', label: 'Auto (recommended)' },\n { value: 'image/webp', label: 'WebP' },\n { value: 'image/avif', label: 'AVIF' },\n { value: 'image/jpeg', label: 'JPEG' },\n { value: 'image/png', label: 'PNG' },\n];\n\nexport function openPreferencesModal(options: OpenPreferencesModalOptions): PreferencesModalHandle {\n let state: KalotypPreferences = options.initial;\n\n function update(patch: Partial<KalotypPreferences>): void {\n state = { ...state, ...patch };\n options.onChange(state);\n }\n\n const body = document.createElement('div');\n body.className = 'kalotyp-preferences-body';\n\n // ----- Output defaults -----\n const outputSection = document.createElement('section');\n outputSection.className = 'kalotyp-preferences-section';\n outputSection.innerHTML = '<h4>Output defaults</h4>';\n\n const formatRow = makeRow('Format');\n const formatSelect = document.createElement('select');\n formatSelect.className = 'kalotyp-output-format';\n formatSelect.setAttribute('aria-label', 'Default output format');\n for (const choice of FORMAT_CHOICES) {\n const option = document.createElement('option');\n option.value = choice.value;\n option.textContent = choice.label;\n formatSelect.appendChild(option);\n }\n formatSelect.value = state.outputMimeChoice;\n formatSelect.addEventListener('change', () => {\n update({ outputMimeChoice: formatSelect.value as OutputMimeChoice });\n });\n formatRow.appendChild(formatSelect);\n outputSection.appendChild(formatRow);\n\n const qualityRow = makeRow('Quality');\n const qualitySlider = document.createElement('input');\n qualitySlider.type = 'range';\n qualitySlider.className = 'kalotyp-output-quality';\n qualitySlider.min = '50';\n qualitySlider.max = '100';\n qualitySlider.step = '1';\n qualitySlider.value = String(Math.round(state.outputQuality * 100));\n qualitySlider.setAttribute('aria-label', 'Default output quality');\n qualitySlider.addEventListener('input', () => {\n update({ outputQuality: qualitySlider.valueAsNumber / 100 });\n qualityReadout.textContent = String(qualitySlider.valueAsNumber);\n });\n const qualityReadout = document.createElement('span');\n qualityReadout.className = 'kalotyp-output-quality-readout';\n qualityReadout.textContent = String(Math.round(state.outputQuality * 100));\n qualityReadout.setAttribute('aria-hidden', 'true');\n qualityRow.appendChild(qualitySlider);\n qualityRow.appendChild(qualityReadout);\n outputSection.appendChild(qualityRow);\n\n // Strip-metadata toggle. Same control as the Output popover —\n // mirrored here so the user can adjust the default without opening\n // the popover.\n const stripToggle = makeToggle(\n 'Strip EXIF, GPS, and camera metadata on save',\n state.outputStripMetadata,\n (checked) => update({ outputStripMetadata: checked }),\n );\n outputSection.appendChild(stripToggle);\n\n // ----- Remember toggles -----\n const memorySection = document.createElement('section');\n memorySection.className = 'kalotyp-preferences-section';\n memorySection.innerHTML = '<h4>Remember across sessions</h4>';\n\n const annotateToggle = makeToggle(\n 'Annotation style (colour + stroke width)',\n state.rememberAnnotationStyle,\n (checked) => update({ rememberAnnotationStyle: checked }),\n );\n const filterToggle = makeToggle('Last filter preset', state.rememberFilter, (checked) =>\n update({ rememberFilter: checked }),\n );\n const frameToggle = makeToggle('Last frame preset', state.rememberFrame, (checked) =>\n update({ rememberFrame: checked }),\n );\n memorySection.appendChild(annotateToggle);\n memorySection.appendChild(filterToggle);\n memorySection.appendChild(frameToggle);\n\n // ----- Footer -----\n const footer = document.createElement('footer');\n footer.className = 'kalotyp-preferences-footer';\n\n const resetButton = document.createElement('button');\n resetButton.type = 'button';\n resetButton.className = 'kalotyp-preferences-reset';\n resetButton.textContent = 'Reset to defaults';\n resetButton.addEventListener('click', () => {\n state = { ...DEFAULT_PREFERENCES };\n options.onChange(state);\n syncControls(state);\n });\n\n const doneButton = document.createElement('button');\n doneButton.type = 'button';\n doneButton.className = 'kalotyp-preferences-done';\n doneButton.textContent = 'Done';\n doneButton.addEventListener('click', () => handle.close());\n\n footer.appendChild(resetButton);\n footer.appendChild(doneButton);\n\n body.appendChild(outputSection);\n body.appendChild(memorySection);\n body.appendChild(footer);\n\n const handle = openNestedModal({\n host: options.host,\n title: 'Preferences',\n body,\n variant: 'kalotyp-preferences-modal',\n showCloseButton: true,\n onClose: options.onClose,\n });\n\n function syncControls(s: KalotypPreferences): void {\n formatSelect.value = s.outputMimeChoice;\n qualitySlider.value = String(Math.round(s.outputQuality * 100));\n qualityReadout.textContent = String(Math.round(s.outputQuality * 100));\n syncToggle(stripToggle, s.outputStripMetadata);\n syncToggle(annotateToggle, s.rememberAnnotationStyle);\n syncToggle(filterToggle, s.rememberFilter);\n syncToggle(frameToggle, s.rememberFrame);\n }\n\n return {\n close: () => handle.close(),\n };\n}\n\nfunction makeRow(label: string): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'kalotyp-output-row';\n const labelSpan = document.createElement('span');\n labelSpan.className = 'kalotyp-output-row-label';\n labelSpan.textContent = label;\n row.appendChild(labelSpan);\n return row;\n}\n\nfunction makeToggle(\n label: string,\n initial: boolean,\n onChange: (checked: boolean) => void,\n): HTMLLabelElement {\n const row = document.createElement('label');\n row.className = 'kalotyp-preferences-toggle';\n const checkbox = document.createElement('input');\n checkbox.type = 'checkbox';\n checkbox.checked = initial;\n checkbox.addEventListener('change', () => onChange(checkbox.checked));\n const span = document.createElement('span');\n span.textContent = label;\n row.appendChild(checkbox);\n row.appendChild(span);\n return row;\n}\n\nfunction syncToggle(row: HTMLLabelElement, value: boolean): void {\n const input = row.querySelector<HTMLInputElement>('input[type=\"checkbox\"]');\n if (input && input.checked !== value) input.checked = value;\n}\n","/**\n * Manifest of every keyboard shortcut. The cheatsheet UI reads this list.\n * This is a manifest, not a registry — handlers live with the features that own them.\n * \"Ctrl\" stands in for Ctrl/Cmd: handlers check `ctrlKey || metaKey`, so one label covers both.\n */\n\nexport type KeyboardShortcutContext = 'editor' | 'annotate' | 'redact' | 'text';\n\nexport interface KeyboardShortcut {\n /** Tokens rendered as `<kbd>` pills joined by \" + \". Use platform-neutral spelling (\"Ctrl\", \"Esc\", \"Arrow keys\"). */\n readonly keys: ReadonlyArray<string>;\n readonly description: string;\n readonly context: KeyboardShortcutContext;\n}\n\nexport const KEYBOARD_SHORTCUTS: ReadonlyArray<KeyboardShortcut> = [\n {\n keys: ['?'],\n description: 'Show keyboard shortcuts',\n context: 'editor',\n },\n {\n keys: ['Esc'],\n description: 'Close editor (or clear current selection)',\n context: 'editor',\n },\n {\n keys: ['Ctrl', 'Z'],\n description: 'Undo',\n context: 'editor',\n },\n {\n keys: ['Ctrl', 'Shift', 'Z'],\n description: 'Redo',\n context: 'editor',\n },\n {\n keys: ['Ctrl', 'Y'],\n description: 'Redo (alternate)',\n context: 'editor',\n },\n {\n keys: ['Tab'],\n description: 'Move focus to next control',\n context: 'editor',\n },\n {\n keys: ['Shift', 'Tab'],\n description: 'Move focus to previous control',\n context: 'editor',\n },\n\n {\n keys: ['Delete'],\n description: 'Delete the selected shape',\n context: 'annotate',\n },\n {\n keys: ['Arrow keys'],\n description: 'Nudge the selected shape by 1 px',\n context: 'annotate',\n },\n {\n keys: ['Shift', 'Arrow keys'],\n description: 'Nudge the selected shape by 10 px',\n context: 'annotate',\n },\n {\n keys: ['Shift', 'while drawing'],\n description: 'Constrain shape (square, 45° line, circle)',\n context: 'annotate',\n },\n\n {\n keys: ['Delete'],\n description: 'Delete the selected redaction region',\n context: 'redact',\n },\n {\n keys: ['Arrow keys'],\n description: 'Nudge the selected region by 1 px',\n context: 'redact',\n },\n {\n keys: ['Shift', 'Arrow keys'],\n description: 'Nudge the selected region by 10 px',\n context: 'redact',\n },\n\n {\n keys: ['Enter'],\n description: 'Commit the text and close the editor',\n context: 'text',\n },\n {\n keys: ['Shift', 'Enter'],\n description: 'Insert a line break',\n context: 'text',\n },\n {\n keys: ['Esc'],\n description: 'Cancel the in-progress edit',\n context: 'text',\n },\n];\n\nexport const KEYBOARD_SHORTCUT_CONTEXT_LABELS: Readonly<Record<KeyboardShortcutContext, string>> = {\n editor: 'Editor',\n annotate: 'Annotate',\n redact: 'Redact',\n text: 'Text editing',\n};\n","/** Cheatsheet modal — renders `KEYBOARD_SHORTCUTS` grouped by context. Opened via `?`. */\n\nimport { openNestedModal } from '../dom/nested-modal.js';\nimport {\n KEYBOARD_SHORTCUTS,\n KEYBOARD_SHORTCUT_CONTEXT_LABELS,\n type KeyboardShortcut,\n type KeyboardShortcutContext,\n} from '../keyboard-shortcuts.js';\n\nexport interface OpenCheatsheetOptions {\n readonly host: HTMLElement;\n onClose(): void;\n}\n\nexport interface CheatsheetHandle {\n close(): void;\n}\n\nconst CONTEXT_ORDER: ReadonlyArray<KeyboardShortcutContext> = [\n 'editor',\n 'annotate',\n 'redact',\n 'text',\n];\n\nexport function openCheatsheet(options: OpenCheatsheetOptions): CheatsheetHandle {\n const body = document.createElement('div');\n body.className = 'kalotyp-cheatsheet-body';\n\n for (const context of CONTEXT_ORDER) {\n const entries = KEYBOARD_SHORTCUTS.filter((s) => s.context === context);\n if (entries.length === 0) continue;\n body.appendChild(buildSection(context, entries));\n }\n\n const handle = openNestedModal({\n host: options.host,\n title: 'Keyboard shortcuts',\n body,\n variant: 'kalotyp-cheatsheet-modal',\n showCloseButton: true,\n onClose: options.onClose,\n });\n\n return {\n close: () => handle.close(),\n };\n}\n\nfunction buildSection(\n context: KeyboardShortcutContext,\n entries: ReadonlyArray<KeyboardShortcut>,\n): HTMLElement {\n const section = document.createElement('section');\n section.className = 'kalotyp-cheatsheet-section';\n\n const heading = document.createElement('h4');\n heading.className = 'kalotyp-cheatsheet-heading';\n heading.textContent = KEYBOARD_SHORTCUT_CONTEXT_LABELS[context];\n section.appendChild(heading);\n\n const list = document.createElement('dl');\n list.className = 'kalotyp-cheatsheet-list';\n\n for (const shortcut of entries) {\n const dt = document.createElement('dt');\n dt.className = 'kalotyp-cheatsheet-keys';\n shortcut.keys.forEach((token, index) => {\n if (index > 0) {\n const plus = document.createElement('span');\n plus.className = 'kalotyp-cheatsheet-plus';\n plus.setAttribute('aria-hidden', 'true');\n plus.textContent = '+';\n dt.appendChild(plus);\n }\n const kbd = document.createElement('kbd');\n kbd.className = 'kalotyp-cheatsheet-kbd';\n kbd.textContent = token;\n dt.appendChild(kbd);\n });\n\n const dd = document.createElement('dd');\n dd.className = 'kalotyp-cheatsheet-description';\n dd.textContent = shortcut.description;\n\n list.appendChild(dt);\n list.appendChild(dd);\n }\n\n section.appendChild(list);\n return section;\n}\n"],"names":["ArrowRight","Check","ChevronDown","Circle","FlipHorizontal2","FlipVertical2","Highlighter","Keyboard","Link2Off","Link2","MousePointer2","Pencil","Plus","Redo2","Settings","Square","Trash2","Type","Undo2","X","DEFAULT_SVG_ATTRS","iconHtml","node","attrs","merged","svgAttrs","k","v","children","tag","childAttrs","resolve","name","TypeIcon","CircleIcon","CloseIcon","CheckIcon","PlusIcon","SettingsIcon","KeyboardIcon","icon","nextEditorId","buildShellDom","options","editorId","titleId","editor","title","modal","closeButton","prefsButton","root","main","stage","utilMain","navTools","utilFooter","exportGroup","exportButton","exportInner","outputSettingsButton","liveRegion","mountShell","dom","onExport","onClose","onOutputSettings","onPrefs","announceFlip","announce","message","IDENTITY_VIEWPORT_TRANSFORM","computeViewport","image","transform","innerWidth","innerHeight","fitScale","zoom","scale","width","height","baselineX","baselineY","x","y","clampedX","clampAxis","clampedY","rawPos","baseline","size","innerSize","padding","innerStart","innerEnd","minLeft","maxLeft","pointDisplayToImage","point","viewport","rectImageToDisplay","rect","createBakeCanvas","canUseOffscreenForBlobs","canvas","getBakeContext2D","bake","ctx","bakeCanvasToBlob","mimeType","quality","reject","blob","mimeSupportCache","canEncodeMime","cached","probe","translateClampedRect","dx","dy","bounds","moved","clampRectInside","roundRect","fitRectToBoundsWithRatio","targetRatio","boundsRatio","applyAspectRatio","anchor","currentRatio","reshaped","anchorRect","clamped","clampedRatio","RATIO_TOLERANCE","fitInsideAtAnchor","fitted","resizeRectFromHandle","handle","pointer","minSize","left","top","right","bottom","newLeft","newTop","newRight","newBottom","nx","ny","nw","nh","resized","anchorFor","isPresetVisible","preset","filter","ratio","filterPresets","presets","initialCropState","input","findCustomIndex","applyPresetByIndex","state","presetIndex","bakeCrop","source","rounded","clamp","w","h","n","lo","hi","DEFAULT_OUTPUT_STATE","clampQuality","value","setOutputMime","mimeChoice","setOutputQuality","setStripMetadata","stripMetadata","initialFlipState","toggleFlip","axis","isFlipNoOp","bakeFlip","sx","sy","tx","ty","FREE_ANGLE_MIN","FREE_ANGLE_MAX","FREE_ANGLE_STEP","initialRotateState","rotateClockwise","rotateCounterClockwise","setFreeAngle","angleDeg","snapped","isRotateNoOp","effectiveAngleDeg","largestInscribedRect","angleRad","c","s","denomA","denomD","capA","EPSILON","capD","outWidth","outHeight","bakeRotate","sub90Deg","isQuarterOnly","inscribed","MAX_DIMENSION","MIN_DIMENSION","initialResizeState","isResizeNoOp","resolveOutputSize","upstream","clampInt","setWidthPx","widthPx","scaleX","scaleY","setHeightPx","heightPx","setPercent","percent","clampScaleForPercent","setLockAspect","locked","bakeResize","targetW","targetH","halvings","countHalvingsNeeded","current","i","stepW","stepH","drawScaled","srcW","srcH","count","outW","outH","FINETUNE_MIN","FINETUNE_MAX","FINETUNE_STEP","DEFAULT_FINETUNE_STATE","FINETUNE_ADJUSTMENTS","initialFinetuneState","isFinetuneNoOp","setFinetune","key","next","clampSliderValue","resetFinetune","resetAllFinetune","buildFinetuneLut","lut","brightnessOffset","contrastFactor","exposureFactor","gammaExponent","gammaExponentFor","slider","applyFinetuneLutAndSaturation","src","dst","len","saturation","r0","g0","b0","r","g","b","applyClarity","blurred","clarity","amount","dr","dg","db","boxBlur3x3","tmp","xm","xp","im","ip","ym","yp","applyFinetuneToImageData","bakeFinetune","FILTER_PRESETS","finetuneStatesEqual","a","findActivePreset","HIGHLIGHT_DEFAULT_COLOR","HIGHLIGHT_DEFAULT_STROKE","TEXT_DEFAULT_FONT_SIZE","DEFAULT_PALETTE_COLOR","DEFAULT_STROKE_WIDTH","defaultStylePalette","initialAnnotateState","mintShapeId","setActiveTool","tool","setStyle","partial","selectShape","id","addShape","shape","replaceShape","changed","existing","deleteShape","normaliseRectExtent","extent","translateShape","p","assertNever","isKeyboardPlaceableKind","kind","createCenteredShape","imageSize","style","shortEdge","cx","cy","length","x1","x2","boundingBoxOf","estimateTextSize","alignToOrigin","head","minX","minY","maxX","maxY","ALL_SELECTION_HANDLES","selectionHandlePositions","rectFromHandleDrag","initial","text","fontSize","lines","maxLineLen","line","anchorX","align","PICK_TOLERANCE","pickShape","shapes","hitTest","pointInRect","inside","normaliseBox","outer","expandRect","inner","box","rx","ry","r2","tolerance","pointNearSegment","expanded","len2","ex","ey","t","projX","projY","MIN_SAMPLE_DISTANCE","decimatePoints","points","out","last","minSq","tail","tracePath","midX","midY","SYSTEM_FONT_STACK","bakeAnnotate","paintShape","paintText","paintRect","paintEllipse","paintArrow","paintFreehand","paintHighlight","lineHeight","headLength","headWidth","ux","uy","shaftEndX","shaftEndY","baseX","baseY","px","py","DEFAULT_REDACT_COLOR","DEFAULT_REDACT_MODE","initialRedactState","mintRegionId","addRegion","region","replaceRegion","deleteRegion","selectRegion","setCurrentMode","mode","setCurrentColor","color","setRegionMode","setRegionColor","selectedRegionOf","normaliseRegionExtent","createCenteredRegion","revalidateAgainstBounds","kept","clampRegion","selectedDropped","bakeRedact","paintRegion","paintSolid","paintPixelate","paintBlur","_source","longer","cells","gridW","gridH","small","createSmallCanvas","smallW","smallH","tinyW","tinyH","tiny","offscreen","FRAME_PRESETS","DEFAULT_FRAME_STATE","initialFrameState","isFrameNoOp","setFramePreset","presetId","currentPreset","findFramePreset","nextPreset","nextColor","setFrameColor","bakeFrame","bakePolaroid","bakeInsideFrame","paintInsideFrame","paintMatSharp","paintMatRound","paintLineSingle","paintCornerHooks","matThickness","drawCornerCutout","corner","inset","stroke","half","arm","drawHook","vx","vy","horizDir","vertDir","shorter","frameOutputSize","inputWidth","inputHeight","CORNER_INSET","positionHandles","display","cornerAnchors","edgeHandles","bodyHitArea","setAnchor","horizontalLength","verticalLength","setHorizontalEdge","setVerticalEdge","renderImageCanvas","stageWidth","stageHeight","dpr","MASK_FILL","OUTLINE_HALO","OUTLINE_HALO_WIDTH","OUTLINE_STROKE","OUTLINE_WIDTH","GRID_HALO","GRID_HALO_WIDTH","GRID_STROKE","GRID_WIDTH","renderOverlayCanvas","cropRectImage","imageRect","drawGridLines","buildPresetRow","visiblePresets","activeIndex","onSelect","container","buttons","index","label","button","setActivePresetButton","CORNERS","EDGES","buildStageElements","imageCanvas","overlayCanvas","handlesLayer","handles","direction","createEdgeButton","createCornerButton","labelFor","bindCropInteractions","elements","store","cleanups","attachResizeGesture","attachTranslateGesture","cleanup","element","attachPointerDrag","aspectRatio","stagePoint","clientToStage","imagePoint","event","originStage","here","dxImage","dyImage","factory","onPointerDown","handlers","pendingPoint","rafScheduled","flush","onPointerMove","moveEvent","finish","committed","onPointerUp","onPointerCancel","final","upEvent","cancelEvent","clientX","clientY","STAGE_PADDING_PX","mountCropUtility","stageHost","utilHost","presetFilter","controller","commit","noop","panelContainer","initialActive","mapToVisibleIndex","presetRow","visibleIndex","fullIndex","dimensions","buildCropDimensionsRow","recomputeViewport","stageDims","paintAll","paintOverlay","resizeObserver","viewportRafScheduled","unsubscribeViewport","overlayRafScheduled","unsubscribe","previous","syncPresetButtons","rectsEqual","interactions","xInput","makeNumericInput","yInput","wInput","hInput","readRect","field","sync","min","max","wrapper","labelSpan","fullPresets","active","UTILITY_ID","createCropPlugin","buildPreviewCanvas","previewViewportFor","intrinsic","paintPreview","draw","mountFlipUtility","preview","panel","buildFlipPanel","paint","syncPanel","horizontalButton","createToggleButton","verticalButton","onClick","createFlipPlugin","INSCRIBED_OUTLINE","INSCRIBED_HALO","mountRotateUtility","buildRotatePanel","deg","boundsW","boundsH","rotatedBounds","drawW","drawH","iw","ih","ix","iy","formatted","formatAngleValue","ccwButton","makeQuarterButton","cwButton","angleSliderLabel","angleSlider","angleInput","angleSuffix","resetButton","quarterGroup","angleInputGroup","straightenGroup","glyph","createRotatePlugin","mountResizeUtility","buildResizePanel","pct","percentDisplay","chainIconSvg","widthInput","makeNumberInput","RESIZE_MIN_DIMENSION","RESIZE_MAX_DIMENSION","heightInput","percentInput","lockButton","summary","dimsRow","scaleRow","createResizePlugin","buildFinetunePreviewPipeline","sourceBitmap","buffers","rebuild","getReadFrequentContext","imageData","dispose","mountFinetuneUtility","pipeline","buildFinetunePanel","upstreamSize","repaint","pxW","pxH","adj","row","rows","buildAdjustmentRow","resetAllButton","labelEl","createFinetunePlugin","THUMBNAIL_MAX_WIDTH","THUMBNAIL_MAX_HEIGHT","computeThumbnailDims","widthRatio","heightRatio","buildThumbnailCache","dims","baselineCanvas","baselineCtx","makeBlankCanvas","baselineImageData","cache","renderPreset","mountFilterUtility","thumbnailCache","strip","buildFilterStrip","syncStripActive","activeId","list","thumbWrap","activeBadge","isActive","createFilterPlugin","filterBakeIdentity","_state","RECT_FIELDS","ARROW_FIELDS","TEXT_FIELDS","buildCoordInputs","activeShape","activeKind","inputs","rebuildFor","fields","fieldsFor","spec","onAnyInputChange","syncValuesFromShape","setVal","el","edit","readCurrentEdit","updated","applyCoordEdit","num","y1","y2","TOOL_DEFS","PRESET_COLORS","buildAnnotatePanel","toolbar","toolButtons","def","styleRow","swatches","swatchGroup","swatch","lastValidHex","normaliseColorForInput","hexInput","normalised","normaliseHexInput","strokeLabel","strokeRange","deleteButton","insertButton","canInsertForTool","targetColor","matches","setCanDelete","canDelete","cleaned","lastShiftKey","schedule","onKeyToggle","keyEvent","clientToElement","prepareCanvas","paintImageLayer","paintShapesLayer","paintLiveLayer","paintMarqueeLayer","marqueeImage","dw","dh","drawX","drawY","buildSelectionLayer","host","toolContext","handleEls","handleLabel","startHandleResizeGesture","update","hideAll","tl","br","positionHandle","imageToDisplay","handlePositions","destroy","displayPoint","origin","selected","applyHandleDrag","cur","arrow","newSize","selectedShapeOf","buildAnnotateStage","shapesCanvas","liveCanvas","hitArea","textOverlay","buildTextEditor","onInput","onKeyDown","onPointerDownOutside","transformOriginFor","maxWidth","range","sel","constrainSquare","start","end","constrainAxisOrDiagonal","angle","constrainStroke","startRectGesture","startImage","nextShapeNumber","lastImage","raw","draft","startEllipseGesture","startArrowGesture","startFreehandGesture","freePoints","lastWasShift","lastConstrainedEnd","isHighlight","strokeWidth","finalPoints","startBodyMoveGesture","shapeId","initialShape","translate","replace","mountAnnotateUtility","liveShape","liveMarquee","selectionLayer","paintShapes","paintLive","setLiveShape","setLiveMarquee","toImageSpace","textEditor","removeHitDrag","startSelectGesture","startTextGesture","picked","startMarqueeGesture","marquee","normaliseExtent","hit","topmostShapeIntersectingMarquee","insertDefaultAtCenter","labelForKind","firstInput","coordInputs","initialState","applyColorToShape","applyStrokeWidthToShape","lastShapes","lastSelected","lastTool","lastStyle","shapesChanged","selectionChanged","target","isEditableTarget","step","bbox","rectsIntersect","createAnnotatePlugin","FIELDS","buildRedactCoordInputs","activeRegion","syncValuesFromRegion","readNumber","MODE_DEFS","buildRedactPanel","modeButtons","colorInput","colorGroup","colorHint","buttonGroup","setActiveMode","isSolid","setColor","paintRedactImageLayer","paintRedactRegionsLayer","regions","selectedId","tempCanvas","dispW","dispH","tempCtx","projected","paintRedactRegion","paintRedactLiveLayer","marqueeFillFor","hexWithAlpha","hex","alpha","match","buildRedactSelectionLayer","positions","handlePositionsFor","selectedRedactRegionOf","replaceRedactRegion","buildRedactStage","regionsCanvas","mountRedactUtility","revalidated","revalidateRedactAgainstBounds","paintImage","paintRegions","pickRegion","selectRedactRegion","startCreateGesture","normaliseRedactExtent","nextRegionNumber","createCenteredRedactRegion","setRedactCurrentMode","setRedactRegionMode","setRedactCurrentColor","setRedactRegionColor","deleteRedactRegion","lastRegions","lastMode","lastColor","regionsChanged","createRedactPlugin","mountFrameUtility","previewContainer","previewCanvas","stripContainer","buildFrameStrip","colorRow","buildColourRow","innerW","innerH","innerOffsetX","innerOffsetY","renderFrameThumbnail","setActive","hint","setEnabled","enabled","createFramePlugin","buildUtilityNav","entries","entry","ids","e","currentId","currentIdx","nextIdx","nextId","setActiveUtilityButton","nav","FOCUSABLE_SELECTOR","installFocusTrap","trigger","getFocusable","seedTarget","focusable","first","WHEEL_ZOOM_PER_DELTA","attachStageGestures","pointers","gesture","stageRect","stageCenter","snapshotMidpointAndDistance","iter","second","midpoint","distance","startGestureIfPaired","snap","endGesture","tracked","zoomDelta","onPointerUpOrCancel","onWheel","factor","openNestedModal","previouslyFocused","overlay","surface","header","heading","requestClose","bodyWrap","positionAnchored","reposition","onClickOutside","closing","hostRect","surfaceRect","FORMAT_CHOICES","openOutputPopover","body","formatLabel","formatLabelText","formatSelect","choice","option","formatHint","qualityLabel","qualityLabelText","qualitySlider","qualityReadout","metadataRow","metadataCheckbox","metadataText","metadataHint","footer","doneButton","saveButton","renderState","describeSelection","qualityActive","describeMetadataHint","mime","DEFAULT_PREFERENCES","STORAGE_KEY_PREFIX","getSiteScope","srcOption","url","path","contentIdx","storageKey","siteScope","loadPreferences","parsed","mergeWithDefaults","savePreferences","prefs","sanitised","outputMimeChoice","isOutputMimeChoice","outputQuality","clampToRange","outputStripMetadata","rememberAnnotationStyle","rememberFilter","rememberFrame","lastAnnotationColor","lastAnnotationStrokeWidth","lastFilterPresetId","lastFramePresetId","lastFrameColor","fallback","openPreferencesModal","patch","outputSection","formatRow","makeRow","qualityRow","stripToggle","makeToggle","checked","memorySection","annotateToggle","filterToggle","frameToggle","syncControls","syncToggle","onChange","checkbox","span","KEYBOARD_SHORTCUTS","KEYBOARD_SHORTCUT_CONTEXT_LABELS","CONTEXT_ORDER","openCheatsheet","context","buildSection","section","shortcut","dt","token","plus","kbd","dd"],"mappings":"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","x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/canvas/stage-gestures.ts","../src/dom/nested-modal.ts","../src/keyboard-shortcuts.ts","../src/cheatsheet/modal.ts","../src/dom/build-util-nav.ts","../src/dom/focus-trap.ts","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/arrow-right.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/check.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/chevron-down.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/circle.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/flip-horizontal-2.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/flip-vertical-2.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/highlighter.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/keyboard.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/link-2.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/link-2-off.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/mouse-pointer-2.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/pencil.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/plus.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/redo-2.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/settings.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/square.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/trash-2.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/type.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/undo-2.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/x.mjs","../src/icons.ts","../../core/src/canvas/bake-canvas.ts","../../core/src/canvas/viewport.ts","../../core/src/geometry/rect.ts","../../core/src/output/state.ts","../../core/src/plugins/annotate/smooth.ts","../../core/src/plugins/annotate/state.ts","../../core/src/plugins/annotate/bake.ts","../../core/src/plugins/annotate/geometry.ts","../../core/src/plugins/annotate/hit-test.ts","../../core/src/plugins/crop/aspect-ratio.ts","../../core/src/plugins/crop/bake.ts","../../core/src/plugins/crop/preset-filter.ts","../../core/src/plugins/crop/resize.ts","../../core/src/plugins/crop/state.ts","../../core/src/plugins/finetune/state.ts","../../core/src/plugins/filter/presets.ts","../../core/src/plugins/finetune/math.ts","../../core/src/plugins/finetune/bake.ts","../../core/src/plugins/flip/state.ts","../../core/src/plugins/flip/bake.ts","../../core/src/plugins/frame/state.ts","../../core/src/plugins/frame/bake.ts","../../core/src/plugins/redact/bake.ts","../../core/src/plugins/redact/state.ts","../../core/src/plugins/resize/state.ts","../../core/src/plugins/resize/bake.ts","../../core/src/plugins/rotate/inscribe.ts","../../core/src/plugins/rotate/state.ts","../../core/src/plugins/rotate/bake.ts","../src/output/popover.ts","../src/plugins/annotate/coord-inputs.ts","../src/plugins/annotate/panel.ts","../src/plugins/annotate/pointer-drag.ts","../src/plugins/annotate/render.ts","../src/plugins/annotate/selection.ts","../src/plugins/annotate/stage.ts","../src/plugins/annotate/text-editor.ts","../src/plugins/annotate/tools.ts","../src/plugins/annotate/mount.ts","../src/plugins/annotate/plugin.ts","../src/canvas/position-handles.ts","../src/canvas/render-image.ts","../src/canvas/render-overlay.ts","../src/dom/build-preset-row.ts","../src/dom/build-stage.ts","../src/plugins/crop/interaction.ts","../src/plugins/crop/mount.ts","../src/plugins/crop/plugin.ts","../src/canvas/preview-canvas.ts","../src/plugins/finetune/preview.ts","../src/plugins/filter/thumbnails.ts","../src/plugins/filter/mount.ts","../src/plugins/filter/plugin.ts","../src/plugins/finetune/mount.ts","../src/plugins/finetune/plugin.ts","../src/plugins/flip/mount.ts","../src/plugins/flip/plugin.ts","../src/plugins/frame/mount.ts","../src/plugins/frame/plugin.ts","../src/plugins/redact/coord-inputs.ts","../src/plugins/redact/panel.ts","../src/plugins/redact/render.ts","../src/plugins/redact/selection.ts","../src/plugins/redact/stage.ts","../src/plugins/redact/mount.ts","../src/plugins/redact/plugin.ts","../src/plugins/resize/mount.ts","../src/plugins/resize/plugin.ts","../src/plugins/rotate/mount.ts","../src/plugins/rotate/plugin.ts","../src/preferences/storage.ts","../src/preferences/modal.ts","../src/dom/build-shell-dom.ts","../src/shell.ts"],"sourcesContent":["import type { ViewportController } from '@magicpages/kalotyp-core';\n\n/**\n * Editor-level wheel + multi-pointer (pinch / two-finger pan) handler driving a `ViewportController`.\n *\n * - Wheel: zoom anchored at the cursor (1.0015 per `deltaY` unit).\n * - Two pointers: pinch + pan applied together each frame (no pinch-vs-pan state machine —\n * applying both feels more natural and matches Snapseed / Apple Photos).\n * - Single pointer: pass-through to plugin gestures.\n */\nexport type StageGestureHandle = () => void;\n\nconst WHEEL_ZOOM_PER_DELTA = 1.0015;\n\ninterface PointerSample {\n readonly id: number;\n x: number;\n y: number;\n}\n\ninterface ActiveGesture {\n lastDistance: number;\n lastMidpoint: { x: number; y: number };\n}\n\nexport function attachStageGestures(\n stage: HTMLElement,\n controller: ViewportController,\n): StageGestureHandle {\n const pointers = new Map<number, PointerSample>();\n let gesture: ActiveGesture | null = null;\n\n function stageRect(): DOMRect {\n return stage.getBoundingClientRect();\n }\n\n function clientToStage(clientX: number, clientY: number): { x: number; y: number } {\n const rect = stageRect();\n return { x: clientX - rect.left, y: clientY - rect.top };\n }\n\n function stageCenter(): { x: number; y: number } {\n const rect = stageRect();\n return { x: rect.width / 2, y: rect.height / 2 };\n }\n\n function snapshotMidpointAndDistance(): {\n midpoint: { x: number; y: number };\n distance: number;\n } | null {\n if (pointers.size < 2) return null;\n // With 3+ pointers (rare on phones, common on multi-touch trackpads), use the first two.\n const iter = pointers.values();\n const first = iter.next().value as PointerSample;\n const second = iter.next().value as PointerSample;\n const midpoint = clientToStage((first.x + second.x) / 2, (first.y + second.y) / 2);\n const dx = second.x - first.x;\n const dy = second.y - first.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n return { midpoint, distance };\n }\n\n function startGestureIfPaired(): void {\n if (pointers.size < 2 || gesture !== null) return;\n const snap = snapshotMidpointAndDistance();\n if (!snap || snap.distance <= 0) return;\n gesture = { lastDistance: snap.distance, lastMidpoint: snap.midpoint };\n controller.setPinching(true);\n }\n\n function endGesture(): void {\n if (gesture === null) return;\n gesture = null;\n controller.setPinching(false);\n }\n\n function onPointerDown(event: PointerEvent): void {\n // No pointer-capture here — plugin handlers may own single-pointer drags. A second pointer\n // opportunistically promotes to a pinch/pan gesture, interrupting any in-flight plugin drag.\n if (event.pointerType === 'mouse' && event.button !== 0) return;\n pointers.set(event.pointerId, {\n id: event.pointerId,\n x: event.clientX,\n y: event.clientY,\n });\n startGestureIfPaired();\n }\n\n function onPointerMove(event: PointerEvent): void {\n const tracked = pointers.get(event.pointerId);\n if (!tracked) return;\n tracked.x = event.clientX;\n tracked.y = event.clientY;\n if (gesture === null) return;\n const snap = snapshotMidpointAndDistance();\n if (!snap || snap.distance <= 0) return;\n\n // Pinch anchored at the midpoint so the gesture's center stays fixed.\n const zoomDelta = snap.distance / gesture.lastDistance;\n if (zoomDelta !== 1) {\n controller.zoomAt(zoomDelta, snap.midpoint, stageCenter());\n }\n\n const dx = snap.midpoint.x - gesture.lastMidpoint.x;\n const dy = snap.midpoint.y - gesture.lastMidpoint.y;\n if (dx !== 0 || dy !== 0) {\n controller.panBy(dx, dy);\n }\n\n gesture.lastDistance = snap.distance;\n gesture.lastMidpoint = snap.midpoint;\n\n event.preventDefault();\n }\n\n function onPointerUpOrCancel(event: PointerEvent): void {\n if (!pointers.has(event.pointerId)) return;\n pointers.delete(event.pointerId);\n if (pointers.size < 2) endGesture();\n }\n\n function onWheel(event: WheelEvent): void {\n // ctrlKey + wheel on macOS trackpads = pinch; we want our zoom for both that and plain wheel.\n const factor = WHEEL_ZOOM_PER_DELTA ** -event.deltaY;\n if (factor === 1) return;\n const anchor = clientToStage(event.clientX, event.clientY);\n controller.zoomAt(factor, anchor, stageCenter());\n event.preventDefault();\n }\n\n // Listeners on bubble phase so plugin handlers see pointerdown first; we only intervene on the 2nd pointer.\n stage.addEventListener('pointerdown', onPointerDown);\n stage.addEventListener('pointermove', onPointerMove);\n stage.addEventListener('pointerup', onPointerUpOrCancel);\n stage.addEventListener('pointercancel', onPointerUpOrCancel);\n stage.addEventListener('pointerleave', onPointerUpOrCancel);\n // { passive: false } required: Chromium/WebKit treat wheel as passive by default, blocking preventDefault.\n stage.addEventListener('wheel', onWheel, { passive: false });\n\n return () => {\n stage.removeEventListener('pointerdown', onPointerDown);\n stage.removeEventListener('pointermove', onPointerMove);\n stage.removeEventListener('pointerup', onPointerUpOrCancel);\n stage.removeEventListener('pointercancel', onPointerUpOrCancel);\n stage.removeEventListener('pointerleave', onPointerUpOrCancel);\n stage.removeEventListener('wheel', onWheel);\n pointers.clear();\n if (gesture !== null) controller.setPinching(false);\n gesture = null;\n };\n}\n","/**\n * Nested overlay (modal or anchored popover) used for the Output popover, Preferences modal,\n * and Keyboard cheatsheet. Lives inside the editor host so the editor's click-capture scope\n * and Ghost's modal-service allowlist still cover it. Traps Tab in capture phase so it wins\n * over the editor's outer trap.\n */\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]',\n].join(', ');\n\nexport interface NestedModalOptions {\n readonly host: HTMLElement;\n /** When supplied, the overlay positions as a popover above this element instead of centred. */\n readonly anchor?: HTMLElement;\n readonly title: string;\n readonly body: HTMLElement;\n /** CSS class added to the overlay container. */\n readonly variant?: string;\n /** Default true; the popover variant typically sets false (Esc / click-outside dismiss). */\n readonly showCloseButton?: boolean;\n readonly onClose: () => void;\n}\n\nexport interface NestedModalHandle {\n readonly element: HTMLElement;\n close(): void;\n}\n\n/** Open a nested modal or anchored popover. Caller owns the body content. */\nexport function openNestedModal(options: NestedModalOptions): NestedModalHandle {\n const previouslyFocused =\n document.activeElement instanceof HTMLElement ? document.activeElement : null;\n\n const overlay = document.createElement('div');\n overlay.className = 'kalotyp-nested-overlay';\n if (options.variant) overlay.classList.add(options.variant);\n\n const surface = document.createElement('div');\n surface.className = 'kalotyp-nested-surface';\n surface.setAttribute('role', 'dialog');\n surface.setAttribute('aria-modal', 'true');\n surface.tabIndex = -1;\n\n const titleId = `kalotyp-nested-title-${Math.random().toString(36).slice(2, 8)}`;\n surface.setAttribute('aria-labelledby', titleId);\n\n const header = document.createElement('div');\n header.className = 'kalotyp-nested-header';\n\n const heading = document.createElement('h3');\n heading.id = titleId;\n heading.className = 'kalotyp-nested-title';\n heading.textContent = options.title;\n header.appendChild(heading);\n\n let closeButton: HTMLButtonElement | undefined;\n if (options.showCloseButton !== false) {\n closeButton = document.createElement('button');\n closeButton.type = 'button';\n closeButton.className = 'kalotyp-nested-close';\n closeButton.setAttribute('aria-label', `Close ${options.title}`);\n closeButton.textContent = '×';\n closeButton.addEventListener('click', () => requestClose());\n header.appendChild(closeButton);\n }\n\n surface.appendChild(header);\n\n const bodyWrap = document.createElement('div');\n bodyWrap.className = 'kalotyp-nested-body';\n bodyWrap.appendChild(options.body);\n surface.appendChild(bodyWrap);\n\n overlay.appendChild(surface);\n options.host.appendChild(overlay);\n\n if (options.anchor) {\n overlay.classList.add('kalotyp-nested-overlay--popover');\n positionAnchored(overlay, surface, options.anchor);\n const reposition = (): void =>\n positionAnchored(overlay, surface, options.anchor as HTMLElement);\n window.addEventListener('resize', reposition);\n overlay.dataset.resizeListenerAttached = '1';\n overlay.addEventListener('kalotyp-nested-cleanup', () => {\n window.removeEventListener('resize', reposition);\n });\n } else {\n overlay.classList.add('kalotyp-nested-overlay--modal');\n }\n\n requestAnimationFrame(() => surface.focus());\n\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n requestClose();\n return;\n }\n if (event.key !== 'Tab') return;\n const focusable = Array.from(surface.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));\n if (focusable.length === 0) {\n event.preventDefault();\n surface.focus();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (!first || !last) return;\n const active = document.activeElement as HTMLElement | null;\n if (event.shiftKey) {\n if (active === first || !surface.contains(active)) {\n event.preventDefault();\n last.focus();\n }\n } else {\n if (active === last || !surface.contains(active)) {\n event.preventDefault();\n first.focus();\n }\n }\n };\n\n // Capture phase so we win over the editor's outer Tab trap.\n document.addEventListener('keydown', onKeyDown, true);\n\n const onClickOutside = (event: MouseEvent): void => {\n const target = event.target as Node | null;\n if (!target) return;\n if (!surface.contains(target)) {\n requestClose();\n }\n };\n // Listen on overlay (dimming layer) not document, so other editor clicks pass through normally.\n overlay.addEventListener('mousedown', onClickOutside);\n\n let closing = false;\n function requestClose(): void {\n if (closing) return;\n closing = true;\n document.removeEventListener('keydown', onKeyDown, true);\n overlay.removeEventListener('mousedown', onClickOutside);\n overlay.dispatchEvent(new Event('kalotyp-nested-cleanup'));\n overlay.remove();\n if (previouslyFocused?.isConnected) {\n try {\n previouslyFocused.focus();\n } catch {\n /* trigger may have been removed; ignore */\n }\n }\n options.onClose();\n }\n\n return {\n element: overlay,\n close: requestClose,\n };\n}\n\nfunction positionAnchored(overlay: HTMLElement, surface: HTMLElement, anchor: HTMLElement): void {\n const anchorRect = anchor.getBoundingClientRect();\n const hostRect = overlay.parentElement?.getBoundingClientRect() ?? {\n left: 0,\n top: 0,\n right: window.innerWidth,\n bottom: window.innerHeight,\n };\n // Bottom edge 8px above anchor's top; right edge aligned to anchor's right (keeps it in the gutter).\n const surfaceRect = surface.getBoundingClientRect();\n const top = anchorRect.top - hostRect.top - surfaceRect.height - 8;\n const right = hostRect.right - anchorRect.right;\n surface.style.position = 'absolute';\n surface.style.top = `${Math.max(8, top)}px`;\n surface.style.right = `${Math.max(8, right)}px`;\n}\n","/**\n * Manifest of every keyboard shortcut. The cheatsheet UI reads this list.\n * This is a manifest, not a registry — handlers live with the features that own them.\n * \"Ctrl\" stands in for Ctrl/Cmd: handlers check `ctrlKey || metaKey`, so one label covers both.\n */\n\nexport type KeyboardShortcutContext = 'editor' | 'annotate' | 'redact' | 'text';\n\nexport interface KeyboardShortcut {\n /** Tokens rendered as `<kbd>` pills joined by \" + \". Use platform-neutral spelling (\"Ctrl\", \"Esc\", \"Arrow keys\"). */\n readonly keys: ReadonlyArray<string>;\n readonly description: string;\n readonly context: KeyboardShortcutContext;\n}\n\nexport const KEYBOARD_SHORTCUTS: ReadonlyArray<KeyboardShortcut> = [\n {\n keys: ['?'],\n description: 'Show keyboard shortcuts',\n context: 'editor',\n },\n {\n keys: ['Esc'],\n description: 'Close editor (or clear current selection)',\n context: 'editor',\n },\n {\n keys: ['Ctrl', 'Z'],\n description: 'Undo',\n context: 'editor',\n },\n {\n keys: ['Ctrl', 'Shift', 'Z'],\n description: 'Redo',\n context: 'editor',\n },\n {\n keys: ['Ctrl', 'Y'],\n description: 'Redo (alternate)',\n context: 'editor',\n },\n {\n keys: ['Tab'],\n description: 'Move focus to next control',\n context: 'editor',\n },\n {\n keys: ['Shift', 'Tab'],\n description: 'Move focus to previous control',\n context: 'editor',\n },\n\n {\n keys: ['Delete'],\n description: 'Delete the selected shape',\n context: 'annotate',\n },\n {\n keys: ['Arrow keys'],\n description: 'Nudge the selected shape by 1 px',\n context: 'annotate',\n },\n {\n keys: ['Shift', 'Arrow keys'],\n description: 'Nudge the selected shape by 10 px',\n context: 'annotate',\n },\n {\n keys: ['Shift', 'while drawing'],\n description: 'Constrain shape (square, 45° line, circle)',\n context: 'annotate',\n },\n\n {\n keys: ['Delete'],\n description: 'Delete the selected redaction region',\n context: 'redact',\n },\n {\n keys: ['Arrow keys'],\n description: 'Nudge the selected region by 1 px',\n context: 'redact',\n },\n {\n keys: ['Shift', 'Arrow keys'],\n description: 'Nudge the selected region by 10 px',\n context: 'redact',\n },\n\n {\n keys: ['Enter'],\n description: 'Commit the text and close the editor',\n context: 'text',\n },\n {\n keys: ['Shift', 'Enter'],\n description: 'Insert a line break',\n context: 'text',\n },\n {\n keys: ['Esc'],\n description: 'Cancel the in-progress edit',\n context: 'text',\n },\n];\n\nexport const KEYBOARD_SHORTCUT_CONTEXT_LABELS: Readonly<Record<KeyboardShortcutContext, string>> = {\n editor: 'Editor',\n annotate: 'Annotate',\n redact: 'Redact',\n text: 'Text editing',\n};\n","/** Cheatsheet modal — renders `KEYBOARD_SHORTCUTS` grouped by context. Opened via `?`. */\n\nimport { openNestedModal } from '../dom/nested-modal.js';\nimport {\n KEYBOARD_SHORTCUT_CONTEXT_LABELS,\n KEYBOARD_SHORTCUTS,\n type KeyboardShortcut,\n type KeyboardShortcutContext,\n} from '../keyboard-shortcuts.js';\n\nexport interface OpenCheatsheetOptions {\n readonly host: HTMLElement;\n onClose(): void;\n}\n\nexport interface CheatsheetHandle {\n close(): void;\n}\n\nconst CONTEXT_ORDER: ReadonlyArray<KeyboardShortcutContext> = [\n 'editor',\n 'annotate',\n 'redact',\n 'text',\n];\n\nexport function openCheatsheet(options: OpenCheatsheetOptions): CheatsheetHandle {\n const body = document.createElement('div');\n body.className = 'kalotyp-cheatsheet-body';\n\n for (const context of CONTEXT_ORDER) {\n const entries = KEYBOARD_SHORTCUTS.filter((s) => s.context === context);\n if (entries.length === 0) continue;\n body.appendChild(buildSection(context, entries));\n }\n\n const handle = openNestedModal({\n host: options.host,\n title: 'Keyboard shortcuts',\n body,\n variant: 'kalotyp-cheatsheet-modal',\n showCloseButton: true,\n onClose: options.onClose,\n });\n\n return {\n close: () => handle.close(),\n };\n}\n\nfunction buildSection(\n context: KeyboardShortcutContext,\n entries: ReadonlyArray<KeyboardShortcut>,\n): HTMLElement {\n const section = document.createElement('section');\n section.className = 'kalotyp-cheatsheet-section';\n\n const heading = document.createElement('h4');\n heading.className = 'kalotyp-cheatsheet-heading';\n heading.textContent = KEYBOARD_SHORTCUT_CONTEXT_LABELS[context];\n section.appendChild(heading);\n\n const list = document.createElement('dl');\n list.className = 'kalotyp-cheatsheet-list';\n\n for (const shortcut of entries) {\n const dt = document.createElement('dt');\n dt.className = 'kalotyp-cheatsheet-keys';\n shortcut.keys.forEach((token, index) => {\n if (index > 0) {\n const plus = document.createElement('span');\n plus.className = 'kalotyp-cheatsheet-plus';\n plus.setAttribute('aria-hidden', 'true');\n plus.textContent = '+';\n dt.appendChild(plus);\n }\n const kbd = document.createElement('kbd');\n kbd.className = 'kalotyp-cheatsheet-kbd';\n kbd.textContent = token;\n dt.appendChild(kbd);\n });\n\n const dd = document.createElement('dd');\n dd.className = 'kalotyp-cheatsheet-description';\n dd.textContent = shortcut.description;\n\n list.appendChild(dt);\n list.appendChild(dd);\n }\n\n section.appendChild(list);\n return section;\n}\n","import type { UtilityId } from '@magicpages/kalotyp-core';\n\nexport interface UtilityNavEntry {\n readonly id: UtilityId;\n readonly label: string;\n}\n\nexport interface UtilityNavElements {\n readonly container: HTMLDivElement;\n readonly buttons: ReadonlyMap<UtilityId, HTMLButtonElement>;\n}\n\nexport interface BuildUtilityNavOptions {\n /** Tabpanel id wired into each tab's `aria-controls` to complete the tablist/tab/tabpanel triple. */\n panelId: string;\n}\n\n/** Build the utility nav. Roving-tabindex per WAI-ARIA APG: active tab `tabindex=0`, others `-1`; Left/Right/Home/End move active state. */\nexport function buildUtilityNav(\n entries: readonly UtilityNavEntry[],\n initialActive: UtilityId,\n onSelect: (id: UtilityId) => void,\n options: BuildUtilityNavOptions,\n): UtilityNavElements {\n const container = document.createElement('div');\n container.className = 'kalotyp-util-nav';\n container.setAttribute('role', 'tablist');\n container.setAttribute('aria-label', 'Editor tools');\n\n const buttons = new Map<UtilityId, HTMLButtonElement>();\n for (const entry of entries) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-util-nav-button';\n button.dataset.utilityId = entry.id;\n button.id = `${options.panelId}-tab-${entry.id}`;\n button.setAttribute('role', 'tab');\n button.setAttribute('aria-selected', entry.id === initialActive ? 'true' : 'false');\n button.setAttribute('aria-controls', options.panelId);\n button.tabIndex = entry.id === initialActive ? 0 : -1;\n button.textContent = entry.label;\n button.addEventListener('click', () => onSelect(entry.id));\n container.appendChild(button);\n buttons.set(entry.id, button);\n }\n\n // WAI-ARIA APG \"automatic activation\" pattern: Left/Right/Home/End change selection + focus.\n container.addEventListener('keydown', (event) => {\n if (\n event.key !== 'ArrowLeft' &&\n event.key !== 'ArrowRight' &&\n event.key !== 'Home' &&\n event.key !== 'End'\n ) {\n return;\n }\n const ids = entries.map((e) => e.id);\n const currentEl = event.target as HTMLElement | null;\n const currentId = currentEl?.dataset?.utilityId as UtilityId | undefined;\n const currentIdx = currentId ? ids.indexOf(currentId) : -1;\n if (currentIdx === -1) return;\n\n let nextIdx = currentIdx;\n if (event.key === 'ArrowLeft') nextIdx = (currentIdx - 1 + ids.length) % ids.length;\n else if (event.key === 'ArrowRight') nextIdx = (currentIdx + 1) % ids.length;\n else if (event.key === 'Home') nextIdx = 0;\n else if (event.key === 'End') nextIdx = ids.length - 1;\n\n const nextId = ids[nextIdx];\n if (!nextId || nextId === currentId) return;\n event.preventDefault();\n onSelect(nextId);\n buttons.get(nextId)?.focus();\n });\n\n return { container, buttons };\n}\n\n/** Update active tab state and scroll the newly-active tab into view (no-op on non-overflowing desktop strips). */\nexport function setActiveUtilityButton(\n nav: UtilityNavElements,\n active: UtilityId,\n panel?: HTMLElement,\n): void {\n for (const [id, button] of nav.buttons.entries()) {\n const isActive = id === active;\n button.setAttribute('aria-selected', isActive ? 'true' : 'false');\n button.tabIndex = isActive ? 0 : -1;\n if (isActive) {\n // jsdom's scrollIntoView stub throws on options args — swallow to keep tests green.\n try {\n button.scrollIntoView({ block: 'nearest', inline: 'nearest', behavior: 'smooth' });\n } catch {\n // ignore\n }\n if (panel) panel.setAttribute('aria-labelledby', button.id);\n }\n }\n}\n","/**\n * Focus trap + initial-focus + restore-on-release for the editor dialog.\n *\n * The Tab keydown path doesn't trap screen readers' virtual cursors — `aria-modal=true`\n * is what makes content outside the dialog inert for assistive tech. On release, focus\n * returns to the element that was active before the trap installed (usually the trigger).\n */\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'details',\n 'summary',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable=\"true\"]',\n].join(', ');\n\nexport interface InstallFocusTrapOptions {\n readonly host: HTMLElement;\n /** Element to focus on mount. Defaults to the first focusable descendant of `host`. */\n readonly initialFocus?: HTMLElement;\n}\n\nexport interface FocusTrapHandle {\n /** Hook for future use; currently a no-op because focusables are re-queried on every Tab. */\n refresh(): void;\n /** Tear down listeners and restore focus to the trigger element. */\n release(): void;\n}\n\n/** Install the focus trap and seed initial focus. */\nexport function installFocusTrap(options: InstallFocusTrapOptions): FocusTrapHandle {\n const { host } = options;\n const trigger =\n document.activeElement instanceof HTMLElement && document.activeElement !== document.body\n ? document.activeElement\n : null;\n\n function getFocusable(): HTMLElement[] {\n // No offsetParent/visibility filtering: the selector handles `disabled`, jsdom\n // reports `offsetParent === null` for every element regardless of layout, and\n // we never display-hide focusable controls mid-interaction.\n return Array.from(host.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));\n }\n\n // rAF so the DOM is laid out before focus — initial tabIndex may have been set in this same tick.\n const seedTarget = options.initialFocus ?? getFocusable()[0];\n if (seedTarget) {\n requestAnimationFrame(() => seedTarget.focus());\n }\n\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key !== 'Tab') return;\n const focusable = getFocusable();\n if (focusable.length === 0) {\n event.preventDefault();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (!first || !last) return;\n const active = document.activeElement as HTMLElement | null;\n if (event.shiftKey) {\n if (active === first || !host.contains(active)) {\n event.preventDefault();\n last.focus();\n }\n } else {\n if (active === last || !host.contains(active)) {\n event.preventDefault();\n first.focus();\n }\n }\n };\n\n document.addEventListener('keydown', onKeyDown, true);\n\n return {\n refresh: () => {},\n release: () => {\n document.removeEventListener('keydown', onKeyDown, true);\n if (trigger?.isConnected) {\n try {\n trigger.focus();\n } catch {\n // ignore\n }\n }\n },\n };\n}\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst ArrowRight = [\n [\"path\", { d: \"M5 12h14\" }],\n [\"path\", { d: \"m12 5 7 7-7 7\" }]\n];\n\nexport { ArrowRight as default };\n//# sourceMappingURL=arrow-right.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Check = [[\"path\", { d: \"M20 6 9 17l-5-5\" }]];\n\nexport { Check as default };\n//# sourceMappingURL=check.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst ChevronDown = [[\"path\", { d: \"m6 9 6 6 6-6\" }]];\n\nexport { ChevronDown as default };\n//# sourceMappingURL=chevron-down.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Circle = [[\"circle\", { cx: \"12\", cy: \"12\", r: \"10\" }]];\n\nexport { Circle as default };\n//# sourceMappingURL=circle.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst FlipHorizontal2 = [\n [\"path\", { d: \"m3 7 5 5-5 5V7\" }],\n [\"path\", { d: \"m21 7-5 5 5 5V7\" }],\n [\"path\", { d: \"M12 20v2\" }],\n [\"path\", { d: \"M12 14v2\" }],\n [\"path\", { d: \"M12 8v2\" }],\n [\"path\", { d: \"M12 2v2\" }]\n];\n\nexport { FlipHorizontal2 as default };\n//# sourceMappingURL=flip-horizontal-2.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst FlipVertical2 = [\n [\"path\", { d: \"m17 3-5 5-5-5h10\" }],\n [\"path\", { d: \"m17 21-5-5-5 5h10\" }],\n [\"path\", { d: \"M4 12H2\" }],\n [\"path\", { d: \"M10 12H8\" }],\n [\"path\", { d: \"M16 12h-2\" }],\n [\"path\", { d: \"M22 12h-2\" }]\n];\n\nexport { FlipVertical2 as default };\n//# sourceMappingURL=flip-vertical-2.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Highlighter = [\n [\"path\", { d: \"m9 11-6 6v3h9l3-3\" }],\n [\"path\", { d: \"m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4\" }]\n];\n\nexport { Highlighter as default };\n//# sourceMappingURL=highlighter.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Keyboard = [\n [\"path\", { d: \"M10 8h.01\" }],\n [\"path\", { d: \"M12 12h.01\" }],\n [\"path\", { d: \"M14 8h.01\" }],\n [\"path\", { d: \"M16 12h.01\" }],\n [\"path\", { d: \"M18 8h.01\" }],\n [\"path\", { d: \"M6 8h.01\" }],\n [\"path\", { d: \"M7 16h10\" }],\n [\"path\", { d: \"M8 12h.01\" }],\n [\"rect\", { width: \"20\", height: \"16\", x: \"2\", y: \"4\", rx: \"2\" }]\n];\n\nexport { Keyboard as default };\n//# sourceMappingURL=keyboard.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Link2 = [\n [\"path\", { d: \"M9 17H7A5 5 0 0 1 7 7h2\" }],\n [\"path\", { d: \"M15 7h2a5 5 0 1 1 0 10h-2\" }],\n [\"line\", { x1: \"8\", x2: \"16\", y1: \"12\", y2: \"12\" }]\n];\n\nexport { Link2 as default };\n//# sourceMappingURL=link-2.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Link2Off = [\n [\"path\", { d: \"M9 17H7A5 5 0 0 1 7 7\" }],\n [\"path\", { d: \"M15 7h2a5 5 0 0 1 4 8\" }],\n [\"line\", { x1: \"8\", x2: \"12\", y1: \"12\", y2: \"12\" }],\n [\"line\", { x1: \"2\", x2: \"22\", y1: \"2\", y2: \"22\" }]\n];\n\nexport { Link2Off as default };\n//# sourceMappingURL=link-2-off.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst MousePointer2 = [\n [\n \"path\",\n {\n d: \"M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z\"\n }\n ]\n];\n\nexport { MousePointer2 as default };\n//# sourceMappingURL=mouse-pointer-2.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Pencil = [\n [\n \"path\",\n {\n d: \"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z\"\n }\n ],\n [\"path\", { d: \"m15 5 4 4\" }]\n];\n\nexport { Pencil as default };\n//# sourceMappingURL=pencil.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Plus = [\n [\"path\", { d: \"M5 12h14\" }],\n [\"path\", { d: \"M12 5v14\" }]\n];\n\nexport { Plus as default };\n//# sourceMappingURL=plus.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Redo2 = [\n [\"path\", { d: \"m15 14 5-5-5-5\" }],\n [\"path\", { d: \"M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13\" }]\n];\n\nexport { Redo2 as default };\n//# sourceMappingURL=redo-2.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Settings = [\n [\n \"path\",\n {\n d: \"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915\"\n }\n ],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\" }]\n];\n\nexport { Settings as default };\n//# sourceMappingURL=settings.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Square = [[\"rect\", { width: \"18\", height: \"18\", x: \"3\", y: \"3\", rx: \"2\" }]];\n\nexport { Square as default };\n//# sourceMappingURL=square.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Trash2 = [\n [\"path\", { d: \"M10 11v6\" }],\n [\"path\", { d: \"M14 11v6\" }],\n [\"path\", { d: \"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\" }],\n [\"path\", { d: \"M3 6h18\" }],\n [\"path\", { d: \"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" }]\n];\n\nexport { Trash2 as default };\n//# sourceMappingURL=trash-2.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Type = [\n [\"path\", { d: \"M12 4v16\" }],\n [\"path\", { d: \"M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2\" }],\n [\"path\", { d: \"M9 20h6\" }]\n];\n\nexport { Type as default };\n//# sourceMappingURL=type.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst Undo2 = [\n [\"path\", { d: \"M9 14 4 9l5-5\" }],\n [\"path\", { d: \"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11\" }]\n];\n\nexport { Undo2 as default };\n//# sourceMappingURL=undo-2.mjs.map\n","/**\n * @license lucide v1.17.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst X = [\n [\"path\", { d: \"M18 6 6 18\" }],\n [\"path\", { d: \"m6 6 12 12\" }]\n];\n\nexport { X as default };\n//# sourceMappingURL=x.mjs.map\n","/**\n * Icon library sourced from Lucide (ISC). Each icon is a named import so the\n * bundler tree-shakes unused glyphs. We stringify nodes ourselves rather than\n * use Lucide's `createElement` to avoid pulling its DOM runtime into the bundle.\n */\n\nimport {\n ArrowRight,\n Check as CheckIcon,\n ChevronDown,\n Circle as CircleIcon,\n X as CloseIcon,\n FlipHorizontal2,\n FlipVertical2,\n Highlighter,\n Keyboard as KeyboardIcon,\n Link2,\n Link2Off,\n MousePointer2,\n Pencil,\n Plus as PlusIcon,\n Redo2,\n Settings as SettingsIcon,\n Square,\n Trash2,\n Type as TypeIcon,\n Undo2,\n} from 'lucide';\n\ntype IconNode = ReadonlyArray<readonly [string, Record<string, string | number>]>;\n\nconst DEFAULT_SVG_ATTRS: Record<string, string | number> = {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 16,\n height: 16,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': 2,\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'aria-hidden': 'true',\n};\n\n/** Stringify a Lucide icon node to inline SVG markup. */\nexport function iconHtml(node: IconNode, attrs: Record<string, string | number> = {}): string {\n const merged = { ...DEFAULT_SVG_ATTRS, ...attrs };\n const svgAttrs = Object.entries(merged)\n .map(([k, v]) => `${k}=\"${v}\"`)\n .join(' ');\n const children = node\n .map(([tag, a]) => {\n const childAttrs = Object.entries(a)\n .map(([k, v]) => `${k}=\"${v}\"`)\n .join(' ');\n return `<${tag} ${childAttrs} />`;\n })\n .join('');\n return `<svg ${svgAttrs}>${children}</svg>`;\n}\n\nexport type IconName =\n | 'select'\n | 'text'\n | 'rect'\n | 'ellipse'\n | 'arrow'\n | 'freehand'\n | 'highlight'\n | 'undo'\n | 'redo'\n | 'close'\n | 'delete'\n | 'check'\n | 'lockClosed'\n | 'lockOpen'\n | 'plus'\n | 'flipHorizontal'\n | 'flipVertical'\n | 'chevronDown'\n | 'settings'\n | 'keyboard';\n\nfunction resolve(name: IconName): IconNode {\n switch (name) {\n case 'select':\n return MousePointer2 as IconNode;\n case 'text':\n return TypeIcon as IconNode;\n case 'rect':\n return Square as IconNode;\n case 'ellipse':\n return CircleIcon as IconNode;\n case 'arrow':\n return ArrowRight as IconNode;\n case 'freehand':\n return Pencil as IconNode;\n case 'highlight':\n return Highlighter as IconNode;\n case 'undo':\n return Undo2 as IconNode;\n case 'redo':\n return Redo2 as IconNode;\n case 'close':\n return CloseIcon as IconNode;\n case 'delete':\n return Trash2 as IconNode;\n case 'check':\n return CheckIcon as IconNode;\n case 'lockClosed':\n return Link2 as IconNode;\n case 'lockOpen':\n return Link2Off as IconNode;\n case 'plus':\n return PlusIcon as IconNode;\n case 'flipHorizontal':\n return FlipHorizontal2 as IconNode;\n case 'flipVertical':\n return FlipVertical2 as IconNode;\n case 'chevronDown':\n return ChevronDown as IconNode;\n case 'settings':\n return SettingsIcon as IconNode;\n case 'keyboard':\n return KeyboardIcon as IconNode;\n }\n}\n\n/** Render an icon by name. */\nexport function icon(name: IconName, attrs?: Record<string, string | number>): string {\n return iconHtml(resolve(name), attrs);\n}\n","/**\n * Allocate a canvas suitable for an off-screen bake operation.\n *\n * The two return shapes are a discriminated union because the `toBlob`\n * vs `convertToBlob` signatures differ — callers pass through whichever\n * they got.\n */\nexport type BakeCanvas =\n | { readonly kind: 'offscreen'; readonly canvas: OffscreenCanvas }\n | { readonly kind: 'html'; readonly canvas: HTMLCanvasElement };\n\nexport function createBakeCanvas(width: number, height: number): BakeCanvas {\n if (canUseOffscreenForBlobs()) {\n const canvas = new OffscreenCanvas(width, height);\n return { kind: 'offscreen', canvas };\n }\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n return { kind: 'html', canvas };\n}\n\n/**\n * Get a 2D rendering context from either canvas shape with a single\n * narrowed type. Branching on `bake.kind` first lets TS narrow each\n * canvas's `getContext('2d')` correctly; calling it on the un-discriminated\n * union collapses the return to the broader `RenderingContext`.\n */\nexport function getBakeContext2D(\n bake: BakeCanvas,\n): CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D {\n if (bake.kind === 'offscreen') {\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n return ctx;\n }\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n return ctx;\n}\n\nexport async function bakeCanvasToBlob(\n bake: BakeCanvas,\n mimeType: string,\n quality: number,\n): Promise<Blob> {\n if (bake.kind === 'offscreen') {\n return bake.canvas.convertToBlob({ type: mimeType, quality });\n }\n return new Promise<Blob>((resolve, reject) => {\n bake.canvas.toBlob(\n (blob) => {\n if (blob) resolve(blob);\n else reject(new Error('toBlob produced null'));\n },\n mimeType,\n quality,\n );\n });\n}\n\nfunction canUseOffscreenForBlobs(): boolean {\n if (typeof OffscreenCanvas === 'undefined') return false;\n // WebKit historically shipped OffscreenCanvas without convertToBlob,\n // so test the actual capability rather than the constructor.\n return typeof OffscreenCanvas.prototype.convertToBlob === 'function';\n}\n\n/** Probe whether the runtime canvas can encode `mimeType` to a non-empty blob. Cached per-mime. */\nconst mimeSupportCache = new Map<string, Promise<boolean>>();\n\nexport function canEncodeMime(mimeType: string): Promise<boolean> {\n const cached = mimeSupportCache.get(mimeType);\n if (cached) return cached;\n const probe = (async () => {\n try {\n const bake = createBakeCanvas(1, 1);\n const blob = await bakeCanvasToBlob(bake, mimeType, 0.5);\n // `toBlob` will silently fall back to PNG on unsupported types,\n // so verify the result advertises the requested mime.\n return blob.type === mimeType && blob.size > 0;\n } catch {\n return false;\n }\n })();\n mimeSupportCache.set(mimeType, probe);\n return probe;\n}\n","import type { Point, Rect, Size } from '../geometry/rect.js';\n\nexport interface StageDimensions {\n /** Stage width in CSS pixels. */\n readonly width: number;\n /** Stage height in CSS pixels. */\n readonly height: number;\n /** Padding on each side around the image, in CSS pixels. */\n readonly padding: number;\n}\n\n/**\n * User-driven zoom and pan applied on top of the fit-to-screen letterbox.\n * `computeViewport` folds the transform into `displayRect` and `scale` so\n * plugin-level draw calls stay zoom-agnostic. Pan is in stage CSS pixels\n * at the current zoom and is clamped at viewport emission.\n */\nexport interface ViewportTransform {\n /** 1 at fit; > 1 zoomed in; < 1 zoomed out. */\n readonly zoom: number;\n /** CSS pixels of pan offset, applied after the centered-fit baseline. */\n readonly panX: number;\n readonly panY: number;\n}\n\nexport const IDENTITY_VIEWPORT_TRANSFORM: ViewportTransform = Object.freeze({\n zoom: 1,\n panX: 0,\n panY: 0,\n});\n\nexport interface Viewport {\n /** Where the image is drawn in stage CSS pixels (post-zoom, post-pan). */\n readonly displayRect: Rect;\n /** Display CSS pixels per 1 image pixel (uniform on both axes, post-zoom). */\n readonly scale: number;\n}\n\n/**\n * Compute the post-zoom, post-pan display rect for an image inside the stage.\n *\n * At identity, the image is fit-scaled inside the stage minus padding and\n * centered. With a non-identity transform the fit scale is multiplied by\n * `zoom`, then the pan offset is added and clamped so at least 1 image\n * pixel remains inside the inner stage area on each axis.\n */\nexport function computeViewport(\n stage: StageDimensions,\n image: Size,\n transform: ViewportTransform = IDENTITY_VIEWPORT_TRANSFORM,\n): Viewport {\n const innerWidth = Math.max(0, stage.width - stage.padding * 2);\n const innerHeight = Math.max(0, stage.height - stage.padding * 2);\n\n if (image.width <= 0 || image.height <= 0 || innerWidth <= 0 || innerHeight <= 0) {\n return {\n displayRect: { x: stage.padding, y: stage.padding, width: 0, height: 0 },\n scale: 0,\n };\n }\n\n const fitScale = Math.min(innerWidth / image.width, innerHeight / image.height);\n const zoom = Math.max(0, transform.zoom || 0);\n const scale = fitScale * zoom;\n\n const width = image.width * scale;\n const height = image.height * scale;\n\n // Centered baseline inside the inner stage area, then add the pan.\n const baselineX = stage.padding + (innerWidth - width) / 2;\n const baselineY = stage.padding + (innerHeight - height) / 2;\n\n const x = baselineX + transform.panX;\n const y = baselineY + transform.panY;\n\n const clampedX = clampAxis(x, baselineX, width, innerWidth, stage.padding);\n const clampedY = clampAxis(y, baselineY, height, innerHeight, stage.padding);\n\n return {\n displayRect: { x: clampedX, y: clampedY, width, height },\n scale,\n };\n}\n\nfunction clampAxis(\n rawPos: number,\n baseline: number,\n size: number,\n innerSize: number,\n padding: number,\n): number {\n // Image narrower than the inner stage: no off-center pan, baseline is centered.\n if (size <= innerSize) return baseline;\n\n // At least 1 image pixel always remains inside the inner stage.\n const innerStart = padding;\n const innerEnd = padding + innerSize;\n const minLeft = innerStart + 1 - size;\n const maxLeft = innerEnd - 1;\n if (rawPos < minLeft) return minLeft;\n if (rawPos > maxLeft) return maxLeft;\n return rawPos;\n}\n\nexport function pointImageToDisplay(point: Point, viewport: Viewport): Point {\n return {\n x: viewport.displayRect.x + point.x * viewport.scale,\n y: viewport.displayRect.y + point.y * viewport.scale,\n };\n}\n\nexport function pointDisplayToImage(point: Point, viewport: Viewport): Point {\n if (viewport.scale === 0) return { x: 0, y: 0 };\n return {\n x: (point.x - viewport.displayRect.x) / viewport.scale,\n y: (point.y - viewport.displayRect.y) / viewport.scale,\n };\n}\n\nexport function rectImageToDisplay(rect: Rect, viewport: Viewport): Rect {\n return {\n x: viewport.displayRect.x + rect.x * viewport.scale,\n y: viewport.displayRect.y + rect.y * viewport.scale,\n width: rect.width * viewport.scale,\n height: rect.height * viewport.scale,\n };\n}\n\nexport function rectDisplayToImage(rect: Rect, viewport: Viewport): Rect {\n if (viewport.scale === 0) return { x: 0, y: 0, width: 0, height: 0 };\n return {\n x: (rect.x - viewport.displayRect.x) / viewport.scale,\n y: (rect.y - viewport.displayRect.y) / viewport.scale,\n width: rect.width / viewport.scale,\n height: rect.height / viewport.scale,\n };\n}\n","export interface Point {\n readonly x: number;\n readonly y: number;\n}\n\nexport interface Rect {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\nexport interface Size {\n readonly width: number;\n readonly height: number;\n}\n\nexport function rectFromPoints(a: Point, b: Point): Rect {\n const x = Math.min(a.x, b.x);\n const y = Math.min(a.y, b.y);\n const width = Math.abs(a.x - b.x);\n const height = Math.abs(a.y - b.y);\n return { x, y, width, height };\n}\n\nexport function rectRight(rect: Rect): number {\n return rect.x + rect.width;\n}\n\nexport function rectBottom(rect: Rect): number {\n return rect.y + rect.height;\n}\n\nexport function rectCenter(rect: Rect): Point {\n return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };\n}\n\nexport function pointInRect(point: Point, rect: Rect): boolean {\n return (\n point.x >= rect.x &&\n point.x <= rect.x + rect.width &&\n point.y >= rect.y &&\n point.y <= rect.y + rect.height\n );\n}\n\nexport function rectsEqual(a: Rect, b: Rect): boolean {\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n}\n\n/**\n * Translate a rect by `(dx, dy)`, then clamp it inside `bounds` so that the\n * full rect remains inside (clamping translates further if needed; size is\n * preserved). If the rect is larger than the bounds in either axis, that axis\n * is left at the bounds origin.\n */\nexport function translateClampedRect(rect: Rect, dx: number, dy: number, bounds: Rect): Rect {\n const moved: Rect = { x: rect.x + dx, y: rect.y + dy, width: rect.width, height: rect.height };\n return clampRectInside(moved, bounds);\n}\n\n/**\n * Clamp a rect so it fits entirely inside `bounds`. If the rect is larger\n * than the bounds in either axis, the rect's extent in that axis is shrunk\n * to fit (preserving the upper-left anchor of `bounds`).\n */\nexport function clampRectInside(rect: Rect, bounds: Rect): Rect {\n let { x, y, width, height } = rect;\n\n if (width > bounds.width) width = bounds.width;\n if (height > bounds.height) height = bounds.height;\n\n if (x < bounds.x) x = bounds.x;\n if (y < bounds.y) y = bounds.y;\n if (x + width > bounds.x + bounds.width) x = bounds.x + bounds.width - width;\n if (y + height > bounds.y + bounds.height) y = bounds.y + bounds.height - height;\n\n return { x, y, width, height };\n}\n\nexport function roundRect(rect: Rect): Rect {\n return {\n x: Math.round(rect.x),\n y: Math.round(rect.y),\n width: Math.round(rect.width),\n height: Math.round(rect.height),\n };\n}\n","/**\n * `'auto'` resolves to the smallest format that preserves alpha on the\n * current runtime (WebP on evergreens, PNG fallback). AVIF never auto-\n * resolves; the user must pick it explicitly.\n */\nexport type OutputMimeChoice = 'auto' | 'image/png' | 'image/jpeg' | 'image/webp' | 'image/avif';\n\nexport interface OutputState {\n readonly mimeChoice: OutputMimeChoice;\n /** 0.0 – 1.0. Ignored for PNG (lossless). */\n readonly quality: number;\n /**\n * Strip EXIF / GPS / camera metadata on save. Canvas `convertToBlob`\n * already strips EXIF; when `false`, we attempt to preserve the source\n * EXIF segment, which only works for JPEG → JPEG. Other combinations\n * strip regardless — the toggle is a hint, not a guarantee.\n */\n readonly stripMetadata: boolean;\n}\n\nexport const DEFAULT_OUTPUT_STATE: OutputState = {\n mimeChoice: 'auto',\n quality: 0.85,\n stripMetadata: true,\n};\n\n/** The four concrete mime types the user can pick from in the popover. */\nexport const ENCODABLE_MIMES: ReadonlyArray<Exclude<OutputMimeChoice, 'auto'>> = [\n 'image/png',\n 'image/jpeg',\n 'image/webp',\n 'image/avif',\n];\n\n/** Clamp a quality value to [0, 1]; non-finite inputs return the default. */\nexport function clampQuality(value: number): number {\n if (!Number.isFinite(value)) return DEFAULT_OUTPUT_STATE.quality;\n if (value < 0) return 0;\n if (value > 1) return 1;\n return value;\n}\n\nexport function setOutputMime(state: OutputState, mimeChoice: OutputMimeChoice): OutputState {\n if (state.mimeChoice === mimeChoice) return state;\n return { ...state, mimeChoice };\n}\n\nexport function setOutputQuality(state: OutputState, quality: number): OutputState {\n const clamped = clampQuality(quality);\n if (state.quality === clamped) return state;\n return { ...state, quality: clamped };\n}\n\nexport function setStripMetadata(state: OutputState, stripMetadata: boolean): OutputState {\n if (state.stripMetadata === stripMetadata) return state;\n return { ...state, stripMetadata };\n}\n","/**\n * Freehand stroke decimation + midpoint-curve smoothing. Decimation drops\n * sub-pixel samples from the raw pointer stream; the curve smoothing\n * happens at draw time so stored points stay unchanged across edits.\n */\n\nimport type { Point } from '../../geometry/rect.js';\n\nexport const MIN_SAMPLE_DISTANCE = 2;\n\n/** Drop interior points closer than `MIN_SAMPLE_DISTANCE` to the previous kept point. */\nexport function decimatePoints(points: ReadonlyArray<Point>): Point[] {\n if (points.length <= 1) return [...points];\n const head = points[0];\n if (!head) return [];\n const out: Point[] = [head];\n let last = head;\n const minSq = MIN_SAMPLE_DISTANCE * MIN_SAMPLE_DISTANCE;\n for (let i = 1; i < points.length - 1; i++) {\n const p = points[i];\n if (!p) continue;\n const dx = p.x - last.x;\n const dy = p.y - last.y;\n if (dx * dx + dy * dy < minSq) continue;\n out.push(p);\n last = p;\n }\n // Always keep the final sample so the stroke ends where the pen lifted.\n const tail = points[points.length - 1];\n if (tail && tail !== last) out.push(tail);\n return out;\n}\n\n/**\n * Trace a smoothed path through `points` using the midpoint-curve technique.\n * Caller owns `beginPath`, stroke style, and `stroke()` so blend modes\n * (highlight) can wrap this without re-implementing the curve math.\n */\nexport function tracePath(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n points: ReadonlyArray<Point>,\n): void {\n if (points.length === 0) return;\n const head = points[0];\n if (!head) return;\n if (points.length === 1) {\n // Single tap: zero-length segment relies on round lineCap to render a dot.\n ctx.moveTo(head.x, head.y);\n ctx.lineTo(head.x, head.y);\n return;\n }\n ctx.moveTo(head.x, head.y);\n for (let i = 1; i < points.length - 1; i++) {\n const a = points[i];\n const b = points[i + 1];\n if (!a || !b) continue;\n const midX = (a.x + b.x) / 2;\n const midY = (a.y + b.y) / 2;\n ctx.quadraticCurveTo(a.x, a.y, midX, midY);\n }\n const last = points[points.length - 1];\n if (last) ctx.lineTo(last.x, last.y);\n}\n","/**\n * Annotation state and pure mutators. Shapes are a flat discriminated\n * union keyed on `kind`; all coordinates are image-space pixels.\n */\n\nimport type { Point } from '../../geometry/rect.js';\n\nexport type ShapeKind = 'text' | 'rect' | 'ellipse' | 'arrow' | 'freehand' | 'highlight';\n\n/** Tools the annotation plugin exposes. `select` is the picker. */\nexport type AnnotateTool = ShapeKind | 'select';\n\ninterface ShapeBase {\n /** Stable per-session id; survives undo/redo. */\n readonly id: string;\n readonly kind: ShapeKind;\n}\n\nexport interface TextShape extends ShapeBase {\n readonly kind: 'text';\n /** Top-left anchor in image-space pixels. */\n readonly x: number;\n readonly y: number;\n readonly text: string;\n /** Font size in image-space pixels. */\n readonly fontSize: number;\n /** CSS colour string. */\n readonly color: string;\n readonly textAlign: 'left' | 'center' | 'right';\n}\n\nexport interface RectShape extends ShapeBase {\n readonly kind: 'rect';\n readonly x: number;\n readonly y: number;\n /** Non-negative after gesture commit; may be negative mid-drag. */\n readonly width: number;\n readonly height: number;\n readonly strokeColor: string;\n readonly strokeWidth: number;\n /** `null` means \"no fill\". */\n readonly fillColor: string | null;\n}\n\nexport interface EllipseShape extends ShapeBase {\n readonly kind: 'ellipse';\n /** Bounding-box top-left; the ellipse fits inside the box. */\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n readonly strokeColor: string;\n readonly strokeWidth: number;\n readonly fillColor: string | null;\n}\n\nexport interface ArrowShape extends ShapeBase {\n readonly kind: 'arrow';\n readonly x1: number;\n readonly y1: number;\n /** Arrowhead is drawn at (x2, y2). */\n readonly x2: number;\n readonly y2: number;\n readonly color: string;\n readonly strokeWidth: number;\n}\n\nexport interface FreehandShape extends ShapeBase {\n readonly kind: 'freehand';\n /** Decimated raw points in image-space; smoothing happens at render time. */\n readonly points: ReadonlyArray<Point>;\n readonly color: string;\n readonly strokeWidth: number;\n}\n\nexport interface HighlightShape extends ShapeBase {\n readonly kind: 'highlight';\n readonly points: ReadonlyArray<Point>;\n /** Default semi-transparent yellow, drawn with `multiply` blend mode. */\n readonly color: string;\n readonly strokeWidth: number;\n}\n\nexport type Shape =\n | TextShape\n | RectShape\n | EllipseShape\n | ArrowShape\n | FreehandShape\n | HighlightShape;\n\nexport interface StylePalette {\n readonly color: string;\n readonly strokeWidth: number;\n /** Used for new rect/ellipse fills; `null` = unfilled. */\n readonly fillColor: string | null;\n /** Used for new text shapes. In image-space pixels. */\n readonly fontSize: number;\n}\n\nexport interface AnnotateState {\n readonly shapes: ReadonlyArray<Shape>;\n readonly selectedId: string | null;\n readonly activeTool: AnnotateTool;\n readonly currentStyle: StylePalette;\n /** Image-space dimensions of the upstream-baked source the plugin was mounted on. */\n readonly imageSize: { readonly width: number; readonly height: number };\n /** Monotonic counter used to mint shape ids. Never decreases. */\n readonly nextShapeNumber: number;\n}\n\n/** Yellow @ 35% alpha; the highlight bake uses `multiply` blending. */\nexport const HIGHLIGHT_DEFAULT_COLOR = 'rgba(255, 235, 59, 0.35)';\nexport const HIGHLIGHT_DEFAULT_STROKE = 18;\nexport const FREEHAND_DEFAULT_STROKE = 6;\nexport const TEXT_DEFAULT_FONT_SIZE = 32;\nexport const DEFAULT_PALETTE_COLOR = '#ff3b30';\nexport const DEFAULT_STROKE_WIDTH = 4;\n\nexport function defaultStylePalette(): StylePalette {\n return {\n color: DEFAULT_PALETTE_COLOR,\n strokeWidth: DEFAULT_STROKE_WIDTH,\n fillColor: null,\n fontSize: TEXT_DEFAULT_FONT_SIZE,\n };\n}\n\nexport interface InitialAnnotateStateInput {\n readonly imageSize: { readonly width: number; readonly height: number };\n}\n\nexport function initialAnnotateState(input: InitialAnnotateStateInput): AnnotateState {\n return {\n shapes: [],\n selectedId: null,\n activeTool: 'select',\n currentStyle: defaultStylePalette(),\n imageSize: input.imageSize,\n nextShapeNumber: 1,\n };\n}\n\n/** Allocate a new shape id from the monotonic counter; caller threads `nextShapeNumber` back into state. */\nexport function mintShapeId(state: AnnotateState): {\n id: string;\n nextShapeNumber: number;\n} {\n return {\n id: `s_${state.nextShapeNumber.toString(36)}`,\n nextShapeNumber: state.nextShapeNumber + 1,\n };\n}\n\nexport function setActiveTool(state: AnnotateState, tool: AnnotateTool): AnnotateState {\n if (state.activeTool === tool) return state;\n // Switching to a drawing tool deselects so the next drag starts a new shape.\n return { ...state, activeTool: tool, selectedId: tool === 'select' ? state.selectedId : null };\n}\n\nexport function setStyle(state: AnnotateState, partial: Partial<StylePalette>): AnnotateState {\n return { ...state, currentStyle: { ...state.currentStyle, ...partial } };\n}\n\nexport function selectShape(state: AnnotateState, id: string | null): AnnotateState {\n if (state.selectedId === id) return state;\n return { ...state, selectedId: id };\n}\n\nexport function addShape(state: AnnotateState, shape: Shape): AnnotateState {\n return { ...state, shapes: [...state.shapes, shape], selectedId: shape.id };\n}\n\nexport function replaceShape(state: AnnotateState, shape: Shape): AnnotateState {\n let changed = false;\n const next = state.shapes.map((existing) => {\n if (existing.id !== shape.id) return existing;\n changed = true;\n return shape;\n });\n if (!changed) return state;\n return { ...state, shapes: next };\n}\n\nexport function deleteShape(state: AnnotateState, id: string): AnnotateState {\n const next = state.shapes.filter((shape) => shape.id !== id);\n if (next.length === state.shapes.length) return state;\n return {\n ...state,\n shapes: next,\n selectedId: state.selectedId === id ? null : state.selectedId,\n };\n}\n\nexport function findShape(state: AnnotateState, id: string | null): Shape | undefined {\n if (id === null) return undefined;\n return state.shapes.find((shape) => shape.id === id);\n}\n\n/** Normalise a rect extent so `width`/`height` are non-negative after a sign-flip drag. */\nexport function normaliseRectExtent(extent: {\n x: number;\n y: number;\n width: number;\n height: number;\n}): { x: number; y: number; width: number; height: number } {\n let { x, y, width, height } = extent;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n return { x, y, width, height };\n}\n\nexport function translateShape(shape: Shape, dx: number, dy: number): Shape {\n switch (shape.kind) {\n case 'text':\n return { ...shape, x: shape.x + dx, y: shape.y + dy };\n case 'rect':\n case 'ellipse':\n return { ...shape, x: shape.x + dx, y: shape.y + dy };\n case 'arrow':\n return {\n ...shape,\n x1: shape.x1 + dx,\n y1: shape.y1 + dy,\n x2: shape.x2 + dx,\n y2: shape.y2 + dy,\n };\n case 'freehand':\n case 'highlight':\n return { ...shape, points: shape.points.map((p) => ({ x: p.x + dx, y: p.y + dy })) };\n default:\n return assertNever(shape);\n }\n}\n\n/** Type-narrowing helper for exhaustive switches over `Shape`. */\nexport function assertNever(value: never): never {\n throw new Error(`Unhandled annotation shape kind: ${JSON.stringify(value)}`);\n}\n\n/**\n * Mirror a shape across an axis of `dims`. Rect/ellipse top-left is\n * remapped so the visible rectangle straddles the same pixels; arrow\n * endpoints and freehand points mirror independently. Text uses its\n * anchor only — the glyph rect walks slightly relative to centre.\n */\nexport function mirrorShape(\n shape: Shape,\n axis: 'horizontal' | 'vertical',\n dims: { readonly width: number; readonly height: number },\n): Shape {\n if (axis === 'horizontal') {\n switch (shape.kind) {\n case 'rect':\n case 'ellipse':\n return { ...shape, x: dims.width - shape.x - shape.width };\n case 'text':\n return { ...shape, x: dims.width - shape.x };\n case 'arrow':\n return { ...shape, x1: dims.width - shape.x1, x2: dims.width - shape.x2 };\n case 'freehand':\n case 'highlight':\n return {\n ...shape,\n points: shape.points.map((p) => ({ x: dims.width - p.x, y: p.y })),\n };\n default:\n return assertNever(shape);\n }\n }\n switch (shape.kind) {\n case 'rect':\n case 'ellipse':\n return { ...shape, y: dims.height - shape.y - shape.height };\n case 'text':\n return { ...shape, y: dims.height - shape.y };\n case 'arrow':\n return { ...shape, y1: dims.height - shape.y1, y2: dims.height - shape.y2 };\n case 'freehand':\n case 'highlight':\n return {\n ...shape,\n points: shape.points.map((p) => ({ x: p.x, y: dims.height - p.y })),\n };\n default:\n return assertNever(shape);\n }\n}\n\n/**\n * Rotate a shape `turns × 90°` CW around the centre of `oldDims`. Returns\n * coordinates in the post-rotation image's coord space (dims swap on odd turns).\n */\nexport function rotateShape(\n shape: Shape,\n turns: 0 | 1 | 2 | 3,\n oldDims: { readonly width: number; readonly height: number },\n): Shape {\n if (turns === 0) return shape;\n const rotatePoint = (x: number, y: number): { x: number; y: number } => {\n if (turns === 1) return { x: oldDims.height - y, y: x };\n if (turns === 2) return { x: oldDims.width - x, y: oldDims.height - y };\n return { x: y, y: oldDims.width - x };\n };\n switch (shape.kind) {\n case 'rect':\n case 'ellipse': {\n // Rotated TL + BR become two corners of the new axis-aligned box.\n const corners = [\n rotatePoint(shape.x, shape.y),\n rotatePoint(shape.x + shape.width, shape.y + shape.height),\n ];\n const newX = Math.min(corners[0].x, corners[1].x);\n const newY = Math.min(corners[0].y, corners[1].y);\n const newW = Math.abs(corners[1].x - corners[0].x);\n const newH = Math.abs(corners[1].y - corners[0].y);\n return { ...shape, x: newX, y: newY, width: newW, height: newH };\n }\n case 'text': {\n const p = rotatePoint(shape.x, shape.y);\n return { ...shape, x: p.x, y: p.y };\n }\n case 'arrow': {\n const p1 = rotatePoint(shape.x1, shape.y1);\n const p2 = rotatePoint(shape.x2, shape.y2);\n return { ...shape, x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y };\n }\n case 'freehand':\n case 'highlight':\n return { ...shape, points: shape.points.map((p) => rotatePoint(p.x, p.y)) };\n default:\n return assertNever(shape);\n }\n}\n\n/** Apply a transformation to every shape in `state.shapes`. */\nexport function transformShapes(\n state: AnnotateState,\n transformer: (shape: Shape) => Shape,\n): AnnotateState {\n if (state.shapes.length === 0) return state;\n return { ...state, shapes: state.shapes.map(transformer) };\n}\n\n/**\n * Kinds placeable from the keyboard. Freehand / highlight are excluded;\n * a \"default at centre\" instance has no honest shape for those.\n */\nexport type KeyboardPlaceableKind = 'text' | 'rect' | 'ellipse' | 'arrow';\n\nexport const KEYBOARD_PLACEABLE_KINDS: ReadonlyArray<KeyboardPlaceableKind> = [\n 'text',\n 'rect',\n 'ellipse',\n 'arrow',\n];\n\nexport function isKeyboardPlaceableKind(kind: ShapeKind): kind is KeyboardPlaceableKind {\n return kind === 'text' || kind === 'rect' || kind === 'ellipse' || kind === 'arrow';\n}\n\nexport interface CreateCenteredShapeContext {\n readonly imageSize: { readonly width: number; readonly height: number };\n readonly style: StylePalette;\n readonly id: string;\n}\n\n/** A `Shape` whose kind is keyboard-placeable (rect / ellipse / arrow / text). */\nexport type KeyboardPlaceableShape = TextShape | RectShape | EllipseShape | ArrowShape;\n\nexport function createCenteredShape(\n kind: KeyboardPlaceableKind,\n ctx: CreateCenteredShapeContext,\n): KeyboardPlaceableShape {\n const { imageSize, style, id } = ctx;\n const shortEdge = Math.min(imageSize.width, imageSize.height);\n const cx = imageSize.width / 2;\n const cy = imageSize.height / 2;\n\n switch (kind) {\n case 'rect':\n case 'ellipse': {\n const size = Math.max(80, Math.round(shortEdge * 0.25));\n const x = Math.round(cx - size / 2);\n const y = Math.round(cy - size / 2);\n if (kind === 'rect') {\n return {\n id,\n kind: 'rect',\n x,\n y,\n width: size,\n height: size,\n strokeColor: style.color,\n strokeWidth: style.strokeWidth,\n fillColor: style.fillColor,\n };\n }\n return {\n id,\n kind: 'ellipse',\n x,\n y,\n width: size,\n height: size,\n strokeColor: style.color,\n strokeWidth: style.strokeWidth,\n fillColor: style.fillColor,\n };\n }\n case 'arrow': {\n const length = Math.max(100, Math.round(shortEdge * 0.3));\n const x1 = Math.round(cx - length / 2);\n const x2 = x1 + length;\n const y = Math.round(cy);\n return {\n id,\n kind: 'arrow',\n x1,\n y1: y,\n x2,\n y2: y,\n color: style.color,\n strokeWidth: style.strokeWidth,\n };\n }\n case 'text': {\n const x = Math.round(cx);\n const y = Math.round(cy - style.fontSize / 2);\n return {\n id,\n kind: 'text',\n x,\n y,\n text: '',\n fontSize: style.fontSize,\n color: style.color,\n textAlign: 'center',\n };\n }\n }\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { tracePath } from './smooth.js';\nimport { assertNever, type Shape } from './state.js';\n\nexport interface AnnotateBakeInput {\n readonly shapes: ReadonlyArray<Shape>;\n}\n\n/**\n * System font stack used at bake; matches what the preview canvas renders.\n * No web font is loaded — the bundle budget rules it out and the bake\n * pipeline has no \"wait for font\" affordance.\n */\nexport const SYSTEM_FONT_STACK =\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif';\n\n/** Paint every shape onto a fresh canvas at the source's dimensions. */\nexport async function bakeAnnotate(\n state: AnnotateBakeInput,\n source: SourceImage,\n): Promise<SourceImage> {\n if (state.shapes.length === 0) return source;\n\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n for (const shape of state.shapes) {\n paintShape(ctx, shape);\n }\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n\n/** Paint one shape; caller positions the context for image-space coordinates. Shared by preview and bake. */\nexport function paintShape(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape,\n): void {\n switch (shape.kind) {\n case 'text':\n paintText(ctx, shape);\n return;\n case 'rect':\n paintRect(ctx, shape);\n return;\n case 'ellipse':\n paintEllipse(ctx, shape);\n return;\n case 'arrow':\n paintArrow(ctx, shape);\n return;\n case 'freehand':\n paintFreehand(ctx, shape);\n return;\n case 'highlight':\n paintHighlight(ctx, shape);\n return;\n default:\n assertNever(shape);\n }\n}\n\nfunction paintText(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'text' },\n): void {\n ctx.save();\n ctx.fillStyle = shape.color;\n ctx.font = `${shape.fontSize}px ${SYSTEM_FONT_STACK}`;\n ctx.textAlign = shape.textAlign;\n ctx.textBaseline = 'top';\n // Paint each line on its own; explicit `\\n` only (no auto-wrap).\n const lines = shape.text.length === 0 ? [''] : shape.text.split('\\n');\n const lineHeight = shape.fontSize * 1.2;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line === undefined) continue;\n ctx.fillText(line, shape.x, shape.y + i * lineHeight);\n }\n ctx.restore();\n}\n\nfunction paintRect(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'rect' },\n): void {\n ctx.save();\n if (shape.fillColor !== null) {\n ctx.fillStyle = shape.fillColor;\n ctx.fillRect(shape.x, shape.y, shape.width, shape.height);\n }\n if (shape.strokeWidth > 0) {\n ctx.strokeStyle = shape.strokeColor;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineJoin = 'miter';\n ctx.strokeRect(shape.x, shape.y, shape.width, shape.height);\n }\n ctx.restore();\n}\n\nfunction paintEllipse(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'ellipse' },\n): void {\n ctx.save();\n const rx = shape.width / 2;\n const ry = shape.height / 2;\n const cx = shape.x + rx;\n const cy = shape.y + ry;\n ctx.beginPath();\n ctx.ellipse(cx, cy, Math.max(0, rx), Math.max(0, ry), 0, 0, Math.PI * 2);\n if (shape.fillColor !== null) {\n ctx.fillStyle = shape.fillColor;\n ctx.fill();\n }\n if (shape.strokeWidth > 0) {\n ctx.strokeStyle = shape.strokeColor;\n ctx.lineWidth = shape.strokeWidth;\n ctx.stroke();\n }\n ctx.restore();\n}\n\nfunction paintArrow(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'arrow' },\n): void {\n const dx = shape.x2 - shape.x1;\n const dy = shape.y2 - shape.y1;\n const length = Math.sqrt(dx * dx + dy * dy);\n if (length < 0.5) return;\n\n ctx.save();\n ctx.strokeStyle = shape.color;\n ctx.fillStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n\n // Floor on head size so thin strokes still get a readable head.\n const headLength = Math.min(Math.max(shape.strokeWidth * 5, 28), length * 0.6);\n const headWidth = Math.max(shape.strokeWidth * 4, 18);\n const ux = dx / length;\n const uy = dy / length;\n // Shaft stops short of the tip so the head's base sits flush with the cap.\n const shaftEndX = shape.x2 - ux * headLength * 0.6;\n const shaftEndY = shape.y2 - uy * headLength * 0.6;\n\n ctx.beginPath();\n ctx.moveTo(shape.x1, shape.y1);\n ctx.lineTo(shaftEndX, shaftEndY);\n ctx.stroke();\n\n const baseX = shape.x2 - ux * headLength;\n const baseY = shape.y2 - uy * headLength;\n const px = -uy;\n const py = ux;\n ctx.beginPath();\n ctx.moveTo(shape.x2, shape.y2);\n ctx.lineTo(baseX + (px * headWidth) / 2, baseY + (py * headWidth) / 2);\n ctx.lineTo(baseX - (px * headWidth) / 2, baseY - (py * headWidth) / 2);\n ctx.closePath();\n ctx.fill();\n ctx.restore();\n}\n\nfunction paintFreehand(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'freehand' },\n): void {\n if (shape.points.length === 0) return;\n ctx.save();\n ctx.strokeStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.beginPath();\n tracePath(ctx, shape.points);\n ctx.stroke();\n ctx.restore();\n}\n\nfunction paintHighlight(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'highlight' },\n): void {\n if (shape.points.length === 0) return;\n ctx.save();\n // `multiply` tints pixels like a highlighter pen; engines without it fall back to alpha-blend.\n ctx.globalCompositeOperation = 'multiply';\n ctx.strokeStyle = shape.color;\n ctx.lineWidth = shape.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.beginPath();\n tracePath(ctx, shape.points);\n ctx.stroke();\n ctx.restore();\n}\n","import type { Rect } from '../../geometry/rect.js';\nimport { assertNever, type Shape } from './state.js';\n\n/**\n * Axis-aligned bounding box in image-space pixels. Text uses a font-metric\n * estimate because jsdom's `measureText` returns 0; the renderer measures\n * real text at paint time.\n */\nexport function boundingBoxOf(shape: Shape): Rect {\n switch (shape.kind) {\n case 'text': {\n const { width, height } = estimateTextSize(shape.text, shape.fontSize);\n const x = alignToOrigin(shape.x, width, shape.textAlign);\n return { x, y: shape.y, width, height };\n }\n case 'rect':\n case 'ellipse':\n return { x: shape.x, y: shape.y, width: shape.width, height: shape.height };\n case 'arrow': {\n const x = Math.min(shape.x1, shape.x2);\n const y = Math.min(shape.y1, shape.y2);\n return {\n x,\n y,\n width: Math.abs(shape.x2 - shape.x1),\n height: Math.abs(shape.y2 - shape.y1),\n };\n }\n case 'freehand':\n case 'highlight': {\n const head = shape.points[0];\n if (!head) return { x: 0, y: 0, width: 0, height: 0 };\n let minX = head.x;\n let minY = head.y;\n let maxX = head.x;\n let maxY = head.y;\n for (const p of shape.points) {\n if (p.x < minX) minX = p.x;\n if (p.x > maxX) maxX = p.x;\n if (p.y < minY) minY = p.y;\n if (p.y > maxY) maxY = p.y;\n }\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n }\n default:\n return assertNever(shape);\n }\n}\n\n/**\n * Eight-handle layout (corners + edges). Arrows reuse `tl`/`br` as\n * endpoint handles; callers detect arrow handles by shape kind.\n */\nexport type SelectionHandle = 'tl' | 'tr' | 'bl' | 'br' | 't' | 'r' | 'b' | 'l';\n\nexport const ALL_SELECTION_HANDLES: readonly SelectionHandle[] = [\n 'tl',\n 'tr',\n 'bl',\n 'br',\n 't',\n 'r',\n 'b',\n 'l',\n];\n\n/** Image-space coordinates for each handle; renderer projects to display. */\nexport function selectionHandlePositions(\n rect: Rect,\n): Record<SelectionHandle, { x: number; y: number }> {\n const left = rect.x;\n const right = rect.x + rect.width;\n const top = rect.y;\n const bottom = rect.y + rect.height;\n const cx = rect.x + rect.width / 2;\n const cy = rect.y + rect.height / 2;\n return {\n tl: { x: left, y: top },\n tr: { x: right, y: top },\n bl: { x: left, y: bottom },\n br: { x: right, y: bottom },\n t: { x: cx, y: top },\n r: { x: right, y: cy },\n b: { x: cx, y: bottom },\n l: { x: left, y: cy },\n };\n}\n\n/** Apply a handle drag to a rect. Returns the new rect; the caller normalises. */\nexport function rectFromHandleDrag(\n initial: Rect,\n handle: SelectionHandle,\n pointer: { x: number; y: number },\n): Rect {\n let x = initial.x;\n let y = initial.y;\n let right = initial.x + initial.width;\n let bottom = initial.y + initial.height;\n\n if (handle === 'tl' || handle === 'l' || handle === 'bl') x = pointer.x;\n if (handle === 'tr' || handle === 'r' || handle === 'br') right = pointer.x;\n if (handle === 'tl' || handle === 't' || handle === 'tr') y = pointer.y;\n if (handle === 'bl' || handle === 'b' || handle === 'br') bottom = pointer.y;\n\n return { x, y, width: right - x, height: bottom - y };\n}\n\n/**\n * Estimate painted text size without `measureText` (jsdom returns 0).\n * Uses 0.55em mean Latin char width and 1.2em line height — close enough\n * for selection-handle placement.\n */\nexport function estimateTextSize(\n text: string,\n fontSize: number,\n): { width: number; height: number } {\n const lines = text.length === 0 ? [''] : text.split('\\n');\n let maxLineLen = 0;\n for (const line of lines) {\n if (line.length > maxLineLen) maxLineLen = line.length;\n }\n const width = Math.max(fontSize * 0.6, maxLineLen * fontSize * 0.55);\n const height = lines.length * fontSize * 1.2;\n return { width, height };\n}\n\n/** Convert a text shape's anchor to the bounding box's top-left given `textAlign`. */\nexport function alignToOrigin(\n anchorX: number,\n width: number,\n align: 'left' | 'center' | 'right',\n): number {\n switch (align) {\n case 'left':\n return anchorX;\n case 'center':\n return anchorX - width / 2;\n case 'right':\n return anchorX - width;\n }\n}\n","import type { Point } from '../../geometry/rect.js';\nimport { boundingBoxOf } from './geometry.js';\nimport { assertNever, type Shape } from './state.js';\n\n/** Picking margin added to every stroked-shape hit-test, in image-space pixels. */\nexport const PICK_TOLERANCE = 4;\n\n/** Find the topmost shape under `point` (image-space). Iterates back-to-front. */\nexport function pickShape(shapes: ReadonlyArray<Shape>, point: Point): Shape | undefined {\n for (let i = shapes.length - 1; i >= 0; i--) {\n const shape = shapes[i];\n if (shape && hitTest(shape, point)) return shape;\n }\n return undefined;\n}\n\nexport function hitTest(shape: Shape, point: Point): boolean {\n switch (shape.kind) {\n case 'text':\n return pointInRect(point, boundingBoxOf(shape));\n case 'rect': {\n const inside = pointInRect(point, normaliseBox(shape));\n // Filled rects pick anywhere inside; outline-only picks on the stroke (with tolerance).\n if (shape.fillColor !== null) return inside;\n const outer = expandRect(normaliseBox(shape), shape.strokeWidth / 2 + PICK_TOLERANCE);\n const inner = expandRect(normaliseBox(shape), -(shape.strokeWidth / 2 + PICK_TOLERANCE));\n return pointInRect(point, outer) && !pointInRect(point, inner);\n }\n case 'ellipse': {\n const box = normaliseBox(shape);\n const rx = box.width / 2;\n const ry = box.height / 2;\n const cx = box.x + rx;\n const cy = box.y + ry;\n if (rx <= 0 || ry <= 0) return false;\n const nx = (point.x - cx) / rx;\n const ny = (point.y - cy) / ry;\n const r2 = nx * nx + ny * ny;\n const tolerance = (shape.strokeWidth / 2 + PICK_TOLERANCE) / Math.min(rx, ry);\n if (shape.fillColor !== null) return r2 <= (1 + tolerance) ** 2;\n return r2 <= (1 + tolerance) ** 2 && r2 >= (1 - tolerance) ** 2;\n }\n case 'arrow':\n return pointNearSegment(\n point,\n { x: shape.x1, y: shape.y1 },\n { x: shape.x2, y: shape.y2 },\n shape.strokeWidth / 2 + PICK_TOLERANCE,\n );\n case 'freehand':\n case 'highlight': {\n const box = boundingBoxOf(shape);\n const expanded = expandRect(box, shape.strokeWidth / 2 + PICK_TOLERANCE);\n if (!pointInRect(point, expanded)) return false;\n const tolerance = shape.strokeWidth / 2 + PICK_TOLERANCE;\n for (let i = 1; i < shape.points.length; i++) {\n const a = shape.points[i - 1];\n const b = shape.points[i];\n if (a && b && pointNearSegment(point, a, b, tolerance)) return true;\n }\n if (shape.points.length === 1) {\n const p = shape.points[0];\n if (!p) return false;\n const dx = p.x - point.x;\n const dy = p.y - point.y;\n return dx * dx + dy * dy <= tolerance * tolerance;\n }\n return false;\n }\n default:\n return assertNever(shape);\n }\n}\n\nfunction pointInRect(\n point: Point,\n rect: { x: number; y: number; width: number; height: number },\n): boolean {\n return (\n point.x >= rect.x &&\n point.y >= rect.y &&\n point.x <= rect.x + rect.width &&\n point.y <= rect.y + rect.height\n );\n}\n\nfunction expandRect(\n rect: { x: number; y: number; width: number; height: number },\n amount: number,\n): { x: number; y: number; width: number; height: number } {\n return {\n x: rect.x - amount,\n y: rect.y - amount,\n width: rect.width + amount * 2,\n height: rect.height + amount * 2,\n };\n}\n\nfunction normaliseBox(shape: { x: number; y: number; width: number; height: number }): {\n x: number;\n y: number;\n width: number;\n height: number;\n} {\n let { x, y, width, height } = shape;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n return { x, y, width, height };\n}\n\nfunction pointNearSegment(point: Point, a: Point, b: Point, tolerance: number): boolean {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const len2 = dx * dx + dy * dy;\n if (len2 === 0) {\n const ex = point.x - a.x;\n const ey = point.y - a.y;\n return ex * ex + ey * ey <= tolerance * tolerance;\n }\n let t = ((point.x - a.x) * dx + (point.y - a.y) * dy) / len2;\n if (t < 0) t = 0;\n else if (t > 1) t = 1;\n const projX = a.x + t * dx;\n const projY = a.y + t * dy;\n const ex = point.x - projX;\n const ey = point.y - projY;\n return ex * ex + ey * ey <= tolerance * tolerance;\n}\n","import { clampRectInside, type Rect } from '../../geometry/rect.js';\n\n/**\n * Compute the largest axis-aligned rectangle of `targetRatio` (= w/h) that\n * fits inside `bounds`, centered. Used to seed the crop rectangle when the\n * user picks an aspect-ratio preset before any drag.\n */\nexport function fitRectToBoundsWithRatio(bounds: Rect, targetRatio: number): Rect {\n if (targetRatio <= 0 || bounds.width <= 0 || bounds.height <= 0) {\n return { x: bounds.x, y: bounds.y, width: 0, height: 0 };\n }\n\n const boundsRatio = bounds.width / bounds.height;\n let width: number;\n let height: number;\n if (targetRatio >= boundsRatio) {\n width = bounds.width;\n height = width / targetRatio;\n } else {\n height = bounds.height;\n width = height * targetRatio;\n }\n\n return {\n x: bounds.x + (bounds.width - width) / 2,\n y: bounds.y + (bounds.height - height) / 2,\n width,\n height,\n };\n}\n\n/**\n * Reshape `rect` to `targetRatio`, anchored at `anchor`, clamped inside\n * `bounds`. If clamping breaks the ratio, falls back to the largest same-ratio\n * sub-rect that fits, anchored identically.\n */\nexport function applyAspectRatio(\n rect: Rect,\n targetRatio: number,\n anchor: AspectAnchor,\n bounds: Rect,\n): Rect {\n if (targetRatio <= 0) return rect;\n if (rect.width <= 0 || rect.height <= 0) return fitRectToBoundsWithRatio(bounds, targetRatio);\n\n const currentRatio = rect.width / rect.height;\n let width: number;\n let height: number;\n if (currentRatio > targetRatio) {\n height = rect.height;\n width = height * targetRatio;\n } else {\n width = rect.width;\n height = width / targetRatio;\n }\n\n const reshaped = anchorRect(rect, width, height, anchor);\n const clamped = clampRectInside(reshaped, bounds);\n\n const clampedRatio = clamped.height === 0 ? 0 : clamped.width / clamped.height;\n if (Math.abs(clampedRatio - targetRatio) <= RATIO_TOLERANCE) {\n return clamped;\n }\n return fitInsideAtAnchor(clamped, targetRatio, anchor);\n}\n\nconst RATIO_TOLERANCE = 1e-6;\n\nexport type AspectAnchor = 'tl' | 'tr' | 'bl' | 'br' | 'center';\n\nfunction anchorRect(rect: Rect, width: number, height: number, anchor: AspectAnchor): Rect {\n switch (anchor) {\n case 'tl':\n return { x: rect.x, y: rect.y, width, height };\n case 'tr':\n return { x: rect.x + rect.width - width, y: rect.y, width, height };\n case 'bl':\n return { x: rect.x, y: rect.y + rect.height - height, width, height };\n case 'br':\n return {\n x: rect.x + rect.width - width,\n y: rect.y + rect.height - height,\n width,\n height,\n };\n case 'center':\n return {\n x: rect.x + (rect.width - width) / 2,\n y: rect.y + (rect.height - height) / 2,\n width,\n height,\n };\n }\n}\n\nfunction fitInsideAtAnchor(bounds: Rect, targetRatio: number, anchor: AspectAnchor): Rect {\n const fitted = fitRectToBoundsWithRatio(bounds, targetRatio);\n return anchorRect(bounds, fitted.width, fitted.height, anchor);\n}\n","import { createBakeCanvas } from '../../canvas/bake-canvas.js';\nimport { type Rect, roundRect } from '../../geometry/rect.js';\nimport type { SourceImage } from '../utility.js';\n\nexport interface CropBakeInput {\n /** The cropped region, in image-space pixels. */\n readonly rect: Rect;\n}\n\n/**\n * Apply a crop and return a SourceImage at the crop's pixel size. The\n * rect is rounded and clamped against the source so an oversized rect\n * doesn't crash — we draw what fits.\n */\nexport function bakeCrop(source: SourceImage, input: CropBakeInput): SourceImage {\n const rounded = roundRect(input.rect);\n const x = clamp(rounded.x, 0, source.width);\n const y = clamp(rounded.y, 0, source.height);\n const w = clamp(rounded.width, 1, source.width - x);\n const h = clamp(rounded.height, 1, source.height - y);\n\n const bake = createBakeCanvas(w, h);\n if (bake.kind === 'offscreen') {\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n ctx.drawImage(source.bitmap, x, y, w, h, 0, 0, w, h);\n return { bitmap: bake.canvas, width: w, height: h, mimeType: source.mimeType };\n }\n const ctx = bake.canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context is not available');\n ctx.drawImage(source.bitmap, x, y, w, h, 0, 0, w, h);\n return { bitmap: bake.canvas, width: w, height: h, mimeType: source.mimeType };\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, n));\n}\n","/**\n * Filters crop presets by aspect-ratio relative to 1. `landscape` keeps\n * ratio ≥ 1, `portrait` keeps ratio < 1; `undefined` ratios (Custom) and\n * unknown tokens stay visible.\n */\nexport type CropPresetFilter = 'landscape' | 'portrait';\nexport type CropPreset = readonly [number | undefined, string];\n\nexport function isPresetVisible(preset: CropPreset, filter: CropPresetFilter | undefined): boolean {\n const [ratio] = preset;\n if (ratio === undefined) return true;\n if (filter === undefined) return true;\n if (filter === 'landscape') return ratio >= 1;\n if (filter === 'portrait') return ratio < 1;\n return true;\n}\n\nexport function filterPresets(\n presets: readonly CropPreset[],\n filter: CropPresetFilter | undefined,\n): readonly CropPreset[] {\n return presets.filter((preset) => isPresetVisible(preset, filter));\n}\n","import { clampRectInside, type Point, type Rect } from '../../geometry/rect.js';\nimport { type AspectAnchor, applyAspectRatio } from './aspect-ratio.js';\n\nexport type CornerHandle = 'tl' | 'tr' | 'bl' | 'br';\nexport type EdgeHandle = 't' | 'r' | 'b' | 'l';\nexport type HandleDirection = CornerHandle | EdgeHandle;\n\nexport interface ResizeOptions {\n /** Image-space bounds the rect must stay inside. */\n readonly bounds: Rect;\n /** Aspect ratio to enforce, or `undefined` for free crop. */\n readonly aspectRatio?: number;\n /** Minimum size on either axis, in image-space units. Defaults to 1. */\n readonly minSize?: number;\n}\n\n/**\n * Resize a rect from one of its eight handles to `pointer`. Opposite\n * corner/edge anchors; result clamped to `bounds` and reshaped to\n * `aspectRatio` (anchored at the same opposite corner) when supplied.\n */\nexport function resizeRectFromHandle(\n rect: Rect,\n handle: HandleDirection,\n pointer: Point,\n options: ResizeOptions,\n): Rect {\n const minSize = options.minSize ?? 1;\n const left = rect.x;\n const top = rect.y;\n const right = rect.x + rect.width;\n const bottom = rect.y + rect.height;\n\n // Pointer may swap sides (drag through the anchor); recompute from anchor + live edge.\n let newLeft = left;\n let newTop = top;\n let newRight = right;\n let newBottom = bottom;\n\n if (handle === 'tl' || handle === 'l' || handle === 'bl') {\n newLeft = pointer.x;\n }\n if (handle === 'tr' || handle === 'r' || handle === 'br') {\n newRight = pointer.x;\n }\n if (handle === 'tl' || handle === 't' || handle === 'tr') {\n newTop = pointer.y;\n }\n if (handle === 'bl' || handle === 'b' || handle === 'br') {\n newBottom = pointer.y;\n }\n\n if (handle === 'l' || handle === 'r') {\n newTop = top;\n newBottom = bottom;\n }\n if (handle === 't' || handle === 'b') {\n newLeft = left;\n newRight = right;\n }\n\n let nx = Math.min(newLeft, newRight);\n let ny = Math.min(newTop, newBottom);\n let nw = Math.abs(newRight - newLeft);\n let nh = Math.abs(newBottom - newTop);\n\n if (nw < minSize) {\n nw = minSize;\n if (handle === 'tl' || handle === 'l' || handle === 'bl') {\n nx = right - minSize;\n } else if (handle === 'tr' || handle === 'r' || handle === 'br') {\n nx = left;\n }\n }\n if (nh < minSize) {\n nh = minSize;\n if (handle === 'tl' || handle === 't' || handle === 'tr') {\n ny = bottom - minSize;\n } else if (handle === 'bl' || handle === 'b' || handle === 'br') {\n ny = top;\n }\n }\n\n let resized: Rect = { x: nx, y: ny, width: nw, height: nh };\n resized = clampRectInside(resized, options.bounds);\n\n if (options.aspectRatio !== undefined && options.aspectRatio > 0) {\n resized = applyAspectRatio(resized, options.aspectRatio, anchorFor(handle), options.bounds);\n }\n\n return resized;\n}\n\nfunction anchorFor(handle: HandleDirection): AspectAnchor {\n switch (handle) {\n case 'tl':\n return 'br';\n case 'tr':\n return 'bl';\n case 'bl':\n return 'tr';\n case 'br':\n return 'tl';\n case 't':\n return 'bl';\n case 'b':\n return 'tl';\n case 'l':\n return 'tr';\n case 'r':\n return 'tl';\n }\n}\n","import type { Rect, Size } from '../../geometry/rect.js';\nimport { fitRectToBoundsWithRatio } from './aspect-ratio.js';\nimport type { CropPreset, CropPresetFilter } from './preset-filter.js';\n\nexport interface CropState {\n /** Crop rectangle in image-space pixels. */\n readonly rect: Rect;\n /** Active aspect-ratio constraint (image w/h), or `undefined` for free. */\n readonly aspectRatio: number | undefined;\n /** Index into `presets`, or `-1` if no preset is active. */\n readonly activePresetIndex: number;\n /** Visible presets after applying `cropSelectPresetFilter`. */\n readonly presets: readonly CropPreset[];\n /** Image dimensions, in pixels. The bounds the crop can move within. */\n readonly imageSize: Size;\n}\n\nexport interface InitialCropStateInput {\n readonly imageSize: Size;\n readonly presets: readonly CropPreset[];\n readonly filter: CropPresetFilter | undefined;\n}\n\n/** Full-frame crop, \"Custom\" preset active. */\nexport function initialCropState(input: InitialCropStateInput): CropState {\n const bounds: Rect = { x: 0, y: 0, width: input.imageSize.width, height: input.imageSize.height };\n return {\n rect: bounds,\n aspectRatio: undefined,\n activePresetIndex: findCustomIndex(input.presets),\n presets: input.presets,\n imageSize: input.imageSize,\n };\n}\n\nexport function applyPresetByIndex(state: CropState, presetIndex: number): CropState {\n const preset = state.presets[presetIndex];\n if (!preset) return state;\n const [ratio] = preset;\n if (ratio === undefined) {\n return { ...state, aspectRatio: undefined, activePresetIndex: presetIndex };\n }\n const bounds: Rect = {\n x: 0,\n y: 0,\n width: state.imageSize.width,\n height: state.imageSize.height,\n };\n const fitted = fitRectToBoundsWithRatio(bounds, ratio);\n return { ...state, rect: fitted, aspectRatio: ratio, activePresetIndex: presetIndex };\n}\n\nfunction findCustomIndex(presets: readonly CropPreset[]): number {\n return presets.findIndex(([ratio]) => ratio === undefined);\n}\n","/**\n * Six tone adjustments stored as slider values in [-100, +100]; math\n * constants (gamma exponent, contrast multiplier, etc.) are computed\n * from these in `math.ts` at LUT-build time.\n */\nexport interface FinetuneState {\n readonly brightness: number;\n readonly contrast: number;\n readonly saturation: number;\n readonly exposure: number;\n readonly clarity: number;\n readonly gamma: number;\n}\n\nexport const FINETUNE_MIN = -100;\nexport const FINETUNE_MAX = 100;\nexport const FINETUNE_STEP = 1;\n\nexport const DEFAULT_FINETUNE_STATE: FinetuneState = {\n brightness: 0,\n contrast: 0,\n saturation: 0,\n exposure: 0,\n clarity: 0,\n gamma: 0,\n};\n\nexport type FinetuneKey = keyof FinetuneState;\n\nexport const FINETUNE_ADJUSTMENTS: readonly {\n readonly key: FinetuneKey;\n readonly label: string;\n}[] = [\n { key: 'brightness', label: 'Brightness' },\n { key: 'contrast', label: 'Contrast' },\n { key: 'saturation', label: 'Saturation' },\n { key: 'exposure', label: 'Exposure' },\n { key: 'clarity', label: 'Clarity' },\n { key: 'gamma', label: 'Gamma' },\n];\n\nexport function initialFinetuneState(): FinetuneState {\n return DEFAULT_FINETUNE_STATE;\n}\n\nexport function isFinetuneNoOp(state: FinetuneState): boolean {\n return (\n state.brightness === 0 &&\n state.contrast === 0 &&\n state.saturation === 0 &&\n state.exposure === 0 &&\n state.clarity === 0 &&\n state.gamma === 0\n );\n}\n\n/** Update a single adjustment, clamped to the legal range. */\nexport function setFinetune(state: FinetuneState, key: FinetuneKey, value: number): FinetuneState {\n const next = clampSliderValue(value);\n if (state[key] === next) return state;\n return { ...state, [key]: next };\n}\n\n/** Reset a single adjustment to its default of 0. */\nexport function resetFinetune(state: FinetuneState, key: FinetuneKey): FinetuneState {\n if (state[key] === 0) return state;\n return { ...state, [key]: 0 };\n}\n\n/** Reset every adjustment to its default. */\nexport function resetAllFinetune(): FinetuneState {\n return DEFAULT_FINETUNE_STATE;\n}\n\nfunction clampSliderValue(value: number): number {\n if (Number.isNaN(value)) return 0;\n if (value <= FINETUNE_MIN) return FINETUNE_MIN;\n if (value >= FINETUNE_MAX) return FINETUNE_MAX;\n // Snap to slider step so programmatic setters round-trip through the input.\n return Math.round(value / FINETUNE_STEP) * FINETUNE_STEP;\n}\n","/**\n * Curated `FinetuneState` shapes that one-click set the six tone numbers.\n * The filter tab is a UI view over the finetune store: clicking a preset\n * here is identical to dragging the matching sliders in finetune.\n */\nimport { DEFAULT_FINETUNE_STATE, type FinetuneState } from '../finetune/state.js';\n\nexport type FilterPresetId = 'none' | 'vivid' | 'mono' | 'soft' | 'punch' | 'mute' | 'bright';\n\nexport interface FilterPreset {\n readonly id: FilterPresetId;\n readonly label: string;\n readonly state: FinetuneState;\n}\n\n/** The seven presets in display order. `none` is the identity / off state. */\nexport const FILTER_PRESETS: readonly FilterPreset[] = [\n {\n id: 'none',\n label: 'None',\n state: DEFAULT_FINETUNE_STATE,\n },\n {\n id: 'vivid',\n label: 'Vivid',\n state: {\n brightness: 0,\n contrast: 10,\n saturation: 40,\n exposure: 0,\n clarity: 5,\n gamma: 0,\n },\n },\n {\n id: 'mono',\n label: 'Mono',\n // -100 saturation is bit-exact grayscale via Rec. 709 luminance.\n state: {\n brightness: 0,\n contrast: 15,\n saturation: -100,\n exposure: 0,\n clarity: 0,\n gamma: 0,\n },\n },\n {\n id: 'soft',\n label: 'Soft',\n state: {\n brightness: 5,\n contrast: -10,\n saturation: 0,\n exposure: 0,\n clarity: -25,\n gamma: 0,\n },\n },\n {\n id: 'punch',\n label: 'Punch',\n state: {\n brightness: 0,\n contrast: 30,\n saturation: 5,\n exposure: 0,\n clarity: 25,\n gamma: 0,\n },\n },\n {\n id: 'mute',\n label: 'Mute',\n state: {\n brightness: 0,\n contrast: 5,\n saturation: -50,\n exposure: 0,\n clarity: -5,\n gamma: 0,\n },\n },\n {\n id: 'bright',\n label: 'Bright',\n state: {\n brightness: 5,\n contrast: 5,\n saturation: 0,\n exposure: 15,\n clarity: 0,\n gamma: 0,\n },\n },\n];\n\n/** Structural equality across the six finetune fields. */\nexport function finetuneStatesEqual(a: FinetuneState, b: FinetuneState): boolean {\n return (\n a.brightness === b.brightness &&\n a.contrast === b.contrast &&\n a.saturation === b.saturation &&\n a.exposure === b.exposure &&\n a.clarity === b.clarity &&\n a.gamma === b.gamma\n );\n}\n\n/** Preset whose state matches `state` exactly, or `undefined` if between presets. */\nexport function findActivePreset(state: FinetuneState): FilterPreset | undefined {\n for (const preset of FILTER_PRESETS) {\n if (finetuneStatesEqual(preset.state, state)) return preset;\n }\n return undefined;\n}\n","/**\n * Pure math for the finetune adjustments. Slider values arrive in\n * [-100, +100] from `state.ts`. Shared by the bake (full-resolution)\n * and the live preview (display-resolution).\n */\n\nimport type { FinetuneState } from './state.js';\n\n/**\n * 256-entry LUT collapsing brightness + contrast + exposure + gamma into\n * one per-byte mapping. Saturation and clarity run in separate passes.\n */\nexport function buildFinetuneLut(state: FinetuneState): Uint8ClampedArray {\n const lut = new Uint8ClampedArray(256);\n\n // brightness: -100 → -0.5, +100 → +0.5 on the normalised value\n const brightnessOffset = state.brightness / 200;\n // contrast multiplier around mid-gray: -100 → 0×, +100 → 2×\n const contrastFactor = 1 + state.contrast / 100;\n // exposure multiplier: -100 → 0.5×, +100 → 1.5×\n const exposureFactor = 1 + state.exposure / 200;\n // gamma exponent: -100 → 2.0, 0 → 1.0, +100 → 0.5\n const gammaExponent = gammaExponentFor(state.gamma);\n\n for (let v = 0; v < 256; v++) {\n let x = v / 255;\n x = x * exposureFactor;\n x = (x - 0.5) * contrastFactor + 0.5;\n x = x + brightnessOffset;\n if (x < 0) x = 0;\n else if (x > 1) x = 1;\n x = x ** gammaExponent;\n if (x < 0) x = 0;\n else if (x > 1) x = 1;\n lut[v] = Math.round(x * 255);\n }\n\n return lut;\n}\n\nfunction gammaExponentFor(slider: number): number {\n if (slider === 0) return 1;\n if (slider > 0) return 1 - 0.5 * (slider / 100);\n return 1 + 1.0 * (-slider / 100);\n}\n\n/**\n * Apply the LUT and saturation in one pass. `src`/`dst` may be the same\n * buffer. Saturation: -100 → grayscale (Rec. 709), 0 → identity, +100 → 2×.\n */\nexport function applyFinetuneLutAndSaturation(\n src: Uint8ClampedArray,\n dst: Uint8ClampedArray,\n lut: Uint8ClampedArray,\n state: FinetuneState,\n): void {\n const len = src.length;\n if (dst.length !== len) {\n throw new Error('applyFinetuneLutAndSaturation: src/dst length mismatch');\n }\n\n const saturation = 1 + state.saturation / 100;\n\n // Fast path for the common identity (saturation === 0).\n if (saturation === 1) {\n for (let i = 0; i < len; i += 4) {\n dst[i] = lut[src[i]];\n dst[i + 1] = lut[src[i + 1]];\n dst[i + 2] = lut[src[i + 2]];\n dst[i + 3] = src[i + 3];\n }\n return;\n }\n\n for (let i = 0; i < len; i += 4) {\n const r0 = lut[src[i]];\n const g0 = lut[src[i + 1]];\n const b0 = lut[src[i + 2]];\n // Rec. 709 luminance.\n const y = 0.2126 * r0 + 0.7152 * g0 + 0.0722 * b0;\n let r = y + (r0 - y) * saturation;\n let g = y + (g0 - y) * saturation;\n let b = y + (b0 - y) * saturation;\n if (r < 0) r = 0;\n else if (r > 255) r = 255;\n if (g < 0) g = 0;\n else if (g > 255) g = 255;\n if (b < 0) b = 0;\n else if (b > 255) b = 255;\n dst[i] = r;\n dst[i + 1] = g;\n dst[i + 2] = b;\n dst[i + 3] = src[i + 3];\n }\n}\n\n/**\n * Unsharp-mask local contrast: `result = dst + (clarity/100) * (dst - blurred)`.\n * `clarity === 0` is a no-op; caller is expected to skip the call.\n */\nexport function applyClarity(\n dst: Uint8ClampedArray,\n blurred: Uint8ClampedArray,\n clarity: number,\n): void {\n if (clarity === 0) return;\n const len = dst.length;\n if (blurred.length !== len) {\n throw new Error('applyClarity: dst/blurred length mismatch');\n }\n const amount = clarity / 100;\n for (let i = 0; i < len; i += 4) {\n const dr = dst[i];\n const dg = dst[i + 1];\n const db = dst[i + 2];\n let r = dr + amount * (dr - blurred[i]);\n let g = dg + amount * (dg - blurred[i + 1]);\n let b = db + amount * (db - blurred[i + 2]);\n if (r < 0) r = 0;\n else if (r > 255) r = 255;\n if (g < 0) g = 0;\n else if (g > 255) g = 255;\n if (b < 0) b = 0;\n else if (b > 255) b = 255;\n dst[i] = r;\n dst[i + 1] = g;\n dst[i + 2] = b;\n }\n}\n\n/** Separable 3×3 box blur (clamp at edges). Used as the unsharp-mask reference. */\nexport function boxBlur3x3(\n src: Uint8ClampedArray,\n tmp: Uint8ClampedArray,\n dst: Uint8ClampedArray,\n width: number,\n height: number,\n): void {\n if (src.length !== tmp.length || src.length !== dst.length) {\n throw new Error('boxBlur3x3: buffer length mismatch');\n }\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const xm = x === 0 ? 0 : x - 1;\n const xp = x === width - 1 ? width - 1 : x + 1;\n const i = (y * width + x) * 4;\n const im = (y * width + xm) * 4;\n const ip = (y * width + xp) * 4;\n tmp[i] = (src[im] + src[i] + src[ip]) / 3;\n tmp[i + 1] = (src[im + 1] + src[i + 1] + src[ip + 1]) / 3;\n tmp[i + 2] = (src[im + 2] + src[i + 2] + src[ip + 2]) / 3;\n tmp[i + 3] = src[i + 3];\n }\n }\n for (let y = 0; y < height; y++) {\n const ym = y === 0 ? 0 : y - 1;\n const yp = y === height - 1 ? height - 1 : y + 1;\n for (let x = 0; x < width; x++) {\n const i = (y * width + x) * 4;\n const im = (ym * width + x) * 4;\n const ip = (yp * width + x) * 4;\n dst[i] = (tmp[im] + tmp[i] + tmp[ip]) / 3;\n dst[i + 1] = (tmp[im + 1] + tmp[i + 1] + tmp[ip + 1]) / 3;\n dst[i + 2] = (tmp[im + 2] + tmp[i + 2] + tmp[ip + 2]) / 3;\n dst[i + 3] = tmp[i + 3];\n }\n }\n}\n\n/** Structural raster shape so tests can pass a plain object (some jsdom versions lack `ImageData`). */\nexport interface RasterImage {\n // ArrayBuffer-pinned so data is assignable to `new ImageData(...)`.\n readonly data: Uint8ClampedArray<ArrayBuffer>;\n readonly width: number;\n readonly height: number;\n}\n\n/** Apply the full pipeline (LUT + saturation + clarity); blur buffers allocate only when clarity ≠ 0. */\nexport function applyFinetuneToImageData(\n state: FinetuneState,\n baseline: RasterImage,\n dst: RasterImage,\n): void {\n if (\n baseline.width !== dst.width ||\n baseline.height !== dst.height ||\n baseline.data.length !== dst.data.length\n ) {\n throw new Error('applyFinetuneToImageData: baseline/dst dimensions mismatch');\n }\n\n const lut = buildFinetuneLut(state);\n applyFinetuneLutAndSaturation(baseline.data, dst.data, lut, state);\n\n if (state.clarity !== 0) {\n // Blur the pre-LUT baseline so the cached blur stays valid as long as\n // the baseline does; high-frequency is roughly order-independent here.\n const tmp = new Uint8ClampedArray(baseline.data.length);\n const blurred = new Uint8ClampedArray(baseline.data.length);\n boxBlur3x3(baseline.data, tmp, blurred, baseline.width, baseline.height);\n applyClarity(dst.data, blurred, state.clarity);\n }\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { applyFinetuneToImageData } from './math.js';\nimport { type FinetuneState, isFinetuneNoOp } from './state.js';\n\n/** Apply the six finetune adjustments at full resolution. Shares the math with the live preview. */\nexport async function bakeFinetune(\n state: FinetuneState,\n source: SourceImage,\n): Promise<SourceImage> {\n if (isFinetuneNoOp(state)) return source;\n\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n const baseline = ctx.getImageData(0, 0, source.width, source.height);\n // When clarity is non-zero the unsharp-mask step needs `dst` and `blurred`\n // simultaneously, so we can't reuse the baseline buffer in place.\n if (state.clarity === 0) {\n applyFinetuneToImageData(state, baseline, baseline);\n ctx.putImageData(baseline, 0, 0);\n } else {\n const dst = new ImageData(\n new Uint8ClampedArray(baseline.data.length),\n baseline.width,\n baseline.height,\n );\n applyFinetuneToImageData(state, baseline, dst);\n ctx.putImageData(dst, 0, 0);\n }\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n","/** Two independent axis flips; their composition equals a 180° rotation. */\nexport interface FlipState {\n readonly horizontal: boolean;\n readonly vertical: boolean;\n}\n\nexport function initialFlipState(): FlipState {\n return { horizontal: false, vertical: false };\n}\n\nexport function toggleFlip(state: FlipState, axis: 'horizontal' | 'vertical'): FlipState {\n return axis === 'horizontal'\n ? { ...state, horizontal: !state.horizontal }\n : { ...state, vertical: !state.vertical };\n}\n\nexport function isFlipNoOp(state: FlipState): boolean {\n return !state.horizontal && !state.vertical;\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { type FlipState, isFlipNoOp } from './state.js';\n\n/** Apply horizontal / vertical flips via a sign-flipped scale on `drawImage`. */\nexport async function bakeFlip(state: FlipState, source: SourceImage): Promise<SourceImage> {\n if (isFlipNoOp(state)) return source;\n\n const { width, height } = source;\n const bake = createBakeCanvas(width, height);\n const ctx = getBakeContext2D(bake);\n\n const sx = state.horizontal ? -1 : 1;\n const sy = state.vertical ? -1 : 1;\n const tx = state.horizontal ? width : 0;\n const ty = state.vertical ? height : 0;\n\n ctx.setTransform(sx, 0, 0, sy, tx, ty);\n ctx.drawImage(source.bitmap, 0, 0);\n\n return {\n bitmap: bake.canvas,\n width,\n height,\n mimeType: source.mimeType,\n };\n}\n","/**\n * Frame preset ids match Ghost's `frameOptions` identifiers directly so\n * a contract change is a state rename, not a translation table.\n */\nexport type FramePresetId =\n | 'none'\n | 'solidSharp'\n | 'solidRound'\n | 'lineSingle'\n | 'hook'\n | 'polaroid';\n\nexport const FRAME_PRESET_IDS: readonly FramePresetId[] = [\n 'none',\n 'solidSharp',\n 'solidRound',\n 'lineSingle',\n 'hook',\n 'polaroid',\n];\n\nexport interface FramePreset {\n readonly id: FramePresetId;\n /** UI label. The Ghost adapter overrides these from `frameOptions[i][1]`. */\n readonly label: string;\n /** Whether the preset's colour can be customised by the user. */\n readonly acceptsColor: boolean;\n /** Default colour when `acceptsColor`. Polaroid defaults to white. */\n readonly defaultColor: string;\n}\n\n/** Six presets in Ghost's `frameOptions` order. Labels are English defaults; adapter localises. */\nexport const FRAME_PRESETS: readonly FramePreset[] = [\n {\n id: 'none',\n label: 'None',\n acceptsColor: false,\n defaultColor: '#000000',\n },\n {\n id: 'solidSharp',\n label: 'Mat Sharp',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'solidRound',\n label: 'Mat Round',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'lineSingle',\n label: 'Line Single',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'hook',\n label: 'Corner Hooks',\n acceptsColor: true,\n defaultColor: '#000000',\n },\n {\n id: 'polaroid',\n label: 'Polaroid',\n acceptsColor: true,\n defaultColor: '#ffffff',\n },\n];\n\nexport interface FrameState {\n readonly presetId: FramePresetId;\n /** CSS hex colour. Used by every preset whose `acceptsColor` is true. */\n readonly color: string;\n}\n\nexport const DEFAULT_FRAME_STATE: FrameState = {\n presetId: 'none',\n color: '#000000',\n};\n\nexport function initialFrameState(): FrameState {\n return DEFAULT_FRAME_STATE;\n}\n\nexport function isFrameNoOp(state: FrameState): boolean {\n return state.presetId === 'none';\n}\n\nexport function setFramePreset(state: FrameState, presetId: FramePresetId): FrameState {\n if (state.presetId === presetId) return state;\n // Reset colour to the new preset's default iff the current colour\n // matches the previous preset's default (i.e. user hadn't customised it).\n const currentPreset = findFramePreset(state.presetId);\n const nextPreset = findFramePreset(presetId);\n if (!nextPreset) return { ...state, presetId };\n const wasOnDefault = currentPreset !== undefined && state.color === currentPreset.defaultColor;\n const nextColor = wasOnDefault ? nextPreset.defaultColor : state.color;\n return { presetId, color: nextColor };\n}\n\nexport function setFrameColor(state: FrameState, color: string): FrameState {\n if (state.color === color) return state;\n return { ...state, color };\n}\n\nexport function findFramePreset(id: FramePresetId): FramePreset | undefined {\n return FRAME_PRESETS.find((p) => p.id === id);\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { type FramePresetId, type FrameState, isFrameNoOp } from './state.js';\n\n/**\n * Apply the active frame preset. Frame thickness scales with `source`\n * so it stays consistent across resize choices. Output dimensions match\n * the input except for Polaroid, which extends the canvas.\n */\nexport async function bakeFrame(state: FrameState, source: SourceImage): Promise<SourceImage> {\n if (isFrameNoOp(state)) return source;\n\n if (state.presetId === 'polaroid') {\n return bakePolaroid(state.color, source);\n }\n if (state.presetId === 'none') return source;\n return bakeInsideFrame(state.presetId, state.color, source);\n}\n\nfunction bakeInsideFrame(\n presetId: Exclude<FramePresetId, 'none' | 'polaroid'>,\n color: string,\n source: SourceImage,\n): SourceImage {\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n paintInsideFrame(ctx, presetId, color, source.width, source.height);\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n\n/** Paint a non-extending frame; caller has already drawn the source image. */\nexport function paintInsideFrame(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n presetId: Exclude<FramePresetId, 'none' | 'polaroid'>,\n color: string,\n width: number,\n height: number,\n): void {\n switch (presetId) {\n case 'solidSharp':\n paintMatSharp(ctx, color, width, height);\n return;\n case 'solidRound':\n paintMatRound(ctx, color, width, height);\n return;\n case 'lineSingle':\n paintLineSingle(ctx, color, width, height);\n return;\n case 'hook':\n paintCornerHooks(ctx, color, width, height);\n return;\n }\n}\n\n/** Mat Sharp: 4%-of-shorter-edge solid border with sharp corners. */\nfunction paintMatSharp(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const t = matThickness(width, height);\n ctx.save();\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, width, t);\n ctx.fillRect(0, height - t, width, t);\n ctx.fillRect(0, t, t, height - 2 * t);\n ctx.fillRect(width - t, t, t, height - 2 * t);\n ctx.restore();\n}\n\n/** Mat Round: Mat Sharp with the four outer corners knocked out via destination-out. */\nfunction paintMatRound(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const t = matThickness(width, height);\n const r = t;\n paintMatSharp(ctx, color, width, height);\n\n ctx.save();\n ctx.globalCompositeOperation = 'destination-out';\n ctx.fillStyle = '#000';\n drawCornerCutout(ctx, 0, 0, r, 'tl');\n drawCornerCutout(ctx, width, 0, r, 'tr');\n drawCornerCutout(ctx, 0, height, r, 'bl');\n drawCornerCutout(ctx, width, height, r, 'br');\n ctx.restore();\n}\n\nfunction drawCornerCutout(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n cx: number,\n cy: number,\n r: number,\n corner: 'tl' | 'tr' | 'bl' | 'br',\n): void {\n ctx.beginPath();\n ctx.moveTo(cx, cy);\n switch (corner) {\n case 'tl':\n ctx.lineTo(cx + r, cy);\n ctx.arc(cx + r, cy + r, r, -Math.PI / 2, Math.PI, true);\n ctx.lineTo(cx, cy);\n break;\n case 'tr':\n ctx.lineTo(cx, cy + r);\n ctx.arc(cx - r, cy + r, r, 0, -Math.PI / 2, true);\n ctx.lineTo(cx, cy);\n break;\n case 'bl':\n ctx.lineTo(cx + r, cy);\n ctx.arc(cx + r, cy - r, r, Math.PI / 2, Math.PI, false);\n ctx.lineTo(cx, cy);\n break;\n case 'br':\n ctx.lineTo(cx - r, cy);\n ctx.arc(cx - r, cy - r, r, Math.PI / 2, 0, true);\n ctx.lineTo(cx, cy);\n break;\n }\n ctx.closePath();\n ctx.fill();\n}\n\n/** Line Single: thin stroked rect inset 5% from the edge. */\nfunction paintLineSingle(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const inset = Math.round(Math.min(width, height) * 0.05);\n const stroke = Math.max(2, Math.round(Math.min(width, height) * 0.01));\n ctx.save();\n ctx.strokeStyle = color;\n ctx.lineWidth = stroke;\n // strokeRect centres on the path; offset by half-stroke to fit inside the inset.\n const half = stroke / 2;\n ctx.strokeRect(\n inset + half,\n inset + half,\n width - 2 * inset - stroke,\n height - 2 * inset - stroke,\n );\n ctx.restore();\n}\n\n/** Corner Hooks: four L-shapes at the corners. */\nfunction paintCornerHooks(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n color: string,\n width: number,\n height: number,\n): void {\n const arm = Math.round(Math.min(width, height) * 0.08);\n const stroke = Math.max(2, Math.round(Math.min(width, height) * 0.01));\n const inset = Math.round(Math.min(width, height) * 0.05);\n\n ctx.save();\n ctx.strokeStyle = color;\n ctx.lineWidth = stroke;\n ctx.lineCap = 'square';\n\n const half = stroke / 2;\n const drawHook = (vx: number, vy: number, horizDir: -1 | 1, vertDir: -1 | 1): void => {\n ctx.beginPath();\n ctx.moveTo(vx + horizDir * arm, vy);\n ctx.lineTo(vx, vy);\n ctx.lineTo(vx, vy + vertDir * arm);\n ctx.stroke();\n };\n\n drawHook(inset + half, inset + half, 1, 1);\n drawHook(width - inset - half, inset + half, -1, 1);\n drawHook(inset + half, height - inset - half, 1, -1);\n drawHook(width - inset - half, height - inset - half, -1, -1);\n ctx.restore();\n}\n\n/** Polaroid: 5% top/left/right + 18% bottom border; output canvas is larger than input. */\nfunction bakePolaroid(color: string, source: SourceImage): SourceImage {\n const shorter = Math.min(source.width, source.height);\n const top = Math.round(shorter * 0.05);\n const left = top;\n const right = top;\n const bottom = Math.round(shorter * 0.18);\n\n const outW = source.width + left + right;\n const outH = source.height + top + bottom;\n\n const bake = createBakeCanvas(outW, outH);\n const ctx = getBakeContext2D(bake);\n\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, outW, outH);\n ctx.drawImage(source.bitmap, left, top, source.width, source.height);\n\n return {\n bitmap: bake.canvas,\n width: outW,\n height: outH,\n mimeType: source.mimeType,\n };\n}\n\n/** 4% of the shorter dimension, floored at 4px. */\nfunction matThickness(width: number, height: number): number {\n return Math.max(4, Math.round(Math.min(width, height) * 0.04));\n}\n\n/** Output dimensions for a preset; equals input except for Polaroid. */\nexport function frameOutputSize(\n presetId: FramePresetId,\n inputWidth: number,\n inputHeight: number,\n): { width: number; height: number } {\n if (presetId !== 'polaroid') {\n return { width: inputWidth, height: inputHeight };\n }\n const shorter = Math.min(inputWidth, inputHeight);\n const top = Math.round(shorter * 0.05);\n const bottom = Math.round(shorter * 0.18);\n return {\n width: inputWidth + 2 * top,\n height: inputHeight + top + bottom,\n };\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport type { RedactRegion } from './state.js';\n\nexport interface RedactBakeInput {\n readonly regions: ReadonlyArray<RedactRegion>;\n}\n\n/** Paint every redaction region onto a copy of `source` in creation order. */\nexport async function bakeRedact(\n state: RedactBakeInput,\n source: SourceImage,\n): Promise<SourceImage> {\n if (state.regions.length === 0) return source;\n\n const bake = createBakeCanvas(source.width, source.height);\n const ctx = getBakeContext2D(bake);\n\n ctx.drawImage(source.bitmap, 0, 0, source.width, source.height);\n\n // Pixelate/blur read from the post-source canvas, so overlapping\n // earlier regions are redacted again — \"redact wins\".\n for (const region of state.regions) {\n paintRegion(ctx, bake.canvas, region, source);\n }\n\n return {\n bitmap: bake.canvas,\n width: source.width,\n height: source.height,\n mimeType: source.mimeType,\n };\n}\n\n/**\n * Paint a single redaction region. `canvas` is needed because pixelate\n * and blur read from it and redraw a transformed copy in place.\n */\nexport function paintRegion(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n canvas: HTMLCanvasElement | OffscreenCanvas,\n region: RedactRegion,\n source: SourceImage,\n): void {\n // Degenerate rects would crash `getImageData` on some engines.\n const w = Math.round(region.width);\n const h = Math.round(region.height);\n if (w < 1 || h < 1) return;\n const x = Math.round(region.x);\n const y = Math.round(region.y);\n\n switch (region.mode) {\n case 'solid':\n paintSolid(ctx, region, x, y, w, h);\n return;\n case 'pixelate':\n paintPixelate(ctx, canvas, source, x, y, w, h);\n return;\n case 'blur':\n paintBlur(ctx, canvas, source, x, y, w, h);\n return;\n }\n}\n\nfunction paintSolid(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n region: RedactRegion,\n x: number,\n y: number,\n w: number,\n h: number,\n): void {\n ctx.save();\n ctx.fillStyle = region.color;\n ctx.fillRect(x, y, w, h);\n ctx.restore();\n}\n\n/**\n * Pixelate by downsampling to a small grid then upsampling with\n * nearest-neighbour. Capped at 8 cells on the longer side; floored at\n * 4 so tiny regions still read as chunky rather than as anti-aliasing.\n */\nfunction paintPixelate(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n canvas: HTMLCanvasElement | OffscreenCanvas,\n _source: SourceImage,\n x: number,\n y: number,\n w: number,\n h: number,\n): void {\n const longer = Math.max(w, h);\n const cells = Math.max(4, Math.round(8 * Math.min(1, longer / 240)));\n const gridW = Math.max(1, Math.round((w / longer) * cells));\n const gridH = Math.max(1, Math.round((h / longer) * cells));\n\n ctx.save();\n const small = createSmallCanvas(gridW, gridH);\n if (!small) {\n ctx.restore();\n return;\n }\n small.ctx.imageSmoothingEnabled = true;\n small.ctx.imageSmoothingQuality = 'low';\n small.ctx.drawImage(canvas, x, y, w, h, 0, 0, gridW, gridH);\n\n ctx.imageSmoothingEnabled = false;\n ctx.drawImage(small.canvas, 0, 0, gridW, gridH, x, y, w, h);\n ctx.restore();\n}\n\n/** Downscale-and-back blur. Two passes (1/8 then 1/2) approximate a Gaussian. */\nfunction paintBlur(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n canvas: HTMLCanvasElement | OffscreenCanvas,\n _source: SourceImage,\n x: number,\n y: number,\n w: number,\n h: number,\n): void {\n const downscale = 1 / 8;\n const smallW = Math.max(1, Math.round(w * downscale));\n const smallH = Math.max(1, Math.round(h * downscale));\n\n const small = createSmallCanvas(smallW, smallH);\n if (!small) return;\n small.ctx.imageSmoothingEnabled = true;\n small.ctx.imageSmoothingQuality = 'high';\n small.ctx.drawImage(canvas, x, y, w, h, 0, 0, smallW, smallH);\n\n const tinyW = Math.max(1, Math.round(smallW * 0.5));\n const tinyH = Math.max(1, Math.round(smallH * 0.5));\n const tiny = createSmallCanvas(tinyW, tinyH);\n if (!tiny) {\n ctx.save();\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(small.canvas, 0, 0, smallW, smallH, x, y, w, h);\n ctx.restore();\n return;\n }\n tiny.ctx.imageSmoothingEnabled = true;\n tiny.ctx.imageSmoothingQuality = 'high';\n tiny.ctx.drawImage(small.canvas, 0, 0, smallW, smallH, 0, 0, tinyW, tinyH);\n\n ctx.save();\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(tiny.canvas, 0, 0, tinyW, tinyH, x, y, w, h);\n ctx.restore();\n}\n\ninterface SmallCanvas {\n readonly canvas: HTMLCanvasElement | OffscreenCanvas;\n readonly ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;\n}\n\n/** Small intermediate canvas; prefers OffscreenCanvas, falls back to detached `<canvas>`. */\nfunction createSmallCanvas(width: number, height: number): SmallCanvas | null {\n if (typeof OffscreenCanvas !== 'undefined') {\n try {\n const offscreen = new OffscreenCanvas(width, height);\n const ctx = offscreen.getContext('2d');\n if (ctx) return { canvas: offscreen, ctx };\n } catch {\n // Some engines throw on zero-size construction.\n }\n }\n if (typeof document === 'undefined') return null;\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n return { canvas, ctx };\n}\n","/**\n * Redact state and mutators. Mirrors the annotate plugin's vocabulary\n * (id + kind, monotonic mint, replace/delete) so the selection layer\n * can be reused.\n */\n\nimport type { Rect } from '../../geometry/rect.js';\n\n/** `pixelate`, `blur`, or `solid` (flat fill). */\nexport type RedactMode = 'pixelate' | 'blur' | 'solid';\n\nexport const REDACT_MODES: readonly RedactMode[] = ['pixelate', 'blur', 'solid'];\n\nexport interface RedactRegion {\n /** Stable per-session id; survives undo/redo. */\n readonly id: string;\n /** Image-space rectangle. May be temporarily negative-extent mid-drag. */\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n readonly mode: RedactMode;\n /** Used only when `mode === 'solid'`. CSS hex string. */\n readonly color: string;\n}\n\nexport interface RedactState {\n readonly regions: ReadonlyArray<RedactRegion>;\n /** Monotonic id source for new regions. Never decreases. */\n readonly nextRegionNumber: number;\n /** The currently-selected region id, or `null` when none. */\n readonly selectedId: string | null;\n /** The mode new regions are created with. Persists across selections. */\n readonly currentMode: RedactMode;\n /** Default fill colour for the `solid` mode. */\n readonly currentColor: string;\n /** Image-space dimensions of the upstream-baked source the plugin was mounted on. */\n readonly imageSize: { readonly width: number; readonly height: number };\n}\n\nexport const DEFAULT_REDACT_COLOR = '#000000';\nexport const DEFAULT_REDACT_MODE: RedactMode = 'pixelate';\n\nexport interface InitialRedactStateInput {\n readonly imageSize: { readonly width: number; readonly height: number };\n}\n\nexport function initialRedactState(input: InitialRedactStateInput): RedactState {\n return {\n regions: [],\n nextRegionNumber: 1,\n selectedId: null,\n currentMode: DEFAULT_REDACT_MODE,\n currentColor: DEFAULT_REDACT_COLOR,\n imageSize: input.imageSize,\n };\n}\n\n/** Allocate a new region id; caller threads `nextRegionNumber` back into state. */\nexport function mintRegionId(state: RedactState): {\n id: string;\n nextRegionNumber: number;\n} {\n return {\n id: `r_${state.nextRegionNumber.toString(36)}`,\n nextRegionNumber: state.nextRegionNumber + 1,\n };\n}\n\nexport function addRegion(state: RedactState, region: RedactRegion): RedactState {\n return {\n ...state,\n regions: [...state.regions, region],\n selectedId: region.id,\n };\n}\n\nexport function replaceRegion(state: RedactState, region: RedactRegion): RedactState {\n let changed = false;\n const next = state.regions.map((existing) => {\n if (existing.id !== region.id) return existing;\n changed = true;\n return region;\n });\n if (!changed) return state;\n return { ...state, regions: next };\n}\n\nexport function deleteRegion(state: RedactState, id: string): RedactState {\n const next = state.regions.filter((region) => region.id !== id);\n if (next.length === state.regions.length) return state;\n return {\n ...state,\n regions: next,\n selectedId: state.selectedId === id ? null : state.selectedId,\n };\n}\n\n/** Mirror every region across an axis of `dims`. */\nexport function mirrorRegions(\n state: RedactState,\n axis: 'horizontal' | 'vertical',\n dims: { readonly width: number; readonly height: number },\n): RedactState {\n if (state.regions.length === 0) return state;\n const next = state.regions.map((region) => {\n if (axis === 'horizontal') {\n return { ...region, x: dims.width - region.x - region.width };\n }\n return { ...region, y: dims.height - region.y - region.height };\n });\n return { ...state, regions: next, imageSize: dims };\n}\n\n/** Translate every region by `(dx, dy)`. Out-of-bounds regions are kept (bake clips on Save). */\nexport function translateRegions(\n state: RedactState,\n dx: number,\n dy: number,\n dims: { readonly width: number; readonly height: number },\n): RedactState {\n if (state.regions.length === 0) return { ...state, imageSize: dims };\n if (dx === 0 && dy === 0 && state.imageSize === dims) return state;\n const next = state.regions.map((region) => ({\n ...region,\n x: region.x + dx,\n y: region.y + dy,\n }));\n return { ...state, regions: next, imageSize: dims };\n}\n\n/**\n * Rotate every region `turns × 90°` CW around the image centre. Caller\n * passes post-rotation dims as `newDims`; pre-rotation dims come from\n * `state.imageSize`.\n */\nexport function rotateRegions(\n state: RedactState,\n turns: 0 | 1 | 2 | 3,\n newDims: { readonly width: number; readonly height: number },\n): RedactState {\n if (turns === 0) return { ...state, imageSize: newDims };\n if (state.regions.length === 0) return { ...state, imageSize: newDims };\n const oldW = state.imageSize.width;\n const oldH = state.imageSize.height;\n const next = state.regions.map((region) => {\n const { x, y, width, height } = region;\n if (turns === 1) {\n return { ...region, x: oldH - y - height, y: x, width: height, height: width };\n }\n if (turns === 2) {\n return {\n ...region,\n x: oldW - x - width,\n y: oldH - y - height,\n };\n }\n return { ...region, x: y, y: oldW - x - width, width: height, height: width };\n });\n return { ...state, regions: next, imageSize: newDims };\n}\n\nexport function selectRegion(state: RedactState, id: string | null): RedactState {\n if (state.selectedId === id) return state;\n return { ...state, selectedId: id };\n}\n\nexport function setCurrentMode(state: RedactState, mode: RedactMode): RedactState {\n if (state.currentMode === mode) return state;\n return { ...state, currentMode: mode };\n}\n\nexport function setCurrentColor(state: RedactState, color: string): RedactState {\n if (state.currentColor === color) return state;\n return { ...state, currentColor: color };\n}\n\n/** Update the mode of a region; `color` is preserved across mode flips. */\nexport function setRegionMode(state: RedactState, id: string, mode: RedactMode): RedactState {\n const region = state.regions.find((r) => r.id === id);\n if (!region) return state;\n if (region.mode === mode) return state;\n return replaceRegion(state, { ...region, mode });\n}\n\nexport function setRegionColor(state: RedactState, id: string, color: string): RedactState {\n const region = state.regions.find((r) => r.id === id);\n if (!region) return state;\n if (region.color === color) return state;\n return replaceRegion(state, { ...region, color });\n}\n\nexport function findRegion(state: RedactState, id: string | null): RedactRegion | undefined {\n if (id === null) return undefined;\n return state.regions.find((r) => r.id === id);\n}\n\nexport function selectedRegionOf(state: RedactState): RedactRegion | null {\n if (state.selectedId === null) return null;\n return state.regions.find((r) => r.id === state.selectedId) ?? null;\n}\n\n/** Normalise a region's rect so `width` / `height` are non-negative. */\nexport function normaliseRegionExtent(extent: {\n x: number;\n y: number;\n width: number;\n height: number;\n}): { x: number; y: number; width: number; height: number } {\n let { x, y, width, height } = extent;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n return { x, y, width, height };\n}\n\n/** Default-sized region centred on the image. Used by the keyboard \"Insert\" path. */\nexport interface CreateCenteredRegionContext {\n readonly imageSize: { readonly width: number; readonly height: number };\n readonly mode: RedactMode;\n readonly color: string;\n readonly id: string;\n}\n\nexport function createCenteredRegion(ctx: CreateCenteredRegionContext): RedactRegion {\n const { imageSize, mode, color, id } = ctx;\n const shortEdge = Math.min(imageSize.width, imageSize.height);\n const size = Math.max(80, Math.round(shortEdge * 0.25));\n const cx = imageSize.width / 2;\n const cy = imageSize.height / 2;\n const x = Math.round(cx - size / 2);\n const y = Math.round(cy - size / 2);\n return {\n id,\n x,\n y,\n width: size,\n height: size,\n mode,\n color,\n };\n}\n\n/** Clamp regions against new bounds; drop regions fully outside. \"Clamp, don't reset\". */\nexport function revalidateAgainstBounds(\n state: RedactState,\n bounds: { width: number; height: number },\n): RedactState {\n if (state.imageSize.width === bounds.width && state.imageSize.height === bounds.height) {\n return state;\n }\n const kept: RedactRegion[] = [];\n for (const region of state.regions) {\n if (\n region.x + region.width <= 0 ||\n region.y + region.height <= 0 ||\n region.x >= bounds.width ||\n region.y >= bounds.height\n ) {\n continue;\n }\n kept.push(clampRegion(region, bounds));\n }\n const selectedDropped = state.selectedId !== null && !kept.some((r) => r.id === state.selectedId);\n return {\n ...state,\n regions: kept,\n imageSize: { width: bounds.width, height: bounds.height },\n selectedId: selectedDropped ? null : state.selectedId,\n };\n}\n\nfunction clampRegion(\n region: RedactRegion,\n bounds: { width: number; height: number },\n): RedactRegion {\n const x = Math.max(0, region.x);\n const y = Math.max(0, region.y);\n const right = Math.min(bounds.width, region.x + region.width);\n const bottom = Math.min(bounds.height, region.y + region.height);\n return {\n ...region,\n x,\n y,\n width: Math.max(0, right - x),\n height: Math.max(0, bottom - y),\n };\n}\n\n/** Bounding-box shape used by the selection layer; matches `Rect`. */\nexport function regionBoundingBox(region: RedactRegion): Rect {\n return { x: region.x, y: region.y, width: region.width, height: region.height };\n}\n","/**\n * Per-axis scale factors. `lockAspect` makes the editor keep `scaleX === scaleY`.\n * Output pixels are computed at bake time as `round(upstream * scale)`,\n * clamped to `[MIN_DIMENSION, MAX_DIMENSION]`.\n */\nexport interface ResizeState {\n readonly scaleX: number;\n readonly scaleY: number;\n readonly lockAspect: boolean;\n}\n\nexport const MAX_DIMENSION = 8000;\nexport const MIN_DIMENSION = 1;\n\nexport function initialResizeState(): ResizeState {\n return { scaleX: 1, scaleY: 1, lockAspect: true };\n}\n\nexport function isResizeNoOp(state: ResizeState): boolean {\n return Math.abs(state.scaleX - 1) < 1e-9 && Math.abs(state.scaleY - 1) < 1e-9;\n}\n\n/** Integer output dimensions for an upstream image, clamped per axis. */\nexport function resolveOutputSize(\n state: ResizeState,\n upstream: { readonly width: number; readonly height: number },\n): { width: number; height: number } {\n const width = clampInt(Math.round(upstream.width * state.scaleX));\n const height = clampInt(Math.round(upstream.height * state.scaleY));\n return { width, height };\n}\n\n/** Set width via a pixel value; with `lockAspect` the vertical scale follows. */\nexport function setWidthPx(\n state: ResizeState,\n widthPx: number,\n upstream: { readonly width: number; readonly height: number },\n): ResizeState {\n if (upstream.width <= 0) return state;\n const target = clampInt(Math.round(widthPx));\n const scaleX = target / upstream.width;\n const scaleY = state.lockAspect ? scaleX : state.scaleY;\n return { ...state, scaleX, scaleY };\n}\n\nexport function setHeightPx(\n state: ResizeState,\n heightPx: number,\n upstream: { readonly width: number; readonly height: number },\n): ResizeState {\n if (upstream.height <= 0) return state;\n const target = clampInt(Math.round(heightPx));\n const scaleY = target / upstream.height;\n const scaleX = state.lockAspect ? scaleY : state.scaleX;\n return { ...state, scaleX, scaleY };\n}\n\n/** Uniform percentage change; always touches both axes regardless of `lockAspect`. */\nexport function setPercent(state: ResizeState, percent: number): ResizeState {\n const scale = clampScaleForPercent(percent / 100);\n return { ...state, scaleX: scale, scaleY: scale };\n}\n\nexport function setLockAspect(state: ResizeState, locked: boolean): ResizeState {\n if (state.lockAspect === locked) return state;\n if (!locked) return { ...state, lockAspect: false };\n // Average the two scales so neither axis arbitrarily wins on lock.\n const merged = (state.scaleX + state.scaleY) / 2;\n return { scaleX: merged, scaleY: merged, lockAspect: true };\n}\n\n/** Percent display value; when axes differ, returns the larger scale. */\nexport function effectivePercent(state: ResizeState): number {\n const max = Math.max(state.scaleX, state.scaleY);\n return Math.round(max * 1000) / 10;\n}\n\nfunction clampInt(n: number): number {\n if (!Number.isFinite(n)) return MIN_DIMENSION;\n return Math.max(MIN_DIMENSION, Math.min(MAX_DIMENSION, Math.trunc(n)));\n}\n\nfunction clampScaleForPercent(scale: number): number {\n if (!Number.isFinite(scale) || scale <= 0) return 0.01;\n return Math.max(0.01, Math.min(scale, MAX_DIMENSION));\n}\n","import { type BakeCanvas, createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { isResizeNoOp, type ResizeState, resolveOutputSize } from './state.js';\n\n/**\n * Resize `source` to the dimensions implied by `state`. Downscales > 2×\n * run a halving pyramid first so the bilinear-ish `drawImage` scaler\n * doesn't alias.\n */\nexport async function bakeResize(state: ResizeState, source: SourceImage): Promise<SourceImage> {\n if (isResizeNoOp(state)) return source;\n const { width: targetW, height: targetH } = resolveOutputSize(state, source);\n if (targetW <= 0 || targetH <= 0) return source;\n\n const halvings = countHalvingsNeeded(source.width, source.height, targetW, targetH);\n\n let current: { bitmap: CanvasImageSource; width: number; height: number } = {\n bitmap: source.bitmap,\n width: source.width,\n height: source.height,\n };\n\n // Each halving writes into its own canvas so the source is never reused as destination.\n const intermediates: BakeCanvas[] = [];\n for (let i = 0; i < halvings; i++) {\n const stepW = Math.max(targetW, Math.floor(current.width / 2));\n const stepH = Math.max(targetH, Math.floor(current.height / 2));\n const step = drawScaled(current, stepW, stepH);\n intermediates.push(step);\n current = { bitmap: step.canvas, width: stepW, height: stepH };\n }\n\n const final = drawScaled(current, targetW, targetH);\n\n intermediates.length = 0;\n\n return {\n bitmap: final.canvas,\n width: targetW,\n height: targetH,\n mimeType: source.mimeType,\n };\n}\n\nfunction countHalvingsNeeded(srcW: number, srcH: number, targetW: number, targetH: number): number {\n let w = srcW;\n let h = srcH;\n let count = 0;\n // Defensive cap so a malformed input can't spin forever.\n while ((w / 2 > targetW || h / 2 > targetH) && count < 16) {\n w = Math.floor(w / 2);\n h = Math.floor(h / 2);\n count += 1;\n }\n return count;\n}\n\nfunction drawScaled(\n current: { bitmap: CanvasImageSource; width: number; height: number },\n outW: number,\n outH: number,\n): BakeCanvas {\n const bake = createBakeCanvas(outW, outH);\n const ctx = getBakeContext2D(bake);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(current.bitmap, 0, 0, current.width, current.height, 0, 0, outW, outH);\n return bake;\n}\n","import type { Size } from '../../geometry/rect.js';\n\n/**\n * Largest axis-aligned rect of the source's aspect ratio that fits\n * inside the source rotated by `angleRad`. Used by free-angle rotation\n * to avoid transparent corners.\n *\n * The two binding constraints reduce to\n * `2x ≤ W² / (W|cosθ| + H|sinθ|)`\n * `2x ≤ W·H / (W|sinθ| + H|cosθ|)`\n * — the inscribed half-width is the smaller bound. Works on absolute\n * sin/cos so the result is symmetric in the sign of the angle.\n */\nexport function largestInscribedRect(source: Size, angleRad: number): Size {\n const width = source.width;\n const height = source.height;\n if (width <= 0 || height <= 0) return { width: 0, height: 0 };\n\n const c = Math.abs(Math.cos(angleRad));\n const s = Math.abs(Math.sin(angleRad));\n\n const denomA = width * c + height * s;\n const denomD = width * s + height * c;\n const capA = denomA > EPSILON ? (width * width) / denomA : Number.POSITIVE_INFINITY;\n const capD = denomD > EPSILON ? (width * height) / denomD : Number.POSITIVE_INFINITY;\n\n const outWidth = Math.min(capA, capD);\n const outHeight = (outWidth * height) / width;\n return { width: outWidth, height: outHeight };\n}\n\nconst EPSILON = 1e-9;\n","/**\n * Independent quarter-turns (lossless 90° CW) and free angle (±45°).\n * Bake applies `quarterTurns * 90° + freeAngle` so a press of \"rotate 90°\"\n * doesn't reset a straighten correction and vice versa.\n */\nexport interface RotateState {\n readonly quarterTurns: 0 | 1 | 2 | 3;\n /** Free-angle offset in degrees. Range: [-45, 45]. */\n readonly freeAngle: number;\n}\n\nexport const FREE_ANGLE_MIN = -45;\nexport const FREE_ANGLE_MAX = 45;\nexport const FREE_ANGLE_STEP = 0.1;\n\nexport function initialRotateState(): RotateState {\n return { quarterTurns: 0, freeAngle: 0 };\n}\n\nexport function rotateClockwise(state: RotateState): RotateState {\n return { ...state, quarterTurns: ((state.quarterTurns + 1) % 4) as 0 | 1 | 2 | 3 };\n}\n\nexport function rotateCounterClockwise(state: RotateState): RotateState {\n return { ...state, quarterTurns: ((state.quarterTurns + 3) % 4) as 0 | 1 | 2 | 3 };\n}\n\nexport function setFreeAngle(state: RotateState, angleDeg: number): RotateState {\n const clamped = clamp(angleDeg, FREE_ANGLE_MIN, FREE_ANGLE_MAX);\n // Snap to 0.1° to match the slider step and avoid sub-step float noise.\n const snapped = Math.round(clamped * 10) / 10;\n return { ...state, freeAngle: snapped };\n}\n\nexport function isRotateNoOp(state: RotateState): boolean {\n return state.quarterTurns === 0 && Math.abs(state.freeAngle) < 1e-6;\n}\n\n/** Effective rotation applied during bake, in degrees clockwise. */\nexport function effectiveAngleDeg(state: RotateState): number {\n return state.quarterTurns * 90 + state.freeAngle;\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, n));\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { largestInscribedRect } from './inscribe.js';\nimport { effectiveAngleDeg, isRotateNoOp, type RotateState } from './state.js';\n\n/**\n * Apply rotation = quarter-turns + free-angle in one `drawImage`. Free\n * angles auto-crop to the largest same-aspect rect inside the rotated\n * bounding box so transparent corners stay out of the output.\n */\nexport async function bakeRotate(state: RotateState, source: SourceImage): Promise<SourceImage> {\n if (isRotateNoOp(state)) return source;\n\n const angleDeg = effectiveAngleDeg(state);\n const angleRad = (angleDeg * Math.PI) / 180;\n\n const sub90Deg = angleDeg - state.quarterTurns * 90; // ∈ [-45, 45]\n const isQuarterOnly = Math.abs(sub90Deg) < 1e-6;\n\n let outWidth: number;\n let outHeight: number;\n\n if (isQuarterOnly) {\n if (state.quarterTurns === 1 || state.quarterTurns === 3) {\n outWidth = source.height;\n outHeight = source.width;\n } else {\n outWidth = source.width;\n outHeight = source.height;\n }\n } else {\n const inscribed = largestInscribedRect(source, angleRad);\n outWidth = Math.max(1, Math.round(inscribed.width));\n outHeight = Math.max(1, Math.round(inscribed.height));\n }\n\n const bake = createBakeCanvas(outWidth, outHeight);\n const ctx = getBakeContext2D(bake);\n\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n\n ctx.translate(outWidth / 2, outHeight / 2);\n ctx.rotate(angleRad);\n ctx.drawImage(source.bitmap, -source.width / 2, -source.height / 2);\n\n return {\n bitmap: bake.canvas,\n width: outWidth,\n height: outHeight,\n mimeType: source.mimeType,\n };\n}\n","/**\n * Output popover anchored to the Save-button caret: format, quality, strip-metadata.\n * State commits immediately to the Store; no apply button. Unsupported formats\n * (per async runtime probe) are disabled in-place when the probe resolves.\n */\n\nimport {\n canEncodeMime,\n type OutputMimeChoice,\n type OutputState,\n type Store,\n setOutputMime,\n setOutputQuality,\n setStripMetadata,\n} from '@magicpages/kalotyp-core';\nimport { openNestedModal } from '../dom/nested-modal.js';\n\nexport interface OpenOutputPopoverOptions {\n readonly host: HTMLElement;\n readonly anchor: HTMLElement;\n readonly store: Store<OutputState>;\n /** Click of \"Save and close\" inside the popover. */\n readonly onSaveAndClose: () => void;\n /** Mirrors the editor's Save-enabled state so the in-popover Save button stays in sync. */\n readonly canSave: () => boolean;\n readonly onClose: () => void;\n}\n\nexport interface OutputPopoverHandle {\n close(): void;\n}\n\ninterface FormatChoice {\n readonly value: OutputMimeChoice;\n readonly label: string;\n readonly description: string;\n /** Mime types the runtime must be able to encode for this option to be usable. */\n readonly requires: ReadonlyArray<string>;\n}\n\nconst FORMAT_CHOICES: ReadonlyArray<FormatChoice> = [\n {\n value: 'auto',\n label: 'Auto',\n description: 'WebP when supported, PNG fallback.',\n requires: [],\n },\n {\n value: 'image/webp',\n label: 'WebP',\n description: 'Best compression for most images.',\n requires: ['image/webp'],\n },\n {\n value: 'image/avif',\n label: 'AVIF',\n description: 'Smallest size; slower encode.',\n requires: ['image/avif'],\n },\n {\n value: 'image/jpeg',\n label: 'JPEG',\n description: 'Universal; no transparency.',\n requires: ['image/jpeg'],\n },\n {\n value: 'image/png',\n label: 'PNG',\n description: 'Lossless; preserves transparency.',\n requires: ['image/png'],\n },\n];\n\nexport function openOutputPopover(options: OpenOutputPopoverOptions): OutputPopoverHandle {\n const { host, anchor, store } = options;\n\n const body = document.createElement('div');\n body.className = 'kalotyp-output-popover-body';\n\n const formatLabel = document.createElement('label');\n formatLabel.className = 'kalotyp-output-row';\n const formatLabelText = document.createElement('span');\n formatLabelText.className = 'kalotyp-output-row-label';\n formatLabelText.textContent = 'Format';\n formatLabel.appendChild(formatLabelText);\n\n const formatSelect = document.createElement('select');\n formatSelect.className = 'kalotyp-output-format';\n formatSelect.setAttribute('aria-label', 'Output format');\n for (const choice of FORMAT_CHOICES) {\n const option = document.createElement('option');\n option.value = choice.value;\n option.textContent = choice.label;\n formatSelect.appendChild(option);\n }\n formatSelect.value = store.get().mimeChoice;\n formatSelect.addEventListener('change', () => {\n const value = formatSelect.value as OutputMimeChoice;\n store.update((current) => setOutputMime(current, value));\n });\n formatLabel.appendChild(formatSelect);\n\n const formatHint = document.createElement('p');\n formatHint.className = 'kalotyp-output-hint';\n formatHint.setAttribute('aria-live', 'polite');\n\n const qualityLabel = document.createElement('label');\n qualityLabel.className = 'kalotyp-output-row';\n const qualityLabelText = document.createElement('span');\n qualityLabelText.className = 'kalotyp-output-row-label';\n qualityLabelText.textContent = 'Quality';\n qualityLabel.appendChild(qualityLabelText);\n\n const qualitySlider = document.createElement('input');\n qualitySlider.type = 'range';\n qualitySlider.className = 'kalotyp-output-quality';\n qualitySlider.min = '50';\n qualitySlider.max = '100';\n qualitySlider.step = '1';\n qualitySlider.value = String(Math.round(store.get().quality * 100));\n qualitySlider.setAttribute('aria-label', 'Output quality');\n qualitySlider.addEventListener('input', () => {\n store.update((current) => setOutputQuality(current, qualitySlider.valueAsNumber / 100));\n });\n qualityLabel.appendChild(qualitySlider);\n\n const qualityReadout = document.createElement('span');\n qualityReadout.className = 'kalotyp-output-quality-readout';\n qualityReadout.setAttribute('aria-hidden', 'true');\n qualityReadout.textContent = `${Math.round(store.get().quality * 100)}`;\n qualityLabel.appendChild(qualityReadout);\n\n const summary = document.createElement('p');\n summary.className = 'kalotyp-output-summary';\n summary.setAttribute('aria-live', 'polite');\n\n // Toggle stores intent; the encoder honours it only when source and output are both JPEG\n // (canvas re-encoding strips metadata otherwise). The hint below surfaces that constraint.\n const metadataRow = document.createElement('label');\n metadataRow.className = 'kalotyp-output-metadata-row';\n const metadataCheckbox = document.createElement('input');\n metadataCheckbox.type = 'checkbox';\n metadataCheckbox.className = 'kalotyp-output-metadata-checkbox';\n metadataCheckbox.checked = store.get().stripMetadata;\n metadataCheckbox.addEventListener('change', () => {\n store.update((current) => setStripMetadata(current, metadataCheckbox.checked));\n });\n const metadataText = document.createElement('span');\n metadataText.className = 'kalotyp-output-metadata-text';\n metadataText.textContent = 'Strip EXIF, GPS, and camera metadata on save';\n metadataRow.appendChild(metadataCheckbox);\n metadataRow.appendChild(metadataText);\n\n const metadataHint = document.createElement('p');\n metadataHint.className = 'kalotyp-output-metadata-hint';\n metadataHint.setAttribute('aria-live', 'polite');\n\n const footer = document.createElement('footer');\n footer.className = 'kalotyp-output-footer';\n\n const doneButton = document.createElement('button');\n doneButton.type = 'button';\n doneButton.className = 'kalotyp-output-done';\n doneButton.textContent = 'Done';\n doneButton.setAttribute('aria-label', 'Close output settings');\n doneButton.addEventListener('click', () => handle.close());\n\n const saveButton = document.createElement('button');\n saveButton.type = 'button';\n saveButton.className = 'kalotyp-output-save';\n saveButton.textContent = 'Save and close';\n saveButton.addEventListener('click', () => {\n if (!options.canSave()) return;\n options.onSaveAndClose();\n });\n\n footer.appendChild(doneButton);\n footer.appendChild(saveButton);\n\n body.appendChild(formatLabel);\n body.appendChild(formatHint);\n body.appendChild(qualityLabel);\n body.appendChild(summary);\n body.appendChild(metadataRow);\n body.appendChild(metadataHint);\n body.appendChild(footer);\n\n anchor.setAttribute('aria-expanded', 'true');\n\n const handle = openNestedModal({\n host,\n anchor,\n title: 'Output settings',\n body,\n variant: 'kalotyp-output-popover',\n showCloseButton: true,\n onClose: () => {\n anchor.setAttribute('aria-expanded', 'false');\n unsubscribe();\n options.onClose();\n },\n });\n\n function renderState(state: OutputState): void {\n if (formatSelect.value !== state.mimeChoice) formatSelect.value = state.mimeChoice;\n const percent = Math.round(state.quality * 100);\n if (qualitySlider.valueAsNumber !== percent) qualitySlider.value = String(percent);\n qualityReadout.textContent = String(percent);\n const choice = FORMAT_CHOICES.find((c) => c.value === state.mimeChoice);\n formatHint.textContent = choice?.description ?? '';\n summary.textContent = describeSelection(state);\n // PNG ignores quality — visually disable the slider for that format.\n const qualityActive = state.mimeChoice !== 'image/png';\n qualitySlider.disabled = !qualityActive;\n qualityReadout.style.opacity = qualityActive ? '1' : '0.4';\n if (metadataCheckbox.checked !== state.stripMetadata) {\n metadataCheckbox.checked = state.stripMetadata;\n }\n metadataHint.textContent = describeMetadataHint(state);\n saveButton.disabled = !options.canSave();\n }\n\n renderState(store.get());\n const unsubscribe = store.subscribe(renderState);\n\n // 'auto' is always enabled because its resolver falls back to PNG internally.\n void (async () => {\n for (const choice of FORMAT_CHOICES) {\n if (choice.requires.length === 0) continue;\n const supported = (\n await Promise.all(choice.requires.map((mime) => canEncodeMime(mime)))\n ).every(Boolean);\n if (!supported) {\n const option = formatSelect.querySelector<HTMLOptionElement>(\n `option[value=\"${choice.value}\"]`,\n );\n if (option) {\n option.disabled = true;\n option.textContent = `${choice.label} (unsupported)`;\n }\n }\n }\n })();\n\n return {\n close: () => handle.close(),\n };\n}\n\nfunction describeSelection(state: OutputState): string {\n if (state.mimeChoice === 'image/png') return 'PNG · lossless';\n const percent = Math.round(state.quality * 100);\n switch (state.mimeChoice) {\n case 'auto':\n return `Auto · ${percent}% quality`;\n case 'image/webp':\n return `WebP · ${percent}% quality`;\n case 'image/avif':\n return `AVIF · ${percent}% quality`;\n case 'image/jpeg':\n return `JPEG · ${percent}% quality`;\n default:\n return `${state.mimeChoice} · ${percent}% quality`;\n }\n}\n\n/** When strip is off, surface the JPEG→JPEG constraint so EXIF loss doesn't surprise the user. */\nfunction describeMetadataHint(state: OutputState): string {\n if (state.stripMetadata) return '';\n if (state.mimeChoice === 'image/jpeg') {\n return 'EXIF preserved when the source is also JPEG.';\n }\n if (state.mimeChoice === 'auto') {\n return 'Metadata is preserved only when the resolved output is JPEG.';\n }\n return 'Metadata can only be preserved when the output is JPEG.';\n}\n","/**\n * Per-shape coordinate-input rows for the keyboard placement path\n *. The inputs are the keyboard-only equivalent\n * of dragging selection handles: a user types image-space pixel\n * coordinates, the shape's geometry updates on `change` (blur or\n * Enter), and the live canvas + selection handles re-paint via the\n * existing store subscription path.\n *\n * Each shape kind exposes a different shape of fields:\n *\n * - rect / ellipse → Left, Top, Width, Height (matches crop's\n * dimension-input pattern from Phase 6.2; same semantics).\n * - arrow → Start X, Start Y, End X, End Y (the two endpoints).\n * - text → X, Y (anchor only; size is driven by the font size\n * control, and the inline editor handles the text content).\n *\n * One row instance is reused across selections; it rebuilds its\n * fields when the selected shape's kind changes. The mount layer\n * subscribes to the selection and calls `updateForShape(shape)` on\n * each change so the inputs reflect the live geometry.\n */\n\nimport type {\n ArrowShape,\n EllipseShape,\n RectShape,\n Shape,\n TextShape,\n} from '@magicpages/kalotyp-core';\n\nexport interface CoordInputsOptions {\n /**\n * Called when a typed value commits (blur or Enter). The handler\n * receives the new shape; the mount layer writes it to the store\n * via `replaceShape` and emits a commit. Keeping the helper\n * store-free means the row's tests don't need to thread a store.\n */\n onShapeChanged(shape: Shape): void;\n}\n\n/**\n * A single coordinate edit. Discriminated on shape kind so the apply\n * step can narrow without re-reading the input ids.\n */\nexport type ShapeCoordEdit =\n | {\n readonly kind: 'rect' | 'ellipse';\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n }\n | {\n readonly kind: 'arrow';\n readonly x1: number;\n readonly y1: number;\n readonly x2: number;\n readonly y2: number;\n }\n | { readonly kind: 'text'; readonly x: number; readonly y: number };\n\nexport interface CoordInputsHandle {\n readonly container: HTMLDivElement;\n /** Show inputs for the given shape and prefill values. Hides when shape is null. */\n updateForShape(shape: Shape | null): void;\n destroy(): void;\n}\n\ninterface FieldSpec {\n readonly id: string;\n readonly label: string;\n readonly min?: number;\n readonly max?: number;\n}\n\nconst RECT_FIELDS: ReadonlyArray<FieldSpec> = [\n { id: 'x', label: 'Left' },\n { id: 'y', label: 'Top' },\n { id: 'width', label: 'Width', min: 1 },\n { id: 'height', label: 'Height', min: 1 },\n];\n\nconst ARROW_FIELDS: ReadonlyArray<FieldSpec> = [\n { id: 'x1', label: 'Start X' },\n { id: 'y1', label: 'Start Y' },\n { id: 'x2', label: 'End X' },\n { id: 'y2', label: 'End Y' },\n];\n\nconst TEXT_FIELDS: ReadonlyArray<FieldSpec> = [\n { id: 'x', label: 'X' },\n { id: 'y', label: 'Y' },\n];\n\n/**\n * Build a single per-selection coordinate-input row. The row reuses\n * the same `kalotyp-annotate-coords-*` class set across shape kinds\n * so styling is one rule set; only the field set changes per shape.\n */\nexport function buildCoordInputs(options: CoordInputsOptions): CoordInputsHandle {\n const container = document.createElement('div');\n container.className = 'kalotyp-annotate-coords';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Selected annotation position');\n container.hidden = true;\n\n let activeShape: Shape | null = null;\n let activeKind: 'rect' | 'ellipse' | 'arrow' | 'text' | null = null;\n const inputs = new Map<string, HTMLInputElement>();\n\n function rebuildFor(kind: 'rect' | 'ellipse' | 'arrow' | 'text'): void {\n container.replaceChildren();\n inputs.clear();\n const fields = fieldsFor(kind);\n for (const spec of fields) {\n const wrapper = document.createElement('label');\n wrapper.className = 'kalotyp-annotate-coords-field';\n\n const labelSpan = document.createElement('span');\n labelSpan.className = 'kalotyp-annotate-coords-label';\n labelSpan.textContent = spec.label;\n\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'kalotyp-annotate-coords-input';\n input.dataset.field = spec.id;\n input.step = '1';\n input.inputMode = 'numeric';\n if (spec.min !== undefined) input.min = String(spec.min);\n if (spec.max !== undefined) input.max = String(spec.max);\n input.setAttribute('aria-label', `${spec.label} (pixels)`);\n input.addEventListener('change', onAnyInputChange);\n\n wrapper.appendChild(labelSpan);\n wrapper.appendChild(input);\n container.appendChild(wrapper);\n inputs.set(spec.id, input);\n }\n activeKind = kind;\n }\n\n function syncValuesFromShape(shape: Shape): void {\n const setVal = (id: string, value: number): void => {\n const el = inputs.get(id);\n if (!el) return;\n const next = String(Math.round(value));\n // Skip the assignment if the user is mid-edit on this very\n // input — overwriting a focused, partially-typed value is the\n // most surprising thing an a11y helper can do. The store-driven\n // sync still catches up after blur via the next `update`.\n if (document.activeElement === el) return;\n if (el.value !== next) el.value = next;\n };\n\n switch (shape.kind) {\n case 'rect':\n case 'ellipse': {\n setVal('x', shape.x);\n setVal('y', shape.y);\n setVal('width', shape.width);\n setVal('height', shape.height);\n return;\n }\n case 'arrow': {\n setVal('x1', shape.x1);\n setVal('y1', shape.y1);\n setVal('x2', shape.x2);\n setVal('y2', shape.y2);\n return;\n }\n case 'text': {\n setVal('x', shape.x);\n setVal('y', shape.y);\n return;\n }\n default:\n // freehand / highlight don't expose coordinate inputs (they're\n // pointer-only per ). The mount layer hides the row\n // before reaching this branch; keeping the case exhaustive\n // means adding a new keyboard-placeable kind shows up as a\n // type error here.\n return;\n }\n }\n\n function onAnyInputChange(): void {\n if (!activeShape || !activeKind) return;\n const edit = readCurrentEdit(activeShape);\n if (!edit) return;\n const updated = applyCoordEdit(activeShape, edit);\n if (updated === activeShape) return;\n activeShape = updated;\n options.onShapeChanged(updated);\n }\n\n function readCurrentEdit(shape: Shape): ShapeCoordEdit | null {\n const num = (id: string): number => {\n const el = inputs.get(id);\n if (!el) return Number.NaN;\n return el.valueAsNumber;\n };\n switch (shape.kind) {\n case 'rect':\n case 'ellipse': {\n const x = Math.round(num('x'));\n const y = Math.round(num('y'));\n const width = Math.round(num('width'));\n const height = Math.round(num('height'));\n if (![x, y, width, height].every(Number.isFinite)) return null;\n return { kind: shape.kind, x, y, width, height };\n }\n case 'arrow': {\n const x1 = Math.round(num('x1'));\n const y1 = Math.round(num('y1'));\n const x2 = Math.round(num('x2'));\n const y2 = Math.round(num('y2'));\n if (![x1, y1, x2, y2].every(Number.isFinite)) return null;\n return { kind: 'arrow', x1, y1, x2, y2 };\n }\n case 'text': {\n const x = Math.round(num('x'));\n const y = Math.round(num('y'));\n if (![x, y].every(Number.isFinite)) return null;\n return { kind: 'text', x, y };\n }\n default:\n return null;\n }\n }\n\n return {\n container,\n updateForShape(shape): void {\n if (!shape) {\n activeShape = null;\n activeKind = null;\n container.hidden = true;\n container.replaceChildren();\n inputs.clear();\n return;\n }\n // Freehand / highlight aren't keyboard-placeable; their\n // selection still works (Delete to remove; arrow keys to\n // nudge), but no coordinate inputs are shown.\n if (shape.kind === 'freehand' || shape.kind === 'highlight') {\n activeShape = shape;\n activeKind = null;\n container.hidden = true;\n container.replaceChildren();\n inputs.clear();\n return;\n }\n activeShape = shape;\n if (activeKind !== shape.kind) {\n rebuildFor(shape.kind);\n }\n syncValuesFromShape(shape);\n container.hidden = false;\n },\n destroy(): void {\n container.replaceChildren();\n inputs.clear();\n container.remove();\n },\n };\n}\n\nfunction fieldsFor(kind: 'rect' | 'ellipse' | 'arrow' | 'text'): ReadonlyArray<FieldSpec> {\n switch (kind) {\n case 'rect':\n case 'ellipse':\n return RECT_FIELDS;\n case 'arrow':\n return ARROW_FIELDS;\n case 'text':\n return TEXT_FIELDS;\n }\n}\n\n/**\n * Apply an edit produced by the inputs to the shape it came from.\n * Exported so the mount layer can compose it with its own clamping\n * before writing to the store. The function is total over the shape\n * union so the caller doesn't need to re-narrow.\n */\nexport function applyCoordEdit(shape: Shape, edit: ShapeCoordEdit): Shape {\n switch (shape.kind) {\n case 'rect': {\n if (edit.kind !== 'rect') return shape;\n const next: RectShape = {\n ...shape,\n x: edit.x,\n y: edit.y,\n width: edit.width,\n height: edit.height,\n };\n return next;\n }\n case 'ellipse': {\n if (edit.kind !== 'ellipse') return shape;\n const next: EllipseShape = {\n ...shape,\n x: edit.x,\n y: edit.y,\n width: edit.width,\n height: edit.height,\n };\n return next;\n }\n case 'arrow': {\n if (edit.kind !== 'arrow') return shape;\n const next: ArrowShape = { ...shape, x1: edit.x1, y1: edit.y1, x2: edit.x2, y2: edit.y2 };\n return next;\n }\n case 'text': {\n if (edit.kind !== 'text') return shape;\n const next: TextShape = { ...shape, x: edit.x, y: edit.y };\n return next;\n }\n default:\n return shape;\n }\n}\n","/**\n * Build the annotation panel: tool toolbar on the left, style\n * controls on the right. Mirrors the layout-rhythm conventions the\n * other Phase 2 panels (rotate, flip, resize) established — flat\n * row(s) of controls under `.kalotyp-util-main`.\n */\n\nimport {\n type AnnotateTool,\n isKeyboardPlaceableKind,\n type StylePalette,\n} from '@magicpages/kalotyp-core';\nimport { icon } from '../../icons.js';\n\nexport interface AnnotatePanelOptions {\n readonly initialTool: AnnotateTool;\n readonly initialStyle: StylePalette;\n readonly canDelete: boolean;\n /**\n * Where the per-selection coordinate-input row mounts. The mount layer owns the row's lifecycle and just\n * needs the panel to expose the slot so the inputs render in the\n * panel rhythm rather than as a free-floating bar.\n */\n readonly coordInputs: HTMLElement;\n onSelectTool(tool: AnnotateTool): void;\n onColorChange(color: string): void;\n onStrokeWidthChange(width: number): void;\n onDeleteSelected(): void;\n /**\n * Insert the active drawing tool's default shape at image centre\n * (Phase 6.3). The panel disables the button when the active tool\n * is `select`, `freehand`, or `highlight` — see\n * `isKeyboardPlaceableKind` in core.\n */\n onInsertAtCenter(): void;\n}\n\nexport interface AnnotatePanel {\n readonly container: HTMLDivElement;\n readonly toolButtons: ReadonlyMap<AnnotateTool, HTMLButtonElement>;\n readonly hexInput: HTMLInputElement;\n readonly colorSwatches: ReadonlyArray<HTMLButtonElement>;\n readonly strokeRange: HTMLInputElement;\n readonly deleteButton: HTMLButtonElement;\n readonly insertButton: HTMLButtonElement;\n setActiveTool(tool: AnnotateTool): void;\n setStyle(style: StylePalette): void;\n setCanDelete(canDelete: boolean): void;\n}\n\n/**\n * Tool icons. Sourced from Lucide via the shared icons module so the\n * whole editor reads as one icon family (history controls, lock\n * toggle, annotation tools). The select cursor is filled to match\n * the conventional pointer-arrow visual; the others are stroked\n * outlines.\n */\nconst TOOL_DEFS: ReadonlyArray<{ id: AnnotateTool; label: string; icon: string }> = [\n {\n id: 'select',\n label: 'Select',\n icon: icon('select', { fill: 'currentColor', 'stroke-width': 1 }),\n },\n { id: 'text', label: 'Text', icon: icon('text') },\n { id: 'rect', label: 'Rectangle', icon: icon('rect') },\n { id: 'ellipse', label: 'Ellipse', icon: icon('ellipse') },\n { id: 'arrow', label: 'Arrow', icon: icon('arrow') },\n { id: 'freehand', label: 'Freehand', icon: icon('freehand') },\n { id: 'highlight', label: 'Highlight', icon: icon('highlight') },\n];\n\n/**\n * Compact preset swatch palette. Six colours covering bright (red,\n * yellow, green, blue) plus white and black for outlines on either\n * background. The custom-colour `<input type=\"color\">` lets the user\n * override; the swatches just save them a click for the common cases.\n */\nconst PRESET_COLORS: readonly string[] = [\n '#ff3b30',\n '#ffcc00',\n '#34c759',\n '#007aff',\n '#ffffff',\n '#000000',\n];\n\nexport function buildAnnotatePanel(options: AnnotatePanelOptions): AnnotatePanel {\n const container = document.createElement('div');\n container.className = 'kalotyp-annotate-panel';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Annotate');\n\n // ----- Tool toolbar -----\n const toolbar = document.createElement('div');\n toolbar.className = 'kalotyp-annotate-toolbar';\n toolbar.setAttribute('role', 'toolbar');\n toolbar.setAttribute('aria-label', 'Annotation tools');\n\n const toolButtons = new Map<AnnotateTool, HTMLButtonElement>();\n for (const def of TOOL_DEFS) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-annotate-tool';\n button.dataset.tool = def.id;\n button.setAttribute('aria-label', def.label);\n button.title = def.label;\n button.setAttribute('aria-pressed', def.id === options.initialTool ? 'true' : 'false');\n // The icon string is inline SVG markup (from Lucide via the\n // shared icons module). Use innerHTML so the SVG actually\n // parses and renders — `textContent` would print the markup as\n // literal text inside the button.\n button.innerHTML = def.icon;\n button.addEventListener('click', () => options.onSelectTool(def.id));\n toolbar.appendChild(button);\n toolButtons.set(def.id, button);\n }\n\n // ----- Style controls (color + stroke) -----\n const styleRow = document.createElement('div');\n styleRow.className = 'kalotyp-annotate-style-row';\n\n const swatches: HTMLButtonElement[] = [];\n const swatchGroup = document.createElement('div');\n swatchGroup.className = 'kalotyp-annotate-swatches';\n swatchGroup.setAttribute('role', 'radiogroup');\n swatchGroup.setAttribute('aria-label', 'Color');\n for (const color of PRESET_COLORS) {\n const swatch = document.createElement('button');\n swatch.type = 'button';\n swatch.className = 'kalotyp-annotate-swatch';\n swatch.setAttribute('role', 'radio');\n swatch.setAttribute('aria-label', `Use color ${color}`);\n swatch.dataset.color = color;\n swatch.style.setProperty('--kalotyp-swatch', color);\n swatch.addEventListener('click', () => options.onColorChange(color));\n swatchGroup.appendChild(swatch);\n swatches.push(swatch);\n }\n\n // Single hex code input. The native `<input type=\"color\">` was\n // dropped (Phase 6.6 polish) — it duplicated the swatches' role\n // and crowded the row. The hex input is the keyboard-accessible\n // path; swatches cover the common-colour quick-pick path. Power\n // users with a custom palette type the hex.\n let lastValidHex = normaliseColorForInput(options.initialStyle.color);\n const hexInput = document.createElement('input');\n hexInput.type = 'text';\n hexInput.className = 'kalotyp-annotate-hex';\n hexInput.value = lastValidHex;\n hexInput.maxLength = 7;\n hexInput.spellcheck = false;\n hexInput.autocomplete = 'off';\n hexInput.setAttribute('aria-label', 'Color hex code');\n hexInput.setAttribute('placeholder', '#000000');\n hexInput.style.setProperty('--kalotyp-hex-swatch', lastValidHex);\n hexInput.addEventListener('change', () => {\n const value = hexInput.value.trim();\n const normalised = normaliseHexInput(value);\n if (normalised) {\n hexInput.value = normalised;\n lastValidHex = normalised;\n hexInput.style.setProperty('--kalotyp-hex-swatch', normalised);\n options.onColorChange(normalised);\n } else {\n // Restore to the last valid colour. The colour swatches and\n // state still reflect the last accepted value; the input\n // visibly resets so the user sees their bad input cleared.\n hexInput.value = lastValidHex;\n }\n });\n\n const strokeLabel = document.createElement('label');\n strokeLabel.className = 'kalotyp-annotate-stroke-label';\n strokeLabel.textContent = 'Width';\n\n const strokeRange = document.createElement('input');\n strokeRange.type = 'range';\n strokeRange.className = 'kalotyp-annotate-stroke';\n strokeRange.min = '1';\n strokeRange.max = '40';\n strokeRange.step = '1';\n strokeRange.value = String(options.initialStyle.strokeWidth);\n strokeRange.setAttribute('aria-label', 'Stroke width');\n strokeRange.addEventListener('change', () =>\n options.onStrokeWidthChange(strokeRange.valueAsNumber),\n );\n\n const deleteButton = document.createElement('button');\n deleteButton.type = 'button';\n deleteButton.className = 'kalotyp-annotate-delete';\n deleteButton.innerHTML = `${icon('delete')}<span>Delete</span>`;\n deleteButton.setAttribute('aria-label', 'Delete selected annotation');\n deleteButton.title = 'Delete (Del)';\n deleteButton.disabled = !options.canDelete;\n deleteButton.addEventListener('click', () => options.onDeleteSelected());\n\n // Insert-at-centre button. The keyboard-only\n // path for placing a shape: pick a tool, press this, get a default\n // shape at image centre, then position via the coordinate inputs.\n // Pointer users can ignore it — the canvas drag still works for\n // them. The button is disabled for tools that can't be placed\n // without a path (freehand, highlight) and for `select`.\n const insertButton = document.createElement('button');\n insertButton.type = 'button';\n insertButton.className = 'kalotyp-annotate-insert';\n insertButton.innerHTML = `${icon('plus')}<span>Insert at centre</span>`;\n insertButton.setAttribute('aria-label', 'Insert annotation at image centre');\n insertButton.title = 'Insert at centre';\n insertButton.disabled = !canInsertForTool(options.initialTool);\n insertButton.addEventListener('click', () => options.onInsertAtCenter());\n\n styleRow.appendChild(swatchGroup);\n styleRow.appendChild(hexInput);\n styleRow.appendChild(strokeLabel);\n styleRow.appendChild(strokeRange);\n styleRow.appendChild(insertButton);\n styleRow.appendChild(deleteButton);\n\n container.appendChild(toolbar);\n container.appendChild(styleRow);\n // Coordinate-input slot. The mount layer\n // appends the inputs into this slot after constructing the panel,\n // so the panel doesn't need to know per-shape geometry. The slot\n // sits below the style row inside the panel container so a\n // keyboard user finds it via natural Tab order.\n container.appendChild(options.coordInputs);\n\n function setActiveTool(tool: AnnotateTool): void {\n for (const [id, button] of toolButtons) {\n button.setAttribute('aria-pressed', id === tool ? 'true' : 'false');\n }\n insertButton.disabled = !canInsertForTool(tool);\n }\n\n function setStyle(style: StylePalette): void {\n const targetColor = normaliseColorForInput(style.color);\n if (hexInput.value.toLowerCase() !== targetColor.toLowerCase()) hexInput.value = targetColor;\n lastValidHex = targetColor;\n hexInput.style.setProperty('--kalotyp-hex-swatch', targetColor);\n if (strokeRange.valueAsNumber !== style.strokeWidth) {\n strokeRange.value = String(style.strokeWidth);\n }\n for (const swatch of swatches) {\n const matches = swatch.dataset.color?.toLowerCase() === style.color.toLowerCase();\n swatch.setAttribute('aria-checked', matches ? 'true' : 'false');\n }\n }\n\n function setCanDelete(canDelete: boolean): void {\n deleteButton.disabled = !canDelete;\n }\n\n setStyle(options.initialStyle);\n\n return {\n container,\n toolButtons,\n hexInput,\n colorSwatches: swatches,\n strokeRange,\n deleteButton,\n insertButton,\n setActiveTool,\n setStyle,\n setCanDelete,\n };\n}\n\n/**\n * Whether the active tool exposes a default-at-centre keyboard\n * placement path. The toolbar's `select` button isn't a drawing\n * tool, and `freehand` / `highlight` are gestural — neither has a\n * meaningful \"default shape\" to place., Phase 6.3.\n */\nfunction canInsertForTool(tool: AnnotateTool): boolean {\n if (tool === 'select') return false;\n return isKeyboardPlaceableKind(tool);\n}\n\n/**\n * `<input type=\"color\">` only accepts `#rrggbb`. Our default palette\n * stores colours in the same form, but state writes from elsewhere\n * (e.g. the highlight default) might use rgba — those just leave the\n * native picker showing the previous valid colour, which is fine.\n */\nfunction normaliseColorForInput(color: string): string {\n if (/^#[0-9a-fA-F]{6}$/.test(color)) return color;\n if (/^#[0-9a-fA-F]{3}$/.test(color)) {\n // Expand short hex to full hex so the input accepts it.\n const r = color[1];\n const g = color[2];\n const b = color[3];\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return '#000000';\n}\n\n/**\n * Accept hex codes the user types into the keyboard-accessible\n * colour input. Tolerant of upper/lower case and\n * missing `#` prefix; returns the canonical `#rrggbb` form for\n * accepted input or `null` if the input isn't a valid hex code.\n */\nfunction normaliseHexInput(value: string): string | null {\n const cleaned = value.startsWith('#') ? value.slice(1) : value;\n if (/^[0-9a-fA-F]{6}$/.test(cleaned)) return `#${cleaned.toLowerCase()}`;\n if (/^[0-9a-fA-F]{3}$/.test(cleaned)) {\n const r = cleaned[0];\n const g = cleaned[1];\n const b = cleaned[2];\n return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();\n }\n return null;\n}\n","/**\n * Pointer-drag helper. Same shape as the crop plugin's\n * `attachPointerDrag` (interaction.ts), kept local here so the\n * annotation tool gestures can layer their own per-gesture state on\n * top without coupling to the crop module.\n *\n * The factory is invoked on `pointerdown`. It must return a\n * `DragHandlers` object whose three callbacks describe the gesture's\n * lifecycle: `onMove(point)` per coalesced animation frame,\n * `onCommit()` once on `pointerup` (after a final drained move), and\n * `onCancel()` once on `pointercancel`.\n *\n * The factory may return `null` to refuse the gesture — useful when\n * a hit-area pointerdown only sometimes starts a drag (e.g. the\n * select tool doesn't drag if no shape is under the pointer).\n */\n\n/**\n * Per-frame point payload. Includes `shiftKey` so tool gestures can\n * apply axis / square / circle constraints while the modifier is\n * held — the standard image-editor convention (Figma, Sketch,\n * Photoshop). The flag reflects shift-state at the most\n * recent pointer event, so releasing/re-pressing shift mid-drag\n * toggles the constraint live.\n */\nexport interface DragPoint {\n readonly clientX: number;\n readonly clientY: number;\n readonly shiftKey: boolean;\n}\n\nexport interface DragHandlers {\n onMove(point: DragPoint): void;\n onCommit(): void;\n onCancel(): void;\n}\n\nexport function attachPointerDrag(\n element: HTMLElement,\n factory: (start: PointerEvent) => DragHandlers | null,\n): () => void {\n const onPointerDown = (event: PointerEvent): void => {\n if (event.button !== 0) return;\n const handlers = factory(event);\n if (!handlers) return;\n event.preventDefault();\n event.stopPropagation();\n\n try {\n element.setPointerCapture(event.pointerId);\n } catch {\n // Synthetic events (test runners, accessibility tools) sometimes\n // present a pointer id the browser can't resolve. Move/up events\n // still flow through the listeners we attach below; capture is a\n // best-effort affordance for OS-level cross-element drag.\n }\n\n let pendingPoint: DragPoint | undefined;\n let rafScheduled = false;\n // Track the latest shift state so a key-up/key-down between\n // pointer moves still flushes a frame with the new constraint.\n let lastShiftKey = event.shiftKey;\n\n const flush = (): void => {\n rafScheduled = false;\n if (!pendingPoint) return;\n const point = pendingPoint;\n pendingPoint = undefined;\n handlers.onMove(point);\n };\n\n const schedule = (point: DragPoint): void => {\n pendingPoint = point;\n if (!rafScheduled) {\n rafScheduled = true;\n requestAnimationFrame(flush);\n }\n };\n\n const onPointerMove = (moveEvent: PointerEvent): void => {\n if (moveEvent.pointerId !== event.pointerId) return;\n lastShiftKey = moveEvent.shiftKey;\n schedule({\n clientX: moveEvent.clientX,\n clientY: moveEvent.clientY,\n shiftKey: moveEvent.shiftKey,\n });\n };\n\n // Re-apply the gesture with the new constraint when the user\n // toggles shift mid-drag without moving the pointer.\n const onKeyToggle = (keyEvent: KeyboardEvent): void => {\n if (keyEvent.key !== 'Shift') return;\n if (keyEvent.shiftKey === lastShiftKey) return;\n lastShiftKey = keyEvent.shiftKey;\n // Use the last committed/pending pointer position; if neither\n // exists yet (no pointermove since pointerdown), use the\n // pointerdown coords.\n const last = pendingPoint;\n const x = last?.clientX ?? event.clientX;\n const y = last?.clientY ?? event.clientY;\n schedule({ clientX: x, clientY: y, shiftKey: keyEvent.shiftKey });\n };\n\n const finish = (committed: boolean): void => {\n element.removeEventListener('pointermove', onPointerMove);\n element.removeEventListener('pointerup', onPointerUp);\n element.removeEventListener('pointercancel', onPointerCancel);\n window.removeEventListener('keydown', onKeyToggle);\n window.removeEventListener('keyup', onKeyToggle);\n try {\n element.releasePointerCapture(event.pointerId);\n } catch {\n // Already released or never captured; nothing to do.\n }\n if (pendingPoint) {\n const final = pendingPoint;\n pendingPoint = undefined;\n handlers.onMove(final);\n }\n if (committed) handlers.onCommit();\n else handlers.onCancel();\n };\n\n const onPointerUp = (upEvent: PointerEvent): void => {\n if (upEvent.pointerId !== event.pointerId) return;\n finish(true);\n };\n const onPointerCancel = (cancelEvent: PointerEvent): void => {\n if (cancelEvent.pointerId !== event.pointerId) return;\n finish(false);\n };\n\n element.addEventListener('pointermove', onPointerMove);\n element.addEventListener('pointerup', onPointerUp);\n element.addEventListener('pointercancel', onPointerCancel);\n window.addEventListener('keydown', onKeyToggle);\n window.addEventListener('keyup', onKeyToggle);\n };\n\n element.addEventListener('pointerdown', onPointerDown);\n return () => element.removeEventListener('pointerdown', onPointerDown);\n}\n\n/** Convert a client-space point to the bounding-rect-local coords of `element`. */\nexport function clientToElement(\n element: HTMLElement,\n clientX: number,\n clientY: number,\n): { x: number; y: number } {\n const rect = element.getBoundingClientRect();\n return { x: clientX - rect.left, y: clientY - rect.top };\n}\n","/**\n * Render helpers for the annotation plugin's three canvas layers.\n *\n * - `paintImageLayer`: paints the upstream-baked source onto the\n * bottom canvas. Called once on mount and on stage resize.\n * - `paintShapesLayer`: paints every committed shape onto the middle\n * canvas. Called when the shape list, selection, or viewport\n * changes.\n * - `paintLiveLayer`: paints whatever the in-progress gesture wants\n * on top — a draft shape during a drag, a marquee, etc. Called\n * per frame during a gesture.\n *\n * All three reuse the same DPR-aware canvas-sizing helper to keep the\n * pixel grids consistent. Shapes are rendered through `paintShape`\n * from the core `bake.ts` module so the live preview is byte-equal to\n * the bake output (per shape).\n */\n\nimport { paintShape, type Shape, type SourceImage, type Viewport } from '@magicpages/kalotyp-core';\n\n/**\n * Resize the canvas's backing store to the stage CSS pixels × DPR\n * and return its 2D context already scaled into CSS-pixel space.\n * Returns `null` if the context is unavailable (jsdom path).\n */\nfunction prepareCanvas(\n canvas: HTMLCanvasElement,\n stageWidth: number,\n stageHeight: number,\n): CanvasRenderingContext2D | null {\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const targetW = Math.max(1, Math.round(stageWidth * dpr));\n const targetH = Math.max(1, Math.round(stageHeight * dpr));\n if (canvas.width !== targetW) canvas.width = targetW;\n if (canvas.height !== targetH) canvas.height = targetH;\n canvas.style.width = `${stageWidth}px`;\n canvas.style.height = `${stageHeight}px`;\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, stageWidth, stageHeight);\n return ctx;\n}\n\nexport function paintImageLayer(\n canvas: HTMLCanvasElement,\n source: SourceImage,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const ctx = prepareCanvas(canvas, stageWidth, stageHeight);\n if (!ctx) return;\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(\n source.bitmap,\n viewport.displayRect.x,\n viewport.displayRect.y,\n viewport.displayRect.width,\n viewport.displayRect.height,\n );\n}\n\n/**\n * Paint a list of shapes by setting up an image-space coordinate\n * frame and calling the shared `paintShape` for each. The image-space\n * frame is established by translating to the displayRect's origin and\n * scaling by the viewport scale; the shapes' image-space coordinates\n * then land at the right display pixels for free.\n */\nexport function paintShapesLayer(\n canvas: HTMLCanvasElement,\n shapes: ReadonlyArray<Shape>,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const ctx = prepareCanvas(canvas, stageWidth, stageHeight);\n if (!ctx) return;\n if (shapes.length === 0) return;\n ctx.save();\n ctx.translate(viewport.displayRect.x, viewport.displayRect.y);\n ctx.scale(viewport.scale, viewport.scale);\n for (const shape of shapes) {\n paintShape(ctx, shape);\n }\n ctx.restore();\n}\n\n/**\n * Paint a single in-progress shape on the live canvas. Same\n * coordinate setup as the shapes layer; passing `null` clears the\n * canvas without drawing — useful when a gesture ends.\n */\nexport function paintLiveLayer(\n canvas: HTMLCanvasElement,\n shape: Shape | null,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const ctx = prepareCanvas(canvas, stageWidth, stageHeight);\n if (!ctx) return;\n if (shape === null) return;\n ctx.save();\n ctx.translate(viewport.displayRect.x, viewport.displayRect.y);\n ctx.scale(viewport.scale, viewport.scale);\n paintShape(ctx, shape);\n ctx.restore();\n}\n\n/**\n * Paint a selection-marquee rectangle (image-space) on the live\n * canvas. Distinct from `paintLiveLayer` because the marquee uses\n * dashed strokes + a faint fill — visual conventions for \"I am\n * marquee-selecting\" — that don't belong to any committed shape.\n */\nexport function paintMarqueeLayer(\n canvas: HTMLCanvasElement,\n marqueeImage: { x: number; y: number; width: number; height: number } | null,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const ctx = prepareCanvas(canvas, stageWidth, stageHeight);\n if (!ctx) return;\n if (!marqueeImage) return;\n // Translate to display space (no scale — drawing in display pixels\n // so the dash pattern stays a constant visual width regardless of\n // the upstream image's pixel density).\n const dx = viewport.displayRect.x + marqueeImage.x * viewport.scale;\n const dy = viewport.displayRect.y + marqueeImage.y * viewport.scale;\n let dw = marqueeImage.width * viewport.scale;\n let dh = marqueeImage.height * viewport.scale;\n // Negative-extent drag: flip the box for rendering so we always\n // pass non-negative dimensions to fillRect/strokeRect.\n let drawX = dx;\n let drawY = dy;\n if (dw < 0) {\n drawX = dx + dw;\n dw = -dw;\n }\n if (dh < 0) {\n drawY = dy + dh;\n dh = -dh;\n }\n ctx.save();\n ctx.fillStyle = 'rgba(99, 102, 241, 0.12)';\n ctx.fillRect(drawX, drawY, dw, dh);\n ctx.strokeStyle = 'rgba(99, 102, 241, 0.95)';\n ctx.lineWidth = 1;\n ctx.setLineDash([4, 3]);\n ctx.strokeRect(drawX + 0.5, drawY + 0.5, dw - 1, dh - 1);\n ctx.restore();\n}\n","/**\n * Selection rendering and per-handle resize gesture for annotation\n * shapes. The selection layer is responsible for:\n *\n * - Rendering the bounding-box outline + 8 corner/edge handles when\n * a shape is selected. Arrows render with two endpoint handles\n * instead (no orthogonal extent to resize).\n * - Wiring each handle's pointerdown to a per-handle resize gesture\n * that mutates the shape's geometry as the user drags.\n * - Cleaning up handles when the selection clears.\n *\n * Handles are positioned in stage CSS pixels using the shared\n * `position-handles` helper that crop already uses for its 8\n * manipulators. Reusing that helper keeps the handle DOM consistent\n * with crop's so future styling can target both with a single rule.\n */\n\nimport {\n ALL_SELECTION_HANDLES,\n type AnnotateState,\n type ArrowShape,\n boundingBoxOf,\n type Rect,\n rectFromHandleDrag,\n replaceShape,\n type SelectionHandle,\n type Shape,\n selectionHandlePositions,\n type Viewport,\n} from '@magicpages/kalotyp-core';\nimport type { DragHandlers } from './pointer-drag.js';\nimport { attachPointerDrag } from './pointer-drag.js';\nimport type { ToolGestureContext } from './tools.js';\n\n/**\n * Build and own the selection-handle DOM. The returned object exposes\n * `update(shape, viewport)` so the caller can re-render handles after\n * any state change without rebuilding the DOM.\n */\nexport interface SelectionLayerOptions {\n readonly host: HTMLDivElement;\n readonly stageElement: HTMLElement;\n readonly toolContext: ToolGestureContext;\n /** Read the current letterbox viewport at gesture-start time. */\n getViewport(): Viewport;\n}\n\nexport interface SelectionLayer {\n /** Update the rendered handles to reflect the selected shape (or clear). */\n update(shape: Shape | null, viewport: Viewport): void;\n destroy(): void;\n}\n\nexport function buildSelectionLayer(options: SelectionLayerOptions): SelectionLayer {\n const { host, toolContext } = options;\n const handleEls = new Map<SelectionHandle, HTMLButtonElement>();\n const cleanups: Array<() => void> = [];\n\n // Eight box handles: only some are shown for non-rectangular shapes\n // (arrow uses just two endpoint handles).\n for (const direction of ALL_SELECTION_HANDLES) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-annotate-handle';\n button.dataset.direction = direction;\n button.setAttribute('aria-label', handleLabel(direction));\n // The handles are pointer-driven affordances. Keyboard users\n // resize via the coordinate inputs (Phase 6.3), which\n // expose every dimension the eight handles do without forcing\n // the user to Tab through eight stops per selection. Removing\n // the handles from the keyboard tab order keeps the Tab-walk\n // through the editor short and meaningful — every stop the user\n // lands on does something via keyboard.\n //\n // We keep `aria-label` so the accessibility tree carries a\n // meaningful name (axe rule \"button-name\") and assistive tools\n // that navigate by something other than Tab still see a label.\n // We don't add `aria-hidden` here because a focusable element\n // (even with tabIndex=-1, since it's still programmatically\n // focusable) inside an aria-hidden subtree trips axe rule\n // \"aria-hidden-focus\".\n button.tabIndex = -1;\n button.style.display = 'none';\n handleEls.set(direction, button);\n host.appendChild(button);\n\n cleanups.push(\n attachPointerDrag(button, (event) => startHandleResizeGesture(toolContext, direction, event)),\n );\n }\n\n function update(shape: Shape | null, viewport: Viewport): void {\n if (!shape) {\n hideAll(handleEls);\n return;\n }\n if (shape.kind === 'arrow') {\n // Arrows only get two handles, mapped to their endpoints.\n hideAll(handleEls);\n const tl = handleEls.get('tl');\n const br = handleEls.get('br');\n if (tl) {\n positionHandle(tl, imageToDisplay({ x: shape.x1, y: shape.y1 }, viewport));\n }\n if (br) {\n positionHandle(br, imageToDisplay({ x: shape.x2, y: shape.y2 }, viewport));\n }\n return;\n }\n const box = boundingBoxOf(shape);\n const handlePositions = selectionHandlePositions(box);\n for (const direction of ALL_SELECTION_HANDLES) {\n const handle = handleEls.get(direction);\n if (!handle) continue;\n positionHandle(handle, imageToDisplay(handlePositions[direction], viewport));\n }\n }\n\n function destroy(): void {\n for (const cleanup of cleanups) cleanup();\n for (const [, button] of handleEls) button.remove();\n handleEls.clear();\n }\n\n return { update, destroy };\n}\n\nfunction imageToDisplay(\n point: { x: number; y: number },\n viewport: Viewport,\n): { x: number; y: number } {\n return {\n x: viewport.displayRect.x + point.x * viewport.scale,\n y: viewport.displayRect.y + point.y * viewport.scale,\n };\n}\n\nfunction positionHandle(handle: HTMLButtonElement, displayPoint: { x: number; y: number }): void {\n handle.style.display = '';\n handle.style.left = `${displayPoint.x}px`;\n handle.style.top = `${displayPoint.y}px`;\n}\n\nfunction hideAll(handles: Map<SelectionHandle, HTMLButtonElement>): void {\n for (const [, button] of handles) {\n button.style.display = 'none';\n }\n}\n\n/**\n * Build the per-handle resize gesture. Snapshots the selected shape\n * at gesture start; each move computes the new image-space pointer\n * position and applies it via the appropriate per-shape mutator.\n *\n * Returns `null` if no shape is selected when the handle is pressed\n * (defensive — UI should hide handles in that case).\n */\nfunction startHandleResizeGesture(\n ctx: ToolGestureContext,\n direction: SelectionHandle,\n origin: PointerEvent,\n): DragHandlers | null {\n const state = ctx.store.get();\n const selected = state.shapes.find((shape) => shape.id === state.selectedId);\n if (!selected) return null;\n const initial = selected;\n\n // `origin` is part of the gesture signature even though we read all\n // needed state from the store at gesture start. It carries pointerId\n // / button info the pointer-drag helper consumes.\n void origin;\n return {\n onMove(point) {\n const image = ctx.toImageSpace(point);\n const next = applyHandleDrag(initial, direction, image);\n if (next) ctx.store.update((cur) => replaceShape(cur, next));\n },\n onCommit() {\n ctx.commit();\n },\n onCancel() {\n ctx.store.update((cur) => replaceShape(cur, initial));\n },\n };\n}\n\nfunction applyHandleDrag(\n shape: Shape,\n direction: SelectionHandle,\n image: { x: number; y: number },\n): Shape | null {\n switch (shape.kind) {\n case 'rect':\n case 'ellipse': {\n const box: Rect = {\n x: shape.x,\n y: shape.y,\n width: shape.width,\n height: shape.height,\n };\n const next = rectFromHandleDrag(box, direction, image);\n // We allow negative width/height during the drag; the next\n // commit normalises. Keeping it un-normalised mid-drag makes\n // the live preview match what the user is doing on screen.\n return { ...shape, x: next.x, y: next.y, width: next.width, height: next.height };\n }\n case 'arrow': {\n const arrow = shape as ArrowShape;\n // 'tl' handle maps to (x1,y1); 'br' maps to (x2,y2). The\n // selection layer hides the other six handles for arrows.\n if (direction === 'tl') return { ...arrow, x1: image.x, y1: image.y };\n if (direction === 'br') return { ...arrow, x2: image.x, y2: image.y };\n return shape;\n }\n case 'text': {\n // Text shapes resize by font size. The bottom-right handle\n // scales the font; other handles aren't shown for text in v1.\n if (direction !== 'br') return shape;\n const dx = image.x - shape.x;\n const dy = image.y - shape.y;\n // Scale so the dragged distance roughly matches the typed\n // box's diagonal. A min font size of 8 px stops the user from\n // making text invisible by accident.\n const newSize = Math.max(8, Math.round(Math.max(dx, dy) * 0.6));\n return { ...shape, fontSize: newSize };\n }\n case 'freehand':\n case 'highlight': {\n // Path shapes scale around their bounding-box centre by the\n // ratio between the initial and dragged box. Move the dragged\n // corner to the pointer; the other corners scale accordingly.\n const box = boundingBoxOf(shape);\n if (box.width === 0 || box.height === 0) return shape;\n const next = rectFromHandleDrag(box, direction, image);\n const scaleX = next.width / box.width;\n const scaleY = next.height / box.height;\n if (!Number.isFinite(scaleX) || !Number.isFinite(scaleY)) return shape;\n const points = shape.points.map((p) => ({\n x: next.x + (p.x - box.x) * scaleX,\n y: next.y + (p.y - box.y) * scaleY,\n }));\n return { ...shape, points };\n }\n }\n}\n\nfunction handleLabel(direction: SelectionHandle): string {\n switch (direction) {\n case 'tl':\n return 'Resize from top-left';\n case 'tr':\n return 'Resize from top-right';\n case 'bl':\n return 'Resize from bottom-left';\n case 'br':\n return 'Resize from bottom-right';\n case 't':\n return 'Resize from top';\n case 'r':\n return 'Resize from right';\n case 'b':\n return 'Resize from bottom';\n case 'l':\n return 'Resize from left';\n }\n}\n\n/**\n * Helper for the mount layer: which shape (if any) is selected, given\n * the current state? Keeps the lookup logic in one place — most\n * callers in `mount.ts` need it via the store subscription.\n */\nexport function selectedShapeOf(state: AnnotateState): Shape | null {\n if (state.selectedId === null) return null;\n return state.shapes.find((shape) => shape.id === state.selectedId) ?? null;\n}\n","/**\n * Build the layered DOM for the annotation plugin's stage. Three\n * canvases (image, committed shapes, live in-progress) plus three DOM\n * layers (a hit-area for pointer input, a handles layer for selection\n * resize handles, and a text-edit overlay) — / Phase 3\n * brief on stacked canvases.\n *\n * The element ordering follows the z-stack: image at the bottom,\n * shapes and live canvases above it, then the pointer hit-area, then\n * the handles layer (so handles can intercept pointerdowns before the\n * hit-area sees them), then the text overlay on top.\n *\n * The hit-area is the surface the tool / selection layer attaches its\n * pointerdown handlers to. It carries `touch-action: none` so the\n * browser doesn't hijack drags for scroll/pinch on touch devices.\n */\n\nexport interface AnnotateStageElements {\n readonly container: HTMLDivElement;\n readonly imageCanvas: HTMLCanvasElement;\n readonly shapesCanvas: HTMLCanvasElement;\n readonly liveCanvas: HTMLCanvasElement;\n readonly hitArea: HTMLDivElement;\n /** Holds selection handles (DOM buttons) when a shape is selected. */\n readonly handlesLayer: HTMLDivElement;\n /** Holds the inline text editor when text is being edited. */\n readonly textOverlay: HTMLDivElement;\n}\n\nexport function buildAnnotateStage(): AnnotateStageElements {\n const container = document.createElement('div');\n container.className = 'kalotyp-annotate-stage';\n\n const imageCanvas = document.createElement('canvas');\n imageCanvas.className = 'kalotyp-annotate-image';\n imageCanvas.setAttribute('aria-hidden', 'true');\n\n const shapesCanvas = document.createElement('canvas');\n shapesCanvas.className = 'kalotyp-annotate-shapes';\n shapesCanvas.setAttribute('aria-hidden', 'true');\n\n const liveCanvas = document.createElement('canvas');\n liveCanvas.className = 'kalotyp-annotate-live';\n liveCanvas.setAttribute('aria-hidden', 'true');\n\n const hitArea = document.createElement('div');\n hitArea.className = 'kalotyp-annotate-hit';\n hitArea.setAttribute('role', 'presentation');\n\n const handlesLayer = document.createElement('div');\n handlesLayer.className = 'kalotyp-annotate-handles';\n handlesLayer.setAttribute('role', 'group');\n handlesLayer.setAttribute('aria-label', 'Selected annotation');\n\n const textOverlay = document.createElement('div');\n textOverlay.className = 'kalotyp-annotate-text-overlay';\n\n container.appendChild(imageCanvas);\n container.appendChild(shapesCanvas);\n container.appendChild(liveCanvas);\n container.appendChild(hitArea);\n container.appendChild(handlesLayer);\n container.appendChild(textOverlay);\n\n return {\n container,\n imageCanvas,\n shapesCanvas,\n liveCanvas,\n hitArea,\n handlesLayer,\n textOverlay,\n };\n}\n","/**\n * Inline text editor for the text annotation tool.\n *\n * The editor renders a contenteditable `<div>` overlaid on the\n * annotation's image-space anchor. Using a `<div>` instead of a\n * `<textarea>` lets us match the canvas-side font and size precisely\n * (textareas restrict the visible padding/size combination on some\n * browsers). The element keeps a fixed width by default and grows\n * vertically with line breaks.\n *\n * Lifecycle:\n * - `open(shape, viewport)`: position the editor over the shape,\n * prefill its text, focus it. Each input event reports the new\n * text via `onInput`. Pressing Enter (without Shift) commits;\n * pressing Escape cancels.\n * - `close()`: hide the editor and blur it.\n *\n * The caller is responsible for committing the shape into the store\n * when the editor closes; the editor is presentational.\n */\n\nimport {\n type SourceImage,\n SYSTEM_FONT_STACK,\n type TextShape,\n type Viewport,\n} from '@magicpages/kalotyp-core';\n\nexport interface TextEditorOptions {\n readonly host: HTMLDivElement;\n onInput(text: string): void;\n onCommit(): void;\n onCancel(): void;\n}\n\nexport interface TextEditorHandle {\n open(shape: TextShape, viewport: Viewport, source: SourceImage): void;\n close(): void;\n destroy(): void;\n}\n\nexport function buildTextEditor(options: TextEditorOptions): TextEditorHandle {\n const editor = document.createElement('div');\n editor.className = 'kalotyp-annotate-text-editor';\n editor.setAttribute('contenteditable', 'true');\n editor.setAttribute('role', 'textbox');\n editor.setAttribute('aria-label', 'Annotation text');\n editor.spellcheck = false;\n editor.style.display = 'none';\n options.host.appendChild(editor);\n\n let activeShape: TextShape | null = null;\n\n const onInput = (): void => {\n options.onInput(editor.innerText);\n };\n\n const onKeyDown = (event: KeyboardEvent): void => {\n // Enter without modifiers commits; Shift+Enter inserts a newline.\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n event.stopPropagation();\n options.onCommit();\n return;\n }\n if (event.key === 'Escape') {\n // Stop propagation so the editor-level Esc-to-close handler\n // doesn't fire while the user is actively editing text.\n event.preventDefault();\n event.stopPropagation();\n options.onCancel();\n }\n };\n\n // Click outside the editor element commits the edit. Listening on\n // the host (the text-overlay div) means anywhere outside the editor\n // bubble closes it; we filter clicks on the editor itself to let\n // them pass through normally.\n const onPointerDownOutside = (event: PointerEvent): void => {\n if (activeShape === null) return;\n if (editor.contains(event.target as Node)) return;\n options.onCommit();\n };\n\n editor.addEventListener('input', onInput);\n editor.addEventListener('keydown', onKeyDown);\n document.addEventListener('pointerdown', onPointerDownOutside, true);\n\n return {\n open(shape, viewport, source): void {\n activeShape = shape;\n // Position in stage CSS pixels: image origin + image-space\n // anchor scaled by viewport.\n const left = viewport.displayRect.x + shape.x * viewport.scale;\n const top = viewport.displayRect.y + shape.y * viewport.scale;\n editor.style.display = '';\n editor.style.left = `${left}px`;\n editor.style.top = `${top}px`;\n editor.style.color = shape.color;\n editor.style.font = `${shape.fontSize * viewport.scale}px ${SYSTEM_FONT_STACK}`;\n editor.style.textAlign = shape.textAlign;\n // Align the editor's anchor to the shape's anchor for the\n // current `textAlign` so typing grows the box outward in the\n // same direction the rendered text would.\n editor.style.transformOrigin = transformOriginFor(shape.textAlign);\n // Constrain width so the user has room to type without the\n // editor sliding off the stage. The displayRect.width is the\n // image's painted width in CSS pixels.\n const maxWidth = Math.max(\n 100,\n viewport.displayRect.x + viewport.displayRect.width - left - 8,\n );\n editor.style.maxWidth = `${maxWidth}px`;\n editor.innerText = shape.text;\n // Defer focus so the layout pass settles before we move the\n // caret. Without this, Safari occasionally focuses but doesn't\n // place the caret.\n requestAnimationFrame(() => {\n editor.focus();\n // Place caret at end.\n const range = document.createRange();\n range.selectNodeContents(editor);\n range.collapse(false);\n const sel = window.getSelection();\n sel?.removeAllRanges();\n sel?.addRange(range);\n });\n // `source` is part of the API surface so the caller can pass it\n // through unconditionally; the position math doesn't need it\n // today but a future per-image-bound clamp would.\n void source;\n },\n close(): void {\n activeShape = null;\n editor.style.display = 'none';\n editor.blur();\n },\n destroy(): void {\n editor.removeEventListener('input', onInput);\n editor.removeEventListener('keydown', onKeyDown);\n document.removeEventListener('pointerdown', onPointerDownOutside, true);\n editor.remove();\n },\n };\n}\n\nfunction transformOriginFor(align: 'left' | 'center' | 'right'): string {\n switch (align) {\n case 'left':\n return 'top left';\n case 'center':\n return 'top center';\n case 'right':\n return 'top right';\n }\n}\n","/**\n * Drawing-tool gesture factories. Each function builds the\n * pointer-drag handlers for one tool (rect, ellipse, arrow, freehand,\n * highlight, plus body-move for the select tool). Text and select\n * dispatch are handled in `mount.ts` because they don't fit the\n * \"drag-to-create\" shape these factories codify.\n *\n * Each gesture:\n * 1. Mints a fresh shape id and creates an in-progress shape.\n * 2. On every coalesced pointermove, the shape's geometry is\n * updated and rendered into the live canvas (caller-supplied).\n * 3. On commit (`pointerup`), the shape is added to the store and\n * a `commit` event is emitted so the editor history snapshots.\n * A degenerate shape (zero-extent rect, single-tap freehand) is\n * dropped instead of committed — the user obviously didn't mean\n * to draw anything.\n */\n\nimport {\n type AnnotateState,\n type ArrowShape,\n addShape,\n decimatePoints,\n type EllipseShape,\n type FreehandShape,\n HIGHLIGHT_DEFAULT_COLOR,\n HIGHLIGHT_DEFAULT_STROKE,\n type HighlightShape,\n mintShapeId,\n normaliseRectExtent,\n type Point,\n type RectShape,\n type Shape,\n type Store,\n selectShape,\n} from '@magicpages/kalotyp-core';\nimport type { DragHandlers } from './pointer-drag.js';\n\n/**\n * Shift-modifier constraint helpers. Three flavours, all returning\n * the constrained image-space end-point:\n *\n * - `constrainSquare`: rect/ellipse drag uses the larger absolute\n * delta on both axes so the resulting box is a square (and thus\n * the inscribed ellipse is a circle).\n * - `constrainAxisOrDiagonal`: arrow drag snaps to the nearest of\n * eight directions (4 cardinal + 4 diagonal). Length matches the\n * user's pointer distance projected onto the chosen axis.\n * - `constrainStroke`: freehand/highlight strokes lock to whichever\n * axis the cursor moved further along, so a quick shift-drag\n * draws a straight horizontal or vertical line.\n *\n * Industry convention across Figma / Sketch / Photoshop / Pintura.\n */\nfunction constrainSquare(start: Point, end: Point): Point {\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n const size = Math.max(Math.abs(dx), Math.abs(dy));\n const sx = dx === 0 ? 1 : Math.sign(dx);\n const sy = dy === 0 ? 1 : Math.sign(dy);\n return { x: start.x + sx * size, y: start.y + sy * size };\n}\n\nfunction constrainAxisOrDiagonal(start: Point, end: Point): Point {\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n const len = Math.sqrt(dx * dx + dy * dy);\n if (len === 0) return start;\n // Snap angle to nearest 45° increment (8 compass directions).\n const angle = Math.atan2(dy, dx);\n const snapped = Math.round(angle / (Math.PI / 4)) * (Math.PI / 4);\n return { x: start.x + Math.cos(snapped) * len, y: start.y + Math.sin(snapped) * len };\n}\n\nfunction constrainStroke(start: Point, end: Point): Point {\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n if (Math.abs(dx) >= Math.abs(dy)) return { x: end.x, y: start.y };\n return { x: start.x, y: end.y };\n}\n\nexport interface ToolGestureContext {\n readonly store: Store<AnnotateState>;\n /** Project a raw client-space pointer to image-space pixels. */\n toImageSpace(clientPoint: { clientX: number; clientY: number }): Point;\n /** Update the live canvas with the in-progress shape. */\n setLiveShape(shape: Shape | null): void;\n /** Emit the editor's history-commit signal. */\n commit(): void;\n}\n\nexport function startRectGesture(ctx: ToolGestureContext, origin: PointerEvent): DragHandlers {\n const startImage = ctx.toImageSpace(origin);\n const state = ctx.store.get();\n const { id, nextShapeNumber } = mintShapeId(state);\n let lastImage = startImage;\n return {\n onMove(point) {\n const raw = ctx.toImageSpace(point);\n // Shift constrains to a square (and thus the inscribed ellipse\n // would be a circle for the ellipse tool).\n lastImage = point.shiftKey ? constrainSquare(startImage, raw) : raw;\n const draft: RectShape = {\n id,\n kind: 'rect',\n x: startImage.x,\n y: startImage.y,\n width: lastImage.x - startImage.x,\n height: lastImage.y - startImage.y,\n strokeColor: state.currentStyle.color,\n strokeWidth: state.currentStyle.strokeWidth,\n fillColor: state.currentStyle.fillColor,\n };\n ctx.setLiveShape(draft);\n },\n onCommit() {\n const extent = normaliseRectExtent({\n x: startImage.x,\n y: startImage.y,\n width: lastImage.x - startImage.x,\n height: lastImage.y - startImage.y,\n });\n ctx.setLiveShape(null);\n // Drop zero-extent gestures (a click that didn't drag).\n if (extent.width < 2 || extent.height < 2) return;\n const shape: RectShape = {\n id,\n kind: 'rect',\n ...extent,\n strokeColor: state.currentStyle.color,\n strokeWidth: state.currentStyle.strokeWidth,\n fillColor: state.currentStyle.fillColor,\n };\n ctx.store.update((current) => ({ ...addShape(current, shape), nextShapeNumber }));\n ctx.commit();\n },\n onCancel() {\n ctx.setLiveShape(null);\n },\n };\n}\n\nexport function startEllipseGesture(ctx: ToolGestureContext, origin: PointerEvent): DragHandlers {\n const startImage = ctx.toImageSpace(origin);\n const state = ctx.store.get();\n const { id, nextShapeNumber } = mintShapeId(state);\n let lastImage = startImage;\n return {\n onMove(point) {\n const raw = ctx.toImageSpace(point);\n // Shift constrains the bounding box to a square so the\n // inscribed ellipse becomes a circle.\n lastImage = point.shiftKey ? constrainSquare(startImage, raw) : raw;\n const draft: EllipseShape = {\n id,\n kind: 'ellipse',\n x: startImage.x,\n y: startImage.y,\n width: lastImage.x - startImage.x,\n height: lastImage.y - startImage.y,\n strokeColor: state.currentStyle.color,\n strokeWidth: state.currentStyle.strokeWidth,\n fillColor: state.currentStyle.fillColor,\n };\n ctx.setLiveShape(draft);\n },\n onCommit() {\n const extent = normaliseRectExtent({\n x: startImage.x,\n y: startImage.y,\n width: lastImage.x - startImage.x,\n height: lastImage.y - startImage.y,\n });\n ctx.setLiveShape(null);\n if (extent.width < 2 || extent.height < 2) return;\n const shape: EllipseShape = {\n id,\n kind: 'ellipse',\n ...extent,\n strokeColor: state.currentStyle.color,\n strokeWidth: state.currentStyle.strokeWidth,\n fillColor: state.currentStyle.fillColor,\n };\n ctx.store.update((current) => ({ ...addShape(current, shape), nextShapeNumber }));\n ctx.commit();\n },\n onCancel() {\n ctx.setLiveShape(null);\n },\n };\n}\n\nexport function startArrowGesture(ctx: ToolGestureContext, origin: PointerEvent): DragHandlers {\n const startImage = ctx.toImageSpace(origin);\n const state = ctx.store.get();\n const { id, nextShapeNumber } = mintShapeId(state);\n let lastImage = startImage;\n return {\n onMove(point) {\n const raw = ctx.toImageSpace(point);\n // Shift snaps arrow direction to the nearest 45° increment\n // (4 cardinal + 4 diagonal). Length follows the projection.\n lastImage = point.shiftKey ? constrainAxisOrDiagonal(startImage, raw) : raw;\n const draft: ArrowShape = {\n id,\n kind: 'arrow',\n x1: startImage.x,\n y1: startImage.y,\n x2: lastImage.x,\n y2: lastImage.y,\n color: state.currentStyle.color,\n strokeWidth: state.currentStyle.strokeWidth,\n };\n ctx.setLiveShape(draft);\n },\n onCommit() {\n ctx.setLiveShape(null);\n const dx = lastImage.x - startImage.x;\n const dy = lastImage.y - startImage.y;\n // Arrow needs at least a few image-space pixels of length to\n // be meaningful; otherwise it's a click.\n if (dx * dx + dy * dy < 16) return;\n const shape: ArrowShape = {\n id,\n kind: 'arrow',\n x1: startImage.x,\n y1: startImage.y,\n x2: lastImage.x,\n y2: lastImage.y,\n color: state.currentStyle.color,\n strokeWidth: state.currentStyle.strokeWidth,\n };\n ctx.store.update((current) => ({ ...addShape(current, shape), nextShapeNumber }));\n ctx.commit();\n },\n onCancel() {\n ctx.setLiveShape(null);\n },\n };\n}\n\nexport function startFreehandGesture(\n ctx: ToolGestureContext,\n origin: PointerEvent,\n options: { kind: 'freehand' | 'highlight' },\n): DragHandlers {\n const startImage = ctx.toImageSpace(origin);\n const state = ctx.store.get();\n const { id, nextShapeNumber } = mintShapeId(state);\n /**\n * Two parallel point streams:\n *\n * - `freePoints` — the natural freehand path, recorded every move\n * regardless of shift state. Used when shift is released so a\n * user can press-and-release shift mid-stroke without losing\n * earlier curvy segments.\n * - The render path is computed per-frame: while shift is held\n * we render a straight axis-locked line from `startImage` to\n * the projected end-point; otherwise we render the full\n * freehand path.\n *\n * On commit we choose: if shift was held at release, the persisted\n * shape is just the two endpoints; otherwise the decimated free path.\n */\n const freePoints: Point[] = [startImage];\n let lastWasShift = false;\n let lastConstrainedEnd: Point = startImage;\n const isHighlight = options.kind === 'highlight';\n const color = isHighlight ? HIGHLIGHT_DEFAULT_COLOR : state.currentStyle.color;\n const strokeWidth = isHighlight ? HIGHLIGHT_DEFAULT_STROKE : state.currentStyle.strokeWidth;\n\n function paint(points: ReadonlyArray<Point>): void {\n const draft: FreehandShape | HighlightShape = {\n id,\n kind: options.kind,\n points,\n color,\n strokeWidth,\n };\n ctx.setLiveShape(draft);\n }\n\n return {\n onMove(point) {\n const raw = ctx.toImageSpace(point);\n lastWasShift = point.shiftKey;\n if (point.shiftKey) {\n // Lock the stroke to a horizontal/vertical line from the\n // gesture's start to the current pointer projection.\n lastConstrainedEnd = constrainStroke(startImage, raw);\n paint([startImage, lastConstrainedEnd]);\n } else {\n freePoints.push(raw);\n paint(freePoints);\n }\n },\n onCommit() {\n ctx.setLiveShape(null);\n const finalPoints: ReadonlyArray<Point> = lastWasShift\n ? [startImage, lastConstrainedEnd]\n : decimatePoints(freePoints);\n if (finalPoints.length < 2) return;\n // Reject zero-length shift-clicks.\n if (finalPoints.length === 2) {\n const a = finalPoints[0];\n const b = finalPoints[1];\n if (a && b) {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n if (dx * dx + dy * dy < 4) return;\n }\n }\n const shape: FreehandShape | HighlightShape = {\n id,\n kind: options.kind,\n points: finalPoints,\n color,\n strokeWidth,\n };\n ctx.store.update((current) => ({ ...addShape(current, shape), nextShapeNumber }));\n ctx.commit();\n },\n onCancel() {\n ctx.setLiveShape(null);\n },\n };\n}\n\n/**\n * Body-drag (move) gesture used by the select tool when the user\n * presses on an already-selected shape and drags. Translates the\n * shape by the per-frame delta; commits on pointerup. The caller\n * supplies the `translate` and `replace` closures so this stays\n * decoupled from the store-write specifics.\n */\nexport function startBodyMoveGesture(\n ctx: ToolGestureContext,\n origin: PointerEvent,\n shapeId: string,\n initialShape: Shape,\n translate: (shape: Shape, dx: number, dy: number) => Shape,\n replace: (shape: Shape) => void,\n): DragHandlers {\n const startImage = ctx.toImageSpace(origin);\n ctx.store.update((current) => selectShape(current, shapeId));\n return {\n onMove(point) {\n const here = ctx.toImageSpace(point);\n const moved = translate(initialShape, here.x - startImage.x, here.y - startImage.y);\n replace(moved);\n },\n onCommit() {\n ctx.commit();\n },\n onCancel() {\n replace(initialShape);\n },\n };\n}\n","/**\n * Mount the annotation plugin's stage UI and wire up:\n * - the three layered canvases (image / shapes / live);\n * - the bottom panel (tool toolbar + style controls);\n * - pointer dispatch (drawing tools vs select tool);\n * - selection handles + per-handle resize gestures;\n * - the inline text editor;\n * - keyboard shortcuts (Delete/Backspace to remove the selected\n * shape; Esc to deselect).\n *\n * The mount keeps two pieces of derived state at module scope:\n *\n * - `viewport` — the current letterbox of the upstream-baked source\n * into the stage. Recomputed on every stage resize.\n * - `liveShape` — the in-progress shape during a draw or move\n * gesture. `null` when no gesture is active.\n */\n\nimport {\n type AnnotateState,\n type AnnotateTool,\n addShape,\n boundingBoxOf,\n computeViewport,\n createCenteredShape,\n deleteShape,\n isKeyboardPlaceableKind,\n mintShapeId,\n type Point,\n pickShape,\n pointDisplayToImage,\n replaceShape,\n type Shape,\n type SourceImage,\n type Store,\n selectShape,\n setActiveTool,\n setStyle,\n TEXT_DEFAULT_FONT_SIZE,\n type TextShape,\n translateShape,\n type Viewport,\n type ViewportController,\n} from '@magicpages/kalotyp-core';\nimport { buildCoordInputs } from './coord-inputs.js';\nimport { type AnnotatePanel, buildAnnotatePanel } from './panel.js';\nimport { attachPointerDrag, clientToElement, type DragHandlers } from './pointer-drag.js';\nimport { paintImageLayer, paintLiveLayer, paintMarqueeLayer, paintShapesLayer } from './render.js';\nimport { buildSelectionLayer, selectedShapeOf } from './selection.js';\nimport { buildAnnotateStage } from './stage.js';\nimport { buildTextEditor } from './text-editor.js';\nimport {\n startArrowGesture,\n startBodyMoveGesture,\n startEllipseGesture,\n startFreehandGesture,\n startRectGesture,\n type ToolGestureContext,\n} from './tools.js';\n\nconst STAGE_PADDING_PX = 32;\n\nexport interface MountAnnotateOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly store: Store<AnnotateState>;\n /** Editor-level zoom + pan controller. Optional in jsdom tests. */\n readonly viewport?: ViewportController;\n /** Called after each user-meaningful annotation mutation. */\n readonly onCommit?: () => void;\n /**\n * Optional live-region announcer. The annotate plugin\n * uses it for state changes that don't move keyboard focus —\n * notably Esc-deselect, where the screen reader would otherwise\n * have no cue that the selection went away.\n */\n readonly onAnnounce?: (message: string) => void;\n}\n\nexport interface MountAnnotateHandle {\n destroy(): void;\n}\n\nexport function mountAnnotateUtility(options: MountAnnotateOptions): MountAnnotateHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? (() => {});\n const announce = options.onAnnounce ?? (() => {});\n\n const stage = buildAnnotateStage();\n stageHost.appendChild(stage.container);\n\n let viewport: Viewport = computeViewport(\n { width: 1, height: 1, padding: STAGE_PADDING_PX },\n { width: source.width, height: source.height },\n );\n let liveShape: Shape | null = null;\n let liveMarquee: { x: number; y: number; width: number; height: number } | null = null;\n\n function recomputeViewport(): void {\n const rect = stage.container.getBoundingClientRect();\n const stageDims = { width: rect.width, height: rect.height, padding: STAGE_PADDING_PX };\n const imageSize = { width: source.width, height: source.height };\n viewport = controller\n ? controller.computeViewport(stageDims, imageSize)\n : computeViewport(stageDims, imageSize);\n }\n\n function paintAll(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n paintImageLayer(stage.imageCanvas, source, rect.width, rect.height, viewport);\n paintShapesLayer(stage.shapesCanvas, store.get().shapes, rect.width, rect.height, viewport);\n paintLiveLayer(stage.liveCanvas, liveShape, rect.width, rect.height, viewport);\n selectionLayer.update(selectedShapeOf(store.get()), viewport);\n }\n\n function paintShapes(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n paintShapesLayer(stage.shapesCanvas, store.get().shapes, rect.width, rect.height, viewport);\n }\n\n function paintLive(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n if (liveMarquee !== null) {\n paintMarqueeLayer(stage.liveCanvas, liveMarquee, rect.width, rect.height, viewport);\n } else {\n paintLiveLayer(stage.liveCanvas, liveShape, rect.width, rect.height, viewport);\n }\n }\n\n function setLiveShape(shape: Shape | null): void {\n liveShape = shape;\n liveMarquee = null;\n paintLive();\n }\n\n function setLiveMarquee(\n rect: { x: number; y: number; width: number; height: number } | null,\n ): void {\n liveMarquee = rect;\n liveShape = null;\n paintLive();\n }\n\n // Project a raw client-space pointer to the upstream-baked source's\n // image-space pixels. Used by every gesture factory.\n function toImageSpace(point: { clientX: number; clientY: number }): Point {\n const stagePoint = clientToElement(stage.container, point.clientX, point.clientY);\n return pointDisplayToImage(stagePoint, viewport);\n }\n\n const toolContext: ToolGestureContext = {\n store,\n toImageSpace,\n setLiveShape,\n commit,\n };\n\n // ----- Selection layer (handles + per-handle resize) -----\n const selectionLayer = buildSelectionLayer({\n host: stage.handlesLayer,\n stageElement: stage.container,\n toolContext,\n getViewport: () => viewport,\n });\n\n // ----- Inline text editor -----\n const textEditor = buildTextEditor({\n host: stage.textOverlay,\n onInput: (text) => {\n const selected = selectedShapeOf(store.get());\n if (selected?.kind !== 'text') return;\n store.update((current) => replaceShape(current, { ...selected, text }));\n },\n onCommit: () => {\n const selected = selectedShapeOf(store.get());\n textEditor.close();\n // Drop the text shape entirely if the user committed an empty\n // string — a blank text shape has no representation.\n if (selected?.kind === 'text' && selected.text.trim().length === 0) {\n store.update((current) => deleteShape(current, selected.id));\n }\n commit();\n // Switch back to select so subsequent clicks pick existing\n // shapes rather than spawning a fresh empty text.\n store.update((current) => setActiveTool(current, 'select'));\n },\n onCancel: () => {\n const selected = selectedShapeOf(store.get());\n textEditor.close();\n // If the user cancelled an empty text (the click that created\n // it), drop the shape so we don't pollute the list.\n if (selected?.kind === 'text' && selected.text.length === 0) {\n store.update((current) => deleteShape(current, selected.id));\n }\n store.update((current) => setActiveTool(current, 'select'));\n },\n });\n\n // ----- Pointer dispatch on the hit area -----\n const removeHitDrag = attachPointerDrag(stage.hitArea, (event) => {\n const state = store.get();\n switch (state.activeTool) {\n case 'select':\n return startSelectGesture(state, event);\n case 'rect':\n return startRectGesture(toolContext, event);\n case 'ellipse':\n return startEllipseGesture(toolContext, event);\n case 'arrow':\n return startArrowGesture(toolContext, event);\n case 'freehand':\n return startFreehandGesture(toolContext, event, { kind: 'freehand' });\n case 'highlight':\n return startFreehandGesture(toolContext, event, { kind: 'highlight' });\n case 'text': {\n // Text doesn't use the drag pipeline — handle it inline.\n startTextGesture(event);\n return null;\n }\n default:\n return null;\n }\n });\n\n function startSelectGesture(state: AnnotateState, event: PointerEvent): DragHandlers | null {\n const image = toImageSpace(event);\n const picked = pickShape(state.shapes, image);\n if (!picked) {\n // Empty space → start a marquee. The marquee renders as a\n // dashed rectangle on the live canvas; on commit, it picks\n // the topmost shape whose bounding box intersects the\n // marquee. A no-move tap (start === end) deselects.\n return startMarqueeGesture(event);\n }\n // If the picked shape is already selected, drag it. Otherwise\n // select it first; the same drag continues through the move.\n if (state.selectedId !== picked.id) {\n store.update((current) => selectShape(current, picked.id));\n }\n return startBodyMoveGesture(toolContext, event, picked.id, picked, translateShape, (next) =>\n store.update((current) => replaceShape(current, next)),\n );\n }\n\n function startMarqueeGesture(event: PointerEvent): DragHandlers {\n const startImage = toImageSpace(event);\n let lastImage = startImage;\n return {\n onMove(point) {\n lastImage = toImageSpace(point);\n setLiveMarquee({\n x: startImage.x,\n y: startImage.y,\n width: lastImage.x - startImage.x,\n height: lastImage.y - startImage.y,\n });\n },\n onCommit() {\n setLiveMarquee(null);\n // No-move click: just deselect.\n const dx = lastImage.x - startImage.x;\n const dy = lastImage.y - startImage.y;\n if (Math.abs(dx) < 2 && Math.abs(dy) < 2) {\n store.update((current) => selectShape(current, null));\n return;\n }\n const marquee = normaliseExtent({\n x: startImage.x,\n y: startImage.y,\n width: dx,\n height: dy,\n });\n const hit = topmostShapeIntersectingMarquee(store.get().shapes, marquee);\n store.update((current) => selectShape(current, hit?.id ?? null));\n },\n onCancel() {\n setLiveMarquee(null);\n },\n };\n }\n\n function startTextGesture(event: PointerEvent): void {\n const state = store.get();\n const image = toImageSpace(event);\n const { id, nextShapeNumber } = mintShapeId(state);\n const shape: TextShape = {\n id,\n kind: 'text',\n x: image.x,\n y: image.y,\n text: '',\n fontSize: state.currentStyle.fontSize ?? TEXT_DEFAULT_FONT_SIZE,\n color: state.currentStyle.color,\n textAlign: 'left',\n };\n store.update((current) => ({ ...addShape(current, shape), nextShapeNumber }));\n // The shape is selected by `addShape`; open the editor on it.\n textEditor.open(shape, viewport, source);\n }\n\n /**\n * Place the active drawing tool's default-sized shape at image\n * centre and select it. The keyboard-only equivalent of dragging a\n * shape onto the canvas. For the text tool\n * the inline editor opens immediately so the keyboard user can\n * type without further navigation; the text-overlay div is\n * already in the focus trap, so the editor receives focus\n * naturally. For rect / ellipse / arrow the shape is selected and\n * the coordinate inputs become available below the style row.\n */\n function insertDefaultAtCenter(): void {\n const state = store.get();\n const tool: AnnotateTool = state.activeTool;\n if (tool === 'select' || !isKeyboardPlaceableKind(tool)) return;\n const { id, nextShapeNumber } = mintShapeId(state);\n const shape = createCenteredShape(tool, {\n imageSize: { width: source.width, height: source.height },\n style: state.currentStyle,\n id,\n });\n store.update((current) => ({ ...addShape(current, shape), nextShapeNumber }));\n if (shape.kind === 'text') {\n // Open the inline editor immediately so the user can start\n // typing. Without this the user would have to find another\n // affordance to enter the text — defeats the point.\n textEditor.open(shape, viewport, source);\n announce('Text annotation placed at centre. Type to enter text.');\n return;\n }\n announce(\n `${labelForKind(shape.kind)} placed at centre. Use arrow keys to nudge, or edit coordinates below.`,\n );\n // Move keyboard focus straight into the first coordinate input\n // so a keyboard-only user doesn't have to hunt for the next\n // affordance after Insert. The store-subscription update has\n // already painted the inputs by the time the click handler\n // returns, but the layout pass may not be settled — defer one\n // animation frame so the input is hit-testable before we focus.\n requestAnimationFrame(() => {\n const firstInput = coordInputs.container.querySelector<HTMLInputElement>(\n '.kalotyp-annotate-coords-input',\n );\n firstInput?.focus();\n firstInput?.select();\n });\n }\n\n // ----- Coordinate inputs (keyboard-only positioning) -----\n // Built first so the panel can host the row in its DOM rhythm. The\n // row is store-free; each typed value commit hands the new shape\n // back to the mount layer, which writes it via replaceShape and\n // emits a history-commit.\n const coordInputs = buildCoordInputs({\n onShapeChanged: (shape) => {\n store.update((current) => replaceShape(current, shape));\n commit();\n },\n });\n\n // ----- Panel -----\n const initialState = store.get();\n const panel: AnnotatePanel = buildAnnotatePanel({\n initialTool: initialState.activeTool,\n initialStyle: initialState.currentStyle,\n canDelete: initialState.selectedId !== null,\n coordInputs: coordInputs.container,\n onSelectTool: (tool) => store.update((current) => setActiveTool(current, tool)),\n onColorChange: (color) => {\n store.update((current) => {\n let next = setStyle(current, { color });\n const selected = selectedShapeOf(current);\n if (selected) next = replaceShape(next, applyColorToShape(selected, color));\n return next;\n });\n commit();\n },\n onStrokeWidthChange: (width) => {\n store.update((current) => {\n let next = setStyle(current, { strokeWidth: width });\n const selected = selectedShapeOf(current);\n if (selected) next = replaceShape(next, applyStrokeWidthToShape(selected, width));\n return next;\n });\n commit();\n },\n onDeleteSelected: () => {\n const id = store.get().selectedId;\n if (!id) return;\n store.update((current) => deleteShape(current, id));\n commit();\n },\n onInsertAtCenter: () => insertDefaultAtCenter(),\n });\n utilHost.appendChild(panel.container);\n\n // ----- Initial paint + observers -----\n recomputeViewport();\n paintAll();\n\n const resizeObserver = new ResizeObserver(() => {\n recomputeViewport();\n paintAll();\n });\n resizeObserver.observe(stage.container);\n\n // Repaint everything on viewport change. Selection handles +\n // text-editor position read from the same viewport so they pick up\n // zoom/pan automatically. RAF-coalesce a burst of emissions into one\n // paint per frame.\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n recomputeViewport();\n paintAll();\n });\n });\n\n // Repaint shapes + handles on store changes; keep panel in sync;\n // open/close the text editor when the selected text shape changes.\n let lastShapes = store.get().shapes;\n let lastSelected = store.get().selectedId;\n let lastTool = store.get().activeTool;\n let lastStyle = store.get().currentStyle;\n\n const unsubscribe = store.subscribe((next) => {\n const shapesChanged = next.shapes !== lastShapes;\n const selectionChanged = next.selectedId !== lastSelected;\n if (shapesChanged) {\n lastShapes = next.shapes;\n paintShapes();\n }\n if (selectionChanged) {\n lastSelected = next.selectedId;\n panel.setCanDelete(next.selectedId !== null);\n }\n if (next.activeTool !== lastTool) {\n lastTool = next.activeTool;\n panel.setActiveTool(next.activeTool);\n }\n if (next.currentStyle !== lastStyle) {\n lastStyle = next.currentStyle;\n panel.setStyle(next.currentStyle);\n }\n selectionLayer.update(selectedShapeOf(next), viewport);\n // Keep the per-selection coordinate inputs in sync.\n // We update on either selection or geometry change so a pointer\n // drag updates the typed values too — the keyboard and pointer\n // paths see the same source of truth, in both directions.\n if (selectionChanged || shapesChanged) {\n coordInputs.updateForShape(selectedShapeOf(next));\n }\n });\n\n // Delete + Esc + arrow-key keyboard handling. Lives on `document`\n // while the plugin is mounted; the editor's broader undo/redo\n // handler already filters out editable targets, so we follow the\n // same rule.\n const onKeyDown = (event: KeyboardEvent): void => {\n const target = event.target as Element | null;\n if (isEditableTarget(target)) return;\n const state = store.get();\n if (event.key === 'Escape') {\n // While an annotation is selected, Esc deselects (and stops the\n // editor-level Esc-to-close handler from firing on top).\n // Without a selection Esc falls through to the editor.\n if (state.selectedId !== null) {\n event.preventDefault();\n event.stopPropagation();\n store.update((current) => selectShape(current, null));\n // Announce the deselection — focus doesn't move,\n // so without this a screen reader user has no cue that\n // anything changed.\n announce('Selection cleared.');\n }\n return;\n }\n if (event.key === 'Delete' || event.key === 'Backspace') {\n if (state.selectedId === null) return;\n event.preventDefault();\n const id = state.selectedId;\n store.update((current) => deleteShape(current, id));\n commit();\n return;\n }\n // Arrow-key shape nudging. With a shape\n // selected, the four arrow keys translate it by 1px in image\n // space. Holding Shift snaps to a 10× step the way professional\n // editors handle nudge, so a keyboard-only user can travel\n // distance quickly without losing precision. The keys only fire\n // when no input/textarea/contenteditable is focused, so the\n // user is free to step through coordinate inputs without the\n // arrow keys hijacking input-internal cursor movement.\n if (\n event.key === 'ArrowUp' ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowLeft' ||\n event.key === 'ArrowRight'\n ) {\n const selected = selectedShapeOf(state);\n if (!selected) return;\n // Don't nudge when modifier keys other than Shift are held —\n // OS-level shortcuts (Ctrl/Alt/Meta + Arrow) shouldn't be\n // consumed by the editor.\n if (event.ctrlKey || event.altKey || event.metaKey) return;\n const step = event.shiftKey ? 10 : 1;\n const dx = event.key === 'ArrowLeft' ? -step : event.key === 'ArrowRight' ? step : 0;\n const dy = event.key === 'ArrowUp' ? -step : event.key === 'ArrowDown' ? step : 0;\n event.preventDefault();\n const moved = translateShape(selected, dx, dy);\n store.update((current) => replaceShape(current, moved));\n commit();\n }\n };\n // Capture-phase so the plugin sees Esc / Delete before the editor's\n // document-level shortcut handler. The plugin only consumes the\n // event (with stopPropagation) when it actually acts on it; events\n // it ignores fall through to the editor.\n document.addEventListener('keydown', onKeyDown, true);\n\n return {\n destroy() {\n document.removeEventListener('keydown', onKeyDown, true);\n removeHitDrag();\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n textEditor.destroy();\n selectionLayer.destroy();\n stage.container.remove();\n panel.container.remove();\n },\n };\n}\n\n/**\n * Apply a colour change to an existing shape. Each shape kind has its\n * own colour-bearing field — text uses `color`, rect/ellipse use\n * `strokeColor` (and never overwrite `fillColor` from the style row),\n * arrow/freehand/highlight use `color`. This is the small price the\n * discriminated union charges us for cross-cutting style edits.\n */\nfunction applyColorToShape(shape: Shape, color: string): Shape {\n switch (shape.kind) {\n case 'text':\n return { ...shape, color };\n case 'rect':\n case 'ellipse':\n return { ...shape, strokeColor: color };\n case 'arrow':\n case 'freehand':\n case 'highlight':\n return { ...shape, color };\n }\n}\n\nfunction applyStrokeWidthToShape(shape: Shape, strokeWidth: number): Shape {\n switch (shape.kind) {\n case 'text':\n // Text doesn't have a stroke width; treat the slider as a\n // \"size scale\" that nudges fontSize in 8 px increments.\n return { ...shape, fontSize: Math.max(8, Math.round(strokeWidth * 4)) };\n case 'rect':\n case 'ellipse':\n case 'arrow':\n case 'freehand':\n case 'highlight':\n return { ...shape, strokeWidth };\n }\n}\n\nfunction normaliseExtent(extent: { x: number; y: number; width: number; height: number }): {\n x: number;\n y: number;\n width: number;\n height: number;\n} {\n let { x, y, width, height } = extent;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n return { x, y, width, height };\n}\n\n/**\n * Topmost (last drawn) shape whose bounding box intersects the\n * marquee rectangle. Returns `undefined` when nothing intersects.\n * Multi-select is out of scope for v1 (per Phase 3 brief), so\n * marquee resolves to a single selection.\n */\nfunction topmostShapeIntersectingMarquee(\n shapes: ReadonlyArray<Shape>,\n marquee: { x: number; y: number; width: number; height: number },\n): Shape | undefined {\n for (let i = shapes.length - 1; i >= 0; i--) {\n const shape = shapes[i];\n if (!shape) continue;\n const bbox = boundingBoxOf(shape);\n if (rectsIntersect(bbox, marquee)) return shape;\n }\n return undefined;\n}\n\nfunction rectsIntersect(\n a: { x: number; y: number; width: number; height: number },\n b: { x: number; y: number; width: number; height: number },\n): boolean {\n return !(\n a.x + a.width < b.x ||\n b.x + b.width < a.x ||\n a.y + a.height < b.y ||\n b.y + b.height < a.y\n );\n}\n\nfunction isEditableTarget(target: Element | null): boolean {\n if (!target) return false;\n const tag = target.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true;\n return (target as HTMLElement).isContentEditable === true;\n}\n\n/**\n * Human-readable label for the keyboard-placement live-region\n * announcement. Each kind reads as a noun in the announcement\n * sentence — \"Rectangle placed at centre. Use arrow keys…\".\n */\nfunction labelForKind(kind: 'rect' | 'ellipse' | 'arrow' | 'text'): string {\n switch (kind) {\n case 'rect':\n return 'Rectangle';\n case 'ellipse':\n return 'Ellipse';\n case 'arrow':\n return 'Arrow';\n case 'text':\n return 'Text annotation';\n }\n}\n","import {\n type AnnotateState,\n bakeAnnotate,\n initialAnnotateState,\n type UtilityPlugin,\n} from '@magicpages/kalotyp-core';\nimport { mountAnnotateUtility } from './mount.js';\n\nexport interface AnnotatePluginOptions {\n /**\n * Where the plugin's panel UI mounts. The plugin's stage UI is\n * mounted into the `host` argument of the standard plugin `mount()`\n * call. Same panel-host-as-closure pattern the other plugins use\n * (see `crop/plugin.ts`).\n */\n readonly panelHost: HTMLElement;\n}\n\n/**\n * Build the annotation `UtilityPlugin` instance for one editor\n * session. Each `openDefaultEditor` call gets a fresh plugin closing\n * over its own panel host.\n *\n * The chain position (annotate before resize) is the editor's\n * concern; this factory just supplies the bake.\n */\nexport function createAnnotatePlugin(options: AnnotatePluginOptions): UtilityPlugin<AnnotateState> {\n return {\n id: 'annotate',\n init: (ctx) =>\n initialAnnotateState({ imageSize: { width: ctx.source.width, height: ctx.source.height } }),\n mount(stageHost, ctx, store) {\n const handle = mountAnnotateUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: 'annotate' }),\n onAnnounce: (message) => ctx.bus.emit('announce', { message }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: (state, source) => bakeAnnotate({ shapes: state.shapes }, source),\n };\n}\n","import {\n type CornerHandle,\n type EdgeHandle,\n type Rect,\n rectImageToDisplay,\n type Viewport,\n} from '@magicpages/kalotyp-core';\n\n/**\n * Position corner anchors, edge handles, and body hit-area to match the crop rect.\n *\n * Corner buttons aren't positioned directly: they live inside an anchor that sits at the\n * corner point, and host CSS (Ghost's `pintura.css` or our `crop.css`) shifts the button\n * by `-20px` so its 20×20 bracket pseudo-element aligns with the corner. Edges are inset\n * by 12px on each end so they don't overlap the corner brackets.\n */\nexport interface PositionHandlesInput {\n readonly cropRectImage: Rect;\n readonly viewport: Viewport;\n readonly cornerAnchors: Readonly<Record<CornerHandle, HTMLElement>>;\n readonly edgeHandles: Readonly<Record<EdgeHandle, HTMLElement>>;\n readonly bodyHitArea: HTMLElement;\n}\n\nconst CORNER_INSET = 12;\n\nexport function positionHandles(input: PositionHandlesInput): void {\n const display = rectImageToDisplay(input.cropRectImage, input.viewport);\n const { cornerAnchors, edgeHandles, bodyHitArea } = input;\n\n bodyHitArea.style.left = `${display.x}px`;\n bodyHitArea.style.top = `${display.y}px`;\n bodyHitArea.style.width = `${display.width}px`;\n bodyHitArea.style.height = `${display.height}px`;\n\n setAnchor(cornerAnchors.tl, display.x, display.y);\n setAnchor(cornerAnchors.tr, display.x + display.width, display.y);\n setAnchor(cornerAnchors.bl, display.x, display.y + display.height);\n setAnchor(cornerAnchors.br, display.x + display.width, display.y + display.height);\n\n const horizontalLength = Math.max(0, display.width - CORNER_INSET * 2);\n const verticalLength = Math.max(0, display.height - CORNER_INSET * 2);\n\n setHorizontalEdge(edgeHandles.t, display.x + CORNER_INSET, display.y, horizontalLength);\n setHorizontalEdge(\n edgeHandles.b,\n display.x + CORNER_INSET,\n display.y + display.height,\n horizontalLength,\n );\n setVerticalEdge(edgeHandles.l, display.x, display.y + CORNER_INSET, verticalLength);\n setVerticalEdge(\n edgeHandles.r,\n display.x + display.width,\n display.y + CORNER_INSET,\n verticalLength,\n );\n}\n\nfunction setAnchor(anchor: HTMLElement, x: number, y: number): void {\n anchor.style.left = `${x}px`;\n anchor.style.top = `${y}px`;\n}\n\nfunction setHorizontalEdge(handle: HTMLElement, x: number, y: number, length: number): void {\n handle.style.left = `${x}px`;\n handle.style.top = `${y}px`;\n handle.style.width = `${length}px`;\n}\n\nfunction setVerticalEdge(handle: HTMLElement, x: number, y: number, length: number): void {\n handle.style.left = `${x}px`;\n handle.style.top = `${y}px`;\n handle.style.height = `${length}px`;\n}\n","import type { Viewport } from '@magicpages/kalotyp-core';\n\n/** Paint the source image onto the canvas at the viewport's display size, DPR-scaled. */\nexport function renderImageCanvas(\n canvas: HTMLCanvasElement,\n source: CanvasImageSource,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const targetW = Math.max(1, Math.round(stageWidth * dpr));\n const targetH = Math.max(1, Math.round(stageHeight * dpr));\n if (canvas.width !== targetW) canvas.width = targetW;\n if (canvas.height !== targetH) canvas.height = targetH;\n canvas.style.width = `${stageWidth}px`;\n canvas.style.height = `${stageHeight}px`;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, stageWidth, stageHeight);\n ctx.drawImage(\n source,\n viewport.displayRect.x,\n viewport.displayRect.y,\n viewport.displayRect.width,\n viewport.displayRect.height,\n );\n}\n","import { type Rect, rectImageToDisplay, type Viewport } from '@magicpages/kalotyp-core';\n\nconst MASK_FILL = 'rgba(0, 0, 0, 0.4)';\n// Halo pattern (wide soft-black under, 1px white over) stays readable on any background\n// without `mix-blend-mode: difference`, which has known Safari bugs over canvas.\nconst OUTLINE_HALO = 'rgba(0, 0, 0, 0.45)';\nconst OUTLINE_HALO_WIDTH = 3;\nconst OUTLINE_STROKE = 'rgba(255, 255, 255, 0.95)';\nconst OUTLINE_WIDTH = 1;\nconst GRID_HALO = 'rgba(0, 0, 0, 0.25)';\nconst GRID_HALO_WIDTH = 2;\nconst GRID_STROKE = 'rgba(255, 255, 255, 0.55)';\nconst GRID_WIDTH = 1;\n\n/** Repaint the overlay: dim mask outside the crop rect, outline, and rule-of-thirds grid. */\nexport function renderOverlayCanvas(\n canvas: HTMLCanvasElement,\n cropRectImage: Rect,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const targetW = Math.max(1, Math.round(stageWidth * dpr));\n const targetH = Math.max(1, Math.round(stageHeight * dpr));\n if (canvas.width !== targetW) canvas.width = targetW;\n if (canvas.height !== targetH) canvas.height = targetH;\n canvas.style.width = `${stageWidth}px`;\n canvas.style.height = `${stageHeight}px`;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, stageWidth, stageHeight);\n\n const display = rectImageToDisplay(cropRectImage, viewport);\n const imageRect = viewport.displayRect;\n\n // Mask is scoped to the image's display rect, not the whole stage — the editor mat\n // outside the image stays clean.\n ctx.save();\n ctx.fillStyle = MASK_FILL;\n ctx.fillRect(imageRect.x, imageRect.y, imageRect.width, imageRect.height);\n ctx.globalCompositeOperation = 'destination-out';\n ctx.fillRect(display.x, display.y, display.width, display.height);\n ctx.restore();\n\n const x = display.x + 0.5;\n const y = display.y + 0.5;\n const w = display.width - 1;\n const h = display.height - 1;\n ctx.strokeStyle = OUTLINE_HALO;\n ctx.lineWidth = OUTLINE_HALO_WIDTH;\n ctx.strokeRect(x, y, w, h);\n ctx.strokeStyle = OUTLINE_STROKE;\n ctx.lineWidth = OUTLINE_WIDTH;\n ctx.strokeRect(x, y, w, h);\n\n drawGridLines(ctx, display, GRID_HALO, GRID_HALO_WIDTH);\n drawGridLines(ctx, display, GRID_STROKE, GRID_WIDTH);\n // Corner visuals are owned by DOM buttons (see build-stage.ts); don't double-draw here.\n}\n\nfunction drawGridLines(\n ctx: CanvasRenderingContext2D,\n display: Rect,\n stroke: string,\n width: number,\n): void {\n ctx.strokeStyle = stroke;\n ctx.lineWidth = width;\n ctx.beginPath();\n for (let i = 1; i < 3; i++) {\n const x = display.x + (display.width * i) / 3;\n const y = display.y + (display.height * i) / 3;\n ctx.moveTo(x, display.y);\n ctx.lineTo(x, display.y + display.height);\n ctx.moveTo(display.x, y);\n ctx.lineTo(display.x + display.width, y);\n }\n ctx.stroke();\n}\n","import type { CropPreset } from '@magicpages/kalotyp-core';\n\nexport interface PresetRowElements {\n readonly container: HTMLDivElement;\n readonly buttons: readonly HTMLButtonElement[];\n}\n\n/** Build the aspect-ratio preset row. `activeIndex` indexes into the visible presets passed in. */\nexport function buildPresetRow(\n visiblePresets: readonly CropPreset[],\n activeIndex: number,\n onSelect: (visibleIndex: number, preset: CropPreset) => void,\n): PresetRowElements {\n const container = document.createElement('div');\n container.className = 'kalotyp-preset-row';\n container.setAttribute('role', 'radiogroup');\n container.setAttribute('aria-label', 'Crop aspect ratio');\n\n const buttons: HTMLButtonElement[] = [];\n visiblePresets.forEach((preset, index) => {\n const [, label] = preset;\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-preset-button';\n button.textContent = label;\n button.setAttribute('role', 'radio');\n button.setAttribute('aria-checked', index === activeIndex ? 'true' : 'false');\n button.dataset.presetIndex = String(index);\n button.addEventListener('click', () => onSelect(index, preset));\n container.appendChild(button);\n buttons.push(button);\n });\n\n return { container, buttons };\n}\n\n/** Update the active state of an existing preset row in place. */\nexport function setActivePresetButton(\n buttons: readonly HTMLButtonElement[],\n activeIndex: number,\n): void {\n buttons.forEach((button, index) => {\n button.setAttribute('aria-checked', index === activeIndex ? 'true' : 'false');\n });\n}\n","import type { CornerHandle, EdgeHandle, HandleDirection } from '@magicpages/kalotyp-core';\n\nexport interface StageElements {\n readonly container: HTMLDivElement;\n readonly imageCanvas: HTMLCanvasElement;\n readonly overlayCanvas: HTMLCanvasElement;\n readonly handlesLayer: HTMLDivElement;\n readonly handles: Readonly<Record<HandleDirection, HTMLButtonElement>>;\n /**\n * Per-corner wrapper providing the positioning context Ghost's pintura.css expects:\n * `[data-direction=tr|bl|br] { left/top: -20px !important }` resolves against this\n * anchor, not the stage. Without it three corner buttons end up off-screen.\n */\n readonly cornerAnchors: Readonly<Record<CornerHandle, HTMLDivElement>>;\n /** Body drag surface — under handles, above canvases in z-order. */\n readonly bodyHitArea: HTMLDivElement;\n}\n\nconst CORNERS: readonly CornerHandle[] = ['tl', 'tr', 'bl', 'br'];\nconst EDGES: readonly EdgeHandle[] = ['t', 'r', 'b', 'l'];\n\n/** Build the interactive crop UI inside the stage. Class names and data-* come from the Ghost contract. */\nexport function buildStageElements(): StageElements {\n const container = document.createElement('div');\n container.className = 'kalotyp-stage-container';\n\n const imageCanvas = document.createElement('canvas');\n imageCanvas.className = 'kalotyp-stage-image';\n imageCanvas.setAttribute('aria-hidden', 'true');\n\n const overlayCanvas = document.createElement('canvas');\n overlayCanvas.className = 'kalotyp-stage-overlay';\n overlayCanvas.setAttribute('aria-hidden', 'true');\n\n const bodyHitArea = document.createElement('div');\n bodyHitArea.className = 'kalotyp-stage-body';\n\n const handlesLayer = document.createElement('div');\n handlesLayer.className = 'kalotyp-handles';\n handlesLayer.setAttribute('role', 'group');\n handlesLayer.setAttribute('aria-label', 'Crop region');\n\n const handles = {} as Record<HandleDirection, HTMLButtonElement>;\n for (const direction of EDGES) {\n const button = createEdgeButton(direction);\n handles[direction] = button;\n handlesLayer.appendChild(button);\n }\n\n const cornerAnchors = {} as Record<CornerHandle, HTMLDivElement>;\n for (const direction of CORNERS) {\n const anchor = document.createElement('div');\n anchor.className = 'kalotyp-corner-anchor';\n anchor.dataset.direction = direction;\n const button = createCornerButton(direction);\n anchor.appendChild(button);\n handles[direction] = button;\n cornerAnchors[direction] = anchor;\n handlesLayer.appendChild(anchor);\n }\n\n container.appendChild(imageCanvas);\n container.appendChild(overlayCanvas);\n container.appendChild(bodyHitArea);\n container.appendChild(handlesLayer);\n\n return {\n container,\n imageCanvas,\n overlayCanvas,\n handlesLayer,\n handles,\n cornerAnchors,\n bodyHitArea,\n };\n}\n\nfunction createCornerButton(direction: CornerHandle): HTMLButtonElement {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-handle';\n button.dataset.shape = 'circle';\n button.dataset.direction = direction;\n button.setAttribute('aria-label', labelFor(direction));\n button.tabIndex = 0;\n return button;\n}\n\nfunction createEdgeButton(direction: EdgeHandle): HTMLButtonElement {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-handle';\n button.dataset.shape = 'edge';\n button.dataset.direction = direction;\n button.setAttribute('aria-label', labelFor(direction));\n button.tabIndex = 0;\n return button;\n}\n\nfunction labelFor(direction: HandleDirection): string {\n switch (direction) {\n case 'tl':\n return 'Top-left crop handle';\n case 'tr':\n return 'Top-right crop handle';\n case 'bl':\n return 'Bottom-left crop handle';\n case 'br':\n return 'Bottom-right crop handle';\n case 't':\n return 'Top crop handle';\n case 'r':\n return 'Right crop handle';\n case 'b':\n return 'Bottom crop handle';\n case 'l':\n return 'Left crop handle';\n }\n}\n","import {\n type CropState,\n type HandleDirection,\n pointDisplayToImage,\n type Rect,\n resizeRectFromHandle,\n type Store,\n translateClampedRect,\n type Viewport,\n} from '@magicpages/kalotyp-core';\n\nexport interface CropInteractionElements {\n readonly stageElement: HTMLElement;\n readonly handles: Readonly<Record<HandleDirection, HTMLElement>>;\n readonly bodyHitArea: HTMLElement;\n}\n\nexport interface CropInteractionContext {\n getViewport(): Viewport;\n /** Called once on pointerup so editor history can snapshot. Optional for tests. */\n onCommit?: () => void;\n}\n\nexport interface CropInteractionHandle {\n destroy(): void;\n}\n\n/** Wire eight-handle resize + body translate onto the crop rect. Per-frame rAF-coalesced. */\nexport function bindCropInteractions(\n elements: CropInteractionElements,\n store: Store<CropState>,\n ctx: CropInteractionContext,\n): CropInteractionHandle {\n const cleanups: Array<() => void> = [];\n\n for (const direction of Object.keys(elements.handles) as HandleDirection[]) {\n const handle = elements.handles[direction];\n cleanups.push(attachResizeGesture(handle, direction, store, ctx));\n }\n cleanups.push(attachTranslateGesture(elements.bodyHitArea, store, ctx));\n\n return {\n destroy() {\n for (const cleanup of cleanups) cleanup();\n },\n };\n}\n\nfunction attachResizeGesture(\n element: HTMLElement,\n direction: HandleDirection,\n store: Store<CropState>,\n ctx: CropInteractionContext,\n): () => void {\n return attachPointerDrag(element, () => {\n const viewport = ctx.getViewport();\n const initial = store.get();\n const bounds: Rect = {\n x: 0,\n y: 0,\n width: initial.imageSize.width,\n height: initial.imageSize.height,\n };\n const aspectRatio = initial.aspectRatio;\n\n return {\n onMove(point) {\n const stagePoint = clientToStage(element, point.clientX, point.clientY);\n const imagePoint = pointDisplayToImage(stagePoint, viewport);\n const next = resizeRectFromHandle(initial.rect, direction, imagePoint, {\n bounds,\n ...(aspectRatio !== undefined ? { aspectRatio } : {}),\n });\n store.set({ rect: next });\n },\n onCommit() {\n ctx.onCommit?.();\n },\n onCancel() {\n store.set({ rect: initial.rect });\n },\n };\n });\n}\n\nfunction attachTranslateGesture(\n element: HTMLElement,\n store: Store<CropState>,\n ctx: CropInteractionContext,\n): () => void {\n return attachPointerDrag(element, (event) => {\n const viewport = ctx.getViewport();\n const initial = store.get();\n const bounds: Rect = {\n x: 0,\n y: 0,\n width: initial.imageSize.width,\n height: initial.imageSize.height,\n };\n const originStage = clientToStage(element, event.clientX, event.clientY);\n\n return {\n onMove(point) {\n const here = clientToStage(element, point.clientX, point.clientY);\n const dxImage = (here.x - originStage.x) / viewport.scale;\n const dyImage = (here.y - originStage.y) / viewport.scale;\n const next = translateClampedRect(initial.rect, dxImage, dyImage, bounds);\n store.set({ rect: next });\n },\n onCommit() {\n ctx.onCommit?.();\n },\n onCancel() {\n store.set({ rect: initial.rect });\n },\n };\n });\n}\n\ninterface DragHandlers {\n onMove(point: { clientX: number; clientY: number }): void;\n onCommit(): void;\n onCancel(): void;\n}\n\n/** rAF-coalesced pointer-drag attach. Factory runs on pointerdown. */\nfunction attachPointerDrag(\n element: HTMLElement,\n factory: (start: PointerEvent) => DragHandlers,\n): () => void {\n const onPointerDown = (event: PointerEvent): void => {\n if (event.button !== 0) return;\n event.preventDefault();\n event.stopPropagation();\n\n const handlers = factory(event);\n element.setPointerCapture(event.pointerId);\n\n let pendingPoint: { clientX: number; clientY: number } | undefined;\n let rafScheduled = false;\n\n const flush = (): void => {\n rafScheduled = false;\n if (!pendingPoint) return;\n const point = pendingPoint;\n pendingPoint = undefined;\n handlers.onMove(point);\n };\n\n const onPointerMove = (moveEvent: PointerEvent): void => {\n if (moveEvent.pointerId !== event.pointerId) return;\n pendingPoint = { clientX: moveEvent.clientX, clientY: moveEvent.clientY };\n if (!rafScheduled) {\n rafScheduled = true;\n requestAnimationFrame(flush);\n }\n };\n\n const finish = (committed: boolean): void => {\n element.removeEventListener('pointermove', onPointerMove);\n element.removeEventListener('pointerup', onPointerUp);\n element.removeEventListener('pointercancel', onPointerCancel);\n try {\n element.releasePointerCapture(event.pointerId);\n } catch {\n // already released or never captured\n }\n // Drain pending frame so the final point lands.\n if (pendingPoint) {\n const final = pendingPoint;\n pendingPoint = undefined;\n handlers.onMove(final);\n }\n if (committed) handlers.onCommit();\n else handlers.onCancel();\n };\n\n const onPointerUp = (upEvent: PointerEvent): void => {\n if (upEvent.pointerId !== event.pointerId) return;\n finish(true);\n };\n const onPointerCancel = (cancelEvent: PointerEvent): void => {\n if (cancelEvent.pointerId !== event.pointerId) return;\n finish(false);\n };\n\n element.addEventListener('pointermove', onPointerMove);\n element.addEventListener('pointerup', onPointerUp);\n element.addEventListener('pointercancel', onPointerCancel);\n };\n\n element.addEventListener('pointerdown', onPointerDown);\n return () => element.removeEventListener('pointerdown', onPointerDown);\n}\n\nfunction clientToStage(\n element: HTMLElement,\n clientX: number,\n clientY: number,\n): { x: number; y: number } {\n // Walk up to the stage container so coords are stage-relative regardless of handle nesting.\n const stage = element.closest<HTMLElement>('.kalotyp-stage-container') ?? element;\n const rect = stage.getBoundingClientRect();\n return { x: clientX - rect.left, y: clientY - rect.top };\n}\n","import {\n applyPresetByIndex,\n type CropPreset,\n type CropPresetFilter,\n type CropState,\n clampRectInside,\n computeViewport,\n filterPresets,\n initialCropState,\n type Rect,\n type SourceImage,\n type Store,\n type Viewport,\n type ViewportController,\n} from '@magicpages/kalotyp-core';\nimport { positionHandles } from '../../canvas/position-handles.js';\nimport { renderImageCanvas } from '../../canvas/render-image.js';\nimport { renderOverlayCanvas } from '../../canvas/render-overlay.js';\nimport { buildPresetRow, setActivePresetButton } from '../../dom/build-preset-row.js';\nimport { buildStageElements } from '../../dom/build-stage.js';\nimport { bindCropInteractions } from './interaction.js';\n\nexport interface MountCropOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly presets: readonly CropPreset[];\n readonly presetFilter: CropPresetFilter | undefined;\n readonly store: Store<CropState>;\n /** Editor-level zoom + pan. Optional — falls back to fit-only viewports when absent. */\n readonly viewport?: ViewportController;\n readonly onCommit?: () => void;\n}\n\nexport interface MountCropHandle {\n destroy(): void;\n}\n\nconst STAGE_PADDING_PX = 32;\n\n/** Mount the crop UI. Expects the store pre-initialised via `initialCropState`. */\nexport function mountCropUtility(options: MountCropOptions): MountCropHandle {\n const {\n stageHost,\n utilHost,\n source,\n presets,\n presetFilter,\n store,\n viewport: controller,\n } = options;\n const commit = options.onCommit ?? noop;\n\n const stage = buildStageElements();\n stageHost.appendChild(stage.container);\n\n const panelContainer = document.createElement('div');\n panelContainer.className = 'kalotyp-crop-panel';\n\n const visiblePresets = filterPresets(presets, presetFilter);\n const initialActive = mapToVisibleIndex(store.get(), presets, visiblePresets);\n const presetRow = buildPresetRow(visiblePresets, initialActive, (visibleIndex, preset) => {\n const fullIndex = presets.indexOf(preset);\n if (fullIndex === -1) return;\n const next = applyPresetByIndex(store.get(), fullIndex);\n store.set({\n rect: next.rect,\n aspectRatio: next.aspectRatio,\n activePresetIndex: fullIndex,\n });\n setActivePresetButton(presetRow.buttons, visibleIndex);\n commit();\n });\n panelContainer.appendChild(presetRow.container);\n\n const dimensions = buildCropDimensionsRow({\n initial: store.get().rect,\n bounds: { width: source.width, height: source.height },\n onCommit(next) {\n const current = store.get();\n const bounds: Rect = {\n x: 0,\n y: 0,\n width: current.imageSize.width,\n height: current.imageSize.height,\n };\n const clamped = clampRectInside(next, bounds);\n store.set({ rect: clamped });\n commit();\n },\n });\n panelContainer.appendChild(dimensions.container);\n\n utilHost.appendChild(panelContainer);\n\n let viewport: Viewport = computeViewport(\n { width: 1, height: 1, padding: STAGE_PADDING_PX },\n { width: source.width, height: source.height },\n );\n\n function recomputeViewport(): void {\n const rect = stage.container.getBoundingClientRect();\n const stageDims = { width: rect.width, height: rect.height, padding: STAGE_PADDING_PX };\n const imageSize = { width: source.width, height: source.height };\n viewport = controller\n ? controller.computeViewport(stageDims, imageSize)\n : computeViewport(stageDims, imageSize);\n }\n\n function paintAll(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n renderImageCanvas(stage.imageCanvas, source.bitmap, rect.width, rect.height, viewport);\n paintOverlay();\n }\n\n function paintOverlay(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n renderOverlayCanvas(stage.overlayCanvas, store.get().rect, rect.width, rect.height, viewport);\n positionHandles({\n cropRectImage: store.get().rect,\n viewport,\n cornerAnchors: stage.cornerAnchors,\n edgeHandles: {\n t: stage.handles.t,\n r: stage.handles.r,\n b: stage.handles.b,\n l: stage.handles.l,\n },\n bodyHitArea: stage.bodyHitArea,\n });\n }\n\n recomputeViewport();\n paintAll();\n\n const resizeObserver = new ResizeObserver(() => {\n recomputeViewport();\n paintAll();\n });\n resizeObserver.observe(stage.container);\n\n // Viewport changes flow outside the store — schedule our own rAF to coalesce wheel/pinch bursts.\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n recomputeViewport();\n paintAll();\n });\n });\n\n let overlayRafScheduled = false;\n const unsubscribe = store.subscribe((next, previous) => {\n syncPresetButtons(next, previous, presets, visiblePresets, presetRow.buttons);\n if (rectsEqual(next.rect, previous.rect)) return;\n dimensions.sync(next.rect);\n if (overlayRafScheduled) return;\n overlayRafScheduled = true;\n requestAnimationFrame(() => {\n overlayRafScheduled = false;\n paintOverlay();\n });\n });\n\n const interactions = bindCropInteractions(\n {\n stageElement: stage.container,\n handles: stage.handles,\n bodyHitArea: stage.bodyHitArea,\n },\n store,\n { getViewport: () => viewport, onCommit: commit },\n );\n\n return {\n destroy() {\n interactions.destroy();\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n stage.container.remove();\n panelContainer.remove();\n },\n };\n}\n\ninterface CropDimensionsRow {\n readonly container: HTMLDivElement;\n sync(rect: Rect): void;\n}\n\ninterface BuildCropDimensionsRowOptions {\n readonly initial: Rect;\n readonly bounds: { readonly width: number; readonly height: number };\n onCommit(rect: Rect): void;\n}\n\n/** Four numeric inputs (x/y/w/h) committing on blur or Enter. Caller clamps to image bounds. */\nfunction buildCropDimensionsRow(options: BuildCropDimensionsRowOptions): CropDimensionsRow {\n const container = document.createElement('div');\n container.className = 'kalotyp-crop-dims';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Crop region dimensions');\n\n const xInput = makeNumericInput('Left', options.initial.x, 0, options.bounds.width);\n const yInput = makeNumericInput('Top', options.initial.y, 0, options.bounds.height);\n const wInput = makeNumericInput('Width', options.initial.width, 1, options.bounds.width);\n const hInput = makeNumericInput('Height', options.initial.height, 1, options.bounds.height);\n\n function readRect(): Rect {\n return {\n x: Math.round(xInput.input.valueAsNumber),\n y: Math.round(yInput.input.valueAsNumber),\n width: Math.round(wInput.input.valueAsNumber),\n height: Math.round(hInput.input.valueAsNumber),\n };\n }\n\n for (const field of [xInput, yInput, wInput, hInput]) {\n field.input.addEventListener('change', () => {\n const next = readRect();\n if (!Number.isFinite(next.x + next.y + next.width + next.height)) return;\n options.onCommit(next);\n });\n }\n\n container.appendChild(xInput.wrapper);\n container.appendChild(yInput.wrapper);\n container.appendChild(wInput.wrapper);\n container.appendChild(hInput.wrapper);\n\n function sync(rect: Rect): void {\n if (xInput.input.valueAsNumber !== rect.x) xInput.input.value = String(Math.round(rect.x));\n if (yInput.input.valueAsNumber !== rect.y) yInput.input.value = String(Math.round(rect.y));\n if (wInput.input.valueAsNumber !== rect.width)\n wInput.input.value = String(Math.round(rect.width));\n if (hInput.input.valueAsNumber !== rect.height)\n hInput.input.value = String(Math.round(rect.height));\n }\n\n return { container, sync };\n}\n\nfunction makeNumericInput(\n label: string,\n value: number,\n min: number,\n max: number,\n): { wrapper: HTMLLabelElement; input: HTMLInputElement } {\n const wrapper = document.createElement('label');\n wrapper.className = 'kalotyp-crop-dims-field';\n\n const labelSpan = document.createElement('span');\n labelSpan.className = 'kalotyp-crop-dims-label';\n labelSpan.textContent = label;\n\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'kalotyp-crop-dims-input';\n input.min = String(min);\n input.max = String(max);\n input.step = '1';\n input.value = String(Math.round(value));\n input.inputMode = 'numeric';\n input.setAttribute('aria-label', `${label} (pixels)`);\n\n wrapper.appendChild(labelSpan);\n wrapper.appendChild(input);\n return { wrapper, input };\n}\n\nexport { initialCropState };\n\nfunction noop(): void {}\n\nfunction rectsEqual(a: Rect, b: Rect): boolean {\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n}\n\nfunction mapToVisibleIndex(\n state: CropState,\n fullPresets: readonly CropPreset[],\n visiblePresets: readonly CropPreset[],\n): number {\n if (state.activePresetIndex === -1) return -1;\n const active = fullPresets[state.activePresetIndex];\n if (!active) return -1;\n return visiblePresets.indexOf(active);\n}\n\nfunction syncPresetButtons(\n next: CropState,\n previous: CropState,\n fullPresets: readonly CropPreset[],\n visiblePresets: readonly CropPreset[],\n buttons: readonly HTMLButtonElement[],\n): void {\n if (next.activePresetIndex === previous.activePresetIndex) return;\n const visibleIndex = mapToVisibleIndex(next, fullPresets, visiblePresets);\n setActivePresetButton(buttons, visibleIndex);\n}\n","import {\n bakeCrop,\n type CropPreset,\n type CropPresetFilter,\n type CropState,\n initialCropState,\n type UtilityPlugin,\n} from '@magicpages/kalotyp-core';\nimport { mountCropUtility } from './mount.js';\n\nconst UTILITY_ID = 'crop';\n\nexport interface CropPluginOptions {\n /** The 12 presets Ghost passes (or any caller-supplied subset). */\n readonly presets: readonly CropPreset[];\n /** Orientation filter (Ghost always passes 'landscape'). */\n readonly presetFilter: CropPresetFilter | undefined;\n /** Panel host (closed-over); stage host is the `mount()` argument. */\n readonly panelHost: HTMLElement;\n}\n\nexport function createCropPlugin(options: CropPluginOptions): UtilityPlugin<CropState> {\n return {\n id: 'crop',\n init(ctx) {\n return initialCropState({\n imageSize: { width: ctx.source.width, height: ctx.source.height },\n presets: options.presets,\n filter: options.presetFilter,\n });\n },\n mount(stageHost, ctx, store) {\n const handle = mountCropUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n presets: options.presets,\n presetFilter: options.presetFilter,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: UTILITY_ID }),\n });\n return { destroy: () => handle.destroy() };\n },\n async bake(state, source) {\n return bakeCrop(source, { rect: state.rect });\n },\n };\n}\n","import {\n computeViewport,\n type SourceImage,\n type Viewport,\n type ViewportController,\n} from '@magicpages/kalotyp-core';\n\nconst STAGE_PADDING_PX = 32;\n\nexport interface PreviewCanvas {\n readonly container: HTMLDivElement;\n readonly canvas: HTMLCanvasElement;\n}\n\nexport function buildPreviewCanvas(): PreviewCanvas {\n const container = document.createElement('div');\n container.className = 'kalotyp-stage-container kalotyp-preview-container';\n\n const canvas = document.createElement('canvas');\n canvas.className = 'kalotyp-stage-image kalotyp-preview-canvas';\n canvas.setAttribute('aria-hidden', 'true');\n\n container.appendChild(canvas);\n return { container, canvas };\n}\n\n/** Compute the letterboxed viewport for a preview. Returns `undefined` if the container has no laid-out size yet. */\nexport function previewViewportFor(\n container: HTMLElement,\n intrinsic: { width: number; height: number },\n controller?: ViewportController,\n): { viewport: Viewport; stageWidth: number; stageHeight: number } | undefined {\n const rect = container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return undefined;\n const stageDims = { width: rect.width, height: rect.height, padding: STAGE_PADDING_PX };\n const viewport = controller\n ? controller.computeViewport(stageDims, intrinsic)\n : computeViewport(stageDims, intrinsic);\n return { viewport, stageWidth: rect.width, stageHeight: rect.height };\n}\n\n/** Paint into a preview canvas. The callback receives a DPR-scaled context positioned at (0,0) in stage CSS pixels. */\nexport function paintPreview(\n canvas: HTMLCanvasElement,\n stageWidth: number,\n stageHeight: number,\n draw: (ctx: CanvasRenderingContext2D) => void,\n): void {\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const targetW = Math.max(1, Math.round(stageWidth * dpr));\n const targetH = Math.max(1, Math.round(stageHeight * dpr));\n if (canvas.width !== targetW) canvas.width = targetW;\n if (canvas.height !== targetH) canvas.height = targetH;\n canvas.style.width = `${stageWidth}px`;\n canvas.style.height = `${stageHeight}px`;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, stageWidth, stageHeight);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n draw(ctx);\n}\n\nexport type { SourceImage };\nexport { STAGE_PADDING_PX };\n","import {\n applyClarity,\n applyFinetuneLutAndSaturation,\n boxBlur3x3,\n buildFinetuneLut,\n type FinetuneState,\n isFinetuneNoOp,\n} from '@magicpages/kalotyp-core';\n\n/**\n * Finetune preview pipeline. Operates on display-res pixels (a 4000×3000 photo letterboxed to\n * ~720×480 at DPR 2 is ~1.4 MP — the six-adjust composed pass measures ~5 ms there).\n */\nexport interface FinetunePreviewPipeline {\n paint(state: FinetuneState): void;\n rebuild(width: number, height: number): void;\n dispose(): void;\n}\n\ninterface PreviewBuffers {\n // ArrayBuffer-pinned so these are assignable to `new ImageData(...)`.\n baseline: Uint8ClampedArray<ArrayBuffer>;\n scratch: Uint8ClampedArray<ArrayBuffer>;\n /** Pre-blurred baseline for clarity; rebuilt with baseline. */\n blurred: Uint8ClampedArray<ArrayBuffer> | undefined;\n width: number;\n height: number;\n}\n\nexport interface BuildPreviewPipelineOptions {\n readonly canvas: HTMLCanvasElement;\n readonly sourceBitmap: CanvasImageSource;\n}\n\nexport function buildFinetunePreviewPipeline(\n options: BuildPreviewPipelineOptions,\n): FinetunePreviewPipeline {\n const { canvas, sourceBitmap } = options;\n let buffers: PreviewBuffers | undefined;\n\n function rebuild(width: number, height: number): void {\n if (width <= 0 || height <= 0) return;\n // willReadFrequently keeps the backing store CPU-side, avoiding a GPU sync per getImageData — load-bearing here.\n const ctx = getReadFrequentContext(canvas);\n if (!ctx) return;\n canvas.width = width;\n canvas.height = height;\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.clearRect(0, 0, width, height);\n ctx.drawImage(sourceBitmap, 0, 0, width, height);\n const imageData = ctx.getImageData(0, 0, width, height);\n buffers = {\n baseline: new Uint8ClampedArray(imageData.data),\n scratch: imageData.data,\n blurred: undefined,\n width,\n height,\n };\n }\n\n function paint(state: FinetuneState): void {\n if (!buffers) return;\n const ctx = getReadFrequentContext(canvas);\n if (!ctx) return;\n\n if (isFinetuneNoOp(state)) {\n // Paint a fresh copy of baseline (not scratch) so releasing every slider is bit-exact \"this is the source\".\n const imageData = new ImageData(\n new Uint8ClampedArray(buffers.baseline),\n buffers.width,\n buffers.height,\n );\n ctx.putImageData(imageData, 0, 0);\n return;\n }\n\n const lut = buildFinetuneLut(state);\n applyFinetuneLutAndSaturation(buffers.baseline, buffers.scratch, lut, state);\n\n if (state.clarity !== 0) {\n // Lazy-cached blurred baseline; invalidated on every rebuild.\n if (!buffers.blurred) {\n const tmp = new Uint8ClampedArray(buffers.baseline.length);\n const blurred = new Uint8ClampedArray(buffers.baseline.length);\n boxBlur3x3(buffers.baseline, tmp, blurred, buffers.width, buffers.height);\n buffers.blurred = blurred;\n }\n applyClarity(buffers.scratch, buffers.blurred, state.clarity);\n }\n\n const imageData = new ImageData(buffers.scratch, buffers.width, buffers.height);\n ctx.putImageData(imageData, 0, 0);\n }\n\n function dispose(): void {\n buffers = undefined;\n }\n\n return { paint, rebuild, dispose };\n}\n\n// getContext returns the first-acquired context; subsequent attribute args are ignored, so this helper is safe to call from both rebuild and paint.\nfunction getReadFrequentContext(canvas: HTMLCanvasElement): CanvasRenderingContext2D | null {\n return canvas.getContext('2d', { willReadFrequently: true });\n}\n","import {\n applyFinetuneToImageData,\n type FilterPreset,\n type FilterPresetId,\n type RasterImage,\n} from '@magicpages/kalotyp-core';\n\n/**\n * Filter-strip thumbnails. Uses the same `applyFinetuneToImageData` as bake+preview, so\n * the thumbnail honestly previews the saved result (modulo source→thumbnail resampling).\n */\n\nexport const THUMBNAIL_MAX_WIDTH = 80;\nexport const THUMBNAIL_MAX_HEIGHT = 60;\n\nexport interface ThumbnailDims {\n readonly width: number;\n readonly height: number;\n}\n\n/** Largest aspect-preserving fit inside the THUMBNAIL_MAX_* box. */\nexport function computeThumbnailDims(source: { width: number; height: number }): ThumbnailDims {\n if (source.width <= 0 || source.height <= 0) {\n return { width: THUMBNAIL_MAX_WIDTH, height: THUMBNAIL_MAX_HEIGHT };\n }\n const widthRatio = THUMBNAIL_MAX_WIDTH / source.width;\n const heightRatio = THUMBNAIL_MAX_HEIGHT / source.height;\n const ratio = Math.min(widthRatio, heightRatio);\n const width = Math.max(1, Math.floor(source.width * ratio));\n const height = Math.max(1, Math.floor(source.height * ratio));\n return { width, height };\n}\n\nexport interface ThumbnailCache {\n get(preset: FilterPreset): HTMLCanvasElement;\n dispose(): void;\n}\n\nexport interface BuildThumbnailCacheOptions {\n readonly source: CanvasImageSource & { readonly width?: number; readonly height?: number };\n readonly dims: ThumbnailDims;\n readonly dpr: number;\n readonly presets?: readonly FilterPreset[];\n}\n\n/** Lazy-rendered preset cache. Baseline ImageData is captured once so per-preset cost is math + putImageData. */\nexport function buildThumbnailCache(options: BuildThumbnailCacheOptions): ThumbnailCache {\n const { source, dims, dpr } = options;\n const pxW = Math.max(1, Math.round(dims.width * dpr));\n const pxH = Math.max(1, Math.round(dims.height * dpr));\n\n // Render source at thumbnail pixel-grid once; this baseline ImageData feeds every preset's math.\n const baselineCanvas = document.createElement('canvas');\n baselineCanvas.width = pxW;\n baselineCanvas.height = pxH;\n const baselineCtx = baselineCanvas.getContext('2d', { willReadFrequently: true });\n if (!baselineCtx) {\n return {\n get: () => makeBlankCanvas(dims, dpr),\n dispose: () => {},\n };\n }\n baselineCtx.imageSmoothingEnabled = true;\n baselineCtx.imageSmoothingQuality = 'high';\n baselineCtx.drawImage(source, 0, 0, pxW, pxH);\n const baselineImageData = baselineCtx.getImageData(0, 0, pxW, pxH);\n const baseline: RasterImage = {\n data: new Uint8ClampedArray(baselineImageData.data),\n width: pxW,\n height: pxH,\n };\n\n const cache = new Map<FilterPresetId, HTMLCanvasElement>();\n\n function renderPreset(preset: FilterPreset): HTMLCanvasElement {\n const canvas = document.createElement('canvas');\n canvas.width = pxW;\n canvas.height = pxH;\n canvas.style.width = `${dims.width}px`;\n canvas.style.height = `${dims.height}px`;\n const ctx = canvas.getContext('2d');\n if (!ctx) return canvas;\n\n const dst: RasterImage = {\n data: new Uint8ClampedArray(baseline.data.length),\n width: pxW,\n height: pxH,\n };\n applyFinetuneToImageData(preset.state, baseline, dst);\n ctx.putImageData(new ImageData(dst.data, pxW, pxH), 0, 0);\n return canvas;\n }\n\n return {\n get(preset: FilterPreset): HTMLCanvasElement {\n const existing = cache.get(preset.id);\n if (existing) return existing;\n const canvas = renderPreset(preset);\n cache.set(preset.id, canvas);\n return canvas;\n },\n dispose(): void {\n cache.clear();\n },\n };\n}\n\nfunction makeBlankCanvas(dims: ThumbnailDims, dpr: number): HTMLCanvasElement {\n const canvas = document.createElement('canvas');\n canvas.width = Math.max(1, Math.round(dims.width * dpr));\n canvas.height = Math.max(1, Math.round(dims.height * dpr));\n canvas.style.width = `${dims.width}px`;\n canvas.style.height = `${dims.height}px`;\n return canvas;\n}\n","import {\n DEFAULT_FINETUNE_STATE,\n FILTER_PRESETS,\n type FilterPreset,\n type FilterPresetId,\n type FinetuneState,\n findActivePreset,\n type SourceImage,\n type Store,\n type ViewportController,\n} from '@magicpages/kalotyp-core';\nimport { buildPreviewCanvas, previewViewportFor } from '../../canvas/preview-canvas.js';\nimport { buildFinetunePreviewPipeline } from '../finetune/preview.js';\nimport { buildThumbnailCache, computeThumbnailDims, type ThumbnailCache } from './thumbnails.js';\n\nexport interface MountFilterOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n /** Upstream-baked source (same input as the finetune tab — filter shares finetune's slot). */\n readonly source: SourceImage;\n /** Shared store with finetune; clicking a thumbnail writes here. */\n readonly store: Store<FinetuneState>;\n readonly viewport?: ViewportController;\n readonly onCommit?: () => void;\n}\n\nexport interface MountFilterHandle {\n destroy(): void;\n}\n\nexport function mountFilterUtility(options: MountFilterOptions): MountFilterHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? noop;\n\n // Mirror of the finetune-tab preview so switching tabs doesn't change what's on screen.\n const preview = buildPreviewCanvas();\n stageHost.appendChild(preview.container);\n const pipeline = buildFinetunePreviewPipeline({\n canvas: preview.canvas,\n sourceBitmap: source.bitmap,\n });\n\n // Upstream-baked source is fixed for this mount, so the seven thumbnails generate once.\n const dims = computeThumbnailDims({ width: source.width, height: source.height });\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const thumbnailCache = buildThumbnailCache({\n source: source.bitmap,\n dims,\n dpr,\n });\n\n const strip = buildFilterStrip({\n presets: FILTER_PRESETS,\n thumbnailCache,\n dims,\n onPresetClick: (preset) => {\n const current = store.get();\n const isActiveNow = findActivePreset(current)?.id === preset.id;\n if (isActiveNow && preset.id !== 'none') {\n // Click-to-deselect → all-zeros. Without it, clearing a filter requires dragging sliders back.\n store.update(() => DEFAULT_FINETUNE_STATE);\n commit();\n return;\n }\n store.update(() => preset.state);\n commit();\n },\n });\n utilHost.appendChild(strip.container);\n\n let upstreamSize = { width: 0, height: 0 };\n\n function repaint(): void {\n const v = previewViewportFor(\n preview.container,\n { width: source.width, height: source.height },\n controller,\n );\n if (!v) return;\n const display = v.viewport.displayRect;\n const pxW = Math.max(1, Math.round(display.width * dpr));\n const pxH = Math.max(1, Math.round(display.height * dpr));\n\n preview.canvas.style.width = `${display.width}px`;\n preview.canvas.style.height = `${display.height}px`;\n preview.canvas.style.position = 'absolute';\n preview.canvas.style.left = `${display.x}px`;\n preview.canvas.style.top = `${display.y}px`;\n\n // Skip the heavy pixel-grid rebuild during a pinch; CSS interp covers it, post-gesture repaint sharpens.\n const pinching = controller?.getPinching() ?? false;\n if (!pinching && (upstreamSize.width !== pxW || upstreamSize.height !== pxH)) {\n upstreamSize = { width: pxW, height: pxH };\n pipeline.rebuild(pxW, pxH);\n }\n\n pipeline.paint(store.get());\n }\n\n function syncStripActive(state: FinetuneState): void {\n const activeId = findActivePreset(state)?.id;\n strip.setActive(activeId);\n }\n\n syncStripActive(store.get());\n repaint();\n\n const resizeObserver = new ResizeObserver(() => repaint());\n resizeObserver.observe(preview.container);\n\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n repaint();\n });\n });\n\n let rafScheduled = false;\n const unsubscribe = store.subscribe((next) => {\n syncStripActive(next);\n if (rafScheduled) return;\n rafScheduled = true;\n requestAnimationFrame(() => {\n rafScheduled = false;\n pipeline.paint(store.get());\n });\n });\n\n return {\n destroy() {\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n pipeline.dispose();\n thumbnailCache.dispose();\n preview.container.remove();\n strip.container.remove();\n },\n };\n}\n\ninterface FilterStripOptions {\n readonly presets: readonly FilterPreset[];\n readonly thumbnailCache: ThumbnailCache;\n readonly dims: { width: number; height: number };\n onPresetClick(preset: FilterPreset): void;\n}\n\ninterface FilterStrip {\n readonly container: HTMLDivElement;\n setActive(id: FilterPresetId | undefined): void;\n}\n\nfunction buildFilterStrip(options: FilterStripOptions): FilterStrip {\n const container = document.createElement('div');\n container.className = 'kalotyp-filter-panel';\n // Radiogroup, not tablist: presets are mutually-exclusive state, not panel switchers.\n container.setAttribute('role', 'radiogroup');\n container.setAttribute('aria-label', 'Filter presets');\n\n const list = document.createElement('div');\n list.className = 'kalotyp-filter-strip';\n container.appendChild(list);\n\n const buttons = new Map<FilterPresetId, HTMLButtonElement>();\n\n for (const preset of options.presets) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-filter-thumb';\n button.dataset.presetId = preset.id;\n button.setAttribute('role', 'radio');\n button.setAttribute('aria-checked', 'false');\n button.setAttribute('aria-label', `${preset.label} filter`);\n button.title = preset.label;\n\n const thumbWrap = document.createElement('span');\n thumbWrap.className = 'kalotyp-filter-thumb-image';\n thumbWrap.style.width = `${options.dims.width}px`;\n thumbWrap.style.height = `${options.dims.height}px`;\n const canvas = options.thumbnailCache.get(preset);\n canvas.classList.add('kalotyp-filter-thumb-canvas');\n thumbWrap.appendChild(canvas);\n\n // Active-state checkmark (avoids color-only signalling). aria-hidden: aria-checked is the state carrier.\n const activeBadge = document.createElement('span');\n activeBadge.className = 'kalotyp-filter-thumb-check';\n activeBadge.setAttribute('aria-hidden', 'true');\n activeBadge.innerHTML = '✓';\n thumbWrap.appendChild(activeBadge);\n\n const labelEl = document.createElement('span');\n labelEl.className = 'kalotyp-filter-thumb-label';\n labelEl.textContent = preset.label;\n\n button.appendChild(thumbWrap);\n button.appendChild(labelEl);\n button.addEventListener('click', () => options.onPresetClick(preset));\n\n list.appendChild(button);\n buttons.set(preset.id, button);\n }\n\n return {\n container,\n setActive(id) {\n for (const [presetId, button] of buttons) {\n const isActive = presetId === id;\n button.setAttribute('aria-checked', isActive ? 'true' : 'false');\n button.classList.toggle('kalotyp-filter-thumb--active', isActive);\n }\n },\n };\n}\n\nfunction noop(): void {}\n","import {\n DEFAULT_FINETUNE_STATE,\n type FinetuneState,\n type SourceImage,\n type UtilityPlugin,\n} from '@magicpages/kalotyp-core';\nimport { mountFilterUtility } from './mount.js';\n\nexport interface FilterPluginOptions {\n readonly panelHost: HTMLElement;\n}\n\n/**\n * Filter plugin. Shares the finetune slot's store; `init`/`bake` here are synthetic\n * (filter isn't in CHAIN_ORDER and the editor registers the slot manually with the shared store).\n */\nexport function createFilterPlugin(options: FilterPluginOptions): UtilityPlugin<FinetuneState> {\n return {\n id: 'filter',\n init: () => DEFAULT_FINETUNE_STATE,\n mount(stageHost, ctx, store) {\n const handle = mountFilterUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: 'filter' }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: filterBakeIdentity,\n };\n}\n\n// Identity bake — never called, exists only to satisfy `UtilityPlugin<TState>`.\nasync function filterBakeIdentity(\n _state: FinetuneState,\n source: SourceImage,\n): Promise<SourceImage> {\n return source;\n}\n","import {\n FINETUNE_ADJUSTMENTS,\n FINETUNE_MAX,\n FINETUNE_MIN,\n FINETUNE_STEP,\n type FinetuneKey,\n type FinetuneState,\n resetAllFinetune,\n resetFinetune,\n type SourceImage,\n type Store,\n setFinetune,\n type ViewportController,\n} from '@magicpages/kalotyp-core';\nimport { buildPreviewCanvas, previewViewportFor } from '../../canvas/preview-canvas.js';\nimport { buildFinetunePreviewPipeline } from './preview.js';\n\nexport interface MountFinetuneOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly store: Store<FinetuneState>;\n /** Editor-level zoom + pan controller. Optional in jsdom tests. */\n readonly viewport?: ViewportController;\n /** Fires on slider `change` / number `change` / reset (one undo entry per drag, not per `input`). */\n readonly onCommit?: () => void;\n}\n\nexport interface MountFinetuneHandle {\n destroy(): void;\n}\n\nexport function mountFinetuneUtility(options: MountFinetuneOptions): MountFinetuneHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? noop;\n\n const preview = buildPreviewCanvas();\n stageHost.appendChild(preview.container);\n\n const pipeline = buildFinetunePreviewPipeline({\n canvas: preview.canvas,\n sourceBitmap: source.bitmap,\n });\n\n const panel = buildFinetunePanel({\n onSliderInput: (key, value) => store.set(setFinetune(store.get(), key, value)),\n onSliderCommit: () => commit(),\n onNumberCommit: (key, value) => {\n store.set(setFinetune(store.get(), key, value));\n commit();\n },\n onRowReset: (key) => {\n store.set(resetFinetune(store.get(), key));\n commit();\n },\n onResetAll: () => {\n store.update(() => resetAllFinetune());\n commit();\n },\n });\n utilHost.appendChild(panel.container);\n\n let upstreamSize = { width: 0, height: 0 };\n\n function repaint(): void {\n const v = previewViewportFor(\n preview.container,\n { width: source.width, height: source.height },\n controller,\n );\n if (!v) return;\n const display = v.viewport.displayRect;\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const pxW = Math.max(1, Math.round(display.width * dpr));\n const pxH = Math.max(1, Math.round(display.height * dpr));\n\n // Canvas is the math's surface (pipeline does getImageData/putImageData) — no transformed ctx, so paintPreview's DPR dance doesn't apply.\n preview.canvas.style.width = `${display.width}px`;\n preview.canvas.style.height = `${display.height}px`;\n preview.canvas.style.position = 'absolute';\n preview.canvas.style.left = `${display.x}px`;\n preview.canvas.style.top = `${display.y}px`;\n\n // Skip the CPU rebuild during pinch — CSS interp on the existing buffer is good enough; post-gesture repaint sharpens.\n const pinching = controller?.getPinching() ?? false;\n if (!pinching && (upstreamSize.width !== pxW || upstreamSize.height !== pxH)) {\n upstreamSize = { width: pxW, height: pxH };\n pipeline.rebuild(pxW, pxH);\n }\n\n pipeline.paint(store.get());\n }\n\n function syncPanel(state: FinetuneState): void {\n for (const adj of FINETUNE_ADJUSTMENTS) {\n const row = panel.rows.get(adj.key);\n if (!row) continue;\n const value = state[adj.key];\n if (row.slider.valueAsNumber !== value) row.slider.valueAsNumber = value;\n if (Number.parseFloat(row.input.value || '0') !== value) row.input.value = String(value);\n }\n }\n\n syncPanel(store.get());\n repaint();\n\n const resizeObserver = new ResizeObserver(() => repaint());\n resizeObserver.observe(preview.container);\n\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n repaint();\n });\n });\n\n let rafScheduled = false;\n const unsubscribe = store.subscribe((next) => {\n syncPanel(next);\n if (rafScheduled) return;\n rafScheduled = true;\n requestAnimationFrame(() => {\n rafScheduled = false;\n pipeline.paint(store.get());\n });\n });\n\n return {\n destroy() {\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n pipeline.dispose();\n preview.container.remove();\n panel.container.remove();\n },\n };\n}\n\ninterface FinetunePanelOptions {\n onSliderInput(key: FinetuneKey, value: number): void;\n onSliderCommit(): void;\n onNumberCommit(key: FinetuneKey, value: number): void;\n onRowReset(key: FinetuneKey): void;\n onResetAll(): void;\n}\n\ninterface FinetuneRowEls {\n readonly row: HTMLDivElement;\n readonly slider: HTMLInputElement;\n readonly input: HTMLInputElement;\n readonly resetButton: HTMLButtonElement;\n}\n\ninterface FinetunePanel {\n container: HTMLDivElement;\n rows: ReadonlyMap<FinetuneKey, FinetuneRowEls>;\n resetAllButton: HTMLButtonElement;\n}\n\nfunction buildFinetunePanel(options: FinetunePanelOptions): FinetunePanel {\n const container = document.createElement('div');\n container.className = 'kalotyp-finetune-panel';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Finetune adjustments');\n\n const rows = new Map<FinetuneKey, FinetuneRowEls>();\n for (const adj of FINETUNE_ADJUSTMENTS) {\n const row = buildAdjustmentRow(adj.key, adj.label, options);\n rows.set(adj.key, row);\n container.appendChild(row.row);\n }\n\n const resetAllButton = document.createElement('button');\n resetAllButton.type = 'button';\n resetAllButton.className = 'kalotyp-finetune-reset-all';\n resetAllButton.textContent = 'Reset all';\n resetAllButton.title = 'Reset every adjustment to 0';\n resetAllButton.addEventListener('click', options.onResetAll);\n container.appendChild(resetAllButton);\n\n return { container, rows, resetAllButton };\n}\n\nfunction buildAdjustmentRow(\n key: FinetuneKey,\n label: string,\n options: FinetunePanelOptions,\n): FinetuneRowEls {\n const row = document.createElement('div');\n row.className = 'kalotyp-finetune-row';\n row.dataset.adjustment = key;\n\n const labelEl = document.createElement('label');\n labelEl.className = 'kalotyp-finetune-label';\n labelEl.textContent = label;\n\n const slider = document.createElement('input');\n slider.type = 'range';\n slider.className = 'kalotyp-finetune-slider';\n slider.min = String(FINETUNE_MIN);\n slider.max = String(FINETUNE_MAX);\n slider.step = String(FINETUNE_STEP);\n slider.value = '0';\n slider.setAttribute('aria-label', `${label} adjustment`);\n slider.addEventListener('input', () => options.onSliderInput(key, slider.valueAsNumber));\n slider.addEventListener('change', () => options.onSliderCommit());\n\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'kalotyp-finetune-input';\n input.min = String(FINETUNE_MIN);\n input.max = String(FINETUNE_MAX);\n input.step = String(FINETUNE_STEP);\n input.value = '0';\n input.inputMode = 'numeric';\n input.setAttribute('aria-label', `${label} value`);\n input.addEventListener('change', () => {\n const v = input.valueAsNumber;\n if (Number.isFinite(v)) options.onNumberCommit(key, v);\n });\n\n const resetButton = document.createElement('button');\n resetButton.type = 'button';\n resetButton.className = 'kalotyp-finetune-row-reset';\n resetButton.setAttribute('aria-label', `Reset ${label}`);\n resetButton.title = `Reset ${label}`;\n resetButton.textContent = '↺';\n resetButton.addEventListener('click', () => options.onRowReset(key));\n\n row.appendChild(labelEl);\n row.appendChild(slider);\n row.appendChild(input);\n row.appendChild(resetButton);\n\n return { row, slider, input, resetButton };\n}\n\nfunction noop(): void {}\n","import {\n bakeFinetune,\n type FinetuneState,\n initialFinetuneState,\n type UtilityPlugin,\n} from '@magicpages/kalotyp-core';\nimport { mountFinetuneUtility } from './mount.js';\n\nexport interface FinetunePluginOptions {\n readonly panelHost: HTMLElement;\n}\n\nexport function createFinetunePlugin(options: FinetunePluginOptions): UtilityPlugin<FinetuneState> {\n return {\n id: 'finetune',\n init: () => initialFinetuneState(),\n mount(stageHost, ctx, store) {\n const handle = mountFinetuneUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: 'finetune' }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: bakeFinetune,\n };\n}\n","import {\n type FlipState,\n type SourceImage,\n type Store,\n toggleFlip,\n type ViewportController,\n} from '@magicpages/kalotyp-core';\nimport {\n buildPreviewCanvas,\n paintPreview,\n previewViewportFor,\n} from '../../canvas/preview-canvas.js';\nimport { icon } from '../../icons.js';\n\nexport interface MountFlipOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly store: Store<FlipState>;\n readonly viewport?: ViewportController;\n readonly onCommit?: () => void;\n}\n\nexport interface MountFlipHandle {\n destroy(): void;\n}\n\nexport function mountFlipUtility(options: MountFlipOptions): MountFlipHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? (() => {});\n\n const preview = buildPreviewCanvas();\n stageHost.appendChild(preview.container);\n\n const panel = buildFlipPanel({\n onToggleHorizontal: () => {\n store.set(toggleFlip(store.get(), 'horizontal'));\n commit();\n },\n onToggleVertical: () => {\n store.set(toggleFlip(store.get(), 'vertical'));\n commit();\n },\n });\n utilHost.appendChild(panel.container);\n\n function paint(): void {\n const v = previewViewportFor(\n preview.container,\n { width: source.width, height: source.height },\n controller,\n );\n if (!v) return;\n const state = store.get();\n paintPreview(preview.canvas, v.stageWidth, v.stageHeight, (ctx) => {\n const display = v.viewport.displayRect;\n const sx = state.horizontal ? -1 : 1;\n const sy = state.vertical ? -1 : 1;\n // Anchor on display centre so the image stays letterboxed in place, just mirrored.\n const cx = display.x + display.width / 2;\n const cy = display.y + display.height / 2;\n ctx.translate(cx, cy);\n ctx.scale(sx, sy);\n ctx.drawImage(\n source.bitmap,\n -display.width / 2,\n -display.height / 2,\n display.width,\n display.height,\n );\n });\n }\n\n function syncPanel(state: FlipState): void {\n panel.horizontalButton.setAttribute('aria-pressed', state.horizontal ? 'true' : 'false');\n panel.verticalButton.setAttribute('aria-pressed', state.vertical ? 'true' : 'false');\n }\n\n syncPanel(store.get());\n paint();\n\n const resizeObserver = new ResizeObserver(() => paint());\n resizeObserver.observe(preview.container);\n\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n paint();\n });\n });\n\n let rafScheduled = false;\n const unsubscribe = store.subscribe((next) => {\n syncPanel(next);\n if (rafScheduled) return;\n rafScheduled = true;\n requestAnimationFrame(() => {\n rafScheduled = false;\n paint();\n });\n });\n\n return {\n destroy() {\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n preview.container.remove();\n panel.container.remove();\n },\n };\n}\n\ninterface FlipPanelOptions {\n onToggleHorizontal(): void;\n onToggleVertical(): void;\n}\n\ninterface FlipPanel {\n container: HTMLDivElement;\n horizontalButton: HTMLButtonElement;\n verticalButton: HTMLButtonElement;\n}\n\nfunction buildFlipPanel(options: FlipPanelOptions): FlipPanel {\n const container = document.createElement('div');\n container.className = 'kalotyp-flip-panel';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Flip');\n\n const horizontalButton = createToggleButton(\n 'Flip horizontal',\n icon('flipHorizontal'),\n options.onToggleHorizontal,\n );\n horizontalButton.classList.add('kalotyp-flip-button-h');\n const verticalButton = createToggleButton(\n 'Flip vertical',\n icon('flipVertical'),\n options.onToggleVertical,\n );\n verticalButton.classList.add('kalotyp-flip-button-v');\n\n container.appendChild(horizontalButton);\n container.appendChild(verticalButton);\n\n return { container, horizontalButton, verticalButton };\n}\n\nfunction createToggleButton(\n label: string,\n iconHtml: string,\n onClick: () => void,\n): HTMLButtonElement {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-toggle-button';\n button.innerHTML = `${iconHtml}<span>${label}</span>`;\n button.setAttribute('aria-pressed', 'false');\n button.setAttribute('aria-label', label);\n button.addEventListener('click', onClick);\n return button;\n}\n","import {\n bakeFlip,\n type FlipState,\n initialFlipState,\n type UtilityPlugin,\n} from '@magicpages/kalotyp-core';\nimport { mountFlipUtility } from './mount.js';\n\nexport interface FlipPluginOptions {\n readonly panelHost: HTMLElement;\n}\n\nexport function createFlipPlugin(options: FlipPluginOptions): UtilityPlugin<FlipState> {\n return {\n id: 'flip',\n init: () => initialFlipState(),\n mount(stageHost, ctx, store) {\n const handle = mountFlipUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: 'flip' }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: bakeFlip,\n };\n}\n","/**\n * Mount the frame plugin: a preset thumbnail strip (six entries\n * matching Ghost's `frameOptions` order) plus a colour picker for the\n * presets that accept colour customisation. The stage shows the\n * upstream-baked source with the active frame composited on top.\n *\n * frame is the chain's tail link, so the live preview\n * is conceptually \"what the user gets on Save.\" We render the source\n * + frame composite into a single preview canvas; clicking a\n * thumbnail updates the store, the preview re-renders, and the\n * editor's history captures the commit.\n */\n\nimport {\n computeViewport,\n FRAME_PRESETS,\n type FramePreset,\n type FramePresetId,\n type FrameState,\n frameOutputSize,\n paintInsideFrame,\n type SourceImage,\n type Store,\n setFrameColor,\n setFramePreset,\n type Viewport,\n type ViewportController,\n} from '@magicpages/kalotyp-core';\n\nconst STAGE_PADDING_PX = 32;\n\nexport interface MountFrameOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly store: Store<FrameState>;\n readonly viewport?: ViewportController | undefined;\n /**\n * Optional locale callbacks Ghost passes via `frameOptions[i][1]`.\n * The factory wires this from the Ghost adapter; the playground\n * passes nothing and the strip falls back to the preset's default\n * label.\n */\n readonly labels?: Partial<Record<FramePresetId, string>> | undefined;\n readonly onCommit?: (() => void) | undefined;\n}\n\nexport interface MountFrameHandle {\n destroy(): void;\n}\n\nexport function mountFrameUtility(options: MountFrameOptions): MountFrameHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? (() => {});\n\n // Stage: a preview canvas the same shape the finetune/filter\n // plugins use. We render the upstream image + frame composite at\n // every paint.\n const previewContainer = document.createElement('div');\n previewContainer.className = 'kalotyp-stage-container kalotyp-frame-preview-container';\n const previewCanvas = document.createElement('canvas');\n previewCanvas.className = 'kalotyp-stage-image kalotyp-frame-preview-canvas';\n previewCanvas.setAttribute('aria-hidden', 'true');\n previewContainer.appendChild(previewCanvas);\n stageHost.appendChild(previewContainer);\n\n // Build the strip + colour picker.\n const initialState = store.get();\n const stripContainer = document.createElement('div');\n stripContainer.className = 'kalotyp-frame-panel';\n\n const strip = buildFrameStrip({\n presets: FRAME_PRESETS,\n initialActiveId: initialState.presetId,\n labels: options.labels,\n source,\n initialState,\n onPresetClick: (preset) => {\n store.update((current) => setFramePreset(current, preset.id));\n commit();\n },\n });\n stripContainer.appendChild(strip.container);\n\n const colorRow = buildColourRow({\n initialColor: initialState.color,\n initialPresetId: initialState.presetId,\n onColorChange: (color) => {\n store.update((current) => setFrameColor(current, color));\n commit();\n },\n });\n stripContainer.appendChild(colorRow.container);\n\n utilHost.appendChild(stripContainer);\n\n let viewport: Viewport = computeViewport(\n { width: 1, height: 1, padding: STAGE_PADDING_PX },\n { width: source.width, height: source.height },\n );\n\n function recomputeViewport(): void {\n const rect = previewContainer.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n const stageDims = { width: rect.width, height: rect.height, padding: STAGE_PADDING_PX };\n // The frame plugin's preview accounts for the *output* dimensions\n // (which differ from the source for Polaroid). Compute the\n // viewport against the frame's output dims so the framed\n // composite letterboxes correctly inside the stage.\n const state = store.get();\n const out = frameOutputSize(state.presetId, source.width, source.height);\n viewport = controller\n ? controller.computeViewport(stageDims, out)\n : computeViewport(stageDims, out);\n }\n\n function repaint(): void {\n const rect = previewContainer.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const targetW = Math.max(1, Math.round(rect.width * dpr));\n const targetH = Math.max(1, Math.round(rect.height * dpr));\n if (previewCanvas.width !== targetW) previewCanvas.width = targetW;\n if (previewCanvas.height !== targetH) previewCanvas.height = targetH;\n previewCanvas.style.width = `${rect.width}px`;\n previewCanvas.style.height = `${rect.height}px`;\n const ctx = previewCanvas.getContext('2d');\n if (!ctx) return;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, rect.width, rect.height);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n\n const state = store.get();\n const out = frameOutputSize(state.presetId, source.width, source.height);\n const dx = viewport.displayRect.x;\n const dy = viewport.displayRect.y;\n const dw = viewport.displayRect.width;\n const dh = viewport.displayRect.height;\n\n // For polaroid we need to draw the polaroid border first then\n // the source image inset by its inner-edge thickness. For the\n // other presets we draw the source full-size then paint the\n // frame on top.\n if (state.presetId === 'polaroid') {\n // Border colour fills the entire output rect.\n ctx.fillStyle = state.color;\n ctx.fillRect(dx, dy, dw, dh);\n // Compute the inset proportions (matching bake.ts). The inner\n // image lives at (top/left, top/top) of the output, sized so\n // its aspect ratio matches the source.\n const innerW = source.width * (dw / out.width);\n const innerH = source.height * (dh / out.height);\n const shorter = Math.min(source.width, source.height);\n const inset = Math.round(shorter * 0.05);\n const innerOffsetX = dx + (inset * dw) / out.width;\n const innerOffsetY = dy + (inset * dh) / out.height;\n ctx.drawImage(source.bitmap, innerOffsetX, innerOffsetY, innerW, innerH);\n } else {\n // Draw the source filling the displayRect.\n ctx.drawImage(source.bitmap, dx, dy, dw, dh);\n if (state.presetId !== 'none') {\n // Paint the frame in display-pixel space at the displayRect's\n // dimensions. Translate so frame coordinates start at (dx, dy).\n ctx.save();\n ctx.translate(dx, dy);\n paintInsideFrame(ctx, state.presetId, state.color, dw, dh);\n ctx.restore();\n }\n }\n }\n\n recomputeViewport();\n repaint();\n\n const resizeObserver = new ResizeObserver(() => {\n recomputeViewport();\n repaint();\n });\n resizeObserver.observe(previewContainer);\n\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n recomputeViewport();\n repaint();\n });\n });\n\n let rafScheduled = false;\n const unsubscribe = store.subscribe((next) => {\n strip.setActive(next.presetId);\n colorRow.setColor(next.color);\n colorRow.setEnabled(next.presetId !== 'none');\n if (rafScheduled) return;\n rafScheduled = true;\n requestAnimationFrame(() => {\n rafScheduled = false;\n recomputeViewport();\n repaint();\n });\n });\n\n return {\n destroy() {\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n previewContainer.remove();\n stripContainer.remove();\n },\n };\n}\n\ninterface FrameStripOptions {\n readonly presets: readonly FramePreset[];\n readonly initialActiveId: FramePresetId;\n readonly labels?: Partial<Record<FramePresetId, string>> | undefined;\n readonly source: SourceImage;\n readonly initialState: FrameState;\n onPresetClick(preset: FramePreset): void;\n}\n\ninterface FrameStrip {\n readonly container: HTMLDivElement;\n setActive(id: FramePresetId): void;\n}\n\nfunction buildFrameStrip(options: FrameStripOptions): FrameStrip {\n const container = document.createElement('div');\n container.className = 'kalotyp-frame-strip-wrap';\n container.setAttribute('role', 'radiogroup');\n container.setAttribute('aria-label', 'Frame presets');\n\n const list = document.createElement('div');\n list.className = 'kalotyp-frame-strip';\n container.appendChild(list);\n\n const buttons = new Map<FramePresetId, HTMLButtonElement>();\n for (const preset of options.presets) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-frame-thumb';\n button.dataset.presetId = preset.id;\n button.setAttribute('role', 'radio');\n button.setAttribute('aria-checked', 'false');\n const label = options.labels?.[preset.id] ?? preset.label;\n button.setAttribute('aria-label', `${label} frame`);\n button.title = label;\n\n const thumbWrap = document.createElement('span');\n thumbWrap.className = 'kalotyp-frame-thumb-image';\n const canvas = renderFrameThumbnail(preset, options.source, options.initialState.color);\n canvas.classList.add('kalotyp-frame-thumb-canvas');\n thumbWrap.appendChild(canvas);\n\n const activeBadge = document.createElement('span');\n activeBadge.className = 'kalotyp-frame-thumb-check';\n activeBadge.setAttribute('aria-hidden', 'true');\n activeBadge.innerHTML = '✓';\n thumbWrap.appendChild(activeBadge);\n\n const labelEl = document.createElement('span');\n labelEl.className = 'kalotyp-frame-thumb-label';\n labelEl.textContent = label;\n\n button.appendChild(thumbWrap);\n button.appendChild(labelEl);\n button.addEventListener('click', () => options.onPresetClick(preset));\n\n list.appendChild(button);\n buttons.set(preset.id, button);\n }\n\n function setActive(id: FramePresetId): void {\n for (const [presetId, button] of buttons) {\n const isActive = presetId === id;\n button.setAttribute('aria-checked', isActive ? 'true' : 'false');\n button.classList.toggle('kalotyp-frame-thumb--active', isActive);\n }\n }\n\n setActive(options.initialActiveId);\n\n return { container, setActive };\n}\n\ninterface ColourRowOptions {\n readonly initialColor: string;\n readonly initialPresetId: FramePresetId;\n onColorChange(color: string): void;\n}\n\ninterface ColourRow {\n readonly container: HTMLDivElement;\n setColor(color: string): void;\n setEnabled(enabled: boolean): void;\n}\n\nfunction buildColourRow(options: ColourRowOptions): ColourRow {\n const container = document.createElement('div');\n container.className = 'kalotyp-frame-color-row';\n\n const label = document.createElement('span');\n label.className = 'kalotyp-frame-color-label';\n label.textContent = 'Colour';\n\n const colorInput = document.createElement('input');\n colorInput.type = 'color';\n colorInput.className = 'kalotyp-frame-color';\n colorInput.value = normaliseColorForInput(options.initialColor);\n colorInput.setAttribute('aria-label', 'Frame colour (visual picker)');\n colorInput.addEventListener('change', () => options.onColorChange(colorInput.value));\n\n const hexInput = document.createElement('input');\n hexInput.type = 'text';\n hexInput.className = 'kalotyp-frame-hex';\n hexInput.value = normaliseColorForInput(options.initialColor);\n hexInput.maxLength = 7;\n hexInput.spellcheck = false;\n hexInput.autocomplete = 'off';\n hexInput.setAttribute('aria-label', 'Frame colour hex code');\n hexInput.setAttribute('placeholder', '#000000');\n hexInput.addEventListener('change', () => {\n const value = hexInput.value.trim();\n const normalised = normaliseHexInput(value);\n if (normalised) {\n hexInput.value = normalised;\n options.onColorChange(normalised);\n } else {\n hexInput.value = colorInput.value;\n }\n });\n\n // Helper text that explains the disabled state under the None\n // preset (Phase 6.6 polish). Hidden when colour customisation is\n // available. `aria-live` so screen-reader users hear the\n // explanation when they switch presets.\n const hint = document.createElement('span');\n hint.className = 'kalotyp-frame-color-hint';\n hint.textContent = 'Pick a frame preset to choose a colour.';\n hint.setAttribute('aria-live', 'polite');\n\n container.appendChild(label);\n container.appendChild(colorInput);\n container.appendChild(hexInput);\n container.appendChild(hint);\n\n // None doesn't accept colour customisation; disable the inputs to\n // avoid the user wasting time on a control that has no effect.\n function setEnabled(enabled: boolean): void {\n colorInput.disabled = !enabled;\n hexInput.disabled = !enabled;\n hint.hidden = enabled;\n }\n function setColor(color: string): void {\n const target = normaliseColorForInput(color);\n if (colorInput.value !== target) colorInput.value = target;\n if (hexInput.value.toLowerCase() !== target.toLowerCase()) hexInput.value = target;\n }\n setEnabled(options.initialPresetId !== 'none');\n\n return { container, setColor, setEnabled };\n}\n\n/**\n * Render a small preview canvas of the source under a given frame\n * preset. The thumbnails fit inside an 80×60 box (matching the filter\n * strip's max), and the source is downscaled to fit. The bake we\n * use here is the same path the chain uses on Save — `bakeFrame` is\n * synchronous-shaped (Promise-returning but resolves immediately),\n * so we render synchronously by reading the resulting canvas\n * bitmap into the thumbnail.\n *\n * For Polaroid the bake output is larger than the input, so the\n * thumbnail's aspect ratio differs from the other presets. We\n * re-letterbox the polaroid output into the same 80×60 box so the\n * strip's row height stays consistent.\n */\nfunction renderFrameThumbnail(\n preset: FramePreset,\n source: SourceImage,\n color: string,\n): HTMLCanvasElement {\n const max = { width: 80, height: 60 };\n const out = frameOutputSize(preset.id, source.width, source.height);\n const ratio = Math.min(max.width / out.width, max.height / out.height);\n const w = Math.max(1, Math.floor(out.width * ratio));\n const h = Math.max(1, Math.floor(out.height * ratio));\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n\n const canvas = document.createElement('canvas');\n canvas.width = Math.max(1, Math.round(w * dpr));\n canvas.height = Math.max(1, Math.round(h * dpr));\n canvas.style.width = `${w}px`;\n canvas.style.height = `${h}px`;\n const ctx = canvas.getContext('2d');\n if (!ctx) return canvas;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n\n // Render the source + frame at the thumbnail's display size. We\n // can't reuse `bakeFrame` directly because the bake produces an\n // output at the frame's full output dimensions; the thumbnail is\n // a downscale of that. So we approximate by drawing inline.\n if (preset.id === 'polaroid') {\n ctx.fillStyle = color;\n ctx.fillRect(0, 0, w, h);\n const shorter = Math.min(source.width, source.height);\n const innerOffsetX = Math.round(((shorter * 0.05) / out.width) * w);\n const innerOffsetY = Math.round(((shorter * 0.05) / out.height) * h);\n const innerW = Math.round((source.width / out.width) * w);\n const innerH = Math.round((source.height / out.height) * h);\n ctx.drawImage(source.bitmap, innerOffsetX, innerOffsetY, innerW, innerH);\n } else {\n ctx.drawImage(source.bitmap, 0, 0, w, h);\n if (preset.id !== 'none') {\n paintInsideFrame(ctx, preset.id, color, w, h);\n }\n }\n\n return canvas;\n}\n\nfunction normaliseColorForInput(color: string): string {\n if (/^#[0-9a-fA-F]{6}$/.test(color)) return color;\n if (/^#[0-9a-fA-F]{3}$/.test(color)) {\n const r = color[1];\n const g = color[2];\n const b = color[3];\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return '#000000';\n}\n\nfunction normaliseHexInput(value: string): string | null {\n const cleaned = value.startsWith('#') ? value.slice(1) : value;\n if (/^[0-9a-fA-F]{6}$/.test(cleaned)) return `#${cleaned.toLowerCase()}`;\n if (/^[0-9a-fA-F]{3}$/.test(cleaned)) {\n const r = cleaned[0];\n const g = cleaned[1];\n const b = cleaned[2];\n return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();\n }\n return null;\n}\n","import {\n bakeFrame,\n type FramePresetId,\n type FrameState,\n initialFrameState,\n type UtilityPlugin,\n} from '@magicpages/kalotyp-core';\nimport { mountFrameUtility } from './mount.js';\n\nexport interface FramePluginOptions {\n /** Where the plugin's panel UI mounts. */\n readonly panelHost: HTMLElement;\n /**\n * Optional locale labels for the six presets. The Ghost adapter\n * passes Ghost's resolved `frameOptions[i][1](locale)` strings here\n * so the strip uses the user's localised labels; the playground\n * passes nothing and the strip falls back to the defaults from the\n * core preset list.\n */\n readonly labels?: Partial<Record<FramePresetId, string>> | undefined;\n}\n\nexport function createFramePlugin(options: FramePluginOptions): UtilityPlugin<FrameState> {\n return {\n id: 'frame',\n init: () => initialFrameState(),\n mount(stageHost, ctx, store) {\n const handle = mountFrameUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n labels: options.labels,\n onCommit: () => ctx.bus.emit('commit', { utility: 'frame' }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: (state, source) => bakeFrame(state, source),\n };\n}\n","/**\n * Per-region coordinate-input row for the redact plugin's keyboard\n * placement path. Same shape as the annotate\n * plugin's coord-inputs.ts but with one shape kind (rect) and four\n * fields (Left, Top, Width, Height).\n */\n\nimport type { RedactRegion } from '@magicpages/kalotyp-core';\n\nexport interface RedactCoordInputsOptions {\n onRegionChanged(region: RedactRegion): void;\n}\n\nexport interface RedactCoordInputsHandle {\n readonly container: HTMLDivElement;\n /** Show inputs for the given region, or hide when null. */\n updateForRegion(region: RedactRegion | null): void;\n destroy(): void;\n}\n\ninterface FieldSpec {\n readonly id: 'x' | 'y' | 'width' | 'height';\n readonly label: string;\n readonly min?: number;\n}\n\nconst FIELDS: ReadonlyArray<FieldSpec> = [\n { id: 'x', label: 'Left' },\n { id: 'y', label: 'Top' },\n { id: 'width', label: 'Width', min: 1 },\n { id: 'height', label: 'Height', min: 1 },\n];\n\nexport function buildRedactCoordInputs(options: RedactCoordInputsOptions): RedactCoordInputsHandle {\n const container = document.createElement('div');\n container.className = 'kalotyp-redact-coords';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Selected redaction position');\n container.hidden = true;\n\n let activeRegion: RedactRegion | null = null;\n const inputs = new Map<FieldSpec['id'], HTMLInputElement>();\n\n for (const spec of FIELDS) {\n const wrapper = document.createElement('label');\n wrapper.className = 'kalotyp-redact-coords-field';\n\n const labelSpan = document.createElement('span');\n labelSpan.className = 'kalotyp-redact-coords-label';\n labelSpan.textContent = spec.label;\n\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'kalotyp-redact-coords-input';\n input.dataset.field = spec.id;\n input.step = '1';\n input.inputMode = 'numeric';\n if (spec.min !== undefined) input.min = String(spec.min);\n input.setAttribute('aria-label', `${spec.label} (pixels)`);\n input.addEventListener('change', onAnyInputChange);\n\n wrapper.appendChild(labelSpan);\n wrapper.appendChild(input);\n container.appendChild(wrapper);\n inputs.set(spec.id, input);\n }\n\n function syncValuesFromRegion(region: RedactRegion): void {\n const setVal = (id: FieldSpec['id'], value: number): void => {\n const el = inputs.get(id);\n if (!el) return;\n const next = String(Math.round(value));\n // Skip the assignment if the user is mid-edit on the input —\n // overwriting a focused, partially-typed value is the most\n // surprising thing an a11y helper can do.\n if (document.activeElement === el) return;\n if (el.value !== next) el.value = next;\n };\n setVal('x', region.x);\n setVal('y', region.y);\n setVal('width', region.width);\n setVal('height', region.height);\n }\n\n function onAnyInputChange(): void {\n if (!activeRegion) return;\n const x = readNumber('x');\n const y = readNumber('y');\n const width = readNumber('width');\n const height = readNumber('height');\n if (![x, y, width, height].every(Number.isFinite)) return;\n const next: RedactRegion = {\n ...activeRegion,\n x,\n y,\n width,\n height,\n };\n if (\n next.x === activeRegion.x &&\n next.y === activeRegion.y &&\n next.width === activeRegion.width &&\n next.height === activeRegion.height\n ) {\n return;\n }\n activeRegion = next;\n options.onRegionChanged(next);\n }\n\n function readNumber(id: FieldSpec['id']): number {\n const el = inputs.get(id);\n if (!el) return Number.NaN;\n return Math.round(el.valueAsNumber);\n }\n\n return {\n container,\n updateForRegion(region): void {\n if (!region) {\n activeRegion = null;\n container.hidden = true;\n return;\n }\n activeRegion = region;\n syncValuesFromRegion(region);\n container.hidden = false;\n },\n destroy(): void {\n container.replaceChildren();\n inputs.clear();\n container.remove();\n },\n };\n}\n","/**\n * Build the redact panel: mode toggle, optional colour picker, the\n * per-selection coordinate inputs row, and Insert/Delete actions.\n *\n * Same panel-rhythm conventions as the annotate plugin (Phase 6.3) —\n * a toolbar of mode buttons, then a style row with colour + Insert +\n * Delete, then a slot for the coordinate inputs row mounted by the\n * mount layer.\n */\n\nimport type { RedactMode } from '@magicpages/kalotyp-core';\nimport { icon } from '../../icons.js';\n\nexport interface RedactPanelOptions {\n readonly initialMode: RedactMode;\n readonly initialColor: string;\n readonly canDelete: boolean;\n /** Where the per-selection coordinate-input row mounts. */\n readonly coordInputs: HTMLElement;\n onSelectMode(mode: RedactMode): void;\n onColorChange(color: string): void;\n onDeleteSelected(): void;\n onInsertAtCenter(): void;\n}\n\nexport interface RedactPanel {\n readonly container: HTMLDivElement;\n readonly modeButtons: ReadonlyMap<RedactMode, HTMLButtonElement>;\n readonly colorInput: HTMLInputElement;\n readonly hexInput: HTMLInputElement;\n readonly insertButton: HTMLButtonElement;\n readonly deleteButton: HTMLButtonElement;\n setActiveMode(mode: RedactMode): void;\n setColor(color: string): void;\n setCanDelete(canDelete: boolean): void;\n}\n\nconst MODE_DEFS: ReadonlyArray<{ id: RedactMode; label: string }> = [\n { id: 'pixelate', label: 'Pixelate' },\n { id: 'blur', label: 'Blur' },\n { id: 'solid', label: 'Solid fill' },\n];\n\nexport function buildRedactPanel(options: RedactPanelOptions): RedactPanel {\n const container = document.createElement('div');\n container.className = 'kalotyp-redact-panel';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Redact');\n\n // ----- Mode toolbar -----\n const toolbar = document.createElement('div');\n toolbar.className = 'kalotyp-redact-toolbar';\n toolbar.setAttribute('role', 'radiogroup');\n toolbar.setAttribute('aria-label', 'Redaction mode');\n\n const modeButtons = new Map<RedactMode, HTMLButtonElement>();\n for (const def of MODE_DEFS) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-redact-mode';\n button.dataset.mode = def.id;\n button.setAttribute('role', 'radio');\n button.setAttribute('aria-checked', def.id === options.initialMode ? 'true' : 'false');\n button.setAttribute('aria-label', `${def.label} redaction`);\n button.title = def.label;\n button.textContent = def.label;\n button.addEventListener('click', () => options.onSelectMode(def.id));\n toolbar.appendChild(button);\n modeButtons.set(def.id, button);\n }\n\n // ----- Style row -----\n const styleRow = document.createElement('div');\n styleRow.className = 'kalotyp-redact-style-row';\n\n // Colour picker for the `solid` mode. We always render the controls\n // and disable them when the mode isn't `solid`, instead of hiding,\n // so the panel layout doesn't reflow as the user switches modes.\n const colorInput = document.createElement('input');\n colorInput.type = 'color';\n colorInput.className = 'kalotyp-redact-color';\n colorInput.value = normaliseColorForInput(options.initialColor);\n colorInput.setAttribute('aria-label', 'Solid fill colour (visual picker)');\n colorInput.addEventListener('change', () => options.onColorChange(colorInput.value));\n\n const hexInput = document.createElement('input');\n hexInput.type = 'text';\n hexInput.className = 'kalotyp-redact-hex';\n hexInput.value = normaliseColorForInput(options.initialColor);\n hexInput.maxLength = 7;\n hexInput.spellcheck = false;\n hexInput.autocomplete = 'off';\n hexInput.setAttribute('aria-label', 'Solid fill hex code');\n hexInput.setAttribute('placeholder', '#000000');\n hexInput.addEventListener('change', () => {\n const value = hexInput.value.trim();\n const normalised = normaliseHexInput(value);\n if (normalised) {\n hexInput.value = normalised;\n options.onColorChange(normalised);\n } else {\n hexInput.value = colorInput.value;\n }\n });\n\n const insertButton = document.createElement('button');\n insertButton.type = 'button';\n insertButton.className = 'kalotyp-redact-insert';\n insertButton.innerHTML = `${icon('plus')}<span>Insert at centre</span>`;\n insertButton.setAttribute('aria-label', 'Insert redaction region at image centre');\n insertButton.title = 'Insert at centre';\n insertButton.addEventListener('click', () => options.onInsertAtCenter());\n\n const deleteButton = document.createElement('button');\n deleteButton.type = 'button';\n deleteButton.className = 'kalotyp-redact-delete';\n deleteButton.innerHTML = `${icon('delete')}<span>Delete</span>`;\n deleteButton.setAttribute('aria-label', 'Delete selected redaction region');\n deleteButton.title = 'Delete (Del)';\n deleteButton.disabled = !options.canDelete;\n deleteButton.addEventListener('click', () => options.onDeleteSelected());\n\n // Group colour controls so they wrap together as a unit (Phase 6.6\n // mobile-layout fix: previously the row could split with the hint\n // floating alone). The Insert/Delete pair is its own group so they\n // also stay together.\n const colorGroup = document.createElement('div');\n colorGroup.className = 'kalotyp-redact-color-group';\n colorGroup.appendChild(colorInput);\n colorGroup.appendChild(hexInput);\n\n // Helper text that explains why the colour controls are disabled\n // outside Solid fill mode. Hidden when the mode is `solid` (the\n // controls are usable). `aria-live` so screen-reader users hear\n // the explanation when they switch modes.\n const colorHint = document.createElement('span');\n colorHint.className = 'kalotyp-redact-color-hint';\n colorHint.textContent = 'Colour applies to Solid fill only.';\n colorHint.setAttribute('aria-live', 'polite');\n\n const buttonGroup = document.createElement('div');\n buttonGroup.className = 'kalotyp-redact-button-group';\n buttonGroup.appendChild(insertButton);\n buttonGroup.appendChild(deleteButton);\n\n styleRow.appendChild(colorGroup);\n styleRow.appendChild(colorHint);\n styleRow.appendChild(buttonGroup);\n\n container.appendChild(toolbar);\n container.appendChild(styleRow);\n container.appendChild(options.coordInputs);\n\n function setActiveMode(mode: RedactMode): void {\n for (const [id, button] of modeButtons) {\n button.setAttribute('aria-checked', id === mode ? 'true' : 'false');\n button.classList.toggle('kalotyp-redact-mode--active', id === mode);\n }\n const isSolid = mode === 'solid';\n colorInput.disabled = !isSolid;\n hexInput.disabled = !isSolid;\n colorHint.hidden = isSolid;\n }\n\n function setColor(color: string): void {\n const target = normaliseColorForInput(color);\n if (colorInput.value !== target) colorInput.value = target;\n if (hexInput.value.toLowerCase() !== target.toLowerCase()) hexInput.value = target;\n }\n\n function setCanDelete(canDelete: boolean): void {\n deleteButton.disabled = !canDelete;\n }\n\n setActiveMode(options.initialMode);\n\n return {\n container,\n modeButtons,\n colorInput,\n hexInput,\n insertButton,\n deleteButton,\n setActiveMode,\n setColor,\n setCanDelete,\n };\n}\n\nfunction normaliseColorForInput(color: string): string {\n if (/^#[0-9a-fA-F]{6}$/.test(color)) return color;\n if (/^#[0-9a-fA-F]{3}$/.test(color)) {\n const r = color[1];\n const g = color[2];\n const b = color[3];\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return '#000000';\n}\n\nfunction normaliseHexInput(value: string): string | null {\n const cleaned = value.startsWith('#') ? value.slice(1) : value;\n if (/^[0-9a-fA-F]{6}$/.test(cleaned)) return `#${cleaned.toLowerCase()}`;\n if (/^[0-9a-fA-F]{3}$/.test(cleaned)) {\n const r = cleaned[0];\n const g = cleaned[1];\n const b = cleaned[2];\n return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();\n }\n return null;\n}\n","/**\n * Render helpers for the redact plugin's three canvas layers. The\n * approach mirrors the annotate plugin's stacked layers exactly:\n *\n * - `paintRedactImageLayer` draws the upstream-baked source onto the\n * bottom canvas. Called once on mount and on stage resize.\n * - `paintRedactRegionsLayer` draws every committed region's preview\n * representation on top of the image. Pixelate/blur are baked\n * into the layer here so the live stage matches the export. Solid\n * regions are simple coloured rectangles.\n * - `paintRedactLiveLayer` draws the in-progress region (during a\n * drag) in display space — a dashed marquee with the current mode\n * rendered as a translucent fill so the user gets a feel for what\n * they'll get on commit.\n *\n * The regions layer reuses the core `paintRegion` so the visible\n * preview is byte-equal to the bake output (per region). The image\n * argument is only needed for pixelate/blur — solid doesn't read it.\n */\n\nimport {\n paintRedactRegion,\n type RedactRegion,\n type SourceImage,\n type Viewport,\n} from '@magicpages/kalotyp-core';\n\n/**\n * Resize the canvas's backing store to stage CSS pixels × DPR and\n * return its 2D context already scaled into CSS-pixel space. Returns\n * `null` if the context is unavailable (jsdom path).\n */\nfunction prepareCanvas(\n canvas: HTMLCanvasElement,\n stageWidth: number,\n stageHeight: number,\n): CanvasRenderingContext2D | null {\n const dpr = Math.max(1, window.devicePixelRatio || 1);\n const targetW = Math.max(1, Math.round(stageWidth * dpr));\n const targetH = Math.max(1, Math.round(stageHeight * dpr));\n if (canvas.width !== targetW) canvas.width = targetW;\n if (canvas.height !== targetH) canvas.height = targetH;\n canvas.style.width = `${stageWidth}px`;\n canvas.style.height = `${stageHeight}px`;\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, stageWidth, stageHeight);\n return ctx;\n}\n\nexport function paintRedactImageLayer(\n canvas: HTMLCanvasElement,\n source: SourceImage,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const ctx = prepareCanvas(canvas, stageWidth, stageHeight);\n if (!ctx) return;\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(\n source.bitmap,\n viewport.displayRect.x,\n viewport.displayRect.y,\n viewport.displayRect.width,\n viewport.displayRect.height,\n );\n}\n\n/**\n * Paint every region onto the regions canvas. Pixelate and blur read\n * from the supplied image-source bitmap (drawn at viewport scale) so\n * the preview is the same composition the bake produces. We draw the\n * image into an off-screen canvas at the viewport scale, then ask\n * the core `paintRegion` to redact each region's pixels in turn.\n *\n * The trade-off here: regions drawn during the live preview composite\n * onto the image at *display* pixels (not source pixels), so the\n * pixelate grid size in the preview differs slightly from the export.\n * The bake re-runs at source resolution; the user sees the right\n * thing on the way out.\n */\nexport function paintRedactRegionsLayer(\n canvas: HTMLCanvasElement,\n source: SourceImage,\n regions: ReadonlyArray<RedactRegion>,\n selectedId: string | null,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const ctx = prepareCanvas(canvas, stageWidth, stageHeight);\n if (!ctx) return;\n if (regions.length === 0) return;\n\n // Build a temporary canvas the size of the visible image rect that\n // mirrors the source image; `paintRegion` will read pixels from it\n // for pixelate/blur. Drawing into this buffer first means the\n // composite-so-far is the previous regions baked in, which matches\n // the export ordering.\n const tempCanvas = document.createElement('canvas');\n const dispW = Math.max(1, Math.round(viewport.displayRect.width));\n const dispH = Math.max(1, Math.round(viewport.displayRect.height));\n tempCanvas.width = dispW;\n tempCanvas.height = dispH;\n const tempCtx = tempCanvas.getContext('2d');\n if (!tempCtx) return;\n tempCtx.imageSmoothingEnabled = true;\n tempCtx.imageSmoothingQuality = 'high';\n tempCtx.drawImage(source.bitmap, 0, 0, dispW, dispH);\n\n // Project each region's image-space rect into the temp canvas's\n // display-pixel space and ask the core renderer to paint it.\n for (const region of regions) {\n const projected: RedactRegion = {\n ...region,\n x: region.x * viewport.scale,\n y: region.y * viewport.scale,\n width: region.width * viewport.scale,\n height: region.height * viewport.scale,\n };\n paintRedactRegion(tempCtx, tempCanvas, projected, {\n bitmap: tempCanvas,\n width: dispW,\n height: dispH,\n mimeType: 'image/png',\n });\n }\n\n // Draw the redacted image preview into the regions canvas at the\n // viewport's display position so it sits exactly over the image\n // layer. Use destination-over composite so a future overlay on\n // the same canvas wouldn't swallow earlier paints, though we\n // currently only paint regions here.\n ctx.drawImage(tempCanvas, viewport.displayRect.x, viewport.displayRect.y, dispW, dispH);\n\n // Draw a thin outline around every region so the user can see\n // where the redaction sits. The selected region gets a brighter\n // accent stroke (the selection-handle layer adds the corner\n // handles on top of this).\n for (const region of regions) {\n const x = viewport.displayRect.x + region.x * viewport.scale;\n const y = viewport.displayRect.y + region.y * viewport.scale;\n const w = region.width * viewport.scale;\n const h = region.height * viewport.scale;\n ctx.save();\n if (region.id === selectedId) {\n ctx.strokeStyle = 'rgba(99, 102, 241, 0.9)';\n ctx.lineWidth = 1.5;\n } else {\n ctx.strokeStyle = 'rgba(255, 255, 255, 0.6)';\n ctx.lineWidth = 1;\n }\n ctx.setLineDash([4, 3]);\n ctx.strokeRect(x + 0.5, y + 0.5, Math.max(0, w - 1), Math.max(0, h - 1));\n ctx.restore();\n }\n}\n\n/**\n * Draw a marquee for the in-progress drag. We don't bake pixelate /\n * blur here — pixelate at the live drag's pixel grid is jittery and\n * doesn't help the user understand the result. Instead we paint a\n * translucent fill in the mode's signature colour:\n *\n * - `solid` → user-chosen colour at 70% alpha\n * - `pixelate` → neutral grey at 60% alpha + a \"pixelate\" texture\n * (a small grid of squares) so the user knows what they're getting\n * - `blur` → low-alpha blue-grey, signalling \"softening\" without\n * actually blurring (we'd have to repeatedly re-read the canvas;\n * the export bake handles the real blur).\n */\nexport function paintRedactLiveLayer(\n canvas: HTMLCanvasElement,\n marquee: {\n x: number;\n y: number;\n width: number;\n height: number;\n mode: RedactRegion['mode'];\n color: string;\n } | null,\n stageWidth: number,\n stageHeight: number,\n viewport: Viewport,\n): void {\n const ctx = prepareCanvas(canvas, stageWidth, stageHeight);\n if (!ctx) return;\n if (!marquee) return;\n\n const dx = viewport.displayRect.x + marquee.x * viewport.scale;\n const dy = viewport.displayRect.y + marquee.y * viewport.scale;\n let dw = marquee.width * viewport.scale;\n let dh = marquee.height * viewport.scale;\n let drawX = dx;\n let drawY = dy;\n if (dw < 0) {\n drawX = dx + dw;\n dw = -dw;\n }\n if (dh < 0) {\n drawY = dy + dh;\n dh = -dh;\n }\n ctx.save();\n ctx.fillStyle = marqueeFillFor(marquee.mode, marquee.color);\n ctx.fillRect(drawX, drawY, dw, dh);\n ctx.strokeStyle = 'rgba(99, 102, 241, 0.95)';\n ctx.lineWidth = 1.5;\n ctx.setLineDash([4, 3]);\n ctx.strokeRect(drawX + 0.75, drawY + 0.75, Math.max(0, dw - 1.5), Math.max(0, dh - 1.5));\n ctx.restore();\n}\n\nfunction marqueeFillFor(mode: RedactRegion['mode'], color: string): string {\n switch (mode) {\n case 'solid':\n return hexWithAlpha(color, 0.7);\n case 'pixelate':\n return 'rgba(120, 120, 120, 0.6)';\n case 'blur':\n return 'rgba(180, 200, 220, 0.45)';\n }\n}\n\n/**\n * Build an `rgba(...)` string from a `#rrggbb` hex and an alpha. Falls\n * back to the input string if the hex doesn't parse (e.g. a CSS\n * keyword) so the live preview still draws something.\n */\nfunction hexWithAlpha(hex: string, alpha: number): string {\n const match = /^#([0-9a-fA-F]{6})$/.exec(hex);\n if (!match) return hex;\n const value = match[1];\n if (!value) return hex;\n const r = Number.parseInt(value.slice(0, 2), 16);\n const g = Number.parseInt(value.slice(2, 4), 16);\n const b = Number.parseInt(value.slice(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n","/**\n * Selection rendering and per-handle resize gesture for redaction\n * regions. The annotate plugin's `selection.ts` does the same job for\n * shapes; the redact version is simpler because there's only one\n * shape kind (rectangle), so we can wire the corner/edge handles\n * directly to a rect-resize without a per-kind dispatch.\n */\n\nimport {\n ALL_SELECTION_HANDLES,\n type Rect,\n type RedactRegion,\n type RedactState,\n rectFromHandleDrag,\n replaceRedactRegion,\n type SelectionHandle,\n type Store,\n selectedRedactRegionOf,\n type Viewport,\n} from '@magicpages/kalotyp-core';\nimport type { DragHandlers } from '../annotate/pointer-drag.js';\nimport { attachPointerDrag } from '../annotate/pointer-drag.js';\n\nexport interface RedactSelectionLayerOptions {\n readonly host: HTMLDivElement;\n readonly store: Store<RedactState>;\n /** Project a raw client-space pointer to image-space pixels. */\n toImageSpace(point: { clientX: number; clientY: number }): { x: number; y: number };\n /** Read the current viewport at gesture start. */\n getViewport(): Viewport;\n /** Emit a history-commit at gesture end. */\n commit(): void;\n}\n\nexport interface RedactSelectionLayer {\n update(region: RedactRegion | null, viewport: Viewport): void;\n destroy(): void;\n}\n\nexport function buildRedactSelectionLayer(\n options: RedactSelectionLayerOptions,\n): RedactSelectionLayer {\n const { host, store } = options;\n const handleEls = new Map<SelectionHandle, HTMLButtonElement>();\n const cleanups: Array<() => void> = [];\n\n for (const direction of ALL_SELECTION_HANDLES) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-redact-handle';\n button.dataset.direction = direction;\n button.setAttribute('aria-label', handleLabel(direction));\n // Same a11y rationale as the annotate plugin: handles are\n // pointer-driven; keyboard users use the coordinate inputs.\n // Exclude from tab order to keep the Tab walk meaningful.\n button.tabIndex = -1;\n button.style.display = 'none';\n handleEls.set(direction, button);\n host.appendChild(button);\n\n cleanups.push(attachPointerDrag(button, (event) => startHandleResizeGesture(direction, event)));\n }\n\n function update(region: RedactRegion | null, viewport: Viewport): void {\n if (!region) {\n for (const [, button] of handleEls) button.style.display = 'none';\n return;\n }\n const box: Rect = {\n x: region.x,\n y: region.y,\n width: region.width,\n height: region.height,\n };\n const positions = handlePositionsFor(box);\n for (const direction of ALL_SELECTION_HANDLES) {\n const handle = handleEls.get(direction);\n if (!handle) continue;\n const display = imageToDisplay(positions[direction], viewport);\n handle.style.display = '';\n handle.style.left = `${display.x}px`;\n handle.style.top = `${display.y}px`;\n }\n }\n\n function destroy(): void {\n for (const cleanup of cleanups) cleanup();\n for (const [, button] of handleEls) button.remove();\n handleEls.clear();\n }\n\n function startHandleResizeGesture(\n direction: SelectionHandle,\n origin: PointerEvent,\n ): DragHandlers | null {\n const initial = selectedRedactRegionOf(store.get());\n if (!initial) return null;\n void origin;\n return {\n onMove(point) {\n const image = options.toImageSpace(point);\n const box: Rect = {\n x: initial.x,\n y: initial.y,\n width: initial.width,\n height: initial.height,\n };\n const next = rectFromHandleDrag(box, direction, image);\n // Allow negative width/height during the drag (the live\n // preview matches the user's gesture); the commit step\n // normalises by sign-flipping. Rect coords go straight onto\n // the region so the regions canvas re-renders the new shape.\n store.update((current) =>\n replaceRedactRegion(current, {\n ...initial,\n x: next.x,\n y: next.y,\n width: next.width,\n height: next.height,\n }),\n );\n },\n onCommit() {\n // Normalise the rect's sign on commit. We re-read the store\n // because the active gesture may have left negative dims;\n // the bake assumes non-negative.\n const region = selectedRedactRegionOf(store.get());\n if (region && (region.width < 0 || region.height < 0)) {\n let { x, y, width, height } = region;\n if (width < 0) {\n x += width;\n width = -width;\n }\n if (height < 0) {\n y += height;\n height = -height;\n }\n store.update((current) =>\n replaceRedactRegion(current, { ...region, x, y, width, height }),\n );\n }\n options.commit();\n },\n onCancel() {\n store.update((current) => replaceRedactRegion(current, initial));\n },\n };\n }\n\n return { update, destroy };\n}\n\nfunction handlePositionsFor(rect: Rect): Record<SelectionHandle, { x: number; y: number }> {\n const left = rect.x;\n const right = rect.x + rect.width;\n const top = rect.y;\n const bottom = rect.y + rect.height;\n const cx = rect.x + rect.width / 2;\n const cy = rect.y + rect.height / 2;\n return {\n tl: { x: left, y: top },\n tr: { x: right, y: top },\n bl: { x: left, y: bottom },\n br: { x: right, y: bottom },\n t: { x: cx, y: top },\n r: { x: right, y: cy },\n b: { x: cx, y: bottom },\n l: { x: left, y: cy },\n };\n}\n\nfunction imageToDisplay(\n point: { x: number; y: number },\n viewport: Viewport,\n): { x: number; y: number } {\n return {\n x: viewport.displayRect.x + point.x * viewport.scale,\n y: viewport.displayRect.y + point.y * viewport.scale,\n };\n}\n\nfunction handleLabel(direction: SelectionHandle): string {\n switch (direction) {\n case 'tl':\n return 'Resize from top-left';\n case 'tr':\n return 'Resize from top-right';\n case 'bl':\n return 'Resize from bottom-left';\n case 'br':\n return 'Resize from bottom-right';\n case 't':\n return 'Resize from top';\n case 'r':\n return 'Resize from right';\n case 'b':\n return 'Resize from bottom';\n case 'l':\n return 'Resize from left';\n }\n}\n","/**\n * Build the layered DOM for the redact plugin's stage. Same structural\n * shape as the annotate plugin (image canvas, regions canvas, live\n * canvas, hit-area, handles layer) — redact reuses the same selection\n * + drag-to-define interaction model with one shape kind, so keeping\n * the DOM rhythm aligned across plugins is the cleanest choice.\n *\n * No text overlay: redactions don't carry typed content.\n */\n\nexport interface RedactStageElements {\n readonly container: HTMLDivElement;\n readonly imageCanvas: HTMLCanvasElement;\n readonly regionsCanvas: HTMLCanvasElement;\n readonly liveCanvas: HTMLCanvasElement;\n readonly hitArea: HTMLDivElement;\n readonly handlesLayer: HTMLDivElement;\n}\n\nexport function buildRedactStage(): RedactStageElements {\n const container = document.createElement('div');\n container.className = 'kalotyp-redact-stage';\n\n const imageCanvas = document.createElement('canvas');\n imageCanvas.className = 'kalotyp-redact-image';\n imageCanvas.setAttribute('aria-hidden', 'true');\n\n const regionsCanvas = document.createElement('canvas');\n regionsCanvas.className = 'kalotyp-redact-regions';\n regionsCanvas.setAttribute('aria-hidden', 'true');\n\n const liveCanvas = document.createElement('canvas');\n liveCanvas.className = 'kalotyp-redact-live';\n liveCanvas.setAttribute('aria-hidden', 'true');\n\n const hitArea = document.createElement('div');\n hitArea.className = 'kalotyp-redact-hit';\n hitArea.setAttribute('role', 'presentation');\n\n const handlesLayer = document.createElement('div');\n handlesLayer.className = 'kalotyp-redact-handles';\n handlesLayer.setAttribute('role', 'group');\n handlesLayer.setAttribute('aria-label', 'Selected redaction region');\n\n container.appendChild(imageCanvas);\n container.appendChild(regionsCanvas);\n container.appendChild(liveCanvas);\n container.appendChild(hitArea);\n container.appendChild(handlesLayer);\n\n return { container, imageCanvas, regionsCanvas, liveCanvas, hitArea, handlesLayer };\n}\n","/**\n * Mount the redact plugin's stage UI and wire up:\n * - the layered canvases (image / regions / live);\n * - the bottom panel (mode toolbar, colour picker, insert/delete);\n * - pointer drag-to-define for new regions;\n * - the selection layer (corner handles + per-handle resize);\n * - the per-region coordinate inputs (Phase 6.4 keyboard a11y);\n * - keyboard shortcuts (Delete/Backspace removes selection; Esc\n * deselects; arrow keys nudge the selected region);\n * - revalidation against upstream-bounds changes.\n */\n\nimport {\n addRegion,\n computeViewport,\n createCenteredRedactRegion,\n deleteRedactRegion,\n mintRegionId,\n normaliseRedactExtent,\n type Point,\n pointDisplayToImage,\n type RedactMode,\n type RedactRegion,\n type RedactState,\n replaceRedactRegion,\n revalidateRedactAgainstBounds,\n type SourceImage,\n type Store,\n selectedRedactRegionOf,\n selectRedactRegion,\n setRedactCurrentColor,\n setRedactCurrentMode,\n setRedactRegionColor,\n setRedactRegionMode,\n type Viewport,\n type ViewportController,\n} from '@magicpages/kalotyp-core';\nimport { attachPointerDrag, clientToElement, type DragHandlers } from '../annotate/pointer-drag.js';\nimport { buildRedactCoordInputs } from './coord-inputs.js';\nimport { buildRedactPanel, type RedactPanel } from './panel.js';\nimport { paintRedactImageLayer, paintRedactLiveLayer, paintRedactRegionsLayer } from './render.js';\nimport { buildRedactSelectionLayer } from './selection.js';\nimport { buildRedactStage } from './stage.js';\n\nconst STAGE_PADDING_PX = 32;\n\nexport interface MountRedactOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly store: Store<RedactState>;\n readonly viewport?: ViewportController;\n readonly onCommit?: () => void;\n readonly onAnnounce?: (message: string) => void;\n}\n\nexport interface MountRedactHandle {\n destroy(): void;\n}\n\nexport function mountRedactUtility(options: MountRedactOptions): MountRedactHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? (() => {});\n const announce = options.onAnnounce ?? (() => {});\n\n // First, revalidate against the upstream source's dimensions —\n // entering redact after a re-crop may bring outdated regions.\n const revalidated = revalidateRedactAgainstBounds(store.get(), {\n width: source.width,\n height: source.height,\n });\n if (revalidated !== store.get()) {\n store.update(() => revalidated);\n }\n\n const stage = buildRedactStage();\n stageHost.appendChild(stage.container);\n\n let viewport: Viewport = computeViewport(\n { width: 1, height: 1, padding: STAGE_PADDING_PX },\n { width: source.width, height: source.height },\n );\n let liveMarquee: {\n x: number;\n y: number;\n width: number;\n height: number;\n mode: RedactMode;\n color: string;\n } | null = null;\n\n function recomputeViewport(): void {\n const rect = stage.container.getBoundingClientRect();\n const stageDims = { width: rect.width, height: rect.height, padding: STAGE_PADDING_PX };\n const imageSize = { width: source.width, height: source.height };\n viewport = controller\n ? controller.computeViewport(stageDims, imageSize)\n : computeViewport(stageDims, imageSize);\n }\n\n function paintImage(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n paintRedactImageLayer(stage.imageCanvas, source, rect.width, rect.height, viewport);\n }\n\n function paintRegions(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n const state = store.get();\n paintRedactRegionsLayer(\n stage.regionsCanvas,\n source,\n state.regions,\n state.selectedId,\n rect.width,\n rect.height,\n viewport,\n );\n }\n\n function paintLive(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n paintRedactLiveLayer(stage.liveCanvas, liveMarquee, rect.width, rect.height, viewport);\n }\n\n function paintAll(): void {\n paintImage();\n paintRegions();\n paintLive();\n selectionLayer.update(selectedRedactRegionOf(store.get()), viewport);\n }\n\n function setLiveMarquee(\n next: {\n x: number;\n y: number;\n width: number;\n height: number;\n mode: RedactMode;\n color: string;\n } | null,\n ): void {\n liveMarquee = next;\n paintLive();\n }\n\n function toImageSpace(point: { clientX: number; clientY: number }): Point {\n const stagePoint = clientToElement(stage.container, point.clientX, point.clientY);\n return pointDisplayToImage(stagePoint, viewport);\n }\n\n // ----- Selection layer (handles + per-handle resize) -----\n const selectionLayer = buildRedactSelectionLayer({\n host: stage.handlesLayer,\n store,\n toImageSpace,\n getViewport: () => viewport,\n commit,\n });\n\n // ----- Pointer dispatch on the hit area -----\n const removeHitDrag = attachPointerDrag(stage.hitArea, (event) => {\n const state = store.get();\n // First: did the click land on an existing region? If so, select\n // it and start a body-move drag. Otherwise, drag-to-create.\n const image = toImageSpace(event);\n const hit = pickRegion(state.regions, image);\n if (hit) {\n if (state.selectedId !== hit.id) {\n store.update((current) => selectRedactRegion(current, hit.id));\n }\n return startBodyMoveGesture(hit, event);\n }\n return startCreateGesture(state, event);\n });\n\n function startBodyMoveGesture(initial: RedactRegion, event: PointerEvent): DragHandlers {\n const startImage = toImageSpace(event);\n return {\n onMove(point) {\n const here = toImageSpace(point);\n const dx = here.x - startImage.x;\n const dy = here.y - startImage.y;\n store.update((current) =>\n replaceRedactRegion(current, {\n ...initial,\n x: initial.x + dx,\n y: initial.y + dy,\n }),\n );\n },\n onCommit() {\n commit();\n },\n onCancel() {\n store.update((current) => replaceRedactRegion(current, initial));\n },\n };\n }\n\n function startCreateGesture(state: RedactState, event: PointerEvent): DragHandlers {\n const startImage = toImageSpace(event);\n let lastImage = startImage;\n return {\n onMove(point) {\n const raw = toImageSpace(point);\n // Shift constrains to a square — same convention the annotate\n // plugin uses for rect/ellipse drag-to-create.\n if (point.shiftKey) {\n const dx = raw.x - startImage.x;\n const dy = raw.y - startImage.y;\n const size = Math.max(Math.abs(dx), Math.abs(dy));\n const sx = dx === 0 ? 1 : Math.sign(dx);\n const sy = dy === 0 ? 1 : Math.sign(dy);\n lastImage = { x: startImage.x + sx * size, y: startImage.y + sy * size };\n } else {\n lastImage = raw;\n }\n setLiveMarquee({\n x: startImage.x,\n y: startImage.y,\n width: lastImage.x - startImage.x,\n height: lastImage.y - startImage.y,\n mode: state.currentMode,\n color: state.currentColor,\n });\n },\n onCommit() {\n setLiveMarquee(null);\n const extent = normaliseRedactExtent({\n x: startImage.x,\n y: startImage.y,\n width: lastImage.x - startImage.x,\n height: lastImage.y - startImage.y,\n });\n // Drop zero-extent gestures (a click that didn't drag).\n if (extent.width < 4 || extent.height < 4) return;\n const { id, nextRegionNumber } = mintRegionId(state);\n const region: RedactRegion = {\n id,\n x: extent.x,\n y: extent.y,\n width: extent.width,\n height: extent.height,\n mode: state.currentMode,\n color: state.currentColor,\n };\n store.update((current) => ({ ...addRegion(current, region), nextRegionNumber }));\n commit();\n },\n onCancel() {\n setLiveMarquee(null);\n },\n };\n }\n\n function insertDefaultAtCenter(): void {\n const state = store.get();\n const { id, nextRegionNumber } = mintRegionId(state);\n const region = createCenteredRedactRegion({\n imageSize: { width: source.width, height: source.height },\n mode: state.currentMode,\n color: state.currentColor,\n id,\n });\n store.update((current) => ({ ...addRegion(current, region), nextRegionNumber }));\n announce(\n 'Redaction region placed at centre. Use arrow keys to nudge, or edit coordinates below.',\n );\n requestAnimationFrame(() => {\n const firstInput = coordInputs.container.querySelector<HTMLInputElement>(\n '.kalotyp-redact-coords-input',\n );\n firstInput?.focus();\n firstInput?.select();\n });\n commit();\n }\n\n // ----- Coordinate inputs (keyboard a11y) -----\n const coordInputs = buildRedactCoordInputs({\n onRegionChanged: (region) => {\n store.update((current) => replaceRedactRegion(current, region));\n commit();\n },\n });\n\n // ----- Panel -----\n const initialState = store.get();\n const panel: RedactPanel = buildRedactPanel({\n initialMode: initialState.currentMode,\n initialColor: initialState.currentColor,\n canDelete: initialState.selectedId !== null,\n coordInputs: coordInputs.container,\n onSelectMode: (mode) => {\n // Mode toggle does double duty: set the mode for new regions\n // *and* update the selected region's mode if one is selected\n // (Phase 6.4 brief — \"mode change on selected region\").\n store.update((current) => {\n const next = setRedactCurrentMode(current, mode);\n if (next.selectedId === null) return next;\n return setRedactRegionMode(next, next.selectedId, mode);\n });\n commit();\n },\n onColorChange: (color) => {\n store.update((current) => {\n const next = setRedactCurrentColor(current, color);\n if (next.selectedId === null) return next;\n return setRedactRegionColor(next, next.selectedId, color);\n });\n commit();\n },\n onInsertAtCenter: () => insertDefaultAtCenter(),\n onDeleteSelected: () => {\n const id = store.get().selectedId;\n if (!id) return;\n store.update((current) => deleteRedactRegion(current, id));\n commit();\n },\n });\n utilHost.appendChild(panel.container);\n\n // ----- Initial paint + observers -----\n recomputeViewport();\n paintAll();\n\n const resizeObserver = new ResizeObserver(() => {\n recomputeViewport();\n paintAll();\n });\n resizeObserver.observe(stage.container);\n\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n recomputeViewport();\n paintAll();\n });\n });\n\n // Repaint regions and panel state on every store change. This is\n // identical to the annotate plugin's subscription rhythm; keeps a\n // pointer drag, a coordinate edit, an undo, and a panel toggle on\n // one consistent re-render path.\n let lastRegions = store.get().regions;\n let lastSelected = store.get().selectedId;\n let lastMode = store.get().currentMode;\n let lastColor = store.get().currentColor;\n\n const unsubscribe = store.subscribe((next) => {\n const regionsChanged = next.regions !== lastRegions;\n const selectionChanged = next.selectedId !== lastSelected;\n if (regionsChanged) {\n lastRegions = next.regions;\n paintRegions();\n }\n if (selectionChanged) {\n lastSelected = next.selectedId;\n panel.setCanDelete(next.selectedId !== null);\n }\n if (next.currentMode !== lastMode) {\n lastMode = next.currentMode;\n panel.setActiveMode(next.currentMode);\n }\n if (next.currentColor !== lastColor) {\n lastColor = next.currentColor;\n panel.setColor(next.currentColor);\n }\n selectionLayer.update(selectedRedactRegionOf(next), viewport);\n if (selectionChanged || regionsChanged) {\n // Sync the coordinate inputs to whichever region is now\n // selected; the inputs are a render of the selected region's\n // image-space coordinates.\n coordInputs.updateForRegion(selectedRedactRegionOf(next));\n }\n });\n\n // ----- Keyboard handlers -----\n const onKeyDown = (event: KeyboardEvent): void => {\n const target = event.target as Element | null;\n if (isEditableTarget(target)) return;\n const state = store.get();\n if (event.key === 'Escape') {\n if (state.selectedId !== null) {\n event.preventDefault();\n event.stopPropagation();\n store.update((current) => selectRedactRegion(current, null));\n announce('Selection cleared.');\n }\n return;\n }\n if (event.key === 'Delete' || event.key === 'Backspace') {\n if (state.selectedId === null) return;\n event.preventDefault();\n const id = state.selectedId;\n store.update((current) => deleteRedactRegion(current, id));\n commit();\n return;\n }\n if (\n event.key === 'ArrowUp' ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowLeft' ||\n event.key === 'ArrowRight'\n ) {\n const selected = selectedRedactRegionOf(state);\n if (!selected) return;\n if (event.ctrlKey || event.altKey || event.metaKey) return;\n const step = event.shiftKey ? 10 : 1;\n const dx = event.key === 'ArrowLeft' ? -step : event.key === 'ArrowRight' ? step : 0;\n const dy = event.key === 'ArrowUp' ? -step : event.key === 'ArrowDown' ? step : 0;\n event.preventDefault();\n store.update((current) =>\n replaceRedactRegion(current, {\n ...selected,\n x: selected.x + dx,\n y: selected.y + dy,\n }),\n );\n commit();\n }\n };\n document.addEventListener('keydown', onKeyDown, true);\n\n return {\n destroy() {\n document.removeEventListener('keydown', onKeyDown, true);\n removeHitDrag();\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n coordInputs.destroy();\n selectionLayer.destroy();\n stage.container.remove();\n panel.container.remove();\n },\n };\n}\n\n/**\n * Topmost (last-drawn) region containing the supplied image-space\n * point. Returns `undefined` when no region matches. Same pattern\n * as the annotate plugin's `pickShape`.\n */\nfunction pickRegion(\n regions: ReadonlyArray<RedactRegion>,\n point: { x: number; y: number },\n): RedactRegion | undefined {\n for (let i = regions.length - 1; i >= 0; i--) {\n const region = regions[i];\n if (!region) continue;\n if (\n point.x >= region.x &&\n point.x <= region.x + region.width &&\n point.y >= region.y &&\n point.y <= region.y + region.height\n ) {\n return region;\n }\n }\n return undefined;\n}\n\nfunction isEditableTarget(target: Element | null): boolean {\n if (!target) return false;\n const tag = target.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true;\n return (target as HTMLElement).isContentEditable === true;\n}\n","import {\n bakeRedact,\n initialRedactState,\n type RedactState,\n type UtilityPlugin,\n} from '@magicpages/kalotyp-core';\nimport { mountRedactUtility } from './mount.js';\n\nexport interface RedactPluginOptions {\n /** Where the plugin's panel UI mounts. */\n readonly panelHost: HTMLElement;\n}\n\n/**\n * Build the redact `UtilityPlugin` instance for one editor session.\n * redact is the chain link between annotate and resize;\n * `bake` is called by the chain runner with the post-annotate\n * composite as input and returns a fresh `SourceImage` with each\n * region redacted.\n */\nexport function createRedactPlugin(options: RedactPluginOptions): UtilityPlugin<RedactState> {\n return {\n id: 'redact',\n init: (ctx) =>\n initialRedactState({ imageSize: { width: ctx.source.width, height: ctx.source.height } }),\n mount(stageHost, ctx, store) {\n const handle = mountRedactUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: 'redact' }),\n onAnnounce: (message) => ctx.bus.emit('announce', { message }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: (state, source) => bakeRedact({ regions: state.regions }, source),\n };\n}\n","import {\n RESIZE_MAX_DIMENSION,\n RESIZE_MIN_DIMENSION,\n type ResizeState,\n resolveOutputSize,\n type SourceImage,\n type Store,\n setHeightPx,\n setLockAspect,\n setPercent,\n setWidthPx,\n type ViewportController,\n} from '@magicpages/kalotyp-core';\nimport {\n buildPreviewCanvas,\n paintPreview,\n previewViewportFor,\n} from '../../canvas/preview-canvas.js';\nimport { icon } from '../../icons.js';\n\nexport interface MountResizeOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly store: Store<ResizeState>;\n /** Editor-level zoom + pan controller. Optional in jsdom tests. */\n readonly viewport?: ViewportController;\n /**\n * Called after each width/height/percent/lock change so the editor\n * can capture an undo snapshot. Inputs already commit on\n * `change` (blur/Enter), not `input`, so a single typed value is one\n * undo step.\n */\n readonly onCommit?: () => void;\n}\n\nexport interface MountResizeHandle {\n destroy(): void;\n}\n\nexport function mountResizeUtility(options: MountResizeOptions): MountResizeHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? (() => {});\n\n const preview = buildPreviewCanvas();\n stageHost.appendChild(preview.container);\n\n const upstream = { width: source.width, height: source.height };\n const panel = buildResizePanel({\n upstream,\n onWidthChange: (px) => {\n store.set(setWidthPx(store.get(), px, upstream));\n commit();\n },\n onHeightChange: (px) => {\n store.set(setHeightPx(store.get(), px, upstream));\n commit();\n },\n onPercentChange: (pct) => {\n store.set(setPercent(store.get(), pct));\n commit();\n },\n onLockChange: (locked) => {\n store.set(setLockAspect(store.get(), locked));\n commit();\n },\n });\n utilHost.appendChild(panel.container);\n\n function paint(): void {\n const v = previewViewportFor(preview.container, upstream, controller);\n if (!v) return;\n paintPreview(preview.canvas, v.stageWidth, v.stageHeight, (ctx) => {\n const display = v.viewport.displayRect;\n ctx.drawImage(source.bitmap, display.x, display.y, display.width, display.height);\n });\n }\n\n function syncPanel(state: ResizeState): void {\n const out = resolveOutputSize(state, upstream);\n if (panel.widthInput.valueAsNumber !== out.width) panel.widthInput.value = String(out.width);\n if (panel.heightInput.valueAsNumber !== out.height)\n panel.heightInput.value = String(out.height);\n const percent = (state.scaleX + state.scaleY) / 2;\n const percentDisplay = Math.round(percent * 1000) / 10;\n if (Number.parseFloat(panel.percentInput.value || '0') !== percentDisplay) {\n panel.percentInput.value = String(percentDisplay);\n }\n panel.lockButton.setAttribute('aria-pressed', state.lockAspect ? 'true' : 'false');\n panel.lockButton.setAttribute(\n 'aria-label',\n state.lockAspect\n ? 'Aspect ratio locked — click to unlock'\n : 'Aspect ratio unlocked — click to lock',\n );\n panel.lockButton.title = state.lockAspect ? 'Aspect ratio locked' : 'Aspect ratio unlocked';\n panel.lockButton.innerHTML = chainIconSvg(state.lockAspect);\n panel.summary.textContent = `${out.width} × ${out.height}px (from ${upstream.width} × ${upstream.height}px)`;\n }\n\n syncPanel(store.get());\n paint();\n\n const resizeObserver = new ResizeObserver(() => paint());\n resizeObserver.observe(preview.container);\n\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n paint();\n });\n });\n\n const unsubscribe = store.subscribe((next) => {\n syncPanel(next);\n });\n\n return {\n destroy() {\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n preview.container.remove();\n panel.container.remove();\n },\n };\n}\n\ninterface ResizePanelOptions {\n readonly upstream: { width: number; height: number };\n onWidthChange(px: number): void;\n onHeightChange(px: number): void;\n onPercentChange(pct: number): void;\n onLockChange(locked: boolean): void;\n}\n\ninterface ResizePanel {\n container: HTMLDivElement;\n widthInput: HTMLInputElement;\n heightInput: HTMLInputElement;\n percentInput: HTMLInputElement;\n lockButton: HTMLButtonElement;\n summary: HTMLSpanElement;\n}\n\nfunction buildResizePanel(options: ResizePanelOptions): ResizePanel {\n const container = document.createElement('div');\n container.className = 'kalotyp-resize-panel';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Resize');\n\n const widthInput = makeNumberInput({\n label: 'Width (px)',\n min: RESIZE_MIN_DIMENSION,\n max: RESIZE_MAX_DIMENSION,\n step: 1,\n value: options.upstream.width,\n onChange: options.onWidthChange,\n });\n const heightInput = makeNumberInput({\n label: 'Height (px)',\n min: RESIZE_MIN_DIMENSION,\n max: RESIZE_MAX_DIMENSION,\n step: 1,\n value: options.upstream.height,\n onChange: options.onHeightChange,\n });\n const percentInput = makeNumberInput({\n label: 'Scale (%)',\n min: 1,\n max: 1000,\n step: 0.1,\n value: 100,\n onChange: options.onPercentChange,\n });\n\n // Small icon-only button between Width and Height — the convention\n // every desktop image-editor uses (GIMP / Photoshop / Photopea /\n // Figma / filerobot's Image Size dialog: a chain-link icon that\n // visually links W to H, closed when ratio is locked and broken\n // when not). The button itself is a 32×32 square with the chain\n // icon centred; aria-pressed carries the state.\n const lockButton = document.createElement('button');\n lockButton.type = 'button';\n lockButton.className = 'kalotyp-resize-lock';\n lockButton.setAttribute('aria-pressed', 'true');\n lockButton.setAttribute('aria-label', 'Lock aspect ratio');\n lockButton.title = 'Lock aspect ratio';\n lockButton.innerHTML = chainIconSvg(true);\n lockButton.addEventListener('click', () => {\n const next = lockButton.getAttribute('aria-pressed') !== 'true';\n options.onLockChange(next);\n });\n\n const summary = document.createElement('span');\n summary.className = 'kalotyp-resize-summary';\n summary.setAttribute('aria-live', 'polite');\n // Helper text moved to a `title` on the panel — surfacing the\n // 8000px cap via tooltip rather than a permanent caption keeps the\n // panel a single tidy row in line with Crop / Flip / Rotate.\n summary.textContent = `${options.upstream.width} × ${options.upstream.height}px`;\n\n container.title = `Maximum ${RESIZE_MAX_DIMENSION}px on either axis`;\n\n // Single flat row — the panel container is flex-row+wrap+centred\n // (transform.css). Width / [lock] / Height puts the chain-link\n // button between the two inputs it relates, matching every\n // desktop editor's Image Size dialog.\n const dimsRow = document.createElement('div');\n dimsRow.className = 'kalotyp-resize-row kalotyp-resize-dims';\n dimsRow.appendChild(widthInput.wrapper);\n dimsRow.appendChild(lockButton);\n dimsRow.appendChild(heightInput.wrapper);\n\n const scaleRow = document.createElement('div');\n scaleRow.className = 'kalotyp-resize-row';\n scaleRow.appendChild(percentInput.wrapper);\n scaleRow.appendChild(summary);\n\n container.appendChild(dimsRow);\n container.appendChild(scaleRow);\n\n return {\n container,\n widthInput: widthInput.input,\n heightInput: heightInput.input,\n percentInput: percentInput.input,\n lockButton,\n summary,\n };\n}\n\ninterface NumberInputOptions {\n readonly label: string;\n readonly min: number;\n readonly max: number;\n readonly step: number;\n readonly value: number;\n onChange(value: number): void;\n}\n\nfunction makeNumberInput(options: NumberInputOptions): {\n wrapper: HTMLLabelElement;\n input: HTMLInputElement;\n} {\n const wrapper = document.createElement('label');\n wrapper.className = 'kalotyp-resize-field';\n\n const labelSpan = document.createElement('span');\n labelSpan.className = 'kalotyp-resize-field-label';\n labelSpan.textContent = options.label;\n\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'kalotyp-resize-input';\n input.min = String(options.min);\n input.max = String(options.max);\n input.step = String(options.step);\n input.value = String(options.value);\n input.inputMode = 'numeric';\n input.setAttribute('aria-label', options.label);\n\n // Commit on `change` (blur/Enter) so the user can type intermediate\n // values without firing a state update on every keystroke.\n input.addEventListener('change', () => {\n const value = input.valueAsNumber;\n if (Number.isFinite(value)) options.onChange(value);\n });\n\n wrapper.appendChild(labelSpan);\n wrapper.appendChild(input);\n return { wrapper, input };\n}\n\n/**\n * Inline SVG for the aspect-lock toggle. Sourced from the shared\n * Lucide-backed icon module (`link-2` for locked, `link-2-off` for\n * unlocked). `currentColor` on the strokes lets the icon inherit the\n * button's text colour for active/hover states.\n */\nfunction chainIconSvg(locked: boolean): string {\n return locked ? icon('lockClosed') : icon('lockOpen');\n}\n","import {\n bakeResize,\n initialResizeState,\n type ResizeState,\n type UtilityPlugin,\n} from '@magicpages/kalotyp-core';\nimport { mountResizeUtility } from './mount.js';\n\nexport interface ResizePluginOptions {\n readonly panelHost: HTMLElement;\n}\n\nexport function createResizePlugin(options: ResizePluginOptions): UtilityPlugin<ResizeState> {\n return {\n id: 'resize',\n init: () => initialResizeState(),\n mount(stageHost, ctx, store) {\n const handle = mountResizeUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: 'resize' }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: bakeResize,\n };\n}\n","import {\n computeViewport,\n effectiveAngleDeg,\n FREE_ANGLE_MAX,\n FREE_ANGLE_MIN,\n FREE_ANGLE_STEP,\n largestInscribedRect,\n type RotateState,\n rotateClockwise,\n rotateCounterClockwise,\n type SourceImage,\n type Store,\n setFreeAngle,\n type ViewportController,\n} from '@magicpages/kalotyp-core';\nimport { buildPreviewCanvas, paintPreview, STAGE_PADDING_PX } from '../../canvas/preview-canvas.js';\n\nexport interface MountRotateOptions {\n readonly stageHost: HTMLElement;\n readonly utilHost: HTMLElement;\n readonly source: SourceImage;\n readonly store: Store<RotateState>;\n /** Editor-level zoom + pan controller. Optional in jsdom tests. */\n readonly viewport?: ViewportController;\n /** Fires on quarter-turn / slider `change` / number `change` / reset — one undo per drag, not per tick. */\n readonly onCommit?: () => void;\n}\n\nexport interface MountRotateHandle {\n destroy(): void;\n}\n\n// Same mask alpha as crop overlay so the \"excluded region\" cue reads consistently across plugins.\nconst MASK_FILL = 'rgba(0, 0, 0, 0.4)';\nconst INSCRIBED_OUTLINE = 'rgba(255, 255, 255, 0.95)';\nconst INSCRIBED_HALO = 'rgba(0, 0, 0, 0.45)';\n\nexport function mountRotateUtility(options: MountRotateOptions): MountRotateHandle {\n const { stageHost, utilHost, source, store, viewport: controller } = options;\n const commit = options.onCommit ?? noop;\n\n const preview = buildPreviewCanvas();\n stageHost.appendChild(preview.container);\n\n const panel = buildRotatePanel({\n onCounterClockwise: () => {\n store.set(rotateCounterClockwise(store.get()));\n commit();\n },\n onClockwise: () => {\n store.set(rotateClockwise(store.get()));\n commit();\n },\n onAngleInput: (deg) => store.set(setFreeAngle(store.get(), deg)),\n onAngleCommit: () => commit(),\n onAngleReset: () => {\n store.set(setFreeAngle(store.get(), 0));\n commit();\n },\n });\n utilHost.appendChild(panel.container);\n\n function paint(): void {\n const rect = preview.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n const state = store.get();\n const angleRad = (effectiveAngleDeg(state) * Math.PI) / 180;\n const sub90Deg = effectiveAngleDeg(state) - state.quarterTurns * 90;\n const isQuarterOnly = Math.abs(sub90Deg) < 1e-6;\n\n // Letterbox the rotated bounding box so the user sees the whole rotated source; the bake's crop\n // shows as the inscribed rect inside it.\n const c = Math.abs(Math.cos(angleRad));\n const s = Math.abs(Math.sin(angleRad));\n const boundsW = source.width * c + source.height * s;\n const boundsH = source.width * s + source.height * c;\n\n // Pass rotated bounds to computeViewport so fit-to-screen treats the rotated frame as intrinsic.\n const stageDims = { width: rect.width, height: rect.height, padding: STAGE_PADDING_PX };\n const rotatedBounds = { width: boundsW, height: boundsH };\n const viewport = controller\n ? controller.computeViewport(stageDims, rotatedBounds)\n : computeViewport(stageDims, rotatedBounds);\n const display = viewport.displayRect;\n const cx = display.x + display.width / 2;\n const cy = display.y + display.height / 2;\n\n // Quarter-only turns: inscribed equals the (axis-swapped) source.\n const inscribed = isQuarterOnly\n ? state.quarterTurns % 2 === 0\n ? { width: source.width, height: source.height }\n : { width: source.height, height: source.width }\n : largestInscribedRect(source, angleRad);\n\n paintPreview(preview.canvas, rect.width, rect.height, (ctx) => {\n // Rotated source centred on the displayRect, scaled to display px.\n const drawW = source.width * viewport.scale;\n const drawH = source.height * viewport.scale;\n ctx.save();\n ctx.translate(cx, cy);\n ctx.rotate(angleRad);\n ctx.drawImage(source.bitmap, -drawW / 2, -drawH / 2, drawW, drawH);\n ctx.restore();\n\n const iw = inscribed.width * viewport.scale;\n const ih = inscribed.height * viewport.scale;\n const ix = cx - iw / 2;\n const iy = cy - ih / 2;\n\n // Mask outside the inscribed rect via even-odd clip. Skip on quarter-only turns (no crop).\n if (!isQuarterOnly) {\n ctx.save();\n ctx.beginPath();\n ctx.rect(0, 0, rect.width, rect.height);\n ctx.rect(ix, iy, iw, ih);\n ctx.clip('evenodd');\n ctx.fillStyle = MASK_FILL;\n ctx.fillRect(0, 0, rect.width, rect.height);\n ctx.restore();\n }\n\n ctx.save();\n ctx.lineWidth = 3;\n ctx.strokeStyle = INSCRIBED_HALO;\n ctx.strokeRect(ix + 0.5, iy + 0.5, iw - 1, ih - 1);\n ctx.lineWidth = 1;\n ctx.strokeStyle = INSCRIBED_OUTLINE;\n ctx.strokeRect(ix + 0.5, iy + 0.5, iw - 1, ih - 1);\n ctx.restore();\n });\n }\n\n function syncPanel(state: RotateState): void {\n if (panel.angleSlider.valueAsNumber !== state.freeAngle) {\n panel.angleSlider.valueAsNumber = state.freeAngle;\n }\n const formatted = formatAngleValue(state.freeAngle);\n if (panel.angleInput.value !== formatted) {\n panel.angleInput.value = formatted;\n }\n }\n\n syncPanel(store.get());\n paint();\n\n const resizeObserver = new ResizeObserver(() => paint());\n resizeObserver.observe(preview.container);\n\n let viewportRafScheduled = false;\n const unsubscribeViewport = controller?.subscribe(() => {\n if (viewportRafScheduled) return;\n viewportRafScheduled = true;\n requestAnimationFrame(() => {\n viewportRafScheduled = false;\n paint();\n });\n });\n\n let rafScheduled = false;\n const unsubscribe = store.subscribe((next) => {\n syncPanel(next);\n if (rafScheduled) return;\n rafScheduled = true;\n requestAnimationFrame(() => {\n rafScheduled = false;\n paint();\n });\n });\n\n return {\n destroy() {\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n preview.container.remove();\n panel.container.remove();\n },\n };\n}\n\ninterface RotatePanelOptions {\n onCounterClockwise(): void;\n onClockwise(): void;\n /** Per-tick state mutation; no commit. */\n onAngleInput(deg: number): void;\n /** Drag-end commit boundary. */\n onAngleCommit(): void;\n onAngleReset(): void;\n}\n\ninterface RotatePanel {\n container: HTMLDivElement;\n angleSlider: HTMLInputElement;\n angleInput: HTMLInputElement;\n}\n\nfunction buildRotatePanel(options: RotatePanelOptions): RotatePanel {\n const container = document.createElement('div');\n container.className = 'kalotyp-rotate-panel';\n container.setAttribute('role', 'group');\n container.setAttribute('aria-label', 'Rotate');\n\n const ccwButton = makeQuarterButton(\n 'Rotate 90° counter-clockwise',\n '↺',\n options.onCounterClockwise,\n );\n const cwButton = makeQuarterButton('Rotate 90° clockwise', '↻', options.onClockwise);\n\n const angleSliderLabel = document.createElement('label');\n angleSliderLabel.className = 'kalotyp-rotate-slider-label';\n angleSliderLabel.textContent = 'Straighten';\n\n const angleSlider = document.createElement('input');\n angleSlider.type = 'range';\n angleSlider.className = 'kalotyp-rotate-slider';\n angleSlider.min = String(FREE_ANGLE_MIN);\n angleSlider.max = String(FREE_ANGLE_MAX);\n angleSlider.step = String(FREE_ANGLE_STEP);\n angleSlider.value = '0';\n angleSlider.setAttribute('aria-label', 'Straighten angle');\n angleSlider.addEventListener('input', () => options.onAngleInput(angleSlider.valueAsNumber));\n angleSlider.addEventListener('change', () => options.onAngleCommit());\n\n const angleInput = document.createElement('input');\n angleInput.type = 'number';\n angleInput.className = 'kalotyp-rotate-input';\n angleInput.min = String(FREE_ANGLE_MIN);\n angleInput.max = String(FREE_ANGLE_MAX);\n angleInput.step = String(FREE_ANGLE_STEP);\n angleInput.value = '0';\n angleInput.setAttribute('aria-label', 'Straighten angle in degrees');\n angleInput.addEventListener('change', () => {\n const value = angleInput.valueAsNumber;\n if (Number.isFinite(value)) {\n options.onAngleInput(value);\n options.onAngleCommit();\n }\n });\n\n const angleSuffix = document.createElement('span');\n angleSuffix.className = 'kalotyp-rotate-suffix';\n angleSuffix.setAttribute('aria-hidden', 'true');\n angleSuffix.textContent = '°';\n\n const resetButton = document.createElement('button');\n resetButton.type = 'button';\n resetButton.className = 'kalotyp-rotate-reset';\n resetButton.textContent = 'Reset';\n resetButton.addEventListener('click', options.onAngleReset);\n\n const quarterGroup = document.createElement('div');\n quarterGroup.className = 'kalotyp-rotate-row';\n quarterGroup.appendChild(ccwButton);\n quarterGroup.appendChild(cwButton);\n\n const angleInputGroup = document.createElement('span');\n angleInputGroup.className = 'kalotyp-rotate-input-group';\n angleInputGroup.appendChild(angleInput);\n angleInputGroup.appendChild(angleSuffix);\n\n const straightenGroup = document.createElement('div');\n straightenGroup.className = 'kalotyp-rotate-row kalotyp-rotate-slider-row';\n straightenGroup.appendChild(angleSliderLabel);\n straightenGroup.appendChild(angleSlider);\n straightenGroup.appendChild(angleInputGroup);\n straightenGroup.appendChild(resetButton);\n\n container.appendChild(quarterGroup);\n container.appendChild(straightenGroup);\n\n return { container, angleSlider, angleInput };\n}\n\n/** Whole numbers render as `15`; sub-unit values keep one decimal (`15.5`). */\nfunction formatAngleValue(value: number): string {\n const rounded = Math.round(value * 10) / 10;\n if (Number.isInteger(rounded)) return String(rounded);\n return rounded.toFixed(1);\n}\n\nfunction noop(): void {}\n\nfunction makeQuarterButton(label: string, glyph: string, onClick: () => void): HTMLButtonElement {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-quarter-button';\n button.setAttribute('aria-label', label);\n button.title = label;\n button.textContent = glyph;\n button.addEventListener('click', onClick);\n return button;\n}\n","import {\n bakeRotate,\n initialRotateState,\n type RotateState,\n type UtilityPlugin,\n} from '@magicpages/kalotyp-core';\nimport { mountRotateUtility } from './mount.js';\n\nexport interface RotatePluginOptions {\n readonly panelHost: HTMLElement;\n}\n\nexport function createRotatePlugin(options: RotatePluginOptions): UtilityPlugin<RotateState> {\n return {\n id: 'rotate',\n init: () => initialRotateState(),\n mount(stageHost, ctx, store) {\n const handle = mountRotateUtility({\n stageHost,\n utilHost: options.panelHost,\n source: ctx.source,\n store,\n viewport: ctx.viewport,\n onCommit: () => ctx.bus.emit('commit', { utility: 'rotate' }),\n });\n return { destroy: () => handle.destroy() };\n },\n bake: bakeRotate,\n };\n}\n","/**\n * Per-site preferences for the editor. Persisted to `localStorage`\n * (browser-origin-scoped automatically); the `siteScope` derived from\n * the Ghost-passed `src` URL further namespaces same-origin-multi-site\n * installs by Ghost-content-root.\n *\n * What's persisted:\n * - Output format and quality (always, no opt-out — saving the\n * user's preferred output format is universally useful).\n * - Annotation default style (colour + stroke width) — opt-in via\n * `rememberAnnotationStyle`.\n * - Last-used filter preset id — opt-in via `rememberFilter`.\n * - Last-used frame preset id and colour — opt-in via\n * `rememberFrame`.\n *\n * What's *not* persisted:\n * - The current edit (transient session state — undo/redo handles\n * in-session reverts).\n * - The image source (Ghost passes a fresh `src` each open).\n * - Anything that could leak identity across sessions (no telemetry,\n * no usage counts).\n *\n * The total payload sits well under 1 KB; quota is not a concern, and\n * even repeated writes-on-commit don't approach `localStorage`'s 5–10 MB\n * per-origin budget.\n */\n\nimport type { OutputMimeChoice } from '@magicpages/kalotyp-core';\n\nexport interface KalotypPreferences {\n readonly outputMimeChoice: OutputMimeChoice;\n readonly outputQuality: number;\n /**\n * Whether to strip EXIF / GPS / camera metadata on save. Stored\n * alongside the output format so the user's privacy choice\n * survives across sessions like the format and quality do.\n */\n readonly outputStripMetadata: boolean;\n readonly rememberAnnotationStyle: boolean;\n readonly rememberFilter: boolean;\n readonly rememberFrame: boolean;\n readonly lastAnnotationColor: string;\n readonly lastAnnotationStrokeWidth: number;\n readonly lastFilterPresetId: string | null;\n readonly lastFramePresetId: string | null;\n readonly lastFrameColor: string;\n}\n\nexport const DEFAULT_PREFERENCES: KalotypPreferences = {\n outputMimeChoice: 'auto',\n outputQuality: 0.85,\n outputStripMetadata: true,\n rememberAnnotationStyle: true,\n rememberFilter: true,\n rememberFrame: true,\n lastAnnotationColor: '#ff3b30',\n lastAnnotationStrokeWidth: 4,\n lastFilterPresetId: null,\n lastFramePresetId: null,\n lastFrameColor: '#000000',\n};\n\n/**\n * Schema version baked into the storage key. Bumped if the shape\n * changes incompatibly — older saved payloads then fail their JSON\n * parse and fall back to defaults rather than producing an\n * ambiguous mix of fields.\n */\nconst STORAGE_KEY_PREFIX = 'kalotyp:prefs:v1';\n\n/**\n * Derive a stable per-site scope identifier from the `src` option\n * Ghost passes. Used as the suffix on the `localStorage` key so two\n * Ghost installs on the same origin (rare but possible, e.g. a host\n * serving `/site-a/` and `/site-b/` Ghost instances) get separate\n * preferences.\n *\n * Strategy:\n * - URL `src`: use `origin + the leading path up to (and excluding)\n * `/content/`. Ghost serves images at `<origin>/content/images/...`,\n * so the prefix uniquely identifies a Ghost root.\n * - Blob/File `src` (test environments, future direct-feed\n * scenarios): fall back to `window.location.origin`.\n * - Anything else falls to `'default'` so the read still succeeds.\n */\nexport function getSiteScope(srcOption: unknown): string {\n if (typeof srcOption === 'string') {\n try {\n const url = new URL(srcOption);\n const path = url.pathname;\n const contentIdx = path.indexOf('/content/');\n const root = contentIdx === -1 ? '' : path.slice(0, contentIdx);\n return `${url.origin}${root}`;\n } catch {\n // Fall through.\n }\n }\n if (typeof window !== 'undefined') {\n return window.location.origin;\n }\n return 'default';\n}\n\nfunction storageKey(siteScope: string): string {\n return `${STORAGE_KEY_PREFIX}:${siteScope}`;\n}\n\n/**\n * Load preferences for `siteScope`. Missing keys fill from defaults\n * (sparse merge) so a partial historical payload doesn't break the\n * editor. Any read error (no storage, quota, parse failure) returns\n * the defaults — preferences are best-effort.\n */\nexport function loadPreferences(siteScope: string): KalotypPreferences {\n try {\n if (typeof localStorage === 'undefined') return DEFAULT_PREFERENCES;\n const raw = localStorage.getItem(storageKey(siteScope));\n if (!raw) return DEFAULT_PREFERENCES;\n const parsed = JSON.parse(raw) as Partial<KalotypPreferences>;\n return mergeWithDefaults(parsed);\n } catch {\n return DEFAULT_PREFERENCES;\n }\n}\n\n/**\n * Save preferences for `siteScope`. Writes are best-effort: quota\n * errors and missing storage are swallowed so a failed save never\n * crashes the editor. Validation happens here too — any non-finite\n * or out-of-range value is replaced with the default before write.\n */\nexport function savePreferences(siteScope: string, prefs: KalotypPreferences): void {\n try {\n if (typeof localStorage === 'undefined') return;\n const sanitised = mergeWithDefaults(prefs);\n localStorage.setItem(storageKey(siteScope), JSON.stringify(sanitised));\n } catch {\n // Quota exceeded, storage disabled, etc. Best-effort: ignore.\n }\n}\n\nfunction mergeWithDefaults(partial: Partial<KalotypPreferences>): KalotypPreferences {\n const outputMimeChoice = isOutputMimeChoice(partial.outputMimeChoice)\n ? partial.outputMimeChoice\n : DEFAULT_PREFERENCES.outputMimeChoice;\n const outputQuality = clampToRange(\n partial.outputQuality,\n 0,\n 1,\n DEFAULT_PREFERENCES.outputQuality,\n );\n const outputStripMetadata =\n typeof partial.outputStripMetadata === 'boolean'\n ? partial.outputStripMetadata\n : DEFAULT_PREFERENCES.outputStripMetadata;\n const rememberAnnotationStyle =\n typeof partial.rememberAnnotationStyle === 'boolean'\n ? partial.rememberAnnotationStyle\n : DEFAULT_PREFERENCES.rememberAnnotationStyle;\n const rememberFilter =\n typeof partial.rememberFilter === 'boolean'\n ? partial.rememberFilter\n : DEFAULT_PREFERENCES.rememberFilter;\n const rememberFrame =\n typeof partial.rememberFrame === 'boolean'\n ? partial.rememberFrame\n : DEFAULT_PREFERENCES.rememberFrame;\n const lastAnnotationColor =\n typeof partial.lastAnnotationColor === 'string'\n ? partial.lastAnnotationColor\n : DEFAULT_PREFERENCES.lastAnnotationColor;\n const lastAnnotationStrokeWidth = clampToRange(\n partial.lastAnnotationStrokeWidth,\n 1,\n 40,\n DEFAULT_PREFERENCES.lastAnnotationStrokeWidth,\n );\n const lastFilterPresetId =\n partial.lastFilterPresetId === null || typeof partial.lastFilterPresetId === 'string'\n ? partial.lastFilterPresetId\n : DEFAULT_PREFERENCES.lastFilterPresetId;\n const lastFramePresetId =\n partial.lastFramePresetId === null || typeof partial.lastFramePresetId === 'string'\n ? partial.lastFramePresetId\n : DEFAULT_PREFERENCES.lastFramePresetId;\n const lastFrameColor =\n typeof partial.lastFrameColor === 'string'\n ? partial.lastFrameColor\n : DEFAULT_PREFERENCES.lastFrameColor;\n\n return {\n outputMimeChoice,\n outputQuality,\n outputStripMetadata,\n rememberAnnotationStyle,\n rememberFilter,\n rememberFrame,\n lastAnnotationColor,\n lastAnnotationStrokeWidth,\n lastFilterPresetId,\n lastFramePresetId,\n lastFrameColor,\n };\n}\n\nfunction isOutputMimeChoice(value: unknown): value is OutputMimeChoice {\n return (\n value === 'auto' ||\n value === 'image/png' ||\n value === 'image/jpeg' ||\n value === 'image/webp' ||\n value === 'image/avif'\n );\n}\n\nfunction clampToRange(value: unknown, lo: number, hi: number, fallback: number): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return fallback;\n if (value < lo) return lo;\n if (value > hi) return hi;\n return value;\n}\n","/**\n * Preferences modal. Lets the user adjust persisted defaults across\n * editor sessions: output format/quality, plus per-utility \"remember\n * what I picked last time\" toggles.\n *\n * Lifecycle: the caller supplies the current preferences and an\n * `onChange` handler that fires on every internal interaction. The\n * caller is responsible for persisting via `savePreferences` —\n * keeping this modal storage-free means it can be tested without\n * a `localStorage` shim and lets the editor coordinate batched\n * writes with its own debounce.\n */\n\nimport type { OutputMimeChoice } from '@magicpages/kalotyp-core';\nimport { openNestedModal } from '../dom/nested-modal.js';\nimport { DEFAULT_PREFERENCES, type KalotypPreferences } from './storage.js';\n\nexport interface OpenPreferencesModalOptions {\n readonly host: HTMLElement;\n readonly initial: KalotypPreferences;\n onChange(next: KalotypPreferences): void;\n onClose(): void;\n}\n\nexport interface PreferencesModalHandle {\n close(): void;\n}\n\ninterface FormatChoice {\n readonly value: OutputMimeChoice;\n readonly label: string;\n}\n\nconst FORMAT_CHOICES: ReadonlyArray<FormatChoice> = [\n { value: 'auto', label: 'Auto (recommended)' },\n { value: 'image/webp', label: 'WebP' },\n { value: 'image/avif', label: 'AVIF' },\n { value: 'image/jpeg', label: 'JPEG' },\n { value: 'image/png', label: 'PNG' },\n];\n\nexport function openPreferencesModal(options: OpenPreferencesModalOptions): PreferencesModalHandle {\n let state: KalotypPreferences = options.initial;\n\n function update(patch: Partial<KalotypPreferences>): void {\n state = { ...state, ...patch };\n options.onChange(state);\n }\n\n const body = document.createElement('div');\n body.className = 'kalotyp-preferences-body';\n\n // ----- Output defaults -----\n const outputSection = document.createElement('section');\n outputSection.className = 'kalotyp-preferences-section';\n outputSection.innerHTML = '<h4>Output defaults</h4>';\n\n const formatRow = makeRow('Format');\n const formatSelect = document.createElement('select');\n formatSelect.className = 'kalotyp-output-format';\n formatSelect.setAttribute('aria-label', 'Default output format');\n for (const choice of FORMAT_CHOICES) {\n const option = document.createElement('option');\n option.value = choice.value;\n option.textContent = choice.label;\n formatSelect.appendChild(option);\n }\n formatSelect.value = state.outputMimeChoice;\n formatSelect.addEventListener('change', () => {\n update({ outputMimeChoice: formatSelect.value as OutputMimeChoice });\n });\n formatRow.appendChild(formatSelect);\n outputSection.appendChild(formatRow);\n\n const qualityRow = makeRow('Quality');\n const qualitySlider = document.createElement('input');\n qualitySlider.type = 'range';\n qualitySlider.className = 'kalotyp-output-quality';\n qualitySlider.min = '50';\n qualitySlider.max = '100';\n qualitySlider.step = '1';\n qualitySlider.value = String(Math.round(state.outputQuality * 100));\n qualitySlider.setAttribute('aria-label', 'Default output quality');\n qualitySlider.addEventListener('input', () => {\n update({ outputQuality: qualitySlider.valueAsNumber / 100 });\n qualityReadout.textContent = String(qualitySlider.valueAsNumber);\n });\n const qualityReadout = document.createElement('span');\n qualityReadout.className = 'kalotyp-output-quality-readout';\n qualityReadout.textContent = String(Math.round(state.outputQuality * 100));\n qualityReadout.setAttribute('aria-hidden', 'true');\n qualityRow.appendChild(qualitySlider);\n qualityRow.appendChild(qualityReadout);\n outputSection.appendChild(qualityRow);\n\n // Strip-metadata toggle. Same control as the Output popover —\n // mirrored here so the user can adjust the default without opening\n // the popover.\n const stripToggle = makeToggle(\n 'Strip EXIF, GPS, and camera metadata on save',\n state.outputStripMetadata,\n (checked) => update({ outputStripMetadata: checked }),\n );\n outputSection.appendChild(stripToggle);\n\n // ----- Remember toggles -----\n const memorySection = document.createElement('section');\n memorySection.className = 'kalotyp-preferences-section';\n memorySection.innerHTML = '<h4>Remember across sessions</h4>';\n\n const annotateToggle = makeToggle(\n 'Annotation style (colour + stroke width)',\n state.rememberAnnotationStyle,\n (checked) => update({ rememberAnnotationStyle: checked }),\n );\n const filterToggle = makeToggle('Last filter preset', state.rememberFilter, (checked) =>\n update({ rememberFilter: checked }),\n );\n const frameToggle = makeToggle('Last frame preset', state.rememberFrame, (checked) =>\n update({ rememberFrame: checked }),\n );\n memorySection.appendChild(annotateToggle);\n memorySection.appendChild(filterToggle);\n memorySection.appendChild(frameToggle);\n\n // ----- Footer -----\n const footer = document.createElement('footer');\n footer.className = 'kalotyp-preferences-footer';\n\n const resetButton = document.createElement('button');\n resetButton.type = 'button';\n resetButton.className = 'kalotyp-preferences-reset';\n resetButton.textContent = 'Reset to defaults';\n resetButton.addEventListener('click', () => {\n state = { ...DEFAULT_PREFERENCES };\n options.onChange(state);\n syncControls(state);\n });\n\n const doneButton = document.createElement('button');\n doneButton.type = 'button';\n doneButton.className = 'kalotyp-preferences-done';\n doneButton.textContent = 'Done';\n doneButton.addEventListener('click', () => handle.close());\n\n footer.appendChild(resetButton);\n footer.appendChild(doneButton);\n\n body.appendChild(outputSection);\n body.appendChild(memorySection);\n body.appendChild(footer);\n\n const handle = openNestedModal({\n host: options.host,\n title: 'Preferences',\n body,\n variant: 'kalotyp-preferences-modal',\n showCloseButton: true,\n onClose: options.onClose,\n });\n\n function syncControls(s: KalotypPreferences): void {\n formatSelect.value = s.outputMimeChoice;\n qualitySlider.value = String(Math.round(s.outputQuality * 100));\n qualityReadout.textContent = String(Math.round(s.outputQuality * 100));\n syncToggle(stripToggle, s.outputStripMetadata);\n syncToggle(annotateToggle, s.rememberAnnotationStyle);\n syncToggle(filterToggle, s.rememberFilter);\n syncToggle(frameToggle, s.rememberFrame);\n }\n\n return {\n close: () => handle.close(),\n };\n}\n\nfunction makeRow(label: string): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'kalotyp-output-row';\n const labelSpan = document.createElement('span');\n labelSpan.className = 'kalotyp-output-row-label';\n labelSpan.textContent = label;\n row.appendChild(labelSpan);\n return row;\n}\n\nfunction makeToggle(\n label: string,\n initial: boolean,\n onChange: (checked: boolean) => void,\n): HTMLLabelElement {\n const row = document.createElement('label');\n row.className = 'kalotyp-preferences-toggle';\n const checkbox = document.createElement('input');\n checkbox.type = 'checkbox';\n checkbox.checked = initial;\n checkbox.addEventListener('change', () => onChange(checkbox.checked));\n const span = document.createElement('span');\n span.textContent = label;\n row.appendChild(checkbox);\n row.appendChild(span);\n return row;\n}\n\nfunction syncToggle(row: HTMLLabelElement, value: boolean): void {\n const input = row.querySelector<HTMLInputElement>('input[type=\"checkbox\"]');\n if (input && input.checked !== value) input.checked = value;\n}\n","import { icon } from '../icons.js';\n\nexport interface ShellDom {\n readonly editor: HTMLElement;\n readonly modal: HTMLElement;\n readonly root: HTMLElement;\n readonly main: HTMLElement;\n readonly stage: HTMLElement;\n readonly navTools: HTMLElement;\n readonly utilMain: HTMLElement;\n readonly utilFooter: HTMLElement;\n readonly closeButton: HTMLButtonElement;\n /** Gear icon — opens the Preferences modal. */\n readonly prefsButton: HTMLButtonElement;\n readonly exportButton: HTMLButtonElement;\n /** Caret next to Save — opens the output-format popover; the Save button itself bypasses it. */\n readonly outputSettingsButton: HTMLButtonElement;\n /** Visually-hidden polite live region for one-shot announcements. */\n readonly liveRegion: HTMLElement;\n readonly titleId: string;\n}\n\nexport interface BuildShellDomOptions {\n exportLabel: string;\n}\n\n// Monotonic id prefix so multiple editor instances on one page get distinct namespaces for aria-controls/labelledby.\nlet nextEditorId = 0;\n\n/**\n * Build the DOM skeleton for an editor session.\n *\n * The editor's own structure is namespaced `kalotyp-*`. Two class tokens are\n * the exception — `pintura-editor` on the host and `PinturaModal` on the modal\n * wrapper. Ghost's runtime looks those up by name (the host class scopes Ghost's\n * theme-variable overrides; the modal class is what Ghost's close-button click\n * handler selects on), so they exist purely for Ghost compatibility — not as\n * branding, and implying no affiliation with or endorsement by the editor Ghost\n * named them after. See `docs/ghost-contract.md`. The integration breaks if\n * those two tokens change.\n *\n * The util-main `aria-labelledby` is updated on tab switch by `setActiveUtilityButton`\n * to complete the tablist/tab/tabpanel triple.\n */\nexport function buildShellDom(options: BuildShellDomOptions): ShellDom {\n const editorId = `kalotyp-editor-${++nextEditorId}`;\n const titleId = `${editorId}-title`;\n\n const editor = document.createElement('div');\n // `pintura-editor`: Ghost-compatibility hook only (theme-variable scope), not\n // branding and no affiliation/endorsement implied. See the file docblock.\n editor.className = 'pintura-editor kalotyp-editor';\n editor.id = editorId;\n editor.setAttribute('role', 'dialog');\n editor.setAttribute('aria-modal', 'true');\n editor.setAttribute('aria-labelledby', titleId);\n // tabindex=-1 so the focus trap lands initial focus on the dialog root, not the close button.\n editor.tabIndex = -1;\n\n const title = document.createElement('h2');\n title.id = titleId;\n title.className = 'kalotyp-visually-hidden';\n title.textContent = 'Image editor';\n\n // `PinturaModal`: Ghost-compatibility hook only — Ghost's body-level click\n // handler selects on `.PinturaModal button[title=\"Close\"]` to detect explicit\n // closes. Not branding, no affiliation/endorsement implied. See the file\n // docblock and docs/ghost-contract.md (Dismissal).\n const modal = document.createElement('div');\n modal.className = 'PinturaModal kalotyp-modal';\n\n const closeButton = document.createElement('button');\n closeButton.type = 'button';\n closeButton.title = 'Close';\n closeButton.setAttribute('aria-label', 'Close image editor');\n closeButton.className = 'kalotyp-button-close';\n closeButton.innerHTML = icon('close');\n\n const prefsButton = document.createElement('button');\n prefsButton.type = 'button';\n prefsButton.title = 'Preferences';\n prefsButton.setAttribute('aria-label', 'Open editor preferences');\n prefsButton.setAttribute('aria-haspopup', 'dialog');\n prefsButton.className = 'kalotyp-button-prefs';\n prefsButton.innerHTML = icon('settings');\n\n const root = document.createElement('div');\n root.className = 'kalotyp-root';\n root.setAttribute('data-env', 'landscape has-navigation');\n\n const main = document.createElement('div');\n main.className = 'kalotyp-main';\n\n const stage = document.createElement('div');\n stage.className = 'kalotyp-stage';\n stage.setAttribute('role', 'region');\n stage.setAttribute('aria-label', 'Image preview');\n\n const utilMain = document.createElement('div');\n utilMain.id = `${editorId}-panel`;\n utilMain.className = 'kalotyp-util-main';\n utilMain.setAttribute('role', 'tabpanel');\n utilMain.setAttribute('tabindex', '0');\n\n const navTools = document.createElement('div');\n navTools.className = 'kalotyp-nav-tools';\n\n const utilFooter = document.createElement('div');\n utilFooter.className = 'kalotyp-util-footer';\n\n const exportGroup = document.createElement('div');\n exportGroup.className = 'kalotyp-export-group';\n\n const exportButton = document.createElement('button');\n exportButton.type = 'button';\n exportButton.className = 'kalotyp-button-export';\n const exportInner = document.createElement('span');\n exportInner.className = 'kalotyp-button-inner';\n exportInner.textContent = options.exportLabel;\n exportButton.appendChild(exportInner);\n\n const outputSettingsButton = document.createElement('button');\n outputSettingsButton.type = 'button';\n outputSettingsButton.className = 'kalotyp-button-output-settings';\n outputSettingsButton.title = 'Output settings';\n outputSettingsButton.setAttribute('aria-label', 'Output settings (format and quality)');\n outputSettingsButton.setAttribute('aria-haspopup', 'dialog');\n outputSettingsButton.setAttribute('aria-expanded', 'false');\n outputSettingsButton.innerHTML = icon('chevronDown');\n\n exportGroup.appendChild(exportButton);\n exportGroup.appendChild(outputSettingsButton);\n utilFooter.appendChild(exportGroup);\n\n // aria-atomic so screen readers read the whole message, not just diffed tokens.\n const liveRegion = document.createElement('div');\n liveRegion.className = 'kalotyp-visually-hidden';\n liveRegion.setAttribute('role', 'status');\n liveRegion.setAttribute('aria-live', 'polite');\n liveRegion.setAttribute('aria-atomic', 'true');\n\n main.appendChild(stage);\n main.appendChild(utilMain);\n\n root.appendChild(navTools);\n root.appendChild(main);\n root.appendChild(utilFooter);\n\n modal.appendChild(title);\n modal.appendChild(prefsButton);\n modal.appendChild(closeButton);\n modal.appendChild(root);\n modal.appendChild(liveRegion);\n\n editor.appendChild(modal);\n\n return {\n editor,\n modal,\n root,\n main,\n stage,\n navTools,\n utilMain,\n utilFooter,\n closeButton,\n prefsButton,\n exportButton,\n outputSettingsButton,\n liveRegion,\n titleId,\n };\n}\n","import { buildShellDom, type ShellDom } from './dom/build-shell-dom.js';\n\nexport interface ShellOptions {\n host: HTMLElement;\n exportLabel: string;\n onExportClick: () => void;\n onCloseClick: () => void;\n onOutputSettingsClick: () => void;\n onPrefsClick: () => void;\n}\n\nexport interface ShellHandle {\n readonly editor: HTMLElement;\n readonly modal: HTMLElement;\n readonly root: HTMLElement;\n readonly exportButton: HTMLButtonElement;\n /** Caret button next to Save — opens the output-format popover. */\n readonly outputSettingsButton: HTMLButtonElement;\n /** Gear button next to close — opens the Preferences modal. */\n readonly prefsButton: HTMLButtonElement;\n readonly closeButton: HTMLButtonElement;\n readonly stage: HTMLElement;\n readonly utilMain: HTMLElement;\n readonly navTools: HTMLElement;\n /** Polite live-region announcer; identical messages re-announce via a trailing-space flip (NVDA/JAWS suppress duplicate text). */\n announce(message: string): void;\n destroy(): void;\n}\n\nexport function mountShell(options: ShellOptions): ShellHandle {\n const dom: ShellDom = buildShellDom({ exportLabel: options.exportLabel });\n\n const onExport = () => options.onExportClick();\n const onClose = () => options.onCloseClick();\n const onOutputSettings = () => options.onOutputSettingsClick();\n const onPrefs = () => options.onPrefsClick();\n dom.exportButton.addEventListener('click', onExport);\n dom.closeButton.addEventListener('click', onClose);\n dom.outputSettingsButton.addEventListener('click', onOutputSettings);\n dom.prefsButton.addEventListener('click', onPrefs);\n\n options.host.appendChild(dom.editor);\n\n // NVDA/JAWS suppress identical-text updates in live regions; flip a trailing space to force re-announce.\n let announceFlip = false;\n function announce(message: string): void {\n announceFlip = !announceFlip;\n dom.liveRegion.textContent = announceFlip ? `${message} ` : message;\n }\n\n return {\n editor: dom.editor,\n modal: dom.modal,\n root: dom.root,\n exportButton: dom.exportButton,\n outputSettingsButton: dom.outputSettingsButton,\n prefsButton: dom.prefsButton,\n closeButton: dom.closeButton,\n stage: dom.stage,\n utilMain: dom.utilMain,\n navTools: dom.navTools,\n announce,\n destroy() {\n dom.exportButton.removeEventListener('click', onExport);\n dom.closeButton.removeEventListener('click', onClose);\n dom.outputSettingsButton.removeEventListener('click', onOutputSettings);\n dom.prefsButton.removeEventListener('click', onPrefs);\n dom.editor.remove();\n },\n };\n}\n"],"x_google_ignoreList":[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25],"mappings":"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"}
|