@tangle-network/agent-app 0.45.28 → 0.45.29

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.
Files changed (38) hide show
  1. package/dist/{DesignCanvas-3RRPVPCP.js → DesignCanvas-RSWQCXM3.js} +2 -2
  2. package/dist/{DesignCanvasEditor-KAMMEX7E.js → DesignCanvasEditor-XZMGIST7.js} +4 -4
  3. package/dist/assistant/index.d.ts +62 -39
  4. package/dist/assistant/index.js +23 -5
  5. package/dist/assistant/index.js.map +1 -1
  6. package/dist/chat-react/index.d.ts +25 -2
  7. package/dist/chat-react/index.js.map +1 -1
  8. package/dist/chat-routes/index.js +1 -1
  9. package/dist/{chunk-WC43OQMR.js → chunk-3I27SDU3.js} +103 -30
  10. package/dist/chunk-3I27SDU3.js.map +1 -0
  11. package/dist/{chunk-NC2V63EL.js → chunk-FNM7JYME.js} +88 -21
  12. package/dist/chunk-FNM7JYME.js.map +1 -0
  13. package/dist/{chunk-4QVG4WJQ.js → chunk-OYTMILRK.js} +10 -3
  14. package/dist/chunk-OYTMILRK.js.map +1 -0
  15. package/dist/{chunk-OYGM5WLD.js → chunk-PURHUAOR.js} +52 -15
  16. package/dist/chunk-PURHUAOR.js.map +1 -0
  17. package/dist/{chunk-AWPK7ZDS.js → chunk-QGOU3VXH.js} +3 -3
  18. package/dist/{chunk-OTLEYHOG.js → chunk-RRH23CGS.js} +24 -4
  19. package/dist/chunk-RRH23CGS.js.map +1 -0
  20. package/dist/design-canvas-react/engine.d.ts +15 -1
  21. package/dist/design-canvas-react/engine.js +6 -2
  22. package/dist/design-canvas-react/index.d.ts +13 -1
  23. package/dist/design-canvas-react/index.js +13 -8
  24. package/dist/design-canvas-react/index.js.map +1 -1
  25. package/dist/design-canvas-react/lazy.js +1 -1
  26. package/dist/sandbox/index.d.ts +11 -1
  27. package/dist/sandbox/index.js +1 -1
  28. package/dist/web-react/index.d.ts +23 -4
  29. package/dist/web-react/index.js +1 -1
  30. package/package.json +14 -3
  31. package/dist/chunk-4QVG4WJQ.js.map +0 -1
  32. package/dist/chunk-NC2V63EL.js.map +0 -1
  33. package/dist/chunk-OTLEYHOG.js.map +0 -1
  34. package/dist/chunk-OYGM5WLD.js.map +0 -1
  35. package/dist/chunk-WC43OQMR.js.map +0 -1
  36. /package/dist/{DesignCanvas-3RRPVPCP.js.map → DesignCanvas-RSWQCXM3.js.map} +0 -0
  37. /package/dist/{DesignCanvasEditor-KAMMEX7E.js.map → DesignCanvasEditor-XZMGIST7.js.map} +0 -0
  38. /package/dist/{chunk-AWPK7ZDS.js.map → chunk-QGOU3VXH.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/design-canvas-react/components/DesignCanvas.tsx","../src/design-canvas-react/components/BrandKnot.tsx","../src/design-canvas-react/components/ruler-math.ts","../src/design-canvas-react/components/BleedTrimOverlay.tsx","../src/design-canvas-react/components/PagesStrip.tsx","../src/design-canvas-react/components/glyphs.tsx","../src/design-canvas-react/components/icon-button.tsx","../src/design-canvas-react/components/Rulers.tsx","../src/design-canvas-react/components/Toolbar.tsx","../src/design-canvas-react/components/ZoomControls.tsx","../src/design-canvas-react/components/ExportControl.tsx"],"sourcesContent":["/**\n * Root editor shell. Products mount exactly this component. Layout mirrors\n * Polotno: optional left side panel | main column | optional right agent panel.\n *\n * Main column stacks top→bottom:\n * Toolbar (undo/redo, view toggles, selection attrs, page props)\n * Rulers + Workspace (the Workspace is injected by the integrator via the\n * `renderWorkspace` prop — it owns Konva and is Konva-\n * specific; this chrome stays canvas-free)\n * bottom row: PagesStrip + ZoomControls\n *\n * Command lifecycle:\n * - The command stack lives here; every panel receives callbacks, never the\n * stack itself.\n * - `onApplyOperations` is called optimistically after every command. If it\n * rejects, the command is rolled back locally. If it resolves with a fresh\n * `document`, the stack rebases via `stack.reset(document)` — this preserves\n * history while reconciling server-minted ids or normalised values.\n * - `setView` (zoom/pan/selection/toggles) never enters history.\n * - Keyboard: Mod+Z undo · Shift+Mod+Z / Mod+Y redo · Delete/Backspace deletes\n * the selection · F fits the page (forwarded to workspace via ref).\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'\nimport { BrandKnot } from './BrandKnot'\nimport type { SceneDocument, SceneElement } from '../../design-canvas/model'\nimport { findElement, requirePage } from '../../design-canvas/model'\nimport type { SceneAttrsPatch, SceneOperation } from '../../design-canvas/operations'\nimport type { PageBleed } from '../../design-canvas/model'\nimport type { DesignCanvasProps, ExportTriggerOptions, SceneCommand } from '../contracts'\nimport { createSceneCommandStack } from '../engine/command-stack'\nimport {\n addPageCommand,\n bindSlotCommand,\n deleteElementCommand,\n deletePageCommand,\n duplicatePageCommand,\n groupElementsCommand,\n multiSetAttrsCommand,\n reorderElementCommand,\n reorderPageCommand,\n setAttrsCommand,\n setPageGuidesCommand,\n setPagePropsCommand,\n ungroupElementCommand,\n} from '../engine/commands'\nimport { clampIndex, indexBackward, indexForward, topIndex } from './ruler-math'\nimport { BleedTrimOverlay } from './BleedTrimOverlay'\nimport { LayersPanel } from './LayersPanel'\nimport { PagesStrip } from './PagesStrip'\nimport { Rulers } from './Rulers'\nimport { Toolbar } from './Toolbar'\nimport { ZoomControls } from './ZoomControls'\nimport { ExportControl } from './ExportControl'\n\n/** Callers inject a workspace renderer so this chrome stays Konva-free. The\n * workspace occupies the scrollable area between the rulers and the bottom bar. */\nexport interface DesignCanvasFullProps extends DesignCanvasProps {\n /**\n * Render the Konva canvas workspace into the slot this shell provides.\n * The shell passes viewport dimensions, view-state, and the shared command\n * stack so `WorkspaceView` can commit gestures through the same stack the\n * chrome uses for undo/redo and layers-panel selection.\n *\n * `onFitRef` is a ref the workspace fills with a fit-page callback; the\n * shell calls it when the user presses F or clicks the Fit button.\n */\n renderWorkspace(ctx: {\n document: SceneDocument\n activePageId: string\n selectedElementIds: string[]\n zoom: number\n panX: number\n panY: number\n gridEnabled: boolean\n gridSize: number\n snapEnabled: boolean\n showBleed: boolean\n canWrite: boolean\n /** The chrome's command stack. Pass to WorkspaceView so gestures, undo,\n * and layers-panel selection share a single state machine. */\n stack: ReturnType<typeof createSceneCommandStack>\n activePage: SceneDocument['pages'][number] | undefined\n onFitRef: React.MutableRefObject<(() => void) | null>\n /** A ref the workspace fills with an export callback. The chrome's Export\n * control calls it with the chosen format/scale; the workspace renders the\n * Konva stage to a data URL and forwards the result to `onExport`. Filled\n * only when `onExport` is wired (the workspace skips it otherwise). */\n onExportRef: React.MutableRefObject<((opts: ExportTriggerOptions) => void) | null>\n /** Forwarded from DesignCanvasProps. Default true: WorkspaceView fits the\n * active page to the viewport once, on the first non-zero measurement. */\n fitOnMount?: boolean\n /** Forwarded from DesignCanvasProps. Fires once after the first real\n * measurement, after the initial fit is applied (or skipped). */\n onReady?(): void\n /** Forwarded from DesignCanvasProps. The Konva render palette; the\n * Konva-free chrome passes it straight through to the workspace. */\n render?: DesignCanvasProps['render']\n /** Forwarded from DesignCanvasProps. Toggles the branded in-canvas empty\n * state and its agent door. */\n showEmptyState?: DesignCanvasProps['showEmptyState']\n onAskAgent?: DesignCanvasProps['onAskAgent']\n onZoomChange(zoom: number): void\n onPanChange(panX: number, panY: number): void\n onSelectElements(ids: string[], additive: boolean): void\n }): React.ReactNode\n\n /**\n * Generates page thumbnails for the PagesStrip. Injected by the integrator\n * (who has Konva access) so the chrome doesn't import Konva directly.\n */\n renderThumbnail(page: SceneDocument['pages'][number]): Promise<string | null>\n}\n\nfunction mintId(): string {\n const uuid = globalThis.crypto && 'randomUUID' in globalThis.crypto\n ? globalThis.crypto.randomUUID()\n : null\n return `local-${uuid ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}`\n}\n\nfunction isTypingTarget(target: EventTarget | null): boolean {\n return (\n target instanceof Element &&\n target.closest('input, textarea, select, [contenteditable=\"true\"]') !== null\n )\n}\n\n/** Commit a command: execute optimistically, persist via `onApplyOperations`,\n * roll back locally on rejection. The command reference is captured at call\n * time so the rejection handler removes that specific command regardless of\n * how many further commands the user has executed while the save was pending. */\nfunction useCommitCommand(\n stack: ReturnType<typeof createSceneCommandStack>,\n onApplyOperations: DesignCanvasProps['onApplyOperations'],\n canWrite: boolean,\n setError: (msg: string | null) => void,\n) {\n return useCallback(\n (command: SceneCommand) => {\n if (!canWrite) return\n try {\n stack.execute(command)\n } catch (error) {\n setError(error instanceof Error ? error.message : String(error))\n return\n }\n const ops = command.operations()\n void onApplyOperations(ops)\n .then((result) => {\n if (result.document) {\n // Server re-minted ids or normalised the document: rebase without\n // clearing history. History holds transforms, not snapshots, so\n // rebasing cannot stale it; a later undo re-applies the inverse\n // transform to the current (rebased) document.\n stack.reset(result.document)\n }\n })\n .catch((error: unknown) => {\n // Roll back the specific command that failed. stack.rollback() splices\n // this command out of history and applies its inverse to the current\n // state — commands the user executed while this save was in-flight are\n // not disturbed (their undo entries are preserved). If the command is\n // no longer in the stack (double-fire or already reset), rollback is a\n // safe no-op.\n stack.rollback(command)\n setError(error instanceof Error ? error.message : String(error))\n })\n },\n [stack, onApplyOperations, canWrite, setError],\n )\n}\n\nexport function DesignCanvas({\n document: initialDocument,\n rev: initialRev,\n canWrite,\n mode = 'edit',\n onApplyOperations,\n onSelectionChange,\n renderAgentPanel,\n renderSidePanel,\n onExport,\n exportDefaults,\n className,\n fitOnMount,\n onReady,\n render,\n showEmptyState,\n onAskAgent,\n pageSizeLabel,\n enableBleedLabel,\n fitLabel,\n renderWorkspace,\n renderThumbnail,\n}: DesignCanvasFullProps) {\n // The command stack is created once from the initial document. Subsequent\n // server refreshes come in via the prop and are applied via reset().\n const stack = useMemo(\n () => createSceneCommandStack(initialDocument, initialDocument.pages[0]?.id ?? 'page-1'),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n )\n\n const editorState = useSyncExternalStore(stack.subscribe, stack.getState, stack.getState)\n\n // Track the applied document prop to detect server refreshes.\n const appliedDocumentRef = useRef(initialDocument)\n useEffect(() => {\n if (appliedDocumentRef.current === initialDocument) return\n appliedDocumentRef.current = initialDocument\n stack.reset(initialDocument)\n }, [initialDocument, stack])\n\n const [commitError, setCommitError] = useState<string | null>(null)\n\n const commit = useCommitCommand(stack, onApplyOperations, canWrite, setCommitError)\n\n // Notify host when selection changes.\n const selectionChangeRef = useRef(onSelectionChange)\n selectionChangeRef.current = onSelectionChange\n useEffect(() => {\n const page = editorState.document.pages.find((p) => p.id === editorState.activePageId)\n if (!page) return\n const selected = editorState.selectedElementIds\n .map((id) => page.elements.find((el) => el.id === id))\n .filter((el): el is SceneElement => el !== undefined)\n selectionChangeRef.current?.(selected)\n }, [editorState.selectedElementIds, editorState.activePageId, editorState.document])\n\n // Workspace fit callback ref — the workspace fills this; the shell calls it.\n const fitRef = useRef<(() => void) | null>(null)\n\n // Workspace export callback ref — the workspace (Konva owner) fills this; the\n // chrome's Export control calls it with the chosen format/scale.\n const exportRef = useRef<((opts: ExportTriggerOptions) => void) | null>(null)\n\n // ---------------------------------------------------------------------------\n // View-state helpers (no history)\n // ---------------------------------------------------------------------------\n\n const setZoom = useCallback((zoom: number) => stack.setView({ zoom }), [stack])\n const setPan = useCallback((panX: number, panY: number) => stack.setView({ panX, panY }), [stack])\n const setActivePage = useCallback((activePageId: string) => stack.setView({ activePageId, selectedElementIds: [] }), [stack])\n\n const setSelectedElements = useCallback(\n (ids: string[], additive: boolean) => {\n if (!additive) {\n stack.setView({ selectedElementIds: ids })\n return\n }\n const current = new Set(editorState.selectedElementIds)\n for (const id of ids) {\n if (current.has(id)) current.delete(id)\n else current.add(id)\n }\n stack.setView({ selectedElementIds: [...current] })\n },\n [stack, editorState.selectedElementIds],\n )\n\n // ---------------------------------------------------------------------------\n // Element commands\n // ---------------------------------------------------------------------------\n\n const handleSetAttrs = useCallback(\n (elementId: string, attrs: SceneAttrsPatch) => {\n const page = requirePage(editorState.document, editorState.activePageId)\n // findElement searches group children; page.elements.find misses nested elements.\n const found = findElement(page, elementId)\n if (!found) return\n if (found.element.locked) return\n const priorAttrs: SceneAttrsPatch = Object.fromEntries(\n Object.keys(attrs).map((k) => [k, (found.element as unknown as Record<string, unknown>)[k]]),\n ) as SceneAttrsPatch\n commit(\n setAttrsCommand({\n pageId: editorState.activePageId,\n elementId,\n attrs,\n priorAttrs,\n }),\n )\n },\n [commit, editorState.activePageId, editorState.document],\n )\n\n const handleMultiSetAttrs = useCallback(\n (patches: Array<{ elementId: string; attrs: SceneAttrsPatch }>) => {\n const page = requirePage(editorState.document, editorState.activePageId)\n const entries = patches.flatMap(({ elementId, attrs }) => {\n const found = findElement(page, elementId)\n // Skip elements that are missing (concurrent delete) or locked.\n if (!found || found.element.locked) return []\n const priorAttrs = Object.fromEntries(\n Object.keys(attrs).map((k) => [k, (found.element as unknown as Record<string, unknown>)[k]]),\n ) as SceneAttrsPatch\n return [{ pageId: editorState.activePageId, elementId, attrs, priorAttrs }]\n })\n if (entries.length === 0) return\n commit(multiSetAttrsCommand(entries))\n },\n [commit, editorState.activePageId, editorState.document],\n )\n\n const handleReorder = useCallback(\n (elementId: string, toIndex: number, ownerLength: number, direction: 'front' | 'back' | 'forward' | 'backward') => {\n const page = requirePage(editorState.document, editorState.activePageId)\n // findElement searches group children so nested elements are reorderable.\n const found = findElement(page, elementId)\n if (!found) return\n const currentIndex = found.index\n const target =\n direction === 'front'\n ? topIndex(ownerLength)\n : direction === 'back'\n ? 0\n : direction === 'forward'\n ? indexForward(currentIndex, ownerLength)\n : indexBackward(currentIndex)\n const clamped = clampIndex(target, ownerLength)\n if (clamped === currentIndex) return\n commit(reorderElementCommand({ pageId: editorState.activePageId, elementId, toIndex: clamped }))\n },\n [commit, editorState.activePageId, editorState.document],\n )\n\n const handleDelete = useCallback(\n (elementIds: string[]) => {\n for (const elementId of elementIds) {\n commit(\n deleteElementCommand({\n document: editorState.document,\n pageId: editorState.activePageId,\n elementId,\n }),\n )\n }\n },\n [commit, editorState.document, editorState.activePageId],\n )\n\n const handleGroup = useCallback(\n (elementIds: string[]) => {\n commit(\n groupElementsCommand({\n document: editorState.document,\n pageId: editorState.activePageId,\n elementIds,\n groupId: mintId(),\n }),\n )\n },\n [commit, editorState.document, editorState.activePageId],\n )\n\n const handleUngroup = useCallback(\n (groupId: string) => {\n commit(ungroupElementCommand({ document: editorState.document, pageId: editorState.activePageId, groupId }))\n },\n [commit, editorState.document, editorState.activePageId],\n )\n\n const handleBindSlot = useCallback(\n (elementId: string, slot: string | null) => {\n commit(bindSlotCommand({ document: editorState.document, pageId: editorState.activePageId, elementId, slot }))\n },\n [commit, editorState.document, editorState.activePageId],\n )\n\n // ---------------------------------------------------------------------------\n // Page commands\n // ---------------------------------------------------------------------------\n\n const handleSetPageProps = useCallback(\n (props: { name?: string; width?: number; height?: number; background?: string; bleed?: PageBleed | null }) => {\n commit(setPagePropsCommand({ document: editorState.document, pageId: editorState.activePageId, props }))\n },\n [commit, editorState.document, editorState.activePageId],\n )\n\n const handleSetPageGuides = useCallback(\n (guides: { vertical: number[]; horizontal: number[] }) => {\n commit(setPageGuidesCommand({ document: editorState.document, pageId: editorState.activePageId, guides }))\n },\n [commit, editorState.document, editorState.activePageId],\n )\n\n const handleAddPage = useCallback(() => {\n const pageId = mintId()\n commit(addPageCommand({ pageId }))\n setActivePage(pageId)\n }, [commit, setActivePage])\n\n const handleDuplicatePage = useCallback(\n (sourcePageId: string) => {\n const pageId = mintId()\n commit(duplicatePageCommand({ document: editorState.document, sourcePageId, pageId }))\n setActivePage(pageId)\n },\n [commit, editorState.document, setActivePage],\n )\n\n const handleDeletePage = useCallback(\n (pageId: string) => {\n commit(deletePageCommand({ document: editorState.document, pageId }))\n },\n [commit, editorState.document],\n )\n\n const handleReorderPage = useCallback(\n (pageId: string, toIndex: number) => {\n commit(reorderPageCommand({ pageId, toIndex }))\n },\n [commit],\n )\n\n // ---------------------------------------------------------------------------\n // Undo / redo\n // ---------------------------------------------------------------------------\n\n const handleUndo = useCallback(() => {\n if (!canWrite || !stack.canUndo()) return\n let command\n try {\n command = stack.undo()\n } catch (error) {\n setCommitError(`Undo failed: ${error instanceof Error ? error.message : String(error)}`)\n return\n }\n // Persist the inverse so the server tracks the undo; on rejection re-apply\n // THIS command's forward transform (stack.reexecute) — captured at undo\n // time, not a blind canRedo()/redo() that diverges from the server when an\n // edit interleaves before the rejection lands.\n void onApplyOperations(command.inverseOperations())\n .then((result) => {\n if (result.document) stack.reset(result.document)\n })\n .catch((error: unknown) => {\n stack.reexecute(command)\n setCommitError(error instanceof Error ? error.message : String(error))\n })\n }, [stack, canWrite, onApplyOperations])\n\n const handleRedo = useCallback(() => {\n if (!canWrite || !stack.canRedo()) return\n let command\n try {\n command = stack.redo()\n } catch (error) {\n setCommitError(`Redo failed: ${error instanceof Error ? error.message : String(error)}`)\n return\n }\n // Persist the redo; on rejection re-apply THIS command's inverse\n // (stack.reundo) — captured at redo time, not a blind canUndo()/undo() that\n // undoes the wrong command when an edit interleaves before the rejection.\n void onApplyOperations(command.operations())\n .then((result) => {\n if (result.document) stack.reset(result.document)\n })\n .catch((error: unknown) => {\n stack.reundo(command)\n setCommitError(error instanceof Error ? error.message : String(error))\n })\n }, [stack, canWrite, onApplyOperations])\n\n // ---------------------------------------------------------------------------\n // Keyboard shortcuts\n // ---------------------------------------------------------------------------\n\n useEffect(() => {\n function onKeyDown(event: KeyboardEvent) {\n const mod = event.metaKey || event.ctrlKey\n if (mod && !isTypingTarget(event.target)) {\n if (event.key.toLowerCase() === 'z') {\n event.preventDefault()\n if (event.shiftKey) handleRedo()\n else handleUndo()\n return\n }\n if (event.key.toLowerCase() === 'y') {\n event.preventDefault()\n handleRedo()\n return\n }\n }\n if ((event.key === 'Delete' || event.key === 'Backspace') && !isTypingTarget(event.target)) {\n if (!canWrite || editorState.selectedElementIds.length === 0) return\n event.preventDefault()\n handleDelete(editorState.selectedElementIds)\n return\n }\n if (event.key.toLowerCase() === 'f' && !isTypingTarget(event.target)) {\n event.preventDefault()\n fitRef.current?.()\n }\n }\n window.addEventListener('keydown', onKeyDown)\n return () => window.removeEventListener('keydown', onKeyDown)\n })\n\n // ---------------------------------------------------------------------------\n // Derived state for render\n // ---------------------------------------------------------------------------\n\n const activePage = useMemo(\n () => editorState.document.pages.find((p) => p.id === editorState.activePageId),\n [editorState.document, editorState.activePageId],\n )\n\n const selectedElements = useMemo(() => {\n if (!activePage) return []\n return editorState.selectedElementIds\n .map((id) => activePage.elements.find((el) => el.id === id))\n .filter((el): el is SceneElement => el !== undefined)\n }, [activePage, editorState.selectedElementIds])\n\n const review = mode === 'review'\n\n if (!activePage) {\n return (\n <div className={`flex h-full items-center justify-center bg-[var(--bg-input)] text-[var(--text-muted)] ${className ?? ''}`}>\n No pages in document\n </div>\n )\n }\n\n // Bleed extents in screen px for the overlay\n const bleedScreen = editorState.showBleed && activePage.bleed\n ? {\n top: activePage.bleed.top * editorState.zoom,\n right: activePage.bleed.right * editorState.zoom,\n bottom: activePage.bleed.bottom * editorState.zoom,\n left: activePage.bleed.left * editorState.zoom,\n }\n : null\n\n return (\n <div className={`flex h-full min-h-0 bg-[var(--bg-input)] text-[var(--text-primary)] ${className ?? ''}`}>\n {/* Optional left side panel (asset/template browser, insert panel, etc.).\n Suppressed in review mode: the lean reviewer hides authoring/insert\n surfaces regardless of what the integrator passes. */}\n {renderSidePanel && !review ? (\n <aside className=\"hidden w-64 shrink-0 flex-col overflow-hidden border-r border-[var(--border-default)] lg:flex\">\n {renderSidePanel({ selectedElements, activePageId: activePage.id, activePage })}\n </aside>\n ) : null}\n\n {/* Main column */}\n <div className=\"flex min-w-0 flex-1 flex-col\">\n {/* Toolbar + chrome-right controls (Export). The Toolbar grows to fill;\n the Export control is pinned to the top-right of the chrome. */}\n <div className=\"flex shrink-0 items-stretch\">\n <div className=\"min-w-0 flex-1\">\n <Toolbar\n page={activePage}\n selectedElements={selectedElements}\n canWrite={canWrite}\n mode={mode}\n canUndo={stack.canUndo()}\n canRedo={stack.canRedo()}\n gridEnabled={editorState.gridEnabled}\n snapEnabled={editorState.snapEnabled}\n showRulers={editorState.showRulers}\n showBleed={editorState.showBleed}\n onUndo={handleUndo}\n onRedo={handleRedo}\n onToggleGrid={() => stack.setView({ gridEnabled: !editorState.gridEnabled })}\n onToggleSnap={() => stack.setView({ snapEnabled: !editorState.snapEnabled })}\n onToggleRulers={() => stack.setView({ showRulers: !editorState.showRulers })}\n onToggleBleed={() => stack.setView({ showBleed: !editorState.showBleed })}\n onSetAttrs={handleSetAttrs}\n onSetPageProps={handleSetPageProps}\n onSetPageGuides={handleSetPageGuides}\n onReorder={handleReorder}\n onGroup={handleGroup}\n onUngroup={handleUngroup}\n onDelete={handleDelete}\n onBindSlot={handleBindSlot}\n pageSizeLabel={pageSizeLabel}\n enableBleedLabel={enableBleedLabel}\n />\n </div>\n {onExport ? (\n <div className=\"flex shrink-0 items-center border-b border-l border-[var(--border-default)] bg-[var(--bg-input)] px-2\">\n <ExportControl\n defaults={exportDefaults}\n onExport={(opts) => exportRef.current?.(opts)}\n />\n </div>\n ) : null}\n </div>\n\n {/* Error bar */}\n {commitError ? (\n <div\n className=\"flex shrink-0 items-center justify-between gap-3 border-b border-[hsl(var(--destructive)/0.3)] bg-[hsl(var(--destructive)/0.1)] px-3 py-1.5 text-xs text-[var(--text-danger)]\"\n role=\"alert\"\n >\n <span className=\"min-w-0 truncate\">{commitError}</span>\n <button\n type=\"button\"\n onClick={() => setCommitError(null)}\n className=\"shrink-0 underline-offset-2 hover:underline\"\n >\n Dismiss\n </button>\n </div>\n ) : null}\n\n {/* Phones (<640px): the Konva editor is unusable squeezed, so show a\n clean, on-brand notice instead of a broken canvas. The full editor\n area renders from sm: up. */}\n <div className=\"flex min-h-0 flex-1 flex-col items-center justify-center gap-3 px-6 text-center sm:hidden\">\n <BrandKnot size={32} className=\"shrink-0\" />\n <div className=\"flex flex-col gap-1\">\n <p className=\"text-sm font-semibold text-[var(--text-primary)]\">Best edited on a larger screen</p>\n <p className=\"text-xs leading-5 text-[var(--text-secondary)]\">\n The design canvas needs room to work. Open this on a tablet or desktop to edit pages, elements, and export.\n </p>\n </div>\n </div>\n\n {/* Rulers + Workspace area. When rulers are shown the slot is inset by\n 20px (RULER_SIZE_PX in Rulers.tsx) so the Konva stage origin\n coincides with the ruler tracks' origin — the stage's doc→screen\n mapping (panX + doc·zoom) then agrees with every tick, marker, and\n guide line (previously a uniform 20px offset). The ruler tracks,\n guide overlay, and corner filler are absolutely positioned against\n the slot's padding box, which padding does not move; only the\n in-flow workspace shifts beneath them. */}\n <div className={`relative hidden min-h-0 flex-1 sm:block ${editorState.showRulers ? 'pl-[20px] pt-[20px]' : ''}`}>\n {/* Rulers overlay inside the workspace container */}\n <Rulers\n pageWidth={activePage.width}\n pageHeight={activePage.height}\n zoom={editorState.zoom}\n scrollLeft={-editorState.panX / editorState.zoom}\n scrollTop={-editorState.panY / editorState.zoom}\n showRulers={editorState.showRulers}\n guides={activePage.guides}\n onGuidesChange={handleSetPageGuides}\n />\n\n {/* Bleed overlay — absolutely positioned over the workspace. Its\n origin must track the stage, so it takes the same ruler inset. */}\n {bleedScreen && activePage.bleed ? (\n <div\n className=\"pointer-events-none absolute inset-0 z-10 overflow-hidden\"\n aria-hidden\n >\n <div\n style={{\n position: 'absolute',\n left: editorState.panX + (editorState.showRulers ? 20 : 0),\n top: editorState.panY + (editorState.showRulers ? 20 : 0),\n }}\n >\n <BleedTrimOverlay\n pageWidthPx={activePage.width * editorState.zoom}\n pageHeightPx={activePage.height * editorState.zoom}\n bleed={bleedScreen}\n />\n </div>\n </div>\n ) : null}\n\n {/* Workspace slot (Konva; injected by integrator) */}\n {renderWorkspace({\n document: editorState.document,\n activePageId: editorState.activePageId,\n selectedElementIds: editorState.selectedElementIds,\n zoom: editorState.zoom,\n panX: editorState.panX,\n panY: editorState.panY,\n gridEnabled: editorState.gridEnabled,\n gridSize: editorState.gridSize,\n snapEnabled: editorState.snapEnabled,\n showBleed: editorState.showBleed,\n canWrite,\n stack,\n activePage,\n onFitRef: fitRef,\n onExportRef: exportRef,\n fitOnMount,\n onReady,\n render,\n // Review mode hides authoring affordances; the empty state's doors\n // create content, so they only belong on the editor surface.\n showEmptyState: review ? false : showEmptyState,\n onAskAgent,\n onZoomChange: setZoom,\n onPanChange: setPan,\n onSelectElements: setSelectedElements,\n })}\n </div>\n\n {/* Bottom row: PagesStrip + ZoomControls */}\n <div className=\"flex shrink-0 items-stretch border-t border-[var(--border-default)]\">\n <div className=\"min-w-0 flex-1 overflow-hidden\">\n <PagesStrip\n pages={editorState.document.pages}\n activePageId={editorState.activePageId}\n canWrite={canWrite}\n canManagePages={!review}\n renderThumbnail={renderThumbnail}\n onSelectPage={setActivePage}\n onAddPage={handleAddPage}\n onDuplicatePage={handleDuplicatePage}\n onDeletePage={handleDeletePage}\n onReorderPage={handleReorderPage}\n />\n </div>\n <div className=\"flex shrink-0 items-center border-l border-[var(--border-default)]\">\n <ZoomControls\n zoom={editorState.zoom}\n onZoom={setZoom}\n onFit={() => fitRef.current?.()}\n fitLabel={fitLabel}\n />\n </div>\n </div>\n </div>\n\n {/* Optional right agent panel */}\n {renderAgentPanel ? (\n <aside className=\"hidden w-80 shrink-0 flex-col overflow-hidden border-l border-[var(--border-default)] lg:flex\">\n {renderAgentPanel({ selectedElements, activePageId: editorState.activePageId })}\n </aside>\n ) : null}\n\n {/* Layers panel lives in the left side panel slot by convention; products\n that want it standalone can render LayersPanel directly. */}\n </div>\n )\n}\n","/**\n * Load the canonical Tangle knot only when a canvas surface renders it.\n * `/brand` imports the optional `@tangle-network/brand` peer, so a failed load\n * renders a fixed-size spacer instead of crashing the editor.\n */\n\nimport { lazy, Suspense } from 'react'\nimport type { ComponentType } from 'react'\n\ninterface BrandKnotProps {\n size?: number\n className?: string\n}\n\nfunction MarkSpacer({ size = 24, className }: BrandKnotProps) {\n return <span aria-hidden style={{ display: 'inline-block', width: size, height: size }} className={className} />\n}\n\nconst LazyKnot = lazy(async () => {\n try {\n const mod = await import('../../brand')\n return { default: mod.TangleKnot as ComponentType<BrandKnotProps> }\n } catch {\n return { default: MarkSpacer as ComponentType<BrandKnotProps> }\n }\n})\n\nexport function BrandKnot({ size = 24, className }: BrandKnotProps) {\n return (\n <Suspense fallback={<MarkSpacer size={size} className={className} />}>\n <LazyKnot size={size} className={className} />\n </Suspense>\n )\n}\n","/**\n * Pure ruler tick-step and label math for the canvas rulers. Nothing here\n * touches React or the DOM — all interaction geometry is extracted so it can\n * be unit-tested without a browser.\n *\n * Canvas rulers show document-coordinate values (CSS px). Tick density adapts\n * to the current zoom so major ticks never sit closer than `minMajorSpacingPx`\n * screen pixels apart. The step table covers typical design zoom ranges; beyond\n * the table the step grows by doubling the last candidate.\n */\n\n/** Ordered candidate major-tick steps in document px. Chosen so common design\n * values (10, 50, 100, 500…) are always available as label boundaries. */\nconst TICK_STEP_CANDIDATES_PX = [1, 2, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000] as const\n\n/** Define spacing and visibility rules for major and minor ticks in a coordinate system */\nexport interface TickStep {\n /** Document-coordinate step between major ticks. */\n major: number\n /** Document-coordinate step between minor ticks (major / 5). */\n minor: number\n /** True when minor ticks should be drawn (they sit ≥ minMinorSpacingPx apart). */\n drawMinor: boolean\n}\n\n/**\n * Select the major tick step that keeps major ticks ≥ minMajorSpacingPx apart\n * at the given zoom. Minor ticks are rendered when they'd clear minMinorSpacingPx.\n *\n * Both spacing thresholds are SCREEN pixels — the caller provides `zoom` (screen\n * px per document px) so the result is zoom-independent.\n */\nexport function selectTickStep(input: {\n zoom: number\n minMajorSpacingPx?: number\n minMinorSpacingPx?: number\n}): TickStep {\n if (!Number.isFinite(input.zoom) || input.zoom <= 0) {\n throw new Error(`zoom must be a positive finite number, got ${input.zoom}`)\n }\n const minMajor = input.minMajorSpacingPx ?? 40\n const minMinor = input.minMinorSpacingPx ?? 8\n\n let major = TICK_STEP_CANDIDATES_PX[TICK_STEP_CANDIDATES_PX.length - 1]!\n for (const candidate of TICK_STEP_CANDIDATES_PX) {\n if (candidate * input.zoom >= minMajor) {\n major = candidate\n break\n }\n }\n // Beyond the table: double until the constraint is satisfied.\n while (major * input.zoom < minMajor) {\n major = major * 2\n }\n\n const minor = major / 5\n const drawMinor = minor * input.zoom >= minMinor\n\n return { major, minor, drawMinor }\n}\n\n/** Define a ruler tick with a position and optional label for measurement markings */\nexport interface RulerTick {\n /** Position in document coordinates. */\n position: number\n /** Label text, or null for a minor tick. */\n label: string | null\n}\n\n/**\n * Generate all ticks visible in a ruler of `documentLength` document-px,\n * given the current tick step. The caller clips to the viewport; this produces\n * all ticks for the full document extent so the ruler can be rendered\n * declaratively without a separate clipping pass.\n */\nexport function buildRulerTicks(input: {\n documentLength: number\n step: TickStep\n}): RulerTick[] {\n if (input.documentLength <= 0) return []\n\n const { major, minor, drawMinor } = input.step\n const ticks: RulerTick[] = []\n\n for (let pos = 0; pos <= input.documentLength; pos += major) {\n ticks.push({ position: pos, label: formatRulerLabel(pos) })\n if (!drawMinor) continue\n for (let m = 1; m < 5; m += 1) {\n const minorPos = pos + m * minor\n if (minorPos >= input.documentLength) break\n ticks.push({ position: minorPos, label: null })\n }\n }\n\n return ticks\n}\n\n/** Format a document-px position as a compact label: integers stay whole,\n * decimals are rounded to 1 place. Values ≥ 1000 are compacted to \"1k\" etc. */\nexport function formatRulerLabel(value: number): string {\n if (!Number.isFinite(value)) return ''\n if (Math.abs(value) >= 1000) {\n const k = value / 1000\n return `${Number.isInteger(k) ? k : k.toFixed(1)}k`\n }\n return Number.isInteger(value) ? String(value) : value.toFixed(1)\n}\n\n/**\n * Convert a screen-coordinate pointer position to a document-coordinate guide\n * drop position, given the ruler's scroll offset and zoom. Used by both the\n * horizontal and vertical ruler drag-guide creation paths.\n *\n * `scrollOffset` is how many document-px of the ruler are scrolled off-screen\n * to the left/top. `pointerScreenPx` is the cursor position in screen px\n * relative to the ruler element's origin.\n */\nexport function screenToDocumentPosition(input: {\n pointerScreenPx: number\n scrollOffset: number\n zoom: number\n}): number {\n if (!Number.isFinite(input.zoom) || input.zoom <= 0) {\n throw new Error(`zoom must be a positive finite number, got ${input.zoom}`)\n }\n return input.pointerScreenPx / input.zoom + input.scrollOffset\n}\n\n/** Snap a guide drop position to the nearest major tick if within\n * `snapThresholdPx` document px; otherwise returns the raw position. */\nexport function snapGuideToTick(position: number, step: TickStep, snapThresholdPx: number): number {\n const nearest = Math.round(position / step.major) * step.major\n return Math.abs(nearest - position) <= snapThresholdPx ? nearest : position\n}\n\n// ---------------------------------------------------------------------------\n// Z-order index math — extracted so Toolbar and LayersPanel agree\n// ---------------------------------------------------------------------------\n\n/** Index of the topmost element for a given owner length. */\nexport function topIndex(ownerLength: number): number {\n return ownerLength - 1\n}\n\n/** Move an element one step toward the top (higher index = above in z-order).\n * Returns the current index unchanged if already at the top. */\nexport function indexForward(current: number, ownerLength: number): number {\n return Math.min(current + 1, ownerLength - 1)\n}\n\n/** Move an element one step toward the bottom (lower index = below). Returns\n * the current index unchanged if already at the bottom. */\nexport function indexBackward(current: number): number {\n return Math.max(current - 1, 0)\n}\n\n/** Clamp an arbitrary target index to valid range. */\nexport function clampIndex(target: number, ownerLength: number): number {\n return Math.max(0, Math.min(target, ownerLength - 1))\n}\n","/**\n * Semi-transparent bleed tint drawn OUTSIDE the page bounds, with trim-mark\n * corner indicators. Renders as an absolutely-positioned div layered over the\n * workspace canvas; node name 'overlay:bleed' lets automated tests target it.\n *\n * Conditionally rendered: parent mounts this only when `showBleed && bleed`.\n * All dimensions are in SCREEN pixels (caller applies the zoom factor).\n */\n\nimport type { PageBleed } from '../../design-canvas/model'\n\ninterface BleedTrimOverlayProps {\n /** Page dimensions in screen pixels (already multiplied by zoom). */\n pageWidthPx: number\n pageHeightPx: number\n /** Bleed extents in SCREEN pixels (caller multiplies doc-px by zoom). */\n bleed: {\n top: number\n right: number\n bottom: number\n left: number\n }\n}\n\n/** Length in screen pixels of each trim-mark arm. */\nconst TRIM_MARK_PX = 12\n\n/** How far trim marks sit from the page corner (in screen px). */\nconst TRIM_MARK_OFFSET_PX = 4\n\nexport function BleedTrimOverlay({ pageWidthPx, pageHeightPx, bleed }: BleedTrimOverlayProps) {\n const totalW = bleed.left + pageWidthPx + bleed.right\n const totalH = bleed.top + pageHeightPx + bleed.bottom\n\n return (\n <div\n data-node=\"overlay:bleed\"\n className=\"pointer-events-none absolute\"\n style={{\n top: -bleed.top,\n left: -bleed.left,\n width: totalW,\n height: totalH,\n }}\n aria-hidden\n >\n {/* Bleed tint strips — four sides */}\n {/* top */}\n <div\n className=\"absolute bg-rose-500/10\"\n style={{ top: 0, left: 0, width: totalW, height: bleed.top }}\n />\n {/* bottom */}\n <div\n className=\"absolute bg-rose-500/10\"\n style={{ bottom: 0, left: 0, width: totalW, height: bleed.bottom }}\n />\n {/* left */}\n <div\n className=\"absolute bg-rose-500/10\"\n style={{ top: bleed.top, left: 0, width: bleed.left, height: pageHeightPx }}\n />\n {/* right */}\n <div\n className=\"absolute bg-rose-500/10\"\n style={{ top: bleed.top, right: 0, width: bleed.right, height: pageHeightPx }}\n />\n\n {/* Trim marks — one per corner, two lines each */}\n <TrimMark corner=\"tl\" bleed={bleed} />\n <TrimMark corner=\"tr\" bleed={bleed} pageWidthPx={pageWidthPx} />\n <TrimMark corner=\"bl\" bleed={bleed} pageHeightPx={pageHeightPx} />\n <TrimMark corner=\"br\" bleed={bleed} pageWidthPx={pageWidthPx} pageHeightPx={pageHeightPx} />\n </div>\n )\n}\n\ntype Corner = 'tl' | 'tr' | 'bl' | 'br'\n\ninterface TrimMarkProps {\n corner: Corner\n bleed: BleedTrimOverlayProps['bleed']\n pageWidthPx?: number\n pageHeightPx?: number\n}\n\nfunction TrimMark({ corner, bleed, pageWidthPx = 0, pageHeightPx = 0 }: TrimMarkProps) {\n const isRight = corner === 'tr' || corner === 'br'\n const isBottom = corner === 'bl' || corner === 'br'\n\n const xBase = isRight ? bleed.left + pageWidthPx : bleed.left\n const yBase = isBottom ? bleed.top + pageHeightPx : bleed.top\n\n // Horizontal arm\n const hX = isRight ? xBase + TRIM_MARK_OFFSET_PX : xBase - TRIM_MARK_OFFSET_PX - TRIM_MARK_PX\n const hY = yBase - 0.5\n\n // Vertical arm\n const vX = xBase - 0.5\n const vY = isBottom ? yBase + TRIM_MARK_OFFSET_PX : yBase - TRIM_MARK_OFFSET_PX - TRIM_MARK_PX\n\n return (\n <>\n <div\n className=\"absolute bg-[var(--text-muted)]\"\n style={{ left: hX, top: hY, width: TRIM_MARK_PX, height: 1 }}\n />\n <div\n className=\"absolute bg-[var(--text-muted)]\"\n style={{ left: vX, top: vY, width: 1, height: TRIM_MARK_PX }}\n />\n </>\n )\n}\n","/**\n * Horizontal page-thumbnail strip shown at the bottom of the editor. The active\n * page is highlighted. Thumbnails are rendered externally (Konva; the host\n * injects `renderThumbnail` so this component stays canvas-free and testable).\n *\n * Actions: add page, duplicate active page, delete active page (disabled when\n * only one page remains), drag-reorder pages. The strip is read-only when\n * `canWrite` is false.\n */\n\nimport { useEffect, useRef, useState } from 'react'\nimport type { ScenePage } from '../../design-canvas/model'\nimport { DuplicateGlyph, PageGlyph, PlusGlyph, TrashGlyph } from './glyphs'\nimport { BTN_SM } from './icon-button'\n\nexport interface PagesStripProps {\n pages: ScenePage[]\n activePageId: string\n canWrite: boolean\n /**\n * The host provides this to generate thumbnail data-URLs. The strip calls it\n * on mount and debounces re-calls on document changes. Returns null when the\n * thumbnail is not yet available (the strip renders a placeholder instead).\n */\n renderThumbnail(page: ScenePage): Promise<string | null>\n onSelectPage(pageId: string): void\n onAddPage(): void\n onDuplicatePage(pageId: string): void\n onDeletePage(pageId: string): void\n onReorderPage(pageId: string, toIndex: number): void\n /** Show page-management affordances (add / duplicate / delete). Default true.\n * The review surface passes false: pages are navigated, not authored. */\n canManagePages?: boolean\n /** Heading shown above the strip so it reads as page management. Overridable;\n * defaults to \"Pages\". Set to '' to hide the visible label (the container\n * keeps its accessible name regardless). */\n label?: string\n}\n\nconst THUMBNAIL_W = 80\nconst THUMBNAIL_H = 56\n\nexport function PagesStrip({\n pages,\n activePageId,\n canWrite,\n renderThumbnail,\n onSelectPage,\n onAddPage,\n onDuplicatePage,\n onDeletePage,\n onReorderPage,\n canManagePages = true,\n label = 'Pages',\n}: PagesStripProps) {\n // Map from page id → data URL; null while loading.\n const [thumbnails, setThumbnails] = useState<Record<string, string | null>>({})\n const thumbnailVersionRef = useRef(0)\n\n useEffect(() => {\n // Debounce thumbnail regeneration: bump the version on every render cycle\n // and only apply results from the CURRENT version.\n const version = ++thumbnailVersionRef.current\n let cancelled = false\n\n async function generate() {\n const results: Record<string, string | null> = {}\n for (const page of pages) {\n if (cancelled) return\n try {\n results[page.id] = await renderThumbnail(page)\n } catch {\n results[page.id] = null\n }\n }\n if (!cancelled && thumbnailVersionRef.current === version) {\n setThumbnails(results)\n }\n }\n\n void generate()\n return () => {\n cancelled = true\n }\n // renderThumbnail is assumed stable (host memoizes it); pages is the dep.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [pages])\n\n // Drag-reorder\n const dragIndexRef = useRef<number | null>(null)\n const [dragOverIndex, setDragOverIndex] = useState<number | null>(null)\n\n return (\n <div className=\"flex shrink-0 flex-col bg-[var(--bg-input)]\">\n {/* Quiet section heading so the thumbnail row reads as page management,\n not a stray strip. The scroll container below keeps the accessible\n \"Pages\" name regardless of whether the visible label is shown. */}\n {label ? (\n <div className=\"flex items-center gap-1.5 px-3 pt-1.5 text-[var(--text-muted)]\">\n <PageGlyph className=\"h-3 w-3\" />\n <span className=\"text-[10px] font-medium uppercase tracking-[0.08em]\">{label}</span>\n </div>\n ) : null}\n <div\n className=\"flex h-[92px] items-center gap-2 overflow-x-auto px-2 pb-2 pt-2\"\n aria-label=\"Pages\"\n >\n {pages.map((page, index) => {\n const isActive = page.id === activePageId\n const thumbUrl = thumbnails[page.id]\n\n return (\n <div\n key={page.id}\n draggable={canWrite}\n onDragStart={() => {\n dragIndexRef.current = index\n }}\n onDragOver={(event) => {\n if (dragIndexRef.current === null) return\n event.preventDefault()\n setDragOverIndex(index)\n }}\n onDragLeave={() => setDragOverIndex(null)}\n onDrop={() => {\n const from = dragIndexRef.current\n if (from !== null && from !== index) {\n onReorderPage(pages[from]!.id, index)\n }\n dragIndexRef.current = null\n setDragOverIndex(null)\n }}\n onDragEnd={() => {\n dragIndexRef.current = null\n setDragOverIndex(null)\n }}\n className={[\n 'group relative flex shrink-0 flex-col items-center gap-1 rounded p-1 transition',\n isActive\n ? 'ring-2 ring-[var(--brand-primary)]'\n : 'hover:bg-[color-mix(in_srgb,var(--border-default)_40%,transparent)]',\n dragOverIndex === index ? 'ring-1 ring-[color-mix(in_srgb,var(--brand-primary)_60%,transparent)]' : '',\n ].join(' ')}\n >\n {/* Selection is a real button so the tile is not an interactive control\n nesting the per-page action buttons (axe: nested-interactive). */}\n <button\n type=\"button\"\n aria-label={`Page ${index + 1}: ${page.name}${isActive ? ' (active)' : ''}`}\n aria-pressed={isActive}\n onClick={() => onSelectPage(page.id)}\n className=\"flex cursor-pointer flex-col items-center gap-1 rounded\"\n >\n {/* Thumbnail or placeholder */}\n <div\n className=\"overflow-hidden rounded border border-[var(--border-default)] bg-[hsl(var(--card))]\"\n style={{ width: THUMBNAIL_W, height: THUMBNAIL_H }}\n >\n {thumbUrl ? (\n <img\n src={thumbUrl}\n alt={page.name}\n className=\"h-full w-full object-contain\"\n draggable={false}\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center\">\n <PageGlyph className=\"h-5 w-5 text-[var(--text-muted)]\" />\n </div>\n )}\n </div>\n\n {/* Page name */}\n <span className=\"max-w-[80px] truncate text-[10px] text-[var(--text-secondary)]\">\n {page.name}\n </span>\n </button>\n\n {/* Per-page action buttons — visible on hover or when active */}\n {canWrite && canManagePages ? (\n <div className=\"pointer-events-none absolute right-1 top-1 flex gap-0.5 opacity-0 transition-opacity group-hover:pointer-events-auto group-hover:opacity-100\">\n <button\n type=\"button\"\n aria-label={`Duplicate page ${page.name}`}\n onClick={(event) => {\n event.stopPropagation()\n onDuplicatePage(page.id)\n }}\n className={BTN_SM}\n >\n <DuplicateGlyph className=\"h-3 w-3\" />\n </button>\n <button\n type=\"button\"\n aria-label={`Delete page ${page.name}`}\n disabled={pages.length <= 1}\n onClick={(event) => {\n event.stopPropagation()\n if (pages.length > 1) onDeletePage(page.id)\n }}\n className={BTN_SM}\n >\n <TrashGlyph className=\"h-3 w-3 text-[var(--text-danger)]\" />\n </button>\n </div>\n ) : null}\n </div>\n )\n })}\n\n {/* Add page button */}\n {canWrite && canManagePages ? (\n <button\n type=\"button\"\n aria-label=\"Add page\"\n onClick={onAddPage}\n className=\"flex h-[72px] w-[80px] shrink-0 flex-col items-center justify-center gap-1 rounded border border-dashed border-[var(--border-default)] text-[var(--text-muted)] transition hover:border-[var(--brand-primary)] hover:text-[var(--brand-primary)]\"\n >\n <PlusGlyph className=\"h-4 w-4\" />\n <span className=\"text-[10px]\">Add page</span>\n </button>\n ) : null}\n </div>\n </div>\n )\n}\n","/**\n * Inline SVG glyphs for the design canvas editor. No external icon dependency —\n * same convention as sequences-react/components/glyphs.tsx.\n */\n\ninterface GlyphProps {\n className?: string\n}\n\nfunction glyph(paths: React.ReactNode) {\n return function Glyph({ className }: GlyphProps) {\n return (\n <svg\n className={className}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n {paths}\n </svg>\n )\n }\n}\n\nexport const UndoGlyph = glyph(<path d=\"M3 7v6h6M3 13a9 9 0 1 0 3-7.7\" />)\nexport const RedoGlyph = glyph(<path d=\"M21 7v6h-6M21 13a9 9 0 1 1-3-7.7\" />)\nexport const SwapGlyph = glyph(<path d=\"M7 4 3 8l4 4M3 8h14M17 20l4-4-4-4M21 16H7\" />)\n\nexport const EyeGlyph = glyph(\n <>\n <path d=\"M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </>,\n)\n\nexport const EyeOffGlyph = glyph(\n <>\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94\" />\n <path d=\"M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19\" />\n <path d=\"m1 1 22 22\" />\n </>,\n)\n\nexport const LockGlyph = glyph(\n <>\n <rect x=\"5\" y=\"11\" width=\"14\" height=\"10\" rx=\"2\" />\n <path d=\"M8 11V7a4 4 0 0 1 8 0v4\" />\n </>,\n)\n\nexport const UnlockGlyph = glyph(\n <>\n <rect x=\"5\" y=\"11\" width=\"14\" height=\"10\" rx=\"2\" />\n <path d=\"M8 11V7a4 4 0 1 1 8 0\" />\n </>,\n)\n\nexport const TrashGlyph = glyph(\n <>\n <path d=\"M3 6h18M19 6l-1 14H6L5 6M10 6V4h4v2\" />\n </>,\n)\n\nexport const GroupGlyph = glyph(\n <>\n <rect x=\"2\" y=\"2\" width=\"8\" height=\"8\" rx=\"1\" />\n <rect x=\"14\" y=\"2\" width=\"8\" height=\"8\" rx=\"1\" />\n <rect x=\"2\" y=\"14\" width=\"8\" height=\"8\" rx=\"1\" />\n <rect x=\"14\" y=\"14\" width=\"8\" height=\"8\" rx=\"1\" />\n </>,\n)\n\nexport const UngroupGlyph = glyph(\n <>\n <path d=\"M3 7V5a2 2 0 0 1 2-2h2M17 3h2a2 2 0 0 1 2 2v2M21 17v2a2 2 0 0 1-2 2h-2M7 21H5a2 2 0 0 1-2-2v-2\" />\n </>,\n)\n\nexport const BringFrontGlyph = glyph(\n <>\n <rect x=\"8\" y=\"8\" width=\"12\" height=\"12\" rx=\"1\" />\n <path d=\"M4 4h12v4H4z\" opacity=\".4\" />\n </>,\n)\n\nexport const SendBackGlyph = glyph(\n <>\n <rect x=\"4\" y=\"4\" width=\"12\" height=\"12\" rx=\"1\" opacity=\".4\" />\n <path d=\"M8 8h12v12H8z\" />\n </>,\n)\n\nexport const AlignLeftGlyph = glyph(\n <>\n <path d=\"M3 4v16M7 8h10M7 16h6\" />\n </>,\n)\nexport const AlignCenterGlyph = glyph(\n <>\n <path d=\"M12 4v16M7 8h10M9 16h6\" />\n </>,\n)\nexport const AlignRightGlyph = glyph(\n <>\n <path d=\"M21 4v16M7 8h10M11 16h6\" />\n </>,\n)\n\nexport const BoldGlyph = glyph(<path d=\"M6 4h8a4 4 0 0 1 0 8H6zM6 12h9a4 4 0 0 1 0 8H6z\" fill=\"currentColor\" stroke=\"none\" />)\nexport const ItalicGlyph = glyph(<path d=\"M11 4h6M7 20h6M14 4 8 20\" />)\n\nexport const PlusGlyph = glyph(<path d=\"M12 5v14M5 12h14\" />)\nexport const ChevronDownGlyph = glyph(<path d=\"m6 9 6 6 6-6\" />)\n\nexport const RectGlyph = glyph(<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />)\nexport const EllipseGlyph = glyph(<ellipse cx=\"12\" cy=\"12\" rx=\"10\" ry=\"7\" />)\nexport const LineGlyph = glyph(<path d=\"M5 19 19 5\" />)\nexport const TextGlyph = glyph(<path d=\"M4 7V4h16v3M9 20h6M12 4v16\" />)\nexport const ImageGlyph = glyph(\n <>\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n <circle cx=\"9\" cy=\"9\" r=\"2\" />\n <path d=\"m21 15-3.1-3.1a2 2 0 0 0-2.8 0L6 21\" />\n </>,\n)\nexport const VideoGlyph = glyph(\n <>\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\" />\n <path d=\"m10 8 6 4-6 4z\" fill=\"currentColor\" stroke=\"none\" />\n </>,\n)\n\nexport const SlotGlyph = glyph(\n <>\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M12 1v4M12 19v4M4.2 4.2l2.8 2.8M17 17l2.8 2.8M1 12h4M19 12h4M4.2 19.8l2.8-2.8M17 7 19.8 4.2\" />\n </>,\n)\n\nexport const PageGlyph = glyph(\n <>\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n <polyline points=\"14 2 14 8 20 8\" />\n </>,\n)\n\nexport const GridGlyph = glyph(\n <>\n <path d=\"M3 3h18v18H3zM3 9h18M3 15h18M9 3v18M15 3v18\" />\n </>,\n)\n\nexport const RulerGlyph = glyph(\n <>\n <path d=\"M1 9v6l12 6V9L1 3z\" />\n <path d=\"m13 15 9-4.5V4.5L13 9\" />\n <path d=\"M5 12v3M8 13.5v2.5M11 15v3\" />\n </>,\n)\n\nexport const MagnetGlyph = glyph(\n <>\n <path d=\"m6 15-4-4 6.75-6.77a7.79 7.79 0 0 1 11 11L13 22l-4-4 6.39-6.36a2.14 2.14 0 0 0-3-3z\" />\n <path d=\"m5 8 4 4M12 15l4 4\" />\n </>,\n)\n\nexport const BleedGlyph = glyph(\n <>\n <rect x=\"4\" y=\"4\" width=\"16\" height=\"16\" strokeDasharray=\"3 2\" />\n <rect x=\"7\" y=\"7\" width=\"10\" height=\"10\" />\n </>,\n)\n\nexport const DuplicateGlyph = glyph(\n <>\n <rect x=\"8\" y=\"8\" width=\"12\" height=\"12\" rx=\"2\" />\n <path d=\"M4 16V4a2 2 0 0 1 2-2h12\" />\n </>,\n)\n\nexport const ZoomFitGlyph = glyph(\n <>\n <path d=\"M15 3h6v6M14 10l6.1-6.1M9 21H3v-6M10 14l-6.1 6.1\" />\n </>,\n)\n\nexport const ExportGlyph = glyph(\n <>\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3\" />\n </>,\n)\n\nexport const ShapesGlyph = glyph(\n <>\n <rect x=\"3\" y=\"13\" width=\"8\" height=\"8\" rx=\"1\" />\n <circle cx=\"17\" cy=\"17\" r=\"4\" />\n <path d=\"M8.5 3 13 11H4z\" />\n </>,\n)\n","/**\n * Shared icon-button contract for the canvas chrome (Toolbar, ZoomControls,\n * PagesStrip). One definition so the focus ring, disabled affordance, and\n * active state can never drift between surfaces.\n *\n * The focus ring uses the `--ring` token (defined in src/theme/tokens.css) and\n * is keyboard-only (`focus-visible`) so mouse clicks don't draw a ring.\n */\n\nimport { forwardRef } from 'react'\nimport type { ButtonHTMLAttributes } from 'react'\n\nconst BTN_BASE =\n 'items-center justify-center rounded border border-[var(--border-default)] text-[var(--text-secondary)] transition hover:text-[var(--text-primary)] disabled:cursor-default disabled:opacity-40'\n\nconst BTN_ACTIVE_EXTRA =\n ' border-[var(--brand-primary)] text-[var(--brand-primary)] hover:text-[var(--brand-primary)]'\n\n/** 28px square (h-7) — used by the main toolbar. */\nexport const BTN = `flex h-7 w-7 ${BTN_BASE}`\nexport const BTN_ACTIVE = BTN + BTN_ACTIVE_EXTRA\n\n/** 24px square (h-6) — used by the zoom controls and pages strip. */\nexport const BTN_SM = `flex h-6 w-6 ${BTN_BASE}`\nexport const BTN_SM_ACTIVE = BTN_SM + BTN_ACTIVE_EXTRA\n\nexport interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n /** Render the active (brand-colored) variant. */\n active?: boolean\n /** 'md' = 28px (toolbar), 'sm' = 24px (zoom/pages). */\n size?: 'md' | 'sm'\n}\n\nexport const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(function IconButton(\n { active = false, size = 'md', className = '', type = 'button', ...rest },\n ref,\n) {\n const base = size === 'sm' ? (active ? BTN_SM_ACTIVE : BTN_SM) : active ? BTN_ACTIVE : BTN\n return <button ref={ref} type={type} className={className ? `${base} ${className}` : base} {...rest} />\n})\n","/**\n * Horizontal (top) and vertical (left) canvas rulers. Each ruler:\n * - Draws zoom-scaled tick marks via `buildRulerTicks` / `selectTickStep`.\n * - Shows a pointer-position indicator (a hairline that follows the cursor).\n * - Supports guide creation by dragging OUT of the ruler: a live preview line\n * appears while dragging; on drop a `set_page_guides` command is emitted.\n * - Existing guides that are dragged BACK into the ruler are deleted.\n * - Renders saved guides persistently, Figma-style: a marker in the ruler\n * track at each guide position, plus a thin line spanning the canvas (the\n * GuidesCanvasOverlay sibling, aligned with the track coordinate space).\n *\n * All interaction math lives in ruler-math.ts and is testable without a DOM.\n * The rulers themselves are pure DOM (no Konva); they sit in a CSS grid slot\n * next to the workspace canvas.\n */\n\nimport { useCallback, useRef, useState } from 'react'\nimport type { PageGuides } from '../../design-canvas/model'\nimport { buildRulerTicks, screenToDocumentPosition, selectTickStep } from './ruler-math'\n\nconst RULER_SIZE_PX = 20\n\nexport interface RulersProps {\n /** Page width in document px. */\n pageWidth: number\n /** Page height in document px. */\n pageHeight: number\n zoom: number\n /** How many doc-px of the canvas are scrolled off-screen left/top. */\n scrollLeft: number\n scrollTop: number\n showRulers: boolean\n guides: PageGuides\n /** Emitted when the user drops a guide or deletes one back into the ruler. */\n onGuidesChange(guides: PageGuides): void\n}\n\n/** Minimum px from ruler edge before a guide drag counts as \"delete\". */\nconst DELETE_THRESHOLD_PX = RULER_SIZE_PX + 4\n\nexport function Rulers({ pageWidth, pageHeight, zoom, scrollLeft, scrollTop, showRulers, guides, onGuidesChange }: RulersProps) {\n if (!showRulers) return null\n\n return (\n <>\n {/* Corner filler */}\n <div\n className=\"absolute top-0 left-0 z-20 shrink-0 border-b border-r border-[var(--border-default)] bg-[var(--bg-input)]\"\n style={{ width: RULER_SIZE_PX, height: RULER_SIZE_PX }}\n />\n\n <HorizontalRuler\n pageWidth={pageWidth}\n zoom={zoom}\n scrollLeft={scrollLeft}\n guides={guides}\n onGuidesChange={onGuidesChange}\n />\n\n <VerticalRuler\n pageHeight={pageHeight}\n zoom={zoom}\n scrollTop={scrollTop}\n guides={guides}\n onGuidesChange={onGuidesChange}\n />\n\n {/* Saved-guide lines spanning the canvas */}\n <GuidesCanvasOverlay guides={guides} zoom={zoom} scrollLeft={scrollLeft} scrollTop={scrollTop} />\n </>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Horizontal ruler\n// ---------------------------------------------------------------------------\n\ninterface HorizontalRulerProps {\n pageWidth: number\n zoom: number\n scrollLeft: number\n guides: PageGuides\n onGuidesChange(guides: PageGuides): void\n}\n\nfunction HorizontalRuler({ pageWidth, zoom, scrollLeft, guides, onGuidesChange }: HorizontalRulerProps) {\n const ref = useRef<HTMLDivElement>(null)\n const [pointerX, setPointerX] = useState<number | null>(null)\n const [dragGuideX, setDragGuideX] = useState<number | null>(null)\n const dragGuideIndexRef = useRef<number | null>(null)\n\n const step = selectTickStep({ zoom, minMajorSpacingPx: 40 })\n const ticks = buildRulerTicks({ documentLength: pageWidth, step })\n\n function screenXToDoc(clientX: number): number {\n if (!ref.current) return 0\n const rect = ref.current.getBoundingClientRect()\n return screenToDocumentPosition({ pointerScreenPx: clientX - rect.left, scrollOffset: scrollLeft, zoom })\n }\n\n function handlePointerDown(event: React.PointerEvent<HTMLDivElement>) {\n if (event.button !== 0) return\n event.preventDefault()\n event.currentTarget.setPointerCapture(event.pointerId)\n const docX = screenXToDoc(event.clientX)\n // Check if pointer is near an existing guide (within 4 screen px).\n const threshold = 4 / zoom\n const nearIdx = guides.vertical.findIndex((g) => Math.abs(g - docX) <= threshold)\n if (nearIdx >= 0) {\n dragGuideIndexRef.current = nearIdx\n } else {\n dragGuideIndexRef.current = null\n }\n setDragGuideX(docX)\n }\n\n function handlePointerMove(event: React.PointerEvent<HTMLDivElement>) {\n if (!ref.current) return\n const rect = ref.current.getBoundingClientRect()\n const localY = event.clientY - rect.top\n setPointerX(event.clientX - rect.left)\n if (!event.currentTarget.hasPointerCapture(event.pointerId)) return\n const docX = screenXToDoc(event.clientX)\n // If pointer moves BELOW the ruler, treat as \"dragging to canvas\" — show guide.\n if (localY > DELETE_THRESHOLD_PX) {\n setDragGuideX(docX)\n } else {\n // Dragged back into ruler — signal delete on release.\n setDragGuideX(null)\n }\n }\n\n function handlePointerUp(event: React.PointerEvent<HTMLDivElement>) {\n if (!event.currentTarget.hasPointerCapture(event.pointerId)) return\n event.currentTarget.releasePointerCapture(event.pointerId)\n\n const rect = ref.current?.getBoundingClientRect()\n const localY = rect ? event.clientY - rect.top : 0\n const deletingExisting = dragGuideIndexRef.current !== null\n const docX = screenXToDoc(event.clientX)\n\n const vertical = [...guides.vertical]\n\n if (localY <= DELETE_THRESHOLD_PX && deletingExisting) {\n // Dragged existing guide back into ruler — delete it.\n vertical.splice(dragGuideIndexRef.current!, 1)\n } else if (localY > DELETE_THRESHOLD_PX) {\n if (deletingExisting) {\n // Moved existing guide to new position.\n vertical[dragGuideIndexRef.current!] = docX\n } else {\n // New guide.\n vertical.push(docX)\n }\n }\n\n dragGuideIndexRef.current = null\n setDragGuideX(null)\n onGuidesChange({ ...guides, vertical })\n }\n\n function handlePointerLeave() {\n setPointerX(null)\n }\n\n return (\n <div\n ref={ref}\n className=\"absolute top-0 left-0 right-0 z-10 cursor-ew-resize select-none overflow-hidden border-b border-[var(--border-default)] bg-[var(--bg-input)]\"\n style={{ height: RULER_SIZE_PX, marginLeft: RULER_SIZE_PX }}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerLeave={handlePointerLeave}\n >\n {ticks.map((tick) => {\n const screenX = tick.position * zoom - scrollLeft * zoom\n return (\n <div\n key={tick.position}\n className={`absolute bottom-0 w-px bg-[var(--border-default)] ${tick.label !== null ? 'top-1.5' : 'top-[14px]'}`}\n style={{ left: screenX }}\n >\n {tick.label !== null ? (\n <span className=\"absolute -top-1 left-0.5 whitespace-nowrap font-mono text-[9px] leading-none text-[var(--text-muted)]\">\n {tick.label}\n </span>\n ) : null}\n </div>\n )\n })}\n\n {/* Saved-guide markers (persistent) */}\n {guides.vertical.map((position, index) => (\n <div\n key={`guide-${index}`}\n data-guide-marker=\"vertical\"\n aria-hidden\n className=\"pointer-events-none absolute top-0 bottom-0 w-px bg-[var(--brand-primary)]\"\n style={{ left: position * zoom - scrollLeft * zoom }}\n />\n ))}\n\n {/* Pointer indicator */}\n {pointerX !== null ? (\n <div\n className=\"pointer-events-none absolute top-0 bottom-0 w-px bg-[color-mix(in_srgb,var(--brand-primary)_60%,transparent)]\"\n style={{ left: pointerX }}\n />\n ) : null}\n\n {/* Live drag-guide preview */}\n {dragGuideX !== null ? (\n <div\n className=\"pointer-events-none absolute top-0 bottom-0 w-px bg-[var(--brand-primary)]\"\n style={{ left: dragGuideX * zoom - scrollLeft * zoom }}\n />\n ) : null}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Vertical ruler\n// ---------------------------------------------------------------------------\n\ninterface VerticalRulerProps {\n pageHeight: number\n zoom: number\n scrollTop: number\n guides: PageGuides\n onGuidesChange(guides: PageGuides): void\n}\n\nfunction VerticalRuler({ pageHeight, zoom, scrollTop, guides, onGuidesChange }: VerticalRulerProps) {\n const ref = useRef<HTMLDivElement>(null)\n const [pointerY, setPointerY] = useState<number | null>(null)\n const [dragGuideY, setDragGuideY] = useState<number | null>(null)\n const dragGuideIndexRef = useRef<number | null>(null)\n\n const step = selectTickStep({ zoom, minMajorSpacingPx: 40 })\n const ticks = buildRulerTicks({ documentLength: pageHeight, step })\n\n function screenYToDoc(clientY: number): number {\n if (!ref.current) return 0\n const rect = ref.current.getBoundingClientRect()\n return screenToDocumentPosition({ pointerScreenPx: clientY - rect.top, scrollOffset: scrollTop, zoom })\n }\n\n function handlePointerDown(event: React.PointerEvent<HTMLDivElement>) {\n if (event.button !== 0) return\n event.preventDefault()\n event.currentTarget.setPointerCapture(event.pointerId)\n const docY = screenYToDoc(event.clientY)\n const threshold = 4 / zoom\n const nearIdx = guides.horizontal.findIndex((g) => Math.abs(g - docY) <= threshold)\n dragGuideIndexRef.current = nearIdx >= 0 ? nearIdx : null\n setDragGuideY(docY)\n }\n\n function handlePointerMove(event: React.PointerEvent<HTMLDivElement>) {\n if (!ref.current) return\n const rect = ref.current.getBoundingClientRect()\n const localX = event.clientX - rect.left\n setPointerY(event.clientY - rect.top)\n if (!event.currentTarget.hasPointerCapture(event.pointerId)) return\n const docY = screenYToDoc(event.clientY)\n if (localX > DELETE_THRESHOLD_PX) {\n setDragGuideY(docY)\n } else {\n setDragGuideY(null)\n }\n }\n\n function handlePointerUp(event: React.PointerEvent<HTMLDivElement>) {\n if (!event.currentTarget.hasPointerCapture(event.pointerId)) return\n event.currentTarget.releasePointerCapture(event.pointerId)\n\n const rect = ref.current?.getBoundingClientRect()\n const localX = rect ? event.clientX - rect.left : 0\n const deletingExisting = dragGuideIndexRef.current !== null\n const docY = screenYToDoc(event.clientY)\n\n const horizontal = [...guides.horizontal]\n\n if (localX <= DELETE_THRESHOLD_PX && deletingExisting) {\n horizontal.splice(dragGuideIndexRef.current!, 1)\n } else if (localX > DELETE_THRESHOLD_PX) {\n if (deletingExisting) {\n horizontal[dragGuideIndexRef.current!] = docY\n } else {\n horizontal.push(docY)\n }\n }\n\n dragGuideIndexRef.current = null\n setDragGuideY(null)\n onGuidesChange({ ...guides, horizontal })\n }\n\n function handlePointerLeave() {\n setPointerY(null)\n }\n\n return (\n <div\n ref={ref}\n className=\"absolute top-0 left-0 bottom-0 z-10 cursor-ns-resize select-none overflow-hidden border-r border-[var(--border-default)] bg-[var(--bg-input)]\"\n style={{ width: RULER_SIZE_PX, marginTop: RULER_SIZE_PX }}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerLeave={handlePointerLeave}\n >\n {ticks.map((tick) => {\n const screenY = tick.position * zoom - scrollTop * zoom\n return (\n <div\n key={tick.position}\n className={`absolute right-0 h-px bg-[var(--border-default)] ${tick.label !== null ? 'left-1.5' : 'left-[14px]'}`}\n style={{ top: screenY }}\n >\n {tick.label !== null ? (\n <span\n className=\"absolute top-0.5 left-0 whitespace-nowrap font-mono text-[9px] leading-none text-[var(--text-muted)]\"\n style={{ transform: 'rotate(-90deg)', transformOrigin: '0 0', marginTop: 4 }}\n >\n {tick.label}\n </span>\n ) : null}\n </div>\n )\n })}\n\n {/* Saved-guide markers (persistent) */}\n {guides.horizontal.map((position, index) => (\n <div\n key={`guide-${index}`}\n data-guide-marker=\"horizontal\"\n aria-hidden\n className=\"pointer-events-none absolute left-0 right-0 h-px bg-[var(--brand-primary)]\"\n style={{ top: position * zoom - scrollTop * zoom }}\n />\n ))}\n\n {/* Pointer indicator */}\n {pointerY !== null ? (\n <div\n className=\"pointer-events-none absolute left-0 right-0 h-px bg-[color-mix(in_srgb,var(--brand-primary)_60%,transparent)]\"\n style={{ top: pointerY }}\n />\n ) : null}\n\n {dragGuideY !== null ? (\n <div\n className=\"pointer-events-none absolute left-0 right-0 h-px bg-[var(--brand-primary)]\"\n style={{ top: dragGuideY * zoom - scrollTop * zoom }}\n />\n ) : null}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Saved-guide canvas overlay\n// ---------------------------------------------------------------------------\n\ninterface GuidesCanvasOverlayProps {\n guides: PageGuides\n zoom: number\n scrollLeft: number\n scrollTop: number\n}\n\n/** Thin lines spanning the canvas for every saved guide. The overlay's origin\n * is offset by RULER_SIZE_PX on both axes so it shares the ruler tracks'\n * coordinate space — each line uses the exact same screen math as its track\n * marker and visually continues it across the canvas. pointer-events-none\n * keeps the canvas below fully interactive; z-10 (with the tracks) paints\n * the lines above the Konva stage, which sits in-flow beneath this overlay. */\nfunction GuidesCanvasOverlay({ guides, zoom, scrollLeft, scrollTop }: GuidesCanvasOverlayProps) {\n if (guides.vertical.length === 0 && guides.horizontal.length === 0) return null\n\n return (\n <div\n aria-hidden\n className=\"pointer-events-none absolute right-0 bottom-0 z-10 overflow-hidden\"\n style={{ left: RULER_SIZE_PX, top: RULER_SIZE_PX }}\n >\n {guides.vertical.map((position, index) => (\n <div\n key={`v-${index}`}\n data-guide-line=\"vertical\"\n className=\"absolute top-0 bottom-0 w-px bg-[var(--brand-primary)]\"\n style={{ left: position * zoom - scrollLeft * zoom }}\n />\n ))}\n {guides.horizontal.map((position, index) => (\n <div\n key={`h-${index}`}\n data-guide-line=\"horizontal\"\n className=\"absolute left-0 right-0 h-px bg-[var(--brand-primary)]\"\n style={{ top: position * zoom - scrollTop * zoom }}\n />\n ))}\n </div>\n )\n}\n","/**\n * Selection-aware toolbar. When elements are selected it shows per-kind\n * attribute controls; when nothing is selected it shows page-props controls.\n * Every number input commits on blur or Enter as a single command (not\n * per-keystroke). The toolbar is stateless beyond transient input focus;\n * the caller owns the command stack.\n *\n * Layout: the root never scrolls horizontally. Global controls (undo/redo +\n * view toggles) are pinned left; the selection/page attribute group wraps so\n * the whole bar fits the editor's center column (viewport minus the w-64 side\n * panel and w-80 agent panel) at 1024–1280px with nothing clipped.\n */\n\nimport { useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { SceneElement, ScenePage, TextElement, RectElement, EllipseElement, ImageElement } from '../../design-canvas/model'\nimport type { SceneAttrsPatch } from '../../design-canvas/operations'\nimport type { PageBleed } from '../../design-canvas/model'\nimport type { DesignCanvasMode } from '../contracts'\nimport { matchPreset, SIZE_PRESETS } from '../../design-canvas/export-presets'\nimport {\n AlignCenterGlyph,\n AlignLeftGlyph,\n AlignRightGlyph,\n BleedGlyph,\n BoldGlyph,\n BringFrontGlyph,\n GridGlyph,\n GroupGlyph,\n ItalicGlyph,\n LockGlyph,\n MagnetGlyph,\n RedoGlyph,\n RulerGlyph,\n SendBackGlyph,\n SlotGlyph,\n SwapGlyph,\n TrashGlyph,\n UndoGlyph,\n UngroupGlyph,\n UnlockGlyph,\n} from './glyphs'\nimport { BTN, BTN_ACTIVE } from './icon-button'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ToolbarProps {\n page: ScenePage\n selectedElements: SceneElement[]\n canWrite: boolean\n /** Capability mode. `'review'` (the lean reviewer) hides the view toggles,\n * page-props controls, and the destructive/structural selection controls\n * (z-order, group/ungroup, lock, slot, delete), keeping only safe direct\n * edits: text content, image fit/replace, opacity/rotation, undo/redo.\n * Defaults to `'edit'` (the full authoring toolbar). */\n mode?: DesignCanvasMode\n canUndo: boolean\n canRedo: boolean\n gridEnabled: boolean\n snapEnabled: boolean\n showRulers: boolean\n showBleed: boolean\n onUndo(): void\n onRedo(): void\n onToggleGrid(): void\n onToggleSnap(): void\n onToggleRulers(): void\n onToggleBleed(): void\n /** Emit attrs patch for each selected element. */\n onSetAttrs(elementId: string, attrs: SceneAttrsPatch): void\n onSetPageProps(props: { name?: string; width?: number; height?: number; background?: string; bleed?: PageBleed | null }): void\n onSetPageGuides(guides: { vertical: number[]; horizontal: number[] }): void\n onReorder(elementId: string, toIndex: number, ownerLength: number, direction: 'front' | 'back' | 'forward' | 'backward'): void\n onGroup(elementIds: string[]): void\n onUngroup(groupId: string): void\n onDelete(elementIds: string[]): void\n onBindSlot(elementId: string, slot: string | null): void\n /** Field label for the page-size control. Defaults to the clearer \"Page size\";\n * overridable for consumers that want the older \"Preset\" wording. */\n pageSizeLabel?: string\n /** Title for the \"turn on print bleed\" action. Defaults to \"Show print bleed\"\n * (the outcome) rather than the print-shop term \"bleed\". */\n enableBleedLabel?: string\n}\n\n// ---------------------------------------------------------------------------\n// Font families offered by the font picker. The model's fontFamily is a free\n// string (agents/templates may set anything); the picker selects from this\n// curated list but always surfaces the element's current family even when it\n// is not a member, so a programmatically-set font is never silently dropped.\n// ---------------------------------------------------------------------------\n\nconst FONT_FAMILIES = [\n 'Inter',\n 'Arial',\n 'Helvetica',\n 'Verdana',\n 'Tahoma',\n 'Trebuchet MS',\n 'Times New Roman',\n 'Georgia',\n 'Garamond',\n 'Courier New',\n 'Brush Script MT',\n 'Impact',\n] as const\n\n// ---------------------------------------------------------------------------\n// Small helpers\n// ---------------------------------------------------------------------------\n\nconst SEP = <div className=\"mx-1 h-5 w-px shrink-0 bg-[var(--border-default)]\" />\n\nconst POPOVER_PANEL =\n 'absolute top-full left-0 z-50 mt-1 flex flex-col rounded border border-[var(--border-default)] bg-[var(--bg-input)] shadow-lg'\n\n/**\n * Click-outside / Escape-to-close popover anchored under its trigger. The\n * trigger renders inside the relative wrapper so the panel positions against\n * it. `open`/`onClose` are owned by the caller so the trigger can stay a\n * stateful glyph button when needed.\n */\nfunction Popover({\n open,\n onClose,\n trigger,\n children,\n}: {\n open: boolean\n onClose(): void\n trigger: ReactNode\n children: ReactNode\n}) {\n const ref = useRef<HTMLDivElement>(null)\n\n useEffect(() => {\n if (!open) return\n function onDocPointer(event: PointerEvent) {\n if (ref.current && !ref.current.contains(event.target as Node)) onClose()\n }\n function onKey(event: KeyboardEvent) {\n if (event.key === 'Escape') onClose()\n }\n document.addEventListener('pointerdown', onDocPointer)\n document.addEventListener('keydown', onKey)\n return () => {\n document.removeEventListener('pointerdown', onDocPointer)\n document.removeEventListener('keydown', onKey)\n }\n }, [open, onClose])\n\n return (\n <div ref={ref} className=\"relative\">\n {trigger}\n {open ? children : null}\n </div>\n )\n}\n\nconst FIELD_LABEL = 'text-[9px] uppercase tracking-wide text-[var(--text-muted)]'\n\nfunction NumberInput({\n label,\n value,\n onCommit,\n min,\n step = 1,\n className = 'w-16',\n}: {\n label: string\n value: number\n onCommit(v: number): void\n min?: number\n step?: number\n className?: string\n}) {\n const [raw, setRaw] = useState<string | null>(null)\n\n function commit(v: string) {\n const n = parseFloat(v)\n if (Number.isFinite(n) && (min === undefined || n >= min)) onCommit(n)\n setRaw(null)\n }\n\n return (\n <label className=\"flex flex-col items-center gap-0.5\">\n <span className={FIELD_LABEL}>{label}</span>\n <input\n type=\"number\"\n value={raw ?? value}\n min={min}\n step={step}\n onChange={(event) => setRaw(event.target.value)}\n onBlur={(event) => commit(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') commit((event.target as HTMLInputElement).value)\n if (event.key === 'Escape') setRaw(null)\n }}\n className={`${className} rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-1 py-0.5 text-center text-[12px] text-[var(--text-primary)] focus:border-[var(--brand-primary)]`}\n />\n </label>\n )\n}\n\n/**\n * Compact dropdown built on the popover pattern (replaces native <select>,\n * which the design system bans). Same value/onChange contract: `value` is the\n * current option id, `onChange` fires with the chosen id.\n */\nfunction SelectControl<T extends string>({\n label,\n value,\n options,\n disabled,\n onChange,\n buttonClassName = 'w-24',\n}: {\n label: string\n value: T\n options: ReadonlyArray<{ value: T; label: string }>\n disabled?: boolean\n onChange(value: T): void\n buttonClassName?: string\n}) {\n const [open, setOpen] = useState(false)\n const current = options.find((o) => o.value === value)\n\n return (\n <label className=\"flex flex-col items-center gap-0.5\">\n <span className={FIELD_LABEL}>{label}</span>\n <Popover\n open={open}\n onClose={() => setOpen(false)}\n trigger={\n <button\n type=\"button\"\n disabled={disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n onClick={() => setOpen((v) => !v)}\n className={`${buttonClassName} flex items-center justify-between gap-1 rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-2 py-0.5 text-left text-[12px] text-[var(--text-primary)] hover:border-[var(--brand-primary)] disabled:cursor-default disabled:opacity-40`}\n >\n <span className=\"truncate\">{current?.label ?? value}</span>\n <span className=\"text-[8px] text-[var(--text-muted)]\">▾</span>\n </button>\n }\n >\n <div role=\"listbox\" className={`${POPOVER_PANEL} max-h-64 w-44 overflow-y-auto py-1`}>\n {options.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n role=\"option\"\n aria-selected={opt.value === value}\n onClick={() => {\n onChange(opt.value)\n setOpen(false)\n }}\n className={`px-3 py-1 text-left text-[12px] hover:bg-[color-mix(in_srgb,var(--brand-primary)_10%,transparent)] ${\n opt.value === value ? 'text-[var(--brand-primary)]' : 'text-[var(--text-primary)]'\n }`}\n >\n {opt.label}\n </button>\n ))}\n </div>\n </Popover>\n </label>\n )\n}\n\n/**\n * Searchable font dropdown. Each option previews in its own family. The\n * element's current family is always present in the list (prepended when it\n * is not one of the curated families) so a typed/agent-set font is never lost.\n */\nfunction FontPicker({\n value,\n disabled,\n onChange,\n}: {\n value: string\n disabled?: boolean\n onChange(value: string): void\n}) {\n const [open, setOpen] = useState(false)\n const [query, setQuery] = useState('')\n\n const families: string[] = FONT_FAMILIES.includes(value as (typeof FONT_FAMILIES)[number])\n ? [...FONT_FAMILIES]\n : [value, ...FONT_FAMILIES]\n const filtered = families.filter((f) => f.toLowerCase().includes(query.trim().toLowerCase()))\n\n return (\n <label className=\"flex flex-col items-center gap-0.5\">\n <span className={FIELD_LABEL}>Font</span>\n <Popover\n open={open}\n onClose={() => {\n setOpen(false)\n setQuery('')\n }}\n trigger={\n <button\n type=\"button\"\n disabled={disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-label=\"Font family\"\n onClick={() => setOpen((v) => !v)}\n className=\"flex w-28 items-center justify-between gap-1 rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-2 py-0.5 text-left text-[12px] text-[var(--text-primary)] hover:border-[var(--brand-primary)] disabled:cursor-default disabled:opacity-40\"\n style={{ fontFamily: value }}\n >\n <span className=\"truncate\">{value}</span>\n <span className=\"text-[8px] text-[var(--text-muted)]\">▾</span>\n </button>\n }\n >\n <div className={`${POPOVER_PANEL} w-52`}>\n <input\n autoFocus\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n placeholder=\"Search fonts\"\n aria-label=\"Search fonts\"\n className=\"m-1 rounded border border-[var(--border-default)] bg-transparent px-2 py-1 text-[12px] text-[var(--text-primary)] focus:border-[var(--brand-primary)]\"\n />\n <div role=\"listbox\" className=\"max-h-60 overflow-y-auto py-1\">\n {filtered.length === 0 ? (\n <div className=\"px-3 py-2 text-[11px] text-[var(--text-muted)]\">No matches</div>\n ) : (\n filtered.map((family) => (\n <button\n key={family}\n type=\"button\"\n role=\"option\"\n aria-selected={family === value}\n onClick={() => {\n onChange(family)\n setOpen(false)\n setQuery('')\n }}\n style={{ fontFamily: family }}\n className={`block w-full px-3 py-1 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--brand-primary)_10%,transparent)] ${\n family === value ? 'text-[var(--brand-primary)]' : 'text-[var(--text-primary)]'\n }`}\n >\n {family}\n </button>\n ))\n )}\n </div>\n </div>\n </Popover>\n </label>\n )\n}\n\n/**\n * Color control: a swatch button (showing the current color) that opens a\n * popover with a native color picker + hex field. Replaces the bare\n * <input type=\"color\"> chrome while keeping the string value/onChange contract.\n * `none` renders the no-color idiom (white swatch with a diagonal slash) for\n * values that are set but not in effect — e.g. a stroke whose width is 0.\n */\nfunction ColorSwatch({ label, value, onCommit, disabled, none = false }: { label: string; value: string; onCommit(v: string): void; disabled?: boolean; none?: boolean }) {\n const [open, setOpen] = useState(false)\n const normalized = value.startsWith('#') ? value : '#ffffff'\n\n return (\n <div className=\"flex flex-col items-center gap-0.5\">\n <span className={FIELD_LABEL}>{label}</span>\n <Popover\n open={open}\n onClose={() => setOpen(false)}\n trigger={\n <button\n type=\"button\"\n disabled={disabled}\n aria-label={none ? `${label} color (none)` : `${label} color`}\n title={none ? 'None' : undefined}\n onClick={() => setOpen((v) => !v)}\n className=\"relative h-6 w-10 overflow-hidden rounded border border-[var(--border-default)] disabled:cursor-default disabled:opacity-40\"\n style={{ backgroundColor: none ? '#ffffff' : normalized }}\n >\n {none ? (\n <svg className=\"absolute inset-0 h-full w-full\" viewBox=\"0 0 40 24\" preserveAspectRatio=\"none\" aria-hidden>\n <line x1=\"1\" y1=\"23\" x2=\"39\" y2=\"1\" stroke=\"var(--text-danger)\" strokeWidth=\"1.5\" />\n </svg>\n ) : null}\n </button>\n }\n >\n <div className={`${POPOVER_PANEL} w-40 gap-2 p-2`}>\n <input\n type=\"color\"\n aria-label={`${label} color picker`}\n value={normalized}\n onChange={(event) => onCommit(event.target.value)}\n className=\"h-8 w-full cursor-pointer rounded border border-[var(--border-default)] p-0.5\"\n />\n <input\n type=\"text\"\n aria-label={`${label} hex value`}\n value={value}\n onChange={(event) => onCommit(event.target.value)}\n className=\"rounded border border-[var(--border-default)] bg-transparent px-2 py-1 text-[12px] text-[var(--text-primary)] focus:border-[var(--brand-primary)]\"\n />\n </div>\n </Popover>\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Main toolbar\n// ---------------------------------------------------------------------------\n\nexport function Toolbar({\n page,\n selectedElements,\n canWrite,\n mode = 'edit',\n canUndo,\n canRedo,\n gridEnabled,\n snapEnabled,\n showRulers,\n showBleed,\n onUndo,\n onRedo,\n onToggleGrid,\n onToggleSnap,\n onToggleRulers,\n onToggleBleed,\n onSetAttrs,\n onSetPageProps,\n onSetPageGuides,\n onReorder,\n onGroup,\n onUngroup,\n onDelete,\n onBindSlot,\n pageSizeLabel = 'Page size',\n enableBleedLabel = 'Show print bleed',\n}: ToolbarProps) {\n const hasSelection = selectedElements.length > 0\n const single = selectedElements.length === 1 ? selectedElements[0]! : null\n const allSameKind = selectedElements.length > 0 && selectedElements.every((e) => e.kind === selectedElements[0]!.kind)\n const firstKind = selectedElements[0]?.kind\n\n // Shared multi-element patch helper.\n function patchAll(attrs: SceneAttrsPatch) {\n for (const el of selectedElements) onSetAttrs(el.id, attrs)\n }\n\n // Z-order helpers operate on the single selection only (multi-element\n // z-order is a complex multi-step operation; single is the common case).\n function reorderSingle(direction: 'front' | 'back' | 'forward' | 'backward') {\n if (!single) return\n // ownerLength requires knowing the owner; we use page.elements length for\n // root-level elements. Group children are handled via the layers panel.\n onReorder(single.id, 0, page.elements.length, direction)\n }\n\n const selectedIds = selectedElements.map((e) => e.id)\n const isGroup = single?.kind === 'group'\n const groupable = selectedElements.length >= 2\n const review = mode === 'review'\n\n // ----\n\n return (\n <div className=\"flex min-h-11 shrink-0 flex-wrap items-center gap-x-2 gap-y-1 border-b border-[var(--border-default)] bg-[var(--bg-input)] px-3 py-1\">\n {/* Global controls: pinned first, never wrap away from the left edge.\n Grouped by intent so history reads apart from the view utilities:\n Undo/Redo (history) | a labelled View cluster (rulers/grid/snap/bleed). */}\n <div className=\"flex shrink-0 items-center gap-2\">\n {/* History: Undo / Redo */}\n <div className=\"flex items-center gap-1\" role=\"group\" aria-label=\"History\">\n <button type=\"button\" aria-label=\"Undo\" disabled={!canUndo || !canWrite} onClick={onUndo} className={BTN}>\n <UndoGlyph className=\"h-3.5 w-3.5\" />\n </button>\n <button type=\"button\" aria-label=\"Redo\" disabled={!canRedo || !canWrite} onClick={onRedo} className={BTN}>\n <RedoGlyph className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n\n {/* View toggles (grid/snap/ruler/bleed) are view utilities, not creation\n actions — a quiet \"View\" label sets them apart by intent. The review\n surface hides them so the bar stays lean. */}\n {!review ? (\n <>\n {SEP}\n <div className=\"flex items-center gap-1\" role=\"group\" aria-label=\"View\">\n <span className={`${FIELD_LABEL} mr-0.5 hidden md:inline`} aria-hidden>\n View\n </span>\n <button type=\"button\" aria-label=\"Toggle rulers\" aria-pressed={showRulers} onClick={onToggleRulers} className={showRulers ? BTN_ACTIVE : BTN} title=\"Rulers\">\n <RulerGlyph className=\"h-3.5 w-3.5\" />\n </button>\n <button type=\"button\" aria-label=\"Toggle grid\" aria-pressed={gridEnabled} onClick={onToggleGrid} className={gridEnabled ? BTN_ACTIVE : BTN} title=\"Grid\">\n <GridGlyph className=\"h-3.5 w-3.5\" />\n </button>\n <button type=\"button\" aria-label=\"Toggle snap\" aria-pressed={snapEnabled} onClick={onToggleSnap} className={snapEnabled ? BTN_ACTIVE : BTN} title=\"Snap to guides\">\n <MagnetGlyph className=\"h-3.5 w-3.5\" />\n </button>\n <button type=\"button\" aria-label=\"Toggle bleed overlay\" aria-pressed={showBleed} onClick={onToggleBleed} className={showBleed ? BTN_ACTIVE : BTN} disabled={!page.bleed} title=\"Show print bleed\">\n <BleedGlyph className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n </>\n ) : null}\n </div>\n\n {/* Selection attributes: this group wraps to the next line when the column\n is narrow so nothing is ever clipped or scrolled away. In review mode\n the page-props controls (no-selection authoring) are omitted entirely\n and the selection controls drop structural/destructive actions. */}\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n {hasSelection ? (\n <>\n {SEP}\n <SelectionControls\n elements={selectedElements}\n single={single}\n isGroup={isGroup}\n groupable={groupable}\n allSameKind={allSameKind}\n firstKind={firstKind}\n canWrite={canWrite}\n review={review}\n patchAll={patchAll}\n reorderSingle={reorderSingle}\n onGroup={() => onGroup(selectedIds)}\n onUngroup={() => { if (single) onUngroup(single.id) }}\n onDelete={() => onDelete(selectedIds)}\n onBindSlot={single ? (slot) => onBindSlot(single.id, slot) : undefined}\n currentSlot={single?.slot ?? null}\n />\n </>\n ) : !review ? (\n <>\n {SEP}\n <PagePropsControls\n page={page}\n canWrite={canWrite}\n onSetPageProps={onSetPageProps}\n onSetPageGuides={onSetPageGuides}\n pageSizeLabel={pageSizeLabel}\n enableBleedLabel={enableBleedLabel}\n />\n </>\n ) : null}\n </div>\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Selection controls\n// ---------------------------------------------------------------------------\n\ninterface SelectionControlsProps {\n elements: SceneElement[]\n single: SceneElement | null\n isGroup: boolean\n groupable: boolean\n allSameKind: boolean\n firstKind: SceneElement['kind'] | undefined\n canWrite: boolean\n /** Review mode drops z-order, group/ungroup, lock, slot-bind, and delete. */\n review: boolean\n patchAll(attrs: SceneAttrsPatch): void\n reorderSingle(direction: 'front' | 'back' | 'forward' | 'backward'): void\n onGroup(): void\n onUngroup(): void\n onDelete(): void\n onBindSlot?(slot: string | null): void\n currentSlot: string | null\n}\n\nfunction SelectionControls({\n elements,\n single,\n isGroup,\n groupable,\n allSameKind,\n firstKind,\n canWrite,\n review,\n patchAll,\n reorderSingle,\n onGroup,\n onUngroup,\n onDelete,\n onBindSlot,\n currentSlot,\n}: SelectionControlsProps) {\n const [slotPopoverOpen, setSlotPopoverOpen] = useState(false)\n const [slotInput, setSlotInput] = useState('')\n const firstEl = elements[0]!\n\n return (\n <>\n {/* Kind-specific attrs */}\n {allSameKind && firstKind === 'text' && single ? (\n <TextControls element={single as TextElement} canWrite={canWrite} onPatch={(attrs) => patchAll(attrs)} />\n ) : null}\n\n {allSameKind && (firstKind === 'rect') && single ? (\n <ShapeControls element={single as RectElement} canWrite={canWrite} onPatch={(attrs) => patchAll(attrs)} showCornerRadius />\n ) : null}\n\n {allSameKind && firstKind === 'ellipse' && single ? (\n <ShapeControls element={single as EllipseElement} canWrite={canWrite} onPatch={(attrs) => patchAll(attrs)} showCornerRadius={false} />\n ) : null}\n\n {allSameKind && firstKind === 'image' && single ? (\n <ImageControls element={single as ImageElement} canWrite={canWrite} onPatch={(attrs) => patchAll(attrs)} />\n ) : null}\n\n {SEP}\n\n {/* Shared: opacity + rotation */}\n <NumberInput\n label=\"Opacity\"\n value={Math.round((firstEl.opacity ?? 1) * 100)}\n min={0}\n onCommit={(v) => patchAll({ opacity: Math.max(0, Math.min(1, v / 100)) })}\n className=\"w-14\"\n />\n <NumberInput\n label=\"Rotation\"\n value={Math.round(firstEl.rotation ?? 0)}\n onCommit={(v) => patchAll({ rotation: v })}\n className=\"w-14\"\n />\n\n {/* Structural + destructive controls: hidden on the review surface, which\n allows only direct attribute edits and reposition (drag). */}\n {review ? null : (\n <>\n {SEP}\n\n {/* Z-order (single selection) */}\n {single ? (\n <>\n <button type=\"button\" aria-label=\"Bring to front\" disabled={!canWrite} onClick={() => reorderSingle('front')} className={BTN}>\n <BringFrontGlyph className=\"h-3.5 w-3.5\" />\n </button>\n <button type=\"button\" aria-label=\"Send to back\" disabled={!canWrite} onClick={() => reorderSingle('back')} className={BTN}>\n <SendBackGlyph className=\"h-3.5 w-3.5\" />\n </button>\n {SEP}\n </>\n ) : null}\n\n {/* Group / Ungroup */}\n {groupable ? (\n <button type=\"button\" aria-label=\"Group elements\" disabled={!canWrite} onClick={onGroup} className={BTN}>\n <GroupGlyph className=\"h-3.5 w-3.5\" />\n </button>\n ) : null}\n {isGroup ? (\n <button type=\"button\" aria-label=\"Ungroup\" disabled={!canWrite} onClick={onUngroup} className={BTN}>\n <UngroupGlyph className=\"h-3.5 w-3.5\" />\n </button>\n ) : null}\n\n {/* Lock */}\n {single ? (\n <button\n type=\"button\"\n aria-label={single.locked ? 'Unlock element' : 'Lock element'}\n aria-pressed={!!single.locked}\n disabled={!canWrite}\n onClick={() => patchAll({ locked: !single.locked })}\n className={single.locked ? BTN_ACTIVE : BTN}\n >\n {single.locked ? <LockGlyph className=\"h-3.5 w-3.5\" /> : <UnlockGlyph className=\"h-3.5 w-3.5\" />}\n </button>\n ) : null}\n\n {/* Slot binding (single element only) */}\n {single && onBindSlot ? (\n <Popover\n open={slotPopoverOpen}\n onClose={() => setSlotPopoverOpen(false)}\n trigger={\n <button\n type=\"button\"\n aria-label={currentSlot ? `Slot: ${currentSlot}` : 'Bind slot'}\n aria-pressed={!!currentSlot}\n aria-haspopup=\"dialog\"\n aria-expanded={slotPopoverOpen}\n onClick={() => { setSlotInput(currentSlot ?? ''); setSlotPopoverOpen((v) => !v) }}\n className={currentSlot ? BTN_ACTIVE : BTN}\n title={currentSlot ? `Slot: ${currentSlot}` : 'Bind slot'}\n >\n <SlotGlyph className=\"h-3.5 w-3.5\" />\n </button>\n }\n >\n <div className={`${POPOVER_PANEL} w-48 gap-2 p-2`}>\n <input\n autoFocus\n value={slotInput}\n onChange={(event) => setSlotInput(event.target.value)}\n placeholder=\"slot-name\"\n className=\"rounded border border-[var(--border-default)] bg-transparent px-2 py-1 text-[12px] text-[var(--text-primary)] focus:border-[var(--brand-primary)]\"\n />\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => { onBindSlot(slotInput.trim() || null); setSlotPopoverOpen(false) }}\n className=\"flex-1 rounded border border-[var(--brand-primary)] px-2 py-0.5 text-[11px] text-[var(--brand-primary)] hover:bg-[color-mix(in_srgb,var(--brand-primary)_10%,transparent)]\"\n >\n {slotInput.trim() ? 'Bind' : 'Unbind'}\n </button>\n <button\n type=\"button\"\n onClick={() => setSlotPopoverOpen(false)}\n className=\"rounded border border-[var(--border-default)] px-2 py-0.5 text-[11px] text-[var(--text-secondary)]\"\n >\n Cancel\n </button>\n </div>\n </div>\n </Popover>\n ) : null}\n\n {SEP}\n\n {/* Delete — destructive, marked apart with a danger-tinted hover. */}\n <button\n type=\"button\"\n aria-label=\"Delete selection\"\n disabled={!canWrite}\n onClick={onDelete}\n className={`${BTN} text-[var(--text-danger)] hover:border-[var(--text-danger)] hover:text-[var(--text-danger)]`}\n >\n <TrashGlyph className=\"h-3.5 w-3.5\" />\n </button>\n </>\n )}\n </>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Kind-specific controls\n// ---------------------------------------------------------------------------\n\nfunction TextControls({ element, canWrite, onPatch }: { element: TextElement; canWrite: boolean; onPatch(attrs: SceneAttrsPatch): void }) {\n return (\n <>\n <FontPicker value={element.fontFamily} disabled={!canWrite} onChange={(fontFamily) => onPatch({ fontFamily })} />\n <NumberInput label=\"Size\" value={element.fontSize} min={1} onCommit={(v) => onPatch({ fontSize: v })} className=\"w-12\" />\n <button\n type=\"button\"\n aria-label=\"Bold\"\n aria-pressed={!!element.fontStyle?.includes('bold')}\n disabled={!canWrite}\n onClick={() => onPatch({ fontStyle: element.fontStyle === 'bold' || element.fontStyle === 'bold italic' ? (element.fontStyle === 'bold italic' ? 'italic' : 'normal') : (element.fontStyle === 'italic' ? 'bold italic' : 'bold') })}\n className={element.fontStyle?.includes('bold') ? BTN_ACTIVE : BTN}\n >\n <BoldGlyph className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n aria-label=\"Italic\"\n aria-pressed={!!element.fontStyle?.includes('italic')}\n disabled={!canWrite}\n onClick={() => onPatch({ fontStyle: element.fontStyle === 'italic' || element.fontStyle === 'bold italic' ? (element.fontStyle === 'bold italic' ? 'bold' : 'normal') : (element.fontStyle === 'bold' ? 'bold italic' : 'italic') })}\n className={element.fontStyle?.includes('italic') ? BTN_ACTIVE : BTN}\n >\n <ItalicGlyph className=\"h-3.5 w-3.5\" />\n </button>\n <div role=\"radiogroup\" aria-label=\"Text alignment\" className=\"flex items-center gap-2\">\n {(['left', 'center', 'right'] as const).map((align) => (\n <button\n key={align}\n type=\"button\"\n role=\"radio\"\n aria-label={`Align ${align}`}\n aria-checked={element.align === align}\n disabled={!canWrite}\n onClick={() => onPatch({ align })}\n className={element.align === align ? BTN_ACTIVE : BTN}\n >\n {align === 'left' ? <AlignLeftGlyph className=\"h-3.5 w-3.5\" /> : align === 'center' ? <AlignCenterGlyph className=\"h-3.5 w-3.5\" /> : <AlignRightGlyph className=\"h-3.5 w-3.5\" />}\n </button>\n ))}\n </div>\n <NumberInput label=\"Line H\" value={element.lineHeight} step={0.1} min={0.5} onCommit={(v) => onPatch({ lineHeight: v })} className=\"w-12\" />\n <NumberInput label=\"Spacing\" value={element.letterSpacing} step={0.5} onCommit={(v) => onPatch({ letterSpacing: v })} className=\"w-14\" />\n <ColorSwatch label=\"Fill\" value={element.fill} onCommit={(v) => onPatch({ fill: v })} disabled={!canWrite} />\n </>\n )\n}\n\nfunction ShapeControls({ element, canWrite, onPatch, showCornerRadius }: { element: RectElement | EllipseElement; canWrite: boolean; onPatch(attrs: SceneAttrsPatch): void; showCornerRadius: boolean }) {\n return (\n <>\n <ColorSwatch label=\"Fill\" value={element.fill} onCommit={(v) => onPatch({ fill: v })} disabled={!canWrite} />\n <ColorSwatch label=\"Stroke\" value={element.stroke ?? '#000000'} onCommit={(v) => onPatch({ stroke: v })} disabled={!canWrite} none={(element.strokeWidth ?? 0) === 0} />\n <NumberInput label=\"Stroke W\" value={element.strokeWidth ?? 0} min={0} onCommit={(v) => onPatch({ strokeWidth: v })} className=\"w-14\" />\n {showCornerRadius && 'cornerRadius' in element ? (\n <NumberInput label=\"Corner R\" value={(element as RectElement).cornerRadius ?? 0} min={0} onCommit={(v) => onPatch({ cornerRadius: v })} className=\"w-14\" />\n ) : null}\n </>\n )\n}\n\nfunction ImageControls({ element, canWrite, onPatch }: { element: ImageElement; canWrite: boolean; onPatch(attrs: SceneAttrsPatch): void }) {\n const [swapOpen, setSwapOpen] = useState(false)\n const [swapUrl, setSwapUrl] = useState('')\n\n return (\n <>\n <SelectControl\n label=\"Fit\"\n value={element.fit}\n disabled={!canWrite}\n onChange={(fit) => onPatch({ fit })}\n buttonClassName=\"w-24\"\n options={[\n { value: 'fill', label: 'Fill' },\n { value: 'cover', label: 'Cover' },\n { value: 'contain', label: 'Contain' },\n ]}\n />\n {/* Image swap: replace the source in place (size/position preserved). A\n safe direct edit, available in review mode. */}\n <Popover\n open={swapOpen}\n onClose={() => setSwapOpen(false)}\n trigger={\n <button\n type=\"button\"\n aria-label=\"Replace image\"\n disabled={!canWrite}\n onClick={() => { setSwapUrl(element.src); setSwapOpen((v) => !v) }}\n className={BTN}\n title=\"Replace image\"\n >\n <SwapGlyph className=\"h-3.5 w-3.5\" />\n </button>\n }\n >\n <div className={`${POPOVER_PANEL} w-64 gap-2 p-2`}>\n <input\n autoFocus\n value={swapUrl}\n onChange={(event) => setSwapUrl(event.target.value)}\n placeholder=\"https://… image URL\"\n aria-label=\"New image URL\"\n className=\"rounded border border-[var(--border-default)] bg-transparent px-2 py-1 text-[12px] text-[var(--text-primary)] focus:border-[var(--brand-primary)]\"\n />\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n disabled={!swapUrl.trim() || swapUrl.trim() === element.src}\n onClick={() => { onPatch({ src: swapUrl.trim() }); setSwapOpen(false) }}\n className=\"flex-1 rounded border border-[var(--brand-primary)] px-2 py-0.5 text-[11px] text-[var(--brand-primary)] hover:bg-[color-mix(in_srgb,var(--brand-primary)_10%,transparent)] disabled:cursor-default disabled:opacity-40\"\n >\n Replace\n </button>\n <button\n type=\"button\"\n onClick={() => setSwapOpen(false)}\n className=\"rounded border border-[var(--border-default)] px-2 py-0.5 text-[11px] text-[var(--text-secondary)]\"\n >\n Cancel\n </button>\n </div>\n </div>\n </Popover>\n </>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Page props controls (no selection)\n// ---------------------------------------------------------------------------\n\ninterface PagePropsControlsProps {\n page: ScenePage\n canWrite: boolean\n onSetPageProps(props: { name?: string; width?: number; height?: number; background?: string; bleed?: PageBleed | null }): void\n onSetPageGuides(guides: { vertical: number[]; horizontal: number[] }): void\n pageSizeLabel?: string\n enableBleedLabel?: string\n}\n\nfunction PagePropsControls({ page, canWrite, onSetPageProps, onSetPageGuides, pageSizeLabel = 'Page size', enableBleedLabel = 'Show print bleed' }: PagePropsControlsProps) {\n const matchedPreset = matchPreset(page.width, page.height)\n const [customW, setCustomW] = useState<string | null>(null)\n const [customH, setCustomH] = useState<string | null>(null)\n\n function commitDimension(dim: 'width' | 'height', raw: string) {\n const v = parseFloat(raw)\n if (Number.isFinite(v) && v > 0) onSetPageProps({ [dim]: v })\n if (dim === 'width') setCustomW(null)\n else setCustomH(null)\n }\n\n const presetOptions = [\n { value: 'custom', label: 'Custom' },\n ...SIZE_PRESETS.map((p) => ({ value: p.id, label: p.label })),\n ]\n\n return (\n <>\n {/* Page name */}\n <input\n type=\"text\"\n aria-label=\"Page name\"\n value={page.name}\n disabled={!canWrite}\n onChange={(event) => onSetPageProps({ name: event.target.value })}\n className=\"w-28 rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-2 py-0.5 text-[12px] text-[var(--text-primary)] focus:border-[var(--brand-primary)]\"\n />\n\n {SEP}\n\n {/* Size preset */}\n <SelectControl\n label={pageSizeLabel}\n value={matchedPreset?.id ?? 'custom'}\n disabled={!canWrite}\n onChange={(id) => {\n const preset = SIZE_PRESETS.find((p) => p.id === id)\n if (preset) onSetPageProps({ width: preset.width, height: preset.height })\n }}\n options={presetOptions}\n // Preset labels run long (\"US Letter Landscape\") — w-24 truncates them\n // to \"Instagram …\"; w-44 matches the listbox width and fits them all.\n buttonClassName=\"w-44\"\n />\n\n {/* Custom W × H */}\n <label className=\"flex flex-col items-center gap-0.5\">\n <span className={FIELD_LABEL}>W</span>\n <input\n type=\"number\"\n value={customW ?? page.width}\n min={1}\n disabled={!canWrite}\n onChange={(event) => setCustomW(event.target.value)}\n onBlur={(event) => commitDimension('width', event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') commitDimension('width', (event.target as HTMLInputElement).value)\n if (event.key === 'Escape') setCustomW(null)\n }}\n className=\"w-16 rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-1 py-0.5 text-center text-[12px] text-[var(--text-primary)] focus:border-[var(--brand-primary)]\"\n />\n </label>\n <span className=\"text-[var(--text-muted)]\">×</span>\n <label className=\"flex flex-col items-center gap-0.5\">\n <span className={FIELD_LABEL}>H</span>\n <input\n type=\"number\"\n value={customH ?? page.height}\n min={1}\n disabled={!canWrite}\n onChange={(event) => setCustomH(event.target.value)}\n onBlur={(event) => commitDimension('height', event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') commitDimension('height', (event.target as HTMLInputElement).value)\n if (event.key === 'Escape') setCustomH(null)\n }}\n className=\"w-16 rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-1 py-0.5 text-center text-[12px] text-[var(--text-primary)] focus:border-[var(--brand-primary)]\"\n />\n </label>\n\n {SEP}\n\n <ColorSwatch label=\"BG\" value={page.background} onCommit={(v) => onSetPageProps({ background: v })} disabled={!canWrite} />\n\n {SEP}\n\n {/* Bleed controls */}\n <BleedControls page={page} canWrite={canWrite} onSetPageProps={onSetPageProps} enableBleedLabel={enableBleedLabel} />\n </>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Bleed sub-controls\n// ---------------------------------------------------------------------------\n\nfunction BleedControls({ page, canWrite, onSetPageProps, enableBleedLabel = 'Show print bleed' }: { page: ScenePage; canWrite: boolean; onSetPageProps: PagePropsControlsProps['onSetPageProps']; enableBleedLabel?: string }) {\n const bleed = page.bleed\n\n function setBleedSide(side: keyof PageBleed, value: number) {\n const current = bleed ?? { top: 0, right: 0, bottom: 0, left: 0 }\n onSetPageProps({ bleed: { ...current, [side]: value } })\n }\n\n if (!bleed) {\n return (\n <button\n type=\"button\"\n aria-label={enableBleedLabel}\n disabled={!canWrite}\n onClick={() => onSetPageProps({ bleed: { top: 3, right: 3, bottom: 3, left: 3 } })}\n className={BTN}\n title={enableBleedLabel}\n >\n <BleedGlyph className=\"h-3.5 w-3.5\" />\n </button>\n )\n }\n\n return (\n <>\n {(['top', 'right', 'bottom', 'left'] as const).map((side) => (\n <NumberInput\n key={side}\n label={side[0]!.toUpperCase() + side.slice(1)}\n value={bleed[side]}\n min={0}\n onCommit={(v) => setBleedSide(side, v)}\n className=\"w-12\"\n />\n ))}\n <button\n type=\"button\"\n disabled={!canWrite}\n onClick={() => onSetPageProps({ bleed: null })}\n className={BTN}\n title=\"Remove print bleed\"\n aria-label=\"Remove print bleed\"\n >\n ×\n </button>\n </>\n )\n}\n","/**\n * Canvas zoom controls: fit-to-page, 100%, zoom-out/in buttons, and a percent\n * readout. Stateless — zoom lives in the editor's view state, updated through\n * onZoom.\n */\n\nimport { ZoomFitGlyph } from './glyphs'\nimport { BTN_SM } from './icon-button'\n\nexport interface ZoomControlsProps {\n zoom: number\n onZoom(zoom: number): void\n onFit(): void\n /** Label/aria for the fit action. Defaults to \"Fit to screen\" — shorter and\n * matching every other tool's wording for the same action. */\n fitLabel?: string\n}\n\nconst STEP = 0.1\nconst MIN = 0.05\nconst MAX = 32\n\nexport function ZoomControls({ zoom, onZoom, onFit, fitLabel = 'Fit to screen' }: ZoomControlsProps) {\n function zoomOut() {\n onZoom(Math.max(MIN, parseFloat((zoom - STEP).toFixed(4))))\n }\n function zoomIn() {\n onZoom(Math.min(MAX, parseFloat((zoom + STEP).toFixed(4))))\n }\n function resetHundred() {\n onZoom(1)\n }\n\n return (\n <div className=\"flex items-center gap-1 px-2\">\n <button\n type=\"button\"\n aria-label={fitLabel}\n onClick={onFit}\n className={BTN_SM}\n title={`${fitLabel} (F)`}\n >\n <ZoomFitGlyph className=\"h-3.5 w-3.5\" />\n </button>\n\n <button\n type=\"button\"\n aria-label=\"Zoom out\"\n onClick={zoomOut}\n disabled={zoom <= MIN}\n className={BTN_SM}\n >\n <span className=\"text-base leading-none\">−</span>\n </button>\n\n <button\n type=\"button\"\n aria-label=\"Reset to 100%\"\n onClick={resetHundred}\n className=\"rounded px-1.5 py-0.5 font-mono text-[11px] tabular-nums text-[var(--text-secondary)] transition hover:bg-[var(--border-default)] hover:text-[var(--text-primary)]\"\n title=\"Reset to 100%\"\n >\n {Math.round(zoom * 100)}%\n </button>\n\n <button\n type=\"button\"\n aria-label=\"Zoom in\"\n onClick={zoomIn}\n disabled={zoom >= MAX}\n className={BTN_SM}\n >\n <span className=\"text-sm leading-none\">+</span>\n </button>\n </div>\n )\n}\n","/**\n * Compact Export control for the editor chrome's top-right slot. A single\n * button opens a small popover to choose format (PNG/JPEG) and scale (1x/2x);\n * confirming calls `onExport({ format, pixelRatio })`.\n *\n * The chrome is Konva-free, so this control does NOT render the image — it only\n * collects the format/scale and delegates to the workspace (which owns the\n * stage) via the callback the DesignCanvas shell wires through `onExportRef`.\n *\n * Tokens/glyphs follow the canvas convention: CSS-var design tokens and inline\n * SVG glyphs, no icon-library or browser-default `<select>`.\n */\n\nimport { useRef, useState } from 'react'\nimport type { ExportTriggerOptions } from '../contracts'\nimport { ChevronDownGlyph, ExportGlyph } from './glyphs'\n\nexport interface ExportControlProps {\n /** Pre-selected format/scale when the popover opens. Default PNG @ 1x. */\n defaults?: ExportTriggerOptions\n /** Called with the chosen format/scale when the user confirms. */\n onExport(opts: ExportTriggerOptions): void\n className?: string\n}\n\nconst FORMATS: ReadonlyArray<{ id: 'png' | 'jpeg'; label: string }> = [\n { id: 'png', label: 'PNG' },\n { id: 'jpeg', label: 'JPEG' },\n]\n\nconst SCALES: ReadonlyArray<{ value: number; label: string }> = [\n { value: 1, label: '1x' },\n { value: 2, label: '2x' },\n]\n\nexport function ExportControl({ defaults, onExport, className }: ExportControlProps) {\n const [open, setOpen] = useState(false)\n const [format, setFormat] = useState<'png' | 'jpeg'>(defaults?.format ?? 'png')\n const [pixelRatio, setPixelRatio] = useState<number>(defaults?.pixelRatio ?? 1)\n const containerRef = useRef<HTMLDivElement>(null)\n\n function confirm() {\n onExport({ format, pixelRatio })\n setOpen(false)\n }\n\n return (\n <div ref={containerRef} className={`relative ${className ?? ''}`}>\n <button\n type=\"button\"\n aria-label=\"Export\"\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n onClick={() => setOpen((v) => !v)}\n // Close the popover when focus leaves the whole control (click-away /\n // tab-away) — but not when moving between the control's own children.\n onBlur={(event) => {\n if (!containerRef.current?.contains(event.relatedTarget as Node | null)) {\n setOpen(false)\n }\n }}\n className=\"flex h-8 items-center gap-1.5 rounded-md border border-[var(--border-default)] px-2.5 text-xs font-medium text-[var(--text-primary)] transition-colors hover:border-[color-mix(in_srgb,var(--brand-primary)_40%,transparent)]\"\n >\n <ExportGlyph className=\"h-3.5 w-3.5\" />\n Export\n <ChevronDownGlyph className=\"h-3 w-3 text-[var(--text-muted)]\" />\n </button>\n\n {open ? (\n <div\n role=\"dialog\"\n aria-label=\"Export options\"\n onBlur={(event) => {\n if (!containerRef.current?.contains(event.relatedTarget as Node | null)) {\n setOpen(false)\n }\n }}\n className=\"absolute right-0 top-full z-50 mt-1 flex w-52 flex-col gap-3 rounded-md border border-[var(--border-default)] bg-[var(--bg-input)] p-3 shadow-lg\"\n >\n <div className=\"flex flex-col gap-1.5\">\n <span className=\"text-[11px] font-medium text-[var(--text-muted)]\">Format</span>\n <div className=\"flex gap-1.5\">\n {FORMATS.map((f) => (\n <button\n key={f.id}\n type=\"button\"\n aria-pressed={format === f.id}\n onClick={() => setFormat(f.id)}\n className={`flex-1 rounded border px-2 py-1 text-[11px] transition-colors ${\n format === f.id\n ? 'border-[var(--brand-primary)] text-[var(--brand-primary)]'\n : 'border-[var(--border-default)] text-[var(--text-secondary)] hover:text-[var(--text-primary)]'\n }`}\n >\n {f.label}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"flex flex-col gap-1.5\">\n <span className=\"text-[11px] font-medium text-[var(--text-muted)]\">Scale</span>\n <div className=\"flex gap-1.5\">\n {SCALES.map((s) => (\n <button\n key={s.value}\n type=\"button\"\n aria-pressed={pixelRatio === s.value}\n onClick={() => setPixelRatio(s.value)}\n className={`flex-1 rounded border px-2 py-1 text-[11px] transition-colors ${\n pixelRatio === s.value\n ? 'border-[var(--brand-primary)] text-[var(--brand-primary)]'\n : 'border-[var(--border-default)] text-[var(--text-secondary)] hover:text-[var(--text-primary)]'\n }`}\n >\n {s.label}\n </button>\n ))}\n </div>\n </div>\n\n <button\n type=\"button\"\n aria-label=\"Export image\"\n onClick={confirm}\n className=\"rounded border border-[var(--brand-primary)] px-2 py-1 text-[11px] font-medium text-[var(--brand-primary)] transition-colors hover:bg-[color-mix(in_srgb,var(--brand-primary)_10%,transparent)]\"\n >\n Export\n </button>\n </div>\n ) : null}\n </div>\n )\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","jsx","Fragment","jsx","jsxs","jsx","IconButton","jsx","jsxs","useRef","useState","Fragment","jsx","jsxs","useRef","useState","useEffect","useRef","useState","Fragment","jsx","jsxs","useRef","useEffect","useState","jsx","jsxs","useRef","useState","jsx","jsxs","useState","useRef","jsx","jsxs","useCallback","useRef","useEffect","useState"]}
@@ -226,8 +226,9 @@ function createZoomPanMath(config) {
226
226
  if (!Number.isFinite(paddingPx) || paddingPx < 0) {
227
227
  throw new Error(`fitPage: paddingPx must be a non-negative finite number, got ${paddingPx}`);
228
228
  }
229
- const availW = viewport.width - paddingPx * 2;
230
- const availH = viewport.height - paddingPx * 2;
229
+ const pad = Math.min(paddingPx, Math.min(viewport.width, viewport.height) * 0.2);
230
+ const availW = viewport.width - pad * 2;
231
+ const availH = viewport.height - pad * 2;
231
232
  const zoom = Math.min(maxZoom, Math.max(minZoom, Math.min(availW / page.width, availH / page.height)));
232
233
  const panX = (viewport.width - page.width * zoom) / 2;
233
234
  const panY = (viewport.height - page.height * zoom) / 2;
@@ -262,6 +263,10 @@ function resolveExportParams(page, opts) {
262
263
  quality: format === "jpeg" ? 0.92 : void 0
263
264
  };
264
265
  }
266
+ var NODE_CACHE_PIXEL_RATIO_MAX = 4;
267
+ function resolveNodeCachePixelRatio(absoluteScale, devicePixelRatio) {
268
+ return Math.min(absoluteScale * devicePixelRatio, NODE_CACHE_PIXEL_RATIO_MAX);
269
+ }
265
270
  function identifyTaintedSrc(imageSrcs) {
266
271
  for (const { src } of imageSrcs) {
267
272
  if (isCrossOriginSrc(src)) return src;
@@ -430,6 +435,8 @@ export {
430
435
  createZoomPanMath,
431
436
  isExportHiddenNodeName,
432
437
  resolveExportParams,
438
+ NODE_CACHE_PIXEL_RATIO_MAX,
439
+ resolveNodeCachePixelRatio,
433
440
  identifyTaintedSrc,
434
441
  isCrossOriginSrc,
435
442
  documentCropToStageCoords,
@@ -440,4 +447,4 @@ export {
440
447
  buildInsertImageOp,
441
448
  DEFAULT_INSERT_TEMPLATES
442
449
  };
443
- //# sourceMappingURL=chunk-4QVG4WJQ.js.map
450
+ //# sourceMappingURL=chunk-OYTMILRK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/design-canvas-react/engine/snap.ts","../src/design-canvas-react/engine/selection.ts","../src/design-canvas-react/engine/zoom-pan.ts","../src/design-canvas-react/export-math.ts","../src/design-canvas-react/insert-builders.ts"],"sourcesContent":["/**\n * 2-axis snap engine for the design-canvas editor. Targets come from element\n * AABBs (edges + centers), page edges + center, saved guides, and grid lines\n * near the moving bounds. Grid lines are generated lazily in the neighborhood\n * of the moving element, not the full page — avoids allocating thousands of\n * targets on large pages with fine grids.\n *\n * Threshold is a SCREEN distance (pixels), divided by zoom to convert to\n * document units — what \"feels close\" is a screen distance.\n *\n * Tie-breaking: non-grid kinds beat grid on equal distance; among equals of\n * the same priority the first in iteration order wins.\n */\n\nimport { elementAabb } from '../../design-canvas/model'\nimport type { Bounds, SceneElement, ScenePage } from '../../design-canvas/model'\nimport type { EditorSceneState, SnapEngine, SnapResult, SnapTarget, SnapTargetKind, SnapTargets } from '../contracts'\n\n/** Priority rank for tie-breaking: lower = higher priority (wins the tie). */\nconst KIND_PRIORITY: Record<SnapTargetKind, number> = {\n 'guide': 0,\n 'page-edge': 1,\n 'page-center': 2,\n 'element-edge': 3,\n 'element-center': 4,\n 'grid': 5,\n}\n\n/** Build a SnapEngine instance to manage snapping targets and behavior within an editor scene */\nexport function createSnapEngine(): SnapEngine {\n return {\n collectTargets(state: EditorSceneState, excludeIds: string[]): SnapTargets {\n const page = state.document.pages.find((p) => p.id === state.activePageId)\n if (!page) throw new Error(`collectTargets: active page ${state.activePageId} not found`)\n\n const vertical: SnapTarget[] = []\n const horizontal: SnapTarget[] = []\n const excludeSet = new Set(excludeIds)\n\n // Page edges + center\n vertical.push({ position: 0, kind: 'page-edge' })\n vertical.push({ position: page.width, kind: 'page-edge' })\n vertical.push({ position: page.width / 2, kind: 'page-center' })\n horizontal.push({ position: 0, kind: 'page-edge' })\n horizontal.push({ position: page.height, kind: 'page-edge' })\n horizontal.push({ position: page.height / 2, kind: 'page-center' })\n\n // Saved ruler guides\n for (const pos of page.guides.vertical) {\n vertical.push({ position: pos, kind: 'guide' })\n }\n for (const pos of page.guides.horizontal) {\n horizontal.push({ position: pos, kind: 'guide' })\n }\n\n // Element edges + centers (visible, unlocked, not excluded)\n collectElementTargets(page.elements, excludeSet, vertical, horizontal)\n\n // Grid lines (deferred to apply() with the moving bounds neighborhood)\n // Grid targets are injected by collectGridTargets() when gridEnabled.\n\n return { vertical, horizontal }\n },\n\n apply(bounds: Bounds, targets: SnapTargets, thresholdPx: number, zoom: number): SnapResult {\n if (!Number.isFinite(zoom) || zoom <= 0) {\n throw new Error(`snap.apply: zoom must be a positive finite number, got ${zoom}`)\n }\n if (!Number.isFinite(thresholdPx) || thresholdPx < 0) {\n throw new Error(`snap.apply: thresholdPx must be a non-negative finite number, got ${thresholdPx}`)\n }\n const threshold = thresholdPx / zoom\n\n const snappedX = snapAxis(bounds.x, bounds.x + bounds.width / 2, bounds.x + bounds.width, targets.vertical, threshold)\n const snappedY = snapAxis(bounds.y, bounds.y + bounds.height / 2, bounds.y + bounds.height, targets.horizontal, threshold)\n\n return {\n x: snappedX !== null ? snappedX.docPosition - snappedX.elementOffset : bounds.x,\n y: snappedY !== null ? snappedY.docPosition - snappedY.elementOffset : bounds.y,\n activeVertical: snappedX !== null ? snappedX.target : null,\n activeHorizontal: snappedY !== null ? snappedY.target : null,\n }\n },\n }\n}\n\n/** Generate grid line targets within a neighborhood around the moving bounds.\n * Call this and append to `SnapTargets.vertical`/`horizontal` before\n * passing to `apply()` when `state.gridEnabled`. */\nexport function collectGridTargets(\n bounds: Bounds,\n gridSize: number,\n page: ScenePage,\n thresholdDocPx: number,\n): { vertical: SnapTarget[]; horizontal: SnapTarget[] } {\n if (!Number.isFinite(gridSize) || gridSize <= 0) {\n throw new Error(`collectGridTargets: gridSize must be positive finite, got ${gridSize}`)\n }\n const vertical: SnapTarget[] = []\n const horizontal: SnapTarget[] = []\n\n // Generate lines that fall within the bounds + threshold neighborhood\n const xMin = Math.floor((bounds.x - thresholdDocPx) / gridSize) * gridSize\n const xMax = Math.ceil((bounds.x + bounds.width + thresholdDocPx) / gridSize) * gridSize\n for (let x = xMin; x <= xMax; x += gridSize) {\n if (x >= 0 && x <= page.width) vertical.push({ position: x, kind: 'grid' })\n }\n\n const yMin = Math.floor((bounds.y - thresholdDocPx) / gridSize) * gridSize\n const yMax = Math.ceil((bounds.y + bounds.height + thresholdDocPx) / gridSize) * gridSize\n for (let y = yMin; y <= yMax; y += gridSize) {\n if (y >= 0 && y <= page.height) horizontal.push({ position: y, kind: 'grid' })\n }\n\n return { vertical, horizontal }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\ninterface SnapHit {\n /** The snap target that won. */\n target: SnapTarget\n /** The document-coordinate position of the snap line. */\n docPosition: number\n /** How far the element's reference point (left/center/right or top/mid/bottom)\n * is from the element's x/y origin — used to reconstruct the new origin. */\n elementOffset: number\n}\n\n/** Try snapping each of the three reference points (start/center/end) to the\n * nearest target. Returns the best hit across all three, or null. */\nfunction snapAxis(start: number, center: number, end: number, targets: SnapTarget[], threshold: number): SnapHit | null {\n const candidates: Array<{ value: number; offset: number }> = [\n { value: start, offset: 0 },\n { value: center, offset: center - start },\n { value: end, offset: end - start },\n ]\n\n let best: SnapHit | null = null\n let bestDistance = Infinity\n let bestPriority = Infinity\n\n for (const { value, offset } of candidates) {\n for (const target of targets) {\n const distance = Math.abs(target.position - value)\n if (distance > threshold) continue\n const priority = KIND_PRIORITY[target.kind]\n if (\n distance < bestDistance ||\n (distance === bestDistance && priority < bestPriority)\n ) {\n bestDistance = distance\n bestPriority = priority\n best = { target, docPosition: target.position, elementOffset: offset }\n }\n }\n }\n\n return best\n}\n\nfunction collectElementTargets(\n elements: SceneElement[],\n excludeIds: Set<string>,\n vertical: SnapTarget[],\n horizontal: SnapTarget[],\n): void {\n for (const el of elements) {\n if (!el.visible || el.locked) continue\n if (excludeIds.has(el.id)) continue\n\n const aabb = elementAabb(el)\n // Left, center, right\n vertical.push({ position: aabb.x, kind: 'element-edge' })\n vertical.push({ position: aabb.x + aabb.width / 2, kind: 'element-center' })\n vertical.push({ position: aabb.x + aabb.width, kind: 'element-edge' })\n // Top, middle, bottom\n horizontal.push({ position: aabb.y, kind: 'element-edge' })\n horizontal.push({ position: aabb.y + aabb.height / 2, kind: 'element-center' })\n horizontal.push({ position: aabb.y + aabb.height, kind: 'element-edge' })\n\n if (el.kind === 'group') {\n collectElementTargets(el.children, excludeIds, vertical, horizontal)\n }\n }\n}\n","/**\n * Selection math for the design-canvas editor: marquee hit-tests, keyboard\n * nudge deltas, and the duplicate offset constant. Pure functions — no DOM, no\n * Konva, no React.\n *\n * Marquee inclusion: by default any element whose AABB intersects the marquee\n * rect is included (the \"touch\" model). Pass `requireFullContainment: true` for\n * the \"surround\" model where the marquee must fully contain the element. Locked\n * and invisible elements are never selected regardless.\n */\n\nimport { boundsIntersect, elementAabb } from '../../design-canvas/model'\nimport type { Bounds, SceneElement, ScenePage } from '../../design-canvas/model'\n\n// ---------------------------------------------------------------------------\n// Point hit-test (model-space)\n// ---------------------------------------------------------------------------\n\n/**\n * Top-most selectable element whose AABB contains the document-space point, or\n * null when the point is over empty space.\n *\n * This is the authoritative \"is the pointer over an element?\" test for the\n * pointer-down gesture router. It runs against the scene model in document\n * coordinates rather than Konva's hit-graph canvas, so it is independent of\n * clip groups, listening flags, and hit-canvas redraw timing — all of which\n * could make `stage.getIntersection` misclassify a press as empty space and\n * silently start a marquee instead of an element drag.\n *\n * Z-order: later elements paint on top, so we scan the array in reverse and\n * return the first hit. Groups descend into children (top child wins); a hit on\n * a group child resolves to the group itself, matching drag/selection which\n * operate on the group as a unit. Locked and invisible elements never hit.\n */\nexport function hitTestPoint(page: ScenePage, x: number, y: number): string | null {\n return hitTestIn(page.elements, x, y, null)\n}\n\nfunction hitTestIn(\n elements: SceneElement[],\n x: number,\n y: number,\n resolveTo: string | null,\n): string | null {\n for (let i = elements.length - 1; i >= 0; i -= 1) {\n const el = elements[i]!\n if (!el.visible || el.locked) continue\n const aabb = elementAabb(el)\n if (!pointInBounds(aabb, x, y)) continue\n if (el.kind === 'group') {\n // A press anywhere inside the group AABB selects the group as a unit,\n // even over the gaps between children — the group is the draggable node.\n const child = hitTestIn(el.children, x, y, el.id)\n return child ?? el.id\n }\n return resolveTo ?? el.id\n }\n return null\n}\n\nfunction pointInBounds(b: Bounds, x: number, y: number): boolean {\n return x >= b.x && x <= b.x + b.width && y >= b.y && y <= b.y + b.height\n}\n\n/** Define options to configure marquee selection behavior including containment requirements */\nexport interface MarqueeSelectOptions {\n /** When true, the element's AABB must be fully inside the marquee; default is\n * intersection (any overlap selects). */\n requireFullContainment?: boolean\n}\n\n/** Returns the ids of selectable elements on `page` whose AABB intersects (or\n * is contained by) `rect`. Locked and invisible elements are excluded. */\nexport function marqueeSelect(page: ScenePage, rect: Bounds, opts: MarqueeSelectOptions = {}): string[] {\n assertValidBounds(rect, 'marqueeSelect rect')\n const result: string[] = []\n collectMarqueeIds(page.elements, rect, opts.requireFullContainment ?? false, result)\n return result\n}\n\nfunction collectMarqueeIds(\n elements: SceneElement[],\n rect: Bounds,\n requireContainment: boolean,\n result: string[],\n): void {\n for (const el of elements) {\n if (!el.visible || el.locked) continue\n const aabb = elementAabb(el)\n\n if (el.kind === 'group') {\n // Groups are transparent to marquee: descend when the group AABB intersects\n // the rect, collecting children individually. The group entity itself is\n // never added — marquee selects leaf elements, not the group wrapper.\n if (boundsIntersect(rect, aabb)) {\n collectMarqueeIds(el.children, rect, requireContainment, result)\n }\n continue\n }\n\n const hit = requireContainment ? boundsContain(rect, aabb) : boundsIntersect(rect, aabb)\n if (hit) result.push(el.id)\n }\n}\n\n/** Returns true when `outer` fully contains `inner` (edges may touch). */\nfunction boundsContain(outer: Bounds, inner: Bounds): boolean {\n return (\n inner.x >= outer.x &&\n inner.y >= outer.y &&\n inner.x + inner.width <= outer.x + outer.width &&\n inner.y + inner.height <= outer.y + outer.height\n )\n}\n\n// ---------------------------------------------------------------------------\n// Keyboard nudge\n// ---------------------------------------------------------------------------\n\n/** Pixels to move per nudge step at normal and shifted speed. */\nconst NUDGE_NORMAL_PX = 1\nconst NUDGE_SHIFT_PX = 10\n\n/** Represent horizontal and vertical displacement values for nudging elements */\nexport interface NudgeDelta {\n dx: number\n dy: number\n}\n\n/** Map an arrow key to a document-px delta. `shift` engages the 10× step.\n * Throws on unknown keys so callers handle only real nudge keys. */\nexport function nudgeDelta(key: 'ArrowLeft' | 'ArrowRight' | 'ArrowUp' | 'ArrowDown', shift: boolean): NudgeDelta {\n const step = shift ? NUDGE_SHIFT_PX : NUDGE_NORMAL_PX\n switch (key) {\n case 'ArrowLeft': return { dx: -step, dy: 0 }\n case 'ArrowRight': return { dx: step, dy: 0 }\n case 'ArrowUp': return { dx: 0, dy: -step }\n case 'ArrowDown': return { dx: 0, dy: step }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Duplicate offset\n// ---------------------------------------------------------------------------\n\n/** Document-px offset applied to duplicated elements so the copy is visually\n * separated from the original (matching common design-tool convention). */\nexport const DUPLICATE_OFFSET: NudgeDelta = { dx: 10, dy: 10 }\n\n// ---------------------------------------------------------------------------\n// Internal\n// ---------------------------------------------------------------------------\n\nfunction assertValidBounds(bounds: Bounds, label: string): void {\n if (!Number.isFinite(bounds.x) || !Number.isFinite(bounds.y) ||\n !Number.isFinite(bounds.width) || !Number.isFinite(bounds.height)) {\n throw new Error(`${label}: all fields must be finite numbers`)\n }\n}\n","/**\n * Zoom + pan coordinate math for the design-canvas editor. Zoom is pixels-per-\n * document-px (e.g. 2 = 200% magnification). The key invariant for wheel-zoom\n * is that the document point under the cursor stays fixed in screen space:\n *\n * docPoint = (screenPoint - pan) / zoom\n * newPan = screenPoint - docPoint * newZoom\n *\n * This module is pure math — no DOM, no Konva, no React.\n */\n\nimport type { ZoomPanMath } from '../contracts'\n\n/** Define configuration options for minimum and maximum zoom levels in a zoom-pan interface */\nexport interface ZoomPanConfig {\n minZoom: number\n maxZoom: number\n}\n\n/** Create zoom and pan math utilities enforcing valid zoom range constraints */\nexport function createZoomPanMath(config: ZoomPanConfig): ZoomPanMath {\n const { minZoom, maxZoom } = config\n if (!Number.isFinite(minZoom) || minZoom <= 0) {\n throw new Error(`minZoom must be a positive finite number, got ${minZoom}`)\n }\n if (!Number.isFinite(maxZoom) || maxZoom <= minZoom) {\n throw new Error(`maxZoom must be finite and greater than minZoom (${minZoom}), got ${maxZoom}`)\n }\n\n return {\n minZoom,\n maxZoom,\n\n zoomAtPoint(\n state: { zoom: number; panX: number; panY: number },\n factor: number,\n screenX: number,\n screenY: number,\n ): { zoom: number; panX: number; panY: number } {\n if (!Number.isFinite(factor) || factor <= 0) {\n throw new Error(`zoomAtPoint: factor must be a positive finite number, got ${factor}`)\n }\n if (!Number.isFinite(screenX) || !Number.isFinite(screenY)) {\n throw new Error(`zoomAtPoint: screenX/screenY must be finite numbers`)\n }\n if (!Number.isFinite(state.zoom) || state.zoom <= 0) {\n throw new Error(`zoomAtPoint: state.zoom must be a positive finite number, got ${state.zoom}`)\n }\n\n const newZoom = Math.min(maxZoom, Math.max(minZoom, state.zoom * factor))\n\n // The document point under the cursor must remain fixed:\n // docX = (screenX - panX) / zoom\n // newPanX = screenX - docX * newZoom\n const docX = (screenX - state.panX) / state.zoom\n const docY = (screenY - state.panY) / state.zoom\n const panX = screenX - docX * newZoom\n const panY = screenY - docY * newZoom\n\n return { zoom: newZoom, panX, panY }\n },\n\n fitPage(\n page: { width: number; height: number },\n viewport: { width: number; height: number },\n paddingPx = 48,\n ): { zoom: number; panX: number; panY: number } {\n if (!Number.isFinite(page.width) || page.width <= 0) {\n throw new Error(`fitPage: page.width must be a positive finite number, got ${page.width}`)\n }\n if (!Number.isFinite(page.height) || page.height <= 0) {\n throw new Error(`fitPage: page.height must be a positive finite number, got ${page.height}`)\n }\n if (!Number.isFinite(viewport.width) || viewport.width <= 0) {\n throw new Error(`fitPage: viewport.width must be a positive finite number, got ${viewport.width}`)\n }\n if (!Number.isFinite(viewport.height) || viewport.height <= 0) {\n throw new Error(`fitPage: viewport.height must be a positive finite number, got ${viewport.height}`)\n }\n if (!Number.isFinite(paddingPx) || paddingPx < 0) {\n throw new Error(`fitPage: paddingPx must be a non-negative finite number, got ${paddingPx}`)\n }\n\n // Cap padding at 20% of the limiting viewport dimension. On tiny real\n // containers (composite mini-editor cells, thumbnail embeds) a fixed\n // 48px margin eats the canvas — a 135px slot would leave 39px and clamp\n // the fit at minZoom, rendering the page as a speck. Large viewports are\n // unaffected (cap ≥ 48 keeps the historical padding byte-identical).\n const pad = Math.min(paddingPx, Math.min(viewport.width, viewport.height) * 0.2)\n const availW = viewport.width - pad * 2\n const availH = viewport.height - pad * 2\n const zoom = Math.min(maxZoom, Math.max(minZoom, Math.min(availW / page.width, availH / page.height)))\n\n // Center the page in the viewport\n const panX = (viewport.width - page.width * zoom) / 2\n const panY = (viewport.height - page.height * zoom) / 2\n\n return { zoom, panX, panY }\n },\n\n documentToScreen(\n state: { zoom: number; panX: number; panY: number },\n x: number,\n y: number,\n ): { x: number; y: number } {\n return { x: x * state.zoom + state.panX, y: y * state.zoom + state.panY }\n },\n\n screenToDocument(\n state: { zoom: number; panX: number; panY: number },\n x: number,\n y: number,\n ): { x: number; y: number } {\n if (!Number.isFinite(state.zoom) || state.zoom === 0) {\n throw new Error(`screenToDocument: zoom must be a non-zero finite number, got ${state.zoom}`)\n }\n return { x: (x - state.panX) / state.zoom, y: (y - state.panY) / state.zoom }\n },\n }\n}\n","/**\n * Pure export math for the design-canvas surface — no Konva, no DOM, no React.\n *\n * All stage-interaction code (save/restore, node visibility, toDataURL) lives\n * in export.ts and delegates the decision math here so it can be tested\n * without a canvas environment.\n */\n\nimport type { ScenePage } from '../design-canvas/model'\nimport {\n bleedAwareExportBounds,\n scaleForPreset,\n type ExportCropRect,\n type ExportPreset,\n} from '../design-canvas/export-presets'\n\n// ---------------------------------------------------------------------------\n// Re-exports so callers only import from one place\n// ---------------------------------------------------------------------------\n\nexport type { ExportCropRect, ExportPreset }\nexport { bleedAwareExportBounds, scaleForPreset }\n\n// ---------------------------------------------------------------------------\n// Overlay / transformer node name predicates\n// ---------------------------------------------------------------------------\n\n/** Returns true for any Konva node that must be hidden during export. Nodes\n * whose names start with 'overlay:' are editor-only chrome (snap lines,\n * selection indicators, rulers, bleed guides). The transformer node is\n * identified by its canonical name 'Transformer'. */\nexport function isExportHiddenNodeName(name: string): boolean {\n return name.startsWith('overlay:') || name === 'Transformer'\n}\n\n// ---------------------------------------------------------------------------\n// Crop rect + pixel ratio resolution\n// ---------------------------------------------------------------------------\n\n/** Define parameters required to resolve export settings including crop, pixel ratio, type, and quality */\nexport interface ResolvedExportParams {\n cropRect: ExportCropRect\n pixelRatio: number\n mimeType: 'image/png' | 'image/jpeg'\n quality: number | undefined\n}\n\n/**\n * Resolve the full set of toDataURL params from a page, format, pixel ratio,\n * bleed flag, and optional preset. When a preset is supplied it overrides\n * `pixelRatio` and `includeBleed` — the explicit args are ignored for those\n * two values so the preset is the single source of truth.\n *\n * `quality` is set to 0.92 for jpeg, undefined for png (Konva ignores it).\n */\nexport function resolveExportParams(\n page: ScenePage,\n opts: {\n format: 'png' | 'jpeg'\n pixelRatio?: number\n includeBleed?: boolean\n preset?: ExportPreset\n },\n): ResolvedExportParams {\n const includeBleed = opts.preset !== undefined\n ? opts.preset.includeBleed\n : (opts.includeBleed ?? false)\n\n const cropRect = bleedAwareExportBounds(page, includeBleed)\n\n const pixelRatio = opts.preset !== undefined\n ? scaleForPreset(opts.preset, cropRect)\n : (opts.pixelRatio ?? 1)\n\n const format = opts.preset !== undefined ? opts.preset.format : opts.format\n const mimeType: 'image/png' | 'image/jpeg' = format === 'jpeg' ? 'image/jpeg' : 'image/png'\n\n return {\n cropRect,\n pixelRatio,\n mimeType,\n quality: format === 'jpeg' ? 0.92 : undefined,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Node bitmap-cache pixel ratio\n// ---------------------------------------------------------------------------\n\n/** Hard ceiling for node cache rasterization. Without it a deep zoom (max 32)\n * on a retina display would rasterize page-size elements into canvases beyond\n * browser backing-store limits; 4x supersampling covers every realistic\n * crispness-critical range (fit-page → 200% at dpr 2). */\nexport const NODE_CACHE_PIXEL_RATIO_MAX = 4\n\n/**\n * Pixel ratio a Konva node bitmap cache should rasterize at so the cached\n * bitmap matches the screen exactly: the node's absolute scale (content-group\n * zoom) × device pixel ratio. Konva's cache default ignores zoom, so the\n * bitmap is re-scaled at draw time — bilinear-soft at fit-page zooms like\n * 0.47, badly upscaled on zoom-in. ElementNode caches at this ratio and\n * export.ts restores cleared caches at it, keeping one policy in one place.\n */\nexport function resolveNodeCachePixelRatio(absoluteScale: number, devicePixelRatio: number): number {\n return Math.min(absoluteScale * devicePixelRatio, NODE_CACHE_PIXEL_RATIO_MAX)\n}\n\n// ---------------------------------------------------------------------------\n// CORS taint detection\n// ---------------------------------------------------------------------------\n\n/**\n * Walk image nodes to find the src of any that may have caused a SecurityError\n * when the stage called toDataURL. Returns the first offending src found, or\n * null if none can be identified.\n *\n * `imageSrcs` is a flat list of { name, src } records collected from all image\n * nodes on the stage before calling toDataURL — export.ts builds it before\n * attempting the export so that if the SecurityError fires we have the data.\n */\nexport function identifyTaintedSrc(imageSrcs: ReadonlyArray<{ name: string; src: string }>): string | null {\n // We cannot re-probe CORS from a caught SecurityError — the browser does not\n // tell us which src triggered it. Return the first src that looks like a\n // cross-origin resource the browser would taint: anything that is not a\n // same-origin relative path or a data: URL.\n for (const { src } of imageSrcs) {\n if (isCrossOriginSrc(src)) return src\n }\n return null\n}\n\n/**\n * Heuristic: a src is potentially cross-origin when it is an absolute http(s)\n * URL. Same-origin relative paths (/api/...) and data: blobs are safe.\n * This is intentionally conservative — the taint check runs only after a\n * SecurityError fires, so a false positive is \"blamed\" without being silently\n * ignored. NOTE: same-origin https:// assets will also match; if a\n * SecurityError fires, the cross-origin culprit may be a different image than\n * the one this function flags first.\n */\nexport function isCrossOriginSrc(src: string): boolean {\n return /^https?:\\/\\//i.test(src)\n}\n\n// ---------------------------------------------------------------------------\n// View state snapshot (used by save/restore helpers)\n// ---------------------------------------------------------------------------\n\n/** Minimal snapshot of stage view state that export.ts saves before mutating\n * zoom/pan/visibility and restores afterward. */\nexport interface ExportViewSnapshot {\n /** Stage scale factor before export. */\n scaleX: number\n scaleY: number\n /** Stage translation before export. */\n x: number\n y: number\n /** node name → prior visibility, for the nodes that were hidden. */\n hiddenNodeNames: string[]\n}\n\n/**\n * Build the toDataURL crop rect translated to stage output coordinates.\n * Konva's `stage.toDataURL({ x, y, width, height })` takes STAGE OUTPUT\n * coordinates, not document coordinates. When the stage has been scaled to fit\n * (zoom × stageScale), the crop rect in document px must be multiplied by the\n * combined scale to land on the right pixels.\n *\n * `stageScale` is the stage's current uniform scale (scaleX == scaleY);\n * `pixelRatio` is separate and handed directly to toDataURL — Konva multiplies\n * it internally when rendering to the backing canvas, so we must NOT include\n * it in the stage-coordinate rect.\n */\nexport function documentCropToStageCoords(\n cropRect: ExportCropRect,\n stageScale: number,\n stageX: number,\n stageY: number,\n): { x: number; y: number; width: number; height: number } {\n return {\n x: stageX + cropRect.x * stageScale,\n y: stageY + cropRect.y * stageScale,\n width: cropRect.width * stageScale,\n height: cropRect.height * stageScale,\n }\n}\n","/**\n * Pure builders for the human \"add element to the canvas\" path — the math and\n * `SceneOperation` construction behind {@link CanvasInsertPanel}, separated from\n * the React/DOM layer so every insert is unit-testable without Konva or a\n * browser. The panel calls these to produce `add_element` operations and hands\n * them to the host's `onApplyOperations` (server-validated, undoable) — the same\n * pipeline every other edit flows through.\n *\n * Media boundary: image inserts must pass {@link assertSceneMediaSrc} (remote\n * http(s) or a rooted `/api/` path — never `data:` blobs or sandbox-local\n * files). Builders that take a src assert it up front so a bad url fails here,\n * not deep in the apply layer.\n */\n\nimport { assertSceneMediaSrc } from '../design-canvas/model'\nimport type {\n EllipseElement,\n ImageElement,\n RectElement,\n SceneElement,\n TextElement,\n} from '../design-canvas/model'\nimport type { SceneOperation } from '../design-canvas/operations'\n\n/** Largest dimension (document px) a freshly inserted element is fitted to,\n * before the page-size cap applies. Keeps a huge upload from filling the page. */\nexport const MAX_INSERT_DIMENSION = 600\n\n/** Active page geometry an insert lands into — drives centering and fitting. */\nexport interface InsertPageGeometry {\n pageId: string\n width: number\n height: number\n background?: string\n}\n\n/** Mint a DOM-safe element id. Prefers `crypto.randomUUID`; falls back to a\n * time+random id where crypto is unavailable (older runtimes, some test envs). */\nexport function mintElementId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n return `el-${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\n/** Fit (naturalW, naturalH) under a max-dimension cap (further bounded by the\n * page), preserving aspect ratio. Never upscales past natural size. */\nexport function fittedSize(\n naturalW: number,\n naturalH: number,\n pageWidth: number,\n pageHeight: number,\n): { width: number; height: number } {\n const cap = Math.min(MAX_INSERT_DIMENSION, pageWidth * 0.8, pageHeight * 0.8)\n const longest = Math.max(naturalW, naturalH)\n const scale = naturalW > 0 && naturalH > 0 ? Math.min(1, cap / longest) : 1\n const width = Math.max(1, Math.round((naturalW || cap) * scale))\n const height = Math.max(1, Math.round((naturalH || cap) * scale))\n return { width, height }\n}\n\n/** Top-left position that centers a (width, height) box on the page. */\nexport function centeredPosition(\n width: number,\n height: number,\n pageWidth: number,\n pageHeight: number,\n): { x: number; y: number } {\n return {\n x: Math.round((pageWidth - width) / 2),\n y: Math.round((pageHeight - height) / 2),\n }\n}\n\n/** Common element fields with a freshly minted id and unrotated/visible defaults. */\nfunction baseAttrs(name: string, x: number, y: number) {\n return { id: mintElementId(), name, x, y, rotation: 0, opacity: 1, locked: false, visible: true }\n}\n\n/** Wrap a single element in an `add_element` op for the active page. */\nfunction addElementOp(pageId: string, element: SceneElement): SceneOperation {\n return { type: 'add_element', pageId, element }\n}\n\nfunction hexLuminance(color: string | undefined): number | null {\n if (!color) return null\n const raw = color.trim()\n const hex = raw.startsWith('#') ? raw.slice(1) : ''\n const full = hex.length === 3\n ? hex.split('').map((ch) => ch + ch).join('')\n : hex.length === 6\n ? hex\n : ''\n if (!/^[0-9a-fA-F]{6}$/.test(full)) return null\n const channels = [0, 2, 4].map((start) => parseInt(full.slice(start, start + 2), 16) / 255)\n const [r, g, b] = channels.map((value) => (\n value <= 0.03928 ? value / 12.92 : Math.pow((value + 0.055) / 1.055, 2.4)\n ))\n return 0.2126 * r! + 0.7152 * g! + 0.0722 * b!\n}\n\nfunction pageTextFill(page: InsertPageGeometry, tone: 'primary' | 'secondary'): string {\n const luminance = hexLuminance(page.background)\n if (luminance !== null && luminance < 0.35) {\n return tone === 'primary' ? '#f8fafc' : '#cbd5e1'\n }\n return tone === 'primary' ? '#111827' : '#374151'\n}\n\n/**\n * Build an `add_element` op placing an image, fitted and centered on the page.\n * `naturalSize` is the probed image dimensions (the panel reads them in the\n * browser); pass `{ width: 0, height: 0 }` when unknown to fall back to the cap.\n *\n * Throws (via `assertSceneMediaSrc`) when `src` is not http(s) or a rooted\n * `/api/` path — callers should surface the error, never insert a `data:` src.\n */\nexport function buildInsertImageOp(\n src: string,\n naturalSize: { width: number; height: number },\n page: InsertPageGeometry,\n): SceneOperation {\n assertSceneMediaSrc(src, 'image src')\n const { width, height } = fittedSize(naturalSize.width, naturalSize.height, page.width, page.height)\n const { x, y } = centeredPosition(width, height, page.width, page.height)\n const element: ImageElement = {\n ...baseAttrs('Image', x, y),\n kind: 'image',\n width,\n height,\n src,\n fit: 'contain',\n }\n return addElementOp(page.pageId, element)\n}\n\n/** A template the insert panel can drop without the agent. `build` is pure and\n * produces the operations for the active page geometry. */\nexport interface InsertTemplate {\n id: string\n label: string\n build(page: InsertPageGeometry): SceneOperation[]\n}\n\n/** The built-in starter templates (heading, body text, rectangle, ellipse).\n * Consumers can pass their own list to {@link CanvasInsertPanel}; this is the\n * default so every canvas gets a usable set out of the box. */\nexport const DEFAULT_INSERT_TEMPLATES: readonly InsertTemplate[] = [\n {\n id: 'heading',\n label: 'Heading',\n build(page) {\n const width = Math.min(480, Math.round(page.width * 0.7))\n const { x, y } = centeredPosition(width, 60, page.width, page.height)\n const element: TextElement = {\n ...baseAttrs('Heading', x, y),\n kind: 'text',\n text: 'Add a headline',\n width,\n fontFamily: 'Inter',\n fontSize: 48,\n fontStyle: 'bold',\n fill: pageTextFill(page, 'primary'),\n align: 'left',\n lineHeight: 1.1,\n letterSpacing: 0,\n }\n return [addElementOp(page.pageId, element)]\n },\n },\n {\n id: 'body',\n label: 'Body text',\n build(page) {\n const width = Math.min(420, Math.round(page.width * 0.6))\n const { x, y } = centeredPosition(width, 80, page.width, page.height)\n const element: TextElement = {\n ...baseAttrs('Body', x, y),\n kind: 'text',\n text: 'Add a paragraph of supporting copy.',\n width,\n fontFamily: 'Inter',\n fontSize: 20,\n fontStyle: 'normal',\n fill: pageTextFill(page, 'secondary'),\n align: 'left',\n lineHeight: 1.4,\n letterSpacing: 0,\n }\n return [addElementOp(page.pageId, element)]\n },\n },\n {\n id: 'rect',\n label: 'Rectangle',\n build(page) {\n const width = Math.min(320, Math.round(page.width * 0.4))\n const height = Math.min(200, Math.round(page.height * 0.3))\n const { x, y } = centeredPosition(width, height, page.width, page.height)\n const element: RectElement = {\n ...baseAttrs('Rectangle', x, y),\n kind: 'rect',\n width,\n height,\n fill: '#6366f1',\n cornerRadius: 12,\n }\n return [addElementOp(page.pageId, element)]\n },\n },\n {\n id: 'ellipse',\n label: 'Ellipse',\n build(page) {\n const size = Math.min(220, Math.round(Math.min(page.width, page.height) * 0.3))\n const { x, y } = centeredPosition(size, size, page.width, page.height)\n const element: EllipseElement = {\n ...baseAttrs('Ellipse', x, y),\n kind: 'ellipse',\n width: size,\n height: size,\n fill: '#10b981',\n }\n return [addElementOp(page.pageId, element)]\n },\n },\n]\n"],"mappings":"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","names":[]}