@magicpages/kalotyp-ui 0.1.2 → 0.2.0

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/index.js.map CHANGED
@@ -1 +1 @@
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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"}
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/bold.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/italic.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/search.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/smile.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/text-align-end.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/text-align-center.mjs","../../../node_modules/.pnpm/lucide@1.17.0/node_modules/lucide/dist/esm/icons/text-align-start.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/fonts.ts","../../core/src/plugins/annotate/smooth.ts","../../core/src/plugins/annotate/state.ts","../../core/src/plugins/annotate/text-layout.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/emoji-data.ts","../src/plugins/annotate/emoji-images.ts","../src/plugins/annotate/emoji-picker.ts","../src/plugins/annotate/fonts-loader.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 Bold = [\n [\"path\", { d: \"M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8\" }]\n];\n\nexport { Bold as default };\n//# sourceMappingURL=bold.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 Italic = [\n [\"line\", { x1: \"19\", x2: \"10\", y1: \"4\", y2: \"4\" }],\n [\"line\", { x1: \"14\", x2: \"5\", y1: \"20\", y2: \"20\" }],\n [\"line\", { x1: \"15\", x2: \"9\", y1: \"4\", y2: \"20\" }]\n];\n\nexport { Italic as default };\n//# sourceMappingURL=italic.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 Search = [\n [\"path\", { d: \"m21 21-4.34-4.34\" }],\n [\"circle\", { cx: \"11\", cy: \"11\", r: \"8\" }]\n];\n\nexport { Search as default };\n//# sourceMappingURL=search.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 Smile = [\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"10\" }],\n [\"path\", { d: \"M8 14s1.5 2 4 2 4-2 4-2\" }],\n [\"line\", { x1: \"9\", x2: \"9.01\", y1: \"9\", y2: \"9\" }],\n [\"line\", { x1: \"15\", x2: \"15.01\", y1: \"9\", y2: \"9\" }]\n];\n\nexport { Smile as default };\n//# sourceMappingURL=smile.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 TextAlignEnd = [\n [\"path\", { d: \"M21 5H3\" }],\n [\"path\", { d: \"M21 12H9\" }],\n [\"path\", { d: \"M21 19H7\" }]\n];\n\nexport { TextAlignEnd as default };\n//# sourceMappingURL=text-align-end.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 TextAlignCenter = [\n [\"path\", { d: \"M21 5H3\" }],\n [\"path\", { d: \"M17 12H7\" }],\n [\"path\", { d: \"M19 19H5\" }]\n];\n\nexport { TextAlignCenter as default };\n//# sourceMappingURL=text-align-center.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 TextAlignStart = [\n [\"path\", { d: \"M21 5H3\" }],\n [\"path\", { d: \"M15 12H3\" }],\n [\"path\", { d: \"M17 19H3\" }]\n];\n\nexport { TextAlignStart as default };\n//# sourceMappingURL=text-align-start.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 AlignCenter,\n AlignLeft,\n AlignRight,\n ArrowRight,\n Bold as BoldIcon,\n Check as CheckIcon,\n ChevronDown,\n Circle as CircleIcon,\n X as CloseIcon,\n FlipHorizontal2,\n FlipVertical2,\n Highlighter,\n Italic as ItalicIcon,\n Keyboard as KeyboardIcon,\n Link2,\n Link2Off,\n MousePointer2,\n Pencil,\n Plus as PlusIcon,\n Redo2,\n Search as SearchIcon,\n Settings as SettingsIcon,\n Smile as SmileIcon,\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 | 'bold'\n | 'italic'\n | 'alignLeft'\n | 'alignCenter'\n | 'alignRight'\n | 'emoji'\n | 'search';\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 case 'bold':\n return BoldIcon as IconNode;\n case 'italic':\n return ItalicIcon as IconNode;\n case 'alignLeft':\n return AlignLeft as IconNode;\n case 'alignCenter':\n return AlignCenter as IconNode;\n case 'alignRight':\n return AlignRight as IconNode;\n case 'emoji':\n return SmileIcon as IconNode;\n case 'search':\n return SearchIcon 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 * Font catalogue for the text annotation tool. The set mirrors the fonts\n * Ghost's own admin offers (loaded from fonts.bunny.net by Ghost's\n * `admin-x-design-system`), so annotations can match a site's typography.\n * The UI loads the same families from Bunny at runtime; this module is the\n * single source for the CSS family stacks and the `font` shorthand used by\n * the inline editor, the canvas preview, and the bake — keeping all three\n * byte-identical (WYSIWYG).\n */\n\nimport type { TextShape } from './state.js';\n\n/**\n * System stack — the default, always available with no web font. Resolves to\n * platform UI fonts.\n */\nexport const SYSTEM_FONT_STACK =\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif';\n\n/**\n * Colour-emoji stack for the emoji sticker tool. Emoji render with the\n * platform's native colour-emoji font (no web font requested), so the baked\n * appearance is whatever the editing browser drew. `sans-serif` is the last\n * resort if no emoji font is installed (renders the dotted-box fallback).\n */\nexport const EMOJI_FONT_STACK =\n '\"Apple Color Emoji\", \"Segoe UI Emoji\", \"Noto Color Emoji\", \"Twemoji Mozilla\", \"Android Emoji\", sans-serif';\n\nexport interface FontDef {\n /** Stable key stored on the shape; resolved to a CSS stack via `fontStackFor`. */\n readonly key: string;\n /** Human label shown in the picker. */\n readonly label: string;\n /** CSS font-family stack, including a generic fallback. */\n readonly stack: string;\n /**\n * fonts.bunny.net family slug, or `null` for the system default (no web\n * font to request). The UI builds one combined Bunny request from these.\n */\n readonly bunnyName: string | null;\n /** Generic CSS fallback class, used to build the stack and as a hint. */\n readonly generic: 'sans-serif' | 'serif' | 'monospace';\n}\n\n/**\n * The catalogue. The first entry (`system`) is the default. The rest mirror\n * Ghost's curated Bunny set; their family names match Bunny's font CSS so the\n * loaded `@font-face` families resolve.\n */\nexport const TEXT_FONTS: readonly FontDef[] = [\n {\n key: 'system',\n label: 'System',\n stack: SYSTEM_FONT_STACK,\n bunnyName: null,\n generic: 'sans-serif',\n },\n {\n key: 'inter',\n label: 'Inter',\n stack: '\"Inter\", sans-serif',\n bunnyName: 'inter',\n generic: 'sans-serif',\n },\n {\n key: 'roboto',\n label: 'Roboto',\n stack: '\"Roboto\", sans-serif',\n bunnyName: 'roboto',\n generic: 'sans-serif',\n },\n {\n key: 'manrope',\n label: 'Manrope',\n stack: '\"Manrope\", sans-serif',\n bunnyName: 'manrope',\n generic: 'sans-serif',\n },\n {\n key: 'poppins',\n label: 'Poppins',\n stack: '\"Poppins\", sans-serif',\n bunnyName: 'poppins',\n generic: 'sans-serif',\n },\n {\n key: 'nunito',\n label: 'Nunito',\n stack: '\"Nunito\", sans-serif',\n bunnyName: 'nunito',\n generic: 'sans-serif',\n },\n {\n key: 'fira-sans',\n label: 'Fira Sans',\n stack: '\"Fira Sans\", sans-serif',\n bunnyName: 'fira-sans',\n generic: 'sans-serif',\n },\n {\n key: 'noto-sans',\n label: 'Noto Sans',\n stack: '\"Noto Sans\", sans-serif',\n bunnyName: 'noto-sans',\n generic: 'sans-serif',\n },\n {\n key: 'tenor-sans',\n label: 'Tenor Sans',\n stack: '\"Tenor Sans\", sans-serif',\n bunnyName: 'tenor-sans',\n generic: 'sans-serif',\n },\n {\n key: 'space-grotesk',\n label: 'Space Grotesk',\n stack: '\"Space Grotesk\", sans-serif',\n bunnyName: 'space-grotesk',\n generic: 'sans-serif',\n },\n {\n key: 'chakra-petch',\n label: 'Chakra Petch',\n stack: '\"Chakra Petch\", sans-serif',\n bunnyName: 'chakra-petch',\n generic: 'sans-serif',\n },\n { key: 'lora', label: 'Lora', stack: '\"Lora\", serif', bunnyName: 'lora', generic: 'serif' },\n {\n key: 'merriweather',\n label: 'Merriweather',\n stack: '\"Merriweather\", serif',\n bunnyName: 'merriweather',\n generic: 'serif',\n },\n {\n key: 'noto-serif',\n label: 'Noto Serif',\n stack: '\"Noto Serif\", serif',\n bunnyName: 'noto-serif',\n generic: 'serif',\n },\n {\n key: 'ibm-plex-serif',\n label: 'IBM Plex Serif',\n stack: '\"IBM Plex Serif\", serif',\n bunnyName: 'ibm-plex-serif',\n generic: 'serif',\n },\n { key: 'cardo', label: 'Cardo', stack: '\"Cardo\", serif', bunnyName: 'cardo', generic: 'serif' },\n {\n key: 'old-standard-tt',\n label: 'Old Standard TT',\n stack: '\"Old Standard TT\", serif',\n bunnyName: 'old-standard-tt',\n generic: 'serif',\n },\n { key: 'prata', label: 'Prata', stack: '\"Prata\", serif', bunnyName: 'prata', generic: 'serif' },\n {\n key: 'rufina',\n label: 'Rufina',\n stack: '\"Rufina\", serif',\n bunnyName: 'rufina',\n generic: 'serif',\n },\n {\n key: 'jetbrains-mono',\n label: 'JetBrains Mono',\n stack: '\"JetBrains Mono\", monospace',\n bunnyName: 'jetbrains-mono',\n generic: 'monospace',\n },\n {\n key: 'fira-mono',\n label: 'Fira Mono',\n stack: '\"Fira Mono\", monospace',\n bunnyName: 'fira-mono',\n generic: 'monospace',\n },\n {\n key: 'space-mono',\n label: 'Space Mono',\n stack: '\"Space Mono\", monospace',\n bunnyName: 'space-mono',\n generic: 'monospace',\n },\n];\n\nconst FONT_BY_KEY = new Map<string, FontDef>(TEXT_FONTS.map((f) => [f.key, f]));\n\n/** Resolve a font key to its CSS family stack, falling back to the system stack. */\nexport function fontStackFor(key: string): string {\n return FONT_BY_KEY.get(key)?.stack ?? SYSTEM_FONT_STACK;\n}\n\n/** Look up a font definition by key, or the default if unknown. */\nexport function fontDefFor(key: string): FontDef {\n return FONT_BY_KEY.get(key) ?? TEXT_FONTS[0];\n}\n\n/**\n * Build the CSS `font` shorthand for a text shape:\n * `\"<style> <weight> <fontSize>px <stack>\"`. The single source consumed by\n * the inline editor, canvas preview, bake, and `document.fonts.load`.\n * `scale` lets the editor render at the on-screen (zoomed) size.\n */\nexport function cssFontString(shape: TextShape, scale = 1): string {\n const style = shape.fontStyle === 'italic' ? 'italic ' : '';\n const weight = shape.fontWeight === 'bold' ? 'bold ' : '';\n const size = shape.fontSize * scale;\n return `${style}${weight}${size}px ${fontStackFor(shape.fontFamily)}`;\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' | 'emoji';\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 type TextFontWeight = 'normal' | 'bold';\nexport type TextFontStyle = 'normal' | 'italic';\nexport type TextAlign = 'left' | 'center' | 'right';\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. Changed via the panel, not by handles. */\n readonly fontSize: number;\n /** CSS colour string. */\n readonly color: string;\n /** Glyph justification (multi-line text aligns relative to the anchor). */\n readonly textAlign: TextAlign;\n /** Font key from the font catalogue (see fonts.ts), e.g. `'system'`, `'inter'`. */\n readonly fontFamily: string;\n readonly fontWeight: TextFontWeight;\n readonly fontStyle: TextFontStyle;\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 interface EmojiShape extends ShapeBase {\n readonly kind: 'emoji';\n /** Top-left of the (square) sticker box, in image-space pixels. */\n readonly x: number;\n readonly y: number;\n /** The emoji character; resolved to vector artwork at paint time. */\n readonly emoji: string;\n /** Box edge length in image-space pixels; the glyph is drawn at this size. */\n readonly size: number;\n /** Clockwise rotation in degrees about the box centre (0 = upright). */\n readonly rotation: number;\n}\n\nexport type Shape =\n | TextShape\n | RectShape\n | EllipseShape\n | ArrowShape\n | FreehandShape\n | HighlightShape\n | EmojiShape;\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 /** Font key for new text shapes (see fonts.ts). */\n readonly fontFamily: string;\n readonly fontWeight: TextFontWeight;\n readonly fontStyle: TextFontStyle;\n readonly textAlign: TextAlign;\n /** The armed emoji for new emoji stickers (see fonts: native OS emoji). */\n readonly emoji: string;\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\n/** Default font key for new text shapes; resolves to the system stack. */\nexport const DEFAULT_FONT_KEY = 'system';\n\n/** The emoji a fresh editor session arms for the emoji sticker tool. */\nexport const DEFAULT_EMOJI = '😀';\n/** Smallest emoji sticker edge in image-space pixels (floor for handle resize). */\nexport const EMOJI_MIN_SIZE = 8;\n\n/**\n * Default edge length for a freshly placed emoji sticker, scaled to the image\n * so a sticker reads at a sensible size on both tiny and huge sources. Shared by\n * click-placement and centre-insert so the two paths agree.\n */\nexport function defaultEmojiSize(imageSize: {\n readonly width: number;\n readonly height: number;\n}): number {\n const shortEdge = Math.min(imageSize.width, imageSize.height);\n return Math.max(64, Math.round(shortEdge * 0.2));\n}\n\n/** Wrap an angle in degrees into the [0, 360) range. */\nexport function normalizeAngle(degrees: number): number {\n return ((degrees % 360) + 360) % 360;\n}\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 fontFamily: DEFAULT_FONT_KEY,\n fontWeight: 'normal',\n fontStyle: 'normal',\n textAlign: 'left',\n emoji: DEFAULT_EMOJI,\n };\n}\n\n/**\n * Fill any missing font fields on a text shape with defaults. Defensive\n * against partial literals (tests) and stale undo snapshots from a prior dev\n * build during hot reload — shapes are never persisted, so this is a safety\n * net, not a schema migration.\n */\nexport function normalizeTextShape(shape: TextShape): TextShape {\n if (\n typeof shape.fontFamily === 'string' &&\n (shape.fontWeight === 'normal' || shape.fontWeight === 'bold') &&\n (shape.fontStyle === 'normal' || shape.fontStyle === 'italic')\n ) {\n return shape;\n }\n return {\n ...shape,\n fontFamily: typeof shape.fontFamily === 'string' ? shape.fontFamily : DEFAULT_FONT_KEY,\n fontWeight: shape.fontWeight === 'bold' ? 'bold' : 'normal',\n fontStyle: shape.fontStyle === 'italic' ? 'italic' : 'normal',\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 case 'emoji':\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 'emoji':\n // Reposition the box so it straddles the mirrored pixels; the glyph\n // itself is not flipped (a flipped emoji reads as broken). Reflection\n // reverses the spin direction.\n return {\n ...shape,\n x: dims.width - shape.x - shape.size,\n rotation: normalizeAngle(-shape.rotation),\n };\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 'emoji':\n // `180 - rotation` keeps H∘V consistent with a 180° image rotation.\n return {\n ...shape,\n y: dims.height - shape.y - shape.size,\n rotation: normalizeAngle(180 - shape.rotation),\n };\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 'emoji': {\n // Rotating a square box yields a square box of the same edge; only the\n // top-left corner moves, so keep `size` and re-derive x/y from the\n // rotated corners. The glyph's own rotation advances with the image.\n const corners = [\n rotatePoint(shape.x, shape.y),\n rotatePoint(shape.x + shape.size, shape.y + shape.size),\n ];\n const newX = Math.min(corners[0].x, corners[1].x);\n const newY = Math.min(corners[0].y, corners[1].y);\n return { ...shape, x: newX, y: newY, rotation: normalizeAngle(shape.rotation + turns * 90) };\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' | 'emoji';\n\nexport const KEYBOARD_PLACEABLE_KINDS: ReadonlyArray<KeyboardPlaceableKind> = [\n 'text',\n 'rect',\n 'ellipse',\n 'arrow',\n 'emoji',\n];\n\nexport function isKeyboardPlaceableKind(kind: ShapeKind): kind is KeyboardPlaceableKind {\n return (\n kind === 'text' || kind === 'rect' || kind === 'ellipse' || kind === 'arrow' || kind === 'emoji'\n );\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 / emoji). */\nexport type KeyboardPlaceableShape = TextShape | RectShape | EllipseShape | ArrowShape | EmojiShape;\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: style.textAlign,\n fontFamily: style.fontFamily,\n fontWeight: style.fontWeight,\n fontStyle: style.fontStyle,\n };\n }\n case 'emoji': {\n const size = defaultEmojiSize(imageSize);\n const x = Math.round(cx - size / 2);\n const y = Math.round(cy - size / 2);\n return { id, kind: 'emoji', x, y, emoji: style.emoji, size, rotation: 0 };\n }\n }\n}\n","/**\n * Text measurement for the text annotation tool. Pure and framework-agnostic\n * so the live canvas preview, the bake, and the geometry (selection outline /\n * hit-test) all share one notion of a text shape's extent.\n *\n * Text is anchor-based and does NOT wrap: lines are split on explicit `\\n`\n * only, and the shape's width is its widest line. The line-measuring function\n * is injected so the same code works with real canvas metrics (browser\n * preview + bake) and with a heuristic estimate in headless/jsdom contexts\n * where `measureText` returns 0.\n */\n\nimport type { TextShape } from './state.js';\n\n/** Line-height multiple of the font size. Shared by layout and bake. */\nexport const TEXT_LINE_HEIGHT = 1.2;\n\n/** Measures the rendered width of a single line of text, in image-space px. */\nexport type MeasureLine = (line: string) => number;\n\n/** Split text into rendered lines. Empty text yields one empty line (caret height). */\nexport function textLines(text: string): string[] {\n return text.length === 0 ? [''] : text.split('\\n');\n}\n\nexport interface TextLayout {\n readonly lines: string[];\n readonly width: number;\n readonly height: number;\n}\n\n/** Measure a text shape's natural extent: widest line × line count. */\nexport function layoutTextLines(shape: TextShape, measure: MeasureLine): TextLayout {\n const lines = textLines(shape.text);\n let width = 0;\n for (const line of lines) {\n const w = measure(line);\n if (w > width) width = w;\n }\n // A floor so an empty box still has a grabbable width.\n width = Math.max(width, shape.fontSize * 0.6);\n return {\n lines,\n width,\n height: lines.length * shape.fontSize * TEXT_LINE_HEIGHT,\n };\n}\n\n/**\n * Heuristic line-width estimate for headless contexts where canvas\n * `measureText` returns 0. Assumes ~0.55em mean Latin glyph advance — close\n * enough for the selection outline; the real renderer measures at paint time.\n */\nexport function estimateLineWidth(line: string, fontSize: number): number {\n return line.length * fontSize * 0.55;\n}\n\n/**\n * Horizontal offset of a line within a left-origin text block, given the\n * block's width (widest line) and this line's width. `textAlign` justifies the\n * line *within* the block — it never moves the block's origin. Shared by the\n * canvas paint and (conceptually) the editor's CSS `text-align`, so both place\n * glyphs identically.\n */\nexport function lineOffset(\n blockWidth: number,\n lineWidth: number,\n align: 'left' | 'center' | 'right',\n): number {\n switch (align) {\n case 'left':\n return 0;\n case 'center':\n return (blockWidth - lineWidth) / 2;\n case 'right':\n return blockWidth - lineWidth;\n }\n}\n","import { createBakeCanvas, getBakeContext2D } from '../../canvas/bake-canvas.js';\nimport type { SourceImage } from '../utility.js';\nimport { cssFontString, EMOJI_FONT_STACK } from './fonts.js';\nimport { tracePath } from './smooth.js';\nimport { assertNever, normalizeTextShape, type Shape } from './state.js';\nimport { lineOffset, TEXT_LINE_HEIGHT, textLines } from './text-layout.js';\n\nexport interface AnnotateBakeInput {\n readonly shapes: ReadonlyArray<Shape>;\n}\n\n/**\n * Resolve an emoji character to a drawable image (its vector artwork). Returns\n * `null` when no image is available yet, so the caller falls back to the OS\n * emoji font. Supplied by the UI layer, which owns the async image cache —\n * keeping this module DOM-free (it just calls `drawImage` on whatever source it\n * is handed).\n */\nexport type ResolveEmojiImage = (emoji: string) => CanvasImageSource | null;\n\nexport interface PaintShapeOptions {\n readonly resolveEmojiImage?: ResolveEmojiImage;\n}\n\n/** Re-exported from fonts.ts; the system stack is the default font key's value. */\nexport { SYSTEM_FONT_STACK } from './fonts.js';\n\n/**\n * Cap on how long the bake waits for web fonts to load before painting. A\n * slow or unreachable CDN must never block Save indefinitely; on timeout we\n * bake with whatever faces are ready (falling back to the generic family).\n */\nconst FONT_LOAD_TIMEOUT_MS = 400;\n\n/**\n * Ensure every font face used by the text shapes is loaded before bake, so the\n * baked output matches the on-screen preview. Bounded by a timeout and a\n * no-op where `document.fonts` is unavailable (jsdom / worker / OffscreenCanvas).\n */\n// Exported for unit testing the font-await behaviour; not part of the public\n// API surface (intentionally absent from index.ts).\nexport async function awaitFontsForBake(shapes: ReadonlyArray<Shape>): Promise<void> {\n if (typeof document === 'undefined' || !('fonts' in document)) return;\n const specs = new Set<string>();\n for (const shape of shapes) {\n if (shape.kind === 'text') specs.add(cssFontString(normalizeTextShape(shape)));\n }\n if (specs.size === 0) return;\n const timeout = new Promise<void>((resolve) => setTimeout(resolve, FONT_LOAD_TIMEOUT_MS));\n // `document.fonts.load` REJECTS when a face is registered (the Bunny CSS\n // parsed) but its woff2 fetch fails — a realistic offline / blocked / flaky\n // case. Swallow it so the bake always proceeds and falls back to the generic\n // family; a font fetch must never break Save or a tab switch.\n const loaded = Promise.all([...specs].map((spec) => document.fonts.load(spec)))\n .then(() => document.fonts.ready)\n .then(() => undefined)\n .catch(() => undefined);\n await Promise.race([loaded, timeout]);\n}\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 opts?: PaintShapeOptions,\n): Promise<SourceImage> {\n if (state.shapes.length === 0) return source;\n\n await awaitFontsForBake(state.shapes);\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, opts);\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 opts?: PaintShapeOptions,\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 case 'emoji':\n paintEmoji(ctx, shape, opts?.resolveEmojiImage);\n return;\n default:\n assertNever(shape);\n }\n}\n\nfunction paintEmoji(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n shape: Shape & { kind: 'emoji' },\n resolveEmojiImage?: ResolveEmojiImage,\n): void {\n // `shape.x, shape.y` is the box's top-left; the glyph fills the square box.\n const image = resolveEmojiImage?.(shape.emoji) ?? null;\n ctx.save();\n if (shape.rotation) {\n // Rotate about the box centre so the sticker spins in place.\n const cx = shape.x + shape.size / 2;\n const cy = shape.y + shape.size / 2;\n ctx.translate(cx, cy);\n ctx.rotate((shape.rotation * Math.PI) / 180);\n ctx.translate(-cx, -cy);\n }\n if (image) {\n // Vector artwork (OpenMoji SVG) rasterised at the box size — crisp at any size.\n ctx.drawImage(image, shape.x, shape.y, shape.size, shape.size);\n } else {\n // Fallback: the OS emoji font (used before the SVG loads, in workers without\n // a resolver, or if the artwork is unavailable). Colour comes from the font.\n ctx.textAlign = 'left';\n ctx.textBaseline = 'top';\n ctx.font = `${shape.size}px ${EMOJI_FONT_STACK}`;\n ctx.fillText(shape.emoji, shape.x, shape.y);\n }\n ctx.restore();\n}\n\nfunction paintText(\n ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n textShape: Shape & { kind: 'text' },\n): void {\n const shape = normalizeTextShape(textShape);\n ctx.save();\n ctx.fillStyle = shape.color;\n ctx.font = cssFontString(shape);\n // `shape.x, shape.y` is ALWAYS the block's top-left. Alignment justifies each\n // line *within* the block via a manual per-line offset — never via the canvas\n // anchor — so the editor (which uses the same top-left origin + CSS\n // text-align) matches exactly, for any alignment. No wrapping: one line per\n // explicit `\\n`.\n ctx.textAlign = 'left';\n ctx.textBaseline = 'top';\n const lines = textLines(shape.text);\n const widths = lines.map((line) => ctx.measureText(line).width);\n const blockWidth = widths.reduce((max, w) => (w > max ? w : max), 0);\n const lineHeight = shape.fontSize * TEXT_LINE_HEIGHT;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line === undefined) continue;\n const dx = lineOffset(blockWidth, widths[i] ?? 0, shape.textAlign);\n ctx.fillText(line, shape.x + dx, 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, normalizeTextShape, type Shape } from './state.js';\nimport { estimateLineWidth, layoutTextLines } from './text-layout.js';\n\n/**\n * Axis-aligned bounding box in image-space pixels. `shape.x, shape.y` is the\n * text block's top-left for every `textAlign` (alignment justifies lines within\n * the block, it doesn't move the origin), so the box origin is the anchor. A\n * font-metric estimate is used here (jsdom's `measureText` returns 0); the\n * renderer measures real text at paint time.\n */\nexport function boundingBoxOf(shape: Shape): Rect {\n switch (shape.kind) {\n case 'text': {\n const text = normalizeTextShape(shape);\n const { width, height } = layoutTextLines(text, (line) =>\n estimateLineWidth(line, text.fontSize),\n );\n return { x: text.x, y: text.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 'emoji':\n return { x: shape.x, y: shape.y, width: shape.size, height: shape.size };\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","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 // Picks anywhere inside the (filled) box.\n return pointInRect(point, boundingBoxOf(shape));\n case 'emoji': {\n const box = boundingBoxOf(shape);\n if (!shape.rotation) return pointInRect(point, box);\n // Inverse-rotate the point about the sticker centre into the box's local\n // (unrotated) frame, so picking follows the rotated glyph rather than its\n // axis-aligned bounds.\n const cx = box.x + box.width / 2;\n const cy = box.y + box.height / 2;\n const rad = (-shape.rotation * Math.PI) / 180;\n const dx = point.x - cx;\n const dy = point.y - cy;\n const local = {\n x: cx + dx * Math.cos(rad) - dy * Math.sin(rad),\n y: cy + dx * Math.sin(rad) + dy * Math.cos(rad),\n };\n return pointInRect(local, box);\n }\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 * - emoji → X, Y, Size, Angle (the square sticker box; Size and Angle are the\n * keyboard equivalents of dragging a corner / the rotate handle).\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 {\n type ArrowShape,\n type EllipseShape,\n EMOJI_MIN_SIZE,\n type EmojiShape,\n normalizeAngle,\n type RectShape,\n type Shape,\n type 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 | {\n readonly kind: 'emoji';\n readonly x: number;\n readonly y: number;\n readonly size: number;\n readonly rotation: number;\n };\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 /** Unit named in the accessible label; defaults to pixels. */\n readonly unit?: string;\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\nconst EMOJI_FIELDS: ReadonlyArray<FieldSpec> = [\n { id: 'x', label: 'X' },\n { id: 'y', label: 'Y' },\n // Keep the control's floor in sync with the clamp in `applyCoordEdit`.\n { id: 'size', label: 'Size', min: EMOJI_MIN_SIZE },\n { id: 'rotation', label: 'Angle', unit: 'degrees' },\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' | 'emoji' | null = null;\n const inputs = new Map<string, HTMLInputElement>();\n\n function rebuildFor(kind: 'rect' | 'ellipse' | 'arrow' | 'text' | 'emoji'): 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} (${spec.unit ?? '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 case 'emoji': {\n setVal('x', shape.x);\n setVal('y', shape.y);\n setVal('size', shape.size);\n setVal('rotation', shape.rotation);\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 case 'emoji': {\n const x = Math.round(num('x'));\n const y = Math.round(num('y'));\n const size = Math.round(num('size'));\n const rotation = Math.round(num('rotation'));\n if (![x, y, size, rotation].every(Number.isFinite)) return null;\n return { kind: 'emoji', x, y, size, rotation };\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(\n kind: 'rect' | 'ellipse' | 'arrow' | 'text' | 'emoji',\n): 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 case 'emoji':\n return EMOJI_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 case 'emoji': {\n if (edit.kind !== 'emoji') return shape;\n const next: EmojiShape = {\n ...shape,\n x: edit.x,\n y: edit.y,\n size: Math.max(EMOJI_MIN_SIZE, edit.size),\n rotation: normalizeAngle(edit.rotation),\n };\n return next;\n }\n default:\n return shape;\n }\n}\n","/**\n * GENERATED FILE — do not edit by hand.\n *\n * Emoji catalogue for the annotate emoji picker. List/names from\n * unicode-emoji-json (MIT); `key` is the OpenMoji (CC-BY-SA-4.0) artwork\n * filename stem, resolved at generation time. Regenerate with `pnpm gen:emoji`\n * — see scripts/gen-emoji-data.mjs.\n *\n * 1914 emojis across 9 groups.\n */\n\nexport interface EmojiEntry {\n /** The emoji character (used for search context and the placed shape). */\n readonly char: string;\n /** CLDR name, used as the search corpus and the picker button's accessible label. */\n readonly name: string;\n /** OpenMoji artwork filename stem; the SVG URL is `<assetBase>/<key>.svg`. */\n readonly key: string;\n}\n\nexport interface EmojiGroup {\n /** Stable group key (the Unicode group slug). */\n readonly id: string;\n /** Human-readable group label for the category tab. */\n readonly label: string;\n readonly emojis: ReadonlyArray<EmojiEntry>;\n}\n\nexport const EMOJI_GROUPS: ReadonlyArray<EmojiGroup> = [\n {\"id\":\"smileys_emotion\",\"label\":\"Smileys & Emotion\",\"emojis\":[{\"char\":\"😀\",\"name\":\"grinning face\",\"key\":\"1F600\"},{\"char\":\"😃\",\"name\":\"grinning face with big eyes\",\"key\":\"1F603\"},{\"char\":\"😄\",\"name\":\"grinning face with smiling eyes\",\"key\":\"1F604\"},{\"char\":\"😁\",\"name\":\"beaming face with smiling eyes\",\"key\":\"1F601\"},{\"char\":\"😆\",\"name\":\"grinning squinting face\",\"key\":\"1F606\"},{\"char\":\"😅\",\"name\":\"grinning face with sweat\",\"key\":\"1F605\"},{\"char\":\"🤣\",\"name\":\"rolling on the floor laughing\",\"key\":\"1F923\"},{\"char\":\"😂\",\"name\":\"face with tears of joy\",\"key\":\"1F602\"},{\"char\":\"🙂\",\"name\":\"slightly smiling face\",\"key\":\"1F642\"},{\"char\":\"🙃\",\"name\":\"upside-down face\",\"key\":\"1F643\"},{\"char\":\"🫠\",\"name\":\"melting face\",\"key\":\"1FAE0\"},{\"char\":\"😉\",\"name\":\"winking face\",\"key\":\"1F609\"},{\"char\":\"😊\",\"name\":\"smiling face with smiling eyes\",\"key\":\"1F60A\"},{\"char\":\"😇\",\"name\":\"smiling face with halo\",\"key\":\"1F607\"},{\"char\":\"🥰\",\"name\":\"smiling face with hearts\",\"key\":\"1F970\"},{\"char\":\"😍\",\"name\":\"smiling face with heart-eyes\",\"key\":\"1F60D\"},{\"char\":\"🤩\",\"name\":\"star-struck\",\"key\":\"1F929\"},{\"char\":\"😘\",\"name\":\"face blowing a kiss\",\"key\":\"1F618\"},{\"char\":\"😗\",\"name\":\"kissing face\",\"key\":\"1F617\"},{\"char\":\"☺️\",\"name\":\"smiling face\",\"key\":\"263A\"},{\"char\":\"😚\",\"name\":\"kissing face with closed eyes\",\"key\":\"1F61A\"},{\"char\":\"😙\",\"name\":\"kissing face with smiling eyes\",\"key\":\"1F619\"},{\"char\":\"🥲\",\"name\":\"smiling face with tear\",\"key\":\"1F972\"},{\"char\":\"😋\",\"name\":\"face savoring food\",\"key\":\"1F60B\"},{\"char\":\"😛\",\"name\":\"face with tongue\",\"key\":\"1F61B\"},{\"char\":\"😜\",\"name\":\"winking face with tongue\",\"key\":\"1F61C\"},{\"char\":\"🤪\",\"name\":\"zany face\",\"key\":\"1F92A\"},{\"char\":\"😝\",\"name\":\"squinting face with tongue\",\"key\":\"1F61D\"},{\"char\":\"🤑\",\"name\":\"money-mouth face\",\"key\":\"1F911\"},{\"char\":\"🤗\",\"name\":\"smiling face with open hands\",\"key\":\"1F917\"},{\"char\":\"🤭\",\"name\":\"face with hand over mouth\",\"key\":\"1F92D\"},{\"char\":\"🫢\",\"name\":\"face with open eyes and hand over mouth\",\"key\":\"1FAE2\"},{\"char\":\"🫣\",\"name\":\"face with peeking eye\",\"key\":\"1FAE3\"},{\"char\":\"🤫\",\"name\":\"shushing face\",\"key\":\"1F92B\"},{\"char\":\"🤔\",\"name\":\"thinking face\",\"key\":\"1F914\"},{\"char\":\"🫡\",\"name\":\"saluting face\",\"key\":\"1FAE1\"},{\"char\":\"🤐\",\"name\":\"zipper-mouth face\",\"key\":\"1F910\"},{\"char\":\"🤨\",\"name\":\"face with raised eyebrow\",\"key\":\"1F928\"},{\"char\":\"😐\",\"name\":\"neutral face\",\"key\":\"1F610\"},{\"char\":\"😑\",\"name\":\"expressionless face\",\"key\":\"1F611\"},{\"char\":\"😶\",\"name\":\"face without mouth\",\"key\":\"1F636\"},{\"char\":\"🫥\",\"name\":\"dotted line face\",\"key\":\"1FAE5\"},{\"char\":\"😶‍🌫️\",\"name\":\"face in clouds\",\"key\":\"1F636-200D-1F32B-FE0F\"},{\"char\":\"😏\",\"name\":\"smirking face\",\"key\":\"1F60F\"},{\"char\":\"😒\",\"name\":\"unamused face\",\"key\":\"1F612\"},{\"char\":\"🙄\",\"name\":\"face with rolling eyes\",\"key\":\"1F644\"},{\"char\":\"😬\",\"name\":\"grimacing face\",\"key\":\"1F62C\"},{\"char\":\"😮‍💨\",\"name\":\"face exhaling\",\"key\":\"1F62E-200D-1F4A8\"},{\"char\":\"🤥\",\"name\":\"lying face\",\"key\":\"1F925\"},{\"char\":\"🫨\",\"name\":\"shaking face\",\"key\":\"1FAE8\"},{\"char\":\"🙂‍↔️\",\"name\":\"head shaking horizontally\",\"key\":\"1F642-200D-2194-FE0F\"},{\"char\":\"🙂‍↕️\",\"name\":\"head shaking vertically\",\"key\":\"1F642-200D-2195-FE0F\"},{\"char\":\"😌\",\"name\":\"relieved face\",\"key\":\"1F60C\"},{\"char\":\"😔\",\"name\":\"pensive face\",\"key\":\"1F614\"},{\"char\":\"😪\",\"name\":\"sleepy face\",\"key\":\"1F62A\"},{\"char\":\"🤤\",\"name\":\"drooling face\",\"key\":\"1F924\"},{\"char\":\"😴\",\"name\":\"sleeping face\",\"key\":\"1F634\"},{\"char\":\"🫩\",\"name\":\"face with bags under eyes\",\"key\":\"1FAE9\"},{\"char\":\"😷\",\"name\":\"face with medical mask\",\"key\":\"1F637\"},{\"char\":\"🤒\",\"name\":\"face with thermometer\",\"key\":\"1F912\"},{\"char\":\"🤕\",\"name\":\"face with head-bandage\",\"key\":\"1F915\"},{\"char\":\"🤢\",\"name\":\"nauseated face\",\"key\":\"1F922\"},{\"char\":\"🤮\",\"name\":\"face vomiting\",\"key\":\"1F92E\"},{\"char\":\"🤧\",\"name\":\"sneezing face\",\"key\":\"1F927\"},{\"char\":\"🥵\",\"name\":\"hot face\",\"key\":\"1F975\"},{\"char\":\"🥶\",\"name\":\"cold face\",\"key\":\"1F976\"},{\"char\":\"🥴\",\"name\":\"woozy face\",\"key\":\"1F974\"},{\"char\":\"😵\",\"name\":\"face with crossed-out eyes\",\"key\":\"1F635\"},{\"char\":\"😵‍💫\",\"name\":\"face with spiral eyes\",\"key\":\"1F635-200D-1F4AB\"},{\"char\":\"🤯\",\"name\":\"exploding head\",\"key\":\"1F92F\"},{\"char\":\"🤠\",\"name\":\"cowboy hat face\",\"key\":\"1F920\"},{\"char\":\"🥳\",\"name\":\"partying face\",\"key\":\"1F973\"},{\"char\":\"🥸\",\"name\":\"disguised face\",\"key\":\"1F978\"},{\"char\":\"😎\",\"name\":\"smiling face with sunglasses\",\"key\":\"1F60E\"},{\"char\":\"🤓\",\"name\":\"nerd face\",\"key\":\"1F913\"},{\"char\":\"🧐\",\"name\":\"face with monocle\",\"key\":\"1F9D0\"},{\"char\":\"😕\",\"name\":\"confused face\",\"key\":\"1F615\"},{\"char\":\"🫤\",\"name\":\"face with diagonal mouth\",\"key\":\"1FAE4\"},{\"char\":\"😟\",\"name\":\"worried face\",\"key\":\"1F61F\"},{\"char\":\"🙁\",\"name\":\"slightly frowning face\",\"key\":\"1F641\"},{\"char\":\"☹️\",\"name\":\"frowning face\",\"key\":\"2639\"},{\"char\":\"😮\",\"name\":\"face with open mouth\",\"key\":\"1F62E\"},{\"char\":\"😯\",\"name\":\"hushed face\",\"key\":\"1F62F\"},{\"char\":\"😲\",\"name\":\"astonished face\",\"key\":\"1F632\"},{\"char\":\"😳\",\"name\":\"flushed face\",\"key\":\"1F633\"},{\"char\":\"🫪\",\"name\":\"distorted face\",\"key\":\"1FAEA\"},{\"char\":\"🥺\",\"name\":\"pleading face\",\"key\":\"1F97A\"},{\"char\":\"🥹\",\"name\":\"face holding back tears\",\"key\":\"1F979\"},{\"char\":\"😦\",\"name\":\"frowning face with open mouth\",\"key\":\"1F626\"},{\"char\":\"😧\",\"name\":\"anguished face\",\"key\":\"1F627\"},{\"char\":\"😨\",\"name\":\"fearful face\",\"key\":\"1F628\"},{\"char\":\"😰\",\"name\":\"anxious face with sweat\",\"key\":\"1F630\"},{\"char\":\"😥\",\"name\":\"sad but relieved face\",\"key\":\"1F625\"},{\"char\":\"😢\",\"name\":\"crying face\",\"key\":\"1F622\"},{\"char\":\"😭\",\"name\":\"loudly crying face\",\"key\":\"1F62D\"},{\"char\":\"😱\",\"name\":\"face screaming in fear\",\"key\":\"1F631\"},{\"char\":\"😖\",\"name\":\"confounded face\",\"key\":\"1F616\"},{\"char\":\"😣\",\"name\":\"persevering face\",\"key\":\"1F623\"},{\"char\":\"😞\",\"name\":\"disappointed face\",\"key\":\"1F61E\"},{\"char\":\"😓\",\"name\":\"downcast face with sweat\",\"key\":\"1F613\"},{\"char\":\"😩\",\"name\":\"weary face\",\"key\":\"1F629\"},{\"char\":\"😫\",\"name\":\"tired face\",\"key\":\"1F62B\"},{\"char\":\"🥱\",\"name\":\"yawning face\",\"key\":\"1F971\"},{\"char\":\"😤\",\"name\":\"face with steam from nose\",\"key\":\"1F624\"},{\"char\":\"😡\",\"name\":\"enraged face\",\"key\":\"1F621\"},{\"char\":\"😠\",\"name\":\"angry face\",\"key\":\"1F620\"},{\"char\":\"🤬\",\"name\":\"face with symbols on mouth\",\"key\":\"1F92C\"},{\"char\":\"😈\",\"name\":\"smiling face with horns\",\"key\":\"1F608\"},{\"char\":\"👿\",\"name\":\"angry face with horns\",\"key\":\"1F47F\"},{\"char\":\"💀\",\"name\":\"skull\",\"key\":\"1F480\"},{\"char\":\"☠️\",\"name\":\"skull and crossbones\",\"key\":\"2620\"},{\"char\":\"💩\",\"name\":\"pile of poo\",\"key\":\"1F4A9\"},{\"char\":\"🤡\",\"name\":\"clown face\",\"key\":\"1F921\"},{\"char\":\"👹\",\"name\":\"ogre\",\"key\":\"1F479\"},{\"char\":\"👺\",\"name\":\"goblin\",\"key\":\"1F47A\"},{\"char\":\"👻\",\"name\":\"ghost\",\"key\":\"1F47B\"},{\"char\":\"👽\",\"name\":\"alien\",\"key\":\"1F47D\"},{\"char\":\"👾\",\"name\":\"alien monster\",\"key\":\"1F47E\"},{\"char\":\"🤖\",\"name\":\"robot\",\"key\":\"1F916\"},{\"char\":\"😺\",\"name\":\"grinning cat\",\"key\":\"1F63A\"},{\"char\":\"😸\",\"name\":\"grinning cat with smiling eyes\",\"key\":\"1F638\"},{\"char\":\"😹\",\"name\":\"cat with tears of joy\",\"key\":\"1F639\"},{\"char\":\"😻\",\"name\":\"smiling cat with heart-eyes\",\"key\":\"1F63B\"},{\"char\":\"😼\",\"name\":\"cat with wry smile\",\"key\":\"1F63C\"},{\"char\":\"😽\",\"name\":\"kissing cat\",\"key\":\"1F63D\"},{\"char\":\"🙀\",\"name\":\"weary cat\",\"key\":\"1F640\"},{\"char\":\"😿\",\"name\":\"crying cat\",\"key\":\"1F63F\"},{\"char\":\"😾\",\"name\":\"pouting cat\",\"key\":\"1F63E\"},{\"char\":\"🙈\",\"name\":\"see-no-evil monkey\",\"key\":\"1F648\"},{\"char\":\"🙉\",\"name\":\"hear-no-evil monkey\",\"key\":\"1F649\"},{\"char\":\"🙊\",\"name\":\"speak-no-evil monkey\",\"key\":\"1F64A\"},{\"char\":\"💌\",\"name\":\"love letter\",\"key\":\"1F48C\"},{\"char\":\"💘\",\"name\":\"heart with arrow\",\"key\":\"1F498\"},{\"char\":\"💝\",\"name\":\"heart with ribbon\",\"key\":\"1F49D\"},{\"char\":\"💖\",\"name\":\"sparkling heart\",\"key\":\"1F496\"},{\"char\":\"💗\",\"name\":\"growing heart\",\"key\":\"1F497\"},{\"char\":\"💓\",\"name\":\"beating heart\",\"key\":\"1F493\"},{\"char\":\"💞\",\"name\":\"revolving hearts\",\"key\":\"1F49E\"},{\"char\":\"💕\",\"name\":\"two hearts\",\"key\":\"1F495\"},{\"char\":\"💟\",\"name\":\"heart decoration\",\"key\":\"1F49F\"},{\"char\":\"❣️\",\"name\":\"heart exclamation\",\"key\":\"2763\"},{\"char\":\"💔\",\"name\":\"broken heart\",\"key\":\"1F494\"},{\"char\":\"❤️‍🔥\",\"name\":\"heart on fire\",\"key\":\"2764-FE0F-200D-1F525\"},{\"char\":\"❤️‍🩹\",\"name\":\"mending heart\",\"key\":\"2764-FE0F-200D-1FA79\"},{\"char\":\"❤️\",\"name\":\"red heart\",\"key\":\"2764\"},{\"char\":\"🩷\",\"name\":\"pink heart\",\"key\":\"1FA77\"},{\"char\":\"🧡\",\"name\":\"orange heart\",\"key\":\"1F9E1\"},{\"char\":\"💛\",\"name\":\"yellow heart\",\"key\":\"1F49B\"},{\"char\":\"💚\",\"name\":\"green heart\",\"key\":\"1F49A\"},{\"char\":\"💙\",\"name\":\"blue heart\",\"key\":\"1F499\"},{\"char\":\"🩵\",\"name\":\"light blue heart\",\"key\":\"1FA75\"},{\"char\":\"💜\",\"name\":\"purple heart\",\"key\":\"1F49C\"},{\"char\":\"🤎\",\"name\":\"brown heart\",\"key\":\"1F90E\"},{\"char\":\"🖤\",\"name\":\"black heart\",\"key\":\"1F5A4\"},{\"char\":\"🩶\",\"name\":\"grey heart\",\"key\":\"1FA76\"},{\"char\":\"🤍\",\"name\":\"white heart\",\"key\":\"1F90D\"},{\"char\":\"💋\",\"name\":\"kiss mark\",\"key\":\"1F48B\"},{\"char\":\"💯\",\"name\":\"hundred points\",\"key\":\"1F4AF\"},{\"char\":\"💢\",\"name\":\"anger symbol\",\"key\":\"1F4A2\"},{\"char\":\"🫯\",\"name\":\"fight cloud\",\"key\":\"1FAEF\"},{\"char\":\"💥\",\"name\":\"collision\",\"key\":\"1F4A5\"},{\"char\":\"💫\",\"name\":\"dizzy\",\"key\":\"1F4AB\"},{\"char\":\"💦\",\"name\":\"sweat droplets\",\"key\":\"1F4A6\"},{\"char\":\"💨\",\"name\":\"dashing away\",\"key\":\"1F4A8\"},{\"char\":\"🕳️\",\"name\":\"hole\",\"key\":\"1F573\"},{\"char\":\"💬\",\"name\":\"speech balloon\",\"key\":\"1F4AC\"},{\"char\":\"👁️‍🗨️\",\"name\":\"eye in speech bubble\",\"key\":\"1F441-FE0F-200D-1F5E8-FE0F\"},{\"char\":\"🗨️\",\"name\":\"left speech bubble\",\"key\":\"1F5E8\"},{\"char\":\"🗯️\",\"name\":\"right anger bubble\",\"key\":\"1F5EF\"},{\"char\":\"💭\",\"name\":\"thought balloon\",\"key\":\"1F4AD\"},{\"char\":\"💤\",\"name\":\"ZZZ\",\"key\":\"1F4A4\"}]},\n {\"id\":\"people_body\",\"label\":\"People & Body\",\"emojis\":[{\"char\":\"👋\",\"name\":\"waving hand\",\"key\":\"1F44B\"},{\"char\":\"🤚\",\"name\":\"raised back of hand\",\"key\":\"1F91A\"},{\"char\":\"🖐️\",\"name\":\"hand with fingers splayed\",\"key\":\"1F590\"},{\"char\":\"✋\",\"name\":\"raised hand\",\"key\":\"270B\"},{\"char\":\"🖖\",\"name\":\"vulcan salute\",\"key\":\"1F596\"},{\"char\":\"🫱\",\"name\":\"rightwards hand\",\"key\":\"1FAF1\"},{\"char\":\"🫲\",\"name\":\"leftwards hand\",\"key\":\"1FAF2\"},{\"char\":\"🫳\",\"name\":\"palm down hand\",\"key\":\"1FAF3\"},{\"char\":\"🫴\",\"name\":\"palm up hand\",\"key\":\"1FAF4\"},{\"char\":\"🫷\",\"name\":\"leftwards pushing hand\",\"key\":\"1FAF7\"},{\"char\":\"🫸\",\"name\":\"rightwards pushing hand\",\"key\":\"1FAF8\"},{\"char\":\"👌\",\"name\":\"OK hand\",\"key\":\"1F44C\"},{\"char\":\"🤌\",\"name\":\"pinched fingers\",\"key\":\"1F90C\"},{\"char\":\"🤏\",\"name\":\"pinching hand\",\"key\":\"1F90F\"},{\"char\":\"✌️\",\"name\":\"victory hand\",\"key\":\"270C\"},{\"char\":\"🤞\",\"name\":\"crossed fingers\",\"key\":\"1F91E\"},{\"char\":\"🫰\",\"name\":\"hand with index finger and thumb crossed\",\"key\":\"1FAF0\"},{\"char\":\"🤟\",\"name\":\"love-you gesture\",\"key\":\"1F91F\"},{\"char\":\"🤘\",\"name\":\"sign of the horns\",\"key\":\"1F918\"},{\"char\":\"🤙\",\"name\":\"call me hand\",\"key\":\"1F919\"},{\"char\":\"👈\",\"name\":\"backhand index pointing left\",\"key\":\"1F448\"},{\"char\":\"👉\",\"name\":\"backhand index pointing right\",\"key\":\"1F449\"},{\"char\":\"👆\",\"name\":\"backhand index pointing up\",\"key\":\"1F446\"},{\"char\":\"🖕\",\"name\":\"middle finger\",\"key\":\"1F595\"},{\"char\":\"👇\",\"name\":\"backhand index pointing down\",\"key\":\"1F447\"},{\"char\":\"☝️\",\"name\":\"index pointing up\",\"key\":\"261D\"},{\"char\":\"🫵\",\"name\":\"index pointing at the viewer\",\"key\":\"1FAF5\"},{\"char\":\"👍\",\"name\":\"thumbs up\",\"key\":\"1F44D\"},{\"char\":\"👎\",\"name\":\"thumbs down\",\"key\":\"1F44E\"},{\"char\":\"✊\",\"name\":\"raised fist\",\"key\":\"270A\"},{\"char\":\"👊\",\"name\":\"oncoming fist\",\"key\":\"1F44A\"},{\"char\":\"🤛\",\"name\":\"left-facing fist\",\"key\":\"1F91B\"},{\"char\":\"🤜\",\"name\":\"right-facing fist\",\"key\":\"1F91C\"},{\"char\":\"👏\",\"name\":\"clapping hands\",\"key\":\"1F44F\"},{\"char\":\"🙌\",\"name\":\"raising hands\",\"key\":\"1F64C\"},{\"char\":\"🫶\",\"name\":\"heart hands\",\"key\":\"1FAF6\"},{\"char\":\"👐\",\"name\":\"open hands\",\"key\":\"1F450\"},{\"char\":\"🤲\",\"name\":\"palms up together\",\"key\":\"1F932\"},{\"char\":\"🤝\",\"name\":\"handshake\",\"key\":\"1F91D\"},{\"char\":\"🙏\",\"name\":\"folded hands\",\"key\":\"1F64F\"},{\"char\":\"✍️\",\"name\":\"writing hand\",\"key\":\"270D\"},{\"char\":\"💅\",\"name\":\"nail polish\",\"key\":\"1F485\"},{\"char\":\"🤳\",\"name\":\"selfie\",\"key\":\"1F933\"},{\"char\":\"💪\",\"name\":\"flexed biceps\",\"key\":\"1F4AA\"},{\"char\":\"🦾\",\"name\":\"mechanical arm\",\"key\":\"1F9BE\"},{\"char\":\"🦿\",\"name\":\"mechanical leg\",\"key\":\"1F9BF\"},{\"char\":\"🦵\",\"name\":\"leg\",\"key\":\"1F9B5\"},{\"char\":\"🦶\",\"name\":\"foot\",\"key\":\"1F9B6\"},{\"char\":\"👂\",\"name\":\"ear\",\"key\":\"1F442\"},{\"char\":\"🦻\",\"name\":\"ear with hearing aid\",\"key\":\"1F9BB\"},{\"char\":\"👃\",\"name\":\"nose\",\"key\":\"1F443\"},{\"char\":\"🧠\",\"name\":\"brain\",\"key\":\"1F9E0\"},{\"char\":\"🫀\",\"name\":\"anatomical heart\",\"key\":\"1FAC0\"},{\"char\":\"🫁\",\"name\":\"lungs\",\"key\":\"1FAC1\"},{\"char\":\"🦷\",\"name\":\"tooth\",\"key\":\"1F9B7\"},{\"char\":\"🦴\",\"name\":\"bone\",\"key\":\"1F9B4\"},{\"char\":\"👀\",\"name\":\"eyes\",\"key\":\"1F440\"},{\"char\":\"👁️\",\"name\":\"eye\",\"key\":\"1F441\"},{\"char\":\"👅\",\"name\":\"tongue\",\"key\":\"1F445\"},{\"char\":\"👄\",\"name\":\"mouth\",\"key\":\"1F444\"},{\"char\":\"🫦\",\"name\":\"biting lip\",\"key\":\"1FAE6\"},{\"char\":\"👶\",\"name\":\"baby\",\"key\":\"1F476\"},{\"char\":\"🧒\",\"name\":\"child\",\"key\":\"1F9D2\"},{\"char\":\"👦\",\"name\":\"boy\",\"key\":\"1F466\"},{\"char\":\"👧\",\"name\":\"girl\",\"key\":\"1F467\"},{\"char\":\"🧑\",\"name\":\"person\",\"key\":\"1F9D1\"},{\"char\":\"👱\",\"name\":\"person blond hair\",\"key\":\"1F471\"},{\"char\":\"👨\",\"name\":\"man\",\"key\":\"1F468\"},{\"char\":\"🧔\",\"name\":\"person beard\",\"key\":\"1F9D4\"},{\"char\":\"🧔‍♂️\",\"name\":\"man beard\",\"key\":\"1F9D4-200D-2642-FE0F\"},{\"char\":\"🧔‍♀️\",\"name\":\"woman beard\",\"key\":\"1F9D4-200D-2640-FE0F\"},{\"char\":\"👨‍🦰\",\"name\":\"man red hair\",\"key\":\"1F468-200D-1F9B0\"},{\"char\":\"👨‍🦱\",\"name\":\"man curly hair\",\"key\":\"1F468-200D-1F9B1\"},{\"char\":\"👨‍🦳\",\"name\":\"man white hair\",\"key\":\"1F468-200D-1F9B3\"},{\"char\":\"👨‍🦲\",\"name\":\"man bald\",\"key\":\"1F468-200D-1F9B2\"},{\"char\":\"👩\",\"name\":\"woman\",\"key\":\"1F469\"},{\"char\":\"👩‍🦰\",\"name\":\"woman red hair\",\"key\":\"1F469-200D-1F9B0\"},{\"char\":\"🧑‍🦰\",\"name\":\"person red hair\",\"key\":\"1F9D1-200D-1F9B0\"},{\"char\":\"👩‍🦱\",\"name\":\"woman curly hair\",\"key\":\"1F469-200D-1F9B1\"},{\"char\":\"🧑‍🦱\",\"name\":\"person curly hair\",\"key\":\"1F9D1-200D-1F9B1\"},{\"char\":\"👩‍🦳\",\"name\":\"woman white hair\",\"key\":\"1F469-200D-1F9B3\"},{\"char\":\"🧑‍🦳\",\"name\":\"person white hair\",\"key\":\"1F9D1-200D-1F9B3\"},{\"char\":\"👩‍🦲\",\"name\":\"woman bald\",\"key\":\"1F469-200D-1F9B2\"},{\"char\":\"🧑‍🦲\",\"name\":\"person bald\",\"key\":\"1F9D1-200D-1F9B2\"},{\"char\":\"👱‍♀️\",\"name\":\"woman blond hair\",\"key\":\"1F471-200D-2640-FE0F\"},{\"char\":\"👱‍♂️\",\"name\":\"man blond hair\",\"key\":\"1F471-200D-2642-FE0F\"},{\"char\":\"🧓\",\"name\":\"older person\",\"key\":\"1F9D3\"},{\"char\":\"👴\",\"name\":\"old man\",\"key\":\"1F474\"},{\"char\":\"👵\",\"name\":\"old woman\",\"key\":\"1F475\"},{\"char\":\"🙍\",\"name\":\"person frowning\",\"key\":\"1F64D\"},{\"char\":\"🙍‍♂️\",\"name\":\"man frowning\",\"key\":\"1F64D-200D-2642-FE0F\"},{\"char\":\"🙍‍♀️\",\"name\":\"woman frowning\",\"key\":\"1F64D-200D-2640-FE0F\"},{\"char\":\"🙎\",\"name\":\"person pouting\",\"key\":\"1F64E\"},{\"char\":\"🙎‍♂️\",\"name\":\"man pouting\",\"key\":\"1F64E-200D-2642-FE0F\"},{\"char\":\"🙎‍♀️\",\"name\":\"woman pouting\",\"key\":\"1F64E-200D-2640-FE0F\"},{\"char\":\"🙅\",\"name\":\"person gesturing NO\",\"key\":\"1F645\"},{\"char\":\"🙅‍♂️\",\"name\":\"man gesturing NO\",\"key\":\"1F645-200D-2642-FE0F\"},{\"char\":\"🙅‍♀️\",\"name\":\"woman gesturing NO\",\"key\":\"1F645-200D-2640-FE0F\"},{\"char\":\"🙆\",\"name\":\"person gesturing OK\",\"key\":\"1F646\"},{\"char\":\"🙆‍♂️\",\"name\":\"man gesturing OK\",\"key\":\"1F646-200D-2642-FE0F\"},{\"char\":\"🙆‍♀️\",\"name\":\"woman gesturing OK\",\"key\":\"1F646-200D-2640-FE0F\"},{\"char\":\"💁\",\"name\":\"person tipping hand\",\"key\":\"1F481\"},{\"char\":\"💁‍♂️\",\"name\":\"man tipping hand\",\"key\":\"1F481-200D-2642-FE0F\"},{\"char\":\"💁‍♀️\",\"name\":\"woman tipping hand\",\"key\":\"1F481-200D-2640-FE0F\"},{\"char\":\"🙋\",\"name\":\"person raising hand\",\"key\":\"1F64B\"},{\"char\":\"🙋‍♂️\",\"name\":\"man raising hand\",\"key\":\"1F64B-200D-2642-FE0F\"},{\"char\":\"🙋‍♀️\",\"name\":\"woman raising hand\",\"key\":\"1F64B-200D-2640-FE0F\"},{\"char\":\"🧏\",\"name\":\"deaf person\",\"key\":\"1F9CF\"},{\"char\":\"🧏‍♂️\",\"name\":\"deaf man\",\"key\":\"1F9CF-200D-2642-FE0F\"},{\"char\":\"🧏‍♀️\",\"name\":\"deaf woman\",\"key\":\"1F9CF-200D-2640-FE0F\"},{\"char\":\"🙇\",\"name\":\"person bowing\",\"key\":\"1F647\"},{\"char\":\"🙇‍♂️\",\"name\":\"man bowing\",\"key\":\"1F647-200D-2642-FE0F\"},{\"char\":\"🙇‍♀️\",\"name\":\"woman bowing\",\"key\":\"1F647-200D-2640-FE0F\"},{\"char\":\"🤦\",\"name\":\"person facepalming\",\"key\":\"1F926\"},{\"char\":\"🤦‍♂️\",\"name\":\"man facepalming\",\"key\":\"1F926-200D-2642-FE0F\"},{\"char\":\"🤦‍♀️\",\"name\":\"woman facepalming\",\"key\":\"1F926-200D-2640-FE0F\"},{\"char\":\"🤷\",\"name\":\"person shrugging\",\"key\":\"1F937\"},{\"char\":\"🤷‍♂️\",\"name\":\"man shrugging\",\"key\":\"1F937-200D-2642-FE0F\"},{\"char\":\"🤷‍♀️\",\"name\":\"woman shrugging\",\"key\":\"1F937-200D-2640-FE0F\"},{\"char\":\"🧑‍⚕️\",\"name\":\"health worker\",\"key\":\"1F9D1-200D-2695-FE0F\"},{\"char\":\"👨‍⚕️\",\"name\":\"man health worker\",\"key\":\"1F468-200D-2695-FE0F\"},{\"char\":\"👩‍⚕️\",\"name\":\"woman health worker\",\"key\":\"1F469-200D-2695-FE0F\"},{\"char\":\"🧑‍🎓\",\"name\":\"student\",\"key\":\"1F9D1-200D-1F393\"},{\"char\":\"👨‍🎓\",\"name\":\"man student\",\"key\":\"1F468-200D-1F393\"},{\"char\":\"👩‍🎓\",\"name\":\"woman student\",\"key\":\"1F469-200D-1F393\"},{\"char\":\"🧑‍🏫\",\"name\":\"teacher\",\"key\":\"1F9D1-200D-1F3EB\"},{\"char\":\"👨‍🏫\",\"name\":\"man teacher\",\"key\":\"1F468-200D-1F3EB\"},{\"char\":\"👩‍🏫\",\"name\":\"woman teacher\",\"key\":\"1F469-200D-1F3EB\"},{\"char\":\"🧑‍⚖️\",\"name\":\"judge\",\"key\":\"1F9D1-200D-2696-FE0F\"},{\"char\":\"👨‍⚖️\",\"name\":\"man judge\",\"key\":\"1F468-200D-2696-FE0F\"},{\"char\":\"👩‍⚖️\",\"name\":\"woman judge\",\"key\":\"1F469-200D-2696-FE0F\"},{\"char\":\"🧑‍🌾\",\"name\":\"farmer\",\"key\":\"1F9D1-200D-1F33E\"},{\"char\":\"👨‍🌾\",\"name\":\"man farmer\",\"key\":\"1F468-200D-1F33E\"},{\"char\":\"👩‍🌾\",\"name\":\"woman farmer\",\"key\":\"1F469-200D-1F33E\"},{\"char\":\"🧑‍🍳\",\"name\":\"cook\",\"key\":\"1F9D1-200D-1F373\"},{\"char\":\"👨‍🍳\",\"name\":\"man cook\",\"key\":\"1F468-200D-1F373\"},{\"char\":\"👩‍🍳\",\"name\":\"woman cook\",\"key\":\"1F469-200D-1F373\"},{\"char\":\"🧑‍🔧\",\"name\":\"mechanic\",\"key\":\"1F9D1-200D-1F527\"},{\"char\":\"👨‍🔧\",\"name\":\"man mechanic\",\"key\":\"1F468-200D-1F527\"},{\"char\":\"👩‍🔧\",\"name\":\"woman mechanic\",\"key\":\"1F469-200D-1F527\"},{\"char\":\"🧑‍🏭\",\"name\":\"factory worker\",\"key\":\"1F9D1-200D-1F3ED\"},{\"char\":\"👨‍🏭\",\"name\":\"man factory worker\",\"key\":\"1F468-200D-1F3ED\"},{\"char\":\"👩‍🏭\",\"name\":\"woman factory worker\",\"key\":\"1F469-200D-1F3ED\"},{\"char\":\"🧑‍💼\",\"name\":\"office worker\",\"key\":\"1F9D1-200D-1F4BC\"},{\"char\":\"👨‍💼\",\"name\":\"man office worker\",\"key\":\"1F468-200D-1F4BC\"},{\"char\":\"👩‍💼\",\"name\":\"woman office worker\",\"key\":\"1F469-200D-1F4BC\"},{\"char\":\"🧑‍🔬\",\"name\":\"scientist\",\"key\":\"1F9D1-200D-1F52C\"},{\"char\":\"👨‍🔬\",\"name\":\"man scientist\",\"key\":\"1F468-200D-1F52C\"},{\"char\":\"👩‍🔬\",\"name\":\"woman scientist\",\"key\":\"1F469-200D-1F52C\"},{\"char\":\"🧑‍💻\",\"name\":\"technologist\",\"key\":\"1F9D1-200D-1F4BB\"},{\"char\":\"👨‍💻\",\"name\":\"man technologist\",\"key\":\"1F468-200D-1F4BB\"},{\"char\":\"👩‍💻\",\"name\":\"woman technologist\",\"key\":\"1F469-200D-1F4BB\"},{\"char\":\"🧑‍🎤\",\"name\":\"singer\",\"key\":\"1F9D1-200D-1F3A4\"},{\"char\":\"👨‍🎤\",\"name\":\"man singer\",\"key\":\"1F468-200D-1F3A4\"},{\"char\":\"👩‍🎤\",\"name\":\"woman singer\",\"key\":\"1F469-200D-1F3A4\"},{\"char\":\"🧑‍🎨\",\"name\":\"artist\",\"key\":\"1F9D1-200D-1F3A8\"},{\"char\":\"👨‍🎨\",\"name\":\"man artist\",\"key\":\"1F468-200D-1F3A8\"},{\"char\":\"👩‍🎨\",\"name\":\"woman artist\",\"key\":\"1F469-200D-1F3A8\"},{\"char\":\"🧑‍✈️\",\"name\":\"pilot\",\"key\":\"1F9D1-200D-2708-FE0F\"},{\"char\":\"👨‍✈️\",\"name\":\"man pilot\",\"key\":\"1F468-200D-2708-FE0F\"},{\"char\":\"👩‍✈️\",\"name\":\"woman pilot\",\"key\":\"1F469-200D-2708-FE0F\"},{\"char\":\"🧑‍🚀\",\"name\":\"astronaut\",\"key\":\"1F9D1-200D-1F680\"},{\"char\":\"👨‍🚀\",\"name\":\"man astronaut\",\"key\":\"1F468-200D-1F680\"},{\"char\":\"👩‍🚀\",\"name\":\"woman astronaut\",\"key\":\"1F469-200D-1F680\"},{\"char\":\"🧑‍🚒\",\"name\":\"firefighter\",\"key\":\"1F9D1-200D-1F692\"},{\"char\":\"👨‍🚒\",\"name\":\"man firefighter\",\"key\":\"1F468-200D-1F692\"},{\"char\":\"👩‍🚒\",\"name\":\"woman firefighter\",\"key\":\"1F469-200D-1F692\"},{\"char\":\"👮\",\"name\":\"police officer\",\"key\":\"1F46E\"},{\"char\":\"👮‍♂️\",\"name\":\"man police officer\",\"key\":\"1F46E-200D-2642-FE0F\"},{\"char\":\"👮‍♀️\",\"name\":\"woman police officer\",\"key\":\"1F46E-200D-2640-FE0F\"},{\"char\":\"🕵️\",\"name\":\"detective\",\"key\":\"1F575\"},{\"char\":\"🕵️‍♂️\",\"name\":\"man detective\",\"key\":\"1F575-FE0F-200D-2642-FE0F\"},{\"char\":\"🕵️‍♀️\",\"name\":\"woman detective\",\"key\":\"1F575-FE0F-200D-2640-FE0F\"},{\"char\":\"💂\",\"name\":\"guard\",\"key\":\"1F482\"},{\"char\":\"💂‍♂️\",\"name\":\"man guard\",\"key\":\"1F482-200D-2642-FE0F\"},{\"char\":\"💂‍♀️\",\"name\":\"woman guard\",\"key\":\"1F482-200D-2640-FE0F\"},{\"char\":\"🥷\",\"name\":\"ninja\",\"key\":\"1F977\"},{\"char\":\"👷\",\"name\":\"construction worker\",\"key\":\"1F477\"},{\"char\":\"👷‍♂️\",\"name\":\"man construction worker\",\"key\":\"1F477-200D-2642-FE0F\"},{\"char\":\"👷‍♀️\",\"name\":\"woman construction worker\",\"key\":\"1F477-200D-2640-FE0F\"},{\"char\":\"🫅\",\"name\":\"person with crown\",\"key\":\"1FAC5\"},{\"char\":\"🤴\",\"name\":\"prince\",\"key\":\"1F934\"},{\"char\":\"👸\",\"name\":\"princess\",\"key\":\"1F478\"},{\"char\":\"👳\",\"name\":\"person wearing turban\",\"key\":\"1F473\"},{\"char\":\"👳‍♂️\",\"name\":\"man wearing turban\",\"key\":\"1F473-200D-2642-FE0F\"},{\"char\":\"👳‍♀️\",\"name\":\"woman wearing turban\",\"key\":\"1F473-200D-2640-FE0F\"},{\"char\":\"👲\",\"name\":\"person with skullcap\",\"key\":\"1F472\"},{\"char\":\"🧕\",\"name\":\"woman with headscarf\",\"key\":\"1F9D5\"},{\"char\":\"🤵\",\"name\":\"person in tuxedo\",\"key\":\"1F935\"},{\"char\":\"🤵‍♂️\",\"name\":\"man in tuxedo\",\"key\":\"1F935-200D-2642-FE0F\"},{\"char\":\"🤵‍♀️\",\"name\":\"woman in tuxedo\",\"key\":\"1F935-200D-2640-FE0F\"},{\"char\":\"👰\",\"name\":\"person with veil\",\"key\":\"1F470\"},{\"char\":\"👰‍♂️\",\"name\":\"man with veil\",\"key\":\"1F470-200D-2642-FE0F\"},{\"char\":\"👰‍♀️\",\"name\":\"woman with veil\",\"key\":\"1F470-200D-2640-FE0F\"},{\"char\":\"🤰\",\"name\":\"pregnant woman\",\"key\":\"1F930\"},{\"char\":\"🫃\",\"name\":\"pregnant man\",\"key\":\"1FAC3\"},{\"char\":\"🫄\",\"name\":\"pregnant person\",\"key\":\"1FAC4\"},{\"char\":\"🤱\",\"name\":\"breast-feeding\",\"key\":\"1F931\"},{\"char\":\"👩‍🍼\",\"name\":\"woman feeding baby\",\"key\":\"1F469-200D-1F37C\"},{\"char\":\"👨‍🍼\",\"name\":\"man feeding baby\",\"key\":\"1F468-200D-1F37C\"},{\"char\":\"🧑‍🍼\",\"name\":\"person feeding baby\",\"key\":\"1F9D1-200D-1F37C\"},{\"char\":\"👼\",\"name\":\"baby angel\",\"key\":\"1F47C\"},{\"char\":\"🎅\",\"name\":\"Santa Claus\",\"key\":\"1F385\"},{\"char\":\"🤶\",\"name\":\"Mrs. Claus\",\"key\":\"1F936\"},{\"char\":\"🧑‍🎄\",\"name\":\"Mx Claus\",\"key\":\"1F9D1-200D-1F384\"},{\"char\":\"🦸\",\"name\":\"superhero\",\"key\":\"1F9B8\"},{\"char\":\"🦸‍♂️\",\"name\":\"man superhero\",\"key\":\"1F9B8-200D-2642-FE0F\"},{\"char\":\"🦸‍♀️\",\"name\":\"woman superhero\",\"key\":\"1F9B8-200D-2640-FE0F\"},{\"char\":\"🦹\",\"name\":\"supervillain\",\"key\":\"1F9B9\"},{\"char\":\"🦹‍♂️\",\"name\":\"man supervillain\",\"key\":\"1F9B9-200D-2642-FE0F\"},{\"char\":\"🦹‍♀️\",\"name\":\"woman supervillain\",\"key\":\"1F9B9-200D-2640-FE0F\"},{\"char\":\"🧙\",\"name\":\"mage\",\"key\":\"1F9D9\"},{\"char\":\"🧙‍♂️\",\"name\":\"man mage\",\"key\":\"1F9D9-200D-2642-FE0F\"},{\"char\":\"🧙‍♀️\",\"name\":\"woman mage\",\"key\":\"1F9D9-200D-2640-FE0F\"},{\"char\":\"🧚\",\"name\":\"fairy\",\"key\":\"1F9DA\"},{\"char\":\"🧚‍♂️\",\"name\":\"man fairy\",\"key\":\"1F9DA-200D-2642-FE0F\"},{\"char\":\"🧚‍♀️\",\"name\":\"woman fairy\",\"key\":\"1F9DA-200D-2640-FE0F\"},{\"char\":\"🧛\",\"name\":\"vampire\",\"key\":\"1F9DB\"},{\"char\":\"🧛‍♂️\",\"name\":\"man vampire\",\"key\":\"1F9DB-200D-2642-FE0F\"},{\"char\":\"🧛‍♀️\",\"name\":\"woman vampire\",\"key\":\"1F9DB-200D-2640-FE0F\"},{\"char\":\"🧜\",\"name\":\"merperson\",\"key\":\"1F9DC\"},{\"char\":\"🧜‍♂️\",\"name\":\"merman\",\"key\":\"1F9DC-200D-2642-FE0F\"},{\"char\":\"🧜‍♀️\",\"name\":\"mermaid\",\"key\":\"1F9DC-200D-2640-FE0F\"},{\"char\":\"🧝\",\"name\":\"elf\",\"key\":\"1F9DD\"},{\"char\":\"🧝‍♂️\",\"name\":\"man elf\",\"key\":\"1F9DD-200D-2642-FE0F\"},{\"char\":\"🧝‍♀️\",\"name\":\"woman elf\",\"key\":\"1F9DD-200D-2640-FE0F\"},{\"char\":\"🧞\",\"name\":\"genie\",\"key\":\"1F9DE\"},{\"char\":\"🧞‍♂️\",\"name\":\"man genie\",\"key\":\"1F9DE-200D-2642-FE0F\"},{\"char\":\"🧞‍♀️\",\"name\":\"woman genie\",\"key\":\"1F9DE-200D-2640-FE0F\"},{\"char\":\"🧟\",\"name\":\"zombie\",\"key\":\"1F9DF\"},{\"char\":\"🧟‍♂️\",\"name\":\"man zombie\",\"key\":\"1F9DF-200D-2642-FE0F\"},{\"char\":\"🧟‍♀️\",\"name\":\"woman zombie\",\"key\":\"1F9DF-200D-2640-FE0F\"},{\"char\":\"🧌\",\"name\":\"troll\",\"key\":\"1F9CC\"},{\"char\":\"🫈\",\"name\":\"hairy creature\",\"key\":\"1FAC8\"},{\"char\":\"💆\",\"name\":\"person getting massage\",\"key\":\"1F486\"},{\"char\":\"💆‍♂️\",\"name\":\"man getting massage\",\"key\":\"1F486-200D-2642-FE0F\"},{\"char\":\"💆‍♀️\",\"name\":\"woman getting massage\",\"key\":\"1F486-200D-2640-FE0F\"},{\"char\":\"💇\",\"name\":\"person getting haircut\",\"key\":\"1F487\"},{\"char\":\"💇‍♂️\",\"name\":\"man getting haircut\",\"key\":\"1F487-200D-2642-FE0F\"},{\"char\":\"💇‍♀️\",\"name\":\"woman getting haircut\",\"key\":\"1F487-200D-2640-FE0F\"},{\"char\":\"🚶\",\"name\":\"person walking\",\"key\":\"1F6B6\"},{\"char\":\"🚶‍♂️\",\"name\":\"man walking\",\"key\":\"1F6B6-200D-2642-FE0F\"},{\"char\":\"🚶‍♀️\",\"name\":\"woman walking\",\"key\":\"1F6B6-200D-2640-FE0F\"},{\"char\":\"🚶‍➡️\",\"name\":\"person walking facing right\",\"key\":\"1F6B6-200D-27A1-FE0F\"},{\"char\":\"🚶‍♀️‍➡️\",\"name\":\"woman walking facing right\",\"key\":\"1F6B6-200D-2640-FE0F-200D-27A1-FE0F\"},{\"char\":\"🚶‍♂️‍➡️\",\"name\":\"man walking facing right\",\"key\":\"1F6B6-200D-2642-FE0F-200D-27A1-FE0F\"},{\"char\":\"🧍\",\"name\":\"person standing\",\"key\":\"1F9CD\"},{\"char\":\"🧍‍♂️\",\"name\":\"man standing\",\"key\":\"1F9CD-200D-2642-FE0F\"},{\"char\":\"🧍‍♀️\",\"name\":\"woman standing\",\"key\":\"1F9CD-200D-2640-FE0F\"},{\"char\":\"🧎\",\"name\":\"person kneeling\",\"key\":\"1F9CE\"},{\"char\":\"🧎‍♂️\",\"name\":\"man kneeling\",\"key\":\"1F9CE-200D-2642-FE0F\"},{\"char\":\"🧎‍♀️\",\"name\":\"woman kneeling\",\"key\":\"1F9CE-200D-2640-FE0F\"},{\"char\":\"🧎‍➡️\",\"name\":\"person kneeling facing right\",\"key\":\"1F9CE-200D-27A1-FE0F\"},{\"char\":\"🧎‍♀️‍➡️\",\"name\":\"woman kneeling facing right\",\"key\":\"1F9CE-200D-2640-FE0F-200D-27A1-FE0F\"},{\"char\":\"🧎‍♂️‍➡️\",\"name\":\"man kneeling facing right\",\"key\":\"1F9CE-200D-2642-FE0F-200D-27A1-FE0F\"},{\"char\":\"🧑‍🦯\",\"name\":\"person with white cane\",\"key\":\"1F9D1-200D-1F9AF\"},{\"char\":\"🧑‍🦯‍➡️\",\"name\":\"person with white cane facing right\",\"key\":\"1F9D1-200D-1F9AF-200D-27A1-FE0F\"},{\"char\":\"👨‍🦯\",\"name\":\"man with white cane\",\"key\":\"1F468-200D-1F9AF\"},{\"char\":\"👨‍🦯‍➡️\",\"name\":\"man with white cane facing right\",\"key\":\"1F468-200D-1F9AF-200D-27A1-FE0F\"},{\"char\":\"👩‍🦯\",\"name\":\"woman with white cane\",\"key\":\"1F469-200D-1F9AF\"},{\"char\":\"👩‍🦯‍➡️\",\"name\":\"woman with white cane facing right\",\"key\":\"1F469-200D-1F9AF-200D-27A1-FE0F\"},{\"char\":\"🧑‍🦼\",\"name\":\"person in motorized wheelchair\",\"key\":\"1F9D1-200D-1F9BC\"},{\"char\":\"🧑‍🦼‍➡️\",\"name\":\"person in motorized wheelchair facing right\",\"key\":\"1F9D1-200D-1F9BC-200D-27A1-FE0F\"},{\"char\":\"👨‍🦼\",\"name\":\"man in motorized wheelchair\",\"key\":\"1F468-200D-1F9BC\"},{\"char\":\"👨‍🦼‍➡️\",\"name\":\"man in motorized wheelchair facing right\",\"key\":\"1F468-200D-1F9BC-200D-27A1-FE0F\"},{\"char\":\"👩‍🦼\",\"name\":\"woman in motorized wheelchair\",\"key\":\"1F469-200D-1F9BC\"},{\"char\":\"👩‍🦼‍➡️\",\"name\":\"woman in motorized wheelchair facing right\",\"key\":\"1F469-200D-1F9BC-200D-27A1-FE0F\"},{\"char\":\"🧑‍🦽\",\"name\":\"person in manual wheelchair\",\"key\":\"1F9D1-200D-1F9BD\"},{\"char\":\"🧑‍🦽‍➡️\",\"name\":\"person in manual wheelchair facing right\",\"key\":\"1F9D1-200D-1F9BD-200D-27A1-FE0F\"},{\"char\":\"👨‍🦽\",\"name\":\"man in manual wheelchair\",\"key\":\"1F468-200D-1F9BD\"},{\"char\":\"👨‍🦽‍➡️\",\"name\":\"man in manual wheelchair facing right\",\"key\":\"1F468-200D-1F9BD-200D-27A1-FE0F\"},{\"char\":\"👩‍🦽\",\"name\":\"woman in manual wheelchair\",\"key\":\"1F469-200D-1F9BD\"},{\"char\":\"👩‍🦽‍➡️\",\"name\":\"woman in manual wheelchair facing right\",\"key\":\"1F469-200D-1F9BD-200D-27A1-FE0F\"},{\"char\":\"🏃\",\"name\":\"person running\",\"key\":\"1F3C3\"},{\"char\":\"🏃‍♂️\",\"name\":\"man running\",\"key\":\"1F3C3-200D-2642-FE0F\"},{\"char\":\"🏃‍♀️\",\"name\":\"woman running\",\"key\":\"1F3C3-200D-2640-FE0F\"},{\"char\":\"🏃‍➡️\",\"name\":\"person running facing right\",\"key\":\"1F3C3-200D-27A1-FE0F\"},{\"char\":\"🏃‍♀️‍➡️\",\"name\":\"woman running facing right\",\"key\":\"1F3C3-200D-2640-FE0F-200D-27A1-FE0F\"},{\"char\":\"🏃‍♂️‍➡️\",\"name\":\"man running facing right\",\"key\":\"1F3C3-200D-2642-FE0F-200D-27A1-FE0F\"},{\"char\":\"🧑‍🩰\",\"name\":\"ballet dancer\",\"key\":\"1F9D1-200D-1FA70\"},{\"char\":\"💃\",\"name\":\"woman dancing\",\"key\":\"1F483\"},{\"char\":\"🕺\",\"name\":\"man dancing\",\"key\":\"1F57A\"},{\"char\":\"🕴️\",\"name\":\"person in suit levitating\",\"key\":\"1F574\"},{\"char\":\"👯\",\"name\":\"people with bunny ears\",\"key\":\"1F46F\"},{\"char\":\"👯‍♂️\",\"name\":\"men with bunny ears\",\"key\":\"1F46F-200D-2642-FE0F\"},{\"char\":\"👯‍♀️\",\"name\":\"women with bunny ears\",\"key\":\"1F46F-200D-2640-FE0F\"},{\"char\":\"🧖\",\"name\":\"person in steamy room\",\"key\":\"1F9D6\"},{\"char\":\"🧖‍♂️\",\"name\":\"man in steamy room\",\"key\":\"1F9D6-200D-2642-FE0F\"},{\"char\":\"🧖‍♀️\",\"name\":\"woman in steamy room\",\"key\":\"1F9D6-200D-2640-FE0F\"},{\"char\":\"🧗\",\"name\":\"person climbing\",\"key\":\"1F9D7\"},{\"char\":\"🧗‍♂️\",\"name\":\"man climbing\",\"key\":\"1F9D7-200D-2642-FE0F\"},{\"char\":\"🧗‍♀️\",\"name\":\"woman climbing\",\"key\":\"1F9D7-200D-2640-FE0F\"},{\"char\":\"🤺\",\"name\":\"person fencing\",\"key\":\"1F93A\"},{\"char\":\"🏇\",\"name\":\"horse racing\",\"key\":\"1F3C7\"},{\"char\":\"⛷️\",\"name\":\"skier\",\"key\":\"26F7\"},{\"char\":\"🏂\",\"name\":\"snowboarder\",\"key\":\"1F3C2\"},{\"char\":\"🏌️\",\"name\":\"person golfing\",\"key\":\"1F3CC\"},{\"char\":\"🏌️‍♂️\",\"name\":\"man golfing\",\"key\":\"1F3CC-FE0F-200D-2642-FE0F\"},{\"char\":\"🏌️‍♀️\",\"name\":\"woman golfing\",\"key\":\"1F3CC-FE0F-200D-2640-FE0F\"},{\"char\":\"🏄\",\"name\":\"person surfing\",\"key\":\"1F3C4\"},{\"char\":\"🏄‍♂️\",\"name\":\"man surfing\",\"key\":\"1F3C4-200D-2642-FE0F\"},{\"char\":\"🏄‍♀️\",\"name\":\"woman surfing\",\"key\":\"1F3C4-200D-2640-FE0F\"},{\"char\":\"🚣\",\"name\":\"person rowing boat\",\"key\":\"1F6A3\"},{\"char\":\"🚣‍♂️\",\"name\":\"man rowing boat\",\"key\":\"1F6A3-200D-2642-FE0F\"},{\"char\":\"🚣‍♀️\",\"name\":\"woman rowing boat\",\"key\":\"1F6A3-200D-2640-FE0F\"},{\"char\":\"🏊\",\"name\":\"person swimming\",\"key\":\"1F3CA\"},{\"char\":\"🏊‍♂️\",\"name\":\"man swimming\",\"key\":\"1F3CA-200D-2642-FE0F\"},{\"char\":\"🏊‍♀️\",\"name\":\"woman swimming\",\"key\":\"1F3CA-200D-2640-FE0F\"},{\"char\":\"⛹️\",\"name\":\"person bouncing ball\",\"key\":\"26F9\"},{\"char\":\"⛹️‍♂️\",\"name\":\"man bouncing ball\",\"key\":\"26F9-FE0F-200D-2642-FE0F\"},{\"char\":\"⛹️‍♀️\",\"name\":\"woman bouncing ball\",\"key\":\"26F9-FE0F-200D-2640-FE0F\"},{\"char\":\"🏋️\",\"name\":\"person lifting weights\",\"key\":\"1F3CB\"},{\"char\":\"🏋️‍♂️\",\"name\":\"man lifting weights\",\"key\":\"1F3CB-FE0F-200D-2642-FE0F\"},{\"char\":\"🏋️‍♀️\",\"name\":\"woman lifting weights\",\"key\":\"1F3CB-FE0F-200D-2640-FE0F\"},{\"char\":\"🚴\",\"name\":\"person biking\",\"key\":\"1F6B4\"},{\"char\":\"🚴‍♂️\",\"name\":\"man biking\",\"key\":\"1F6B4-200D-2642-FE0F\"},{\"char\":\"🚴‍♀️\",\"name\":\"woman biking\",\"key\":\"1F6B4-200D-2640-FE0F\"},{\"char\":\"🚵\",\"name\":\"person mountain biking\",\"key\":\"1F6B5\"},{\"char\":\"🚵‍♂️\",\"name\":\"man mountain biking\",\"key\":\"1F6B5-200D-2642-FE0F\"},{\"char\":\"🚵‍♀️\",\"name\":\"woman mountain biking\",\"key\":\"1F6B5-200D-2640-FE0F\"},{\"char\":\"🤸\",\"name\":\"person cartwheeling\",\"key\":\"1F938\"},{\"char\":\"🤸‍♂️\",\"name\":\"man cartwheeling\",\"key\":\"1F938-200D-2642-FE0F\"},{\"char\":\"🤸‍♀️\",\"name\":\"woman cartwheeling\",\"key\":\"1F938-200D-2640-FE0F\"},{\"char\":\"🤼\",\"name\":\"people wrestling\",\"key\":\"1F93C\"},{\"char\":\"🤼‍♂️\",\"name\":\"men wrestling\",\"key\":\"1F93C-200D-2642-FE0F\"},{\"char\":\"🤼‍♀️\",\"name\":\"women wrestling\",\"key\":\"1F93C-200D-2640-FE0F\"},{\"char\":\"🤽\",\"name\":\"person playing water polo\",\"key\":\"1F93D\"},{\"char\":\"🤽‍♂️\",\"name\":\"man playing water polo\",\"key\":\"1F93D-200D-2642-FE0F\"},{\"char\":\"🤽‍♀️\",\"name\":\"woman playing water polo\",\"key\":\"1F93D-200D-2640-FE0F\"},{\"char\":\"🤾\",\"name\":\"person playing handball\",\"key\":\"1F93E\"},{\"char\":\"🤾‍♂️\",\"name\":\"man playing handball\",\"key\":\"1F93E-200D-2642-FE0F\"},{\"char\":\"🤾‍♀️\",\"name\":\"woman playing handball\",\"key\":\"1F93E-200D-2640-FE0F\"},{\"char\":\"🤹\",\"name\":\"person juggling\",\"key\":\"1F939\"},{\"char\":\"🤹‍♂️\",\"name\":\"man juggling\",\"key\":\"1F939-200D-2642-FE0F\"},{\"char\":\"🤹‍♀️\",\"name\":\"woman juggling\",\"key\":\"1F939-200D-2640-FE0F\"},{\"char\":\"🧘\",\"name\":\"person in lotus position\",\"key\":\"1F9D8\"},{\"char\":\"🧘‍♂️\",\"name\":\"man in lotus position\",\"key\":\"1F9D8-200D-2642-FE0F\"},{\"char\":\"🧘‍♀️\",\"name\":\"woman in lotus position\",\"key\":\"1F9D8-200D-2640-FE0F\"},{\"char\":\"🛀\",\"name\":\"person taking bath\",\"key\":\"1F6C0\"},{\"char\":\"🛌\",\"name\":\"person in bed\",\"key\":\"1F6CC\"},{\"char\":\"🧑‍🤝‍🧑\",\"name\":\"people holding hands\",\"key\":\"1F9D1-200D-1F91D-200D-1F9D1\"},{\"char\":\"👭\",\"name\":\"women holding hands\",\"key\":\"1F46D\"},{\"char\":\"👫\",\"name\":\"woman and man holding hands\",\"key\":\"1F46B\"},{\"char\":\"👬\",\"name\":\"men holding hands\",\"key\":\"1F46C\"},{\"char\":\"💏\",\"name\":\"kiss\",\"key\":\"1F48F\"},{\"char\":\"👩‍❤️‍💋‍👨\",\"name\":\"kiss woman, man\",\"key\":\"1F469-200D-2764-FE0F-200D-1F48B-200D-1F468\"},{\"char\":\"👨‍❤️‍💋‍👨\",\"name\":\"kiss man, man\",\"key\":\"1F468-200D-2764-FE0F-200D-1F48B-200D-1F468\"},{\"char\":\"👩‍❤️‍💋‍👩\",\"name\":\"kiss woman, woman\",\"key\":\"1F469-200D-2764-FE0F-200D-1F48B-200D-1F469\"},{\"char\":\"💑\",\"name\":\"couple with heart\",\"key\":\"1F491\"},{\"char\":\"👩‍❤️‍👨\",\"name\":\"couple with heart woman, man\",\"key\":\"1F469-200D-2764-FE0F-200D-1F468\"},{\"char\":\"👨‍❤️‍👨\",\"name\":\"couple with heart man, man\",\"key\":\"1F468-200D-2764-FE0F-200D-1F468\"},{\"char\":\"👩‍❤️‍👩\",\"name\":\"couple with heart woman, woman\",\"key\":\"1F469-200D-2764-FE0F-200D-1F469\"},{\"char\":\"👨‍👩‍👦\",\"name\":\"family man, woman, boy\",\"key\":\"1F468-200D-1F469-200D-1F466\"},{\"char\":\"👨‍👩‍👧\",\"name\":\"family man, woman, girl\",\"key\":\"1F468-200D-1F469-200D-1F467\"},{\"char\":\"👨‍👩‍👧‍👦\",\"name\":\"family man, woman, girl, boy\",\"key\":\"1F468-200D-1F469-200D-1F467-200D-1F466\"},{\"char\":\"👨‍👩‍👦‍👦\",\"name\":\"family man, woman, boy, boy\",\"key\":\"1F468-200D-1F469-200D-1F466-200D-1F466\"},{\"char\":\"👨‍👩‍👧‍👧\",\"name\":\"family man, woman, girl, girl\",\"key\":\"1F468-200D-1F469-200D-1F467-200D-1F467\"},{\"char\":\"👨‍👨‍👦\",\"name\":\"family man, man, boy\",\"key\":\"1F468-200D-1F468-200D-1F466\"},{\"char\":\"👨‍👨‍👧\",\"name\":\"family man, man, girl\",\"key\":\"1F468-200D-1F468-200D-1F467\"},{\"char\":\"👨‍👨‍👧‍👦\",\"name\":\"family man, man, girl, boy\",\"key\":\"1F468-200D-1F468-200D-1F467-200D-1F466\"},{\"char\":\"👨‍👨‍👦‍👦\",\"name\":\"family man, man, boy, boy\",\"key\":\"1F468-200D-1F468-200D-1F466-200D-1F466\"},{\"char\":\"👨‍👨‍👧‍👧\",\"name\":\"family man, man, girl, girl\",\"key\":\"1F468-200D-1F468-200D-1F467-200D-1F467\"},{\"char\":\"👩‍👩‍👦\",\"name\":\"family woman, woman, boy\",\"key\":\"1F469-200D-1F469-200D-1F466\"},{\"char\":\"👩‍👩‍👧\",\"name\":\"family woman, woman, girl\",\"key\":\"1F469-200D-1F469-200D-1F467\"},{\"char\":\"👩‍👩‍👧‍👦\",\"name\":\"family woman, woman, girl, boy\",\"key\":\"1F469-200D-1F469-200D-1F467-200D-1F466\"},{\"char\":\"👩‍👩‍👦‍👦\",\"name\":\"family woman, woman, boy, boy\",\"key\":\"1F469-200D-1F469-200D-1F466-200D-1F466\"},{\"char\":\"👩‍👩‍👧‍👧\",\"name\":\"family woman, woman, girl, girl\",\"key\":\"1F469-200D-1F469-200D-1F467-200D-1F467\"},{\"char\":\"👨‍👦\",\"name\":\"family man, boy\",\"key\":\"1F468-200D-1F466\"},{\"char\":\"👨‍👦‍👦\",\"name\":\"family man, boy, boy\",\"key\":\"1F468-200D-1F466-200D-1F466\"},{\"char\":\"👨‍👧\",\"name\":\"family man, girl\",\"key\":\"1F468-200D-1F467\"},{\"char\":\"👨‍👧‍👦\",\"name\":\"family man, girl, boy\",\"key\":\"1F468-200D-1F467-200D-1F466\"},{\"char\":\"👨‍👧‍👧\",\"name\":\"family man, girl, girl\",\"key\":\"1F468-200D-1F467-200D-1F467\"},{\"char\":\"👩‍👦\",\"name\":\"family woman, boy\",\"key\":\"1F469-200D-1F466\"},{\"char\":\"👩‍👦‍👦\",\"name\":\"family woman, boy, boy\",\"key\":\"1F469-200D-1F466-200D-1F466\"},{\"char\":\"👩‍👧\",\"name\":\"family woman, girl\",\"key\":\"1F469-200D-1F467\"},{\"char\":\"👩‍👧‍👦\",\"name\":\"family woman, girl, boy\",\"key\":\"1F469-200D-1F467-200D-1F466\"},{\"char\":\"👩‍👧‍👧\",\"name\":\"family woman, girl, girl\",\"key\":\"1F469-200D-1F467-200D-1F467\"},{\"char\":\"🗣️\",\"name\":\"speaking head\",\"key\":\"1F5E3\"},{\"char\":\"👤\",\"name\":\"bust in silhouette\",\"key\":\"1F464\"},{\"char\":\"👥\",\"name\":\"busts in silhouette\",\"key\":\"1F465\"},{\"char\":\"🫂\",\"name\":\"people hugging\",\"key\":\"1FAC2\"},{\"char\":\"👪\",\"name\":\"family\",\"key\":\"1F46A\"},{\"char\":\"🧑‍🧑‍🧒\",\"name\":\"family adult, adult, child\",\"key\":\"1F9D1-200D-1F9D1-200D-1F9D2\"},{\"char\":\"🧑‍🧑‍🧒‍🧒\",\"name\":\"family adult, adult, child, child\",\"key\":\"1F9D1-200D-1F9D1-200D-1F9D2-200D-1F9D2\"},{\"char\":\"🧑‍🧒\",\"name\":\"family adult, child\",\"key\":\"1F9D1-200D-1F9D2\"},{\"char\":\"🧑‍🧒‍🧒\",\"name\":\"family adult, child, child\",\"key\":\"1F9D1-200D-1F9D2-200D-1F9D2\"},{\"char\":\"👣\",\"name\":\"footprints\",\"key\":\"1F463\"},{\"char\":\"🫆\",\"name\":\"fingerprint\",\"key\":\"1FAC6\"}]},\n {\"id\":\"animals_nature\",\"label\":\"Animals & Nature\",\"emojis\":[{\"char\":\"🐵\",\"name\":\"monkey face\",\"key\":\"1F435\"},{\"char\":\"🐒\",\"name\":\"monkey\",\"key\":\"1F412\"},{\"char\":\"🦍\",\"name\":\"gorilla\",\"key\":\"1F98D\"},{\"char\":\"🦧\",\"name\":\"orangutan\",\"key\":\"1F9A7\"},{\"char\":\"🐶\",\"name\":\"dog face\",\"key\":\"1F436\"},{\"char\":\"🐕\",\"name\":\"dog\",\"key\":\"1F415\"},{\"char\":\"🦮\",\"name\":\"guide dog\",\"key\":\"1F9AE\"},{\"char\":\"🐕‍🦺\",\"name\":\"service dog\",\"key\":\"1F415-200D-1F9BA\"},{\"char\":\"🐩\",\"name\":\"poodle\",\"key\":\"1F429\"},{\"char\":\"🐺\",\"name\":\"wolf\",\"key\":\"1F43A\"},{\"char\":\"🦊\",\"name\":\"fox\",\"key\":\"1F98A\"},{\"char\":\"🦝\",\"name\":\"raccoon\",\"key\":\"1F99D\"},{\"char\":\"🐱\",\"name\":\"cat face\",\"key\":\"1F431\"},{\"char\":\"🐈\",\"name\":\"cat\",\"key\":\"1F408\"},{\"char\":\"🐈‍⬛\",\"name\":\"black cat\",\"key\":\"1F408-200D-2B1B\"},{\"char\":\"🦁\",\"name\":\"lion\",\"key\":\"1F981\"},{\"char\":\"🐯\",\"name\":\"tiger face\",\"key\":\"1F42F\"},{\"char\":\"🐅\",\"name\":\"tiger\",\"key\":\"1F405\"},{\"char\":\"🐆\",\"name\":\"leopard\",\"key\":\"1F406\"},{\"char\":\"🐴\",\"name\":\"horse face\",\"key\":\"1F434\"},{\"char\":\"🫎\",\"name\":\"moose\",\"key\":\"1FACE\"},{\"char\":\"🫏\",\"name\":\"donkey\",\"key\":\"1FACF\"},{\"char\":\"🐎\",\"name\":\"horse\",\"key\":\"1F40E\"},{\"char\":\"🦄\",\"name\":\"unicorn\",\"key\":\"1F984\"},{\"char\":\"🦓\",\"name\":\"zebra\",\"key\":\"1F993\"},{\"char\":\"🦌\",\"name\":\"deer\",\"key\":\"1F98C\"},{\"char\":\"🦬\",\"name\":\"bison\",\"key\":\"1F9AC\"},{\"char\":\"🐮\",\"name\":\"cow face\",\"key\":\"1F42E\"},{\"char\":\"🐂\",\"name\":\"ox\",\"key\":\"1F402\"},{\"char\":\"🐃\",\"name\":\"water buffalo\",\"key\":\"1F403\"},{\"char\":\"🐄\",\"name\":\"cow\",\"key\":\"1F404\"},{\"char\":\"🐷\",\"name\":\"pig face\",\"key\":\"1F437\"},{\"char\":\"🐖\",\"name\":\"pig\",\"key\":\"1F416\"},{\"char\":\"🐗\",\"name\":\"boar\",\"key\":\"1F417\"},{\"char\":\"🐽\",\"name\":\"pig nose\",\"key\":\"1F43D\"},{\"char\":\"🐏\",\"name\":\"ram\",\"key\":\"1F40F\"},{\"char\":\"🐑\",\"name\":\"ewe\",\"key\":\"1F411\"},{\"char\":\"🐐\",\"name\":\"goat\",\"key\":\"1F410\"},{\"char\":\"🐪\",\"name\":\"camel\",\"key\":\"1F42A\"},{\"char\":\"🐫\",\"name\":\"two-hump camel\",\"key\":\"1F42B\"},{\"char\":\"🦙\",\"name\":\"llama\",\"key\":\"1F999\"},{\"char\":\"🦒\",\"name\":\"giraffe\",\"key\":\"1F992\"},{\"char\":\"🐘\",\"name\":\"elephant\",\"key\":\"1F418\"},{\"char\":\"🦣\",\"name\":\"mammoth\",\"key\":\"1F9A3\"},{\"char\":\"🦏\",\"name\":\"rhinoceros\",\"key\":\"1F98F\"},{\"char\":\"🦛\",\"name\":\"hippopotamus\",\"key\":\"1F99B\"},{\"char\":\"🐭\",\"name\":\"mouse face\",\"key\":\"1F42D\"},{\"char\":\"🐁\",\"name\":\"mouse\",\"key\":\"1F401\"},{\"char\":\"🐀\",\"name\":\"rat\",\"key\":\"1F400\"},{\"char\":\"🐹\",\"name\":\"hamster\",\"key\":\"1F439\"},{\"char\":\"🐰\",\"name\":\"rabbit face\",\"key\":\"1F430\"},{\"char\":\"🐇\",\"name\":\"rabbit\",\"key\":\"1F407\"},{\"char\":\"🐿️\",\"name\":\"chipmunk\",\"key\":\"1F43F\"},{\"char\":\"🦫\",\"name\":\"beaver\",\"key\":\"1F9AB\"},{\"char\":\"🦔\",\"name\":\"hedgehog\",\"key\":\"1F994\"},{\"char\":\"🦇\",\"name\":\"bat\",\"key\":\"1F987\"},{\"char\":\"🐻\",\"name\":\"bear\",\"key\":\"1F43B\"},{\"char\":\"🐻‍❄️\",\"name\":\"polar bear\",\"key\":\"1F43B-200D-2744-FE0F\"},{\"char\":\"🐨\",\"name\":\"koala\",\"key\":\"1F428\"},{\"char\":\"🐼\",\"name\":\"panda\",\"key\":\"1F43C\"},{\"char\":\"🦥\",\"name\":\"sloth\",\"key\":\"1F9A5\"},{\"char\":\"🦦\",\"name\":\"otter\",\"key\":\"1F9A6\"},{\"char\":\"🦨\",\"name\":\"skunk\",\"key\":\"1F9A8\"},{\"char\":\"🦘\",\"name\":\"kangaroo\",\"key\":\"1F998\"},{\"char\":\"🦡\",\"name\":\"badger\",\"key\":\"1F9A1\"},{\"char\":\"🐾\",\"name\":\"paw prints\",\"key\":\"1F43E\"},{\"char\":\"🦃\",\"name\":\"turkey\",\"key\":\"1F983\"},{\"char\":\"🐔\",\"name\":\"chicken\",\"key\":\"1F414\"},{\"char\":\"🐓\",\"name\":\"rooster\",\"key\":\"1F413\"},{\"char\":\"🐣\",\"name\":\"hatching chick\",\"key\":\"1F423\"},{\"char\":\"🐤\",\"name\":\"baby chick\",\"key\":\"1F424\"},{\"char\":\"🐥\",\"name\":\"front-facing baby chick\",\"key\":\"1F425\"},{\"char\":\"🐦\",\"name\":\"bird\",\"key\":\"1F426\"},{\"char\":\"🐧\",\"name\":\"penguin\",\"key\":\"1F427\"},{\"char\":\"🕊️\",\"name\":\"dove\",\"key\":\"1F54A\"},{\"char\":\"🦅\",\"name\":\"eagle\",\"key\":\"1F985\"},{\"char\":\"🦆\",\"name\":\"duck\",\"key\":\"1F986\"},{\"char\":\"🦢\",\"name\":\"swan\",\"key\":\"1F9A2\"},{\"char\":\"🦉\",\"name\":\"owl\",\"key\":\"1F989\"},{\"char\":\"🦤\",\"name\":\"dodo\",\"key\":\"1F9A4\"},{\"char\":\"🪶\",\"name\":\"feather\",\"key\":\"1FAB6\"},{\"char\":\"🦩\",\"name\":\"flamingo\",\"key\":\"1F9A9\"},{\"char\":\"🦚\",\"name\":\"peacock\",\"key\":\"1F99A\"},{\"char\":\"🦜\",\"name\":\"parrot\",\"key\":\"1F99C\"},{\"char\":\"🪽\",\"name\":\"wing\",\"key\":\"1FABD\"},{\"char\":\"🐦‍⬛\",\"name\":\"black bird\",\"key\":\"1F426-200D-2B1B\"},{\"char\":\"🪿\",\"name\":\"goose\",\"key\":\"1FABF\"},{\"char\":\"🐦‍🔥\",\"name\":\"phoenix\",\"key\":\"1F426-200D-1F525\"},{\"char\":\"🐸\",\"name\":\"frog\",\"key\":\"1F438\"},{\"char\":\"🐊\",\"name\":\"crocodile\",\"key\":\"1F40A\"},{\"char\":\"🐢\",\"name\":\"turtle\",\"key\":\"1F422\"},{\"char\":\"🦎\",\"name\":\"lizard\",\"key\":\"1F98E\"},{\"char\":\"🐍\",\"name\":\"snake\",\"key\":\"1F40D\"},{\"char\":\"🐲\",\"name\":\"dragon face\",\"key\":\"1F432\"},{\"char\":\"🐉\",\"name\":\"dragon\",\"key\":\"1F409\"},{\"char\":\"🦕\",\"name\":\"sauropod\",\"key\":\"1F995\"},{\"char\":\"🦖\",\"name\":\"T-Rex\",\"key\":\"1F996\"},{\"char\":\"🐳\",\"name\":\"spouting whale\",\"key\":\"1F433\"},{\"char\":\"🐋\",\"name\":\"whale\",\"key\":\"1F40B\"},{\"char\":\"🐬\",\"name\":\"dolphin\",\"key\":\"1F42C\"},{\"char\":\"🫍\",\"name\":\"orca\",\"key\":\"1FACD\"},{\"char\":\"🦭\",\"name\":\"seal\",\"key\":\"1F9AD\"},{\"char\":\"🐟\",\"name\":\"fish\",\"key\":\"1F41F\"},{\"char\":\"🐠\",\"name\":\"tropical fish\",\"key\":\"1F420\"},{\"char\":\"🐡\",\"name\":\"blowfish\",\"key\":\"1F421\"},{\"char\":\"🦈\",\"name\":\"shark\",\"key\":\"1F988\"},{\"char\":\"🐙\",\"name\":\"octopus\",\"key\":\"1F419\"},{\"char\":\"🐚\",\"name\":\"spiral shell\",\"key\":\"1F41A\"},{\"char\":\"🪸\",\"name\":\"coral\",\"key\":\"1FAB8\"},{\"char\":\"🪼\",\"name\":\"jellyfish\",\"key\":\"1FABC\"},{\"char\":\"🦀\",\"name\":\"crab\",\"key\":\"1F980\"},{\"char\":\"🦞\",\"name\":\"lobster\",\"key\":\"1F99E\"},{\"char\":\"🦐\",\"name\":\"shrimp\",\"key\":\"1F990\"},{\"char\":\"🦑\",\"name\":\"squid\",\"key\":\"1F991\"},{\"char\":\"🦪\",\"name\":\"oyster\",\"key\":\"1F9AA\"},{\"char\":\"🐌\",\"name\":\"snail\",\"key\":\"1F40C\"},{\"char\":\"🦋\",\"name\":\"butterfly\",\"key\":\"1F98B\"},{\"char\":\"🐛\",\"name\":\"bug\",\"key\":\"1F41B\"},{\"char\":\"🐜\",\"name\":\"ant\",\"key\":\"1F41C\"},{\"char\":\"🐝\",\"name\":\"honeybee\",\"key\":\"1F41D\"},{\"char\":\"🪲\",\"name\":\"beetle\",\"key\":\"1FAB2\"},{\"char\":\"🐞\",\"name\":\"lady beetle\",\"key\":\"1F41E\"},{\"char\":\"🦗\",\"name\":\"cricket\",\"key\":\"1F997\"},{\"char\":\"🪳\",\"name\":\"cockroach\",\"key\":\"1FAB3\"},{\"char\":\"🕷️\",\"name\":\"spider\",\"key\":\"1F577\"},{\"char\":\"🕸️\",\"name\":\"spider web\",\"key\":\"1F578\"},{\"char\":\"🦂\",\"name\":\"scorpion\",\"key\":\"1F982\"},{\"char\":\"🦟\",\"name\":\"mosquito\",\"key\":\"1F99F\"},{\"char\":\"🪰\",\"name\":\"fly\",\"key\":\"1FAB0\"},{\"char\":\"🪱\",\"name\":\"worm\",\"key\":\"1FAB1\"},{\"char\":\"🦠\",\"name\":\"microbe\",\"key\":\"1F9A0\"},{\"char\":\"💐\",\"name\":\"bouquet\",\"key\":\"1F490\"},{\"char\":\"🌸\",\"name\":\"cherry blossom\",\"key\":\"1F338\"},{\"char\":\"💮\",\"name\":\"white flower\",\"key\":\"1F4AE\"},{\"char\":\"🪷\",\"name\":\"lotus\",\"key\":\"1FAB7\"},{\"char\":\"🏵️\",\"name\":\"rosette\",\"key\":\"1F3F5\"},{\"char\":\"🌹\",\"name\":\"rose\",\"key\":\"1F339\"},{\"char\":\"🥀\",\"name\":\"wilted flower\",\"key\":\"1F940\"},{\"char\":\"🌺\",\"name\":\"hibiscus\",\"key\":\"1F33A\"},{\"char\":\"🌻\",\"name\":\"sunflower\",\"key\":\"1F33B\"},{\"char\":\"🌼\",\"name\":\"blossom\",\"key\":\"1F33C\"},{\"char\":\"🌷\",\"name\":\"tulip\",\"key\":\"1F337\"},{\"char\":\"🪻\",\"name\":\"hyacinth\",\"key\":\"1FABB\"},{\"char\":\"🌱\",\"name\":\"seedling\",\"key\":\"1F331\"},{\"char\":\"🪴\",\"name\":\"potted plant\",\"key\":\"1FAB4\"},{\"char\":\"🌲\",\"name\":\"evergreen tree\",\"key\":\"1F332\"},{\"char\":\"🌳\",\"name\":\"deciduous tree\",\"key\":\"1F333\"},{\"char\":\"🌴\",\"name\":\"palm tree\",\"key\":\"1F334\"},{\"char\":\"🌵\",\"name\":\"cactus\",\"key\":\"1F335\"},{\"char\":\"🌾\",\"name\":\"sheaf of rice\",\"key\":\"1F33E\"},{\"char\":\"🌿\",\"name\":\"herb\",\"key\":\"1F33F\"},{\"char\":\"☘️\",\"name\":\"shamrock\",\"key\":\"2618\"},{\"char\":\"🍀\",\"name\":\"four leaf clover\",\"key\":\"1F340\"},{\"char\":\"🍁\",\"name\":\"maple leaf\",\"key\":\"1F341\"},{\"char\":\"🍂\",\"name\":\"fallen leaf\",\"key\":\"1F342\"},{\"char\":\"🍃\",\"name\":\"leaf fluttering in wind\",\"key\":\"1F343\"},{\"char\":\"🪹\",\"name\":\"empty nest\",\"key\":\"1FAB9\"},{\"char\":\"🪺\",\"name\":\"nest with eggs\",\"key\":\"1FABA\"},{\"char\":\"🍄\",\"name\":\"mushroom\",\"key\":\"1F344\"},{\"char\":\"🪾\",\"name\":\"leafless tree\",\"key\":\"1FABE\"}]},\n {\"id\":\"food_drink\",\"label\":\"Food & Drink\",\"emojis\":[{\"char\":\"🍇\",\"name\":\"grapes\",\"key\":\"1F347\"},{\"char\":\"🍈\",\"name\":\"melon\",\"key\":\"1F348\"},{\"char\":\"🍉\",\"name\":\"watermelon\",\"key\":\"1F349\"},{\"char\":\"🍊\",\"name\":\"tangerine\",\"key\":\"1F34A\"},{\"char\":\"🍋\",\"name\":\"lemon\",\"key\":\"1F34B\"},{\"char\":\"🍋‍🟩\",\"name\":\"lime\",\"key\":\"1F34B-200D-1F7E9\"},{\"char\":\"🍌\",\"name\":\"banana\",\"key\":\"1F34C\"},{\"char\":\"🍍\",\"name\":\"pineapple\",\"key\":\"1F34D\"},{\"char\":\"🥭\",\"name\":\"mango\",\"key\":\"1F96D\"},{\"char\":\"🍎\",\"name\":\"red apple\",\"key\":\"1F34E\"},{\"char\":\"🍏\",\"name\":\"green apple\",\"key\":\"1F34F\"},{\"char\":\"🍐\",\"name\":\"pear\",\"key\":\"1F350\"},{\"char\":\"🍑\",\"name\":\"peach\",\"key\":\"1F351\"},{\"char\":\"🍒\",\"name\":\"cherries\",\"key\":\"1F352\"},{\"char\":\"🍓\",\"name\":\"strawberry\",\"key\":\"1F353\"},{\"char\":\"🫐\",\"name\":\"blueberries\",\"key\":\"1FAD0\"},{\"char\":\"🥝\",\"name\":\"kiwi fruit\",\"key\":\"1F95D\"},{\"char\":\"🍅\",\"name\":\"tomato\",\"key\":\"1F345\"},{\"char\":\"🫒\",\"name\":\"olive\",\"key\":\"1FAD2\"},{\"char\":\"🥥\",\"name\":\"coconut\",\"key\":\"1F965\"},{\"char\":\"🥑\",\"name\":\"avocado\",\"key\":\"1F951\"},{\"char\":\"🍆\",\"name\":\"eggplant\",\"key\":\"1F346\"},{\"char\":\"🥔\",\"name\":\"potato\",\"key\":\"1F954\"},{\"char\":\"🥕\",\"name\":\"carrot\",\"key\":\"1F955\"},{\"char\":\"🌽\",\"name\":\"ear of corn\",\"key\":\"1F33D\"},{\"char\":\"🌶️\",\"name\":\"hot pepper\",\"key\":\"1F336\"},{\"char\":\"🫑\",\"name\":\"bell pepper\",\"key\":\"1FAD1\"},{\"char\":\"🥒\",\"name\":\"cucumber\",\"key\":\"1F952\"},{\"char\":\"🥬\",\"name\":\"leafy green\",\"key\":\"1F96C\"},{\"char\":\"🥦\",\"name\":\"broccoli\",\"key\":\"1F966\"},{\"char\":\"🧄\",\"name\":\"garlic\",\"key\":\"1F9C4\"},{\"char\":\"🧅\",\"name\":\"onion\",\"key\":\"1F9C5\"},{\"char\":\"🥜\",\"name\":\"peanuts\",\"key\":\"1F95C\"},{\"char\":\"🫘\",\"name\":\"beans\",\"key\":\"1FAD8\"},{\"char\":\"🌰\",\"name\":\"chestnut\",\"key\":\"1F330\"},{\"char\":\"🫚\",\"name\":\"ginger root\",\"key\":\"1FADA\"},{\"char\":\"🫛\",\"name\":\"pea pod\",\"key\":\"1FADB\"},{\"char\":\"🍄‍🟫\",\"name\":\"brown mushroom\",\"key\":\"1F344-200D-1F7EB\"},{\"char\":\"🫜\",\"name\":\"root vegetable\",\"key\":\"1FADC\"},{\"char\":\"🍞\",\"name\":\"bread\",\"key\":\"1F35E\"},{\"char\":\"🥐\",\"name\":\"croissant\",\"key\":\"1F950\"},{\"char\":\"🥖\",\"name\":\"baguette bread\",\"key\":\"1F956\"},{\"char\":\"🫓\",\"name\":\"flatbread\",\"key\":\"1FAD3\"},{\"char\":\"🥨\",\"name\":\"pretzel\",\"key\":\"1F968\"},{\"char\":\"🥯\",\"name\":\"bagel\",\"key\":\"1F96F\"},{\"char\":\"🥞\",\"name\":\"pancakes\",\"key\":\"1F95E\"},{\"char\":\"🧇\",\"name\":\"waffle\",\"key\":\"1F9C7\"},{\"char\":\"🧀\",\"name\":\"cheese wedge\",\"key\":\"1F9C0\"},{\"char\":\"🍖\",\"name\":\"meat on bone\",\"key\":\"1F356\"},{\"char\":\"🍗\",\"name\":\"poultry leg\",\"key\":\"1F357\"},{\"char\":\"🥩\",\"name\":\"cut of meat\",\"key\":\"1F969\"},{\"char\":\"🥓\",\"name\":\"bacon\",\"key\":\"1F953\"},{\"char\":\"🍔\",\"name\":\"hamburger\",\"key\":\"1F354\"},{\"char\":\"🍟\",\"name\":\"french fries\",\"key\":\"1F35F\"},{\"char\":\"🍕\",\"name\":\"pizza\",\"key\":\"1F355\"},{\"char\":\"🌭\",\"name\":\"hot dog\",\"key\":\"1F32D\"},{\"char\":\"🥪\",\"name\":\"sandwich\",\"key\":\"1F96A\"},{\"char\":\"🌮\",\"name\":\"taco\",\"key\":\"1F32E\"},{\"char\":\"🌯\",\"name\":\"burrito\",\"key\":\"1F32F\"},{\"char\":\"🫔\",\"name\":\"tamale\",\"key\":\"1FAD4\"},{\"char\":\"🥙\",\"name\":\"stuffed flatbread\",\"key\":\"1F959\"},{\"char\":\"🧆\",\"name\":\"falafel\",\"key\":\"1F9C6\"},{\"char\":\"🥚\",\"name\":\"egg\",\"key\":\"1F95A\"},{\"char\":\"🍳\",\"name\":\"cooking\",\"key\":\"1F373\"},{\"char\":\"🥘\",\"name\":\"shallow pan of food\",\"key\":\"1F958\"},{\"char\":\"🍲\",\"name\":\"pot of food\",\"key\":\"1F372\"},{\"char\":\"🫕\",\"name\":\"fondue\",\"key\":\"1FAD5\"},{\"char\":\"🥣\",\"name\":\"bowl with spoon\",\"key\":\"1F963\"},{\"char\":\"🥗\",\"name\":\"green salad\",\"key\":\"1F957\"},{\"char\":\"🍿\",\"name\":\"popcorn\",\"key\":\"1F37F\"},{\"char\":\"🧈\",\"name\":\"butter\",\"key\":\"1F9C8\"},{\"char\":\"🧂\",\"name\":\"salt\",\"key\":\"1F9C2\"},{\"char\":\"🥫\",\"name\":\"canned food\",\"key\":\"1F96B\"},{\"char\":\"🍱\",\"name\":\"bento box\",\"key\":\"1F371\"},{\"char\":\"🍘\",\"name\":\"rice cracker\",\"key\":\"1F358\"},{\"char\":\"🍙\",\"name\":\"rice ball\",\"key\":\"1F359\"},{\"char\":\"🍚\",\"name\":\"cooked rice\",\"key\":\"1F35A\"},{\"char\":\"🍛\",\"name\":\"curry rice\",\"key\":\"1F35B\"},{\"char\":\"🍜\",\"name\":\"steaming bowl\",\"key\":\"1F35C\"},{\"char\":\"🍝\",\"name\":\"spaghetti\",\"key\":\"1F35D\"},{\"char\":\"🍠\",\"name\":\"roasted sweet potato\",\"key\":\"1F360\"},{\"char\":\"🍢\",\"name\":\"oden\",\"key\":\"1F362\"},{\"char\":\"🍣\",\"name\":\"sushi\",\"key\":\"1F363\"},{\"char\":\"🍤\",\"name\":\"fried shrimp\",\"key\":\"1F364\"},{\"char\":\"🍥\",\"name\":\"fish cake with swirl\",\"key\":\"1F365\"},{\"char\":\"🥮\",\"name\":\"moon cake\",\"key\":\"1F96E\"},{\"char\":\"🍡\",\"name\":\"dango\",\"key\":\"1F361\"},{\"char\":\"🥟\",\"name\":\"dumpling\",\"key\":\"1F95F\"},{\"char\":\"🥠\",\"name\":\"fortune cookie\",\"key\":\"1F960\"},{\"char\":\"🥡\",\"name\":\"takeout box\",\"key\":\"1F961\"},{\"char\":\"🍦\",\"name\":\"soft ice cream\",\"key\":\"1F366\"},{\"char\":\"🍧\",\"name\":\"shaved ice\",\"key\":\"1F367\"},{\"char\":\"🍨\",\"name\":\"ice cream\",\"key\":\"1F368\"},{\"char\":\"🍩\",\"name\":\"doughnut\",\"key\":\"1F369\"},{\"char\":\"🍪\",\"name\":\"cookie\",\"key\":\"1F36A\"},{\"char\":\"🎂\",\"name\":\"birthday cake\",\"key\":\"1F382\"},{\"char\":\"🍰\",\"name\":\"shortcake\",\"key\":\"1F370\"},{\"char\":\"🧁\",\"name\":\"cupcake\",\"key\":\"1F9C1\"},{\"char\":\"🥧\",\"name\":\"pie\",\"key\":\"1F967\"},{\"char\":\"🍫\",\"name\":\"chocolate bar\",\"key\":\"1F36B\"},{\"char\":\"🍬\",\"name\":\"candy\",\"key\":\"1F36C\"},{\"char\":\"🍭\",\"name\":\"lollipop\",\"key\":\"1F36D\"},{\"char\":\"🍮\",\"name\":\"custard\",\"key\":\"1F36E\"},{\"char\":\"🍯\",\"name\":\"honey pot\",\"key\":\"1F36F\"},{\"char\":\"🍼\",\"name\":\"baby bottle\",\"key\":\"1F37C\"},{\"char\":\"🥛\",\"name\":\"glass of milk\",\"key\":\"1F95B\"},{\"char\":\"☕\",\"name\":\"hot beverage\",\"key\":\"2615\"},{\"char\":\"🫖\",\"name\":\"teapot\",\"key\":\"1FAD6\"},{\"char\":\"🍵\",\"name\":\"teacup without handle\",\"key\":\"1F375\"},{\"char\":\"🍶\",\"name\":\"sake\",\"key\":\"1F376\"},{\"char\":\"🍾\",\"name\":\"bottle with popping cork\",\"key\":\"1F37E\"},{\"char\":\"🍷\",\"name\":\"wine glass\",\"key\":\"1F377\"},{\"char\":\"🍸\",\"name\":\"cocktail glass\",\"key\":\"1F378\"},{\"char\":\"🍹\",\"name\":\"tropical drink\",\"key\":\"1F379\"},{\"char\":\"🍺\",\"name\":\"beer mug\",\"key\":\"1F37A\"},{\"char\":\"🍻\",\"name\":\"clinking beer mugs\",\"key\":\"1F37B\"},{\"char\":\"🥂\",\"name\":\"clinking glasses\",\"key\":\"1F942\"},{\"char\":\"🥃\",\"name\":\"tumbler glass\",\"key\":\"1F943\"},{\"char\":\"🫗\",\"name\":\"pouring liquid\",\"key\":\"1FAD7\"},{\"char\":\"🥤\",\"name\":\"cup with straw\",\"key\":\"1F964\"},{\"char\":\"🧋\",\"name\":\"bubble tea\",\"key\":\"1F9CB\"},{\"char\":\"🧃\",\"name\":\"beverage box\",\"key\":\"1F9C3\"},{\"char\":\"🧉\",\"name\":\"mate\",\"key\":\"1F9C9\"},{\"char\":\"🧊\",\"name\":\"ice\",\"key\":\"1F9CA\"},{\"char\":\"🥢\",\"name\":\"chopsticks\",\"key\":\"1F962\"},{\"char\":\"🍽️\",\"name\":\"fork and knife with plate\",\"key\":\"1F37D\"},{\"char\":\"🍴\",\"name\":\"fork and knife\",\"key\":\"1F374\"},{\"char\":\"🥄\",\"name\":\"spoon\",\"key\":\"1F944\"},{\"char\":\"🔪\",\"name\":\"kitchen knife\",\"key\":\"1F52A\"},{\"char\":\"🫙\",\"name\":\"jar\",\"key\":\"1FAD9\"},{\"char\":\"🏺\",\"name\":\"amphora\",\"key\":\"1F3FA\"}]},\n {\"id\":\"travel_places\",\"label\":\"Travel & Places\",\"emojis\":[{\"char\":\"🌍\",\"name\":\"globe showing Europe-Africa\",\"key\":\"1F30D\"},{\"char\":\"🌎\",\"name\":\"globe showing Americas\",\"key\":\"1F30E\"},{\"char\":\"🌏\",\"name\":\"globe showing Asia-Australia\",\"key\":\"1F30F\"},{\"char\":\"🌐\",\"name\":\"globe with meridians\",\"key\":\"1F310\"},{\"char\":\"🗺️\",\"name\":\"world map\",\"key\":\"1F5FA\"},{\"char\":\"🗾\",\"name\":\"map of Japan\",\"key\":\"1F5FE\"},{\"char\":\"🧭\",\"name\":\"compass\",\"key\":\"1F9ED\"},{\"char\":\"🏔️\",\"name\":\"snow-capped mountain\",\"key\":\"1F3D4\"},{\"char\":\"⛰️\",\"name\":\"mountain\",\"key\":\"26F0\"},{\"char\":\"🛘\",\"name\":\"landslide\",\"key\":\"1F6D8\"},{\"char\":\"🌋\",\"name\":\"volcano\",\"key\":\"1F30B\"},{\"char\":\"🗻\",\"name\":\"mount fuji\",\"key\":\"1F5FB\"},{\"char\":\"🏕️\",\"name\":\"camping\",\"key\":\"1F3D5\"},{\"char\":\"🏖️\",\"name\":\"beach with umbrella\",\"key\":\"1F3D6\"},{\"char\":\"🏜️\",\"name\":\"desert\",\"key\":\"1F3DC\"},{\"char\":\"🏝️\",\"name\":\"desert island\",\"key\":\"1F3DD\"},{\"char\":\"🏞️\",\"name\":\"national park\",\"key\":\"1F3DE\"},{\"char\":\"🏟️\",\"name\":\"stadium\",\"key\":\"1F3DF\"},{\"char\":\"🏛️\",\"name\":\"classical building\",\"key\":\"1F3DB\"},{\"char\":\"🏗️\",\"name\":\"building construction\",\"key\":\"1F3D7\"},{\"char\":\"🧱\",\"name\":\"brick\",\"key\":\"1F9F1\"},{\"char\":\"🪨\",\"name\":\"rock\",\"key\":\"1FAA8\"},{\"char\":\"🪵\",\"name\":\"wood\",\"key\":\"1FAB5\"},{\"char\":\"🛖\",\"name\":\"hut\",\"key\":\"1F6D6\"},{\"char\":\"🏘️\",\"name\":\"houses\",\"key\":\"1F3D8\"},{\"char\":\"🏚️\",\"name\":\"derelict house\",\"key\":\"1F3DA\"},{\"char\":\"🏠\",\"name\":\"house\",\"key\":\"1F3E0\"},{\"char\":\"🏡\",\"name\":\"house with garden\",\"key\":\"1F3E1\"},{\"char\":\"🏢\",\"name\":\"office building\",\"key\":\"1F3E2\"},{\"char\":\"🏣\",\"name\":\"Japanese post office\",\"key\":\"1F3E3\"},{\"char\":\"🏤\",\"name\":\"post office\",\"key\":\"1F3E4\"},{\"char\":\"🏥\",\"name\":\"hospital\",\"key\":\"1F3E5\"},{\"char\":\"🏦\",\"name\":\"bank\",\"key\":\"1F3E6\"},{\"char\":\"🏨\",\"name\":\"hotel\",\"key\":\"1F3E8\"},{\"char\":\"🏩\",\"name\":\"love hotel\",\"key\":\"1F3E9\"},{\"char\":\"🏪\",\"name\":\"convenience store\",\"key\":\"1F3EA\"},{\"char\":\"🏫\",\"name\":\"school\",\"key\":\"1F3EB\"},{\"char\":\"🏬\",\"name\":\"department store\",\"key\":\"1F3EC\"},{\"char\":\"🏭\",\"name\":\"factory\",\"key\":\"1F3ED\"},{\"char\":\"🏯\",\"name\":\"Japanese castle\",\"key\":\"1F3EF\"},{\"char\":\"🏰\",\"name\":\"castle\",\"key\":\"1F3F0\"},{\"char\":\"💒\",\"name\":\"wedding\",\"key\":\"1F492\"},{\"char\":\"🗼\",\"name\":\"Tokyo tower\",\"key\":\"1F5FC\"},{\"char\":\"🗽\",\"name\":\"Statue of Liberty\",\"key\":\"1F5FD\"},{\"char\":\"⛪\",\"name\":\"church\",\"key\":\"26EA\"},{\"char\":\"🕌\",\"name\":\"mosque\",\"key\":\"1F54C\"},{\"char\":\"🛕\",\"name\":\"hindu temple\",\"key\":\"1F6D5\"},{\"char\":\"🕍\",\"name\":\"synagogue\",\"key\":\"1F54D\"},{\"char\":\"⛩️\",\"name\":\"shinto shrine\",\"key\":\"26E9\"},{\"char\":\"🕋\",\"name\":\"kaaba\",\"key\":\"1F54B\"},{\"char\":\"⛲\",\"name\":\"fountain\",\"key\":\"26F2\"},{\"char\":\"⛺\",\"name\":\"tent\",\"key\":\"26FA\"},{\"char\":\"🌁\",\"name\":\"foggy\",\"key\":\"1F301\"},{\"char\":\"🌃\",\"name\":\"night with stars\",\"key\":\"1F303\"},{\"char\":\"🏙️\",\"name\":\"cityscape\",\"key\":\"1F3D9\"},{\"char\":\"🌄\",\"name\":\"sunrise over mountains\",\"key\":\"1F304\"},{\"char\":\"🌅\",\"name\":\"sunrise\",\"key\":\"1F305\"},{\"char\":\"🌆\",\"name\":\"cityscape at dusk\",\"key\":\"1F306\"},{\"char\":\"🌇\",\"name\":\"sunset\",\"key\":\"1F307\"},{\"char\":\"🌉\",\"name\":\"bridge at night\",\"key\":\"1F309\"},{\"char\":\"♨️\",\"name\":\"hot springs\",\"key\":\"2668\"},{\"char\":\"🎠\",\"name\":\"carousel horse\",\"key\":\"1F3A0\"},{\"char\":\"🛝\",\"name\":\"playground slide\",\"key\":\"1F6DD\"},{\"char\":\"🎡\",\"name\":\"ferris wheel\",\"key\":\"1F3A1\"},{\"char\":\"🎢\",\"name\":\"roller coaster\",\"key\":\"1F3A2\"},{\"char\":\"💈\",\"name\":\"barber pole\",\"key\":\"1F488\"},{\"char\":\"🎪\",\"name\":\"circus tent\",\"key\":\"1F3AA\"},{\"char\":\"🚂\",\"name\":\"locomotive\",\"key\":\"1F682\"},{\"char\":\"🚃\",\"name\":\"railway car\",\"key\":\"1F683\"},{\"char\":\"🚄\",\"name\":\"high-speed train\",\"key\":\"1F684\"},{\"char\":\"🚅\",\"name\":\"bullet train\",\"key\":\"1F685\"},{\"char\":\"🚆\",\"name\":\"train\",\"key\":\"1F686\"},{\"char\":\"🚇\",\"name\":\"metro\",\"key\":\"1F687\"},{\"char\":\"🚈\",\"name\":\"light rail\",\"key\":\"1F688\"},{\"char\":\"🚉\",\"name\":\"station\",\"key\":\"1F689\"},{\"char\":\"🚊\",\"name\":\"tram\",\"key\":\"1F68A\"},{\"char\":\"🚝\",\"name\":\"monorail\",\"key\":\"1F69D\"},{\"char\":\"🚞\",\"name\":\"mountain railway\",\"key\":\"1F69E\"},{\"char\":\"🚋\",\"name\":\"tram car\",\"key\":\"1F68B\"},{\"char\":\"🚌\",\"name\":\"bus\",\"key\":\"1F68C\"},{\"char\":\"🚍\",\"name\":\"oncoming bus\",\"key\":\"1F68D\"},{\"char\":\"🚎\",\"name\":\"trolleybus\",\"key\":\"1F68E\"},{\"char\":\"🚐\",\"name\":\"minibus\",\"key\":\"1F690\"},{\"char\":\"🚑\",\"name\":\"ambulance\",\"key\":\"1F691\"},{\"char\":\"🚒\",\"name\":\"fire engine\",\"key\":\"1F692\"},{\"char\":\"🚓\",\"name\":\"police car\",\"key\":\"1F693\"},{\"char\":\"🚔\",\"name\":\"oncoming police car\",\"key\":\"1F694\"},{\"char\":\"🚕\",\"name\":\"taxi\",\"key\":\"1F695\"},{\"char\":\"🚖\",\"name\":\"oncoming taxi\",\"key\":\"1F696\"},{\"char\":\"🚗\",\"name\":\"automobile\",\"key\":\"1F697\"},{\"char\":\"🚘\",\"name\":\"oncoming automobile\",\"key\":\"1F698\"},{\"char\":\"🚙\",\"name\":\"sport utility vehicle\",\"key\":\"1F699\"},{\"char\":\"🛻\",\"name\":\"pickup truck\",\"key\":\"1F6FB\"},{\"char\":\"🚚\",\"name\":\"delivery truck\",\"key\":\"1F69A\"},{\"char\":\"🚛\",\"name\":\"articulated lorry\",\"key\":\"1F69B\"},{\"char\":\"🚜\",\"name\":\"tractor\",\"key\":\"1F69C\"},{\"char\":\"🏎️\",\"name\":\"racing car\",\"key\":\"1F3CE\"},{\"char\":\"🏍️\",\"name\":\"motorcycle\",\"key\":\"1F3CD\"},{\"char\":\"🛵\",\"name\":\"motor scooter\",\"key\":\"1F6F5\"},{\"char\":\"🦽\",\"name\":\"manual wheelchair\",\"key\":\"1F9BD\"},{\"char\":\"🦼\",\"name\":\"motorized wheelchair\",\"key\":\"1F9BC\"},{\"char\":\"🛺\",\"name\":\"auto rickshaw\",\"key\":\"1F6FA\"},{\"char\":\"🚲\",\"name\":\"bicycle\",\"key\":\"1F6B2\"},{\"char\":\"🛴\",\"name\":\"kick scooter\",\"key\":\"1F6F4\"},{\"char\":\"🛹\",\"name\":\"skateboard\",\"key\":\"1F6F9\"},{\"char\":\"🛼\",\"name\":\"roller skate\",\"key\":\"1F6FC\"},{\"char\":\"🚏\",\"name\":\"bus stop\",\"key\":\"1F68F\"},{\"char\":\"🛣️\",\"name\":\"motorway\",\"key\":\"1F6E3\"},{\"char\":\"🛤️\",\"name\":\"railway track\",\"key\":\"1F6E4\"},{\"char\":\"🛢️\",\"name\":\"oil drum\",\"key\":\"1F6E2\"},{\"char\":\"⛽\",\"name\":\"fuel pump\",\"key\":\"26FD\"},{\"char\":\"🛞\",\"name\":\"wheel\",\"key\":\"1F6DE\"},{\"char\":\"🚨\",\"name\":\"police car light\",\"key\":\"1F6A8\"},{\"char\":\"🚥\",\"name\":\"horizontal traffic light\",\"key\":\"1F6A5\"},{\"char\":\"🚦\",\"name\":\"vertical traffic light\",\"key\":\"1F6A6\"},{\"char\":\"🛑\",\"name\":\"stop sign\",\"key\":\"1F6D1\"},{\"char\":\"🚧\",\"name\":\"construction\",\"key\":\"1F6A7\"},{\"char\":\"⚓\",\"name\":\"anchor\",\"key\":\"2693\"},{\"char\":\"🛟\",\"name\":\"ring buoy\",\"key\":\"1F6DF\"},{\"char\":\"⛵\",\"name\":\"sailboat\",\"key\":\"26F5\"},{\"char\":\"🛶\",\"name\":\"canoe\",\"key\":\"1F6F6\"},{\"char\":\"🚤\",\"name\":\"speedboat\",\"key\":\"1F6A4\"},{\"char\":\"🛳️\",\"name\":\"passenger ship\",\"key\":\"1F6F3\"},{\"char\":\"⛴️\",\"name\":\"ferry\",\"key\":\"26F4\"},{\"char\":\"🛥️\",\"name\":\"motor boat\",\"key\":\"1F6E5\"},{\"char\":\"🚢\",\"name\":\"ship\",\"key\":\"1F6A2\"},{\"char\":\"✈️\",\"name\":\"airplane\",\"key\":\"2708\"},{\"char\":\"🛩️\",\"name\":\"small airplane\",\"key\":\"1F6E9\"},{\"char\":\"🛫\",\"name\":\"airplane departure\",\"key\":\"1F6EB\"},{\"char\":\"🛬\",\"name\":\"airplane arrival\",\"key\":\"1F6EC\"},{\"char\":\"🪂\",\"name\":\"parachute\",\"key\":\"1FA82\"},{\"char\":\"💺\",\"name\":\"seat\",\"key\":\"1F4BA\"},{\"char\":\"🚁\",\"name\":\"helicopter\",\"key\":\"1F681\"},{\"char\":\"🚟\",\"name\":\"suspension railway\",\"key\":\"1F69F\"},{\"char\":\"🚠\",\"name\":\"mountain cableway\",\"key\":\"1F6A0\"},{\"char\":\"🚡\",\"name\":\"aerial tramway\",\"key\":\"1F6A1\"},{\"char\":\"🛰️\",\"name\":\"satellite\",\"key\":\"1F6F0\"},{\"char\":\"🚀\",\"name\":\"rocket\",\"key\":\"1F680\"},{\"char\":\"🛸\",\"name\":\"flying saucer\",\"key\":\"1F6F8\"},{\"char\":\"🛎️\",\"name\":\"bellhop bell\",\"key\":\"1F6CE\"},{\"char\":\"🧳\",\"name\":\"luggage\",\"key\":\"1F9F3\"},{\"char\":\"⌛\",\"name\":\"hourglass done\",\"key\":\"231B\"},{\"char\":\"⏳\",\"name\":\"hourglass not done\",\"key\":\"23F3\"},{\"char\":\"⌚\",\"name\":\"watch\",\"key\":\"231A\"},{\"char\":\"⏰\",\"name\":\"alarm clock\",\"key\":\"23F0\"},{\"char\":\"⏱️\",\"name\":\"stopwatch\",\"key\":\"23F1\"},{\"char\":\"⏲️\",\"name\":\"timer clock\",\"key\":\"23F2\"},{\"char\":\"🕰️\",\"name\":\"mantelpiece clock\",\"key\":\"1F570\"},{\"char\":\"🕛\",\"name\":\"twelve o’clock\",\"key\":\"1F55B\"},{\"char\":\"🕧\",\"name\":\"twelve-thirty\",\"key\":\"1F567\"},{\"char\":\"🕐\",\"name\":\"one o’clock\",\"key\":\"1F550\"},{\"char\":\"🕜\",\"name\":\"one-thirty\",\"key\":\"1F55C\"},{\"char\":\"🕑\",\"name\":\"two o’clock\",\"key\":\"1F551\"},{\"char\":\"🕝\",\"name\":\"two-thirty\",\"key\":\"1F55D\"},{\"char\":\"🕒\",\"name\":\"three o’clock\",\"key\":\"1F552\"},{\"char\":\"🕞\",\"name\":\"three-thirty\",\"key\":\"1F55E\"},{\"char\":\"🕓\",\"name\":\"four o’clock\",\"key\":\"1F553\"},{\"char\":\"🕟\",\"name\":\"four-thirty\",\"key\":\"1F55F\"},{\"char\":\"🕔\",\"name\":\"five o’clock\",\"key\":\"1F554\"},{\"char\":\"🕠\",\"name\":\"five-thirty\",\"key\":\"1F560\"},{\"char\":\"🕕\",\"name\":\"six o’clock\",\"key\":\"1F555\"},{\"char\":\"🕡\",\"name\":\"six-thirty\",\"key\":\"1F561\"},{\"char\":\"🕖\",\"name\":\"seven o’clock\",\"key\":\"1F556\"},{\"char\":\"🕢\",\"name\":\"seven-thirty\",\"key\":\"1F562\"},{\"char\":\"🕗\",\"name\":\"eight o’clock\",\"key\":\"1F557\"},{\"char\":\"🕣\",\"name\":\"eight-thirty\",\"key\":\"1F563\"},{\"char\":\"🕘\",\"name\":\"nine o’clock\",\"key\":\"1F558\"},{\"char\":\"🕤\",\"name\":\"nine-thirty\",\"key\":\"1F564\"},{\"char\":\"🕙\",\"name\":\"ten o’clock\",\"key\":\"1F559\"},{\"char\":\"🕥\",\"name\":\"ten-thirty\",\"key\":\"1F565\"},{\"char\":\"🕚\",\"name\":\"eleven o’clock\",\"key\":\"1F55A\"},{\"char\":\"🕦\",\"name\":\"eleven-thirty\",\"key\":\"1F566\"},{\"char\":\"🌑\",\"name\":\"new moon\",\"key\":\"1F311\"},{\"char\":\"🌒\",\"name\":\"waxing crescent moon\",\"key\":\"1F312\"},{\"char\":\"🌓\",\"name\":\"first quarter moon\",\"key\":\"1F313\"},{\"char\":\"🌔\",\"name\":\"waxing gibbous moon\",\"key\":\"1F314\"},{\"char\":\"🌕\",\"name\":\"full moon\",\"key\":\"1F315\"},{\"char\":\"🌖\",\"name\":\"waning gibbous moon\",\"key\":\"1F316\"},{\"char\":\"🌗\",\"name\":\"last quarter moon\",\"key\":\"1F317\"},{\"char\":\"🌘\",\"name\":\"waning crescent moon\",\"key\":\"1F318\"},{\"char\":\"🌙\",\"name\":\"crescent moon\",\"key\":\"1F319\"},{\"char\":\"🌚\",\"name\":\"new moon face\",\"key\":\"1F31A\"},{\"char\":\"🌛\",\"name\":\"first quarter moon face\",\"key\":\"1F31B\"},{\"char\":\"🌜\",\"name\":\"last quarter moon face\",\"key\":\"1F31C\"},{\"char\":\"🌡️\",\"name\":\"thermometer\",\"key\":\"1F321\"},{\"char\":\"☀️\",\"name\":\"sun\",\"key\":\"2600\"},{\"char\":\"🌝\",\"name\":\"full moon face\",\"key\":\"1F31D\"},{\"char\":\"🌞\",\"name\":\"sun with face\",\"key\":\"1F31E\"},{\"char\":\"🪐\",\"name\":\"ringed planet\",\"key\":\"1FA90\"},{\"char\":\"⭐\",\"name\":\"star\",\"key\":\"2B50\"},{\"char\":\"🌟\",\"name\":\"glowing star\",\"key\":\"1F31F\"},{\"char\":\"🌠\",\"name\":\"shooting star\",\"key\":\"1F320\"},{\"char\":\"🌌\",\"name\":\"milky way\",\"key\":\"1F30C\"},{\"char\":\"☁️\",\"name\":\"cloud\",\"key\":\"2601\"},{\"char\":\"⛅\",\"name\":\"sun behind cloud\",\"key\":\"26C5\"},{\"char\":\"⛈️\",\"name\":\"cloud with lightning and rain\",\"key\":\"26C8\"},{\"char\":\"🌤️\",\"name\":\"sun behind small cloud\",\"key\":\"1F324\"},{\"char\":\"🌥️\",\"name\":\"sun behind large cloud\",\"key\":\"1F325\"},{\"char\":\"🌦️\",\"name\":\"sun behind rain cloud\",\"key\":\"1F326\"},{\"char\":\"🌧️\",\"name\":\"cloud with rain\",\"key\":\"1F327\"},{\"char\":\"🌨️\",\"name\":\"cloud with snow\",\"key\":\"1F328\"},{\"char\":\"🌩️\",\"name\":\"cloud with lightning\",\"key\":\"1F329\"},{\"char\":\"🌪️\",\"name\":\"tornado\",\"key\":\"1F32A\"},{\"char\":\"🌫️\",\"name\":\"fog\",\"key\":\"1F32B\"},{\"char\":\"🌬️\",\"name\":\"wind face\",\"key\":\"1F32C\"},{\"char\":\"🌀\",\"name\":\"cyclone\",\"key\":\"1F300\"},{\"char\":\"🌈\",\"name\":\"rainbow\",\"key\":\"1F308\"},{\"char\":\"🌂\",\"name\":\"closed umbrella\",\"key\":\"1F302\"},{\"char\":\"☂️\",\"name\":\"umbrella\",\"key\":\"2602\"},{\"char\":\"☔\",\"name\":\"umbrella with rain drops\",\"key\":\"2614\"},{\"char\":\"⛱️\",\"name\":\"umbrella on ground\",\"key\":\"26F1\"},{\"char\":\"⚡\",\"name\":\"high voltage\",\"key\":\"26A1\"},{\"char\":\"❄️\",\"name\":\"snowflake\",\"key\":\"2744\"},{\"char\":\"☃️\",\"name\":\"snowman\",\"key\":\"2603\"},{\"char\":\"⛄\",\"name\":\"snowman without snow\",\"key\":\"26C4\"},{\"char\":\"☄️\",\"name\":\"comet\",\"key\":\"2604\"},{\"char\":\"🔥\",\"name\":\"fire\",\"key\":\"1F525\"},{\"char\":\"💧\",\"name\":\"droplet\",\"key\":\"1F4A7\"},{\"char\":\"🌊\",\"name\":\"water wave\",\"key\":\"1F30A\"}]},\n {\"id\":\"activities\",\"label\":\"Activities\",\"emojis\":[{\"char\":\"🎃\",\"name\":\"jack-o-lantern\",\"key\":\"1F383\"},{\"char\":\"🎄\",\"name\":\"Christmas tree\",\"key\":\"1F384\"},{\"char\":\"🎆\",\"name\":\"fireworks\",\"key\":\"1F386\"},{\"char\":\"🎇\",\"name\":\"sparkler\",\"key\":\"1F387\"},{\"char\":\"🧨\",\"name\":\"firecracker\",\"key\":\"1F9E8\"},{\"char\":\"✨\",\"name\":\"sparkles\",\"key\":\"2728\"},{\"char\":\"🎈\",\"name\":\"balloon\",\"key\":\"1F388\"},{\"char\":\"🎉\",\"name\":\"party popper\",\"key\":\"1F389\"},{\"char\":\"🎊\",\"name\":\"confetti ball\",\"key\":\"1F38A\"},{\"char\":\"🎋\",\"name\":\"tanabata tree\",\"key\":\"1F38B\"},{\"char\":\"🎍\",\"name\":\"pine decoration\",\"key\":\"1F38D\"},{\"char\":\"🎎\",\"name\":\"Japanese dolls\",\"key\":\"1F38E\"},{\"char\":\"🎏\",\"name\":\"carp streamer\",\"key\":\"1F38F\"},{\"char\":\"🎐\",\"name\":\"wind chime\",\"key\":\"1F390\"},{\"char\":\"🎑\",\"name\":\"moon viewing ceremony\",\"key\":\"1F391\"},{\"char\":\"🧧\",\"name\":\"red envelope\",\"key\":\"1F9E7\"},{\"char\":\"🎀\",\"name\":\"ribbon\",\"key\":\"1F380\"},{\"char\":\"🎁\",\"name\":\"wrapped gift\",\"key\":\"1F381\"},{\"char\":\"🎗️\",\"name\":\"reminder ribbon\",\"key\":\"1F397\"},{\"char\":\"🎟️\",\"name\":\"admission tickets\",\"key\":\"1F39F\"},{\"char\":\"🎫\",\"name\":\"ticket\",\"key\":\"1F3AB\"},{\"char\":\"🎖️\",\"name\":\"military medal\",\"key\":\"1F396\"},{\"char\":\"🏆\",\"name\":\"trophy\",\"key\":\"1F3C6\"},{\"char\":\"🏅\",\"name\":\"sports medal\",\"key\":\"1F3C5\"},{\"char\":\"🥇\",\"name\":\"1st place medal\",\"key\":\"1F947\"},{\"char\":\"🥈\",\"name\":\"2nd place medal\",\"key\":\"1F948\"},{\"char\":\"🥉\",\"name\":\"3rd place medal\",\"key\":\"1F949\"},{\"char\":\"⚽\",\"name\":\"soccer ball\",\"key\":\"26BD\"},{\"char\":\"⚾\",\"name\":\"baseball\",\"key\":\"26BE\"},{\"char\":\"🥎\",\"name\":\"softball\",\"key\":\"1F94E\"},{\"char\":\"🏀\",\"name\":\"basketball\",\"key\":\"1F3C0\"},{\"char\":\"🏐\",\"name\":\"volleyball\",\"key\":\"1F3D0\"},{\"char\":\"🏈\",\"name\":\"american football\",\"key\":\"1F3C8\"},{\"char\":\"🏉\",\"name\":\"rugby football\",\"key\":\"1F3C9\"},{\"char\":\"🎾\",\"name\":\"tennis\",\"key\":\"1F3BE\"},{\"char\":\"🥏\",\"name\":\"flying disc\",\"key\":\"1F94F\"},{\"char\":\"🎳\",\"name\":\"bowling\",\"key\":\"1F3B3\"},{\"char\":\"🏏\",\"name\":\"cricket game\",\"key\":\"1F3CF\"},{\"char\":\"🏑\",\"name\":\"field hockey\",\"key\":\"1F3D1\"},{\"char\":\"🏒\",\"name\":\"ice hockey\",\"key\":\"1F3D2\"},{\"char\":\"🥍\",\"name\":\"lacrosse\",\"key\":\"1F94D\"},{\"char\":\"🏓\",\"name\":\"ping pong\",\"key\":\"1F3D3\"},{\"char\":\"🏸\",\"name\":\"badminton\",\"key\":\"1F3F8\"},{\"char\":\"🥊\",\"name\":\"boxing glove\",\"key\":\"1F94A\"},{\"char\":\"🥋\",\"name\":\"martial arts uniform\",\"key\":\"1F94B\"},{\"char\":\"🥅\",\"name\":\"goal net\",\"key\":\"1F945\"},{\"char\":\"⛳\",\"name\":\"flag in hole\",\"key\":\"26F3\"},{\"char\":\"⛸️\",\"name\":\"ice skate\",\"key\":\"26F8\"},{\"char\":\"🎣\",\"name\":\"fishing pole\",\"key\":\"1F3A3\"},{\"char\":\"🤿\",\"name\":\"diving mask\",\"key\":\"1F93F\"},{\"char\":\"🎽\",\"name\":\"running shirt\",\"key\":\"1F3BD\"},{\"char\":\"🎿\",\"name\":\"skis\",\"key\":\"1F3BF\"},{\"char\":\"🛷\",\"name\":\"sled\",\"key\":\"1F6F7\"},{\"char\":\"🥌\",\"name\":\"curling stone\",\"key\":\"1F94C\"},{\"char\":\"🎯\",\"name\":\"bullseye\",\"key\":\"1F3AF\"},{\"char\":\"🪀\",\"name\":\"yo-yo\",\"key\":\"1FA80\"},{\"char\":\"🪁\",\"name\":\"kite\",\"key\":\"1FA81\"},{\"char\":\"🔫\",\"name\":\"water pistol\",\"key\":\"1F52B\"},{\"char\":\"🎱\",\"name\":\"pool 8 ball\",\"key\":\"1F3B1\"},{\"char\":\"🔮\",\"name\":\"crystal ball\",\"key\":\"1F52E\"},{\"char\":\"🪄\",\"name\":\"magic wand\",\"key\":\"1FA84\"},{\"char\":\"🎮\",\"name\":\"video game\",\"key\":\"1F3AE\"},{\"char\":\"🕹️\",\"name\":\"joystick\",\"key\":\"1F579\"},{\"char\":\"🎰\",\"name\":\"slot machine\",\"key\":\"1F3B0\"},{\"char\":\"🎲\",\"name\":\"game die\",\"key\":\"1F3B2\"},{\"char\":\"🧩\",\"name\":\"puzzle piece\",\"key\":\"1F9E9\"},{\"char\":\"🧸\",\"name\":\"teddy bear\",\"key\":\"1F9F8\"},{\"char\":\"🪅\",\"name\":\"piñata\",\"key\":\"1FA85\"},{\"char\":\"🪩\",\"name\":\"mirror ball\",\"key\":\"1FAA9\"},{\"char\":\"🪆\",\"name\":\"nesting dolls\",\"key\":\"1FA86\"},{\"char\":\"♠️\",\"name\":\"spade suit\",\"key\":\"2660\"},{\"char\":\"♥️\",\"name\":\"heart suit\",\"key\":\"2665\"},{\"char\":\"♦️\",\"name\":\"diamond suit\",\"key\":\"2666\"},{\"char\":\"♣️\",\"name\":\"club suit\",\"key\":\"2663\"},{\"char\":\"♟️\",\"name\":\"chess pawn\",\"key\":\"265F\"},{\"char\":\"🃏\",\"name\":\"joker\",\"key\":\"1F0CF\"},{\"char\":\"🀄\",\"name\":\"mahjong red dragon\",\"key\":\"1F004\"},{\"char\":\"🎴\",\"name\":\"flower playing cards\",\"key\":\"1F3B4\"},{\"char\":\"🎭\",\"name\":\"performing arts\",\"key\":\"1F3AD\"},{\"char\":\"🖼️\",\"name\":\"framed picture\",\"key\":\"1F5BC\"},{\"char\":\"🎨\",\"name\":\"artist palette\",\"key\":\"1F3A8\"},{\"char\":\"🧵\",\"name\":\"thread\",\"key\":\"1F9F5\"},{\"char\":\"🪡\",\"name\":\"sewing needle\",\"key\":\"1FAA1\"},{\"char\":\"🧶\",\"name\":\"yarn\",\"key\":\"1F9F6\"},{\"char\":\"🪢\",\"name\":\"knot\",\"key\":\"1FAA2\"}]},\n {\"id\":\"objects\",\"label\":\"Objects\",\"emojis\":[{\"char\":\"👓\",\"name\":\"glasses\",\"key\":\"1F453\"},{\"char\":\"🕶️\",\"name\":\"sunglasses\",\"key\":\"1F576\"},{\"char\":\"🥽\",\"name\":\"goggles\",\"key\":\"1F97D\"},{\"char\":\"🥼\",\"name\":\"lab coat\",\"key\":\"1F97C\"},{\"char\":\"🦺\",\"name\":\"safety vest\",\"key\":\"1F9BA\"},{\"char\":\"👔\",\"name\":\"necktie\",\"key\":\"1F454\"},{\"char\":\"👕\",\"name\":\"t-shirt\",\"key\":\"1F455\"},{\"char\":\"👖\",\"name\":\"jeans\",\"key\":\"1F456\"},{\"char\":\"🧣\",\"name\":\"scarf\",\"key\":\"1F9E3\"},{\"char\":\"🧤\",\"name\":\"gloves\",\"key\":\"1F9E4\"},{\"char\":\"🧥\",\"name\":\"coat\",\"key\":\"1F9E5\"},{\"char\":\"🧦\",\"name\":\"socks\",\"key\":\"1F9E6\"},{\"char\":\"👗\",\"name\":\"dress\",\"key\":\"1F457\"},{\"char\":\"👘\",\"name\":\"kimono\",\"key\":\"1F458\"},{\"char\":\"🥻\",\"name\":\"sari\",\"key\":\"1F97B\"},{\"char\":\"🩱\",\"name\":\"one-piece swimsuit\",\"key\":\"1FA71\"},{\"char\":\"🩲\",\"name\":\"briefs\",\"key\":\"1FA72\"},{\"char\":\"🩳\",\"name\":\"shorts\",\"key\":\"1FA73\"},{\"char\":\"👙\",\"name\":\"bikini\",\"key\":\"1F459\"},{\"char\":\"👚\",\"name\":\"woman’s clothes\",\"key\":\"1F45A\"},{\"char\":\"🪭\",\"name\":\"folding hand fan\",\"key\":\"1FAAD\"},{\"char\":\"👛\",\"name\":\"purse\",\"key\":\"1F45B\"},{\"char\":\"👜\",\"name\":\"handbag\",\"key\":\"1F45C\"},{\"char\":\"👝\",\"name\":\"clutch bag\",\"key\":\"1F45D\"},{\"char\":\"🛍️\",\"name\":\"shopping bags\",\"key\":\"1F6CD\"},{\"char\":\"🎒\",\"name\":\"backpack\",\"key\":\"1F392\"},{\"char\":\"🩴\",\"name\":\"thong sandal\",\"key\":\"1FA74\"},{\"char\":\"👞\",\"name\":\"man’s shoe\",\"key\":\"1F45E\"},{\"char\":\"👟\",\"name\":\"running shoe\",\"key\":\"1F45F\"},{\"char\":\"🥾\",\"name\":\"hiking boot\",\"key\":\"1F97E\"},{\"char\":\"🥿\",\"name\":\"flat shoe\",\"key\":\"1F97F\"},{\"char\":\"👠\",\"name\":\"high-heeled shoe\",\"key\":\"1F460\"},{\"char\":\"👡\",\"name\":\"woman’s sandal\",\"key\":\"1F461\"},{\"char\":\"🩰\",\"name\":\"ballet shoes\",\"key\":\"1FA70\"},{\"char\":\"👢\",\"name\":\"woman’s boot\",\"key\":\"1F462\"},{\"char\":\"🪮\",\"name\":\"hair pick\",\"key\":\"1FAAE\"},{\"char\":\"👑\",\"name\":\"crown\",\"key\":\"1F451\"},{\"char\":\"👒\",\"name\":\"woman’s hat\",\"key\":\"1F452\"},{\"char\":\"🎩\",\"name\":\"top hat\",\"key\":\"1F3A9\"},{\"char\":\"🎓\",\"name\":\"graduation cap\",\"key\":\"1F393\"},{\"char\":\"🧢\",\"name\":\"billed cap\",\"key\":\"1F9E2\"},{\"char\":\"🪖\",\"name\":\"military helmet\",\"key\":\"1FA96\"},{\"char\":\"⛑️\",\"name\":\"rescue worker’s helmet\",\"key\":\"26D1\"},{\"char\":\"📿\",\"name\":\"prayer beads\",\"key\":\"1F4FF\"},{\"char\":\"💄\",\"name\":\"lipstick\",\"key\":\"1F484\"},{\"char\":\"💍\",\"name\":\"ring\",\"key\":\"1F48D\"},{\"char\":\"💎\",\"name\":\"gem stone\",\"key\":\"1F48E\"},{\"char\":\"🔇\",\"name\":\"muted speaker\",\"key\":\"1F507\"},{\"char\":\"🔈\",\"name\":\"speaker low volume\",\"key\":\"1F508\"},{\"char\":\"🔉\",\"name\":\"speaker medium volume\",\"key\":\"1F509\"},{\"char\":\"🔊\",\"name\":\"speaker high volume\",\"key\":\"1F50A\"},{\"char\":\"📢\",\"name\":\"loudspeaker\",\"key\":\"1F4E2\"},{\"char\":\"📣\",\"name\":\"megaphone\",\"key\":\"1F4E3\"},{\"char\":\"📯\",\"name\":\"postal horn\",\"key\":\"1F4EF\"},{\"char\":\"🔔\",\"name\":\"bell\",\"key\":\"1F514\"},{\"char\":\"🔕\",\"name\":\"bell with slash\",\"key\":\"1F515\"},{\"char\":\"🎼\",\"name\":\"musical score\",\"key\":\"1F3BC\"},{\"char\":\"🎵\",\"name\":\"musical note\",\"key\":\"1F3B5\"},{\"char\":\"🎶\",\"name\":\"musical notes\",\"key\":\"1F3B6\"},{\"char\":\"🎙️\",\"name\":\"studio microphone\",\"key\":\"1F399\"},{\"char\":\"🎚️\",\"name\":\"level slider\",\"key\":\"1F39A\"},{\"char\":\"🎛️\",\"name\":\"control knobs\",\"key\":\"1F39B\"},{\"char\":\"🎤\",\"name\":\"microphone\",\"key\":\"1F3A4\"},{\"char\":\"🎧\",\"name\":\"headphone\",\"key\":\"1F3A7\"},{\"char\":\"📻\",\"name\":\"radio\",\"key\":\"1F4FB\"},{\"char\":\"🎷\",\"name\":\"saxophone\",\"key\":\"1F3B7\"},{\"char\":\"🎺\",\"name\":\"trumpet\",\"key\":\"1F3BA\"},{\"char\":\"🪊\",\"name\":\"trombone\",\"key\":\"1FA8A\"},{\"char\":\"🪗\",\"name\":\"accordion\",\"key\":\"1FA97\"},{\"char\":\"🎸\",\"name\":\"guitar\",\"key\":\"1F3B8\"},{\"char\":\"🎹\",\"name\":\"musical keyboard\",\"key\":\"1F3B9\"},{\"char\":\"🎻\",\"name\":\"violin\",\"key\":\"1F3BB\"},{\"char\":\"🪕\",\"name\":\"banjo\",\"key\":\"1FA95\"},{\"char\":\"🥁\",\"name\":\"drum\",\"key\":\"1F941\"},{\"char\":\"🪘\",\"name\":\"long drum\",\"key\":\"1FA98\"},{\"char\":\"🪇\",\"name\":\"maracas\",\"key\":\"1FA87\"},{\"char\":\"🪈\",\"name\":\"flute\",\"key\":\"1FA88\"},{\"char\":\"🪉\",\"name\":\"harp\",\"key\":\"1FA89\"},{\"char\":\"📱\",\"name\":\"mobile phone\",\"key\":\"1F4F1\"},{\"char\":\"📲\",\"name\":\"mobile phone with arrow\",\"key\":\"1F4F2\"},{\"char\":\"☎️\",\"name\":\"telephone\",\"key\":\"260E\"},{\"char\":\"📞\",\"name\":\"telephone receiver\",\"key\":\"1F4DE\"},{\"char\":\"📟\",\"name\":\"pager\",\"key\":\"1F4DF\"},{\"char\":\"📠\",\"name\":\"fax machine\",\"key\":\"1F4E0\"},{\"char\":\"🔋\",\"name\":\"battery\",\"key\":\"1F50B\"},{\"char\":\"🪫\",\"name\":\"low battery\",\"key\":\"1FAAB\"},{\"char\":\"🔌\",\"name\":\"electric plug\",\"key\":\"1F50C\"},{\"char\":\"💻\",\"name\":\"laptop\",\"key\":\"1F4BB\"},{\"char\":\"🖥️\",\"name\":\"desktop computer\",\"key\":\"1F5A5\"},{\"char\":\"🖨️\",\"name\":\"printer\",\"key\":\"1F5A8\"},{\"char\":\"⌨️\",\"name\":\"keyboard\",\"key\":\"2328\"},{\"char\":\"🖱️\",\"name\":\"computer mouse\",\"key\":\"1F5B1\"},{\"char\":\"🖲️\",\"name\":\"trackball\",\"key\":\"1F5B2\"},{\"char\":\"💽\",\"name\":\"computer disk\",\"key\":\"1F4BD\"},{\"char\":\"💾\",\"name\":\"floppy disk\",\"key\":\"1F4BE\"},{\"char\":\"💿\",\"name\":\"optical disk\",\"key\":\"1F4BF\"},{\"char\":\"📀\",\"name\":\"dvd\",\"key\":\"1F4C0\"},{\"char\":\"🧮\",\"name\":\"abacus\",\"key\":\"1F9EE\"},{\"char\":\"🎥\",\"name\":\"movie camera\",\"key\":\"1F3A5\"},{\"char\":\"🎞️\",\"name\":\"film frames\",\"key\":\"1F39E\"},{\"char\":\"📽️\",\"name\":\"film projector\",\"key\":\"1F4FD\"},{\"char\":\"🎬\",\"name\":\"clapper board\",\"key\":\"1F3AC\"},{\"char\":\"📺\",\"name\":\"television\",\"key\":\"1F4FA\"},{\"char\":\"📷\",\"name\":\"camera\",\"key\":\"1F4F7\"},{\"char\":\"📸\",\"name\":\"camera with flash\",\"key\":\"1F4F8\"},{\"char\":\"📹\",\"name\":\"video camera\",\"key\":\"1F4F9\"},{\"char\":\"📼\",\"name\":\"videocassette\",\"key\":\"1F4FC\"},{\"char\":\"🔍\",\"name\":\"magnifying glass tilted left\",\"key\":\"1F50D\"},{\"char\":\"🔎\",\"name\":\"magnifying glass tilted right\",\"key\":\"1F50E\"},{\"char\":\"🕯️\",\"name\":\"candle\",\"key\":\"1F56F\"},{\"char\":\"💡\",\"name\":\"light bulb\",\"key\":\"1F4A1\"},{\"char\":\"🔦\",\"name\":\"flashlight\",\"key\":\"1F526\"},{\"char\":\"🏮\",\"name\":\"red paper lantern\",\"key\":\"1F3EE\"},{\"char\":\"🪔\",\"name\":\"diya lamp\",\"key\":\"1FA94\"},{\"char\":\"📔\",\"name\":\"notebook with decorative cover\",\"key\":\"1F4D4\"},{\"char\":\"📕\",\"name\":\"closed book\",\"key\":\"1F4D5\"},{\"char\":\"📖\",\"name\":\"open book\",\"key\":\"1F4D6\"},{\"char\":\"📗\",\"name\":\"green book\",\"key\":\"1F4D7\"},{\"char\":\"📘\",\"name\":\"blue book\",\"key\":\"1F4D8\"},{\"char\":\"📙\",\"name\":\"orange book\",\"key\":\"1F4D9\"},{\"char\":\"📚\",\"name\":\"books\",\"key\":\"1F4DA\"},{\"char\":\"📓\",\"name\":\"notebook\",\"key\":\"1F4D3\"},{\"char\":\"📒\",\"name\":\"ledger\",\"key\":\"1F4D2\"},{\"char\":\"📃\",\"name\":\"page with curl\",\"key\":\"1F4C3\"},{\"char\":\"📜\",\"name\":\"scroll\",\"key\":\"1F4DC\"},{\"char\":\"📄\",\"name\":\"page facing up\",\"key\":\"1F4C4\"},{\"char\":\"📰\",\"name\":\"newspaper\",\"key\":\"1F4F0\"},{\"char\":\"🗞️\",\"name\":\"rolled-up newspaper\",\"key\":\"1F5DE\"},{\"char\":\"📑\",\"name\":\"bookmark tabs\",\"key\":\"1F4D1\"},{\"char\":\"🔖\",\"name\":\"bookmark\",\"key\":\"1F516\"},{\"char\":\"🏷️\",\"name\":\"label\",\"key\":\"1F3F7\"},{\"char\":\"🪙\",\"name\":\"coin\",\"key\":\"1FA99\"},{\"char\":\"💰\",\"name\":\"money bag\",\"key\":\"1F4B0\"},{\"char\":\"🪎\",\"name\":\"treasure chest\",\"key\":\"1FA8E\"},{\"char\":\"💴\",\"name\":\"yen banknote\",\"key\":\"1F4B4\"},{\"char\":\"💵\",\"name\":\"dollar banknote\",\"key\":\"1F4B5\"},{\"char\":\"💶\",\"name\":\"euro banknote\",\"key\":\"1F4B6\"},{\"char\":\"💷\",\"name\":\"pound banknote\",\"key\":\"1F4B7\"},{\"char\":\"💸\",\"name\":\"money with wings\",\"key\":\"1F4B8\"},{\"char\":\"💳\",\"name\":\"credit card\",\"key\":\"1F4B3\"},{\"char\":\"🧾\",\"name\":\"receipt\",\"key\":\"1F9FE\"},{\"char\":\"💹\",\"name\":\"chart increasing with yen\",\"key\":\"1F4B9\"},{\"char\":\"✉️\",\"name\":\"envelope\",\"key\":\"2709\"},{\"char\":\"📧\",\"name\":\"e-mail\",\"key\":\"1F4E7\"},{\"char\":\"📨\",\"name\":\"incoming envelope\",\"key\":\"1F4E8\"},{\"char\":\"📩\",\"name\":\"envelope with arrow\",\"key\":\"1F4E9\"},{\"char\":\"📤\",\"name\":\"outbox tray\",\"key\":\"1F4E4\"},{\"char\":\"📥\",\"name\":\"inbox tray\",\"key\":\"1F4E5\"},{\"char\":\"📦\",\"name\":\"package\",\"key\":\"1F4E6\"},{\"char\":\"📫\",\"name\":\"closed mailbox with raised flag\",\"key\":\"1F4EB\"},{\"char\":\"📪\",\"name\":\"closed mailbox with lowered flag\",\"key\":\"1F4EA\"},{\"char\":\"📬\",\"name\":\"open mailbox with raised flag\",\"key\":\"1F4EC\"},{\"char\":\"📭\",\"name\":\"open mailbox with lowered flag\",\"key\":\"1F4ED\"},{\"char\":\"📮\",\"name\":\"postbox\",\"key\":\"1F4EE\"},{\"char\":\"🗳️\",\"name\":\"ballot box with ballot\",\"key\":\"1F5F3\"},{\"char\":\"✏️\",\"name\":\"pencil\",\"key\":\"270F\"},{\"char\":\"✒️\",\"name\":\"black nib\",\"key\":\"2712\"},{\"char\":\"🖋️\",\"name\":\"fountain pen\",\"key\":\"1F58B\"},{\"char\":\"🖊️\",\"name\":\"pen\",\"key\":\"1F58A\"},{\"char\":\"🖌️\",\"name\":\"paintbrush\",\"key\":\"1F58C\"},{\"char\":\"🖍️\",\"name\":\"crayon\",\"key\":\"1F58D\"},{\"char\":\"📝\",\"name\":\"memo\",\"key\":\"1F4DD\"},{\"char\":\"💼\",\"name\":\"briefcase\",\"key\":\"1F4BC\"},{\"char\":\"📁\",\"name\":\"file folder\",\"key\":\"1F4C1\"},{\"char\":\"📂\",\"name\":\"open file folder\",\"key\":\"1F4C2\"},{\"char\":\"🗂️\",\"name\":\"card index dividers\",\"key\":\"1F5C2\"},{\"char\":\"📅\",\"name\":\"calendar\",\"key\":\"1F4C5\"},{\"char\":\"📆\",\"name\":\"tear-off calendar\",\"key\":\"1F4C6\"},{\"char\":\"🗒️\",\"name\":\"spiral notepad\",\"key\":\"1F5D2\"},{\"char\":\"🗓️\",\"name\":\"spiral calendar\",\"key\":\"1F5D3\"},{\"char\":\"📇\",\"name\":\"card index\",\"key\":\"1F4C7\"},{\"char\":\"📈\",\"name\":\"chart increasing\",\"key\":\"1F4C8\"},{\"char\":\"📉\",\"name\":\"chart decreasing\",\"key\":\"1F4C9\"},{\"char\":\"📊\",\"name\":\"bar chart\",\"key\":\"1F4CA\"},{\"char\":\"📋\",\"name\":\"clipboard\",\"key\":\"1F4CB\"},{\"char\":\"📌\",\"name\":\"pushpin\",\"key\":\"1F4CC\"},{\"char\":\"📍\",\"name\":\"round pushpin\",\"key\":\"1F4CD\"},{\"char\":\"📎\",\"name\":\"paperclip\",\"key\":\"1F4CE\"},{\"char\":\"🖇️\",\"name\":\"linked paperclips\",\"key\":\"1F587\"},{\"char\":\"📏\",\"name\":\"straight ruler\",\"key\":\"1F4CF\"},{\"char\":\"📐\",\"name\":\"triangular ruler\",\"key\":\"1F4D0\"},{\"char\":\"✂️\",\"name\":\"scissors\",\"key\":\"2702\"},{\"char\":\"🗃️\",\"name\":\"card file box\",\"key\":\"1F5C3\"},{\"char\":\"🗄️\",\"name\":\"file cabinet\",\"key\":\"1F5C4\"},{\"char\":\"🗑️\",\"name\":\"wastebasket\",\"key\":\"1F5D1\"},{\"char\":\"🔒\",\"name\":\"locked\",\"key\":\"1F512\"},{\"char\":\"🔓\",\"name\":\"unlocked\",\"key\":\"1F513\"},{\"char\":\"🔏\",\"name\":\"locked with pen\",\"key\":\"1F50F\"},{\"char\":\"🔐\",\"name\":\"locked with key\",\"key\":\"1F510\"},{\"char\":\"🔑\",\"name\":\"key\",\"key\":\"1F511\"},{\"char\":\"🗝️\",\"name\":\"old key\",\"key\":\"1F5DD\"},{\"char\":\"🔨\",\"name\":\"hammer\",\"key\":\"1F528\"},{\"char\":\"🪓\",\"name\":\"axe\",\"key\":\"1FA93\"},{\"char\":\"⛏️\",\"name\":\"pick\",\"key\":\"26CF\"},{\"char\":\"⚒️\",\"name\":\"hammer and pick\",\"key\":\"2692\"},{\"char\":\"🛠️\",\"name\":\"hammer and wrench\",\"key\":\"1F6E0\"},{\"char\":\"🗡️\",\"name\":\"dagger\",\"key\":\"1F5E1\"},{\"char\":\"⚔️\",\"name\":\"crossed swords\",\"key\":\"2694\"},{\"char\":\"💣\",\"name\":\"bomb\",\"key\":\"1F4A3\"},{\"char\":\"🪃\",\"name\":\"boomerang\",\"key\":\"1FA83\"},{\"char\":\"🏹\",\"name\":\"bow and arrow\",\"key\":\"1F3F9\"},{\"char\":\"🛡️\",\"name\":\"shield\",\"key\":\"1F6E1\"},{\"char\":\"🪚\",\"name\":\"carpentry saw\",\"key\":\"1FA9A\"},{\"char\":\"🔧\",\"name\":\"wrench\",\"key\":\"1F527\"},{\"char\":\"🪛\",\"name\":\"screwdriver\",\"key\":\"1FA9B\"},{\"char\":\"🔩\",\"name\":\"nut and bolt\",\"key\":\"1F529\"},{\"char\":\"⚙️\",\"name\":\"gear\",\"key\":\"2699\"},{\"char\":\"🗜️\",\"name\":\"clamp\",\"key\":\"1F5DC\"},{\"char\":\"⚖️\",\"name\":\"balance scale\",\"key\":\"2696\"},{\"char\":\"🦯\",\"name\":\"white cane\",\"key\":\"1F9AF\"},{\"char\":\"🔗\",\"name\":\"link\",\"key\":\"1F517\"},{\"char\":\"⛓️‍💥\",\"name\":\"broken chain\",\"key\":\"26D3-FE0F-200D-1F4A5\"},{\"char\":\"⛓️\",\"name\":\"chains\",\"key\":\"26D3\"},{\"char\":\"🪝\",\"name\":\"hook\",\"key\":\"1FA9D\"},{\"char\":\"🧰\",\"name\":\"toolbox\",\"key\":\"1F9F0\"},{\"char\":\"🧲\",\"name\":\"magnet\",\"key\":\"1F9F2\"},{\"char\":\"🪜\",\"name\":\"ladder\",\"key\":\"1FA9C\"},{\"char\":\"🪏\",\"name\":\"shovel\",\"key\":\"1FA8F\"},{\"char\":\"⚗️\",\"name\":\"alembic\",\"key\":\"2697\"},{\"char\":\"🧪\",\"name\":\"test tube\",\"key\":\"1F9EA\"},{\"char\":\"🧫\",\"name\":\"petri dish\",\"key\":\"1F9EB\"},{\"char\":\"🧬\",\"name\":\"dna\",\"key\":\"1F9EC\"},{\"char\":\"🔬\",\"name\":\"microscope\",\"key\":\"1F52C\"},{\"char\":\"🔭\",\"name\":\"telescope\",\"key\":\"1F52D\"},{\"char\":\"📡\",\"name\":\"satellite antenna\",\"key\":\"1F4E1\"},{\"char\":\"💉\",\"name\":\"syringe\",\"key\":\"1F489\"},{\"char\":\"🩸\",\"name\":\"drop of blood\",\"key\":\"1FA78\"},{\"char\":\"💊\",\"name\":\"pill\",\"key\":\"1F48A\"},{\"char\":\"🩹\",\"name\":\"adhesive bandage\",\"key\":\"1FA79\"},{\"char\":\"🩼\",\"name\":\"crutch\",\"key\":\"1FA7C\"},{\"char\":\"🩺\",\"name\":\"stethoscope\",\"key\":\"1FA7A\"},{\"char\":\"🩻\",\"name\":\"x-ray\",\"key\":\"1FA7B\"},{\"char\":\"🚪\",\"name\":\"door\",\"key\":\"1F6AA\"},{\"char\":\"🛗\",\"name\":\"elevator\",\"key\":\"1F6D7\"},{\"char\":\"🪞\",\"name\":\"mirror\",\"key\":\"1FA9E\"},{\"char\":\"🪟\",\"name\":\"window\",\"key\":\"1FA9F\"},{\"char\":\"🛏️\",\"name\":\"bed\",\"key\":\"1F6CF\"},{\"char\":\"🛋️\",\"name\":\"couch and lamp\",\"key\":\"1F6CB\"},{\"char\":\"🪑\",\"name\":\"chair\",\"key\":\"1FA91\"},{\"char\":\"🚽\",\"name\":\"toilet\",\"key\":\"1F6BD\"},{\"char\":\"🪠\",\"name\":\"plunger\",\"key\":\"1FAA0\"},{\"char\":\"🚿\",\"name\":\"shower\",\"key\":\"1F6BF\"},{\"char\":\"🛁\",\"name\":\"bathtub\",\"key\":\"1F6C1\"},{\"char\":\"🪤\",\"name\":\"mouse trap\",\"key\":\"1FAA4\"},{\"char\":\"🪒\",\"name\":\"razor\",\"key\":\"1FA92\"},{\"char\":\"🧴\",\"name\":\"lotion bottle\",\"key\":\"1F9F4\"},{\"char\":\"🧷\",\"name\":\"safety pin\",\"key\":\"1F9F7\"},{\"char\":\"🧹\",\"name\":\"broom\",\"key\":\"1F9F9\"},{\"char\":\"🧺\",\"name\":\"basket\",\"key\":\"1F9FA\"},{\"char\":\"🧻\",\"name\":\"roll of paper\",\"key\":\"1F9FB\"},{\"char\":\"🪣\",\"name\":\"bucket\",\"key\":\"1FAA3\"},{\"char\":\"🧼\",\"name\":\"soap\",\"key\":\"1F9FC\"},{\"char\":\"🫧\",\"name\":\"bubbles\",\"key\":\"1FAE7\"},{\"char\":\"🪥\",\"name\":\"toothbrush\",\"key\":\"1FAA5\"},{\"char\":\"🧽\",\"name\":\"sponge\",\"key\":\"1F9FD\"},{\"char\":\"🧯\",\"name\":\"fire extinguisher\",\"key\":\"1F9EF\"},{\"char\":\"🛒\",\"name\":\"shopping cart\",\"key\":\"1F6D2\"},{\"char\":\"🚬\",\"name\":\"cigarette\",\"key\":\"1F6AC\"},{\"char\":\"⚰️\",\"name\":\"coffin\",\"key\":\"26B0\"},{\"char\":\"🪦\",\"name\":\"headstone\",\"key\":\"1FAA6\"},{\"char\":\"⚱️\",\"name\":\"funeral urn\",\"key\":\"26B1\"},{\"char\":\"🧿\",\"name\":\"nazar amulet\",\"key\":\"1F9FF\"},{\"char\":\"🪬\",\"name\":\"hamsa\",\"key\":\"1FAAC\"},{\"char\":\"🗿\",\"name\":\"moai\",\"key\":\"1F5FF\"},{\"char\":\"🪧\",\"name\":\"placard\",\"key\":\"1FAA7\"},{\"char\":\"🪪\",\"name\":\"identification card\",\"key\":\"1FAAA\"}]},\n {\"id\":\"symbols\",\"label\":\"Symbols\",\"emojis\":[{\"char\":\"🏧\",\"name\":\"ATM sign\",\"key\":\"1F3E7\"},{\"char\":\"🚮\",\"name\":\"litter in bin sign\",\"key\":\"1F6AE\"},{\"char\":\"🚰\",\"name\":\"potable water\",\"key\":\"1F6B0\"},{\"char\":\"♿\",\"name\":\"wheelchair symbol\",\"key\":\"267F\"},{\"char\":\"🚹\",\"name\":\"men’s room\",\"key\":\"1F6B9\"},{\"char\":\"🚺\",\"name\":\"women’s room\",\"key\":\"1F6BA\"},{\"char\":\"🚻\",\"name\":\"restroom\",\"key\":\"1F6BB\"},{\"char\":\"🚼\",\"name\":\"baby symbol\",\"key\":\"1F6BC\"},{\"char\":\"🚾\",\"name\":\"water closet\",\"key\":\"1F6BE\"},{\"char\":\"🛂\",\"name\":\"passport control\",\"key\":\"1F6C2\"},{\"char\":\"🛃\",\"name\":\"customs\",\"key\":\"1F6C3\"},{\"char\":\"🛄\",\"name\":\"baggage claim\",\"key\":\"1F6C4\"},{\"char\":\"🛅\",\"name\":\"left luggage\",\"key\":\"1F6C5\"},{\"char\":\"⚠️\",\"name\":\"warning\",\"key\":\"26A0\"},{\"char\":\"🚸\",\"name\":\"children crossing\",\"key\":\"1F6B8\"},{\"char\":\"⛔\",\"name\":\"no entry\",\"key\":\"26D4\"},{\"char\":\"🚫\",\"name\":\"prohibited\",\"key\":\"1F6AB\"},{\"char\":\"🚳\",\"name\":\"no bicycles\",\"key\":\"1F6B3\"},{\"char\":\"🚭\",\"name\":\"no smoking\",\"key\":\"1F6AD\"},{\"char\":\"🚯\",\"name\":\"no littering\",\"key\":\"1F6AF\"},{\"char\":\"🚱\",\"name\":\"non-potable water\",\"key\":\"1F6B1\"},{\"char\":\"🚷\",\"name\":\"no pedestrians\",\"key\":\"1F6B7\"},{\"char\":\"📵\",\"name\":\"no mobile phones\",\"key\":\"1F4F5\"},{\"char\":\"🔞\",\"name\":\"no one under eighteen\",\"key\":\"1F51E\"},{\"char\":\"☢️\",\"name\":\"radioactive\",\"key\":\"2622\"},{\"char\":\"☣️\",\"name\":\"biohazard\",\"key\":\"2623\"},{\"char\":\"⬆️\",\"name\":\"up arrow\",\"key\":\"2B06\"},{\"char\":\"↗️\",\"name\":\"up-right arrow\",\"key\":\"2197\"},{\"char\":\"➡️\",\"name\":\"right arrow\",\"key\":\"27A1\"},{\"char\":\"↘️\",\"name\":\"down-right arrow\",\"key\":\"2198\"},{\"char\":\"⬇️\",\"name\":\"down arrow\",\"key\":\"2B07\"},{\"char\":\"↙️\",\"name\":\"down-left arrow\",\"key\":\"2199\"},{\"char\":\"⬅️\",\"name\":\"left arrow\",\"key\":\"2B05\"},{\"char\":\"↖️\",\"name\":\"up-left arrow\",\"key\":\"2196\"},{\"char\":\"↕️\",\"name\":\"up-down arrow\",\"key\":\"2195\"},{\"char\":\"↔️\",\"name\":\"left-right arrow\",\"key\":\"2194\"},{\"char\":\"↩️\",\"name\":\"right arrow curving left\",\"key\":\"21A9\"},{\"char\":\"↪️\",\"name\":\"left arrow curving right\",\"key\":\"21AA\"},{\"char\":\"⤴️\",\"name\":\"right arrow curving up\",\"key\":\"2934\"},{\"char\":\"⤵️\",\"name\":\"right arrow curving down\",\"key\":\"2935\"},{\"char\":\"🔃\",\"name\":\"clockwise vertical arrows\",\"key\":\"1F503\"},{\"char\":\"🔄\",\"name\":\"counterclockwise arrows button\",\"key\":\"1F504\"},{\"char\":\"🔙\",\"name\":\"BACK arrow\",\"key\":\"1F519\"},{\"char\":\"🔚\",\"name\":\"END arrow\",\"key\":\"1F51A\"},{\"char\":\"🔛\",\"name\":\"ON! arrow\",\"key\":\"1F51B\"},{\"char\":\"🔜\",\"name\":\"SOON arrow\",\"key\":\"1F51C\"},{\"char\":\"🔝\",\"name\":\"TOP arrow\",\"key\":\"1F51D\"},{\"char\":\"🛐\",\"name\":\"place of worship\",\"key\":\"1F6D0\"},{\"char\":\"⚛️\",\"name\":\"atom symbol\",\"key\":\"269B\"},{\"char\":\"🕉️\",\"name\":\"om\",\"key\":\"1F549\"},{\"char\":\"✡️\",\"name\":\"star of David\",\"key\":\"2721\"},{\"char\":\"☸️\",\"name\":\"wheel of dharma\",\"key\":\"2638\"},{\"char\":\"☯️\",\"name\":\"yin yang\",\"key\":\"262F\"},{\"char\":\"✝️\",\"name\":\"latin cross\",\"key\":\"271D\"},{\"char\":\"☦️\",\"name\":\"orthodox cross\",\"key\":\"2626\"},{\"char\":\"☪️\",\"name\":\"star and crescent\",\"key\":\"262A\"},{\"char\":\"☮️\",\"name\":\"peace symbol\",\"key\":\"262E\"},{\"char\":\"🕎\",\"name\":\"menorah\",\"key\":\"1F54E\"},{\"char\":\"🔯\",\"name\":\"dotted six-pointed star\",\"key\":\"1F52F\"},{\"char\":\"🪯\",\"name\":\"khanda\",\"key\":\"1FAAF\"},{\"char\":\"♈\",\"name\":\"Aries\",\"key\":\"2648\"},{\"char\":\"♉\",\"name\":\"Taurus\",\"key\":\"2649\"},{\"char\":\"♊\",\"name\":\"Gemini\",\"key\":\"264A\"},{\"char\":\"♋\",\"name\":\"Cancer\",\"key\":\"264B\"},{\"char\":\"♌\",\"name\":\"Leo\",\"key\":\"264C\"},{\"char\":\"♍\",\"name\":\"Virgo\",\"key\":\"264D\"},{\"char\":\"♎\",\"name\":\"Libra\",\"key\":\"264E\"},{\"char\":\"♏\",\"name\":\"Scorpio\",\"key\":\"264F\"},{\"char\":\"♐\",\"name\":\"Sagittarius\",\"key\":\"2650\"},{\"char\":\"♑\",\"name\":\"Capricorn\",\"key\":\"2651\"},{\"char\":\"♒\",\"name\":\"Aquarius\",\"key\":\"2652\"},{\"char\":\"♓\",\"name\":\"Pisces\",\"key\":\"2653\"},{\"char\":\"⛎\",\"name\":\"Ophiuchus\",\"key\":\"26CE\"},{\"char\":\"🔀\",\"name\":\"shuffle tracks button\",\"key\":\"1F500\"},{\"char\":\"🔁\",\"name\":\"repeat button\",\"key\":\"1F501\"},{\"char\":\"🔂\",\"name\":\"repeat single button\",\"key\":\"1F502\"},{\"char\":\"▶️\",\"name\":\"play button\",\"key\":\"25B6\"},{\"char\":\"⏩\",\"name\":\"fast-forward button\",\"key\":\"23E9\"},{\"char\":\"⏭️\",\"name\":\"next track button\",\"key\":\"23ED\"},{\"char\":\"⏯️\",\"name\":\"play or pause button\",\"key\":\"23EF\"},{\"char\":\"◀️\",\"name\":\"reverse button\",\"key\":\"25C0\"},{\"char\":\"⏪\",\"name\":\"fast reverse button\",\"key\":\"23EA\"},{\"char\":\"⏮️\",\"name\":\"last track button\",\"key\":\"23EE\"},{\"char\":\"🔼\",\"name\":\"upwards button\",\"key\":\"1F53C\"},{\"char\":\"⏫\",\"name\":\"fast up button\",\"key\":\"23EB\"},{\"char\":\"🔽\",\"name\":\"downwards button\",\"key\":\"1F53D\"},{\"char\":\"⏬\",\"name\":\"fast down button\",\"key\":\"23EC\"},{\"char\":\"⏸️\",\"name\":\"pause button\",\"key\":\"23F8\"},{\"char\":\"⏹️\",\"name\":\"stop button\",\"key\":\"23F9\"},{\"char\":\"⏺️\",\"name\":\"record button\",\"key\":\"23FA\"},{\"char\":\"⏏️\",\"name\":\"eject button\",\"key\":\"23CF\"},{\"char\":\"🎦\",\"name\":\"cinema\",\"key\":\"1F3A6\"},{\"char\":\"🔅\",\"name\":\"dim button\",\"key\":\"1F505\"},{\"char\":\"🔆\",\"name\":\"bright button\",\"key\":\"1F506\"},{\"char\":\"📶\",\"name\":\"antenna bars\",\"key\":\"1F4F6\"},{\"char\":\"🛜\",\"name\":\"wireless\",\"key\":\"1F6DC\"},{\"char\":\"📳\",\"name\":\"vibration mode\",\"key\":\"1F4F3\"},{\"char\":\"📴\",\"name\":\"mobile phone off\",\"key\":\"1F4F4\"},{\"char\":\"♀️\",\"name\":\"female sign\",\"key\":\"2640\"},{\"char\":\"♂️\",\"name\":\"male sign\",\"key\":\"2642\"},{\"char\":\"⚧️\",\"name\":\"transgender symbol\",\"key\":\"26A7\"},{\"char\":\"✖️\",\"name\":\"multiply\",\"key\":\"2716\"},{\"char\":\"➕\",\"name\":\"plus\",\"key\":\"2795\"},{\"char\":\"➖\",\"name\":\"minus\",\"key\":\"2796\"},{\"char\":\"➗\",\"name\":\"divide\",\"key\":\"2797\"},{\"char\":\"🟰\",\"name\":\"heavy equals sign\",\"key\":\"1F7F0\"},{\"char\":\"♾️\",\"name\":\"infinity\",\"key\":\"267E\"},{\"char\":\"‼️\",\"name\":\"double exclamation mark\",\"key\":\"203C\"},{\"char\":\"⁉️\",\"name\":\"exclamation question mark\",\"key\":\"2049\"},{\"char\":\"❓\",\"name\":\"red question mark\",\"key\":\"2753\"},{\"char\":\"❔\",\"name\":\"white question mark\",\"key\":\"2754\"},{\"char\":\"❕\",\"name\":\"white exclamation mark\",\"key\":\"2755\"},{\"char\":\"❗\",\"name\":\"red exclamation mark\",\"key\":\"2757\"},{\"char\":\"〰️\",\"name\":\"wavy dash\",\"key\":\"3030\"},{\"char\":\"💱\",\"name\":\"currency exchange\",\"key\":\"1F4B1\"},{\"char\":\"💲\",\"name\":\"heavy dollar sign\",\"key\":\"1F4B2\"},{\"char\":\"⚕️\",\"name\":\"medical symbol\",\"key\":\"2695\"},{\"char\":\"♻️\",\"name\":\"recycling symbol\",\"key\":\"267B\"},{\"char\":\"⚜️\",\"name\":\"fleur-de-lis\",\"key\":\"269C\"},{\"char\":\"🔱\",\"name\":\"trident emblem\",\"key\":\"1F531\"},{\"char\":\"📛\",\"name\":\"name badge\",\"key\":\"1F4DB\"},{\"char\":\"🔰\",\"name\":\"Japanese symbol for beginner\",\"key\":\"1F530\"},{\"char\":\"⭕\",\"name\":\"hollow red circle\",\"key\":\"2B55\"},{\"char\":\"✅\",\"name\":\"check mark button\",\"key\":\"2705\"},{\"char\":\"☑️\",\"name\":\"check box with check\",\"key\":\"2611\"},{\"char\":\"✔️\",\"name\":\"check mark\",\"key\":\"2714\"},{\"char\":\"❌\",\"name\":\"cross mark\",\"key\":\"274C\"},{\"char\":\"❎\",\"name\":\"cross mark button\",\"key\":\"274E\"},{\"char\":\"➰\",\"name\":\"curly loop\",\"key\":\"27B0\"},{\"char\":\"➿\",\"name\":\"double curly loop\",\"key\":\"27BF\"},{\"char\":\"〽️\",\"name\":\"part alternation mark\",\"key\":\"303D\"},{\"char\":\"✳️\",\"name\":\"eight-spoked asterisk\",\"key\":\"2733\"},{\"char\":\"✴️\",\"name\":\"eight-pointed star\",\"key\":\"2734\"},{\"char\":\"❇️\",\"name\":\"sparkle\",\"key\":\"2747\"},{\"char\":\"©️\",\"name\":\"copyright\",\"key\":\"00A9\"},{\"char\":\"®️\",\"name\":\"registered\",\"key\":\"00AE\"},{\"char\":\"™️\",\"name\":\"trade mark\",\"key\":\"2122\"},{\"char\":\"🫟\",\"name\":\"splatter\",\"key\":\"1FADF\"},{\"char\":\"#️⃣\",\"name\":\"keycap #\",\"key\":\"0023-FE0F-20E3\"},{\"char\":\"*️⃣\",\"name\":\"keycap *\",\"key\":\"002A-FE0F-20E3\"},{\"char\":\"0️⃣\",\"name\":\"keycap 0\",\"key\":\"0030-FE0F-20E3\"},{\"char\":\"1️⃣\",\"name\":\"keycap 1\",\"key\":\"0031-FE0F-20E3\"},{\"char\":\"2️⃣\",\"name\":\"keycap 2\",\"key\":\"0032-FE0F-20E3\"},{\"char\":\"3️⃣\",\"name\":\"keycap 3\",\"key\":\"0033-FE0F-20E3\"},{\"char\":\"4️⃣\",\"name\":\"keycap 4\",\"key\":\"0034-FE0F-20E3\"},{\"char\":\"5️⃣\",\"name\":\"keycap 5\",\"key\":\"0035-FE0F-20E3\"},{\"char\":\"6️⃣\",\"name\":\"keycap 6\",\"key\":\"0036-FE0F-20E3\"},{\"char\":\"7️⃣\",\"name\":\"keycap 7\",\"key\":\"0037-FE0F-20E3\"},{\"char\":\"8️⃣\",\"name\":\"keycap 8\",\"key\":\"0038-FE0F-20E3\"},{\"char\":\"9️⃣\",\"name\":\"keycap 9\",\"key\":\"0039-FE0F-20E3\"},{\"char\":\"🔟\",\"name\":\"keycap 10\",\"key\":\"1F51F\"},{\"char\":\"🔠\",\"name\":\"input latin uppercase\",\"key\":\"1F520\"},{\"char\":\"🔡\",\"name\":\"input latin lowercase\",\"key\":\"1F521\"},{\"char\":\"🔢\",\"name\":\"input numbers\",\"key\":\"1F522\"},{\"char\":\"🔣\",\"name\":\"input symbols\",\"key\":\"1F523\"},{\"char\":\"🔤\",\"name\":\"input latin letters\",\"key\":\"1F524\"},{\"char\":\"🅰️\",\"name\":\"A button (blood type)\",\"key\":\"1F170\"},{\"char\":\"🆎\",\"name\":\"AB button (blood type)\",\"key\":\"1F18E\"},{\"char\":\"🅱️\",\"name\":\"B button (blood type)\",\"key\":\"1F171\"},{\"char\":\"🆑\",\"name\":\"CL button\",\"key\":\"1F191\"},{\"char\":\"🆒\",\"name\":\"COOL button\",\"key\":\"1F192\"},{\"char\":\"🆓\",\"name\":\"FREE button\",\"key\":\"1F193\"},{\"char\":\"ℹ️\",\"name\":\"information\",\"key\":\"2139\"},{\"char\":\"🆔\",\"name\":\"ID button\",\"key\":\"1F194\"},{\"char\":\"Ⓜ️\",\"name\":\"circled M\",\"key\":\"24C2\"},{\"char\":\"🆕\",\"name\":\"NEW button\",\"key\":\"1F195\"},{\"char\":\"🆖\",\"name\":\"NG button\",\"key\":\"1F196\"},{\"char\":\"🅾️\",\"name\":\"O button (blood type)\",\"key\":\"1F17E\"},{\"char\":\"🆗\",\"name\":\"OK button\",\"key\":\"1F197\"},{\"char\":\"🅿️\",\"name\":\"P button\",\"key\":\"1F17F\"},{\"char\":\"🆘\",\"name\":\"SOS button\",\"key\":\"1F198\"},{\"char\":\"🆙\",\"name\":\"UP! button\",\"key\":\"1F199\"},{\"char\":\"🆚\",\"name\":\"VS button\",\"key\":\"1F19A\"},{\"char\":\"🈁\",\"name\":\"Japanese “here” button\",\"key\":\"1F201\"},{\"char\":\"🈂️\",\"name\":\"Japanese “service charge” button\",\"key\":\"1F202\"},{\"char\":\"🈷️\",\"name\":\"Japanese “monthly amount” button\",\"key\":\"1F237\"},{\"char\":\"🈶\",\"name\":\"Japanese “not free of charge” button\",\"key\":\"1F236\"},{\"char\":\"🈯\",\"name\":\"Japanese “reserved” button\",\"key\":\"1F22F\"},{\"char\":\"🉐\",\"name\":\"Japanese “bargain” button\",\"key\":\"1F250\"},{\"char\":\"🈹\",\"name\":\"Japanese “discount” button\",\"key\":\"1F239\"},{\"char\":\"🈚\",\"name\":\"Japanese “free of charge” button\",\"key\":\"1F21A\"},{\"char\":\"🈲\",\"name\":\"Japanese “prohibited” button\",\"key\":\"1F232\"},{\"char\":\"🉑\",\"name\":\"Japanese “acceptable” button\",\"key\":\"1F251\"},{\"char\":\"🈸\",\"name\":\"Japanese “application” button\",\"key\":\"1F238\"},{\"char\":\"🈴\",\"name\":\"Japanese “passing grade” button\",\"key\":\"1F234\"},{\"char\":\"🈳\",\"name\":\"Japanese “vacancy” button\",\"key\":\"1F233\"},{\"char\":\"㊗️\",\"name\":\"Japanese “congratulations” button\",\"key\":\"3297\"},{\"char\":\"㊙️\",\"name\":\"Japanese “secret” button\",\"key\":\"3299\"},{\"char\":\"🈺\",\"name\":\"Japanese “open for business” button\",\"key\":\"1F23A\"},{\"char\":\"🈵\",\"name\":\"Japanese “no vacancy” button\",\"key\":\"1F235\"},{\"char\":\"🔴\",\"name\":\"red circle\",\"key\":\"1F534\"},{\"char\":\"🟠\",\"name\":\"orange circle\",\"key\":\"1F7E0\"},{\"char\":\"🟡\",\"name\":\"yellow circle\",\"key\":\"1F7E1\"},{\"char\":\"🟢\",\"name\":\"green circle\",\"key\":\"1F7E2\"},{\"char\":\"🔵\",\"name\":\"blue circle\",\"key\":\"1F535\"},{\"char\":\"🟣\",\"name\":\"purple circle\",\"key\":\"1F7E3\"},{\"char\":\"🟤\",\"name\":\"brown circle\",\"key\":\"1F7E4\"},{\"char\":\"⚫\",\"name\":\"black circle\",\"key\":\"26AB\"},{\"char\":\"⚪\",\"name\":\"white circle\",\"key\":\"26AA\"},{\"char\":\"🟥\",\"name\":\"red square\",\"key\":\"1F7E5\"},{\"char\":\"🟧\",\"name\":\"orange square\",\"key\":\"1F7E7\"},{\"char\":\"🟨\",\"name\":\"yellow square\",\"key\":\"1F7E8\"},{\"char\":\"🟩\",\"name\":\"green square\",\"key\":\"1F7E9\"},{\"char\":\"🟦\",\"name\":\"blue square\",\"key\":\"1F7E6\"},{\"char\":\"🟪\",\"name\":\"purple square\",\"key\":\"1F7EA\"},{\"char\":\"🟫\",\"name\":\"brown square\",\"key\":\"1F7EB\"},{\"char\":\"⬛\",\"name\":\"black large square\",\"key\":\"2B1B\"},{\"char\":\"⬜\",\"name\":\"white large square\",\"key\":\"2B1C\"},{\"char\":\"◼️\",\"name\":\"black medium square\",\"key\":\"25FC\"},{\"char\":\"◻️\",\"name\":\"white medium square\",\"key\":\"25FB\"},{\"char\":\"◾\",\"name\":\"black medium-small square\",\"key\":\"25FE\"},{\"char\":\"◽\",\"name\":\"white medium-small square\",\"key\":\"25FD\"},{\"char\":\"▪️\",\"name\":\"black small square\",\"key\":\"25AA\"},{\"char\":\"▫️\",\"name\":\"white small square\",\"key\":\"25AB\"},{\"char\":\"🔶\",\"name\":\"large orange diamond\",\"key\":\"1F536\"},{\"char\":\"🔷\",\"name\":\"large blue diamond\",\"key\":\"1F537\"},{\"char\":\"🔸\",\"name\":\"small orange diamond\",\"key\":\"1F538\"},{\"char\":\"🔹\",\"name\":\"small blue diamond\",\"key\":\"1F539\"},{\"char\":\"🔺\",\"name\":\"red triangle pointed up\",\"key\":\"1F53A\"},{\"char\":\"🔻\",\"name\":\"red triangle pointed down\",\"key\":\"1F53B\"},{\"char\":\"💠\",\"name\":\"diamond with a dot\",\"key\":\"1F4A0\"},{\"char\":\"🔘\",\"name\":\"radio button\",\"key\":\"1F518\"},{\"char\":\"🔳\",\"name\":\"white square button\",\"key\":\"1F533\"},{\"char\":\"🔲\",\"name\":\"black square button\",\"key\":\"1F532\"}]},\n {\"id\":\"flags\",\"label\":\"Flags\",\"emojis\":[{\"char\":\"🏁\",\"name\":\"chequered flag\",\"key\":\"1F3C1\"},{\"char\":\"🚩\",\"name\":\"triangular flag\",\"key\":\"1F6A9\"},{\"char\":\"🎌\",\"name\":\"crossed flags\",\"key\":\"1F38C\"},{\"char\":\"🏴\",\"name\":\"black flag\",\"key\":\"1F3F4\"},{\"char\":\"🏳️\",\"name\":\"white flag\",\"key\":\"1F3F3\"},{\"char\":\"🏳️‍🌈\",\"name\":\"rainbow flag\",\"key\":\"1F3F3-FE0F-200D-1F308\"},{\"char\":\"🏳️‍⚧️\",\"name\":\"transgender flag\",\"key\":\"1F3F3-FE0F-200D-26A7-FE0F\"},{\"char\":\"🏴‍☠️\",\"name\":\"pirate flag\",\"key\":\"1F3F4-200D-2620-FE0F\"},{\"char\":\"🇦🇨\",\"name\":\"flag Ascension Island\",\"key\":\"1F1E6-1F1E8\"},{\"char\":\"🇦🇩\",\"name\":\"flag Andorra\",\"key\":\"1F1E6-1F1E9\"},{\"char\":\"🇦🇪\",\"name\":\"flag United Arab Emirates\",\"key\":\"1F1E6-1F1EA\"},{\"char\":\"🇦🇫\",\"name\":\"flag Afghanistan\",\"key\":\"1F1E6-1F1EB\"},{\"char\":\"🇦🇬\",\"name\":\"flag Antigua & Barbuda\",\"key\":\"1F1E6-1F1EC\"},{\"char\":\"🇦🇮\",\"name\":\"flag Anguilla\",\"key\":\"1F1E6-1F1EE\"},{\"char\":\"🇦🇱\",\"name\":\"flag Albania\",\"key\":\"1F1E6-1F1F1\"},{\"char\":\"🇦🇲\",\"name\":\"flag Armenia\",\"key\":\"1F1E6-1F1F2\"},{\"char\":\"🇦🇴\",\"name\":\"flag Angola\",\"key\":\"1F1E6-1F1F4\"},{\"char\":\"🇦🇶\",\"name\":\"flag Antarctica\",\"key\":\"1F1E6-1F1F6\"},{\"char\":\"🇦🇷\",\"name\":\"flag Argentina\",\"key\":\"1F1E6-1F1F7\"},{\"char\":\"🇦🇸\",\"name\":\"flag American Samoa\",\"key\":\"1F1E6-1F1F8\"},{\"char\":\"🇦🇹\",\"name\":\"flag Austria\",\"key\":\"1F1E6-1F1F9\"},{\"char\":\"🇦🇺\",\"name\":\"flag Australia\",\"key\":\"1F1E6-1F1FA\"},{\"char\":\"🇦🇼\",\"name\":\"flag Aruba\",\"key\":\"1F1E6-1F1FC\"},{\"char\":\"🇦🇽\",\"name\":\"flag Åland Islands\",\"key\":\"1F1E6-1F1FD\"},{\"char\":\"🇦🇿\",\"name\":\"flag Azerbaijan\",\"key\":\"1F1E6-1F1FF\"},{\"char\":\"🇧🇦\",\"name\":\"flag Bosnia & Herzegovina\",\"key\":\"1F1E7-1F1E6\"},{\"char\":\"🇧🇧\",\"name\":\"flag Barbados\",\"key\":\"1F1E7-1F1E7\"},{\"char\":\"🇧🇩\",\"name\":\"flag Bangladesh\",\"key\":\"1F1E7-1F1E9\"},{\"char\":\"🇧🇪\",\"name\":\"flag Belgium\",\"key\":\"1F1E7-1F1EA\"},{\"char\":\"🇧🇫\",\"name\":\"flag Burkina Faso\",\"key\":\"1F1E7-1F1EB\"},{\"char\":\"🇧🇬\",\"name\":\"flag Bulgaria\",\"key\":\"1F1E7-1F1EC\"},{\"char\":\"🇧🇭\",\"name\":\"flag Bahrain\",\"key\":\"1F1E7-1F1ED\"},{\"char\":\"🇧🇮\",\"name\":\"flag Burundi\",\"key\":\"1F1E7-1F1EE\"},{\"char\":\"🇧🇯\",\"name\":\"flag Benin\",\"key\":\"1F1E7-1F1EF\"},{\"char\":\"🇧🇱\",\"name\":\"flag St. Barthélemy\",\"key\":\"1F1E7-1F1F1\"},{\"char\":\"🇧🇲\",\"name\":\"flag Bermuda\",\"key\":\"1F1E7-1F1F2\"},{\"char\":\"🇧🇳\",\"name\":\"flag Brunei\",\"key\":\"1F1E7-1F1F3\"},{\"char\":\"🇧🇴\",\"name\":\"flag Bolivia\",\"key\":\"1F1E7-1F1F4\"},{\"char\":\"🇧🇶\",\"name\":\"flag Caribbean Netherlands\",\"key\":\"1F1E7-1F1F6\"},{\"char\":\"🇧🇷\",\"name\":\"flag Brazil\",\"key\":\"1F1E7-1F1F7\"},{\"char\":\"🇧🇸\",\"name\":\"flag Bahamas\",\"key\":\"1F1E7-1F1F8\"},{\"char\":\"🇧🇹\",\"name\":\"flag Bhutan\",\"key\":\"1F1E7-1F1F9\"},{\"char\":\"🇧🇻\",\"name\":\"flag Bouvet Island\",\"key\":\"1F1E7-1F1FB\"},{\"char\":\"🇧🇼\",\"name\":\"flag Botswana\",\"key\":\"1F1E7-1F1FC\"},{\"char\":\"🇧🇾\",\"name\":\"flag Belarus\",\"key\":\"1F1E7-1F1FE\"},{\"char\":\"🇧🇿\",\"name\":\"flag Belize\",\"key\":\"1F1E7-1F1FF\"},{\"char\":\"🇨🇦\",\"name\":\"flag Canada\",\"key\":\"1F1E8-1F1E6\"},{\"char\":\"🇨🇨\",\"name\":\"flag Cocos (Keeling) Islands\",\"key\":\"1F1E8-1F1E8\"},{\"char\":\"🇨🇩\",\"name\":\"flag Congo - Kinshasa\",\"key\":\"1F1E8-1F1E9\"},{\"char\":\"🇨🇫\",\"name\":\"flag Central African Republic\",\"key\":\"1F1E8-1F1EB\"},{\"char\":\"🇨🇬\",\"name\":\"flag Congo - Brazzaville\",\"key\":\"1F1E8-1F1EC\"},{\"char\":\"🇨🇭\",\"name\":\"flag Switzerland\",\"key\":\"1F1E8-1F1ED\"},{\"char\":\"🇨🇮\",\"name\":\"flag Côte d’Ivoire\",\"key\":\"1F1E8-1F1EE\"},{\"char\":\"🇨🇰\",\"name\":\"flag Cook Islands\",\"key\":\"1F1E8-1F1F0\"},{\"char\":\"🇨🇱\",\"name\":\"flag Chile\",\"key\":\"1F1E8-1F1F1\"},{\"char\":\"🇨🇲\",\"name\":\"flag Cameroon\",\"key\":\"1F1E8-1F1F2\"},{\"char\":\"🇨🇳\",\"name\":\"flag China\",\"key\":\"1F1E8-1F1F3\"},{\"char\":\"🇨🇴\",\"name\":\"flag Colombia\",\"key\":\"1F1E8-1F1F4\"},{\"char\":\"🇨🇵\",\"name\":\"flag Clipperton Island\",\"key\":\"1F1E8-1F1F5\"},{\"char\":\"🇨🇶\",\"name\":\"flag Sark\",\"key\":\"1F1E8-1F1F6\"},{\"char\":\"🇨🇷\",\"name\":\"flag Costa Rica\",\"key\":\"1F1E8-1F1F7\"},{\"char\":\"🇨🇺\",\"name\":\"flag Cuba\",\"key\":\"1F1E8-1F1FA\"},{\"char\":\"🇨🇻\",\"name\":\"flag Cape Verde\",\"key\":\"1F1E8-1F1FB\"},{\"char\":\"🇨🇼\",\"name\":\"flag Curaçao\",\"key\":\"1F1E8-1F1FC\"},{\"char\":\"🇨🇽\",\"name\":\"flag Christmas Island\",\"key\":\"1F1E8-1F1FD\"},{\"char\":\"🇨🇾\",\"name\":\"flag Cyprus\",\"key\":\"1F1E8-1F1FE\"},{\"char\":\"🇨🇿\",\"name\":\"flag Czechia\",\"key\":\"1F1E8-1F1FF\"},{\"char\":\"🇩🇪\",\"name\":\"flag Germany\",\"key\":\"1F1E9-1F1EA\"},{\"char\":\"🇩🇬\",\"name\":\"flag Diego Garcia\",\"key\":\"1F1E9-1F1EC\"},{\"char\":\"🇩🇯\",\"name\":\"flag Djibouti\",\"key\":\"1F1E9-1F1EF\"},{\"char\":\"🇩🇰\",\"name\":\"flag Denmark\",\"key\":\"1F1E9-1F1F0\"},{\"char\":\"🇩🇲\",\"name\":\"flag Dominica\",\"key\":\"1F1E9-1F1F2\"},{\"char\":\"🇩🇴\",\"name\":\"flag Dominican Republic\",\"key\":\"1F1E9-1F1F4\"},{\"char\":\"🇩🇿\",\"name\":\"flag Algeria\",\"key\":\"1F1E9-1F1FF\"},{\"char\":\"🇪🇦\",\"name\":\"flag Ceuta & Melilla\",\"key\":\"1F1EA-1F1E6\"},{\"char\":\"🇪🇨\",\"name\":\"flag Ecuador\",\"key\":\"1F1EA-1F1E8\"},{\"char\":\"🇪🇪\",\"name\":\"flag Estonia\",\"key\":\"1F1EA-1F1EA\"},{\"char\":\"🇪🇬\",\"name\":\"flag Egypt\",\"key\":\"1F1EA-1F1EC\"},{\"char\":\"🇪🇭\",\"name\":\"flag Western Sahara\",\"key\":\"1F1EA-1F1ED\"},{\"char\":\"🇪🇷\",\"name\":\"flag Eritrea\",\"key\":\"1F1EA-1F1F7\"},{\"char\":\"🇪🇸\",\"name\":\"flag Spain\",\"key\":\"1F1EA-1F1F8\"},{\"char\":\"🇪🇹\",\"name\":\"flag Ethiopia\",\"key\":\"1F1EA-1F1F9\"},{\"char\":\"🇪🇺\",\"name\":\"flag European Union\",\"key\":\"1F1EA-1F1FA\"},{\"char\":\"🇫🇮\",\"name\":\"flag Finland\",\"key\":\"1F1EB-1F1EE\"},{\"char\":\"🇫🇯\",\"name\":\"flag Fiji\",\"key\":\"1F1EB-1F1EF\"},{\"char\":\"🇫🇰\",\"name\":\"flag Falkland Islands\",\"key\":\"1F1EB-1F1F0\"},{\"char\":\"🇫🇲\",\"name\":\"flag Micronesia\",\"key\":\"1F1EB-1F1F2\"},{\"char\":\"🇫🇴\",\"name\":\"flag Faroe Islands\",\"key\":\"1F1EB-1F1F4\"},{\"char\":\"🇫🇷\",\"name\":\"flag France\",\"key\":\"1F1EB-1F1F7\"},{\"char\":\"🇬🇦\",\"name\":\"flag Gabon\",\"key\":\"1F1EC-1F1E6\"},{\"char\":\"🇬🇧\",\"name\":\"flag United Kingdom\",\"key\":\"1F1EC-1F1E7\"},{\"char\":\"🇬🇩\",\"name\":\"flag Grenada\",\"key\":\"1F1EC-1F1E9\"},{\"char\":\"🇬🇪\",\"name\":\"flag Georgia\",\"key\":\"1F1EC-1F1EA\"},{\"char\":\"🇬🇫\",\"name\":\"flag French Guiana\",\"key\":\"1F1EC-1F1EB\"},{\"char\":\"🇬🇬\",\"name\":\"flag Guernsey\",\"key\":\"1F1EC-1F1EC\"},{\"char\":\"🇬🇭\",\"name\":\"flag Ghana\",\"key\":\"1F1EC-1F1ED\"},{\"char\":\"🇬🇮\",\"name\":\"flag Gibraltar\",\"key\":\"1F1EC-1F1EE\"},{\"char\":\"🇬🇱\",\"name\":\"flag Greenland\",\"key\":\"1F1EC-1F1F1\"},{\"char\":\"🇬🇲\",\"name\":\"flag Gambia\",\"key\":\"1F1EC-1F1F2\"},{\"char\":\"🇬🇳\",\"name\":\"flag Guinea\",\"key\":\"1F1EC-1F1F3\"},{\"char\":\"🇬🇵\",\"name\":\"flag Guadeloupe\",\"key\":\"1F1EC-1F1F5\"},{\"char\":\"🇬🇶\",\"name\":\"flag Equatorial Guinea\",\"key\":\"1F1EC-1F1F6\"},{\"char\":\"🇬🇷\",\"name\":\"flag Greece\",\"key\":\"1F1EC-1F1F7\"},{\"char\":\"🇬🇸\",\"name\":\"flag South Georgia & South Sandwich Islands\",\"key\":\"1F1EC-1F1F8\"},{\"char\":\"🇬🇹\",\"name\":\"flag Guatemala\",\"key\":\"1F1EC-1F1F9\"},{\"char\":\"🇬🇺\",\"name\":\"flag Guam\",\"key\":\"1F1EC-1F1FA\"},{\"char\":\"🇬🇼\",\"name\":\"flag Guinea-Bissau\",\"key\":\"1F1EC-1F1FC\"},{\"char\":\"🇬🇾\",\"name\":\"flag Guyana\",\"key\":\"1F1EC-1F1FE\"},{\"char\":\"🇭🇰\",\"name\":\"flag Hong Kong SAR China\",\"key\":\"1F1ED-1F1F0\"},{\"char\":\"🇭🇲\",\"name\":\"flag Heard & McDonald Islands\",\"key\":\"1F1ED-1F1F2\"},{\"char\":\"🇭🇳\",\"name\":\"flag Honduras\",\"key\":\"1F1ED-1F1F3\"},{\"char\":\"🇭🇷\",\"name\":\"flag Croatia\",\"key\":\"1F1ED-1F1F7\"},{\"char\":\"🇭🇹\",\"name\":\"flag Haiti\",\"key\":\"1F1ED-1F1F9\"},{\"char\":\"🇭🇺\",\"name\":\"flag Hungary\",\"key\":\"1F1ED-1F1FA\"},{\"char\":\"🇮🇨\",\"name\":\"flag Canary Islands\",\"key\":\"1F1EE-1F1E8\"},{\"char\":\"🇮🇩\",\"name\":\"flag Indonesia\",\"key\":\"1F1EE-1F1E9\"},{\"char\":\"🇮🇪\",\"name\":\"flag Ireland\",\"key\":\"1F1EE-1F1EA\"},{\"char\":\"🇮🇱\",\"name\":\"flag Israel\",\"key\":\"1F1EE-1F1F1\"},{\"char\":\"🇮🇲\",\"name\":\"flag Isle of Man\",\"key\":\"1F1EE-1F1F2\"},{\"char\":\"🇮🇳\",\"name\":\"flag India\",\"key\":\"1F1EE-1F1F3\"},{\"char\":\"🇮🇴\",\"name\":\"flag British Indian Ocean Territory\",\"key\":\"1F1EE-1F1F4\"},{\"char\":\"🇮🇶\",\"name\":\"flag Iraq\",\"key\":\"1F1EE-1F1F6\"},{\"char\":\"🇮🇷\",\"name\":\"flag Iran\",\"key\":\"1F1EE-1F1F7\"},{\"char\":\"🇮🇸\",\"name\":\"flag Iceland\",\"key\":\"1F1EE-1F1F8\"},{\"char\":\"🇮🇹\",\"name\":\"flag Italy\",\"key\":\"1F1EE-1F1F9\"},{\"char\":\"🇯🇪\",\"name\":\"flag Jersey\",\"key\":\"1F1EF-1F1EA\"},{\"char\":\"🇯🇲\",\"name\":\"flag Jamaica\",\"key\":\"1F1EF-1F1F2\"},{\"char\":\"🇯🇴\",\"name\":\"flag Jordan\",\"key\":\"1F1EF-1F1F4\"},{\"char\":\"🇯🇵\",\"name\":\"flag Japan\",\"key\":\"1F1EF-1F1F5\"},{\"char\":\"🇰🇪\",\"name\":\"flag Kenya\",\"key\":\"1F1F0-1F1EA\"},{\"char\":\"🇰🇬\",\"name\":\"flag Kyrgyzstan\",\"key\":\"1F1F0-1F1EC\"},{\"char\":\"🇰🇭\",\"name\":\"flag Cambodia\",\"key\":\"1F1F0-1F1ED\"},{\"char\":\"🇰🇮\",\"name\":\"flag Kiribati\",\"key\":\"1F1F0-1F1EE\"},{\"char\":\"🇰🇲\",\"name\":\"flag Comoros\",\"key\":\"1F1F0-1F1F2\"},{\"char\":\"🇰🇳\",\"name\":\"flag St. Kitts & Nevis\",\"key\":\"1F1F0-1F1F3\"},{\"char\":\"🇰🇵\",\"name\":\"flag North Korea\",\"key\":\"1F1F0-1F1F5\"},{\"char\":\"🇰🇷\",\"name\":\"flag South Korea\",\"key\":\"1F1F0-1F1F7\"},{\"char\":\"🇰🇼\",\"name\":\"flag Kuwait\",\"key\":\"1F1F0-1F1FC\"},{\"char\":\"🇰🇾\",\"name\":\"flag Cayman Islands\",\"key\":\"1F1F0-1F1FE\"},{\"char\":\"🇰🇿\",\"name\":\"flag Kazakhstan\",\"key\":\"1F1F0-1F1FF\"},{\"char\":\"🇱🇦\",\"name\":\"flag Laos\",\"key\":\"1F1F1-1F1E6\"},{\"char\":\"🇱🇧\",\"name\":\"flag Lebanon\",\"key\":\"1F1F1-1F1E7\"},{\"char\":\"🇱🇨\",\"name\":\"flag St. Lucia\",\"key\":\"1F1F1-1F1E8\"},{\"char\":\"🇱🇮\",\"name\":\"flag Liechtenstein\",\"key\":\"1F1F1-1F1EE\"},{\"char\":\"🇱🇰\",\"name\":\"flag Sri Lanka\",\"key\":\"1F1F1-1F1F0\"},{\"char\":\"🇱🇷\",\"name\":\"flag Liberia\",\"key\":\"1F1F1-1F1F7\"},{\"char\":\"🇱🇸\",\"name\":\"flag Lesotho\",\"key\":\"1F1F1-1F1F8\"},{\"char\":\"🇱🇹\",\"name\":\"flag Lithuania\",\"key\":\"1F1F1-1F1F9\"},{\"char\":\"🇱🇺\",\"name\":\"flag Luxembourg\",\"key\":\"1F1F1-1F1FA\"},{\"char\":\"🇱🇻\",\"name\":\"flag Latvia\",\"key\":\"1F1F1-1F1FB\"},{\"char\":\"🇱🇾\",\"name\":\"flag Libya\",\"key\":\"1F1F1-1F1FE\"},{\"char\":\"🇲🇦\",\"name\":\"flag Morocco\",\"key\":\"1F1F2-1F1E6\"},{\"char\":\"🇲🇨\",\"name\":\"flag Monaco\",\"key\":\"1F1F2-1F1E8\"},{\"char\":\"🇲🇩\",\"name\":\"flag Moldova\",\"key\":\"1F1F2-1F1E9\"},{\"char\":\"🇲🇪\",\"name\":\"flag Montenegro\",\"key\":\"1F1F2-1F1EA\"},{\"char\":\"🇲🇫\",\"name\":\"flag St. Martin\",\"key\":\"1F1F2-1F1EB\"},{\"char\":\"🇲🇬\",\"name\":\"flag Madagascar\",\"key\":\"1F1F2-1F1EC\"},{\"char\":\"🇲🇭\",\"name\":\"flag Marshall Islands\",\"key\":\"1F1F2-1F1ED\"},{\"char\":\"🇲🇰\",\"name\":\"flag North Macedonia\",\"key\":\"1F1F2-1F1F0\"},{\"char\":\"🇲🇱\",\"name\":\"flag Mali\",\"key\":\"1F1F2-1F1F1\"},{\"char\":\"🇲🇲\",\"name\":\"flag Myanmar (Burma)\",\"key\":\"1F1F2-1F1F2\"},{\"char\":\"🇲🇳\",\"name\":\"flag Mongolia\",\"key\":\"1F1F2-1F1F3\"},{\"char\":\"🇲🇴\",\"name\":\"flag Macao SAR China\",\"key\":\"1F1F2-1F1F4\"},{\"char\":\"🇲🇵\",\"name\":\"flag Northern Mariana Islands\",\"key\":\"1F1F2-1F1F5\"},{\"char\":\"🇲🇶\",\"name\":\"flag Martinique\",\"key\":\"1F1F2-1F1F6\"},{\"char\":\"🇲🇷\",\"name\":\"flag Mauritania\",\"key\":\"1F1F2-1F1F7\"},{\"char\":\"🇲🇸\",\"name\":\"flag Montserrat\",\"key\":\"1F1F2-1F1F8\"},{\"char\":\"🇲🇹\",\"name\":\"flag Malta\",\"key\":\"1F1F2-1F1F9\"},{\"char\":\"🇲🇺\",\"name\":\"flag Mauritius\",\"key\":\"1F1F2-1F1FA\"},{\"char\":\"🇲🇻\",\"name\":\"flag Maldives\",\"key\":\"1F1F2-1F1FB\"},{\"char\":\"🇲🇼\",\"name\":\"flag Malawi\",\"key\":\"1F1F2-1F1FC\"},{\"char\":\"🇲🇽\",\"name\":\"flag Mexico\",\"key\":\"1F1F2-1F1FD\"},{\"char\":\"🇲🇾\",\"name\":\"flag Malaysia\",\"key\":\"1F1F2-1F1FE\"},{\"char\":\"🇲🇿\",\"name\":\"flag Mozambique\",\"key\":\"1F1F2-1F1FF\"},{\"char\":\"🇳🇦\",\"name\":\"flag Namibia\",\"key\":\"1F1F3-1F1E6\"},{\"char\":\"🇳🇨\",\"name\":\"flag New Caledonia\",\"key\":\"1F1F3-1F1E8\"},{\"char\":\"🇳🇪\",\"name\":\"flag Niger\",\"key\":\"1F1F3-1F1EA\"},{\"char\":\"🇳🇫\",\"name\":\"flag Norfolk Island\",\"key\":\"1F1F3-1F1EB\"},{\"char\":\"🇳🇬\",\"name\":\"flag Nigeria\",\"key\":\"1F1F3-1F1EC\"},{\"char\":\"🇳🇮\",\"name\":\"flag Nicaragua\",\"key\":\"1F1F3-1F1EE\"},{\"char\":\"🇳🇱\",\"name\":\"flag Netherlands\",\"key\":\"1F1F3-1F1F1\"},{\"char\":\"🇳🇴\",\"name\":\"flag Norway\",\"key\":\"1F1F3-1F1F4\"},{\"char\":\"🇳🇵\",\"name\":\"flag Nepal\",\"key\":\"1F1F3-1F1F5\"},{\"char\":\"🇳🇷\",\"name\":\"flag Nauru\",\"key\":\"1F1F3-1F1F7\"},{\"char\":\"🇳🇺\",\"name\":\"flag Niue\",\"key\":\"1F1F3-1F1FA\"},{\"char\":\"🇳🇿\",\"name\":\"flag New Zealand\",\"key\":\"1F1F3-1F1FF\"},{\"char\":\"🇴🇲\",\"name\":\"flag Oman\",\"key\":\"1F1F4-1F1F2\"},{\"char\":\"🇵🇦\",\"name\":\"flag Panama\",\"key\":\"1F1F5-1F1E6\"},{\"char\":\"🇵🇪\",\"name\":\"flag Peru\",\"key\":\"1F1F5-1F1EA\"},{\"char\":\"🇵🇫\",\"name\":\"flag French Polynesia\",\"key\":\"1F1F5-1F1EB\"},{\"char\":\"🇵🇬\",\"name\":\"flag Papua New Guinea\",\"key\":\"1F1F5-1F1EC\"},{\"char\":\"🇵🇭\",\"name\":\"flag Philippines\",\"key\":\"1F1F5-1F1ED\"},{\"char\":\"🇵🇰\",\"name\":\"flag Pakistan\",\"key\":\"1F1F5-1F1F0\"},{\"char\":\"🇵🇱\",\"name\":\"flag Poland\",\"key\":\"1F1F5-1F1F1\"},{\"char\":\"🇵🇲\",\"name\":\"flag St. Pierre & Miquelon\",\"key\":\"1F1F5-1F1F2\"},{\"char\":\"🇵🇳\",\"name\":\"flag Pitcairn Islands\",\"key\":\"1F1F5-1F1F3\"},{\"char\":\"🇵🇷\",\"name\":\"flag Puerto Rico\",\"key\":\"1F1F5-1F1F7\"},{\"char\":\"🇵🇸\",\"name\":\"flag Palestinian Territories\",\"key\":\"1F1F5-1F1F8\"},{\"char\":\"🇵🇹\",\"name\":\"flag Portugal\",\"key\":\"1F1F5-1F1F9\"},{\"char\":\"🇵🇼\",\"name\":\"flag Palau\",\"key\":\"1F1F5-1F1FC\"},{\"char\":\"🇵🇾\",\"name\":\"flag Paraguay\",\"key\":\"1F1F5-1F1FE\"},{\"char\":\"🇶🇦\",\"name\":\"flag Qatar\",\"key\":\"1F1F6-1F1E6\"},{\"char\":\"🇷🇪\",\"name\":\"flag Réunion\",\"key\":\"1F1F7-1F1EA\"},{\"char\":\"🇷🇴\",\"name\":\"flag Romania\",\"key\":\"1F1F7-1F1F4\"},{\"char\":\"🇷🇸\",\"name\":\"flag Serbia\",\"key\":\"1F1F7-1F1F8\"},{\"char\":\"🇷🇺\",\"name\":\"flag Russia\",\"key\":\"1F1F7-1F1FA\"},{\"char\":\"🇷🇼\",\"name\":\"flag Rwanda\",\"key\":\"1F1F7-1F1FC\"},{\"char\":\"🇸🇦\",\"name\":\"flag Saudi Arabia\",\"key\":\"1F1F8-1F1E6\"},{\"char\":\"🇸🇧\",\"name\":\"flag Solomon Islands\",\"key\":\"1F1F8-1F1E7\"},{\"char\":\"🇸🇨\",\"name\":\"flag Seychelles\",\"key\":\"1F1F8-1F1E8\"},{\"char\":\"🇸🇩\",\"name\":\"flag Sudan\",\"key\":\"1F1F8-1F1E9\"},{\"char\":\"🇸🇪\",\"name\":\"flag Sweden\",\"key\":\"1F1F8-1F1EA\"},{\"char\":\"🇸🇬\",\"name\":\"flag Singapore\",\"key\":\"1F1F8-1F1EC\"},{\"char\":\"🇸🇭\",\"name\":\"flag St. Helena\",\"key\":\"1F1F8-1F1ED\"},{\"char\":\"🇸🇮\",\"name\":\"flag Slovenia\",\"key\":\"1F1F8-1F1EE\"},{\"char\":\"🇸🇯\",\"name\":\"flag Svalbard & Jan Mayen\",\"key\":\"1F1F8-1F1EF\"},{\"char\":\"🇸🇰\",\"name\":\"flag Slovakia\",\"key\":\"1F1F8-1F1F0\"},{\"char\":\"🇸🇱\",\"name\":\"flag Sierra Leone\",\"key\":\"1F1F8-1F1F1\"},{\"char\":\"🇸🇲\",\"name\":\"flag San Marino\",\"key\":\"1F1F8-1F1F2\"},{\"char\":\"🇸🇳\",\"name\":\"flag Senegal\",\"key\":\"1F1F8-1F1F3\"},{\"char\":\"🇸🇴\",\"name\":\"flag Somalia\",\"key\":\"1F1F8-1F1F4\"},{\"char\":\"🇸🇷\",\"name\":\"flag Suriname\",\"key\":\"1F1F8-1F1F7\"},{\"char\":\"🇸🇸\",\"name\":\"flag South Sudan\",\"key\":\"1F1F8-1F1F8\"},{\"char\":\"🇸🇹\",\"name\":\"flag São Tomé & Príncipe\",\"key\":\"1F1F8-1F1F9\"},{\"char\":\"🇸🇻\",\"name\":\"flag El Salvador\",\"key\":\"1F1F8-1F1FB\"},{\"char\":\"🇸🇽\",\"name\":\"flag Sint Maarten\",\"key\":\"1F1F8-1F1FD\"},{\"char\":\"🇸🇾\",\"name\":\"flag Syria\",\"key\":\"1F1F8-1F1FE\"},{\"char\":\"🇸🇿\",\"name\":\"flag Eswatini\",\"key\":\"1F1F8-1F1FF\"},{\"char\":\"🇹🇦\",\"name\":\"flag Tristan da Cunha\",\"key\":\"1F1F9-1F1E6\"},{\"char\":\"🇹🇨\",\"name\":\"flag Turks & Caicos Islands\",\"key\":\"1F1F9-1F1E8\"},{\"char\":\"🇹🇩\",\"name\":\"flag Chad\",\"key\":\"1F1F9-1F1E9\"},{\"char\":\"🇹🇫\",\"name\":\"flag French Southern Territories\",\"key\":\"1F1F9-1F1EB\"},{\"char\":\"🇹🇬\",\"name\":\"flag Togo\",\"key\":\"1F1F9-1F1EC\"},{\"char\":\"🇹🇭\",\"name\":\"flag Thailand\",\"key\":\"1F1F9-1F1ED\"},{\"char\":\"🇹🇯\",\"name\":\"flag Tajikistan\",\"key\":\"1F1F9-1F1EF\"},{\"char\":\"🇹🇰\",\"name\":\"flag Tokelau\",\"key\":\"1F1F9-1F1F0\"},{\"char\":\"🇹🇱\",\"name\":\"flag Timor-Leste\",\"key\":\"1F1F9-1F1F1\"},{\"char\":\"🇹🇲\",\"name\":\"flag Turkmenistan\",\"key\":\"1F1F9-1F1F2\"},{\"char\":\"🇹🇳\",\"name\":\"flag Tunisia\",\"key\":\"1F1F9-1F1F3\"},{\"char\":\"🇹🇴\",\"name\":\"flag Tonga\",\"key\":\"1F1F9-1F1F4\"},{\"char\":\"🇹🇷\",\"name\":\"flag Türkiye\",\"key\":\"1F1F9-1F1F7\"},{\"char\":\"🇹🇹\",\"name\":\"flag Trinidad & Tobago\",\"key\":\"1F1F9-1F1F9\"},{\"char\":\"🇹🇻\",\"name\":\"flag Tuvalu\",\"key\":\"1F1F9-1F1FB\"},{\"char\":\"🇹🇼\",\"name\":\"flag Taiwan\",\"key\":\"1F1F9-1F1FC\"},{\"char\":\"🇹🇿\",\"name\":\"flag Tanzania\",\"key\":\"1F1F9-1F1FF\"},{\"char\":\"🇺🇦\",\"name\":\"flag Ukraine\",\"key\":\"1F1FA-1F1E6\"},{\"char\":\"🇺🇬\",\"name\":\"flag Uganda\",\"key\":\"1F1FA-1F1EC\"},{\"char\":\"🇺🇲\",\"name\":\"flag U.S. Outlying Islands\",\"key\":\"1F1FA-1F1F2\"},{\"char\":\"🇺🇳\",\"name\":\"flag United Nations\",\"key\":\"1F1FA-1F1F3\"},{\"char\":\"🇺🇸\",\"name\":\"flag United States\",\"key\":\"1F1FA-1F1F8\"},{\"char\":\"🇺🇾\",\"name\":\"flag Uruguay\",\"key\":\"1F1FA-1F1FE\"},{\"char\":\"🇺🇿\",\"name\":\"flag Uzbekistan\",\"key\":\"1F1FA-1F1FF\"},{\"char\":\"🇻🇦\",\"name\":\"flag Vatican City\",\"key\":\"1F1FB-1F1E6\"},{\"char\":\"🇻🇨\",\"name\":\"flag St. Vincent & Grenadines\",\"key\":\"1F1FB-1F1E8\"},{\"char\":\"🇻🇪\",\"name\":\"flag Venezuela\",\"key\":\"1F1FB-1F1EA\"},{\"char\":\"🇻🇬\",\"name\":\"flag British Virgin Islands\",\"key\":\"1F1FB-1F1EC\"},{\"char\":\"🇻🇮\",\"name\":\"flag U.S. Virgin Islands\",\"key\":\"1F1FB-1F1EE\"},{\"char\":\"🇻🇳\",\"name\":\"flag Vietnam\",\"key\":\"1F1FB-1F1F3\"},{\"char\":\"🇻🇺\",\"name\":\"flag Vanuatu\",\"key\":\"1F1FB-1F1FA\"},{\"char\":\"🇼🇫\",\"name\":\"flag Wallis & Futuna\",\"key\":\"1F1FC-1F1EB\"},{\"char\":\"🇼🇸\",\"name\":\"flag Samoa\",\"key\":\"1F1FC-1F1F8\"},{\"char\":\"🇽🇰\",\"name\":\"flag Kosovo\",\"key\":\"1F1FD-1F1F0\"},{\"char\":\"🇾🇪\",\"name\":\"flag Yemen\",\"key\":\"1F1FE-1F1EA\"},{\"char\":\"🇾🇹\",\"name\":\"flag Mayotte\",\"key\":\"1F1FE-1F1F9\"},{\"char\":\"🇿🇦\",\"name\":\"flag South Africa\",\"key\":\"1F1FF-1F1E6\"},{\"char\":\"🇿🇲\",\"name\":\"flag Zambia\",\"key\":\"1F1FF-1F1F2\"},{\"char\":\"🇿🇼\",\"name\":\"flag Zimbabwe\",\"key\":\"1F1FF-1F1FC\"},{\"char\":\"🏴󠁧󠁢󠁥󠁮󠁧󠁿\",\"name\":\"flag England\",\"key\":\"1F3F4-E0067-E0062-E0065-E006E-E0067-E007F\"},{\"char\":\"🏴󠁧󠁢󠁳󠁣󠁴󠁿\",\"name\":\"flag Scotland\",\"key\":\"1F3F4-E0067-E0062-E0073-E0063-E0074-E007F\"},{\"char\":\"🏴󠁧󠁢󠁷󠁬󠁳󠁿\",\"name\":\"flag Wales\",\"key\":\"1F3F4-E0067-E0062-E0077-E006C-E0073-E007F\"}]},\n];\n","/**\n * Emoji artwork loader for the annotate emoji sticker tool.\n *\n * Emoji stickers render as OpenMoji SVGs (crisp at any size) rather than the\n * platform's bitmap emoji font (which blurs when enlarged). The SVGs ship as\n * static same-origin assets next to the bundle; this module resolves their URL,\n * loads them on demand into an `<img>` cache, and hands the decoded image to the\n * shared `paintShape` so the on-screen preview and the bake draw the same pixels.\n *\n * Nothing is fetched until an emoji is actually browsed or placed, and each\n * glyph is loaded once and cached. Until an image is ready (or if the asset is\n * unavailable) `paintShape` falls back to the OS emoji font, so the feature\n * degrades gracefully and never blocks.\n */\n\nimport type { Shape } from '@magicpages/kalotyp-core';\nimport { EMOJI_GROUPS } from './emoji-data.js';\n\nconst KEY_BY_CHAR = new Map<string, string>();\nfor (const group of EMOJI_GROUPS) {\n for (const entry of group.emojis) KEY_BY_CHAR.set(entry.char, entry.key);\n}\n\n/** The OpenMoji artwork key for an emoji character, or `undefined` if unknown. */\nexport function emojiKeyFor(char: string): string | undefined {\n return KEY_BY_CHAR.get(char);\n}\n\nlet assetBaseOverride: string | null = null;\n\n/**\n * Override where emoji SVGs are loaded from. The host (e.g. the Ghost loader)\n * calls this when the assets aren't served at the default `/emoji/` path. A\n * trailing slash is enforced.\n *\n * Best set before the first emoji is used, but a later change is honoured:\n * already-cached images were loaded from the old base, so changing it drops the\n * cache and the next paint reloads from the new base.\n */\nexport function setEmojiAssetBase(url: string): void {\n const next = url.endsWith('/') ? url : `${url}/`;\n if (next === assetBaseOverride) return;\n assetBaseOverride = next;\n // Invalidate images keyed to the previous base; a repaint reloads them.\n cache.clear();\n notifyLoaded();\n}\n\nfunction withSlash(url: string): string {\n return url.endsWith('/') ? url : `${url}/`;\n}\n\nfunction assetBase(): string {\n if (assetBaseOverride) return assetBaseOverride;\n // Cast: `__KALOTYP_EMOJI_BASE__` is an optional host-set global not on the\n // standard `Window` type; we read it as `unknown` and validate it's a string\n // below before use.\n const fromGlobal =\n typeof window !== 'undefined'\n ? (window as { __KALOTYP_EMOJI_BASE__?: unknown }).__KALOTYP_EMOJI_BASE__\n : undefined;\n if (typeof fromGlobal === 'string' && fromGlobal.length > 0) return withSlash(fromGlobal);\n return '/emoji/';\n}\n\n/** The same-origin SVG URL for an OpenMoji artwork key. */\nexport function emojiSvgUrlForKey(key: string): string {\n return `${assetBase()}${key}.svg`;\n}\n\n/** The same-origin SVG URL for an emoji character, or `null` if it has no artwork. */\nexport function emojiSvgUrl(char: string): string | null {\n const key = KEY_BY_CHAR.get(char);\n return key ? emojiSvgUrlForKey(key) : null;\n}\n\ninterface CacheEntry {\n readonly image: HTMLImageElement;\n loaded: boolean;\n failed: boolean;\n}\n\nconst cache = new Map<string, CacheEntry>();\nconst loadListeners = new Set<() => void>();\n\n/** Register a callback fired whenever an emoji image finishes loading (for repaint). */\nexport function onEmojiImageLoad(callback: () => void): () => void {\n loadListeners.add(callback);\n return () => {\n loadListeners.delete(callback);\n };\n}\n\nfunction notifyLoaded(): void {\n for (const callback of loadListeners) callback();\n}\n\nfunction ensureEntry(key: string): CacheEntry {\n const existing = cache.get(key);\n if (existing) return existing;\n const image = new Image();\n const entry: CacheEntry = { image, loaded: false, failed: false };\n cache.set(key, entry);\n image.addEventListener('load', () => {\n entry.loaded = true;\n notifyLoaded();\n });\n image.addEventListener('error', () => {\n entry.failed = true;\n });\n // Same-origin SVG → the bake canvas stays untainted, so Save can still export.\n image.src = `${assetBase()}${key}.svg`;\n return entry;\n}\n\n/**\n * Synchronous resolver consumed by `paintShape`: returns the decoded image if\n * it's ready, otherwise `null` (and kicks off the load so a later repaint — via\n * `onEmojiImageLoad` — can draw it). `null` makes `paintShape` fall back to the\n * OS emoji font.\n */\nexport function resolveEmojiImage(char: string): CanvasImageSource | null {\n const key = KEY_BY_CHAR.get(char);\n if (!key) return null;\n const entry = ensureEntry(key);\n return entry.loaded ? entry.image : null;\n}\n\n/** Cap on how long the bake waits for emoji artwork; on timeout it bakes the font fallback. */\nconst PRELOAD_TIMEOUT_MS = 1500;\n\n/**\n * Load the artwork for every emoji shape and await it (for the bake, so the\n * saved image gets the crisp SVG rather than the fallback). Bounded by a\n * timeout — a slow or blocked asset must never wedge Save.\n */\nexport async function ensureEmojiImagesLoaded(shapes: ReadonlyArray<Shape>): Promise<void> {\n const keys = new Set<string>();\n for (const shape of shapes) {\n if (shape.kind !== 'emoji') continue;\n const key = KEY_BY_CHAR.get(shape.emoji);\n if (key) keys.add(key);\n }\n if (keys.size === 0) return;\n const waits = [...keys].map((key) => {\n const entry = ensureEntry(key);\n if (entry.loaded || entry.failed) return Promise.resolve();\n return new Promise<void>((res) => {\n entry.image.addEventListener('load', () => res(), { once: true });\n entry.image.addEventListener('error', () => res(), { once: true });\n });\n });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<void>((res) => {\n timer = setTimeout(res, PRELOAD_TIMEOUT_MS);\n });\n try {\n await Promise.race([Promise.all(waits).then(() => undefined), timeout]);\n } finally {\n // Don't leave a dangling timer once the artwork has settled.\n if (timer) clearTimeout(timer);\n }\n}\n","/**\n * Emoji picker popover for the annotate emoji sticker tool.\n *\n * A self-contained overlay (owned by the mount layer, like the inline text\n * editor) with a search box, category tabs, and a scrollable grid. To keep the\n * DOM small the grid renders only the active category — or, while searching,\n * the capped set of name matches — never all ~1,900 cells at once.\n *\n * The picker is purely presentational: clicking a cell reports the chosen\n * character via `onSelect`; the caller arms it as the current emoji and places\n * it on the canvas. The popover is positioned by CSS (absolute, anchored above\n * the panel) so showing it never reflows the stage.\n */\n\nimport { icon } from '../../icons.js';\nimport { EMOJI_GROUPS, type EmojiEntry } from './emoji-data.js';\nimport { emojiSvgUrlForKey } from './emoji-images.js';\n\nexport interface EmojiPickerOptions {\n readonly host: HTMLElement;\n /** A cell was chosen; the value is the emoji character. */\n onSelect(char: string): void;\n /** The close (×) button was pressed. */\n onClose(): void;\n}\n\nexport interface EmojiPickerHandle {\n readonly element: HTMLDivElement;\n show(): void;\n hide(): void;\n readonly isOpen: boolean;\n destroy(): void;\n}\n\n/** Fixed column count so the grid lays out predictably and arrow-key navigation can move by row. */\nconst GRID_COLUMNS = 8;\n/** Cap on rendered search matches — a guard against rendering a huge result DOM. */\nconst MAX_SEARCH_RESULTS = 180;\n\nexport function buildEmojiPicker(options: EmojiPickerOptions): EmojiPickerHandle {\n let open = false;\n let activeGroupIndex = 0;\n\n const root = document.createElement('div');\n root.className = 'kalotyp-annotate-emoji-picker';\n root.setAttribute('role', 'group');\n root.setAttribute('aria-label', 'Emoji picker');\n root.hidden = true;\n\n // ----- Header: title + close -----\n const header = document.createElement('div');\n header.className = 'kalotyp-annotate-emoji-header';\n const title = document.createElement('span');\n title.className = 'kalotyp-annotate-emoji-title';\n title.textContent = 'Emoji';\n const closeButton = document.createElement('button');\n closeButton.type = 'button';\n closeButton.className = 'kalotyp-annotate-emoji-close';\n closeButton.innerHTML = icon('close');\n closeButton.setAttribute('aria-label', 'Close emoji picker');\n closeButton.title = 'Close';\n closeButton.addEventListener('click', () => options.onClose());\n header.appendChild(title);\n header.appendChild(closeButton);\n\n // ----- Search -----\n const searchWrap = document.createElement('div');\n searchWrap.className = 'kalotyp-annotate-emoji-search';\n searchWrap.innerHTML = icon('search', { 'aria-hidden': 'true' });\n const searchInput = document.createElement('input');\n searchInput.type = 'search';\n searchInput.className = 'kalotyp-annotate-emoji-search-input';\n searchInput.setAttribute('aria-label', 'Search emoji');\n searchInput.placeholder = 'Search emoji';\n searchInput.autocomplete = 'off';\n searchInput.spellcheck = false;\n searchWrap.appendChild(searchInput);\n\n // ----- Category tabs -----\n const tabs = document.createElement('div');\n tabs.className = 'kalotyp-annotate-emoji-tabs';\n // Plain category-filter buttons (aria-pressed), not an ARIA tablist — there\n // are no tab panels and search can leave none active, so tab semantics would\n // mislead assistive tech.\n tabs.setAttribute('role', 'group');\n tabs.setAttribute('aria-label', 'Emoji categories');\n const tabButtons: HTMLButtonElement[] = [];\n EMOJI_GROUPS.forEach((group, index) => {\n const tab = document.createElement('button');\n tab.type = 'button';\n tab.className = 'kalotyp-annotate-emoji-tab';\n tab.setAttribute('aria-label', group.label);\n tab.title = group.label;\n // The group's first emoji as the tab icon — same OpenMoji artwork as the\n // cells (crisp + consistent), rather than the OS glyph.\n const firstKey = group.emojis[0]?.key;\n if (firstKey) {\n const tabImg = document.createElement('img');\n tabImg.className = 'kalotyp-annotate-emoji-tab-img';\n tabImg.setAttribute('loading', 'lazy');\n tabImg.alt = '';\n tabImg.draggable = false;\n tabImg.src = emojiSvgUrlForKey(firstKey);\n tab.appendChild(tabImg);\n }\n tab.addEventListener('click', () => {\n searchInput.value = '';\n setActiveGroup(index);\n });\n tabs.appendChild(tab);\n tabButtons.push(tab);\n });\n\n // ----- Grid + empty state -----\n const grid = document.createElement('div');\n grid.className = 'kalotyp-annotate-emoji-grid';\n grid.setAttribute('role', 'group');\n grid.setAttribute('aria-label', 'Emoji');\n grid.style.setProperty('--kalotyp-emoji-cols', String(GRID_COLUMNS));\n\n const empty = document.createElement('p');\n empty.className = 'kalotyp-annotate-emoji-empty';\n empty.textContent = 'No emoji found.';\n empty.hidden = true;\n\n root.appendChild(header);\n root.appendChild(searchWrap);\n root.appendChild(tabs);\n root.appendChild(grid);\n root.appendChild(empty);\n options.host.appendChild(root);\n\n function setActiveGroup(index: number): void {\n activeGroupIndex = index;\n tabButtons.forEach((tab, i) => {\n tab.setAttribute('aria-pressed', i === index ? 'true' : 'false');\n });\n const group = EMOJI_GROUPS[index];\n renderCells(group ? group.emojis : []);\n }\n\n function runSearch(query: string): void {\n const q = query.trim().toLowerCase();\n if (q === '') {\n setActiveGroup(activeGroupIndex);\n return;\n }\n // Searching spans all groups; clear the active category so the highlight\n // doesn't imply the results are scoped to one.\n for (const tab of tabButtons) tab.setAttribute('aria-pressed', 'false');\n const matches: EmojiEntry[] = [];\n for (const group of EMOJI_GROUPS) {\n for (const entry of group.emojis) {\n if (entry.name.includes(q)) {\n matches.push(entry);\n if (matches.length >= MAX_SEARCH_RESULTS) break;\n }\n }\n if (matches.length >= MAX_SEARCH_RESULTS) break;\n }\n renderCells(matches);\n }\n\n function renderCells(entries: ReadonlyArray<EmojiEntry>): void {\n grid.replaceChildren();\n empty.hidden = entries.length > 0;\n grid.hidden = entries.length === 0;\n entries.forEach((entry, i) => {\n const cell = document.createElement('button');\n cell.type = 'button';\n cell.className = 'kalotyp-annotate-emoji-cell';\n // Roving tabindex: only the first cell is in the Tab order; arrow keys\n // move focus among cells. Keeps a several-hundred-cell grid from adding\n // that many Tab stops.\n cell.tabIndex = i === 0 ? 0 : -1;\n cell.dataset.char = entry.char;\n cell.setAttribute('aria-label', entry.name);\n cell.title = entry.name;\n // Render the OpenMoji SVG (same artwork the canvas bakes), lazily so only\n // the cells scrolled into view fetch. Decorative — the button carries the\n // accessible name.\n const img = document.createElement('img');\n img.className = 'kalotyp-annotate-emoji-cell-img';\n img.setAttribute('loading', 'lazy');\n img.setAttribute('decoding', 'async');\n img.alt = '';\n img.draggable = false;\n img.src = emojiSvgUrlForKey(entry.key);\n cell.appendChild(img);\n cell.addEventListener('click', () => options.onSelect(entry.char));\n grid.appendChild(cell);\n });\n grid.scrollTop = 0;\n }\n\n function focusCell(index: number): void {\n const cells = grid.querySelectorAll<HTMLButtonElement>('.kalotyp-annotate-emoji-cell');\n const target = cells[index];\n if (!target) return;\n for (const cell of cells) cell.tabIndex = -1;\n target.tabIndex = 0;\n target.focus();\n }\n\n function onGridKeyDown(event: KeyboardEvent): void {\n const target = event.target;\n if (\n !(target instanceof HTMLElement) ||\n !target.classList.contains('kalotyp-annotate-emoji-cell')\n ) {\n return;\n }\n const cells = Array.from(\n grid.querySelectorAll<HTMLButtonElement>('.kalotyp-annotate-emoji-cell'),\n );\n const current = cells.indexOf(target as HTMLButtonElement);\n if (current === -1) return;\n let next = current;\n switch (event.key) {\n case 'ArrowRight':\n next = Math.min(current + 1, cells.length - 1);\n break;\n case 'ArrowLeft':\n next = Math.max(current - 1, 0);\n break;\n case 'ArrowDown':\n next = Math.min(current + GRID_COLUMNS, cells.length - 1);\n break;\n case 'ArrowUp':\n next = Math.max(current - GRID_COLUMNS, 0);\n break;\n case 'Home':\n next = 0;\n break;\n case 'End':\n next = cells.length - 1;\n break;\n default:\n return;\n }\n event.preventDefault();\n focusCell(next);\n }\n\n const onSearchInput = (): void => runSearch(searchInput.value);\n // Keep the wheel inside the picker: the grid scrolls natively, but the event\n // must not reach the editor's stage zoom handler (which would zoom the image\n // instead). The stage listener is in the bubble phase, so stopping\n // propagation here is enough; we don't preventDefault so native scroll stays.\n const onWheel = (event: WheelEvent): void => event.stopPropagation();\n searchInput.addEventListener('input', onSearchInput);\n grid.addEventListener('keydown', onGridKeyDown);\n root.addEventListener('wheel', onWheel);\n\n setActiveGroup(0);\n\n return {\n element: root,\n get isOpen(): boolean {\n return open;\n },\n show(): void {\n open = true;\n root.hidden = false;\n // Defer focus so the show-time layout settles first.\n requestAnimationFrame(() => searchInput.focus());\n },\n hide(): void {\n open = false;\n root.hidden = true;\n },\n destroy(): void {\n searchInput.removeEventListener('input', onSearchInput);\n grid.removeEventListener('keydown', onGridKeyDown);\n root.removeEventListener('wheel', onWheel);\n root.remove();\n },\n };\n}\n","/**\n * Loads the text-annotation web fonts from fonts.bunny.net — the same\n * GDPR-friendly CDN and family set Ghost's own admin uses, so annotations can\n * match a site's typography. Injection is idempotent: if the link is already\n * present (Ghost may have loaded these, or we ran before), it's a no-op.\n *\n * The bake awaits `document.fonts.load` (with a timeout) before painting, so a\n * slow load never produces a wrong baked image — it just falls back to the\n * generic family if the face isn't ready in time.\n */\n\nimport { TEXT_FONTS } from '@magicpages/kalotyp-core';\n\nconst LINK_ID = 'kalotyp-annotate-fonts';\n\n/** Build the combined Bunny CSS URL requesting regular+bold, roman+italic per family. */\nfunction buildBunnyUrl(): string {\n const families = TEXT_FONTS.flatMap((f) => (f.bunnyName ? [f.bunnyName] : []));\n // Request 400/700 with italics so bold + italic render as real faces, not\n // synthesized obliques.\n const familyParam = families.map((name) => `${name}:400,400i,700,700i`).join('|');\n return `https://fonts.bunny.net/css?family=${familyParam}&display=swap`;\n}\n\n/**\n * Inject the Bunny stylesheet once (idempotent) and invoke `onFontsChanged`\n * whenever font faces finish loading. Web fonts arrive asynchronously: the\n * canvas first paints with a fallback face, then the real face swaps in with\n * different metrics — so the caller must repaint the shapes when fonts load,\n * or committed text appears to shift on the next interaction. `loadingdone`\n * fires for each batch the document loads (including fonts Ghost or other tools\n * trigger), and `fonts.ready` covers any already-pending load. Returns a\n * cleanup that detaches the listener.\n */\nexport function ensureAnnotateFontsLoaded(onFontsChanged?: () => void): () => void {\n if (typeof document === 'undefined') return () => {};\n if (!document.getElementById(LINK_ID)) {\n const link = document.createElement('link');\n link.id = LINK_ID;\n link.rel = 'stylesheet';\n link.href = buildBunnyUrl();\n document.head.appendChild(link);\n }\n if (!onFontsChanged || !('fonts' in document)) return () => {};\n const onLoadingDone = (): void => onFontsChanged();\n document.fonts.addEventListener('loadingdone', onLoadingDone);\n // Also catch fonts already pending when we mount.\n void document.fonts.ready.then(() => onFontsChanged());\n return () => document.fonts.removeEventListener('loadingdone', onLoadingDone);\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 TEXT_FONTS,\n type TextAlign,\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 onFontFamilyChange(fontFamily: string): void;\n onFontSizeChange(fontSize: number): void;\n onToggleBold(): void;\n onToggleItalic(): void;\n onAlignChange(align: TextAlign): 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 fontSelect: HTMLSelectElement;\n readonly fontSizeInput: HTMLInputElement;\n readonly boldButton: HTMLButtonElement;\n readonly italicButton: HTMLButtonElement;\n readonly alignButtons: ReadonlyMap<TextAlign, HTMLButtonElement>;\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 * Show the per-mode controls for the active tool / selection. `text` reveals\n * the font/size/bold/italic/align row; `emoji` hides the colour + stroke\n * controls (an emoji carries its own colour). Stroke width hides for both.\n */\n setControlsMode(mode: { readonly text: boolean; readonly emoji: 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 { id: 'emoji', label: 'Emoji', icon: icon('emoji') },\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\n/** Font-size bounds in image-space px; the single source for the input's\n * min/max attributes and the typed-value validation (kept from drifting). */\nconst FONT_SIZE_MIN = 8;\nconst FONT_SIZE_MAX = 400;\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 // Stroke \"Width\" control, wrapped so it can be hidden for text (text has no\n // stroke; its size is the font-size stepper in the text row).\n const strokeWrap = document.createElement('span');\n strokeWrap.className = 'kalotyp-annotate-stroke-wrap';\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 strokeLabel.appendChild(strokeRange);\n strokeWrap.appendChild(strokeLabel);\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(strokeWrap);\n styleRow.appendChild(insertButton);\n styleRow.appendChild(deleteButton);\n\n // ----- Text controls (font family / size / bold / italic / align) -----\n const textRow = document.createElement('div');\n textRow.className = 'kalotyp-annotate-text-row';\n textRow.style.display = 'none';\n\n const fontSelect = document.createElement('select');\n fontSelect.className = 'kalotyp-annotate-font';\n fontSelect.setAttribute('aria-label', 'Font');\n for (const font of TEXT_FONTS) {\n const option = document.createElement('option');\n option.value = font.key;\n option.textContent = font.label;\n fontSelect.appendChild(option);\n }\n fontSelect.value = options.initialStyle.fontFamily;\n fontSelect.addEventListener('change', () => options.onFontFamilyChange(fontSelect.value));\n\n // Last accepted font size, used to revert invalid typed input. Kept in sync\n // by `setStyle` so a typo restores the *current* size, not the palette default.\n let lastValidFontSize = options.initialStyle.fontSize;\n const fontSizeInput = document.createElement('input');\n fontSizeInput.type = 'number';\n fontSizeInput.className = 'kalotyp-annotate-font-size';\n fontSizeInput.min = String(FONT_SIZE_MIN);\n fontSizeInput.max = String(FONT_SIZE_MAX);\n fontSizeInput.step = '1';\n fontSizeInput.value = String(options.initialStyle.fontSize);\n fontSizeInput.setAttribute('aria-label', 'Font size');\n fontSizeInput.addEventListener('change', () => {\n const value = Math.round(fontSizeInput.valueAsNumber);\n // Enforce both bounds (the min/max attributes only constrain the spinner\n // arrows, not typed input); revert out-of-range entries to the last valid.\n if (Number.isFinite(value) && value >= FONT_SIZE_MIN && value <= FONT_SIZE_MAX) {\n lastValidFontSize = value;\n options.onFontSizeChange(value);\n } else {\n fontSizeInput.value = String(lastValidFontSize);\n }\n });\n\n const boldButton = document.createElement('button');\n boldButton.type = 'button';\n boldButton.className = 'kalotyp-annotate-text-toggle';\n boldButton.innerHTML = icon('bold');\n boldButton.setAttribute('aria-label', 'Bold');\n boldButton.title = 'Bold';\n boldButton.setAttribute(\n 'aria-pressed',\n options.initialStyle.fontWeight === 'bold' ? 'true' : 'false',\n );\n boldButton.addEventListener('click', () => options.onToggleBold());\n\n const italicButton = document.createElement('button');\n italicButton.type = 'button';\n italicButton.className = 'kalotyp-annotate-text-toggle';\n italicButton.innerHTML = icon('italic');\n italicButton.setAttribute('aria-label', 'Italic');\n italicButton.title = 'Italic';\n italicButton.setAttribute(\n 'aria-pressed',\n options.initialStyle.fontStyle === 'italic' ? 'true' : 'false',\n );\n italicButton.addEventListener('click', () => options.onToggleItalic());\n\n const alignGroup = document.createElement('div');\n alignGroup.className = 'kalotyp-annotate-align';\n alignGroup.setAttribute('role', 'radiogroup');\n alignGroup.setAttribute('aria-label', 'Text alignment');\n const alignButtons = new Map<TextAlign, HTMLButtonElement>();\n const ALIGN_DEFS: ReadonlyArray<{ id: TextAlign; label: string; icon: string }> = [\n { id: 'left', label: 'Align left', icon: icon('alignLeft') },\n { id: 'center', label: 'Align center', icon: icon('alignCenter') },\n { id: 'right', label: 'Align right', icon: icon('alignRight') },\n ];\n for (const def of ALIGN_DEFS) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'kalotyp-annotate-align-button';\n button.setAttribute('role', 'radio');\n button.setAttribute('aria-label', def.label);\n button.title = def.label;\n button.innerHTML = def.icon;\n button.setAttribute(\n 'aria-checked',\n def.id === options.initialStyle.textAlign ? 'true' : 'false',\n );\n button.addEventListener('click', () => options.onAlignChange(def.id));\n alignGroup.appendChild(button);\n alignButtons.set(def.id, button);\n }\n\n textRow.appendChild(fontSelect);\n textRow.appendChild(fontSizeInput);\n textRow.appendChild(boldButton);\n textRow.appendChild(italicButton);\n textRow.appendChild(alignGroup);\n\n container.appendChild(toolbar);\n container.appendChild(styleRow);\n container.appendChild(textRow);\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 // Text controls reflect the same palette (current style for new shapes, or\n // the selected text shape's attributes, both threaded through here).\n if (fontSelect.value !== style.fontFamily) fontSelect.value = style.fontFamily;\n lastValidFontSize = style.fontSize;\n if (Math.round(fontSizeInput.valueAsNumber) !== style.fontSize) {\n fontSizeInput.value = String(style.fontSize);\n }\n boldButton.setAttribute('aria-pressed', style.fontWeight === 'bold' ? 'true' : 'false');\n italicButton.setAttribute('aria-pressed', style.fontStyle === 'italic' ? 'true' : 'false');\n for (const [id, button] of alignButtons) {\n button.setAttribute('aria-checked', id === style.textAlign ? 'true' : 'false');\n }\n }\n\n function setCanDelete(canDelete: boolean): void {\n deleteButton.disabled = !canDelete;\n }\n\n function setControlsMode(mode: { readonly text: boolean; readonly emoji: boolean }): void {\n textRow.style.display = mode.text ? '' : 'none';\n // An emoji sticker carries its own colour, so the colour controls don't\n // apply — hide the swatches + hex input while emoji is in play.\n swatchGroup.style.display = mode.emoji ? 'none' : '';\n hexInput.style.display = mode.emoji ? 'none' : '';\n // Stroke width is meaningless for both text (font-size drives it) and emoji;\n // swap it out so the relevant row owns sizing.\n strokeWrap.style.display = mode.text || mode.emoji ? 'none' : '';\n }\n\n setStyle(options.initialStyle);\n\n return {\n container,\n toolButtons,\n hexInput,\n colorSwatches: swatches,\n strokeRange,\n fontSelect,\n fontSizeInput,\n boldButton,\n italicButton,\n alignButtons,\n deleteButton,\n insertButton,\n setActiveTool,\n setStyle,\n setCanDelete,\n setControlsMode,\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 {\n type PaintShapeOptions,\n paintShape,\n type Shape,\n type SourceImage,\n type Viewport,\n} 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 opts?: PaintShapeOptions,\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, opts);\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 opts?: PaintShapeOptions,\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, opts);\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 EMOJI_MIN_SIZE,\n normalizeAngle,\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/** Corner handles shown for emoji stickers (they resize uniformly — no edges). */\nconst EMOJI_CORNER_HANDLES: ReadonlyArray<SelectionHandle> = ['tl', 'tr', 'bl', 'br'];\n/** Distance (display px) the rotate handle sits beyond the box's top edge. */\nconst ROTATE_HANDLE_OFFSET = 24;\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 // Dedicated rotate handle for emoji stickers — orbits above the box.\n const rotateHandle = document.createElement('button');\n rotateHandle.type = 'button';\n rotateHandle.className = 'kalotyp-annotate-rotate-handle';\n rotateHandle.setAttribute('aria-label', 'Rotate');\n rotateHandle.tabIndex = -1;\n rotateHandle.style.display = 'none';\n host.appendChild(rotateHandle);\n cleanups.push(attachPointerDrag(rotateHandle, (event) => startRotateGesture(toolContext, event)));\n\n function update(shape: Shape | null, viewport: Viewport): void {\n // The rotate handle is emoji-only; hide it unless an emoji branch re-shows it.\n rotateHandle.style.display = 'none';\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 if (shape.kind === 'text') {\n // Text isn't handle-resizable — its size is set in the panel (font size),\n // not by dragging. The selection outline (drawn by CSS via the host)\n // still shows what's selected, and a body drag still moves it. Hide all\n // resize handles so there's just one box, not a handle frame too.\n hideAll(handleEls);\n return;\n }\n if (shape.kind === 'emoji') {\n // Emoji stickers scale uniformly, so only the four corner handles show.\n hideAll(handleEls);\n const positions = selectionHandlePositions(boundingBoxOf(shape));\n for (const direction of EMOJI_CORNER_HANDLES) {\n const handle = handleEls.get(direction);\n if (handle) positionHandle(handle, imageToDisplay(positions[direction], viewport));\n }\n // Rotate handle orbits above the box at the sticker's current angle.\n const center = imageToDisplay(\n { x: shape.x + shape.size / 2, y: shape.y + shape.size / 2 },\n viewport,\n );\n const radius = (shape.size / 2) * viewport.scale + ROTATE_HANDLE_OFFSET;\n const theta = (shape.rotation * Math.PI) / 180;\n positionHandle(rotateHandle, {\n x: center.x + radius * Math.sin(theta),\n y: center.y - radius * Math.cos(theta),\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 rotateHandle.remove();\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 */\n/**\n * The rotation (degrees, CW) that aims the box's \"up\" edge toward `pointer`,\n * about the box `center`. Pointer directly above the centre → 0°. Exported so\n * the angle math can be unit-tested without a live gesture.\n */\nexport function emojiRotationFromPointer(\n center: { x: number; y: number },\n pointer: { x: number; y: number },\n): number {\n const deg = (Math.atan2(pointer.y - center.y, pointer.x - center.x) * 180) / Math.PI + 90;\n return normalizeAngle(deg);\n}\n\n/**\n * Drag-to-rotate gesture for the emoji rotate handle. Rotates about the box\n * centre; holding Shift snaps to 15° increments. Returns `null` if the selected\n * shape isn't an emoji (defensive — the handle only shows for emoji).\n */\nfunction startRotateGesture(ctx: ToolGestureContext, origin: PointerEvent): DragHandlers | null {\n const state = ctx.store.get();\n const selected = state.shapes.find((shape) => shape.id === state.selectedId);\n if (selected?.kind !== 'emoji') return null;\n const initial = selected;\n const center = { x: initial.x + initial.size / 2, y: initial.y + initial.size / 2 };\n void origin;\n return {\n onMove(point) {\n let rotation = emojiRotationFromPointer(center, ctx.toImageSpace(point));\n if (point.shiftKey) rotation = normalizeAngle(Math.round(rotation / 15) * 15);\n ctx.store.update((cur) => replaceShape(cur, { ...initial, rotation: Math.round(rotation) }));\n },\n onCommit() {\n ctx.commit();\n },\n onCancel() {\n ctx.store.update((cur) => replaceShape(cur, initial));\n },\n };\n}\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\n/**\n * Map a handle drag to a new shape. Exported for unit testing; the gesture\n * wrapper calls it on each pointer move.\n */\nexport function 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 isn't handle-resizable (its size is the panel's font-size\n // control). The selection layer hides text handles, so this is\n // unreachable; return unchanged for exhaustiveness.\n return shape;\n case 'emoji': {\n // Uniform square resize: the corner opposite the dragged handle stays\n // anchored, and the box edge follows the larger pointer-axis delta so it\n // never distorts. Only corners are shown; edge handles no-op.\n const corner =\n direction === 'tl' || direction === 'tr' || direction === 'bl' || direction === 'br';\n if (!corner) return shape;\n const { x, y, size } = shape;\n const right = x + size;\n const bottom = y + size;\n const anchorRight = direction === 'tl' || direction === 'bl';\n const anchorBottom = direction === 'tl' || direction === 'tr';\n const dx = anchorRight ? right - image.x : image.x - x;\n const dy = anchorBottom ? bottom - image.y : image.y - y;\n const nextSize = Math.max(EMOJI_MIN_SIZE, Math.round(Math.max(dx, dy)));\n const nextX = anchorRight ? right - nextSize : x;\n const nextY = anchorBottom ? bottom - nextSize : y;\n return { ...shape, x: nextX, y: nextY, size: nextSize };\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 auto-sizes to its content and grows with the\n * text the user types (line breaks via Shift+Enter).\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 cssFontString,\n type SourceImage,\n TEXT_LINE_HEIGHT,\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 /** Re-apply font/colour/alignment/position to the open editor without\n * resetting its text or moving the caret (used when panel controls\n * restyle the text mid-edit). */\n restyle(shape: TextShape, viewport: Viewport): 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-multiline', 'true');\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 commits the edit — but NOT clicks on the\n // annotation panel. The font picker, size stepper, bold/italic and\n // alignment controls are part of the editing session; touching them must\n // restyle the live text, not commit and close the editor. Only clicks\n // elsewhere (the canvas, the page) commit.\n const onPointerDownOutside = (event: PointerEvent): void => {\n if (activeShape === null) return;\n // `event.target` can be a non-Element (e.g. a text node); narrow first so\n // `contains`/`closest` are always safe to call.\n const target = event.target;\n if (!(target instanceof Element)) return;\n if (editor.contains(target)) return;\n if (target.closest('.kalotyp-annotate-panel')) return;\n options.onCommit();\n };\n\n editor.addEventListener('input', onInput);\n editor.addEventListener('keydown', onKeyDown);\n document.addEventListener('pointerdown', onPointerDownOutside, true);\n\n /**\n * Position the editor over the shape and match its font metrics. The editor's\n * OWN text is kept transparent — the visible glyphs are painted on the canvas\n * by the same `paintText` the bake uses, so what you edit is byte-identical to\n * what bakes and can never jump on commit. This element exists only to capture\n * keystrokes and show the caret; aligning a contenteditable's text layout to\n * canvas `textBaseline:'top'` pixel-for-pixel is font-metric-dependent and\n * unreliable, so we don't try — we just place the caret close.\n */\n function applyStyles(shape: TextShape, viewport: Viewport): void {\n // `shape.x, shape.y` is the block's top-left for every alignment (same as\n // the canvas).\n const left = viewport.displayRect.x + shape.x * viewport.scale;\n const top = viewport.displayRect.y + shape.y * viewport.scale;\n editor.style.left = `${left}px`;\n editor.style.top = `${top}px`;\n // Text is invisible (the canvas shows it); only the caret is coloured.\n editor.style.color = 'transparent';\n editor.style.caretColor = shape.color;\n // Same font/size/zoom as the canvas so the caret tracks the painted glyphs\n // horizontally (identical advances) and the line box matches vertically.\n editor.style.font = cssFontString(shape, viewport.scale);\n // The `font` shorthand resets `line-height` to `normal`; pin it to the\n // canvas line-height multiple so the caret height matches a painted line.\n editor.style.lineHeight = String(TEXT_LINE_HEIGHT);\n editor.style.textAlign = shape.textAlign;\n editor.style.transformOrigin = 'top left';\n // Auto-size to content; cap the width so a long line can't slide off-stage.\n editor.style.width = 'max-content';\n const maxWidth = Math.max(100, viewport.displayRect.x + viewport.displayRect.width - left - 8);\n editor.style.maxWidth = `${maxWidth}px`;\n }\n\n return {\n open(shape, viewport, source): void {\n activeShape = shape;\n editor.style.display = '';\n applyStyles(shape, viewport);\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 restyle(shape, viewport): void {\n if (activeShape === null) return;\n activeShape = shape;\n // Restyle only — keep the text and the caret/selection untouched so the\n // user can keep typing after, say, picking a font.\n applyStyles(shape, viewport);\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","/**\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 EmojiShape,\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 * Emoji sticker placement. Unlike the drag-to-create tools, an emoji is placed\n * on press (centred on the pointer at the armed size) and a drag before release\n * just repositions it — so a plain tap is a valid placement, and a press-drag\n * lets you fine-tune the drop point in one gesture. The committed sticker is\n * selected so its resize handles are immediately available.\n */\nexport function startEmojiGesture(\n ctx: ToolGestureContext,\n origin: PointerEvent,\n spec: { emoji: string; size: number },\n): DragHandlers {\n const state = ctx.store.get();\n const { id, nextShapeNumber } = mintShapeId(state);\n const { emoji, size } = spec;\n const draftAt = (clientPoint: { clientX: number; clientY: number }): EmojiShape => {\n const center = ctx.toImageSpace(clientPoint);\n return {\n id,\n kind: 'emoji',\n x: Math.round(center.x - size / 2),\n y: Math.round(center.y - size / 2),\n emoji,\n size,\n rotation: 0,\n };\n };\n let last = draftAt(origin);\n ctx.setLiveShape(last);\n return {\n onMove(point) {\n last = draftAt(point);\n ctx.setLiveShape(last);\n },\n onCommit() {\n ctx.setLiveShape(null);\n ctx.store.update((current) => ({ ...addShape(current, last), 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 defaultEmojiSize,\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 { onEmojiImageLoad, resolveEmojiImage } from './emoji-images.js';\nimport { buildEmojiPicker, type EmojiPickerHandle } from './emoji-picker.js';\nimport { ensureAnnotateFontsLoaded } from './fonts-loader.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 startEmojiGesture,\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 // Shared paint options: the emoji resolver lets `paintShape` draw crisp SVG\n // artwork (preview == bake) and fall back to the OS font until it loads.\n const paintOpts = { resolveEmojiImage };\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(\n stage.shapesCanvas,\n store.get().shapes,\n rect.width,\n rect.height,\n viewport,\n paintOpts,\n );\n paintLiveLayer(stage.liveCanvas, liveShape, rect.width, rect.height, viewport, paintOpts);\n selectionLayer.update(shapeForHandles(store.get()), viewport);\n repositionOpenEditor();\n }\n\n /**\n * Keep the open text editor's overlay aligned with the SAME `viewport` the\n * canvas just painted with. Activating the text tool grows the panel (the\n * font/size/style row), which shrinks the stage and recomputes the viewport\n * scale; without this, the editor would keep the scale captured at open time\n * while the canvas repaints at the new scale — the caret and the painted\n * glyphs would drift apart.\n */\n function repositionOpenEditor(): void {\n if (editingTextId === null) return;\n const editing = store.get().shapes.find((s) => s.id === editingTextId);\n if (editing?.kind === 'text') textEditor.restyle(editing, viewport);\n }\n\n /**\n * The selected shape to draw handles for, or `null` while the text editor is\n * open — the editing text shows its own glyphs on the canvas (the editor\n * overlay is transparent), and we don't want a handle frame around it.\n */\n function shapeForHandles(state: AnnotateState): Shape | null {\n if (editingTextId !== null) return null;\n return selectedShapeOf(state);\n }\n\n function paintShapes(): void {\n const rect = stage.container.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return;\n paintShapesLayer(\n stage.shapesCanvas,\n store.get().shapes,\n rect.width,\n rect.height,\n viewport,\n paintOpts,\n );\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, paintOpts);\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 // Id of the text shape currently open in the inline editor, or null. While\n // editing, that shape is NOT painted on the shapes canvas and its selection\n // handles are suppressed — so the only thing the user sees is the editor\n // itself (one box, WYSIWYG), not the editor stacked over the painted text.\n let editingTextId: string | null = null;\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 editingTextId = null;\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 editingTextId = null;\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 case 'emoji':\n return startEmojiToolGesture(event);\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. The select\n // tool only selects/moves — it never enters text edit. Re-editing a text\n // shape is done with the Text tool (click it to reopen the editor).\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 /**\n * Emoji tool pointer-down. Clicking an existing emoji selects and moves it\n * (a body drag) rather than stacking a new one on top — mirroring how the\n * text tool re-targets existing text. Clicking empty space places a fresh\n * sticker at the armed glyph + default size.\n */\n function startEmojiToolGesture(event: PointerEvent): DragHandlers {\n const state = store.get();\n const image = toImageSpace(event);\n const picked = pickShape(state.shapes, image);\n if (picked?.kind === 'emoji') {\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 return startEmojiGesture(toolContext, event, {\n emoji: state.currentStyle.emoji,\n size: defaultEmojiSize({ width: source.width, height: source.height }),\n });\n }\n\n function startTextGesture(event: PointerEvent): void {\n const state = store.get();\n const image = toImageSpace(event);\n // Clicking an existing text box re-opens its editor instead of spawning a\n // new one — so the text tool both creates and edits, and a click never\n // stacks a second box on top of an existing one.\n const picked = pickShape(state.shapes, image);\n if (picked?.kind === 'text') {\n if (state.selectedId !== picked.id) {\n store.update((current) => selectShape(current, picked.id));\n }\n openTextEditor(picked);\n return;\n }\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: state.currentStyle.textAlign,\n fontFamily: state.currentStyle.fontFamily,\n fontWeight: state.currentStyle.fontWeight,\n fontStyle: state.currentStyle.fontStyle,\n };\n store.update((current) => ({ ...addShape(current, shape), nextShapeNumber }));\n // The shape is selected by `addShape`; open the editor on it.\n openTextEditor(shape);\n }\n\n /**\n * Open the transparent input overlay on a text shape and suppress the\n * selection handles while editing. The shape keeps rendering on the canvas\n * (the overlay's own text is transparent), so the glyphs you edit are the\n * same ones that bake — no jump on commit.\n */\n function openTextEditor(shape: TextShape): void {\n editingTextId = shape.id;\n textEditor.open(shape, viewport, source);\n selectionLayer.update(null, viewport);\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 openTextEditor(shape);\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 /** The selected shape if it's a text shape, else `undefined`. */\n function selectedTextShape(): TextShape | undefined {\n const selected = selectedShapeOf(store.get());\n return selected?.kind === 'text' ? selected : undefined;\n }\n\n /**\n * Reflect the active tool / selection in the panel's per-mode controls. Text\n * mode reveals the font row; emoji mode hides the colour + stroke controls.\n * When a text shape is selected, the controls mirror that shape's attributes;\n * otherwise they reflect the current style for new shapes.\n */\n function syncToolControls(state: AnnotateState): void {\n const selected = state.selectedId\n ? state.shapes.find((s) => s.id === state.selectedId)\n : undefined;\n const selectedText = selected?.kind === 'text' ? selected : undefined;\n const showText = state.activeTool === 'text' || selectedText !== undefined;\n const showEmoji = state.activeTool === 'emoji' || selected?.kind === 'emoji';\n panel.setControlsMode({ text: showText, emoji: showEmoji });\n if (selectedText) {\n panel.setStyle({\n ...state.currentStyle,\n fontFamily: selectedText.fontFamily,\n fontSize: selectedText.fontSize,\n fontWeight: selectedText.fontWeight,\n fontStyle: selectedText.fontStyle,\n textAlign: selectedText.textAlign,\n color: selectedText.color,\n });\n } else {\n panel.setStyle(state.currentStyle);\n }\n }\n\n /**\n * Apply a text-attribute change to the panel's `currentStyle` (so the next\n * new text inherits it) and, if a text shape is selected, to that shape.\n * Mirrors the color/stroke flow.\n */\n function applyTextStyle(\n partial: Partial<\n Pick<TextShape, 'fontFamily' | 'fontSize' | 'fontWeight' | 'fontStyle' | 'textAlign'>\n >,\n ): void {\n store.update((current) => {\n let next = setStyle(current, partial);\n const selected = selectedShapeOf(current);\n if (selected?.kind === 'text') {\n next = replaceShape(next, { ...selected, ...partial });\n }\n return next;\n });\n // If we're editing this text, re-render the live editor with the new\n // style so the change shows immediately (and the editor keeps focus —\n // the panel click didn't commit, per the editor's outside-click filter).\n const editing = selectedTextShape();\n if (editingTextId !== null && editing && editing.id === editingTextId) {\n textEditor.restyle(editing, viewport);\n }\n commit();\n }\n\n /**\n * Arm an emoji as the current sticker glyph (so the next placement uses it)\n * and, if an emoji shape is selected, swap that shape's glyph too. Picking\n * closes the picker so the canvas is free to place the armed emoji.\n */\n function applyEmojiSelection(char: string): void {\n store.update((current) => {\n let next = setStyle(current, { emoji: char });\n const selected = selectedShapeOf(current);\n if (selected?.kind === 'emoji') {\n next = replaceShape(next, { ...selected, emoji: char });\n }\n return next;\n });\n commit();\n emojiPicker.hide();\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 // ----- Emoji sticker picker -----\n // A self-contained overlay anchored over the stage. Opens when the emoji tool\n // is selected; picking arms the glyph and closes it (the canvas is then free\n // to place the sticker). Closing the picker exits to the select tool.\n const emojiPicker: EmojiPickerHandle = buildEmojiPicker({\n host: stage.container,\n onSelect: (char) => applyEmojiSelection(char),\n onClose: () => {\n emojiPicker.hide();\n store.update((current) => setActiveTool(current, 'select'));\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) => {\n store.update((current) => setActiveTool(current, tool));\n // Selecting the emoji tool opens the picker (re-clicking it reopens after\n // a pick closed it); switching to any other tool closes it.\n if (tool === 'emoji') emojiPicker.show();\n else emojiPicker.hide();\n },\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 onFontFamilyChange: (fontFamily) => applyTextStyle({ fontFamily }),\n onFontSizeChange: (fontSize) => applyTextStyle({ fontSize }),\n onToggleBold: () => {\n const current = selectedTextShape() ?? null;\n const base = current ? current.fontWeight : store.get().currentStyle.fontWeight;\n applyTextStyle({ fontWeight: base === 'bold' ? 'normal' : 'bold' });\n },\n onToggleItalic: () => {\n const current = selectedTextShape() ?? null;\n const base = current ? current.fontStyle : store.get().currentStyle.fontStyle;\n applyTextStyle({ fontStyle: base === 'italic' ? 'normal' : 'italic' });\n },\n onAlignChange: (textAlign) => applyTextStyle({ textAlign }),\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 // Load the annotation web fonts (idempotent; Ghost may have them already) and\n // repaint when faces arrive — a web font swaps in async with different\n // metrics, so without this the committed text renders with a fallback face\n // and only corrects (appears to \"jump\") on the next interaction.\n const stopFontWatch = ensureAnnotateFontsLoaded(() => paintShapes());\n // Repaint when emoji artwork finishes loading so a placed sticker swaps from\n // the font fallback to the crisp SVG without needing another interaction.\n const stopEmojiWatch = onEmojiImageLoad(() => {\n paintShapes();\n paintLive();\n });\n // Reflect the initial tool/selection in the per-mode control visibility.\n syncToolControls(initialState);\n if (initialState.activeTool === 'emoji') emojiPicker.show();\n // Seed the per-tool hit-area cursor (see annotate.css).\n stage.container.dataset.tool = initialState.activeTool;\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 const toolChanged = next.activeTool !== lastTool;\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 (toolChanged) {\n lastTool = next.activeTool;\n panel.setActiveTool(next.activeTool);\n // Drives the per-tool hit-area cursor (see annotate.css): `select` →\n // default arrow, `text` → I-beam, `emoji` → copy, others → crosshair.\n stage.container.dataset.tool = next.activeTool;\n // Any programmatic switch away from the emoji tool also dismisses the\n // picker (e.g. committing text flips back to select).\n if (next.activeTool !== 'emoji') emojiPicker.hide();\n }\n if (next.currentStyle !== lastStyle) {\n lastStyle = next.currentStyle;\n panel.setStyle(next.currentStyle);\n }\n if (selectionChanged || toolChanged) {\n syncToolControls(next);\n }\n selectionLayer.update(shapeForHandles(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 stopFontWatch();\n stopEmojiWatch();\n removeHitDrag();\n unsubscribe();\n unsubscribeViewport?.();\n resizeObserver.disconnect();\n textEditor.destroy();\n emojiPicker.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 case 'emoji':\n // An emoji carries its own colour; the colour controls are hidden in\n // emoji mode, so this is unreachable — return it untouched.\n return shape;\n }\n}\n\nfunction applyStrokeWidthToShape(shape: Shape, strokeWidth: number): Shape {\n switch (shape.kind) {\n case 'text':\n // Text has no stroke width; its size is the font-size stepper in the\n // text controls. The Width slider is hidden when text is in play, so\n // this branch is unreachable in practice — leave the shape untouched.\n return shape;\n case 'rect':\n case 'ellipse':\n case 'arrow':\n case 'freehand':\n case 'highlight':\n return { ...shape, strokeWidth };\n case 'emoji':\n // No stroke for emoji stickers; the Width control is hidden in emoji mode.\n return shape;\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' | 'emoji'): string {\n switch (kind) {\n case 'rect':\n return 'Rectangle';\n case 'ellipse':\n return 'Ellipse';\n case 'arrow':\n return 'Arrow';\n case 'emoji':\n return 'Emoji';\n }\n}\n","import {\n type AnnotateState,\n bakeAnnotate,\n initialAnnotateState,\n type UtilityPlugin,\n} from '@magicpages/kalotyp-core';\nimport { ensureEmojiImagesLoaded, resolveEmojiImage } from './emoji-images.js';\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: async (state, source) => {\n // Make sure each placed emoji's SVG is loaded so the bake draws the crisp\n // artwork (not the font fallback); bounded by a timeout inside the loader.\n await ensureEmojiImagesLoaded(state.shapes);\n return bakeAnnotate({ shapes: state.shapes }, source, { resolveEmojiImage });\n },\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,26,27,28,29,30,31,32],"mappings":"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"}