@tangle-network/agent-app 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/{DesignCanvas-3JEEIT6Y.js → DesignCanvas-JTSAL6KX.js} +2 -2
  2. package/dist/DesignCanvasEditor-YPVETLZG.js +9 -0
  3. package/dist/{chunk-F5KTWRO7.js → chunk-2W4YCAFH.js} +5 -1
  4. package/dist/chunk-2W4YCAFH.js.map +1 -0
  5. package/dist/{chunk-4NXVI7PW.js → chunk-FA4XR66Y.js} +2 -2
  6. package/dist/chunk-FA4XR66Y.js.map +1 -0
  7. package/dist/{chunk-QAQBR6KQ.js → chunk-JZZ6AWF4.js} +3 -2
  8. package/dist/{chunk-QAQBR6KQ.js.map → chunk-JZZ6AWF4.js.map} +1 -1
  9. package/dist/{chunk-ETX4O4BB.js → chunk-LUE4HO5C.js} +95 -177
  10. package/dist/chunk-LUE4HO5C.js.map +1 -0
  11. package/dist/{chunk-SSX2A6XX.js → chunk-MH6AVXQ7.js} +2 -2
  12. package/dist/{chunk-2Q73HGDI.js → chunk-NSKJFV4Y.js} +17 -5
  13. package/dist/chunk-NSKJFV4Y.js.map +1 -0
  14. package/dist/design-canvas/index.d.ts +2 -2
  15. package/dist/design-canvas-react/index.d.ts +16 -2
  16. package/dist/design-canvas-react/index.js +4 -4
  17. package/dist/eval/index.d.ts +1 -1
  18. package/dist/eval/index.js +1 -1
  19. package/dist/index.d.ts +5 -3
  20. package/dist/index.js +10 -8
  21. package/dist/{mcp-CIupfjxV.d.ts → mcp-eZCmkgCF.d.ts} +1 -1
  22. package/dist/preset-cloudflare/index.d.ts +1 -1
  23. package/dist/runtime/index.d.ts +94 -161
  24. package/dist/runtime/index.js +8 -6
  25. package/dist/sequences/index.d.ts +2 -2
  26. package/dist/tools/index.d.ts +4 -4
  27. package/dist/tools/index.js +2 -2
  28. package/dist/{types-By4B3K37.d.ts → types-2rOJo8Hc.d.ts} +6 -3
  29. package/package.json +5 -4
  30. package/dist/DesignCanvasEditor-37LPJIIR.js +0 -9
  31. package/dist/chunk-2Q73HGDI.js.map +0 -1
  32. package/dist/chunk-4NXVI7PW.js.map +0 -1
  33. package/dist/chunk-ETX4O4BB.js.map +0 -1
  34. package/dist/chunk-F5KTWRO7.js.map +0 -1
  35. /package/dist/{DesignCanvas-3JEEIT6Y.js.map → DesignCanvas-JTSAL6KX.js.map} +0 -0
  36. /package/dist/{DesignCanvasEditor-37LPJIIR.js.map → DesignCanvasEditor-YPVETLZG.js.map} +0 -0
  37. /package/dist/{chunk-SSX2A6XX.js.map → chunk-MH6AVXQ7.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/design-canvas-react/components/Workspace.tsx","../src/design-canvas-react/engine/snap.ts","../src/design-canvas-react/engine/zoom-pan.ts","../src/design-canvas-react/engine/selection.ts","../src/design-canvas-react/components/ElementNode.tsx","../src/design-canvas-react/components/transform-math.ts","../src/design-canvas-react/components/SelectionLayer.tsx","../src/design-canvas-react/components/GridLayer.tsx","../src/design-canvas-react/components/SnapGuidesOverlay.tsx","../src/design-canvas-react/components/InlineTextEditor.tsx","../src/design-canvas-react/components/DesignCanvasEditor.tsx"],"sourcesContent":["/**\n * Konva Stage host for the design-canvas editor. Renders the active page\n * (background rect + element nodes), wires all interaction gestures, and\n * delegates persistence through the command stack.\n *\n * COORDINATE SYSTEM:\n * The Konva stage sits at (0,0) in screen space. The content Layer group is\n * translated by (panX, panY) and scaled by (zoom, zoom). Document coordinates\n * map to screen via: screenX = panX + docX * zoom.\n *\n * GESTURES (mutually exclusive; escape cancels any live gesture):\n * - Wheel: zoom-to-cursor via ZoomPanMath.zoomAtPoint; setView only.\n * - Middle-button drag / space+drag: pan; setView only.\n * - Empty-space drag: marquee selection; setView only.\n * - Element drag: move via onDragMove preview + onDragEnd command.\n * - Transformer: resize/rotate; handled by SelectionLayer.\n * - Double-click on text: opens InlineTextEditor.\n *\n * KEYBOARD (active when canvas wrapper div is focused):\n * - delete/backspace → deleteElementCommand for each selected id.\n * - arrows → nudge (shift ×10); ONE command emitted on keyup (coalescing).\n * - mod+z / shift+mod+z → undo / redo.\n * - mod+d → duplicate selection at +10,+10 offset.\n * - mod+g → group selection (≥2 elements).\n * - shift+mod+g → ungroup selected group.\n * - mod+a → select all on current page.\n * - escape → cancel live gesture or clear selection.\n *\n * POINTER CAPTURE: marquee and pan gestures call setPointerCapture on the\n * wrapper div so drag events don't escape on fast pointer moves.\n *\n * DEVICEPIXELRATIO: Konva's pixelRatio prop multiplies the backing canvas\n * resolution for crisp rendering at any DPR.\n *\n * Composition:\n * - `WorkspaceView` is the injectable form: receives a pre-created stack and\n * a guaranteed non-null activePage, commits all gestures through that shared\n * stack. `DesignCanvasEditor` passes the chrome's stack here so undo/redo\n * and layers-panel selection stay coherent across both surfaces.\n * - `Workspace` is the standalone wrapper: creates its own stack and renders\n * WorkspaceView. Existing consumers (tests, bare embeds) mount this without\n * any chrome.\n */\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { Stage, Layer, Rect, Group } from 'react-konva'\nimport type Konva from 'konva'\n\nimport type { DesignCanvasProps } from '../contracts'\nimport { createSceneCommandStack } from '../engine/command-stack'\nimport { createSnapEngine, collectGridTargets } from '../engine/snap'\nimport { createZoomPanMath } from '../engine/zoom-pan'\nimport { marqueeSelect } from '../engine/selection'\nimport {\n addElementCommand,\n setAttrsCommand,\n multiSetAttrsCommand,\n deleteElementCommand,\n groupElementsCommand,\n ungroupElementCommand,\n} from '../engine/commands'\nimport type { MultiSetAttrsEntry } from '../engine/commands'\n\nimport { ElementNode } from './ElementNode'\nimport { SelectionLayer } from './SelectionLayer'\nimport { GridLayer } from './GridLayer'\nimport { SnapGuidesOverlay } from './SnapGuidesOverlay'\nimport { InlineTextEditor } from './InlineTextEditor'\nimport { BleedTrimOverlay } from './BleedTrimOverlay'\n\nimport { normalizeMarquee, nudgeDelta as nudgeDeltaMath } from './transform-math'\nimport { elementAabb, findElement } from '../../design-canvas/model'\nimport type { SceneElement, ScenePage, TextElement } from '../../design-canvas/model'\nimport type { SnapResult, SnapTarget, SceneCommand } from '../contracts'\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst SNAP_THRESHOLD_SCREEN_PX = 8\nconst DUPLICATE_OFFSET = 10\nconst ZOOM_FACTOR_WHEEL = 0.998\nconst ZOOM_MIN = 0.05\nconst ZOOM_MAX = 32\n\n// ---------------------------------------------------------------------------\n// Gesture / marquee types\n// ---------------------------------------------------------------------------\n\ninterface MarqueeState {\n active: boolean\n startDocX: number\n startDocY: number\n endDocX: number\n endDocY: number\n}\n\nconst NO_MARQUEE: MarqueeState = { active: false, startDocX: 0, startDocY: 0, endDocX: 0, endDocY: 0 }\n\ntype GestureMode = 'idle' | 'pan' | 'marquee' | 'drag'\n\n// ---------------------------------------------------------------------------\n// WorkspaceViewProps — injectable form (shared stack, guaranteed activePage)\n// ---------------------------------------------------------------------------\n\nexport interface WorkspaceViewProps {\n /** The command stack this view commits gestures through. Must be the same\n * instance the chrome (DesignCanvasEditor) owns so undo/redo and layers-\n * panel selection are coherent. */\n stack: ReturnType<typeof createSceneCommandStack>\n /** Active page resolved before render — WorkspaceView has no conditional\n * hook guards; the caller ensures this is never null. */\n activePage: ScenePage\n canWrite: boolean\n onApplyOperations: DesignCanvasProps['onApplyOperations']\n onSelectionChange?: DesignCanvasProps['onSelectionChange']\n renderAgentPanel?: DesignCanvasProps['renderAgentPanel']\n renderSidePanel?: DesignCanvasProps['renderSidePanel']\n className?: string\n /** Ref the chrome fills with a fit-page callback. The chrome calls it on F /\n * Fit button; when injected via DesignCanvasEditor the ref is shared. */\n onFitRef?: React.MutableRefObject<(() => void) | null>\n}\n\n// ---------------------------------------------------------------------------\n// WorkspaceView — all interaction + Konva render; commits through shared stack\n// ---------------------------------------------------------------------------\n\nexport function WorkspaceView({\n canWrite,\n onApplyOperations,\n onSelectionChange,\n renderAgentPanel,\n renderSidePanel,\n className,\n stack,\n activePage,\n onFitRef,\n}: WorkspaceViewProps) {\n // Re-render when command stack changes state.\n const [, setTick] = useState(0)\n const forceRender = useCallback(() => setTick((t) => t + 1), [])\n useEffect(() => stack.subscribe(forceRender), [stack, forceRender])\n\n const state = stack.getState()\n const { document, activePageId, selectedElementIds, zoom, panX, panY, gridEnabled, gridSize, snapEnabled, showBleed } = state\n\n // -------------------------------------------------------------------------\n // Stable engines\n // -------------------------------------------------------------------------\n\n const zoomPanMath = useMemo(() => createZoomPanMath({ minZoom: ZOOM_MIN, maxZoom: ZOOM_MAX }), [])\n const snapEngine = useMemo(() => createSnapEngine(), [])\n\n // -------------------------------------------------------------------------\n // Container sizing\n // -------------------------------------------------------------------------\n\n const containerRef = useRef<HTMLDivElement>(null)\n const [containerSize, setContainerSize] = useState({ width: 800, height: 600 })\n\n useLayoutEffect(() => {\n const el = containerRef.current\n if (!el) return\n const ro = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n const { width, height } = entry.contentRect\n setContainerSize({ width, height })\n })\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n\n const stageRef = useRef<Konva.Stage | null>(null)\n\n // -------------------------------------------------------------------------\n // Fit-page callback — exposed via onFitRef so the chrome can trigger it\n // -------------------------------------------------------------------------\n\n useEffect(() => {\n if (!onFitRef) return\n onFitRef.current = () => {\n const { width, height } = containerSize\n if (width <= 0 || height <= 0) return\n const view = zoomPanMath.fitPage(activePage, { width, height })\n stack.setView(view)\n }\n return () => {\n // Only clear if we still own the slot (multiple WorkspaceView mounts\n // would each try to own it; the last to mount wins, but on unmount we\n // must not clear a ref filled by a newer mount).\n if (onFitRef.current !== null) onFitRef.current = null\n }\n })\n\n // -------------------------------------------------------------------------\n // Gesture refs\n // -------------------------------------------------------------------------\n\n const gestureRef = useRef<GestureMode>('idle')\n const panOriginRef = useRef({ screenX: 0, screenY: 0, panX: 0, panY: 0 })\n const [marquee, setMarquee] = useState<MarqueeState>(NO_MARQUEE)\n const marqueeRef = useRef<MarqueeState>(NO_MARQUEE)\n const spaceHeldRef = useRef(false)\n // Stores origin position AND AABB dimensions captured at drag start so\n // snap reference points (center, far edge) are correct for any element size.\n const dragOriginRef = useRef<Map<string, { x: number; y: number; width: number; height: number }>>(new Map())\n const [activeSnap, setActiveSnap] = useState<SnapResult | null>(null)\n const [editingElementId, setEditingElementId] = useState<string | null>(null)\n const editingPreRef = useRef<string>('')\n const nudgeHeldRef = useRef<{\n key: string\n ids: string[]\n origin: Map<string, { x: number; y: number }>\n } | null>(null)\n\n // -------------------------------------------------------------------------\n // Persist helper — optimistic execute then remote apply, rollback on throw\n // -------------------------------------------------------------------------\n\n async function persist(command: SceneCommand): Promise<void> {\n if (!canWrite) return\n stack.execute(command)\n try {\n const result = await onApplyOperations(command.operations())\n if (result.document) stack.reset(result.document)\n } catch {\n // Roll back by splicing this specific command from history and applying its\n // inverse to the current state. Using stack.rollback(command) rather than\n // stack.execute(synthetic-rollback) avoids polluting the undo stack with a\n // phantom entry and preserves any commands the user executed while this save\n // was in-flight.\n stack.rollback(command)\n }\n }\n\n // -------------------------------------------------------------------------\n // Wheel zoom-to-cursor\n // -------------------------------------------------------------------------\n\n function handleWheel(e: React.WheelEvent<HTMLDivElement>) {\n e.preventDefault()\n const rect = containerRef.current?.getBoundingClientRect()\n if (!rect) return\n const screenX = e.clientX - rect.left\n const screenY = e.clientY - rect.top\n const factor = Math.pow(ZOOM_FACTOR_WHEEL, e.deltaY)\n const next = zoomPanMath.zoomAtPoint({ zoom, panX, panY }, factor, screenX, screenY)\n stack.setView(next)\n }\n\n // -------------------------------------------------------------------------\n // Pointer handlers\n // -------------------------------------------------------------------------\n\n function handlePointerDown(e: React.PointerEvent<HTMLDivElement>) {\n if (e.button === 1 || spaceHeldRef.current) {\n e.preventDefault()\n ;(e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId)\n gestureRef.current = 'pan'\n panOriginRef.current = { screenX: e.clientX, screenY: e.clientY, panX, panY }\n return\n }\n if (e.button !== 0) return\n\n const stage = stageRef.current\n const rect = containerRef.current?.getBoundingClientRect()\n if (!stage || !rect) return\n const screenX = e.clientX - rect.left\n const screenY = e.clientY - rect.top\n const hitNode = stage.getIntersection({ x: screenX, y: screenY })\n\n if (hitNode && !hitNode.name().startsWith('overlay:') && hitNode.name() !== 'page-background') {\n return\n }\n\n e.preventDefault()\n ;(e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId)\n gestureRef.current = 'marquee'\n const docPos = zoomPanMath.screenToDocument({ zoom, panX, panY }, screenX, screenY)\n const m: MarqueeState = { active: true, startDocX: docPos.x, startDocY: docPos.y, endDocX: docPos.x, endDocY: docPos.y }\n marqueeRef.current = m\n setMarquee(m)\n stack.setView({ selectedElementIds: [] })\n }\n\n function handlePointerMove(e: React.PointerEvent<HTMLDivElement>) {\n const mode = gestureRef.current\n const rect = containerRef.current?.getBoundingClientRect()\n if (!rect) return\n\n if (mode === 'pan') {\n const dx = e.clientX - panOriginRef.current.screenX\n const dy = e.clientY - panOriginRef.current.screenY\n stack.setView({ panX: panOriginRef.current.panX + dx, panY: panOriginRef.current.panY + dy })\n return\n }\n\n if (mode === 'marquee') {\n const screenX = e.clientX - rect.left\n const screenY = e.clientY - rect.top\n const docPos = zoomPanMath.screenToDocument({ zoom, panX, panY }, screenX, screenY)\n const m: MarqueeState = { ...marqueeRef.current, active: true, endDocX: docPos.x, endDocY: docPos.y }\n marqueeRef.current = m\n setMarquee(m)\n const normalized = normalizeMarquee(m.startDocX, m.startDocY, m.endDocX, m.endDocY)\n const ids = marqueeSelect(activePage, normalized)\n stack.setView({ selectedElementIds: ids })\n }\n }\n\n function handlePointerUp(e: React.PointerEvent<HTMLDivElement>) {\n gestureRef.current = 'idle'\n setMarquee(NO_MARQUEE)\n marqueeRef.current = NO_MARQUEE\n setActiveSnap(null)\n ;(e.currentTarget as HTMLDivElement).releasePointerCapture(e.pointerId)\n }\n\n // -------------------------------------------------------------------------\n // Element drag callbacks\n // -------------------------------------------------------------------------\n\n function handleElementDragStart(elementId: string) {\n if (!canWrite) return\n const ids = selectedElementIds.includes(elementId) ? selectedElementIds : [elementId]\n const origins = new Map<string, { x: number; y: number; width: number; height: number }>()\n for (const id of ids) {\n const found = findElement(activePage, id)\n if (found) {\n const aabb = elementAabb(found.element)\n origins.set(id, { x: found.element.x, y: found.element.y, width: aabb.width, height: aabb.height })\n }\n }\n dragOriginRef.current = origins\n gestureRef.current = 'drag'\n if (!selectedElementIds.includes(elementId)) {\n stack.setView({ selectedElementIds: [elementId] })\n }\n }\n\n function handleElementDragMove(elementId: string, dx: number, dy: number) {\n if (!canWrite) return\n const origin = dragOriginRef.current.get(elementId)\n if (!origin) return\n const dragging = selectedElementIds.includes(elementId) ? selectedElementIds : [elementId]\n const proposedX = origin.x + dx\n const proposedY = origin.y + dy\n // Use the actual element AABB dimensions so center and far-edge snap points\n // are correct for elements of any size (not the former hardcoded 100×100).\n const snapBounds = { x: proposedX, y: proposedY, width: origin.width, height: origin.height }\n\n if (snapEnabled) {\n const targets = snapEngine.collectTargets(state, dragging)\n if (gridEnabled) {\n const thresholdDoc = SNAP_THRESHOLD_SCREEN_PX / zoom\n const gt = collectGridTargets(snapBounds, gridSize, activePage, thresholdDoc)\n targets.vertical.push(...gt.vertical)\n targets.horizontal.push(...gt.horizontal)\n }\n const snapResult = snapEngine.apply(snapBounds, targets, SNAP_THRESHOLD_SCREEN_PX, zoom)\n setActiveSnap(snapResult)\n }\n }\n\n async function handleElementDragEnd(elementId: string, finalX: number, finalY: number) {\n if (!canWrite) return\n gestureRef.current = 'idle'\n setActiveSnap(null)\n\n const origin = dragOriginRef.current.get(elementId)\n if (!origin) return\n\n const dx = finalX - origin.x\n const dy = finalY - origin.y\n const draggingIds = selectedElementIds.includes(elementId) ? selectedElementIds : [elementId]\n\n if (draggingIds.length === 1) {\n const el = findElement(activePage, elementId)?.element\n if (!el) return\n await persist(\n setAttrsCommand({\n pageId: activePageId,\n elementId,\n attrs: { x: finalX, y: finalY },\n priorAttrs: { x: el.x, y: el.y },\n }),\n )\n } else {\n const entries: MultiSetAttrsEntry[] = []\n for (const id of draggingIds) {\n const el = findElement(activePage, id)?.element\n const orig = dragOriginRef.current.get(id)\n if (!el || !orig) continue\n entries.push({\n pageId: activePageId,\n elementId: id,\n attrs: { x: orig.x + dx, y: orig.y + dy },\n priorAttrs: { x: orig.x, y: orig.y },\n })\n }\n if (entries.length > 0) await persist(multiSetAttrsCommand(entries))\n }\n\n dragOriginRef.current = new Map()\n }\n\n // -------------------------------------------------------------------------\n // Element interactions\n // -------------------------------------------------------------------------\n\n function handleElementClick(elementId: string) {\n stack.setView({ selectedElementIds: [elementId] })\n }\n\n function handleElementDoubleClick(elementId: string) {\n if (!canWrite) return\n const found = findElement(activePage, elementId)\n if (!found || found.element.kind !== 'text') return\n editingPreRef.current = (found.element as TextElement).text\n setEditingElementId(elementId)\n }\n\n async function handleTextCommit(text: string) {\n const id = editingElementId\n setEditingElementId(null)\n if (!id || !canWrite) return\n const found = findElement(activePage, id)\n if (!found || found.element.kind !== 'text') return\n if (text === editingPreRef.current) return\n await persist(\n setAttrsCommand({\n pageId: activePageId,\n elementId: id,\n attrs: { text },\n priorAttrs: { text: editingPreRef.current },\n }),\n )\n }\n\n function handleTextCancel() {\n setEditingElementId(null)\n }\n\n // -------------------------------------------------------------------------\n // Transformer end\n // -------------------------------------------------------------------------\n\n async function handleTransformEnd(entries: MultiSetAttrsEntry[]) {\n if (!canWrite || entries.length === 0) return\n await persist(multiSetAttrsCommand(entries))\n }\n\n // -------------------------------------------------------------------------\n // Keyboard\n // -------------------------------------------------------------------------\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLDivElement>) {\n if (e.key === ' ') {\n e.preventDefault()\n spaceHeldRef.current = true\n return\n }\n\n const mod = e.metaKey || e.ctrlKey\n\n if (e.key === 'Escape') {\n e.preventDefault()\n if (gestureRef.current !== 'idle') {\n gestureRef.current = 'idle'\n setMarquee(NO_MARQUEE)\n setActiveSnap(null)\n } else if (editingElementId) {\n setEditingElementId(null)\n } else {\n stack.setView({ selectedElementIds: [] })\n }\n return\n }\n\n if (editingElementId) return\n\n if ((e.key === 'Delete' || e.key === 'Backspace') && selectedElementIds.length > 0) {\n e.preventDefault()\n for (const id of [...selectedElementIds]) {\n try {\n persist(deleteElementCommand({ document, pageId: activePageId, elementId: id }))\n } catch { /* element gone — skip */ }\n }\n stack.setView({ selectedElementIds: [] })\n return\n }\n\n if (mod && e.key === 'z' && !e.shiftKey) {\n e.preventDefault()\n if (stack.canUndo()) stack.undo()\n return\n }\n if (mod && ((e.key === 'z' && e.shiftKey) || e.key === 'y')) {\n e.preventDefault()\n if (stack.canRedo()) stack.redo()\n return\n }\n\n if (mod && e.key === 'a') {\n e.preventDefault()\n const ids = activePage.elements.filter((el) => !el.locked && el.visible).map((el) => el.id)\n stack.setView({ selectedElementIds: ids })\n return\n }\n\n if (mod && e.key === 'd' && selectedElementIds.length > 0) {\n e.preventDefault()\n for (const id of selectedElementIds) {\n const found = findElement(activePage, id)\n if (!found) continue\n const clone: SceneElement = {\n ...structuredClone(found.element),\n id: crypto.randomUUID(),\n x: found.element.x + DUPLICATE_OFFSET,\n y: found.element.y + DUPLICATE_OFFSET,\n }\n persist(addElementCommand({ pageId: activePageId, element: clone }))\n }\n return\n }\n\n if (mod && !e.shiftKey && e.key === 'g' && selectedElementIds.length >= 2) {\n e.preventDefault()\n persist(groupElementsCommand({\n document,\n pageId: activePageId,\n elementIds: selectedElementIds,\n groupId: crypto.randomUUID(),\n }))\n return\n }\n\n if (mod && e.shiftKey && e.key === 'g' && selectedElementIds.length === 1) {\n e.preventDefault()\n const id = selectedElementIds[0]!\n persist(ungroupElementCommand({ document, pageId: activePageId, groupId: id }))\n return\n }\n\n if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key) && selectedElementIds.length > 0) {\n e.preventDefault()\n const key = e.key as 'ArrowLeft' | 'ArrowRight' | 'ArrowUp' | 'ArrowDown'\n if (nudgeHeldRef.current && nudgeHeldRef.current.key !== key) {\n flushNudge()\n }\n if (!nudgeHeldRef.current) {\n const origins = new Map<string, { x: number; y: number }>()\n for (const id of selectedElementIds) {\n const found = findElement(activePage, id)\n if (found) origins.set(id, { x: found.element.x, y: found.element.y })\n }\n nudgeHeldRef.current = { key, ids: selectedElementIds.slice(), origin: origins }\n }\n // Apply nudge optimistically each keydown via the command stack so the\n // user sees live movement; the held ref lets us coalesce all steps into\n // one undo entry on keyup.\n const { dx, dy } = nudgeDeltaMath(key, e.shiftKey)\n const entries: MultiSetAttrsEntry[] = []\n for (const [id, orig] of nudgeHeldRef.current.origin) {\n const found = findElement(activePage, id)\n if (!found) continue\n entries.push({\n pageId: activePageId,\n elementId: id,\n attrs: { x: found.element.x + dx, y: found.element.y + dy },\n priorAttrs: { x: found.element.x, y: found.element.y },\n })\n }\n if (entries.length > 0) {\n // Preview: silent local apply without remote persist (flushed on keyup).\n stack.execute(multiSetAttrsCommand(entries))\n }\n }\n }\n\n function handleKeyUp(e: React.KeyboardEvent<HTMLDivElement>) {\n if (e.key === ' ') {\n spaceHeldRef.current = false\n return\n }\n if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key)) {\n flushNudge()\n }\n }\n\n function flushNudge() {\n const held = nudgeHeldRef.current\n if (!held) return\n nudgeHeldRef.current = null\n if (!canWrite || held.ids.length === 0) return\n\n // Build a single coalesced set_attrs from original positions to current.\n const entries: MultiSetAttrsEntry[] = []\n for (const id of held.ids) {\n const orig = held.origin.get(id)\n const found = findElement(activePage, id)\n if (!orig || !found) continue\n entries.push({\n pageId: activePageId,\n elementId: id,\n attrs: { x: found.element.x, y: found.element.y },\n priorAttrs: { x: orig.x, y: orig.y },\n })\n }\n if (entries.length > 0) {\n // Persist the coalesced nudge; the intermediate stack entries from\n // keydown previews become part of the committed ops batch.\n onApplyOperations(multiSetAttrsCommand(entries).operations()).catch(() => {\n // Roll back if remote rejects.\n for (const [id, orig] of held.origin) {\n const found = findElement(activePage, id)\n if (!found) continue\n persist(setAttrsCommand({\n pageId: activePageId,\n elementId: id,\n attrs: { x: orig.x, y: orig.y },\n priorAttrs: { x: found.element.x, y: found.element.y },\n }))\n }\n })\n }\n }\n\n // -------------------------------------------------------------------------\n // Notify host of selection changes\n // -------------------------------------------------------------------------\n\n useEffect(() => {\n if (!onSelectionChange) return\n const elements = selectedElementIds\n .map((id) => findElement(activePage, id)?.element)\n .filter((el): el is SceneElement => !!el)\n onSelectionChange(elements)\n }, [selectedElementIds, activePage, onSelectionChange])\n\n // -------------------------------------------------------------------------\n // Derived values for render\n // -------------------------------------------------------------------------\n\n const selectedElements = useMemo(\n () =>\n selectedElementIds\n .map((id) => findElement(activePage, id)?.element)\n .filter((el): el is SceneElement => !!el),\n [selectedElementIds, activePage],\n )\n\n const editingTextElement = editingElementId\n ? (findElement(activePage, editingElementId)?.element as TextElement | undefined) ?? null\n : null\n\n const stageRect = containerRef.current?.getBoundingClientRect() ?? { left: 0, top: 0 }\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio : 1\n\n const pageScreenX = panX\n const pageScreenY = panY\n const pageScreenW = activePage.width * zoom\n const pageScreenH = activePage.height * zoom\n\n const activeVerticalGuide: SnapTarget | null = activeSnap?.activeVertical ?? null\n const activeHorizontalGuide: SnapTarget | null = activeSnap?.activeHorizontal ?? null\n\n const normalizedMarquee = marquee.active\n ? normalizeMarquee(marquee.startDocX, marquee.startDocY, marquee.endDocX, marquee.endDocY)\n : null\n\n // -------------------------------------------------------------------------\n // Render\n // -------------------------------------------------------------------------\n\n return (\n <div\n className={`design-canvas-workspace relative overflow-hidden bg-[#1a1a1a] outline-none ${className ?? ''}`}\n ref={containerRef}\n tabIndex={0}\n onWheel={handleWheel}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onKeyDown={handleKeyDown}\n onKeyUp={handleKeyUp}\n style={{ cursor: spaceHeldRef.current ? 'grab' : 'default' }}\n >\n {/* Left side panel */}\n {renderSidePanel && (\n <div className=\"absolute left-0 top-0 bottom-0 z-20 w-60 pointer-events-auto\">\n {renderSidePanel()}\n </div>\n )}\n\n {/* Konva Stage */}\n <Stage\n ref={stageRef}\n width={containerSize.width}\n height={containerSize.height}\n pixelRatio={dpr}\n listening={true}\n >\n {/* Grid beneath content */}\n {gridEnabled && (\n <GridLayer\n pageWidth={activePage.width}\n pageHeight={activePage.height}\n gridSize={gridSize}\n zoom={zoom}\n />\n )}\n\n {/* Page content */}\n <Layer>\n <Group x={panX} y={panY} scaleX={zoom} scaleY={zoom}>\n <Rect\n name=\"page-background\"\n x={0}\n y={0}\n width={activePage.width}\n height={activePage.height}\n fill={activePage.background}\n shadowColor=\"rgba(0,0,0,0.4)\"\n shadowBlur={24 / zoom}\n shadowOffset={{ x: 0, y: 4 / zoom }}\n listening={false}\n />\n <Group\n clipX={0}\n clipY={0}\n clipWidth={activePage.width}\n clipHeight={activePage.height}\n >\n {activePage.elements.map((element) => (\n <ElementNode\n key={element.id}\n element={element}\n isSelected={selectedElementIds.includes(element.id)}\n zoom={zoom}\n onClick={handleElementClick}\n onDragStart={handleElementDragStart}\n onDragMove={handleElementDragMove}\n onDragEnd={handleElementDragEnd}\n onDoubleClick={handleElementDoubleClick}\n />\n ))}\n </Group>\n </Group>\n </Layer>\n\n {/* Snap guides — same doc-space coordinate group */}\n {(activeVerticalGuide || activeHorizontalGuide) && (\n <Layer>\n <Group x={panX} y={panY} scaleX={zoom} scaleY={zoom}>\n <SnapGuidesOverlay\n pageWidth={activePage.width}\n pageHeight={activePage.height}\n activeVertical={activeVerticalGuide}\n activeHorizontal={activeHorizontalGuide}\n zoom={zoom}\n />\n </Group>\n </Layer>\n )}\n\n {/* Transformer / selection handles */}\n <SelectionLayer\n stageRef={stageRef}\n selectedIds={selectedElementIds}\n selectedElements={selectedElements}\n canWrite={canWrite}\n onTransformEnd={handleTransformEnd}\n pageId={activePageId}\n />\n </Stage>\n\n {/* Bleed overlay */}\n {showBleed && activePage.bleed && (\n <div\n className=\"pointer-events-none absolute\"\n style={{ left: pageScreenX, top: pageScreenY, width: pageScreenW, height: pageScreenH }}\n >\n <BleedTrimOverlay\n pageWidthPx={pageScreenW}\n pageHeightPx={pageScreenH}\n bleed={{\n top: activePage.bleed.top * zoom,\n right: activePage.bleed.right * zoom,\n bottom: activePage.bleed.bottom * zoom,\n left: activePage.bleed.left * zoom,\n }}\n />\n </div>\n )}\n\n {/* Marquee rect */}\n {normalizedMarquee && (\n <div\n className=\"pointer-events-none absolute border border-blue-400 bg-blue-400/10\"\n style={{\n left: panX + normalizedMarquee.x * zoom,\n top: panY + normalizedMarquee.y * zoom,\n width: normalizedMarquee.width * zoom,\n height: normalizedMarquee.height * zoom,\n }}\n />\n )}\n\n {/* Inline text editor */}\n {editingTextElement && (\n <InlineTextEditor\n element={editingTextElement}\n zoom={zoom}\n panX={panX}\n panY={panY}\n stageRect={{ left: stageRect.left, top: stageRect.top }}\n onCommit={handleTextCommit}\n onCancel={handleTextCancel}\n />\n )}\n\n {/* Agent panel (right) */}\n {renderAgentPanel && (\n <div className=\"absolute right-0 top-0 bottom-0 z-20 w-80 pointer-events-auto\">\n {renderAgentPanel({ selectedElements, activePageId })}\n </div>\n )}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Workspace — standalone wrapper for bare embeds and tests (no chrome)\n// ---------------------------------------------------------------------------\n\n/**\n * Self-contained Konva workspace that creates its own command stack. Mount\n * this when you want the canvas without the toolbar/rulers/pages-strip chrome.\n *\n * Products that want the full editor (chrome + workspace sharing one stack)\n * should mount `DesignCanvasEditor` instead.\n */\nexport function Workspace(props: DesignCanvasProps) {\n // Stack outlives WorkspaceView re-mounts; created once from the initial doc.\n const stackRef = useRef(\n createSceneCommandStack(\n props.document,\n props.document.pages[0]?.id ?? '',\n ),\n )\n\n const [, setTick] = useState(0)\n const forceRender = useCallback(() => setTick((t) => t + 1), [])\n\n useEffect(() => {\n return stackRef.current.subscribe(forceRender)\n }, [forceRender])\n\n // Rebase on server refresh (host changes rev + document together).\n const prevRevRef = useRef(props.rev)\n useEffect(() => {\n if (props.rev !== prevRevRef.current) {\n prevRevRef.current = props.rev\n stackRef.current.reset(props.document)\n }\n }, [props.rev, props.document])\n\n const state = stackRef.current.getState()\n const activePage =\n state.document.pages.find((p) => p.id === state.activePageId) ??\n state.document.pages[0]\n\n // No pages — document is empty shell, nothing to render.\n if (!activePage) return null\n\n return (\n <WorkspaceView\n stack={stackRef.current}\n activePage={activePage}\n canWrite={props.canWrite}\n onApplyOperations={props.onApplyOperations}\n onSelectionChange={props.onSelectionChange}\n renderAgentPanel={props.renderAgentPanel}\n renderSidePanel={props.renderSidePanel}\n className={props.className}\n />\n )\n}\n","/**\n * 2-axis snap engine for the design-canvas editor. Targets come from element\n * AABBs (edges + centers), page edges + center, saved guides, and grid lines\n * near the moving bounds. Grid lines are generated lazily in the neighborhood\n * of the moving element, not the full page — avoids allocating thousands of\n * targets on large pages with fine grids.\n *\n * Threshold is a SCREEN distance (pixels), divided by zoom to convert to\n * document units — what \"feels close\" is a screen distance.\n *\n * Tie-breaking: non-grid kinds beat grid on equal distance; among equals of\n * the same priority the first in iteration order wins.\n */\n\nimport { elementAabb } from '../../design-canvas/model'\nimport type { Bounds, SceneElement, ScenePage } from '../../design-canvas/model'\nimport type { EditorSceneState, SnapEngine, SnapResult, SnapTarget, SnapTargetKind, SnapTargets } from '../contracts'\n\n/** Priority rank for tie-breaking: lower = higher priority (wins the tie). */\nconst KIND_PRIORITY: Record<SnapTargetKind, number> = {\n 'guide': 0,\n 'page-edge': 1,\n 'page-center': 2,\n 'element-edge': 3,\n 'element-center': 4,\n 'grid': 5,\n}\n\nexport function createSnapEngine(): SnapEngine {\n return {\n collectTargets(state: EditorSceneState, excludeIds: string[]): SnapTargets {\n const page = state.document.pages.find((p) => p.id === state.activePageId)\n if (!page) throw new Error(`collectTargets: active page ${state.activePageId} not found`)\n\n const vertical: SnapTarget[] = []\n const horizontal: SnapTarget[] = []\n const excludeSet = new Set(excludeIds)\n\n // Page edges + center\n vertical.push({ position: 0, kind: 'page-edge' })\n vertical.push({ position: page.width, kind: 'page-edge' })\n vertical.push({ position: page.width / 2, kind: 'page-center' })\n horizontal.push({ position: 0, kind: 'page-edge' })\n horizontal.push({ position: page.height, kind: 'page-edge' })\n horizontal.push({ position: page.height / 2, kind: 'page-center' })\n\n // Saved ruler guides\n for (const pos of page.guides.vertical) {\n vertical.push({ position: pos, kind: 'guide' })\n }\n for (const pos of page.guides.horizontal) {\n horizontal.push({ position: pos, kind: 'guide' })\n }\n\n // Element edges + centers (visible, unlocked, not excluded)\n collectElementTargets(page.elements, excludeSet, vertical, horizontal)\n\n // Grid lines (deferred to apply() with the moving bounds neighborhood)\n // Grid targets are injected by collectGridTargets() when gridEnabled.\n\n return { vertical, horizontal }\n },\n\n apply(bounds: Bounds, targets: SnapTargets, thresholdPx: number, zoom: number): SnapResult {\n if (!Number.isFinite(zoom) || zoom <= 0) {\n throw new Error(`snap.apply: zoom must be a positive finite number, got ${zoom}`)\n }\n if (!Number.isFinite(thresholdPx) || thresholdPx < 0) {\n throw new Error(`snap.apply: thresholdPx must be a non-negative finite number, got ${thresholdPx}`)\n }\n const threshold = thresholdPx / zoom\n\n const snappedX = snapAxis(bounds.x, bounds.x + bounds.width / 2, bounds.x + bounds.width, targets.vertical, threshold)\n const snappedY = snapAxis(bounds.y, bounds.y + bounds.height / 2, bounds.y + bounds.height, targets.horizontal, threshold)\n\n return {\n x: snappedX !== null ? snappedX.docPosition - snappedX.elementOffset : bounds.x,\n y: snappedY !== null ? snappedY.docPosition - snappedY.elementOffset : bounds.y,\n activeVertical: snappedX !== null ? snappedX.target : null,\n activeHorizontal: snappedY !== null ? snappedY.target : null,\n }\n },\n }\n}\n\n/** Generate grid line targets within a neighborhood around the moving bounds.\n * Call this and append to `SnapTargets.vertical`/`horizontal` before\n * passing to `apply()` when `state.gridEnabled`. */\nexport function collectGridTargets(\n bounds: Bounds,\n gridSize: number,\n page: ScenePage,\n thresholdDocPx: number,\n): { vertical: SnapTarget[]; horizontal: SnapTarget[] } {\n if (!Number.isFinite(gridSize) || gridSize <= 0) {\n throw new Error(`collectGridTargets: gridSize must be positive finite, got ${gridSize}`)\n }\n const vertical: SnapTarget[] = []\n const horizontal: SnapTarget[] = []\n\n // Generate lines that fall within the bounds + threshold neighborhood\n const xMin = Math.floor((bounds.x - thresholdDocPx) / gridSize) * gridSize\n const xMax = Math.ceil((bounds.x + bounds.width + thresholdDocPx) / gridSize) * gridSize\n for (let x = xMin; x <= xMax; x += gridSize) {\n if (x >= 0 && x <= page.width) vertical.push({ position: x, kind: 'grid' })\n }\n\n const yMin = Math.floor((bounds.y - thresholdDocPx) / gridSize) * gridSize\n const yMax = Math.ceil((bounds.y + bounds.height + thresholdDocPx) / gridSize) * gridSize\n for (let y = yMin; y <= yMax; y += gridSize) {\n if (y >= 0 && y <= page.height) horizontal.push({ position: y, kind: 'grid' })\n }\n\n return { vertical, horizontal }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\ninterface SnapHit {\n /** The snap target that won. */\n target: SnapTarget\n /** The document-coordinate position of the snap line. */\n docPosition: number\n /** How far the element's reference point (left/center/right or top/mid/bottom)\n * is from the element's x/y origin — used to reconstruct the new origin. */\n elementOffset: number\n}\n\n/** Try snapping each of the three reference points (start/center/end) to the\n * nearest target. Returns the best hit across all three, or null. */\nfunction snapAxis(start: number, center: number, end: number, targets: SnapTarget[], threshold: number): SnapHit | null {\n const candidates: Array<{ value: number; offset: number }> = [\n { value: start, offset: 0 },\n { value: center, offset: center - start },\n { value: end, offset: end - start },\n ]\n\n let best: SnapHit | null = null\n let bestDistance = Infinity\n let bestPriority = Infinity\n\n for (const { value, offset } of candidates) {\n for (const target of targets) {\n const distance = Math.abs(target.position - value)\n if (distance > threshold) continue\n const priority = KIND_PRIORITY[target.kind]\n if (\n distance < bestDistance ||\n (distance === bestDistance && priority < bestPriority)\n ) {\n bestDistance = distance\n bestPriority = priority\n best = { target, docPosition: target.position, elementOffset: offset }\n }\n }\n }\n\n return best\n}\n\nfunction collectElementTargets(\n elements: SceneElement[],\n excludeIds: Set<string>,\n vertical: SnapTarget[],\n horizontal: SnapTarget[],\n): void {\n for (const el of elements) {\n if (!el.visible || el.locked) continue\n if (excludeIds.has(el.id)) continue\n\n const aabb = elementAabb(el)\n // Left, center, right\n vertical.push({ position: aabb.x, kind: 'element-edge' })\n vertical.push({ position: aabb.x + aabb.width / 2, kind: 'element-center' })\n vertical.push({ position: aabb.x + aabb.width, kind: 'element-edge' })\n // Top, middle, bottom\n horizontal.push({ position: aabb.y, kind: 'element-edge' })\n horizontal.push({ position: aabb.y + aabb.height / 2, kind: 'element-center' })\n horizontal.push({ position: aabb.y + aabb.height, kind: 'element-edge' })\n\n if (el.kind === 'group') {\n collectElementTargets(el.children, excludeIds, vertical, horizontal)\n }\n }\n}\n","/**\n * Zoom + pan coordinate math for the design-canvas editor. Zoom is pixels-per-\n * document-px (e.g. 2 = 200% magnification). The key invariant for wheel-zoom\n * is that the document point under the cursor stays fixed in screen space:\n *\n * docPoint = (screenPoint - pan) / zoom\n * newPan = screenPoint - docPoint * newZoom\n *\n * This module is pure math — no DOM, no Konva, no React.\n */\n\nimport type { ZoomPanMath } from '../contracts'\n\nexport interface ZoomPanConfig {\n minZoom: number\n maxZoom: number\n}\n\nexport function createZoomPanMath(config: ZoomPanConfig): ZoomPanMath {\n const { minZoom, maxZoom } = config\n if (!Number.isFinite(minZoom) || minZoom <= 0) {\n throw new Error(`minZoom must be a positive finite number, got ${minZoom}`)\n }\n if (!Number.isFinite(maxZoom) || maxZoom <= minZoom) {\n throw new Error(`maxZoom must be finite and greater than minZoom (${minZoom}), got ${maxZoom}`)\n }\n\n return {\n minZoom,\n maxZoom,\n\n zoomAtPoint(\n state: { zoom: number; panX: number; panY: number },\n factor: number,\n screenX: number,\n screenY: number,\n ): { zoom: number; panX: number; panY: number } {\n if (!Number.isFinite(factor) || factor <= 0) {\n throw new Error(`zoomAtPoint: factor must be a positive finite number, got ${factor}`)\n }\n if (!Number.isFinite(screenX) || !Number.isFinite(screenY)) {\n throw new Error(`zoomAtPoint: screenX/screenY must be finite numbers`)\n }\n if (!Number.isFinite(state.zoom) || state.zoom <= 0) {\n throw new Error(`zoomAtPoint: state.zoom must be a positive finite number, got ${state.zoom}`)\n }\n\n const newZoom = Math.min(maxZoom, Math.max(minZoom, state.zoom * factor))\n\n // The document point under the cursor must remain fixed:\n // docX = (screenX - panX) / zoom\n // newPanX = screenX - docX * newZoom\n const docX = (screenX - state.panX) / state.zoom\n const docY = (screenY - state.panY) / state.zoom\n const panX = screenX - docX * newZoom\n const panY = screenY - docY * newZoom\n\n return { zoom: newZoom, panX, panY }\n },\n\n fitPage(\n page: { width: number; height: number },\n viewport: { width: number; height: number },\n paddingPx = 48,\n ): { zoom: number; panX: number; panY: number } {\n if (!Number.isFinite(page.width) || page.width <= 0) {\n throw new Error(`fitPage: page.width must be a positive finite number, got ${page.width}`)\n }\n if (!Number.isFinite(page.height) || page.height <= 0) {\n throw new Error(`fitPage: page.height must be a positive finite number, got ${page.height}`)\n }\n if (!Number.isFinite(viewport.width) || viewport.width <= 0) {\n throw new Error(`fitPage: viewport.width must be a positive finite number, got ${viewport.width}`)\n }\n if (!Number.isFinite(viewport.height) || viewport.height <= 0) {\n throw new Error(`fitPage: viewport.height must be a positive finite number, got ${viewport.height}`)\n }\n if (!Number.isFinite(paddingPx) || paddingPx < 0) {\n throw new Error(`fitPage: paddingPx must be a non-negative finite number, got ${paddingPx}`)\n }\n\n const availW = viewport.width - paddingPx * 2\n const availH = viewport.height - paddingPx * 2\n const zoom = Math.min(maxZoom, Math.max(minZoom, Math.min(availW / page.width, availH / page.height)))\n\n // Center the page in the viewport\n const panX = (viewport.width - page.width * zoom) / 2\n const panY = (viewport.height - page.height * zoom) / 2\n\n return { zoom, panX, panY }\n },\n\n documentToScreen(\n state: { zoom: number; panX: number; panY: number },\n x: number,\n y: number,\n ): { x: number; y: number } {\n return { x: x * state.zoom + state.panX, y: y * state.zoom + state.panY }\n },\n\n screenToDocument(\n state: { zoom: number; panX: number; panY: number },\n x: number,\n y: number,\n ): { x: number; y: number } {\n if (!Number.isFinite(state.zoom) || state.zoom === 0) {\n throw new Error(`screenToDocument: zoom must be a non-zero finite number, got ${state.zoom}`)\n }\n return { x: (x - state.panX) / state.zoom, y: (y - state.panY) / state.zoom }\n },\n }\n}\n","/**\n * Selection math for the design-canvas editor: marquee hit-tests, keyboard\n * nudge deltas, and the duplicate offset constant. Pure functions — no DOM, no\n * Konva, no React.\n *\n * Marquee inclusion: by default any element whose AABB intersects the marquee\n * rect is included (the \"touch\" model). Pass `requireFullContainment: true` for\n * the \"surround\" model where the marquee must fully contain the element. Locked\n * and invisible elements are never selected regardless.\n */\n\nimport { boundsIntersect, elementAabb } from '../../design-canvas/model'\nimport type { Bounds, SceneElement, ScenePage } from '../../design-canvas/model'\n\nexport interface MarqueeSelectOptions {\n /** When true, the element's AABB must be fully inside the marquee; default is\n * intersection (any overlap selects). */\n requireFullContainment?: boolean\n}\n\n/** Returns the ids of selectable elements on `page` whose AABB intersects (or\n * is contained by) `rect`. Locked and invisible elements are excluded. */\nexport function marqueeSelect(page: ScenePage, rect: Bounds, opts: MarqueeSelectOptions = {}): string[] {\n assertValidBounds(rect, 'marqueeSelect rect')\n const result: string[] = []\n collectMarqueeIds(page.elements, rect, opts.requireFullContainment ?? false, result)\n return result\n}\n\nfunction collectMarqueeIds(\n elements: SceneElement[],\n rect: Bounds,\n requireContainment: boolean,\n result: string[],\n): void {\n for (const el of elements) {\n if (!el.visible || el.locked) continue\n const aabb = elementAabb(el)\n\n if (el.kind === 'group') {\n // Groups are transparent to marquee: descend when the group AABB intersects\n // the rect, collecting children individually. The group entity itself is\n // never added — marquee selects leaf elements, not the group wrapper.\n if (boundsIntersect(rect, aabb)) {\n collectMarqueeIds(el.children, rect, requireContainment, result)\n }\n continue\n }\n\n const hit = requireContainment ? boundsContain(rect, aabb) : boundsIntersect(rect, aabb)\n if (hit) result.push(el.id)\n }\n}\n\n/** Returns true when `outer` fully contains `inner` (edges may touch). */\nfunction boundsContain(outer: Bounds, inner: Bounds): boolean {\n return (\n inner.x >= outer.x &&\n inner.y >= outer.y &&\n inner.x + inner.width <= outer.x + outer.width &&\n inner.y + inner.height <= outer.y + outer.height\n )\n}\n\n// ---------------------------------------------------------------------------\n// Keyboard nudge\n// ---------------------------------------------------------------------------\n\n/** Pixels to move per nudge step at normal and shifted speed. */\nconst NUDGE_NORMAL_PX = 1\nconst NUDGE_SHIFT_PX = 10\n\nexport interface NudgeDelta {\n dx: number\n dy: number\n}\n\n/** Map an arrow key to a document-px delta. `shift` engages the 10× step.\n * Throws on unknown keys so callers handle only real nudge keys. */\nexport function nudgeDelta(key: 'ArrowLeft' | 'ArrowRight' | 'ArrowUp' | 'ArrowDown', shift: boolean): NudgeDelta {\n const step = shift ? NUDGE_SHIFT_PX : NUDGE_NORMAL_PX\n switch (key) {\n case 'ArrowLeft': return { dx: -step, dy: 0 }\n case 'ArrowRight': return { dx: step, dy: 0 }\n case 'ArrowUp': return { dx: 0, dy: -step }\n case 'ArrowDown': return { dx: 0, dy: step }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Duplicate offset\n// ---------------------------------------------------------------------------\n\n/** Document-px offset applied to duplicated elements so the copy is visually\n * separated from the original (matching common design-tool convention). */\nexport const DUPLICATE_OFFSET: NudgeDelta = { dx: 10, dy: 10 }\n\n// ---------------------------------------------------------------------------\n// Internal\n// ---------------------------------------------------------------------------\n\nfunction assertValidBounds(bounds: Bounds, label: string): void {\n if (!Number.isFinite(bounds.x) || !Number.isFinite(bounds.y) ||\n !Number.isFinite(bounds.width) || !Number.isFinite(bounds.height)) {\n throw new Error(`${label}: all fields must be finite numbers`)\n }\n}\n","/**\n * Per-kind Konva node rendering for SceneElement. Each element kind maps to\n * the appropriate Konva primitive; geometry is converted from the model's\n * top-left convention where needed.\n *\n * Ellipse center-offset invariant: the model stores (x, y) as the top-left\n * corner of the bounding box. Konva.Ellipse draws from its center point.\n * The conversion is: centerX = x + width/2, centerY = y + height/2,\n * radiusX = width/2, radiusY = height/2. The reverse applies when reading\n * back from transformer output (see transform-math.ts:ellipseTopLeftFromCenter).\n *\n * Image loading: src is loaded async into an HTMLImageElement via useEffect,\n * cached per src in a module-level Map so repeated renders of the same src\n * don't re-fetch. A broken-image placeholder rect is shown while loading or on\n * error; its `name` carries the src for diagnostics.\n *\n * Video elements render as their poster image when posterSrc is set, or a\n * placeholder rect. This is intentional — motion belongs to the sequences\n * surface; the canvas surface is for static layout.\n *\n * Node name: each node's `name` prop carries the element id so hit→model\n * mapping in Workspace works: `stage.getIntersection(pos)?.name()`.\n *\n * locked → listening(false) except on the click handler for selection.\n * hidden → not rendered.\n */\n\nimport { useEffect, useRef, useState } from 'react'\nimport { Group, Rect, Ellipse, Line, Text, Image as KonvaImage } from 'react-konva'\nimport { ellipseCenterFromTopLeft } from './transform-math'\nimport type {\n SceneElement,\n RectElement,\n EllipseElement,\n LineElement,\n TextElement,\n ImageElement,\n VideoElement,\n GroupElement,\n} from '../../design-canvas/model'\n\n// ---------------------------------------------------------------------------\n// Image cache — module-level so it survives re-renders\n// ---------------------------------------------------------------------------\n\n// LRU bound: large asset libraries with many unique src URLs would accumulate\n// held HTMLImageElement objects indefinitely without eviction. 256 entries\n// covers any reasonable single-session use and keeps memory predictable.\nconst IMAGE_CACHE_MAX = 256\n\nconst imageCache = new Map<string, HTMLImageElement>()\n\n/** Evict the least-recently-inserted entry when the cache is at capacity.\n * Map iteration order is insertion order, so the first key is the oldest. */\nfunction imageCacheSet(src: string, img: HTMLImageElement): void {\n if (imageCache.size >= IMAGE_CACHE_MAX) {\n const oldest = imageCache.keys().next().value\n if (oldest !== undefined) imageCache.delete(oldest)\n }\n imageCache.set(src, img)\n}\n\nfunction useImage(src: string): HTMLImageElement | null {\n const [, setVersion] = useState(0)\n\n useEffect(() => {\n if (imageCache.has(src)) return\n const img = new window.Image()\n img.crossOrigin = 'anonymous'\n img.onload = () => {\n imageCacheSet(src, img)\n setVersion((v) => v + 1)\n }\n img.onerror = () => {\n // Store a sentinel so we don't retry infinitely; placeholder renders instead.\n imageCacheSet(src, img)\n setVersion((v) => v + 1)\n }\n img.src = src\n }, [src])\n\n return imageCache.get(src) ?? null\n}\n\n// ---------------------------------------------------------------------------\n// Element node dispatcher\n// ---------------------------------------------------------------------------\n\nexport interface ElementNodeProps {\n element: SceneElement\n isSelected: boolean\n zoom: number\n onClick?(elementId: string): void\n onDragStart?(elementId: string): void\n onDragMove?(elementId: string, dx: number, dy: number): void\n onDragEnd?(elementId: string, finalX: number, finalY: number): void\n onDoubleClick?(elementId: string): void\n}\n\nexport function ElementNode(props: ElementNodeProps) {\n const { element } = props\n if (!element.visible) return null\n\n switch (element.kind) {\n case 'rect': return <RectNode {...props} element={element} />\n case 'ellipse': return <EllipseNode {...props} element={element} />\n case 'line': return <LineNode {...props} element={element} />\n case 'text': return <TextNode {...props} element={element} />\n case 'image': return <ImageNode {...props} element={element} />\n case 'video': return <VideoNode {...props} element={element} />\n case 'group': return <GroupNode {...props} element={element} />\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared drag handlers factory\n// ---------------------------------------------------------------------------\n\ninterface DragBindings {\n draggable: boolean\n listening: boolean\n onDragStart?: (e: { target: { x(): number; y(): number } }) => void\n onDragMove?: (e: { target: { x(): number; y(): number } }) => void\n onDragEnd?: (e: { target: { x(): number; y(): number } }) => void\n onClick?: (e: unknown) => void\n onDblClick?: (e: unknown) => void\n}\n\nfunction useDragBindings(props: ElementNodeProps, originX: number, originY: number): DragBindings {\n const { element, onClick, onDragStart, onDragMove, onDragEnd, onDoubleClick } = props\n const isDraggable = !element.locked && !!onDragEnd\n const originRef = useRef({ x: originX, y: originY })\n originRef.current = { x: originX, y: originY }\n\n return {\n draggable: isDraggable,\n // locked elements still receive click for selection; listening must be true.\n listening: true,\n onDragStart: isDraggable ? () => { onDragStart?.(element.id) } : undefined,\n onDragMove: isDraggable ? (e: { target: { x(): number; y(): number } }) => {\n onDragMove?.(element.id, e.target.x() - originRef.current.x, e.target.y() - originRef.current.y)\n } : undefined,\n onDragEnd: isDraggable ? (e: { target: { x(): number; y(): number } }) => {\n onDragEnd?.(element.id, e.target.x(), e.target.y())\n } : undefined,\n onClick: () => onClick?.(element.id),\n onDblClick: () => onDoubleClick?.(element.id),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Rect\n// ---------------------------------------------------------------------------\n\nfunction RectNode({ element, ...rest }: ElementNodeProps & { element: RectElement }) {\n const drag = useDragBindings({ element, ...rest }, element.x, element.y)\n return (\n <Rect\n name={element.id}\n x={element.x}\n y={element.y}\n width={element.width}\n height={element.height}\n rotation={element.rotation}\n opacity={element.opacity}\n fill={element.fill}\n stroke={element.stroke}\n strokeWidth={element.strokeWidth}\n cornerRadius={element.cornerRadius ?? 0}\n {...drag}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Ellipse\n// ---------------------------------------------------------------------------\n\nfunction EllipseNode({ element, ...rest }: ElementNodeProps & { element: EllipseElement }) {\n // Model: top-left (x, y) + width/height bounding box.\n // Konva.Ellipse: center (x, y) + radiusX/radiusY.\n const { x: cx, y: cy, radiusX, radiusY } = ellipseCenterFromTopLeft(element)\n const drag = useDragBindings({ element, ...rest }, element.x, element.y)\n\n return (\n <Ellipse\n name={element.id}\n x={cx}\n y={cy}\n radiusX={radiusX}\n radiusY={radiusY}\n rotation={element.rotation}\n opacity={element.opacity}\n fill={element.fill}\n stroke={element.stroke}\n strokeWidth={element.strokeWidth}\n {...drag}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Line\n// ---------------------------------------------------------------------------\n\nfunction LineNode({ element, ...rest }: ElementNodeProps & { element: LineElement }) {\n const drag = useDragBindings({ element, ...rest }, element.x, element.y)\n return (\n <Line\n name={element.id}\n x={element.x}\n y={element.y}\n points={element.points}\n rotation={element.rotation}\n opacity={element.opacity}\n stroke={element.stroke}\n strokeWidth={element.strokeWidth}\n dash={element.dash}\n {...drag}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Text\n// ---------------------------------------------------------------------------\n\nconst FONT_STYLE_MAP: Record<TextElement['fontStyle'], { fontStyle: string; fontVariant: string; fontWeight: string }> = {\n 'normal': { fontStyle: 'normal', fontVariant: 'normal', fontWeight: 'normal' },\n 'bold': { fontStyle: 'normal', fontVariant: 'normal', fontWeight: 'bold' },\n 'italic': { fontStyle: 'italic', fontVariant: 'normal', fontWeight: 'normal' },\n 'bold italic': { fontStyle: 'italic', fontVariant: 'normal', fontWeight: 'bold' },\n}\n\nfunction TextNode({ element, ...rest }: ElementNodeProps & { element: TextElement }) {\n const drag = useDragBindings({ element, ...rest }, element.x, element.y)\n const { fontStyle, fontWeight } = FONT_STYLE_MAP[element.fontStyle]\n\n return (\n <Text\n name={element.id}\n x={element.x}\n y={element.y}\n width={element.width}\n rotation={element.rotation}\n opacity={element.opacity}\n text={element.text}\n fontFamily={element.fontFamily}\n fontSize={element.fontSize}\n fontStyle={`${fontStyle} ${fontWeight}`.trim()}\n fill={element.fill}\n align={element.align}\n lineHeight={element.lineHeight}\n letterSpacing={element.letterSpacing}\n {...drag}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Image\n// ---------------------------------------------------------------------------\n\nconst BROKEN_FILL = '#e5e7eb'\nconst BROKEN_STROKE = '#9ca3af'\n\nfunction ImageNode({ element, ...rest }: ElementNodeProps & { element: ImageElement }) {\n const img = useImage(element.src)\n const drag = useDragBindings({ element, ...rest }, element.x, element.y)\n\n if (!img || !img.complete || img.naturalWidth === 0) {\n // Placeholder while loading or broken: a grey rect with the src in its name.\n return (\n <Rect\n name={element.id}\n x={element.x}\n y={element.y}\n width={element.width}\n height={element.height}\n rotation={element.rotation}\n opacity={element.opacity}\n fill={BROKEN_FILL}\n stroke={BROKEN_STROKE}\n strokeWidth={1}\n {...drag}\n />\n )\n }\n\n // Konva handles crop-to-fit natively via crop attrs; we compute them for\n // 'cover' mode. 'contain' and 'fill' use default stretch behavior.\n let cropProps: object = {}\n if (element.fit === 'cover') {\n const srcAspect = img.naturalWidth / img.naturalHeight\n const dstAspect = element.width / element.height\n if (srcAspect > dstAspect) {\n // Source is wider — crop horizontally\n const visibleW = img.naturalHeight * dstAspect\n cropProps = {\n crop: {\n x: (img.naturalWidth - visibleW) / 2,\n y: 0,\n width: visibleW,\n height: img.naturalHeight,\n },\n }\n } else {\n // Source is taller — crop vertically\n const visibleH = img.naturalWidth / dstAspect\n cropProps = {\n crop: {\n x: 0,\n y: (img.naturalHeight - visibleH) / 2,\n width: img.naturalWidth,\n height: visibleH,\n },\n }\n }\n }\n\n return (\n <KonvaImage\n name={element.id}\n x={element.x}\n y={element.y}\n width={element.width}\n height={element.height}\n rotation={element.rotation}\n opacity={element.opacity}\n image={img}\n {...cropProps}\n {...drag}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Video (renders poster or placeholder — no playback on the canvas surface)\n// ---------------------------------------------------------------------------\n\nfunction VideoNode({ element, ...rest }: ElementNodeProps & { element: VideoElement }) {\n // Poster image path — never silently drop; show placeholder when absent.\n const img = useImage(element.posterSrc ?? '')\n const drag = useDragBindings({ element, ...rest }, element.x, element.y)\n\n if (!element.posterSrc || !img || !img.complete || img.naturalWidth === 0) {\n return (\n <Rect\n name={element.id}\n x={element.x}\n y={element.y}\n width={element.width}\n height={element.height}\n rotation={element.rotation}\n opacity={element.opacity}\n fill=\"#1f2937\"\n stroke=\"#374151\"\n strokeWidth={1}\n {...drag}\n />\n )\n }\n\n return (\n <KonvaImage\n name={element.id}\n x={element.x}\n y={element.y}\n width={element.width}\n height={element.height}\n rotation={element.rotation}\n opacity={element.opacity}\n image={img}\n {...drag}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// Group (recursive)\n// ---------------------------------------------------------------------------\n\nfunction GroupNode({ element, ...rest }: ElementNodeProps & { element: GroupElement }) {\n const drag = useDragBindings({ element, ...rest }, element.x, element.y)\n return (\n <Group\n name={element.id}\n x={element.x}\n y={element.y}\n rotation={element.rotation}\n opacity={element.opacity}\n {...drag}\n >\n {element.children.map((child) => (\n <ElementNode key={child.id} {...rest} element={child} />\n ))}\n </Group>\n )\n}\n","/**\n * Pure geometry helpers extracted from component drag/transform logic so\n * every interactive math path is unit-testable without Konva or a DOM.\n *\n * Invariants:\n * - All inputs and outputs are in document-coordinate pixels unless noted.\n * - Rotation angles are degrees, clockwise, matching the Konva + model convention.\n * - \"Baking\" scale into width/height resets Konva's scaleX/scaleY to 1; that\n * is required so the model's width/height always reflects true size, never a\n * scaled-but-uncollapsed state.\n */\n\n// ---------------------------------------------------------------------------\n// Konva transformer end-state baking\n// ---------------------------------------------------------------------------\n\nexport interface TransformerNode {\n x: number\n y: number\n width: number\n height: number\n /** Konva's scale after the transformer gesture; 1 when already baked. */\n scaleX: number\n scaleY: number\n rotation: number\n}\n\nexport interface BakedNodeAttrs {\n x: number\n y: number\n /** True pixel size after scale is collapsed into dimensions. */\n width: number\n height: number\n rotation: number\n}\n\n/**\n * Collapse Konva scaleX/scaleY into width/height so the model always stores\n * true pixel dimensions. The transformer mutates scale on drag; we bake it\n * once at dragend/transformend and emit width/height — scale resets to 1\n * implicitly (the emitted attrs do not include scale, so the next render\n * starts from scaleX=1).\n *\n * Konva rotates about the top-left origin and also shifts x/y to compensate\n * for scale — the transformer gives us the post-rotation, post-scale x/y\n * directly, so no further rotation math is needed here.\n */\nexport function bakeRectTransform(node: TransformerNode): BakedNodeAttrs {\n return {\n x: node.x,\n y: node.y,\n width: Math.abs(node.width * node.scaleX),\n height: Math.abs(node.height * node.scaleY),\n rotation: node.rotation,\n }\n}\n\n/**\n * Lines store points as a flat [x0, y0, x1, y1, ...] array relative to the\n * line element's (x, y). When the transformer scales the group containing the\n * line, we bake scaleX into every x-component of points and scaleY into every\n * y-component, then reset scale to 1. The element x/y is the Konva group\n * origin and is taken directly from the transformer output.\n */\nexport function bakeLineTransform(node: TransformerNode & { points: number[] }): BakedNodeAttrs & { points: number[] } {\n const points = node.points.map((v, i) => (i % 2 === 0 ? v * node.scaleX : v * node.scaleY))\n return {\n x: node.x,\n y: node.y,\n // Width/height for a line derive from its points; baking is for points only.\n // We still return them so callers have a uniform shape.\n width: node.width * Math.abs(node.scaleX),\n height: node.height * Math.abs(node.scaleY),\n rotation: node.rotation,\n points,\n }\n}\n\n/**\n * Text nodes have a fixed wrap width; scaling that width is what the\n * transformer controls. Height is content-derived and is NOT baked here —\n * it re-derives from text content at render time via estimateTextHeight.\n * Only scaleX is baked into width; scaleY into fontSize so text scales\n * proportionally when the user resizes with keepRatio.\n */\nexport function bakeTextTransform(\n node: TransformerNode & { fontSize: number },\n): BakedNodeAttrs & { fontSize: number } {\n return {\n x: node.x,\n y: node.y,\n width: Math.abs(node.width * node.scaleX),\n // Height is excluded — it re-derives from content.\n height: node.height,\n rotation: node.rotation,\n fontSize: Math.max(1, node.fontSize * Math.abs(node.scaleY)),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Ellipse center-offset math\n// ---------------------------------------------------------------------------\n\n/**\n * The model stores ellipse position as the top-left corner of the bounding\n * box (matching every other element kind). Konva.Ellipse draws from its center\n * (radiusX, radiusY). This converts model top-left to Konva center.\n *\n * INVARIANT: radiusX = width/2, radiusY = height/2; x/y offsets are exactly\n * half the dimensions because there is no separate offset field.\n */\nexport function ellipseCenterFromTopLeft(\n topLeft: { x: number; y: number; width: number; height: number },\n): { x: number; y: number; radiusX: number; radiusY: number } {\n return {\n x: topLeft.x + topLeft.width / 2,\n y: topLeft.y + topLeft.height / 2,\n radiusX: topLeft.width / 2,\n radiusY: topLeft.height / 2,\n }\n}\n\n/**\n * Inverse: Konva center + radius → model top-left. Used when reading back\n * from a transformer node whose origin is the Konva center.\n */\nexport function ellipseTopLeftFromCenter(\n center: { x: number; y: number; radiusX: number; radiusY: number },\n): { x: number; y: number; width: number; height: number } {\n return {\n x: center.x - center.radiusX,\n y: center.y - center.radiusY,\n width: center.radiusX * 2,\n height: center.radiusY * 2,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Marquee selection\n// ---------------------------------------------------------------------------\n\nexport interface MarqueeRect {\n x: number\n y: number\n width: number\n height: number\n}\n\n/**\n * Normalize a marquee rectangle from two arbitrary corners into a canonical\n * top-left origin with positive width/height, regardless of drag direction.\n */\nexport function normalizeMarquee(\n startX: number,\n startY: number,\n endX: number,\n endY: number,\n): MarqueeRect {\n const x = Math.min(startX, endX)\n const y = Math.min(startY, endY)\n return {\n x,\n y,\n width: Math.abs(endX - startX),\n height: Math.abs(endY - startY),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Inline text editor overlay positioning\n// ---------------------------------------------------------------------------\n\nexport interface OverlayPositionInput {\n /** Element top-left in document coordinates. */\n elementX: number\n elementY: number\n elementWidth: number\n elementHeight: number\n /** Current zoom and pan. */\n zoom: number\n panX: number\n panY: number\n /** Stage offset in the viewport (e.g. from stage.container().getBoundingClientRect). */\n stageLeft: number\n stageTop: number\n}\n\nexport interface OverlayPosition {\n /** CSS left/top for the overlay textarea relative to the canvas container. */\n left: number\n top: number\n width: number\n /** fontSize to mirror from the element, scaled by zoom. */\n fontSize: number\n}\n\n/**\n * Compute the screen-space position for an inline text editor overlay.\n *\n * V1 simplification: rotated text elements are edited in a non-rotated overlay\n * positioned at the element's AABB. This means the overlay does not visually\n * align with the rotated text, but keeps the textarea DOM element axis-aligned\n * which avoids browser textarea rotation bugs. A future v2 may apply a CSS\n * transform to the textarea to match rotation.\n */\nexport function computeTextOverlayPosition(\n input: OverlayPositionInput & { elementFontSize: number },\n): OverlayPosition {\n // Document-to-screen: screenX = panX + elementX * zoom\n const left = input.panX + input.elementX * input.zoom + input.stageLeft\n const top = input.panY + input.elementY * input.zoom + input.stageTop\n return {\n left,\n top,\n width: input.elementWidth * input.zoom,\n fontSize: input.elementFontSize * input.zoom,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Rotation snapping\n// ---------------------------------------------------------------------------\n\nconst SNAP_ANGLES_DEG = [0, 45, 90, 135, 180, 225, 270, 315, 360]\n\n/**\n * Snap a rotation angle to the nearest cardinal/diagonal if within\n * `thresholdDeg` of it (default 5°). Returns the original value when no snap\n * activates. Normalizes output to [0, 360).\n */\nexport function snapRotation(angleDeg: number, thresholdDeg = 5): number {\n const normalized = ((angleDeg % 360) + 360) % 360\n let best = normalized\n let bestDist = Infinity\n for (const snap of SNAP_ANGLES_DEG) {\n const dist = Math.abs(normalized - snap)\n if (dist < bestDist) {\n bestDist = dist\n best = snap % 360\n }\n }\n return bestDist <= thresholdDeg ? best : normalized\n}\n\n// ---------------------------------------------------------------------------\n// Nudge delta\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a keyboard arrow event into a {dx, dy} nudge in document px.\n * Shift multiplies by 10 (the \"big nudge\" convention).\n */\nexport function nudgeDelta(\n key: 'ArrowLeft' | 'ArrowRight' | 'ArrowUp' | 'ArrowDown',\n shift: boolean,\n): { dx: number; dy: number } {\n const step = shift ? 10 : 1\n switch (key) {\n case 'ArrowLeft': return { dx: -step, dy: 0 }\n case 'ArrowRight': return { dx: step, dy: 0 }\n case 'ArrowUp': return { dx: 0, dy: -step }\n case 'ArrowDown': return { dx: 0, dy: step }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Marquee hit-test\n// ---------------------------------------------------------------------------\n\n/**\n * Return true when bounds B is entirely contained within marquee A.\n * Partial intersection does NOT select — the marquee must fully enclose.\n */\nexport function marqueeContains(marquee: MarqueeRect, bounds: MarqueeRect): boolean {\n return (\n bounds.x >= marquee.x &&\n bounds.y >= marquee.y &&\n bounds.x + bounds.width <= marquee.x + marquee.width &&\n bounds.y + bounds.height <= marquee.y + marquee.height\n )\n}\n\n// ---------------------------------------------------------------------------\n// Grid ruler tick density\n// ---------------------------------------------------------------------------\n\n/**\n * Return true when grid lines at `gridSize` document px would render at least\n * `minScreenPx` apart at the current zoom. Used to skip grid drawing when\n * zoomed out far enough that lines would clutter.\n */\nexport function gridVisible(gridSize: number, zoom: number, minScreenPx = 4): boolean {\n return gridSize * zoom >= minScreenPx\n}\n","/**\n * Transformer-based selection layer. Attaches a Konva.Transformer to all\n * currently-selected Konva nodes so the user can resize and rotate them.\n *\n * SINGLE vs. MULTI:\n * - Single selection: resize + rotate.\n * - Multi-selection: rigid resize (keepRatio enforced) + rotate.\n *\n * Rotation snap: within 5° of 0/45/90/135/180 the rotation locks to the\n * nearest cardinal/diagonal (same threshold as the snapRotation pure function).\n *\n * Scale baking: Konva's Transformer sets scaleX/scaleY on the node during\n * a transform gesture. On transformend we read the final x/y/width/height/\n * scaleX/scaleY/rotation from each node, bake scale into width/height, and\n * emit ONE multiSetAttrsCommand. Scale never persists on Konva nodes across\n * render cycles — the next render resets scaleX=scaleY=1 by rendering from\n * model attrs.\n *\n * Special baking per kind:\n * - rect/ellipse/image/video/group: bakeRectTransform (scale → width/height).\n * - line: bakeLineTransform (scale baked into points array).\n * - text: bakeTextTransform (scaleX→width, scaleY→fontSize).\n * - ellipse: center position is adjusted back to top-left model coords via\n * ellipseTopLeftFromCenter before emitting attrs.\n *\n * Min-size guard: width/height are clamped to ≥ 4px after baking so users\n * cannot collapse an element to zero and lose it.\n *\n * The overlay: prefix on the Transformer name lets export logic skip it.\n */\n\nimport { useEffect, useRef } from 'react'\nimport { Layer, Transformer } from 'react-konva'\nimport type Konva from 'konva'\nimport {\n bakeRectTransform,\n bakeLineTransform,\n bakeTextTransform,\n ellipseTopLeftFromCenter,\n snapRotation,\n} from './transform-math'\nimport type { MultiSetAttrsEntry } from '../engine/commands'\nimport type { SceneAttrsPatch } from '../../design-canvas/operations'\nimport type { SceneElement } from '../../design-canvas/model'\n\nexport interface SelectionLayerProps {\n /** Konva stage reference to look up selected nodes by name. */\n stageRef: React.RefObject<Konva.Stage | null>\n /** Selected element ids from editor state. */\n selectedIds: string[]\n /** The model elements corresponding to selectedIds (pre-gesture snapshot). */\n selectedElements: SceneElement[]\n /** Whether the canvas is writable. False → transformer renders but is not interactive. */\n canWrite: boolean\n /** Emitted when a transform gesture completes with final attrs per element. */\n onTransformEnd(entries: MultiSetAttrsEntry[]): void\n /** Active page id — every entry in onTransformEnd carries this. */\n pageId: string\n}\n\nconst MIN_SIZE = 4\n\nexport function SelectionLayer({\n stageRef,\n selectedIds,\n selectedElements,\n canWrite,\n onTransformEnd,\n pageId,\n}: SelectionLayerProps) {\n const trRef = useRef<Konva.Transformer | null>(null)\n\n // Sync the transformer's attached nodes whenever selection changes.\n useEffect(() => {\n const tr = trRef.current\n const stage = stageRef.current\n if (!tr || !stage) return\n\n const nodes: Konva.Node[] = []\n for (const id of selectedIds) {\n // Nodes carry their element id as their `name` prop.\n const node = stage.findOne(`[name=\"${id}\"]`)\n if (node) nodes.push(node)\n }\n tr.nodes(nodes)\n tr.getLayer()?.batchDraw()\n }, [selectedIds, stageRef])\n\n function handleTransformEnd() {\n const tr = trRef.current\n if (!tr) return\n const nodes = tr.nodes() as Konva.Node[]\n if (nodes.length === 0) return\n\n const entries: MultiSetAttrsEntry[] = []\n\n for (const node of nodes) {\n const elementId = node.name()\n const element = selectedElements.find((e) => e.id === elementId)\n if (!element) continue\n\n const priorAttrs = elementToPriorAttrs(element)\n const finalAttrs = bakeNodeToAttrs(node, element)\n if (!finalAttrs) continue\n\n entries.push({ pageId, elementId, attrs: finalAttrs, priorAttrs })\n }\n\n if (entries.length > 0) onTransformEnd(entries)\n }\n\n const multiSelect = selectedIds.length > 1\n\n return (\n <Layer name=\"overlay:selection\" listening={false}>\n <Transformer\n ref={trRef}\n name=\"overlay:transformer\"\n // keepRatio for multi-select so the group scales uniformly.\n keepRatio={multiSelect}\n // Rotation snap: the rotationSnaps array controls the magnetism in\n // Konva's transformer. We pass the cardinal/diagonal angles; Konva\n // applies a threshold internally. We also call snapRotation() on\n // transformend for fine-grain 5° threshold enforcement.\n rotationSnaps={[0, 45, 90, 135, 180, 225, 270, 315]}\n rotationSnapTolerance={5}\n // Min size so elements cannot be collapsed.\n boundBoxFunc={(oldBox, newBox) => {\n if (newBox.width < MIN_SIZE || newBox.height < MIN_SIZE) return oldBox\n return newBox\n }}\n listening={canWrite}\n onTransformEnd={canWrite ? handleTransformEnd : undefined}\n />\n </Layer>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Extract the current model attrs as a \"prior\" patch for undo. */\nfunction elementToPriorAttrs(element: SceneElement): SceneAttrsPatch {\n const base: SceneAttrsPatch = {\n x: element.x,\n y: element.y,\n rotation: element.rotation,\n }\n switch (element.kind) {\n case 'rect':\n case 'image':\n case 'video':\n return { ...base, width: element.width, height: element.height }\n case 'ellipse':\n return { ...base, width: element.width, height: element.height }\n case 'line':\n return { ...base, points: element.points.slice() }\n case 'text':\n return { ...base, width: element.width, fontSize: element.fontSize }\n case 'group':\n return { ...base, width: undefined, height: undefined }\n }\n}\n\n/**\n * Read final attrs from a Konva node after a transformer gesture and bake\n * scale into model dimensions. Returns null when the element kind has no\n * meaningful size to bake (shouldn't happen in practice).\n */\nfunction bakeNodeToAttrs(node: Konva.Node, element: SceneElement): SceneAttrsPatch | null {\n // Snap rotation within 5° of cardinal/diagonal angles.\n const rawRotation = node.rotation()\n const snappedRotation = snapRotation(rawRotation, 5)\n\n const baseNode = {\n x: node.x(),\n y: node.y(),\n width: node.width(),\n height: node.height(),\n scaleX: node.scaleX(),\n scaleY: node.scaleY(),\n rotation: snappedRotation,\n }\n\n switch (element.kind) {\n case 'rect':\n case 'image':\n case 'video':\n case 'group': {\n const baked = bakeRectTransform(baseNode)\n return {\n x: baked.x,\n y: baked.y,\n width: Math.max(MIN_SIZE, baked.width),\n height: Math.max(MIN_SIZE, baked.height),\n rotation: baked.rotation,\n }\n }\n case 'ellipse': {\n // Konva.Ellipse's transformer node is centered (Konva reports x/y as\n // center coords, radiusX/radiusY via width/height halved). We must\n // convert back to model top-left coords.\n const baked = bakeRectTransform(baseNode)\n // After baking, node.x/y for an ellipse is its center because that's\n // how we placed it in ElementNode. Convert to top-left.\n const topLeft = ellipseTopLeftFromCenter({\n x: baked.x,\n y: baked.y,\n // radiusX/radiusY = half of baked width/height\n radiusX: baked.width / 2,\n radiusY: baked.height / 2,\n })\n return {\n x: topLeft.x,\n y: topLeft.y,\n width: Math.max(MIN_SIZE, topLeft.width),\n height: Math.max(MIN_SIZE, topLeft.height),\n rotation: baked.rotation,\n }\n }\n case 'line': {\n const points = (node as Konva.Line).points()\n const baked = bakeLineTransform({ ...baseNode, points })\n return {\n x: baked.x,\n y: baked.y,\n rotation: baked.rotation,\n points: baked.points,\n }\n }\n case 'text': {\n const baked = bakeTextTransform({\n ...baseNode,\n fontSize: element.fontSize,\n })\n return {\n x: baked.x,\n y: baked.y,\n width: Math.max(MIN_SIZE, baked.width),\n rotation: baked.rotation,\n fontSize: Math.max(1, baked.fontSize),\n }\n }\n }\n}\n","/**\n * Grid overlay rendered beneath page content. Uses Konva.Layer with Konva.Line\n * nodes at `gridSize` document-px spacing, scaled by zoom. Grid lines are\n * skipped entirely when they would be closer than 4 screen pixels apart —\n * below that density the grid becomes visual noise rather than guidance.\n *\n * All nodes carry the name prefix 'overlay:' so export logic can exclude this\n * layer from rasterization.\n */\n\nimport { Layer, Line } from 'react-konva'\nimport { gridVisible } from './transform-math'\n\nexport interface GridLayerProps {\n /** Page width in document px. */\n pageWidth: number\n /** Page height in document px. */\n pageHeight: number\n /** Document px between grid lines. */\n gridSize: number\n /** Screen px per document px. */\n zoom: number\n /** Grid line color. */\n color?: string\n /** Grid line opacity (0–1). */\n opacity?: number\n}\n\nexport function GridLayer({\n pageWidth,\n pageHeight,\n gridSize,\n zoom,\n color = '#c0c0c0',\n opacity = 0.5,\n}: GridLayerProps) {\n // Skip rendering when lines would be denser than 4 screen px.\n if (!gridVisible(gridSize, zoom, 4)) return null\n\n const verticals: number[] = []\n for (let x = gridSize; x < pageWidth; x += gridSize) {\n verticals.push(x)\n }\n\n const horizontals: number[] = []\n for (let y = gridSize; y < pageHeight; y += gridSize) {\n horizontals.push(y)\n }\n\n return (\n <Layer name=\"overlay:grid\" listening={false}>\n {verticals.map((x) => (\n <Line\n key={`v-${x}`}\n name=\"overlay:grid-line\"\n points={[x, 0, x, pageHeight]}\n stroke={color}\n strokeWidth={1 / zoom}\n opacity={opacity}\n listening={false}\n perfectDrawEnabled={false}\n />\n ))}\n {horizontals.map((y) => (\n <Line\n key={`h-${y}`}\n name=\"overlay:grid-line\"\n points={[0, y, pageWidth, y]}\n stroke={color}\n strokeWidth={1 / zoom}\n opacity={opacity}\n listening={false}\n perfectDrawEnabled={false}\n />\n ))}\n </Layer>\n )\n}\n","/**\n * Active snap guides rendered as full-page-extent lines during drag gestures.\n * Renders into a dedicated Konva.Layer (name 'overlay:snap') so export logic\n * can exclude it.\n *\n * Guide line colors differ by kind so users can distinguish grid snaps (faint)\n * from element-edge/center snaps (accent) from saved ruler guides (blue).\n */\n\nimport { Layer, Line } from 'react-konva'\nimport type { SnapTarget, SnapTargetKind } from '../contracts'\n\nexport interface SnapGuidesOverlayProps {\n /** Page dimensions in document px. */\n pageWidth: number\n pageHeight: number\n /** Active vertical guide, or null when not snapping. */\n activeVertical: SnapTarget | null\n /** Active horizontal guide, or null when not snapping. */\n activeHorizontal: SnapTarget | null\n /** Screen px per document px — used to compute 1-px-screen stroke widths. */\n zoom: number\n}\n\nconst KIND_COLOR: Record<SnapTargetKind, string> = {\n 'grid': '#a0a0a0',\n 'guide': '#3b82f6',\n 'page-edge': '#f59e0b',\n 'page-center': '#f59e0b',\n 'element-edge': '#f43f5e',\n 'element-center': '#f43f5e',\n}\n\nexport function SnapGuidesOverlay({\n pageWidth,\n pageHeight,\n activeVertical,\n activeHorizontal,\n zoom,\n}: SnapGuidesOverlayProps) {\n if (!activeVertical && !activeHorizontal) return null\n\n const strokeWidth = 1 / zoom\n\n return (\n <Layer name=\"overlay:snap\" listening={false}>\n {activeVertical && (\n <Line\n name=\"overlay:snap-vertical\"\n points={[activeVertical.position, -99999, activeVertical.position, 99999]}\n stroke={KIND_COLOR[activeVertical.kind]}\n strokeWidth={strokeWidth}\n dash={[4 / zoom, 3 / zoom]}\n listening={false}\n perfectDrawEnabled={false}\n />\n )}\n {activeHorizontal && (\n <Line\n name=\"overlay:snap-horizontal\"\n points={[-99999, activeHorizontal.position, 99999, activeHorizontal.position]}\n stroke={KIND_COLOR[activeHorizontal.kind]}\n strokeWidth={strokeWidth}\n dash={[4 / zoom, 3 / zoom]}\n listening={false}\n perfectDrawEnabled={false}\n />\n )}\n </Layer>\n )\n}\n","/**\n * Inline text editor: a positioned <textarea> that appears over a text element\n * on double-click, mirroring its font properties at the current zoom. The\n * parent Workspace mounts this when `editingElementId` is set and unmounts it\n * on commit/cancel.\n *\n * V1 simplification: rotation is NOT applied to the textarea — rotated text\n * elements are edited in a non-rotated overlay at the element's AABB origin.\n * The textarea stays axis-aligned to avoid browser textarea rotation bugs.\n * Document the intent: a v2 pass may add a CSS transform to align with rotation.\n *\n * Commit: Meta+Enter or blur → emits `onCommit(newText)`.\n * Cancel: Escape → emits `onCancel()` and restores pre-edit text.\n */\n\nimport { useEffect, useRef } from 'react'\nimport { computeTextOverlayPosition } from './transform-math'\nimport type { TextElement } from '../../design-canvas/model'\n\nexport interface InlineTextEditorProps {\n element: TextElement\n zoom: number\n panX: number\n panY: number\n /** Bounding rect of the Konva stage container in the viewport. */\n stageRect: { left: number; top: number }\n onCommit(text: string): void\n onCancel(): void\n}\n\nexport function InlineTextEditor({\n element,\n zoom,\n panX,\n panY,\n stageRect,\n onCommit,\n onCancel,\n}: InlineTextEditorProps) {\n const ref = useRef<HTMLTextAreaElement>(null)\n\n const pos = computeTextOverlayPosition({\n elementX: element.x,\n elementY: element.y,\n elementWidth: element.width,\n elementHeight: element.fontSize * element.lineHeight * 4, // generous initial height\n zoom,\n panX,\n panY,\n stageLeft: stageRect.left,\n stageTop: stageRect.top,\n elementFontSize: element.fontSize,\n })\n\n useEffect(() => {\n const el = ref.current\n if (!el) return\n el.focus()\n el.select()\n }, [])\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>) {\n if (e.key === 'Escape') {\n e.preventDefault()\n onCancel()\n return\n }\n // Meta+Enter or Ctrl+Enter commits\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n onCommit(ref.current?.value ?? element.text)\n }\n }\n\n function handleBlur() {\n onCommit(ref.current?.value ?? element.text)\n }\n\n // Map model fontStyle → CSS font-style + font-weight\n const fontWeight = element.fontStyle.includes('bold') ? 'bold' : 'normal'\n const fontStyle = element.fontStyle.includes('italic') ? 'italic' : 'normal'\n\n return (\n <textarea\n ref={ref}\n defaultValue={element.text}\n onKeyDown={handleKeyDown}\n onBlur={handleBlur}\n style={{\n position: 'absolute',\n left: pos.left,\n top: pos.top,\n width: pos.width,\n // Height auto-grows via CSS; min so single-line text has room.\n minHeight: pos.fontSize * element.lineHeight * 1.5,\n fontSize: pos.fontSize,\n fontFamily: element.fontFamily,\n fontWeight,\n fontStyle,\n textAlign: element.align,\n lineHeight: element.lineHeight,\n letterSpacing: element.letterSpacing * zoom,\n color: element.fill,\n background: 'rgba(255,255,255,0.95)',\n border: '2px solid #3b82f6',\n borderRadius: 2,\n padding: 2,\n resize: 'none',\n outline: 'none',\n overflow: 'hidden',\n boxSizing: 'border-box',\n zIndex: 1000,\n // Do NOT apply rotation — see V1 simplification note in module header.\n }}\n aria-label={`Editing text element \"${element.name}\"`}\n />\n )\n}\n","/**\n * Batteries-included composition: DesignCanvas chrome + WorkspaceView sharing\n * one command stack. This is the component products mount.\n *\n * Stack ownership:\n * DesignCanvas (chrome) owns the command stack. WorkspaceView receives that\n * same stack via renderWorkspace(ctx), so every gesture, undo, redo, and\n * layers-panel selection mutation touches exactly one state machine.\n *\n * Thumbnail rendering:\n * Thumbnails are produced imperatively with the Konva JS API (no React tree)\n * on an off-DOM stage at ~96px height. Only geometry + solid-fill shapes are\n * rendered — image nodes are skipped because async loading would require a\n * second pass. Cache key: `${pageId}:${cheapHash(elements)}`. Thumbnail\n * absence is cosmetic; PagesStrip handles a null result gracefully.\n */\n\nimport type { SceneDocument } from '../../design-canvas/model'\nimport type { DesignCanvasProps } from '../contracts'\nimport { DesignCanvas } from './DesignCanvas'\nimport { WorkspaceView } from './Workspace'\n\n// ---------------------------------------------------------------------------\n// Thumbnail cache\n// ---------------------------------------------------------------------------\n\nconst THUMBNAIL_HEIGHT_PX = 96\n\n/** Keyed by `pageId:contentHash`. Evicted only when the cache reaches the\n * hard limit (prevents unbounded growth across long sessions). */\nconst thumbnailCache = new Map<string, string>()\nconst THUMBNAIL_CACHE_LIMIT = 200\n\nfunction evictIfNeeded(): void {\n if (thumbnailCache.size < THUMBNAIL_CACHE_LIMIT) return\n // Drop the oldest 20 % when we hit the ceiling.\n const toDrop = Math.floor(THUMBNAIL_CACHE_LIMIT * 0.2)\n let dropped = 0\n for (const key of thumbnailCache.keys()) {\n if (dropped >= toDrop) break\n thumbnailCache.delete(key)\n dropped++\n }\n}\n\n/** Fast, non-cryptographic hash for cache-key generation. Not collision-free —\n * false hits produce a stale thumbnail (cosmetic only). */\nfunction cheapHash(value: unknown): string {\n const s = JSON.stringify(value) ?? ''\n let h = 0x811c9dc5\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i)\n // imul handles 32-bit overflow correctly\n h = Math.imul(h, 0x01000193)\n }\n return (h >>> 0).toString(36)\n}\n\n/** Render a single page to a ~96px-tall data URL using the Konva JS API. The\n * render is synchronous (no image loading); only geometry + solid fills are\n * drawn, which is sufficient for thumbnail use.\n *\n * Returns null when Konva is unavailable (SSR / test environments without\n * a canvas implementation) or on any render failure — callers must treat\n * null as \"no thumbnail yet\" rather than an error. */\nasync function renderPageThumbnail(page: SceneDocument['pages'][number]): Promise<string | null> {\n const cacheKey = `${page.id}:${cheapHash(page.elements)}`\n const cached = thumbnailCache.get(cacheKey)\n if (cached !== undefined) return cached\n\n // Lazily import Konva so this file compiles in SSR bundles.\n let Konva: typeof import('konva').default\n try {\n const mod = await import('konva')\n Konva = mod.default\n } catch {\n return null\n }\n\n const aspectRatio = page.width > 0 ? page.width / page.height : 1\n const thumbH = THUMBNAIL_HEIGHT_PX\n const thumbW = Math.round(thumbH * aspectRatio)\n const scale = page.height > 0 ? thumbH / page.height : 1\n\n let stage: InstanceType<typeof Konva.Stage> | null = null\n try {\n const container = globalThis.document?.createElement('div')\n if (!container) return null\n container.style.position = 'absolute'\n container.style.left = '-9999px'\n container.style.top = '-9999px'\n globalThis.document.body.appendChild(container)\n\n stage = new Konva.Stage({ container, width: thumbW, height: thumbH })\n const layer = new Konva.Layer()\n stage.add(layer)\n\n // Page background\n layer.add(new Konva.Rect({\n x: 0, y: 0,\n width: thumbW,\n height: thumbH,\n fill: page.background,\n listening: false,\n }))\n\n // Simplified element shapes — geometry + solid fills only; images skipped\n const group = new Konva.Group({ x: 0, y: 0, scaleX: scale, scaleY: scale, listening: false })\n layer.add(group)\n paintElements(Konva, group, page.elements)\n\n const dataUrl = stage.toDataURL({ mimeType: 'image/png', pixelRatio: 1 })\n evictIfNeeded()\n thumbnailCache.set(cacheKey, dataUrl)\n return dataUrl\n } catch {\n return null\n } finally {\n if (stage) {\n stage.destroy()\n const el = stage.container()\n el.parentNode?.removeChild(el)\n }\n }\n}\n\ntype KonvaStatic = typeof import('konva').default\n\nfunction paintElements(\n Konva: KonvaStatic,\n parent: InstanceType<typeof import('konva').default.Group>,\n elements: SceneDocument['pages'][number]['elements'],\n): void {\n for (const el of elements) {\n if (!el.visible) continue\n switch (el.kind) {\n case 'rect':\n parent.add(new Konva.Rect({\n x: el.x, y: el.y,\n width: el.width, height: el.height,\n rotation: el.rotation,\n opacity: el.opacity,\n fill: el.fill ?? undefined,\n cornerRadius: el.cornerRadius ?? 0,\n listening: false,\n }))\n break\n case 'ellipse':\n parent.add(new Konva.Ellipse({\n x: el.x + el.width / 2, y: el.y + el.height / 2,\n radiusX: el.width / 2, radiusY: el.height / 2,\n rotation: el.rotation,\n opacity: el.opacity,\n fill: el.fill ?? undefined,\n listening: false,\n }))\n break\n case 'text':\n parent.add(new Konva.Text({\n x: el.x, y: el.y,\n width: el.width,\n rotation: el.rotation,\n opacity: el.opacity,\n text: el.text,\n fontSize: el.fontSize,\n fontFamily: el.fontFamily,\n fill: el.fill,\n align: el.align,\n listening: false,\n }))\n break\n case 'group': {\n const g = new Konva.Group({\n x: el.x, y: el.y,\n rotation: el.rotation,\n opacity: el.opacity,\n listening: false,\n })\n parent.add(g)\n paintElements(Konva, g, el.children)\n break\n }\n // image and video: skip — async loading not viable in sync thumbnail render\n default:\n break\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// DesignCanvasEditor\n// ---------------------------------------------------------------------------\n\n/**\n * Mount this component to get the full editor: toolbar, rulers, layers panel,\n * pages strip, zoom controls, and the Konva canvas — all sharing one command\n * stack so undo/redo and selection are coherent across every surface.\n */\nexport function DesignCanvasEditor(props: DesignCanvasProps) {\n return (\n <DesignCanvas\n {...props}\n renderWorkspace={(ctx) => {\n if (!ctx.activePage) return null\n return (\n <WorkspaceView\n stack={ctx.stack}\n activePage={ctx.activePage}\n canWrite={ctx.canWrite}\n onApplyOperations={props.onApplyOperations}\n onSelectionChange={props.onSelectionChange}\n renderAgentPanel={props.renderAgentPanel}\n renderSidePanel={props.renderSidePanel}\n // Shares the chrome's fit ref so F / Fit button trigger workspace\n // fit-to-page through the same callback slot.\n onFitRef={ctx.onFitRef}\n />\n )\n }}\n renderThumbnail={renderPageThumbnail}\n />\n )\n}\n"],"mappings":"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","names":["useEffect","useRef","useState","Layer","Rect","Group","nudgeDelta","useEffect","useRef","jsx","useRef","useEffect","Layer","Line","jsx","Layer","Line","Layer","Line","jsx","jsxs","useEffect","useRef","jsx","useRef","useEffect","jsx","jsxs","DUPLICATE_OFFSET","useState","useEffect","useRef","nudgeDelta","Layer","Group","Rect","jsx"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/eval/index.ts"],"sourcesContent":["/**\n * Eval — the app-shell BRIDGE to `@tangle-network/agent-eval`, not a reimpl.\n *\n * The completion/scoring ENGINE lives in agent-eval (a peer dependency):\n * `verifyCompletion`, `extractProducedState`, `weightedComposite`,\n * `createLlmCorrectnessChecker`, and the `CompletionRequirement` / `TaskGold` /\n * `ProducedState` types — all re-exported here so a consumer has one import\n * root. This module adds only what agent-eval doesn't have and what is\n * app-shell-specific:\n *\n * 1. {@link producedFromToolEvents} — the bridge: turn the structured app-tool\n * side channel's `AppToolProducedEvent`s (from a tool runtime executor's\n * `onProduced`) into the `RuntimeEventLike`s agent-eval's\n * `extractProducedState` consumes. This is the one piece that knows about\n * the app-tool channel, so it belongs here, not in the engine.\n * 2. {@link createTokenRecallChecker} — a deterministic, no-LLM\n * `CorrectnessChecker` (agent-eval ships only the LLM one). For apps/tests\n * that gate completion without a judge call.\n *\n * Full campaigns (persona simulation, traces, scorecards, held-out gates) are\n * agent-eval's `runEvalCampaign` / `AgentDriver` / `BenchmarkRunner` — use them\n * directly; this module composes with them.\n */\nimport type { RuntimeEventLike, CompletionRequirement } from '@tangle-network/agent-eval'\nimport type { AppToolProducedEvent } from '../tools/types'\n\n// Re-export the engine so consumers import completion + scoring from one place.\nexport { verifyCompletion, extractProducedState, weightedComposite, createLlmCorrectnessChecker } from '@tangle-network/agent-eval'\nexport type {\n CompletionRequirement,\n TaskGold,\n ProducedState,\n SatisfiedBy,\n CompletionVerdict,\n CorrectnessChecker,\n RuntimeEventLike,\n} from '@tangle-network/agent-eval'\n\n/**\n * Bridge the app-tool side channel's produced events into the runtime-event\n * shape agent-eval's `extractProducedState` reads. Pipe it:\n * `verifyCompletion(taskGold, extractProducedState(producedFromToolEvents(events)), checker)`\n */\nexport function producedFromToolEvents(events: readonly AppToolProducedEvent[]): RuntimeEventLike[] {\n return events.map((e) =>\n e.type === 'proposal_created'\n ? { type: 'proposal_created', proposalId: e.proposalId, title: e.title, status: e.status }\n : { type: 'artifact', artifactId: `vault:${e.path}`, name: e.path, uri: `vault://${e.path}`, mimeType: 'text/markdown', content: e.content },\n )\n}\n\nconst STOPWORDS = new Set(['the', 'a', 'an', 'and', 'or', 'for', 'to', 'of', 'in', 'on', 'with', 'review', 'update', 'new', 'proposed'])\n\n/**\n * A deterministic `CorrectnessChecker` (agent-eval exports only\n * `createLlmCorrectnessChecker`). A produced item fulfils a requirement when\n * its content is substantive and recalls ≥ `minRecall` of the requirement\n * title's significant tokens. No network — the default gate for apps/tests\n * without an LLM judge. Pass to `verifyCompletion` as the checker.\n */\nexport function createTokenRecallChecker(opts: { minRecall?: number; minContentLength?: number } = {}): (\n requirement: CompletionRequirement,\n content: string,\n) => Promise<{ correct: boolean; reason: string }> {\n const minRecall = opts.minRecall ?? 0.5\n const minLen = opts.minContentLength ?? 120\n return async (requirement, content) => {\n const body = content.trim()\n if (body.length < minLen) return { correct: false, reason: `content too thin (${body.length} chars) to be the deliverable` }\n const tokens = requirement.title.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 2 && !STOPWORDS.has(t))\n if (tokens.length === 0) return { correct: true, reason: 'requirement title has no significant tokens — structural match accepted' }\n const lower = body.toLowerCase()\n const hits = tokens.filter((t) => lower.includes(t)).length\n const recall = hits / tokens.length\n return recall >= minRecall\n ? { correct: true, reason: `content recalls ${hits}/${tokens.length} requirement tokens` }\n : { correct: false, reason: `content recalls only ${hits}/${tokens.length} requirement tokens` }\n }\n}\n"],"mappings":";AA2BA,SAAS,kBAAkB,sBAAsB,mBAAmB,mCAAmC;AAgBhG,SAAS,uBAAuB,QAA6D;AAClG,SAAO,OAAO;AAAA,IAAI,CAAC,MACjB,EAAE,SAAS,qBACP,EAAE,MAAM,oBAAoB,YAAY,EAAE,YAAY,OAAO,EAAE,OAAO,QAAQ,EAAE,OAAO,IACvF,EAAE,MAAM,YAAY,YAAY,SAAS,EAAE,IAAI,IAAI,MAAM,EAAE,MAAM,KAAK,WAAW,EAAE,IAAI,IAAI,UAAU,iBAAiB,SAAS,EAAE,QAAQ;AAAA,EAC/I;AACF;AAEA,IAAM,YAAY,oBAAI,IAAI,CAAC,OAAO,KAAK,MAAM,OAAO,MAAM,OAAO,MAAM,MAAM,MAAM,MAAM,QAAQ,UAAU,UAAU,OAAO,UAAU,CAAC;AAShI,SAAS,yBAAyB,OAA0D,CAAC,GAGjD;AACjD,QAAM,YAAY,KAAK,aAAa;AACpC,QAAM,SAAS,KAAK,oBAAoB;AACxC,SAAO,OAAO,aAAa,YAAY;AACrC,UAAM,OAAO,QAAQ,KAAK;AAC1B,QAAI,KAAK,SAAS,OAAQ,QAAO,EAAE,SAAS,OAAO,QAAQ,qBAAqB,KAAK,MAAM,gCAAgC;AAC3H,UAAM,SAAS,YAAY,MAAM,YAAY,EAAE,MAAM,YAAY,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC;AAClH,QAAI,OAAO,WAAW,EAAG,QAAO,EAAE,SAAS,MAAM,QAAQ,+EAA0E;AACnI,UAAM,QAAQ,KAAK,YAAY;AAC/B,UAAM,OAAO,OAAO,OAAO,CAAC,MAAM,MAAM,SAAS,CAAC,CAAC,EAAE;AACrD,UAAM,SAAS,OAAO,OAAO;AAC7B,WAAO,UAAU,YACb,EAAE,SAAS,MAAM,QAAQ,mBAAmB,IAAI,IAAI,OAAO,MAAM,sBAAsB,IACvF,EAAE,SAAS,OAAO,QAAQ,wBAAwB,IAAI,IAAI,OAAO,MAAM,sBAAsB;AAAA,EACnG;AACF;","names":[]}
@@ -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 LoopToolCall,\n type StreamLoopYield,\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 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 * 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 — the mechanism every app's chat runtime\n * hand-rolls on top of `@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: stream a turn, collect the executable tool\n * calls, stop if there are none / no executor / the turn cap is hit, otherwise\n * execute each, append the results to history in OpenAI function-calling shape,\n * and re-run so the model reads them. Bounded by `maxToolTurns` so a model\n * looping on a failing action can't run forever.\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 model\n * that validates tool history) needs this to read its own tool use back; folding\n * results into a `user` message instead makes such models re-issue the same call\n * in a loop.\n *\n * Substrate-free by design: the app supplies `streamTurn` (wrapping whatever\n * backend / `runAgentTaskStream` it uses) and `executeToolCall` (routing to its\n * integration + app-tool executors). This package owns the LOOP; the app owns\n * the model and the executors.\n *\n * LAYERING NOTE: this turn-level tool-dispatch loop is a generic RUNTIME\n * capability. It has been CONTRIBUTED DOWN and MERGED into\n * `@tangle-network/agent-runtime` as `runToolLoop` / `streamToolLoop` (PR #137),\n * but is not yet PUBLISHED (agent-runtime main is ahead of its last npm release;\n * cutting that release is the agent-runtime maintainer's call). TERMINAL STATE:\n * the moment agent-runtime publishes a version carrying #137, bump the\n * `@tangle-network/agent-runtime` peer-dep here and replace the bodies below with\n * a thin re-export — `streamAppToolLoop = streamToolLoop`, `runAppToolLoop =\n * runToolLoop` (types alias 1:1; `AppToolOutcome` ≡ `ToolCallOutcome`). Kept\n * substrate-free + shipping until then so consumers aren't blocked on the release.\n */\nimport type { AppToolOutcome } from '../tools/types'\n\nexport interface LoopToolCall {\n toolCallId?: string\n toolName: string\n args: Record<string, unknown>\n}\n\n/** One OpenAI-shaped tool-call entry on an assistant message. */\nexport interface LoopAssistantToolCall {\n id: string\n type: 'function'\n function: { name: string; arguments: string }\n}\n\n/**\n * A message in the running conversation the loop sends to `streamTurn`.\n *\n * The base `{ role, content }` covers `system` / `user` / plain `assistant`\n * turns. Two optional fields carry the OpenAI function-calling contract so the\n * model reads its own tool use back correctly instead of re-issuing it:\n *\n * - an assistant turn that emitted tool calls carries `tool_calls`, and its\n * `content` is `null` when the turn was tool-only;\n * - each tool result is its own `{ role: 'tool', tool_call_id, content }`\n * message keyed to the call that produced it.\n *\n * Widening is additive: a `streamTurn` that reads only `role` + `content` still\n * works; one that forwards the whole message to an OpenAI-compatible endpoint\n * now gets correct tool history. */\nexport interface LoopMessage {\n role: string\n content: string | null\n tool_calls?: LoopAssistantToolCall[]\n tool_call_id?: string\n}\n\n/** A tool-call id is required to key a `role: 'tool'` result back to its call.\n * When the model omitted one, derive a stable id from the tool name so the\n * assistant `tool_calls` entry and its `tool` result still match. */\nfunction toolCallId(call: LoopToolCall): string {\n return call.toolCallId ?? `call_${call.toolName}`\n}\n\n/** The assistant turn that emitted `pending`, in OpenAI shape: text content\n * (null when the turn was tool-only) plus its `tool_calls` array. */\nfunction assistantToolCallMessage(turnText: string, pending: LoopToolCall[]): LoopMessage {\n return {\n role: 'assistant',\n content: turnText.trim() || null,\n tool_calls: pending.map((call) => ({\n id: toolCallId(call),\n type: 'function',\n function: { name: call.toolName, arguments: JSON.stringify(call.args) },\n })),\n }\n}\n\n/** One `role: 'tool'` result message keyed to its call by `tool_call_id`. */\nfunction toolResultMessage(call: LoopToolCall, content: string): LoopMessage {\n return { role: 'tool', tool_call_id: toolCallId(call), content }\n}\n\n/** Events a turn stream yields. `text` accumulates into the final answer;\n * `tool_call` is collected for dispatch; `reasoning` and `usage` pass through\n * for UIs that render thinking sections and per-message token/cost metrics.\n * Extra event types pass through untouched (the caller re-emits them to its\n * own UI stream). */\nexport type LoopEvent =\n | { type: 'text'; text: string }\n | { type: 'reasoning'; text: string }\n | { type: 'tool_call'; call: LoopToolCall }\n | { type: 'usage'; usage: { promptTokens: number; completionTokens: number } }\n | { type: 'other'; event: unknown }\n\n/** Why the loop stopped. `completed` = model finished naturally; `stuck-loop` =\n * ≥3 consecutive identical tool calls (same tool + args); `backstop` = hit the\n * runaway-backstop cap (200 by default); `deadline` = wall-clock deadlineMs\n * exceeded; `budget` = maxCostUsd exhausted. Non-`completed` stops are infra /\n * resource outcomes — eval scoring must distinguish them from capability failure. */\nexport type ToolLoopStopReason = 'completed' | 'stuck-loop' | 'backstop' | 'deadline' | 'budget'\n\nexport interface ToolLoopResult {\n /** The model's final text across the loop. */\n finalText: string\n /** Every tool call executed, with its outcome, in order. */\n toolResults: Array<{ call: LoopToolCall; label: string; outcome: AppToolOutcome }>\n /** Number of model turns run (1 + tool-driven re-runs). */\n turns: number\n /** Why the loop stopped. */\n stopReason: ToolLoopStopReason\n /** @deprecated Use `stopReason !== 'completed'` instead. */\n cappedOut: boolean\n}\n\nexport interface AppToolLoopOptions {\n systemPrompt: string\n userMessage: string\n priorMessages?: Array<{ role: string; content: string }>\n /** Stream one model turn over the running message list. The app wraps its\n * backend here. Messages follow {@link LoopMessage}: a tool-calling assistant\n * turn carries `tool_calls`, and each tool result is a `role: 'tool'` message.\n * A backend that reads only `role` + `content` is unaffected. */\n streamTurn: (messages: LoopMessage[]) => AsyncIterable<LoopEvent>\n /** Execute one tool call. The app routes to its integration executor / app-tool\n * executor and returns the outcome. */\n executeToolCall: (call: LoopToolCall) => Promise<AppToolOutcome>\n /** Which emitted tool names are executable (others are ignored — e.g. a UI-only\n * tool the app renders but doesn't run here). */\n isExecutableTool: (toolName: string) => boolean\n /** Runaway-backstop cap. Default 200 — set far above any legitimate workflow.\n * For per-workflow limits use `maxCostUsd` or `deadlineMs` instead. */\n maxToolTurns?: number\n /** Wall-clock deadline in ms since epoch (Date.now()-based). When exceeded the\n * loop stops with stopReason `deadline`. */\n deadlineMs?: number\n /** Maximum total cost in USD. Requires `costOf` to meter each tool call. */\n maxCostUsd?: number\n /** Return the USD cost of one outcome. Required for `maxCostUsd` to work. */\n costOf?: (call: LoopToolCall, outcome: AppToolOutcome) => number\n /** Render one tool outcome as the `content` of its `role: 'tool'` message.\n * Default is a compact `<label> → ok/failed: …`. */\n renderResult?: (label: string, outcome: AppToolOutcome) => string\n /** Map a tool call to the label its result is keyed under (default: toolName). */\n labelFor?: (call: LoopToolCall) => string\n}\n\n/** Runaway-backstop: stops an infinite tool loop where cost is unmetered. Set\n * far above any legitimate workflow — this is a watchdog, not a policy cap. */\nconst RUNAWAY_BACKSTOP_TURNS = 200\n/** Consecutive identical calls (same tool + canonical-JSON args) that trigger\n * stuck-loop detection. The window resets on any different call. */\nconst STUCK_LOOP_THRESHOLD = 3\n\n/** Canonical identifier for a tool call used by stuck-loop detection.\n * Keys are sorted so {b:1,a:2} and {a:2,b:1} produce the same hash. */\nfunction canonicalCallHash(call: LoopToolCall): string {\n const sortedArgs = Object.fromEntries(\n Object.entries(call.args).sort(([a], [b]) => a.localeCompare(b)),\n )\n return `${call.toolName}:${JSON.stringify(sortedArgs)}`\n}\n\nfunction defaultRender(label: string, outcome: AppToolOutcome): string {\n if (outcome.ok) return `${label} → ok: ${JSON.stringify(outcome.result)}`\n return `${label} → failed (${outcome.code}): ${outcome.message}`\n}\n\n/**\n * Run the bounded tool loop and return the final text + every executed tool\n * outcome. Yields nothing — it's an awaitable driver; callers that need to\n * re-emit events to a UI stream should do so inside `streamTurn`. (A streaming\n * variant can wrap this later; keeping the core awaitable makes it trivially\n * testable.)\n */\nexport async function runAppToolLoop(opts: AppToolLoopOptions): Promise<ToolLoopResult> {\n const backstop = opts.maxToolTurns ?? RUNAWAY_BACKSTOP_TURNS\n const render = opts.renderResult ?? defaultRender\n const labelFor = opts.labelFor ?? ((c: LoopToolCall) => c.toolName)\n\n const messages: LoopMessage[] = [\n { role: 'system', content: opts.systemPrompt },\n ...(opts.priorMessages ?? []),\n { role: 'user', content: opts.userMessage },\n ]\n\n const toolResults: ToolLoopResult['toolResults'] = []\n let finalText = ''\n let turns = 0\n let accumulatedCostUsd = 0\n let lastCallHash: string | null = null\n let consecutiveCount = 0\n\n for (let toolTurn = 0; ; toolTurn++) {\n turns++\n\n // Wall-clock deadline check before every new turn.\n if (opts.deadlineMs !== undefined && Date.now() >= opts.deadlineMs) {\n return { finalText, toolResults, turns, stopReason: 'deadline', cappedOut: true }\n }\n\n let turnText = ''\n const pending: LoopToolCall[] = []\n\n for await (const ev of opts.streamTurn([...messages])) {\n if (ev.type === 'text') {\n turnText += ev.text\n finalText += ev.text\n } else if (ev.type === 'tool_call' && opts.isExecutableTool(ev.call.toolName)) {\n pending.push(ev.call)\n }\n }\n\n if (pending.length === 0) break\n\n // Runaway backstop — the model keeps emitting calls past the safety ceiling.\n if (toolTurn >= backstop) {\n return { finalText, toolResults, turns, stopReason: 'backstop', cappedOut: true }\n }\n\n // The assistant turn that emitted the calls — with its tool_calls array —\n // so the model sees its own tool use in history.\n messages.push(assistantToolCallMessage(turnText, pending))\n\n for (const call of pending) {\n // Stuck-loop detection.\n const callHash = canonicalCallHash(call)\n if (callHash === lastCallHash) {\n consecutiveCount++\n } else {\n lastCallHash = callHash\n consecutiveCount = 1\n }\n if (consecutiveCount >= STUCK_LOOP_THRESHOLD) {\n return { finalText, toolResults, turns, stopReason: 'stuck-loop', cappedOut: true }\n }\n\n let outcome: AppToolOutcome\n try {\n outcome = await opts.executeToolCall(call)\n } catch (err) {\n outcome = { ok: false, code: 'executor_error', message: err instanceof Error ? err.message : String(err) }\n }\n\n // Budget check after each tool call.\n if (opts.maxCostUsd !== undefined && opts.costOf !== undefined) {\n accumulatedCostUsd += opts.costOf(call, outcome)\n if (accumulatedCostUsd >= opts.maxCostUsd) {\n const label = labelFor(call)\n toolResults.push({ call, label, outcome })\n messages.push(toolResultMessage(call, render(label, outcome)))\n return { finalText, toolResults, turns, stopReason: 'budget', cappedOut: true }\n }\n }\n\n const label = labelFor(call)\n toolResults.push({ call, label, outcome })\n // One role:'tool' message per result, keyed to its call by tool_call_id.\n messages.push(toolResultMessage(call, render(label, outcome)))\n }\n }\n\n return { finalText, toolResults, turns, stopReason: 'completed', cappedOut: false }\n}\n\n// ── Streaming variant ──────────────────────────────────────────────────────\n//\n// `runAppToolLoop` is awaitable — perfect for tests and drain-only callers. A\n// real chat runtime instead needs to STREAM each model event to the client (SSE)\n// AND record telemetry per event as it happens. `streamAppToolLoop` is the same\n// bounded loop as an async generator: it yields every raw turn event (the app\n// maps + telemetries + re-emits it) and every executed tool result (same), while\n// owning the loop control flow (collect → stop/dispatch → append → re-run, capped).\n// `Raw` is the app's own runtime-event type — this package stays substrate-free.\n\nexport type StreamLoopYield<Raw> =\n | { kind: 'event'; event: Raw }\n | { kind: 'tool_result'; toolName: string; toolCallId?: string; label: string; outcome: AppToolOutcome }\n | { kind: 'capped'; pending: number; stopReason: Exclude<ToolLoopStopReason, 'completed'> }\n\nexport interface StreamAppToolLoopOptions<Raw> {\n systemPrompt: string\n userMessage: string\n priorMessages?: Array<{ role: string; content: string }>\n /** Stream one model turn (the app wraps its backend / runAgentTaskStream).\n * Messages follow {@link LoopMessage}: a tool-calling assistant turn carries\n * `tool_calls`, and each tool result is a `role: 'tool'` message. */\n streamTurn: (messages: LoopMessage[]) => AsyncIterable<Raw>\n /** Text contribution of a raw event, '' if none — used to record the\n * assistant's turn so the next turn has its context. */\n extractText: (event: Raw) => string\n /** The tool call a raw event represents, or null. */\n extractToolCall: (event: Raw) => LoopToolCall | null\n /** Which tool names are executable here (others pass through, unexecuted). */\n isExecutableTool: (toolName: string) => boolean\n /** Execute one call — the app routes to its integration / app-tool executor. */\n executeToolCall: (call: LoopToolCall) => Promise<AppToolOutcome>\n /** Runaway-backstop cap. Default 200 — set far above any legitimate workflow. */\n maxToolTurns?: number\n /** Wall-clock deadline in ms since epoch (Date.now()-based). */\n deadlineMs?: number\n /** Maximum total cost in USD. Requires `costOf` to meter each tool call. */\n maxCostUsd?: number\n /** Return the USD cost of one outcome. Required for `maxCostUsd` to work. */\n costOf?: (call: LoopToolCall, outcome: AppToolOutcome) => number\n renderResult?: (label: string, outcome: AppToolOutcome) => string\n labelFor?: (call: LoopToolCall) => string\n}\n\n/**\n * The streaming bounded tool loop. Yields `event` for each raw turn event and\n * `tool_result` for each executed tool; emits a single `capped` (with stopReason)\n * when it stops for any non-completed reason. The app drives telemetry + UI\n * emission off the yielded items.\n */\nexport async function* streamAppToolLoop<Raw>(opts: StreamAppToolLoopOptions<Raw>): AsyncGenerator<StreamLoopYield<Raw>, void, unknown> {\n const backstop = opts.maxToolTurns ?? RUNAWAY_BACKSTOP_TURNS\n const render = opts.renderResult ?? defaultRender\n const labelFor = opts.labelFor ?? ((c: LoopToolCall) => c.toolName)\n\n const messages: LoopMessage[] = [\n { role: 'system', content: opts.systemPrompt },\n ...(opts.priorMessages ?? []),\n { role: 'user', content: opts.userMessage },\n ]\n\n let accumulatedCostUsd = 0\n let lastCallHash: string | null = null\n let consecutiveCount = 0\n\n for (let toolTurn = 0; ; toolTurn++) {\n // Wall-clock deadline check before every new turn.\n if (opts.deadlineMs !== undefined && Date.now() >= opts.deadlineMs) {\n yield { kind: 'capped', pending: 0, stopReason: 'deadline' }\n return\n }\n\n let turnText = ''\n const pending: LoopToolCall[] = []\n\n for await (const event of opts.streamTurn([...messages])) {\n yield { kind: 'event', event }\n turnText += opts.extractText(event)\n const call = opts.extractToolCall(event)\n if (call && opts.isExecutableTool(call.toolName)) pending.push(call)\n }\n\n if (pending.length === 0) return\n\n // Runaway backstop.\n if (toolTurn >= backstop) {\n yield { kind: 'capped', pending: pending.length, stopReason: 'backstop' }\n return\n }\n\n // The assistant turn that emitted the calls — with its tool_calls array.\n messages.push(assistantToolCallMessage(turnText, pending))\n\n for (const call of pending) {\n // Stuck-loop detection.\n const callHash = canonicalCallHash(call)\n if (callHash === lastCallHash) {\n consecutiveCount++\n } else {\n lastCallHash = callHash\n consecutiveCount = 1\n }\n if (consecutiveCount >= STUCK_LOOP_THRESHOLD) {\n yield { kind: 'capped', pending: pending.length, stopReason: 'stuck-loop' }\n return\n }\n\n let outcome: AppToolOutcome\n try {\n outcome = await opts.executeToolCall(call)\n } catch (err) {\n outcome = { ok: false, code: 'executor_error', message: err instanceof Error ? err.message : String(err) }\n }\n\n // Budget check after each tool call.\n if (opts.maxCostUsd !== undefined && opts.costOf !== undefined) {\n accumulatedCostUsd += opts.costOf(call, outcome)\n if (accumulatedCostUsd >= opts.maxCostUsd) {\n const label = labelFor(call)\n yield { kind: 'tool_result', toolName: call.toolName, toolCallId: call.toolCallId, label, outcome }\n messages.push(toolResultMessage(call, render(label, outcome)))\n yield { kind: 'capped', pending: pending.length, stopReason: 'budget' }\n return\n }\n }\n\n const label = labelFor(call)\n yield { kind: 'tool_result', toolName: call.toolName, toolCallId: call.toolCallId, label, outcome }\n // One role:'tool' message per result, keyed to its call by tool_call_id.\n messages.push(toolResultMessage(call, render(label, outcome)))\n }\n }\n}\n"],"mappings":"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","names":["label"]}