@tangle-network/agent-app 0.15.0 → 0.16.1

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.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/design-canvas-react/components/DesignCanvas.tsx","../src/design-canvas-react/engine/command-stack.ts","../src/design-canvas-react/engine/commands.ts","../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/Rulers.tsx","../src/design-canvas-react/components/Toolbar.tsx","../src/design-canvas-react/components/ZoomControls.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 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, 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'\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 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 onApplyOperations,\n onSelectionChange,\n renderAgentPanel,\n renderSidePanel,\n onExport,\n className,\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 // ---------------------------------------------------------------------------\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-execute\n // to re-apply the original forward state.\n void onApplyOperations(command.inverseOperations())\n .then((result) => {\n if (result.document) stack.reset(result.document)\n })\n .catch((error: unknown) => {\n if (stack.canRedo()) {\n try { stack.redo() } catch { /* concurrent edit; next reset reconciles */ }\n }\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 void onApplyOperations(command.operations())\n .then((result) => {\n if (result.document) stack.reset(result.document)\n })\n .catch((error: unknown) => {\n if (stack.canUndo()) {\n try { stack.undo() } catch { /* concurrent edit; next reset reconciles */ }\n }\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 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, etc.) */}\n {renderSidePanel ? (\n <aside className=\"flex w-64 shrink-0 flex-col overflow-hidden border-r border-[var(--border-default)]\">\n {renderSidePanel()}\n </aside>\n ) : null}\n\n {/* Main column */}\n <div className=\"flex min-w-0 flex-1 flex-col\">\n {/* Toolbar */}\n <Toolbar\n page={activePage}\n selectedElements={selectedElements}\n canWrite={canWrite}\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 />\n\n {/* Error bar */}\n {commitError ? (\n <div\n className=\"flex shrink-0 items-center justify-between gap-3 border-b border-rose-500/30 bg-rose-500/10 px-3 py-1.5 text-xs text-rose-300\"\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 {/* Rulers + Workspace area */}\n <div className=\"relative min-h-0 flex-1\">\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 */}\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,\n top: editorState.panY,\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 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 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 />\n </div>\n </div>\n </div>\n\n {/* Optional right agent panel */}\n {renderAgentPanel ? (\n <aside className=\"flex w-80 shrink-0 flex-col overflow-hidden border-l border-[var(--border-default)]\">\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\nexport default DesignCanvas\n","/**\n * Undo/redo command stack over immutable `EditorSceneState`.\n *\n * History entries hold COMMANDS (state transforms + durable operations\n * captured at construction), never snapshots. Rebasing the document via\n * `reset()` therefore cannot stale the history: a later undo re-applies the\n * inverse transform to whatever document is current. If the rebase removed an\n * element a historical command targets, that transform throws (fail loud)\n * rather than silently editing the wrong element.\n *\n * `setView` updates volatile view state (zoom/pan/selection/toggles) without\n * touching history — view changes are never undo steps.\n */\n\nimport type { SceneDocument } from '../../design-canvas/model'\nimport type { EditorSceneState, SceneCommand, SceneCommandStack } from '../contracts'\n\n/** Oldest entries are dropped past this bound; redo stack is cleared on execute. */\nexport const SCENE_COMMAND_HISTORY_LIMIT = 200\n\nexport function createSceneCommandStack(document: SceneDocument, activePageId: string): SceneCommandStack {\n let state: EditorSceneState = {\n document,\n activePageId,\n selectedElementIds: [],\n zoom: 1,\n panX: 0,\n panY: 0,\n gridEnabled: false,\n gridSize: 10,\n snapEnabled: true,\n showRulers: true,\n showBleed: false,\n }\n\n const undoStack: SceneCommand[] = []\n const redoStack: SceneCommand[] = []\n const listeners = new Set<() => void>()\n\n const notify = (): void => {\n for (const listener of [...listeners]) listener()\n }\n\n return {\n execute(command: SceneCommand): void {\n state = command.execute(state)\n undoStack.push(command)\n if (undoStack.length > SCENE_COMMAND_HISTORY_LIMIT) {\n undoStack.splice(0, undoStack.length - SCENE_COMMAND_HISTORY_LIMIT)\n }\n redoStack.length = 0\n notify()\n },\n\n // Both transforms run BEFORE the stacks move: a throwing transform (e.g.\n // missing element after reset()) leaves history and state exactly as they\n // were. The entry is never silently destroyed — the caller can retry after\n // the next server refresh restores the target.\n undo(): SceneCommand {\n const command = undoStack[undoStack.length - 1]\n if (!command) throw new Error('nothing to undo — guard with canUndo() before calling undo()')\n state = command.undo(state)\n undoStack.pop()\n redoStack.push(command)\n notify()\n return command\n },\n\n redo(): SceneCommand {\n const command = redoStack[redoStack.length - 1]\n if (!command) throw new Error('nothing to redo — guard with canRedo() before calling redo()')\n state = command.execute(state)\n redoStack.pop()\n undoStack.push(command)\n notify()\n return command\n },\n\n canUndo(): boolean {\n return undoStack.length > 0\n },\n\n canRedo(): boolean {\n return redoStack.length > 0\n },\n\n subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n\n getState(): EditorSceneState {\n return state\n },\n\n setView(patch: Partial<Omit<EditorSceneState, 'document'>>): void {\n state = { ...state, ...patch }\n notify()\n },\n\n rollback(command: SceneCommand): void {\n const idx = undoStack.lastIndexOf(command)\n // Command not in history — stale or double-fire rejection handler; no-op.\n if (idx === -1) return\n\n // Apply the command's inverse against the current state. For non-overlapping\n // edits (different elements or different attributes) this is exact: the\n // inverse commutes freely past all subsequent commands. For overlapping edits\n // on the same attribute the result is defined but not semantically perfect;\n // the persistence layer should trigger an onResyncRequired refetch in that\n // case so the server document reconciles.\n state = command.undo(state)\n\n // Splice the target command out of the undo stack.\n undoStack.splice(idx, 1)\n\n // Clear the redo stack: its entries were computed relative to a history that\n // included this command, so they are now stale.\n redoStack.length = 0\n\n notify()\n },\n\n /** Rebase onto a server-refreshed document. History survives; selection\n * drops ids the refresh removed so view state never dangles. */\n reset(newDocument: SceneDocument): void {\n // Collect all live element ids across all pages for selection cleanup\n const liveElementIds = new Set<string>()\n for (const page of newDocument.pages) {\n collectElementIds(page.elements, liveElementIds)\n }\n // If the active page was removed, fall back to the first page\n const activePageExists = newDocument.pages.some((p) => p.id === state.activePageId)\n const activePageId = activePageExists ? state.activePageId : (newDocument.pages[0]?.id ?? state.activePageId)\n state = {\n ...state,\n document: newDocument,\n activePageId,\n selectedElementIds: state.selectedElementIds.filter((id) => liveElementIds.has(id)),\n }\n notify()\n },\n }\n}\n\nfunction collectElementIds(elements: ReturnType<typeof Array.prototype.slice>, ids: Set<string>): void {\n for (const el of elements as Array<{ id: string; kind: string; children?: unknown[] }>) {\n ids.add(el.id)\n if (el.kind === 'group' && Array.isArray(el.children)) {\n collectElementIds(el.children as Array<{ id: string; kind: string; children?: unknown[] }>, ids)\n }\n }\n}\n","/**\n * Concrete `SceneCommand` factories for the design-canvas editor. Every factory\n * captures the inverse from PRE-state at construction — undo is a value\n * computed once, never re-derived from current state later. Local execute/undo\n * update `EditorSceneState` immutably by routing through `applySceneOperation`\n * so optimistic state matches what the server-side dispatcher will persist.\n *\n * Drag gestures coalesce: pointer moves update volatile state, ONE command\n * executes on pointer release with (finalAttrs, priorAttrs). `setAttrsCommand`\n * is that gesture command — one undo step per drag/transform/toolbar change.\n * `multiSetAttrsCommand` collects N set_attrs ops into one undo step for\n * multi-select transforms.\n *\n * Group/ungroup route through `applySceneOperation` so the group-origin\n * rebasing in `apply.ts` is the single source of truth for both optimistic and\n * server state.\n */\n\nimport type { SceneDocument, SceneElement, ScenePage } from '../../design-canvas/model'\nimport { requireElement, requirePage } from '../../design-canvas/model'\nimport { applySceneOperation, applySceneOperations } from '../../design-canvas/apply'\nimport type {\n AddElementOperation,\n DeleteElementOperation,\n SceneAttrsPatch,\n SceneOperation,\n} from '../../design-canvas/operations'\nimport type { EditorSceneState, SceneCommand } from '../contracts'\n\n// ---------------------------------------------------------------------------\n// Shared helpers\n// ---------------------------------------------------------------------------\n\nfunction applyOps(state: EditorSceneState, ops: SceneOperation[]): EditorSceneState {\n return { ...state, document: applySceneOperations(state.document, ops) }\n}\n\nfunction applyOp(state: EditorSceneState, op: SceneOperation): EditorSceneState {\n return { ...state, document: applySceneOperation(state.document, op) }\n}\n\n// ---------------------------------------------------------------------------\n// add_element / inverse = delete_element\n// ---------------------------------------------------------------------------\n\nexport interface AddElementInput {\n pageId: string\n element: SceneElement\n /** Insertion z-index within owner; omitted → top. */\n index?: number\n /** Parent group id; omitted → page root. */\n parentGroupId?: string\n}\n\nexport function addElementCommand(input: AddElementInput): SceneCommand {\n const addOp: SceneOperation = {\n type: 'add_element',\n pageId: input.pageId,\n element: structuredClone(input.element),\n ...(input.index !== undefined ? { index: input.index } : {}),\n ...(input.parentGroupId !== undefined ? { parentGroupId: input.parentGroupId } : {}),\n }\n const deleteOp: SceneOperation = {\n type: 'delete_element',\n pageId: input.pageId,\n elementId: input.element.id,\n }\n\n return {\n label: `Add ${input.element.kind}`,\n execute: (state) => applyOp(state, addOp),\n undo: (state) => applyOp(state, deleteOp),\n operations: () => [structuredClone(addOp)],\n inverseOperations: () => [structuredClone(deleteOp)],\n }\n}\n\n// ---------------------------------------------------------------------------\n// set_attrs — THE gesture command (one undo step per drag/transform)\n// ---------------------------------------------------------------------------\n\nexport interface SetAttrsInput {\n pageId: string\n elementId: string\n /** Final attribute values after the gesture. */\n attrs: SceneAttrsPatch\n /** Attribute values BEFORE the gesture began — inverse is built from these. */\n priorAttrs: SceneAttrsPatch\n}\n\nexport function setAttrsCommand(input: SetAttrsInput): SceneCommand {\n const forwardOp: SceneOperation = {\n type: 'set_attrs',\n pageId: input.pageId,\n elementId: input.elementId,\n attrs: structuredClone(input.attrs),\n }\n const inverseOp: SceneOperation = {\n type: 'set_attrs',\n pageId: input.pageId,\n elementId: input.elementId,\n attrs: structuredClone(input.priorAttrs),\n }\n\n return {\n label: 'Edit element',\n execute: (state) => applyOp(state, forwardOp),\n undo: (state) => applyOp(state, inverseOp),\n operations: () => [structuredClone(forwardOp)],\n inverseOperations: () => [structuredClone(inverseOp)],\n }\n}\n\n// ---------------------------------------------------------------------------\n// multi-select set_attrs — N set_attrs ops, one undo step\n// ---------------------------------------------------------------------------\n\nexport interface MultiSetAttrsEntry {\n pageId: string\n elementId: string\n attrs: SceneAttrsPatch\n priorAttrs: SceneAttrsPatch\n}\n\nexport function multiSetAttrsCommand(entries: MultiSetAttrsEntry[]): SceneCommand {\n if (entries.length === 0) throw new Error('multiSetAttrsCommand: entries must not be empty')\n\n const forwardOps: SceneOperation[] = entries.map((e) => ({\n type: 'set_attrs' as const,\n pageId: e.pageId,\n elementId: e.elementId,\n attrs: structuredClone(e.attrs),\n }))\n const inverseOps: SceneOperation[] = entries.map((e) => ({\n type: 'set_attrs' as const,\n pageId: e.pageId,\n elementId: e.elementId,\n attrs: structuredClone(e.priorAttrs),\n }))\n\n return {\n label: `Edit ${entries.length} elements`,\n execute: (state) => applyOps(state, forwardOps),\n undo: (state) => applyOps(state, inverseOps),\n operations: () => structuredClone(forwardOps),\n inverseOperations: () => structuredClone(inverseOps),\n }\n}\n\n// ---------------------------------------------------------------------------\n// reorder_element\n// ---------------------------------------------------------------------------\n\nexport interface ReorderElementInput {\n pageId: string\n elementId: string\n toIndex: number\n}\n\nexport function reorderElementCommand(input: ReorderElementInput): SceneCommand {\n // Capture the element's current index at construction for the inverse\n let capturedFromIndex: number | null = null\n\n const forwardOp: SceneOperation = {\n type: 'reorder_element',\n pageId: input.pageId,\n elementId: input.elementId,\n toIndex: input.toIndex,\n }\n\n return {\n label: 'Reorder element',\n execute: (state) => {\n // Guard: on redo the element is already at toIndex, so we must not\n // overwrite the captured origin with the wrong position.\n if (capturedFromIndex === null) {\n const page = requirePage(state.document, input.pageId)\n const { index } = requireElement(page, input.elementId)\n capturedFromIndex = index\n }\n return applyOp(state, forwardOp)\n },\n undo: (state) => {\n if (capturedFromIndex === null) {\n throw new Error('reorderElementCommand: undo called before execute')\n }\n return applyOp(state, {\n type: 'reorder_element',\n pageId: input.pageId,\n elementId: input.elementId,\n toIndex: capturedFromIndex,\n })\n },\n operations: () => [structuredClone(forwardOp)],\n inverseOperations: () => {\n if (capturedFromIndex === null) {\n throw new Error('reorderElementCommand: inverseOperations called before execute')\n }\n return [{\n type: 'reorder_element' as const,\n pageId: input.pageId,\n elementId: input.elementId,\n toIndex: capturedFromIndex,\n }]\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// delete_element / inverse = add_element (full snapshot + index + parent)\n// ---------------------------------------------------------------------------\n\nexport interface DeleteElementInput {\n document: SceneDocument\n pageId: string\n elementId: string\n}\n\nexport function deleteElementCommand(input: DeleteElementInput): SceneCommand {\n const page = requirePage(input.document, input.pageId)\n const { element, owner, index } = requireElement(page, input.elementId)\n const snapshot = structuredClone(element)\n\n // Determine parentGroupId by finding which group owns this element\n const parentGroupId = findParentGroupId(page, owner)\n\n const deleteOp: DeleteElementOperation = {\n type: 'delete_element',\n pageId: input.pageId,\n elementId: input.elementId,\n }\n const addOp: AddElementOperation = {\n type: 'add_element',\n pageId: input.pageId,\n element: snapshot,\n index,\n ...(parentGroupId !== undefined ? { parentGroupId } : {}),\n }\n\n return {\n label: `Delete ${element.kind}`,\n execute: (state) => {\n const next = applyOp(state, deleteOp)\n return {\n ...next,\n selectedElementIds: next.selectedElementIds.filter((id) => id !== input.elementId),\n }\n },\n undo: (state) => applyOp(state, addOp),\n operations: () => [structuredClone(deleteOp)],\n inverseOperations: () => [structuredClone(addOp)],\n }\n}\n\nfunction findParentGroupId(page: ScenePage, owner: SceneElement[]): string | undefined {\n if (owner === page.elements) return undefined\n // Walk the element tree looking for a group whose children === owner\n return findGroupWithChildren(page.elements, owner)\n}\n\nfunction findGroupWithChildren(elements: SceneElement[], target: SceneElement[]): string | undefined {\n for (const el of elements) {\n if (el.kind === 'group') {\n if (el.children === target) return el.id\n const found = findGroupWithChildren(el.children, target)\n if (found !== undefined) return found\n }\n }\n return undefined\n}\n\n// ---------------------------------------------------------------------------\n// group_elements / inverse = ungroup_element\n// ---------------------------------------------------------------------------\n\nexport interface GroupElementsInput {\n document: SceneDocument\n pageId: string\n elementIds: string[]\n groupId: string\n name?: string\n}\n\nexport function groupElementsCommand(input: GroupElementsInput): SceneCommand {\n if (input.elementIds.length < 2) {\n throw new Error('groupElementsCommand: requires ≥ 2 elementIds')\n }\n\n const groupOp: SceneOperation = {\n type: 'group_elements',\n pageId: input.pageId,\n elementIds: input.elementIds.slice(),\n groupId: input.groupId,\n ...(input.name !== undefined ? { name: input.name } : {}),\n }\n const ungroupOp: SceneOperation = {\n type: 'ungroup_element',\n pageId: input.pageId,\n groupId: input.groupId,\n }\n\n return {\n label: `Group ${input.elementIds.length} elements`,\n execute: (state) => {\n const next = applyOp(state, groupOp)\n return {\n ...next,\n selectedElementIds: [input.groupId],\n }\n },\n undo: (state) => {\n const next = applyOp(state, ungroupOp)\n return { ...next, selectedElementIds: input.elementIds.slice() }\n },\n operations: () => [structuredClone(groupOp)],\n inverseOperations: () => [structuredClone(ungroupOp)],\n }\n}\n\n// ---------------------------------------------------------------------------\n// ungroup_element / inverse = group_elements (re-using original ids)\n// ---------------------------------------------------------------------------\n\nexport interface UngroupElementInput {\n document: SceneDocument\n pageId: string\n groupId: string\n}\n\nexport function ungroupElementCommand(input: UngroupElementInput): SceneCommand {\n const page = requirePage(input.document, input.pageId)\n const { element } = requireElement(page, input.groupId)\n if (element.kind !== 'group') {\n throw new Error(`ungroupElementCommand: element ${input.groupId} is kind ${element.kind}, not group`)\n }\n const childIds = element.children.map((c) => c.id)\n\n const ungroupOp: SceneOperation = {\n type: 'ungroup_element',\n pageId: input.pageId,\n groupId: input.groupId,\n }\n const regroupOp: SceneOperation = {\n type: 'group_elements',\n pageId: input.pageId,\n elementIds: childIds,\n groupId: input.groupId,\n name: element.name,\n }\n\n return {\n label: `Ungroup`,\n execute: (state) => {\n const next = applyOp(state, ungroupOp)\n return { ...next, selectedElementIds: childIds.slice() }\n },\n undo: (state) => {\n const next = applyOp(state, regroupOp)\n return { ...next, selectedElementIds: [input.groupId] }\n },\n operations: () => [structuredClone(ungroupOp)],\n inverseOperations: () => [structuredClone(regroupOp)],\n }\n}\n\n// ---------------------------------------------------------------------------\n// Page commands\n// ---------------------------------------------------------------------------\n\nexport interface AddPageInput {\n pageId: string\n options?: import('../../design-canvas/model').NewPageOptions\n index?: number\n}\n\nexport function addPageCommand(input: AddPageInput): SceneCommand {\n const addOp: SceneOperation = {\n type: 'add_page',\n pageId: input.pageId,\n ...(input.options !== undefined ? { options: input.options } : {}),\n ...(input.index !== undefined ? { index: input.index } : {}),\n }\n const deleteOp: SceneOperation = { type: 'delete_page', pageId: input.pageId }\n\n return {\n label: 'Add page',\n execute: (state) => {\n const next = applyOp(state, addOp)\n return { ...next, activePageId: input.pageId }\n },\n undo: (state) => {\n const next = applyOp(state, deleteOp)\n // Switch active page to first surviving page if we deleted the active one\n const activeExists = next.document.pages.some((p) => p.id === next.activePageId)\n return activeExists ? next : { ...next, activePageId: next.document.pages[0]!.id }\n },\n operations: () => [structuredClone(addOp)],\n inverseOperations: () => [structuredClone(deleteOp)],\n }\n}\n\nexport interface DuplicatePageInput {\n document: SceneDocument\n sourcePageId: string\n /** Caller-minted id for the copy. */\n pageId: string\n}\n\nexport function duplicatePageCommand(input: DuplicatePageInput): SceneCommand {\n requirePage(input.document, input.sourcePageId)\n\n const dupOp: SceneOperation = {\n type: 'duplicate_page',\n sourcePageId: input.sourcePageId,\n pageId: input.pageId,\n }\n const deleteOp: SceneOperation = { type: 'delete_page', pageId: input.pageId }\n\n return {\n label: 'Duplicate page',\n execute: (state) => {\n const next = applyOp(state, dupOp)\n return { ...next, activePageId: input.pageId }\n },\n undo: (state) => {\n const next = applyOp(state, deleteOp)\n const activeExists = next.document.pages.some((p) => p.id === next.activePageId)\n return activeExists ? next : { ...next, activePageId: input.sourcePageId }\n },\n operations: () => [structuredClone(dupOp)],\n inverseOperations: () => [structuredClone(deleteOp)],\n }\n}\n\nexport interface DeletePageInput {\n document: SceneDocument\n pageId: string\n}\n\nexport function deletePageCommand(input: DeletePageInput): SceneCommand {\n if (input.document.pages.length <= 1) {\n throw new Error('deletePageCommand: cannot delete the last page')\n }\n const page = requirePage(input.document, input.pageId)\n const currentIndex = input.document.pages.findIndex((p) => p.id === input.pageId)\n const snapshot = structuredClone(page)\n\n const deleteOp: SceneOperation = { type: 'delete_page', pageId: input.pageId }\n\n // Undo uses add_page + per-element add_element to restore the full snapshot.\n // The page shell is added first, then elements are inserted in z-order so\n // subsequent undo/redo round-trips see the correct stack. This also makes\n // inverseOperations() safe to emit server-side: the server will see the same\n // full restore instead of a bare shell.\n function buildRestoreOps(): SceneOperation[] {\n const ops: SceneOperation[] = [\n {\n type: 'add_page',\n pageId: snapshot.id,\n options: {\n name: snapshot.name,\n width: snapshot.width,\n height: snapshot.height,\n background: snapshot.background,\n },\n index: currentIndex,\n },\n ]\n if (snapshot.bleed) {\n ops.push({ type: 'set_page_props', pageId: snapshot.id, bleed: snapshot.bleed })\n }\n if (snapshot.guides.vertical.length > 0 || snapshot.guides.horizontal.length > 0) {\n ops.push({ type: 'set_page_guides', pageId: snapshot.id, guides: snapshot.guides })\n }\n for (let i = 0; i < snapshot.elements.length; i++) {\n ops.push({\n type: 'add_element',\n pageId: snapshot.id,\n element: structuredClone(snapshot.elements[i]!),\n index: i,\n })\n }\n return ops\n }\n\n return {\n label: 'Delete page',\n execute: (state) => {\n const next = applyOp(state, deleteOp)\n const activeExists = next.document.pages.some((p) => p.id === next.activePageId)\n if (activeExists) return next\n const fallbackIndex = Math.min(currentIndex, next.document.pages.length - 1)\n return { ...next, activePageId: next.document.pages[fallbackIndex]!.id }\n },\n undo: (state) => {\n const next = applyOps(state, buildRestoreOps())\n return { ...next, activePageId: input.pageId }\n },\n operations: () => [structuredClone(deleteOp)],\n inverseOperations: () => buildRestoreOps(),\n }\n}\n\nexport interface ReorderPageInput {\n pageId: string\n toIndex: number\n}\n\nexport function reorderPageCommand(input: ReorderPageInput): SceneCommand {\n let capturedFromIndex: number | null = null\n\n const forwardOp: SceneOperation = {\n type: 'reorder_page',\n pageId: input.pageId,\n toIndex: input.toIndex,\n }\n\n return {\n label: 'Reorder page',\n execute: (state) => {\n // Guard: on redo the page is already at toIndex; capture only on first execute.\n if (capturedFromIndex === null) {\n capturedFromIndex = state.document.pages.findIndex((p) => p.id === input.pageId)\n if (capturedFromIndex === -1) throw new Error(`reorderPageCommand: page ${input.pageId} not found`)\n }\n return applyOp(state, forwardOp)\n },\n undo: (state) => {\n if (capturedFromIndex === null) throw new Error('reorderPageCommand: undo called before execute')\n return applyOp(state, {\n type: 'reorder_page',\n pageId: input.pageId,\n toIndex: capturedFromIndex,\n })\n },\n operations: () => [structuredClone(forwardOp)],\n inverseOperations: () => {\n if (capturedFromIndex === null) throw new Error('reorderPageCommand: inverseOperations called before execute')\n return [{ type: 'reorder_page' as const, pageId: input.pageId, toIndex: capturedFromIndex }]\n },\n }\n}\n\nexport interface SetPagePropsInput {\n document: SceneDocument\n pageId: string\n props: {\n name?: string\n width?: number\n height?: number\n background?: string\n bleed?: import('../../design-canvas/model').PageBleed | null\n }\n}\n\nexport function setPagePropsCommand(input: SetPagePropsInput): SceneCommand {\n const page = requirePage(input.document, input.pageId)\n const prior: NonNullable<import('../../design-canvas/operations').SetPagePropsOperation> = {\n type: 'set_page_props',\n pageId: input.pageId,\n ...(input.props.name !== undefined ? { name: page.name } : {}),\n ...(input.props.width !== undefined ? { width: page.width } : {}),\n ...(input.props.height !== undefined ? { height: page.height } : {}),\n ...(input.props.background !== undefined ? { background: page.background } : {}),\n ...(input.props.bleed !== undefined ? { bleed: page.bleed } : {}),\n }\n\n const forwardOp: SceneOperation = {\n type: 'set_page_props',\n pageId: input.pageId,\n ...input.props,\n }\n\n return {\n label: 'Edit page',\n execute: (state) => applyOp(state, forwardOp),\n undo: (state) => applyOp(state, prior),\n operations: () => [structuredClone(forwardOp)],\n inverseOperations: () => [structuredClone(prior) as SceneOperation],\n }\n}\n\nexport interface SetPageGuidesInput {\n document: SceneDocument\n pageId: string\n guides: import('../../design-canvas/model').PageGuides\n}\n\nexport function setPageGuidesCommand(input: SetPageGuidesInput): SceneCommand {\n const page = requirePage(input.document, input.pageId)\n const priorGuides = structuredClone(page.guides)\n\n const forwardOp: SceneOperation = {\n type: 'set_page_guides',\n pageId: input.pageId,\n guides: structuredClone(input.guides),\n }\n const inverseOp: SceneOperation = {\n type: 'set_page_guides',\n pageId: input.pageId,\n guides: priorGuides,\n }\n\n return {\n label: 'Edit guides',\n execute: (state) => applyOp(state, forwardOp),\n undo: (state) => applyOp(state, inverseOp),\n operations: () => [structuredClone(forwardOp)],\n inverseOperations: () => [structuredClone(inverseOp)],\n }\n}\n\n// ---------------------------------------------------------------------------\n// bind_slot\n// ---------------------------------------------------------------------------\n\nexport interface BindSlotInput {\n document: SceneDocument\n pageId: string\n elementId: string\n slot: string | null\n}\n\nexport function bindSlotCommand(input: BindSlotInput): SceneCommand {\n const page = requirePage(input.document, input.pageId)\n const { element } = requireElement(page, input.elementId)\n const priorSlot = element.slot ?? null\n\n const forwardOp: SceneOperation = {\n type: 'bind_slot',\n pageId: input.pageId,\n elementId: input.elementId,\n slot: input.slot,\n }\n const inverseOp: SceneOperation = {\n type: 'bind_slot',\n pageId: input.pageId,\n elementId: input.elementId,\n slot: priorSlot,\n }\n\n return {\n label: input.slot === null ? 'Unbind slot' : `Bind slot \"${input.slot}\"`,\n execute: (state) => applyOp(state, forwardOp),\n undo: (state) => applyOp(state, inverseOp),\n operations: () => [structuredClone(forwardOp)],\n inverseOperations: () => [structuredClone(inverseOp)],\n }\n}\n\n// ---------------------------------------------------------------------------\n// set_document_title\n// ---------------------------------------------------------------------------\n\nexport interface SetDocumentTitleInput {\n document: SceneDocument\n title: string\n}\n\nexport function setDocumentTitleCommand(input: SetDocumentTitleInput): SceneCommand {\n const priorTitle = input.document.title\n\n const forwardOp: SceneOperation = { type: 'set_document_title', title: input.title }\n const inverseOp: SceneOperation = { type: 'set_document_title', title: priorTitle }\n\n return {\n label: `Rename document`,\n execute: (state) => applyOp(state, forwardOp),\n undo: (state) => applyOp(state, inverseOp),\n operations: () => [structuredClone(forwardOp)],\n inverseOperations: () => [structuredClone(inverseOp)],\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\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\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\nexport interface 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'\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}\n\nconst THUMBNAIL_W = 80\nconst THUMBNAIL_H = 56\n\nconst BTN =\n 'flex h-6 w-6 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\nexport function PagesStrip({\n pages,\n activePageId,\n canWrite,\n renderThumbnail,\n onSelectPage,\n onAddPage,\n onDuplicatePage,\n onDeletePage,\n onReorderPage,\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\n className=\"flex h-[84px] shrink-0 items-center gap-2 overflow-x-auto border-t border-[var(--border-default)] bg-[var(--bg-input)] px-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 role=\"button\"\n tabIndex={0}\n aria-label={`Page ${index + 1}: ${page.name}${isActive ? ' (active)' : ''}`}\n aria-pressed={isActive}\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 onClick={() => onSelectPage(page.id)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n onSelectPage(page.id)\n }\n }}\n className={[\n 'group relative flex shrink-0 cursor-pointer flex-col items-center gap-1 rounded p-1 transition',\n isActive\n ? 'ring-2 ring-[var(--brand-primary)]'\n : 'hover:bg-[var(--border-default)]/40',\n dragOverIndex === index ? 'ring-1 ring-[var(--brand-primary)]/60' : '',\n ].join(' ')}\n >\n {/* Thumbnail or placeholder */}\n <div\n className=\"overflow-hidden rounded border border-[var(--border-default)] bg-white\"\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-cover\"\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\n {/* Per-page action buttons — visible on hover or when active */}\n {canWrite ? (\n <div className=\"pointer-events-none absolute -top-1 right-0 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}\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}\n >\n <TrashGlyph className=\"h-3 w-3 text-rose-400\" />\n </button>\n </div>\n ) : null}\n </div>\n )\n })}\n\n {/* Add page button */}\n {canWrite ? (\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 )\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\" />)\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\" />)\nexport const ChevronRightGlyph = glyph(<path d=\"m9 18 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 FitGlyph = glyph(\n <>\n <path d=\"M3 3h4v4H3zM17 3h4v4h-4zM3 17h4v4H3zM17 17h4v4h-4z\" />\n <path d=\"M7 5h10M5 7v10M19 7v10M7 19h10\" />\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","/**\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 *\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 )\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 {/* Pointer indicator */}\n {pointerX !== null ? (\n <div className=\"pointer-events-none absolute top-0 bottom-0 w-px bg-[var(--brand-primary)]/60\" style={{ left: pointerX }} />\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 {pointerY !== null ? (\n <div className=\"pointer-events-none absolute left-0 right-0 h-px bg-[var(--brand-primary)]/60\" style={{ top: pointerY }} />\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 * 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\nimport { useRef, useState } 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 { matchPreset, SIZE_PRESETS } from '../../design-canvas/export-presets'\nimport {\n AlignCenterGlyph,\n AlignLeftGlyph,\n AlignRightGlyph,\n BleedGlyph,\n BoldGlyph,\n BringFrontGlyph,\n DuplicateGlyph,\n FitGlyph,\n GridGlyph,\n GroupGlyph,\n ItalicGlyph,\n LockGlyph,\n MagnetGlyph,\n RedoGlyph,\n RulerGlyph,\n SendBackGlyph,\n SlotGlyph,\n TrashGlyph,\n UndoGlyph,\n UngroupGlyph,\n UnlockGlyph,\n} from './glyphs'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ToolbarProps {\n page: ScenePage\n selectedElements: SceneElement[]\n canWrite: boolean\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}\n\n// ---------------------------------------------------------------------------\n// Small helpers\n// ---------------------------------------------------------------------------\n\nconst BTN =\n 'flex h-7 w-7 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 = `${BTN} border-[var(--brand-primary)] text-[var(--brand-primary)] hover:text-[var(--brand-primary)]`\n\nconst SEP = <div className=\"mx-1 h-5 w-px bg-[var(--border-default)]\" />\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=\"text-[9px] uppercase tracking-wide text-[var(--text-muted)]\">{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)] outline-none focus:border-[var(--brand-primary)]`}\n />\n </label>\n )\n}\n\nfunction ColorSwatch({ label, value, onCommit }: { label: string; value: string; onCommit(v: string): void }) {\n return (\n <label className=\"flex flex-col items-center gap-0.5 cursor-pointer\">\n <span className=\"text-[9px] uppercase tracking-wide text-[var(--text-muted)]\">{label}</span>\n <input\n type=\"color\"\n value={value.startsWith('#') ? value : '#ffffff'}\n onChange={(event) => onCommit(event.target.value)}\n className=\"h-6 w-10 cursor-pointer rounded border border-[var(--border-default)] p-0.5\"\n />\n </label>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Main toolbar\n// ---------------------------------------------------------------------------\n\nexport function Toolbar({\n page,\n selectedElements,\n canWrite,\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}: 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\n // ----\n\n return (\n <div className=\"flex h-11 shrink-0 items-center gap-2 overflow-x-auto border-b border-[var(--border-default)] bg-[var(--bg-input)] px-3\">\n {/* Undo / Redo */}\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\n {SEP}\n\n {/* View toggles */}\n <button type=\"button\" aria-label=\"Toggle rulers\" aria-pressed={showRulers} onClick={onToggleRulers} className={showRulers ? BTN_ACTIVE : BTN}>\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}>\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}>\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}>\n <BleedGlyph className=\"h-3.5 w-3.5\" />\n </button>\n\n {SEP}\n\n {hasSelection ? (\n <SelectionControls\n elements={selectedElements}\n single={single}\n isGroup={isGroup}\n groupable={groupable}\n allSameKind={allSameKind}\n firstKind={firstKind}\n canWrite={canWrite}\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 <PagePropsControls\n page={page}\n canWrite={canWrite}\n onSetPageProps={onSetPageProps}\n onSetPageGuides={onSetPageGuides}\n />\n )}\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 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 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 {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 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 <div className=\"relative\">\n <button\n type=\"button\"\n aria-label={currentSlot ? `Slot: ${currentSlot}` : 'Bind slot'}\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 {slotPopoverOpen ? (\n <div className=\"absolute top-full left-0 z-50 mt-1 flex w-48 flex-col gap-2 rounded border border-[var(--border-default)] bg-[var(--bg-input)] p-2 shadow-lg\">\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)] outline-none 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-[var(--brand-primary)]/10\"\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 ) : null}\n </div>\n ) : null}\n\n {SEP}\n\n {/* Delete */}\n <button type=\"button\" aria-label=\"Delete selection\" disabled={!canWrite} onClick={onDelete} className={BTN}>\n <TrashGlyph className=\"h-3.5 w-3.5 text-rose-400\" />\n </button>\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 <input\n type=\"text\"\n aria-label=\"Font family\"\n value={element.fontFamily}\n disabled={!canWrite}\n onChange={(event) => onPatch({ fontFamily: 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)] outline-none focus:border-[var(--brand-primary)]\"\n placeholder=\"Font\"\n />\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 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 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 {(['left', 'center', 'right'] as const).map((align) => (\n <button\n key={align}\n type=\"button\"\n aria-label={`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 <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 })} />\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 })} />\n <ColorSwatch label=\"Stroke\" value={element.stroke ?? '#000000'} onCommit={(v) => onPatch({ stroke: v })} />\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 return (\n <label className=\"flex flex-col items-center gap-0.5\">\n <span className=\"text-[9px] uppercase tracking-wide text-[var(--text-muted)]\">Fit</span>\n <select\n value={element.fit}\n disabled={!canWrite}\n onChange={(event) => onPatch({ fit: event.target.value as ImageElement['fit'] })}\n className=\"rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-1 py-0.5 text-[12px] text-[var(--text-primary)] outline-none focus:border-[var(--brand-primary)]\"\n >\n <option value=\"fill\">Fill</option>\n <option value=\"cover\">Cover</option>\n <option value=\"contain\">Contain</option>\n </select>\n </label>\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}\n\nfunction PagePropsControls({ page, canWrite, onSetPageProps, onSetPageGuides }: 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 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)] outline-none focus:border-[var(--brand-primary)]\"\n />\n\n {SEP}\n\n {/* Size preset */}\n <label className=\"flex flex-col items-center gap-0.5\">\n <span className=\"text-[9px] uppercase tracking-wide text-[var(--text-muted)]\">Preset</span>\n <select\n value={matchedPreset?.id ?? 'custom'}\n disabled={!canWrite}\n onChange={(event) => {\n const preset = SIZE_PRESETS.find((p) => p.id === event.target.value)\n if (preset) onSetPageProps({ width: preset.width, height: preset.height })\n }}\n className=\"rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-1 py-0.5 text-[12px] text-[var(--text-primary)] outline-none focus:border-[var(--brand-primary)]\"\n >\n <option value=\"custom\">Custom</option>\n {SIZE_PRESETS.map((p) => (\n <option key={p.id} value={p.id}>{p.label}</option>\n ))}\n </select>\n </label>\n\n {/* Custom W × H */}\n <label className=\"flex flex-col items-center gap-0.5\">\n <span className=\"text-[9px] uppercase tracking-wide text-[var(--text-muted)]\">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)] outline-none 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=\"text-[9px] uppercase tracking-wide text-[var(--text-muted)]\">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)] outline-none focus:border-[var(--brand-primary)]\"\n />\n </label>\n\n {SEP}\n\n <ColorSwatch label=\"BG\" value={page.background} onCommit={(v) => onSetPageProps({ background: v })} />\n\n {SEP}\n\n {/* Bleed controls */}\n <BleedControls page={page} canWrite={canWrite} onSetPageProps={onSetPageProps} />\n </>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Bleed sub-controls\n// ---------------------------------------------------------------------------\n\nfunction BleedControls({ page, canWrite, onSetPageProps }: { page: ScenePage; canWrite: boolean; onSetPageProps: PagePropsControlsProps['onSetPageProps'] }) {\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 disabled={!canWrite}\n onClick={() => onSetPageProps({ bleed: { top: 3, right: 3, bottom: 3, left: 3 } })}\n className={BTN}\n title=\"Enable bleed\"\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={`Bleed ${side[0]!.toUpperCase()}`}\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 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'\n\nexport interface ZoomControlsProps {\n zoom: number\n onZoom(zoom: number): void\n onFit(): void\n}\n\nconst STEP = 0.1\nconst MIN = 0.05\nconst MAX = 32\n\nconst BTN =\n 'flex h-6 w-6 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\nexport function ZoomControls({ zoom, onZoom, onFit }: 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=\"Fit page to viewport\"\n onClick={onFit}\n className={BTN}\n title=\"Fit page (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}\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}\n >\n <span className=\"text-sm leading-none\">+</span>\n </button>\n </div>\n )\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","activePageId","Fragment","jsx","jsxs","jsx","jsxs","useRef","useState","Fragment","jsx","jsxs","useRef","useState","useState","Fragment","jsx","jsxs","BTN","useState","jsx","jsxs","BTN","jsx","jsxs","useCallback","useRef","useEffect","useState"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/runtime/model-catalog.ts","../src/runtime/openai-stream.ts","../src/runtime/agent.ts","../src/runtime/surface-profile.ts","../src/runtime/index.ts"],"sourcesContent":["/**\n * Model catalogue — computed live from the Tangle Router, never hand-curated.\n * Lifted from tuner-agent so every agent app's model picker shares one\n * filter/dedupe/rank/feature pipeline instead of re-deriving it.\n *\n * The router's /models endpoint returns every routeable model (~200), which is\n * unusable as a picker list: it mixes chat models with TTS/embedding/realtime\n * endpoints, dated snapshots alias their parents, and provider-prefixed ids\n * duplicate canonical ones. This module turns that into a product catalogue:\n *\n * filter (chat-capable, routeable) → dedupe (snapshot/prefix/:free aliases)\n * → rank (provider tier, family, version) → feature (best model per family)\n * → default (env override or first featured)\n *\n * Freshness is automatic: everything is derived from the live router response,\n * so new models surface as soon as the router lists them. The only static\n * knowledge here is slow-moving: provider display order and family name\n * patterns (e.g. \"claude-sonnet-*\", \"gpt-N\"). A new Sonnet or GPT release\n * outranks its predecessor by version comparison with zero code change; only\n * a brand-new *family name* (rare) needs a one-line rule addition.\n */\n\nexport interface RouterModel {\n id: string\n name?: string\n description?: string\n _provider?: string\n pricing?: { prompt?: string | null; completion?: string | null }\n context_length?: number\n architecture?: {\n modality?: string\n input_modalities?: string[]\n output_modalities?: string[]\n }\n supported_parameters?: string[]\n routeability?: {\n status?: string\n routeable?: boolean\n provider?: string\n }\n}\n\nexport interface CatalogModel {\n id: string\n name: string\n provider: string\n description?: string\n contextLength?: number\n pricing?: { prompt?: string; completion?: string }\n supportsTools: boolean\n supportsReasoning: boolean\n featured: boolean\n}\n\nexport interface ModelCatalog {\n defaultModelId: string | null\n fetchedAt: string\n models: CatalogModel[]\n}\n\n/** Display order. Unlisted providers sort after these, alphabetically. */\nconst PROVIDER_TIER: string[] = [\n 'anthropic',\n 'openai',\n 'google',\n 'xai',\n 'deepseek',\n 'moonshotai',\n 'moonshot',\n 'zai',\n 'z-ai',\n 'mistral',\n 'groq',\n 'nvidia',\n 'cohere',\n 'cerebras',\n]\n\n/** Non-chat endpoints that pollute the router list (matched on normalized id). */\nconst EXCLUDED_ID = /(embedding|tts|transcribe|whisper|audio|realtime|image|lyria|sora|dall-e|moderation|content-safety|search-preview|search-api|deep-research)/\n\n/**\n * Featured families, in display order. Each rule surfaces the highest-version\n * routeable model whose normalized id matches. Patterns anchor on the family\n * name and stop before specialty suffixes (codex, nano, lite, …) so the\n * mainline model wins.\n */\nconst FEATURED_RULES: Array<{ providers: string[]; match: RegExp }> = [\n { providers: ['anthropic'], match: /^claude-sonnet-[\\d-]+$/ },\n { providers: ['anthropic'], match: /^claude-opus-[\\d-]+$/ },\n { providers: ['anthropic'], match: /^claude-haiku-[\\d-]+$/ },\n { providers: ['openai'], match: /^gpt-\\d+(\\.\\d+)?$/ },\n { providers: ['openai'], match: /^gpt-\\d+(\\.\\d+)?-mini$/ },\n { providers: ['google'], match: /^gemini-[\\d.]+-pro(-preview)?$/ },\n { providers: ['google'], match: /^gemini-[\\d.]+-flash(-preview)?$/ },\n { providers: ['xai'], match: /^grok-[\\d.]+$/ },\n { providers: ['deepseek'], match: /^deepseek-(chat|v[\\d.]+(-\\w+)?)$/ },\n { providers: ['moonshotai', 'moonshot'], match: /^kimi-k[\\d.]+$/ },\n { providers: ['zai', 'z-ai'], match: /^glm-[\\d.]+$/ },\n { providers: ['mistral'], match: /^mistral-(large|medium)-?[\\d.-]*$/ },\n]\n\n/** Families known to support tool calls even when router metadata omits it\n * (dated snapshots often lack the supported_parameters of their parent). */\nconst TOOL_CAPABLE_FAMILY = /^(claude|gpt-[45]|gpt-oss|o[134]|gemini|grok|deepseek|glm|kimi|mistral|ministral|magistral|command|nemotron|llama)/\n\n/** Strip provider prefix, :free suffix, and trailing date stamps. */\nexport function normalizeModelId(id: string): string {\n let tail = id.split('/').pop() ?? id\n tail = tail.replace(/:free$/, '')\n tail = tail.replace(/-\\d{8}$/, '')\n tail = tail.replace(/-\\d{4}-\\d{2}-\\d{2}$/, '')\n return tail\n}\n\n/** All numeric groups in a normalized id, for version comparison. */\nfunction versionOf(normId: string): number[] {\n return (normId.match(/\\d+/g) ?? []).map(Number)\n}\n\nfunction compareVersions(a: number[], b: number[]): number {\n const len = Math.max(a.length, b.length)\n for (let i = 0; i < len; i++) {\n const d = (a[i] ?? -1) - (b[i] ?? -1)\n if (d !== 0) return d\n }\n return 0\n}\n\n/** Lower = preferred representative for an alias group. */\nfunction aliasPenalty(id: string): number {\n let p = 0\n if (id.includes('/')) p += 4\n if (/-\\d{8}$|-\\d{4}-\\d{2}-\\d{2}$/.test(id.replace(/:free$/, ''))) p += 2\n if (id.endsWith(':free')) p += 1\n return p\n}\n\nfunction providerRank(provider: string): number {\n const i = PROVIDER_TIER.indexOf(provider)\n return i === -1 ? PROVIDER_TIER.length : i\n}\n\nfunction isChatModel(m: RouterModel): boolean {\n const arch = m.architecture\n if (!arch?.input_modalities || !arch?.output_modalities) return true\n return arch.input_modalities.includes('text') && arch.output_modalities.includes('text')\n}\n\nfunction isRouteable(m: RouterModel): boolean {\n return m.routeability?.routeable !== false && m.routeability?.status !== 'unavailable'\n}\n\nfunction familyOf(normId: string): string {\n return normId.replace(/[\\d.]+/g, '').replace(/-+/g, '-').replace(/-$/, '')\n}\n\n/**\n * Pure catalogue pipeline. `preferredDefault` (typically the MODEL_NAME env\n * var) wins when it survives filtering; otherwise the first featured model.\n */\nexport function buildCatalog(raw: RouterModel[], opts?: { preferredDefault?: string }): ModelCatalog {\n // Filter to chat-capable, routeable, non-specialty models\n const candidates = raw.filter(\n (m) => m.id && isRouteable(m) && isChatModel(m) && !EXCLUDED_ID.test(normalizeModelId(m.id)),\n )\n\n // Dedupe alias groups (dated snapshots, provider prefixes, :free variants).\n // Within a group, merge metadata so the representative keeps the richest\n // supported_parameters claim (snapshots often omit what the parent lists).\n const groups = new Map<string, RouterModel[]>()\n for (const m of candidates) {\n const key = `${m._provider ?? ''}::${normalizeModelId(m.id)}`\n const g = groups.get(key)\n if (g) g.push(m)\n else groups.set(key, [m])\n }\n\n const reps: Array<{ model: RouterModel; normId: string; mergedParams: Set<string> }> = []\n for (const group of groups.values()) {\n group.sort((a, b) => aliasPenalty(a.id) - aliasPenalty(b.id) || a.id.length - b.id.length)\n const rep = group[0]!\n const mergedParams = new Set<string>(group.flatMap((m) => m.supported_parameters ?? []))\n reps.push({ model: rep, normId: normalizeModelId(rep.id), mergedParams })\n }\n\n // Featured: best version per family rule, in rule order\n const featuredIds: string[] = []\n for (const rule of FEATURED_RULES) {\n const matches = reps.filter(\n (r) =>\n rule.providers.includes(r.model._provider ?? '') &&\n rule.match.test(r.normId) &&\n !featuredIds.includes(r.model.id),\n )\n if (!matches.length) continue\n matches.sort(\n (a, b) =>\n compareVersions(versionOf(b.normId), versionOf(a.normId)) ||\n Number(a.normId.includes('preview')) - Number(b.normId.includes('preview')) ||\n a.model.id.length - b.model.id.length,\n )\n featuredIds.push(matches[0]!.model.id)\n }\n\n const toCatalogModel = (r: (typeof reps)[number]): CatalogModel => {\n const m = r.model\n const provider = m._provider ?? 'unknown'\n return {\n id: m.id,\n name: m.name ?? m.id,\n provider,\n description: m.description ? m.description.slice(0, 160) : undefined,\n contextLength: m.context_length,\n pricing:\n m.pricing?.prompt || m.pricing?.completion\n ? { prompt: m.pricing.prompt ?? undefined, completion: m.pricing.completion ?? undefined }\n : undefined,\n supportsTools: r.mergedParams.has('tools') || TOOL_CAPABLE_FAMILY.test(r.normId),\n supportsReasoning: r.mergedParams.has('reasoning') || r.mergedParams.has('include_reasoning'),\n featured: featuredIds.includes(m.id),\n }\n }\n\n // Sort: featured first (rule order), then provider tier → family → version desc\n const featured = featuredIds\n .map((id) => reps.find((r) => r.model.id === id)!)\n .map(toCatalogModel)\n const rest = reps\n .filter((r) => !featuredIds.includes(r.model.id))\n .sort((a, b) => {\n const pa = providerRank(a.model._provider ?? '')\n const pb = providerRank(b.model._provider ?? '')\n if (pa !== pb) return pa - pb\n const fa = familyOf(a.normId)\n const fb = familyOf(b.normId)\n if (fa !== fb) return fa.localeCompare(fb)\n return compareVersions(versionOf(b.normId), versionOf(a.normId)) || a.model.id.localeCompare(b.model.id)\n })\n .map(toCatalogModel)\n\n const models = [...featured, ...rest]\n\n const preferred = opts?.preferredDefault\n const defaultModelId =\n (preferred && models.find((m) => m.id === preferred || normalizeModelId(m.id) === normalizeModelId(preferred))?.id) ||\n featured.find((m) => m.supportsTools)?.id ||\n models[0]?.id ||\n null\n\n return { defaultModelId, fetchedAt: new Date().toISOString(), models }\n}\n\n// ── Cached fetch ─────────────────────────────────────────────────────────\n\nconst CATALOG_TTL_MS = 5 * 60 * 1000\n\nlet _cache: { catalog: ModelCatalog; at: number } | null = null\n\n/**\n * Fetch the router model list and build the catalogue, with an in-isolate\n * cache (TTL 5 min). On router failure a stale catalogue is served rather\n * than erroring the picker.\n */\nexport async function fetchModelCatalog(cfg: {\n baseUrl: string\n apiKey: string\n preferredDefault?: string\n}): Promise<ModelCatalog> {\n if (_cache && Date.now() - _cache.at < CATALOG_TTL_MS) {\n return _cache.catalog\n }\n try {\n const res = await fetch(`${cfg.baseUrl}/models`, {\n headers: { Authorization: `Bearer ${cfg.apiKey}` },\n })\n if (!res.ok) throw new Error(`Router /models returned ${res.status}`)\n const data = (await res.json()) as { data?: RouterModel[] }\n const catalog = buildCatalog(data.data ?? [], { preferredDefault: cfg.preferredDefault })\n _cache = { catalog, at: Date.now() }\n return catalog\n } catch (err) {\n if (_cache) return _cache.catalog\n throw err\n }\n}\n\n/** Test-only: clear the catalogue cache. */\nexport function __resetCatalogCache(): void {\n _cache = null\n}\n","/**\n * OpenAI-compatible stream → `LoopEvent` adapter, for NON-sandbox copilots.\n *\n * `streamAppToolLoop` takes a `streamTurn` seam that yields `LoopEvent`s. A\n * sandboxed agent produces those from its container; a browser/edge copilot\n * instead calls a model directly. The Tangle Router, the tcloud SDK, and most\n * providers all speak the OpenAI Chat Completions streaming shape — so the ONE\n * reusable piece is assembling that stream (content deltas + FRAGMENTED\n * tool-call deltas) into `LoopEvent`s. That assembly is the boilerplate every\n * copilot would re-write (and get wrong — OpenAI streams tool-call arguments in\n * pieces across chunks).\n *\n * This does NOT implement an HTTP client beyond a minimal `fetch` + SSE reader\n * (browser/edge/Node-safe, zero deps). For richer transport use the tcloud SDK\n * or the Vercel AI SDK and pipe their stream through {@link toLoopEvents}.\n */\nimport type { LoopEvent, LoopMessage, LoopToolCall } from './index'\n\n/** Minimal OpenAI Chat Completions streaming chunk (structural — no `openai` dep). */\nexport interface OpenAIStreamChunk {\n choices?: Array<{\n delta?: {\n content?: string | null\n /** Reasoning deltas — DeepSeek/router use `reasoning_content`; some proxies use `thinking`. */\n reasoning_content?: string | null\n thinking?: string | null\n tool_calls?: Array<{\n index: number\n id?: string\n function?: { name?: string; arguments?: string }\n }>\n }\n finish_reason?: string | null\n }>\n /** Final-chunk token accounting (requires `stream_options.include_usage`). */\n usage?: {\n prompt_tokens?: number\n completion_tokens?: number\n } | null\n}\n\ninterface PartialToolCall {\n id?: string\n name: string\n args: string\n}\n\n/**\n * Map an OpenAI-compat streaming chunk iterator to `LoopEvent`s: each content\n * delta → a `text` event; tool-call deltas are accumulated by index across\n * chunks and emitted as one complete `tool_call` event when the stream finishes\n * (arguments JSON-parsed; an empty/garbled args string yields `{}` rather than\n * throwing). Works for the Tangle Router, tcloud, or any OpenAI-compat source.\n */\nexport async function* toLoopEvents(chunks: AsyncIterable<OpenAIStreamChunk>): AsyncIterable<LoopEvent> {\n const calls = new Map<number, PartialToolCall>()\n for await (const chunk of chunks) {\n // Usage rides the final chunk, which has an empty choices array — handle\n // it before the choice guard.\n if (chunk.usage?.prompt_tokens != null || chunk.usage?.completion_tokens != null) {\n yield {\n type: 'usage',\n usage: {\n promptTokens: chunk.usage.prompt_tokens ?? 0,\n completionTokens: chunk.usage.completion_tokens ?? 0,\n },\n }\n }\n const choice = chunk.choices?.[0]\n if (!choice) continue\n const content = choice.delta?.content\n if (content) yield { type: 'text', text: content }\n const reasoning = choice.delta?.reasoning_content ?? choice.delta?.thinking\n if (reasoning) yield { type: 'reasoning', text: reasoning }\n for (const tc of choice.delta?.tool_calls ?? []) {\n const cur = calls.get(tc.index) ?? { name: '', args: '' }\n if (tc.id) cur.id = tc.id\n if (tc.function?.name) cur.name += tc.function.name\n if (tc.function?.arguments) cur.args += tc.function.arguments\n calls.set(tc.index, cur)\n }\n }\n for (const [, c] of [...calls.entries()].sort((a, b) => a[0] - b[0])) {\n if (!c.name) continue\n yield { type: 'tool_call', call: { toolCallId: c.id, toolName: c.name, args: safeParse(c.args) } satisfies LoopToolCall }\n }\n}\n\nfunction safeParse(s: string): Record<string, unknown> {\n if (!s.trim()) return {}\n try {\n const v = JSON.parse(s)\n return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : {}\n } catch {\n return {}\n }\n}\n\nexport interface OpenAICompatStreamTurnOptions {\n /** OpenAI-compat base URL (e.g. the Tangle Router `https://router.tangle.tools/v1`). */\n baseUrl: string\n apiKey: string\n model: string\n /** OpenAI tool definitions — pass `buildAppToolOpenAITools(taxonomy)` so the\n * model can call the app tools. Omit for a tool-free copilot. */\n tools?: unknown[]\n temperature?: number\n fetchImpl?: typeof fetch\n /** Extra body fields (e.g. `max_tokens`). */\n extraBody?: Record<string, unknown>\n}\n\n/**\n * Build a `streamTurn` that calls an OpenAI-compatible `/chat/completions`\n * endpoint (Tangle Router / tcloud / any compat provider) with `stream: true`\n * and yields `LoopEvent`s via {@link toLoopEvents}. Browser/edge/Node-safe —\n * just `fetch` + an SSE reader. Drop straight into `streamAppToolLoop`:\n *\n * const cfg = resolveTangleModelConfig() // or { baseUrl, apiKey, model }\n * streamAppToolLoop({ streamTurn: createOpenAICompatStreamTurn({ ...cfg, tools }), executeToolCall, ... })\n */\nexport function createOpenAICompatStreamTurn(\n opts: OpenAICompatStreamTurnOptions,\n): (messages: LoopMessage[]) => AsyncIterable<LoopEvent> {\n const base = opts.baseUrl.replace(/\\/+$/, '')\n const doFetch = opts.fetchImpl ?? fetch\n return (messages) =>\n toLoopEvents(\n streamChatCompletions(doFetch, `${base}/chat/completions`, opts.apiKey, {\n model: opts.model,\n messages,\n stream: true,\n stream_options: { include_usage: true },\n ...(opts.tools && opts.tools.length > 0 ? { tools: opts.tools } : {}),\n ...(opts.temperature != null ? { temperature: opts.temperature } : {}),\n ...opts.extraBody,\n }),\n )\n}\n\n/** Stream + parse an OpenAI-compat SSE response into chunks. Tolerates `data:`\n * framing, multi-line buffers, and the terminal `[DONE]`. */\nasync function* streamChatCompletions(\n doFetch: typeof fetch,\n url: string,\n apiKey: string,\n body: Record<string, unknown>,\n): AsyncIterable<OpenAIStreamChunk> {\n const res = await doFetch(url, {\n method: 'POST',\n headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', Accept: 'text/event-stream' },\n body: JSON.stringify(body),\n })\n if (!res.ok || !res.body) {\n const text = res.body ? await res.text().catch(() => '') : ''\n throw new Error(`OpenAI-compat stream failed (HTTP ${res.status})${text ? `: ${text.slice(0, 200)}` : ''}`)\n }\n const reader = res.body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n for (const line of lines) {\n const trimmed = line.trim()\n if (!trimmed.startsWith('data:')) continue\n const data = trimmed.slice(5).trim()\n if (data === '[DONE]') return\n try {\n yield JSON.parse(data) as OpenAIStreamChunk\n } catch {\n /* skip a partial/garbled SSE frame */\n }\n }\n }\n}\n","/**\n * `createAgentRuntime` — the in-process agent core, assembled.\n *\n * The bricks to run an agent turn WITHOUT a sandbox already exist in this\n * package, but a consumer must hand-wire five of them every time: resolve the\n * model config, build the OpenAI tool schemas from the taxonomy, build a\n * `streamTurn` over the model endpoint, build an `executeToolCall` over the\n * product's handlers, and drive `runAppToolLoop` / `streamAppToolLoop` with an\n * `isExecutableTool` predicate. That boilerplate is identical across every\n * sandbox-free surface (an edge/browser copilot, an eval harness, a Node CLI),\n * and getting it subtly wrong — e.g. NOT advertising the tools, so the model\n * never emits a `tool_call` and no side effect ever fires — is exactly the\n * failure that makes a tool-driven agent score zero off-sandbox.\n *\n * This factory bundles those five into one object configured for ONE agent:\n *\n * const runtime = createAgentRuntime({ model, taxonomy, handlers, systemPrompt })\n * const result = await runtime.run(userMessage, { ctx }) // awaitable\n * for await (const y of runtime.stream(userMessage, { ctx })) {…} // streaming\n *\n * The model is advertised the app tools (so it CAN call them); each call is\n * dispatched against the product's `handlers` (so the side effect is real); the\n * `onProduced` hook fires at the real side-effect site (so an eval/UI credits a\n * persisted proposal or artifact). Substrate-free: no `@tangle-network/sandbox`,\n * no Durable Object, no `@tangle-network/agent-runtime` import. The SAME core\n * the Cloudflare Worker runs, runnable anywhere a `fetch` to an OpenAI-compatible\n * endpoint works.\n *\n * Domain stays out: the proposal taxonomy, the handlers, and the system prompt\n * are all injected — the factory knows nothing about insurance, law, tax, etc.\n */\nimport {\n type AppToolHandlers,\n type AppToolContext,\n type AppToolOutcome,\n type AppToolProducedEvent,\n type AppToolTaxonomy,\n} from '../tools/types'\nimport { buildAppToolOpenAITools, isAppToolName } from '../tools/openai'\nimport { createAppToolRuntimeExecutor } from '../tools/runtime'\nimport {\n runAppToolLoop,\n streamAppToolLoop,\n type LoopEvent,\n type LoopMessage,\n type LoopToolCall,\n type StreamLoopYield,\n type ToolLoopEvent,\n type ToolLoopResult,\n} from './index'\nimport { createOpenAICompatStreamTurn } from './openai-stream'\n\n/** OpenAI-compatible model endpoint (Tangle Router / tcloud / any compat\n * provider). Build from {@link resolveTangleModelConfig} or pass literals. */\nexport interface AgentRuntimeModelConfig {\n baseUrl: string\n apiKey: string\n model: string\n temperature?: number\n fetchImpl?: typeof fetch\n /** Extra request-body fields (e.g. `max_tokens`, a `reasoning` block). */\n extraBody?: Record<string, unknown>\n}\n\nexport interface CreateAgentRuntimeOptions {\n /** The model endpoint the turns stream from. */\n model: AgentRuntimeModelConfig\n /** The product's proposal taxonomy — advertises `submit_proposal`'s `type`\n * enum to the model and labels the regulated subset on the result. */\n taxonomy: AppToolTaxonomy\n /** Domain handlers persisting each tool to the product's store/vault. */\n handlers: AppToolHandlers\n /** Default agent identity / system prompt. A turn may override it. */\n systemPrompt: string\n /** Runaway-backstop cap. Default 200 — set far above any legitimate workflow.\n * For per-workflow limits use `deadlineMs` or `maxCostUsd` on the loop options. */\n maxToolTurns?: number\n /** Extra OpenAI tool definitions advertised ALONGSIDE the four app tools\n * (e.g. `integration_invoke`). Pair with {@link executeOtherTool}. */\n extraTools?: unknown[]\n /** Execute a tool that is NOT one of the four app tools (e.g. an integration\n * action). Only consulted for names {@link isOtherExecutableTool} accepts. */\n executeOtherTool?: (call: LoopToolCall, ctx: AppToolContext) => Promise<AppToolOutcome>\n /** Which non-app tool names are executable here. Required if {@link executeOtherTool} is set. */\n isOtherExecutableTool?: (toolName: string) => boolean\n}\n\nexport interface AgentTurnOptions {\n /** The trusted per-turn context (who/where the turn runs as). */\n ctx: AppToolContext\n /** Prior conversation turns, in order. */\n priorMessages?: Array<{ role: string; content: string }>\n /** Override the factory's default system prompt for this turn. */\n systemPrompt?: string\n /** Fires at the real side-effect site for each produced proposal/artifact. */\n onProduced?: (event: AppToolProducedEvent) => void\n}\n\nexport interface AgentRuntime {\n /** Run the bounded tool loop to completion; resolve with final text + every\n * executed tool outcome. */\n run(userMessage: string, turn: AgentTurnOptions): Promise<ToolLoopResult>\n /** Stream the bounded tool loop: yields each raw model event and each executed\n * tool result as it happens (for SSE re-emission + telemetry). */\n stream(userMessage: string, turn: AgentTurnOptions): AsyncGenerator<StreamLoopYield<LoopEvent>, void, unknown>\n}\n\n/**\n * Create an in-process agent runtime for one agent. See the module doc for the\n * full rationale; the short version: it advertises the app tools to the model,\n * dispatches each emitted call against `handlers`, and drives the bounded loop —\n * the whole agent core, sandbox-free.\n */\nexport function createAgentRuntime(opts: CreateAgentRuntimeOptions): AgentRuntime {\n if (opts.executeOtherTool && !opts.isOtherExecutableTool) {\n throw new Error('createAgentRuntime: isOtherExecutableTool is required when executeOtherTool is set')\n }\n\n // Tool schemas + the streamTurn are stable across turns — build once. The\n // model MUST be advertised the tools or it never emits a tool_call (the exact\n // failure that scores a tool-driven agent zero off-sandbox).\n const tools = [...buildAppToolOpenAITools(opts.taxonomy), ...(opts.extraTools ?? [])]\n const m = opts.model\n const streamTurn = createOpenAICompatStreamTurn({\n baseUrl: m.baseUrl,\n apiKey: m.apiKey,\n model: m.model,\n tools,\n temperature: m.temperature,\n fetchImpl: m.fetchImpl,\n extraBody: m.extraBody,\n })\n\n const isExecutableTool = (name: string): boolean =>\n isAppToolName(name) || (opts.isOtherExecutableTool?.(name) ?? false)\n\n const buildExecutor = (turn: AgentTurnOptions) => {\n const appExecutor = createAppToolRuntimeExecutor({\n handlers: opts.handlers,\n taxonomy: opts.taxonomy,\n ctx: turn.ctx,\n onProduced: turn.onProduced,\n })\n return async (call: LoopToolCall): Promise<AppToolOutcome> => {\n if (isAppToolName(call.toolName)) return appExecutor({ toolName: call.toolName, args: call.args })\n if (opts.executeOtherTool && opts.isOtherExecutableTool?.(call.toolName)) {\n return opts.executeOtherTool(call, turn.ctx)\n }\n return { ok: false, code: 'unknown_tool', message: `No executor for tool: ${call.toolName}` }\n }\n }\n\n return {\n run(userMessage, turn) {\n return runAppToolLoop({\n systemPrompt: turn.systemPrompt ?? opts.systemPrompt,\n userMessage,\n priorMessages: turn.priorMessages,\n // The awaitable loop consumes only text + tool_call; the app's UI-only\n // reasoning/usage events ride the substrate's `other` channel.\n streamTurn: narrowToToolLoopEvents(streamTurn),\n executeToolCall: buildExecutor(turn),\n isExecutableTool,\n maxToolTurns: opts.maxToolTurns,\n })\n },\n stream(userMessage, turn) {\n return streamAppToolLoop<LoopEvent>({\n systemPrompt: turn.systemPrompt ?? opts.systemPrompt,\n userMessage,\n priorMessages: turn.priorMessages,\n streamTurn,\n extractText: (ev) => (ev.type === 'text' ? ev.text : ''),\n extractToolCall: (ev) => (ev.type === 'tool_call' ? ev.call : null),\n isExecutableTool,\n executeToolCall: buildExecutor(turn),\n maxToolTurns: opts.maxToolTurns,\n })\n },\n }\n}\n\n/**\n * Adapt the app's rich {@link LoopEvent} stream to the substrate awaitable\n * loop's `ToolLoopEvent` contract. The loop reads only `text` (accumulated into\n * the answer) and `tool_call` (dispatched); the app's UI-only `reasoning` /\n * `usage` events have no awaitable meaning, so they collapse onto the\n * substrate's `other` channel and are ignored by the loop.\n */\nfunction narrowToToolLoopEvents(\n streamTurn: (messages: LoopMessage[]) => AsyncIterable<LoopEvent>,\n): (messages: LoopMessage[]) => AsyncIterable<ToolLoopEvent> {\n return (messages) =>\n (async function* () {\n for await (const ev of streamTurn(messages)) {\n if (ev.type === 'text') yield { type: 'text', text: ev.text }\n else if (ev.type === 'tool_call') yield { type: 'tool_call', call: ev.call }\n else yield { type: 'other', event: ev }\n }\n })()\n}\n","/**\n * Surface-scoped profile overlay — the seam letting any product page (a\n * sequence editor, a brief composer, a dataset view) add MCP servers, a prompt\n * addendum, and permission tightening to the workspace agent profile for turns\n * initiated FROM that surface, without the chat orchestrator knowing any\n * surface's specifics. The orchestrator resolves `(kind, ctx)` through a\n * registry the REQUEST HANDLER constructs per request (construction is a Map\n * build — cheap) and merges the result into the base profile it was about to\n * send to the sandbox. Per-request construction is the trust mechanism, not an\n * optimization target: each `build()` closes over server-trusted request state\n * (env bindings, secrets, the AUTHENTICATED user/workspace), which on Workers\n * exists only per request — a startup-built registry would force identity\n * through the untrusted client `ctx`.\n *\n * SECURITY INVARIANT: the surface `kind` and the ids inside `ctx` arrive on\n * the client request and are pure ROUTING data — never trusted content, never\n * identity. Identity comes from the closure (see above). The registered\n * `build()` runs server-side only: it validates the routing ids against the\n * product's access control, then mints its own URLs and capability tokens from\n * server configuration (`buildHttpMcpServer` + `createCapabilityToken` in\n * ../tools). A client can therefore never inject an arbitrary MCP url, header,\n * or token into the agent profile: the overlay's `mcp` values are typed as\n * {@link SurfaceMcpServer} (= the server-built `AppToolMcpServer` entry shape),\n * and only build() constructs them.\n */\n\nimport type { AppToolMcpServer } from '../tools/mcp'\n\n/** Sandbox permission posture values, ranked deny > ask > allow for merging. */\nexport type SurfacePermissionValue = 'allow' | 'ask' | 'deny'\n\n/** The only MCP entry shape an overlay may carry: the server-built bridge\n * entry from ../tools/mcp (transport, url, headers, and capability token all\n * assembled server-side). The alias exists so overlay authors reach for the\n * builders in ../tools rather than hand-rolling `{ url: ctx.url }` shapes\n * that would let request data become a dialable endpoint. */\nexport type SurfaceMcpServer = AppToolMcpServer\n\n/** What one surface contributes to the agent profile for a single turn. */\nexport interface SurfaceOverlay {\n /** MCP servers to mount for this turn, keyed by tool-routing name. Names\n * must not collide with the base profile's — see {@link mergeSurfaceOverlay}. */\n mcp?: Record<string, SurfaceMcpServer>\n /** Appended to the base system-prompt addendum with a blank-line separator. */\n promptAddendum?: string\n /** Per-key posture the surface wants for its turns. Merging is monotone\n * fail-closed: the stricter of base/overlay wins, so a surface can tighten\n * the workspace posture but never relax it. */\n permissions?: Record<string, SurfacePermissionValue>\n}\n\n/**\n * One registered surface kind. `TCtx` is the shape build() expects — a CLAIM\n * about the client payload, not a guarantee: the registry hands build() the\n * request's `ctx` unvalidated, so build() must treat every field as an\n * untrusted id (resolve it through access control that throws on a bad or\n * foreign id) before minting anything from it.\n */\nexport interface SurfaceKindDefinition<TCtx> {\n kind: string\n build: (ctx: TCtx) => SurfaceOverlay | Promise<SurfaceOverlay>\n}\n\n/** The variance-erased form a registry accepts (`build` is contravariant in\n * `TCtx`, so every concrete definition is assignable to this). */\nexport type AnySurfaceKind = SurfaceKindDefinition<never>\n\n/**\n * Declare one surface kind. The `kind` string is the client-visible routing\n * key (e.g. `'sequences'`); `build` is the server-side factory that turns a\n * validated ctx into the overlay for one turn.\n */\nexport function defineSurfaceKind<TCtx>(opts: {\n kind: string\n build: (ctx: TCtx) => SurfaceOverlay | Promise<SurfaceOverlay>\n}): SurfaceKindDefinition<TCtx> {\n if (typeof opts.kind !== 'string' || opts.kind.length === 0 || /\\s/.test(opts.kind)) {\n throw new Error(`surface kind must be a non-empty string without whitespace (got ${JSON.stringify(opts.kind)})`)\n }\n if (typeof opts.build !== 'function') {\n throw new Error(`surface kind '${opts.kind}' requires a build function`)\n }\n return { kind: opts.kind, build: opts.build }\n}\n\nexport interface SurfaceRegistry {\n /** Build the overlay for one turn. Throws on an unknown kind — an unknown\n * surface is a routing bug (client and server registries drifted), and\n * silently returning an empty overlay would strip the surface's tools from\n * the turn with no signal anywhere. Build errors propagate unwrapped. */\n resolve(kind: string, ctx: unknown): Promise<SurfaceOverlay>\n}\n\n/**\n * Assemble the product's surface registry from its registered kinds. Duplicate\n * kinds throw at construction: two builders behind one routing key would make\n * the mounted toolset depend on registration order.\n */\nexport function createSurfaceRegistry(kinds: readonly AnySurfaceKind[]): SurfaceRegistry {\n const byKind = new Map<string, AnySurfaceKind>()\n for (const definition of kinds) {\n if (byKind.has(definition.kind)) {\n throw new Error(`duplicate surface kind '${definition.kind}' — each kind must be registered exactly once`)\n }\n byKind.set(definition.kind, definition)\n }\n\n return {\n async resolve(kind, ctx) {\n const definition = byKind.get(kind)\n if (!definition) {\n const known = [...byKind.keys()].join(', ') || '(none)'\n throw new Error(\n `unknown surface kind '${kind}' — registered kinds: ${known}. ` +\n 'An unknown surface is a routing bug: register the kind via defineSurfaceKind before clients can reference it.',\n )\n }\n // The trust boundary where static ctx typing ends: the request payload is\n // handed to build() as-is, and build() validates it (see SurfaceKindDefinition).\n const overlay = await definition.build(ctx as never)\n assertSurfaceOverlay(overlay, `surface kind '${kind}'`)\n return overlay\n },\n }\n}\n\n/** Base-profile slice the merge reads/writes. Real callers pass their full\n * profile object; every field outside this slice passes through untouched. */\nexport interface SurfaceMergeBase {\n mcp?: Record<string, unknown>\n systemPromptAddendum?: string\n permissions?: Record<string, SurfacePermissionValue>\n}\n\nconst PERMISSION_SEVERITY: Record<SurfacePermissionValue, number> = { allow: 0, ask: 1, deny: 2 }\n\n/**\n * Merge one surface overlay into a base profile, returning a new object\n * (the base is never mutated; untouched nested records are shared by\n * reference).\n *\n * - `mcp`: overlay servers are added under their own names. A name already\n * present on the base THROWS — a collision is two servers claiming one\n * routing name, and renaming either silently would corrupt tool routing for\n * whichever caller expected the original binding.\n * - `systemPromptAddendum`: the overlay's `promptAddendum` appends after a\n * blank-line separator (no separator when the base has no addendum).\n * - `permissions`: per key the STRICTER value wins (deny > ask > allow). A\n * surface can tighten the base posture for its turns; a base 'deny' survives\n * any overlay.\n */\nexport function mergeSurfaceOverlay<TBase extends SurfaceMergeBase>(\n base: TBase,\n overlay: SurfaceOverlay,\n): TBase & SurfaceMergeBase {\n assertSurfaceOverlay(overlay, 'surface overlay')\n const merged: SurfaceMergeBase = { ...base }\n\n if (overlay.mcp && Object.keys(overlay.mcp).length > 0) {\n const baseMcp = base.mcp ?? {}\n const collisions = Object.keys(overlay.mcp).filter((name) => name in baseMcp)\n if (collisions.length > 0) {\n throw new Error(\n `surface overlay MCP name collision: ${collisions.map((n) => `'${n}'`).join(', ')} already exist on the base profile. ` +\n 'Two servers cannot claim one name — give the surface server a distinct name.',\n )\n }\n merged.mcp = { ...baseMcp, ...overlay.mcp }\n }\n\n if (overlay.promptAddendum !== undefined) {\n merged.systemPromptAddendum = base.systemPromptAddendum\n ? `${base.systemPromptAddendum}\\n\\n${overlay.promptAddendum}`\n : overlay.promptAddendum\n }\n\n if (overlay.permissions && Object.keys(overlay.permissions).length > 0) {\n const permissions: Record<string, SurfacePermissionValue> = { ...(base.permissions ?? {}) }\n for (const [key, value] of Object.entries(overlay.permissions)) {\n const existing = permissions[key]\n permissions[key] =\n existing === undefined || PERMISSION_SEVERITY[value] > PERMISSION_SEVERITY[existing] ? value : existing\n }\n merged.permissions = permissions\n }\n\n // merged began as a shallow copy of base; only the three slice fields were\n // replaced, so the intersection type is the true shape.\n return merged as TBase & SurfaceMergeBase\n}\n\n/** Reject overlays a build() (or hand-rolled caller) malformed, with the exact\n * field named: a relative MCP url, a blank addendum, or an off-vocabulary\n * permission would otherwise surface only as an opaque sandbox failure. */\nfunction assertSurfaceOverlay(overlay: SurfaceOverlay, label: string): void {\n if (overlay.promptAddendum !== undefined) {\n if (typeof overlay.promptAddendum !== 'string' || overlay.promptAddendum.trim().length === 0) {\n throw new Error(`${label}: promptAddendum must be a non-blank string when provided`)\n }\n }\n if (overlay.mcp !== undefined) {\n for (const [name, server] of Object.entries(overlay.mcp)) {\n if (name.trim().length === 0) throw new Error(`${label}: MCP server names must be non-empty`)\n if (server.transport !== 'http') {\n throw new Error(`${label}: MCP server '${name}' must use transport 'http' (got ${JSON.stringify((server as { transport?: unknown }).transport)})`)\n }\n let parsed: URL\n try {\n parsed = new URL(server.url)\n } catch {\n throw new Error(`${label}: MCP server '${name}' url must be an absolute URL (got ${JSON.stringify(server.url)})`)\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new Error(`${label}: MCP server '${name}' url must be http(s) (got ${JSON.stringify(server.url)})`)\n }\n }\n }\n if (overlay.permissions !== undefined) {\n for (const [key, value] of Object.entries(overlay.permissions)) {\n if (!(value in PERMISSION_SEVERITY)) {\n throw new Error(`${label}: permission '${key}' must be 'allow' | 'ask' | 'deny' (got ${JSON.stringify(value)})`)\n }\n }\n }\n}\n","export * from './model-catalog'\nexport * from './model'\nexport * from './openai-stream'\nexport * from './agent'\nexport * from './surface-profile'\n/**\n * The bounded agent tool-loop — owned by `@tangle-network/agent-runtime`.\n *\n * A model turn may emit tool calls (integration-hub actions, the app tools from\n * `../tools`, delegation). The loop streams a turn, collects the executable tool\n * calls, dispatches each, appends the results to history in OpenAI\n * function-calling shape, and re-runs so the model reads them — bounded by\n * `maxToolTurns`, a wall-clock `deadlineMs`, and a `maxCostUsd` budget.\n *\n * The history shape is the OpenAI function-calling contract: the assistant turn\n * that emitted tool calls is preserved as an `assistant` message carrying its\n * `tool_calls` array, and each result is its own `{ role: 'tool', tool_call_id,\n * content }` message keyed to the call. A strict model (Claude, and any\n * OpenAI-compatible provider that validates tool history) needs this to read its\n * own tool use back; folding results into a `user` message makes such models\n * re-issue the same call in a loop.\n *\n * The loop is substrate-owned (`runToolLoop` / `streamToolLoop`); the app\n * supplies `streamTurn` (wrapping its model endpoint) and `executeToolCall`\n * (routing to its integration + app-tool executors). The app-facing names below\n * are 1:1 aliases of the canonical symbols, kept so this package's consumers and\n * the in-package `createAgentRuntime` read against a single, stable vocabulary.\n */\nexport {\n runToolLoop as runAppToolLoop,\n streamToolLoop as streamAppToolLoop,\n} from '@tangle-network/agent-runtime'\nexport type {\n ToolLoopCall as LoopToolCall,\n ToolLoopAssistantToolCall as LoopAssistantToolCall,\n ToolLoopMessage as LoopMessage,\n ToolLoopEvent,\n ToolLoopStopReason,\n ToolLoopResult,\n RunToolLoopOptions as AppToolLoopOptions,\n StreamToolLoopOptions as StreamAppToolLoopOptions,\n StreamToolLoopYield as StreamLoopYield,\n} from '@tangle-network/agent-runtime'\n\n/**\n * Events the app's OpenAI-compat stream adapter ({@link toLoopEvents}) yields.\n *\n * This is the app's own `Raw` event type for the streaming loop — the canonical\n * `streamToolLoop<Raw>` is generic over it. It widens the substrate's\n * tool-loop event with `reasoning` (DeepSeek/router `reasoning_content` /\n * `thinking` deltas, rendered as thinking sections) and `usage` (per-message\n * token accounting) — neither belongs in the substrate's loop contract, so they\n * stay here. The adapter maps each into the `streamTurn` seam; `text` and\n * `tool_call` drive the loop, `reasoning` / `usage` pass through to the UI.\n */\nexport type LoopEvent =\n | { type: 'text'; text: string }\n | { type: 'reasoning'; text: string }\n | { type: 'tool_call'; call: import('@tangle-network/agent-runtime').ToolLoopCall }\n | { type: 'usage'; usage: { promptTokens: number; completionTokens: number } }\n | { type: 'other'; event: unknown }\n"],"mappings":"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","names":[]}