silvery 0.17.2 → 0.17.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devtools-DS9NseGT.mjs +2 -0
- package/dist/{devtools-CscuKaDK.mjs → devtools-owvUPfBi.mjs} +2 -2
- package/dist/{devtools-CscuKaDK.mjs.map → devtools-owvUPfBi.mjs.map} +1 -1
- package/dist/index.mjs +9 -9
- package/dist/{pipeline-DDOPrjuY.mjs → pipeline-BmfaZb1O.mjs} +2 -2
- package/dist/{pipeline-DDOPrjuY.mjs.map → pipeline-BmfaZb1O.mjs.map} +1 -1
- package/dist/{reconciler-2lp5VXK7.mjs → reconciler-B-NaZvbO.mjs} +2 -2
- package/dist/{reconciler-2lp5VXK7.mjs.map → reconciler-B-NaZvbO.mjs.map} +1 -1
- package/dist/{render-string-BXvxTg5P.mjs → render-string-Bvh1XzBv.mjs} +3 -3
- package/dist/{render-string-BXvxTg5P.mjs.map → render-string-Bvh1XzBv.mjs.map} +1 -1
- package/dist/{render-string-hvfpVtoP.mjs → render-string-CZKpuKXo.mjs} +1 -1
- package/dist/{runtime-BjDHNTxJ.mjs → runtime-PH2xY1DM.mjs} +3 -3
- package/dist/{runtime-BjDHNTxJ.mjs.map → runtime-PH2xY1DM.mjs.map} +1 -1
- package/dist/runtime.mjs +2 -2
- package/package.json +4 -6
- package/dist/devtools-D4oGc6LY.mjs +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-BjDHNTxJ.mjs","names":["getTabOrder","ESC","CSI","RESET","moveCursor","_enableMouse","_disableMouse","_enableKittyKeyboard","_disableKittyKeyboard","BEL","ESC","log","globalGetCursorState","isTerm","DEFAULT_CONFIG","BORDER_CHARS","ANSI_COLORS","resolveColor","createStore","process"],"sources":["../packages/ag-term/src/scroll-utils.ts","../packages/ag-react/src/hooks/useInput.ts","../packages/ag-term/src/history-buffer.ts","../packages/ag-term/src/list-document.ts","../packages/ag-term/src/text-surface.ts","../packages/ag-term/src/viewport-compositor.ts","../packages/ag-term/src/search-overlay.ts","../packages/ag-term/src/pane-manager.ts","../packages/ag-term/src/output.ts","../packages/ag-react/src/hooks/useExit.ts","../packages/ag-term/src/scroll-region.ts","../packages/ag-react/src/hooks/useCursor.ts","../packages/ag-react/src/error-boundary.tsx","../packages/ag/src/focus-queries.ts","../packages/ag/src/interactive-signals.ts","../packages/ag/src/focus-manager.ts","../packages/ag/src/tree-utils.ts","../packages/ag/src/focus-events.ts","../packages/ag-term/src/hit-registry-core.ts","../packages/test/src/debug-mismatch.ts","../packages/ag-term/src/non-tty.ts","../packages/ag-term/src/clipboard.ts","../packages/ag-term/src/scheduler.ts","../packages/ag-term/src/term-def.ts","../packages/ag-term/src/osc-palette.ts","../packages/ag-term/src/osc-markers.ts","../packages/ag-term/src/kitty-detect.ts","../packages/ag-term/src/termtest.ts","../packages/ag-term/src/measurer.ts","../packages/ag-term/src/cursor-query.ts","../packages/ag-term/src/terminal-colors.ts","../packages/ag-term/src/device-attrs.ts","../packages/ag-term/src/mode-query.ts","../packages/ag-term/src/pixel-size.ts","../packages/ag-term/src/adapters/canvas-adapter.ts","../packages/ag-term/src/adapters/dom-adapter.ts","../packages/ag-term/src/mouse-events.ts","../packages/ag-term/src/inspector.ts","../packages/create/src/core/index.ts","../packages/create/src/store/index.ts","../packages/create/src/streams/index.ts","../packages/create/src/internal/capability-registry.ts","../packages/create/src/internal/capabilities.ts","../packages/headless/src/selection.ts","../packages/ag-term/src/features/selection.ts","../packages/ag-term/src/ansi/width-detection.ts","../packages/ag-term/src/selection-renderer.ts","../packages/ag-term/src/virtual-scrollback.ts","../packages/ag-term/src/index.ts","../packages/ag-term/src/runtime/layout.ts","../packages/ag-term/src/runtime/diff.ts","../packages/ag-term/src/runtime/create-buffer.ts","../packages/ag-term/src/runtime/create-runtime.ts","../packages/create/src/signal-store.ts","../packages/ag-term/src/runtime/event-handlers.ts","../packages/ag-term/src/runtime/terminal-lifecycle.ts","../packages/ag-term/src/runtime/perf.ts","../packages/ag-term/src/runtime/create-app.tsx","../packages/ag-react/src/hooks/usePasteCallback.ts","../packages/ag-term/src/runtime/run.tsx","../packages/ag-term/src/runtime/tick.ts"],"sourcesContent":["/**\n * Scroll Utilities\n *\n * Shared functions for edge-based scrolling behavior across VirtualList,\n * HorizontalVirtualList, and other scroll-aware components.\n */\n\n/**\n * Calculate edge-based scroll offset.\n *\n * Only scrolls when cursor approaches the edge of the visible area.\n * This provides smoother scrolling by starting to scroll before hitting\n * the absolute edge, maintaining context around the selected item.\n *\n * ## Algorithm\n *\n * The viewport is divided into zones:\n * ```\n * |<padding>|<------ safe zone ------>|<padding>|\n * | scroll | no scroll needed | scroll |\n * | if < | | if > |\n * ```\n *\n * When the selected item enters a padding zone, the viewport scrolls\n * to keep the item visible with margin.\n *\n * ## Asymmetry Note\n *\n * The +1 in the \"scroll down/right\" case is intentional:\n * - Offset points to the TOP/LEFT of the viewport\n * - We want the selected item to be `padding` items from the BOTTOM/RIGHT\n * - Formula: `selectedIndex - visibleCount + padding + 1`\n *\n * Example: visibleCount=10, padding=2, selectedIndex=15\n * offset = 15 - 10 + 2 + 1 = 8\n * viewport shows items 8-17, selected item 15 is at position 7 (2 from bottom)\n *\n * @param selectedIndex - Currently selected item index\n * @param currentOffset - Current scroll offset (topmost/leftmost visible item)\n * @param visibleCount - Number of items visible in viewport\n * @param totalCount - Total number of items\n * @param padding - Items to keep visible before/after cursor (default: 1)\n * @returns New scroll offset\n */\nexport function calcEdgeBasedScrollOffset(\n selectedIndex: number,\n currentOffset: number,\n visibleCount: number,\n totalCount: number,\n padding = 1,\n): number {\n // If everything fits, no scrolling needed\n if (totalCount <= visibleCount) return 0\n\n // Reduce padding when viewport is too small to have a non-empty safe zone.\n // With padding=1 and visibleCount=2, paddedStart > paddedEnd (inverted zone),\n // causing every re-render to trigger a scroll in one direction.\n const effectivePadding = padding * 2 >= visibleCount ? 0 : padding\n\n // Calculate visible range\n const visibleStart = currentOffset\n const visibleEnd = currentOffset + visibleCount - 1\n\n // Define the \"safe zone\" where cursor doesn't trigger scroll\n const paddedStart = visibleStart + effectivePadding\n const paddedEnd = visibleEnd - effectivePadding\n\n let newOffset = currentOffset\n\n if (selectedIndex < paddedStart) {\n // Scrolling UP/LEFT: place item `effectivePadding` rows from top\n newOffset = Math.max(0, selectedIndex - effectivePadding)\n } else if (\n effectivePadding === 0 &&\n selectedIndex === paddedStart &&\n currentOffset > 0 &&\n // Only scroll back if the viewport is large enough to show both the\n // context item and the selected item. When visibleCount <= padding,\n // scrolling back pushes the selected item out of view, which triggers\n // a forward scroll on the next render → infinite oscillation.\n visibleCount > padding\n ) {\n // Small viewport (effectivePadding forced to 0): cursor at the very first visible\n // position should still scroll back to provide context. Without this, scrolling\n // right works (cursor past last visible triggers scroll) but scrolling left doesn't\n // (cursor at first visible doesn't trigger), creating asymmetric behavior.\n // Use original padding for the offset formula to show context before the cursor.\n newOffset = Math.max(0, selectedIndex - padding)\n } else if (selectedIndex > paddedEnd) {\n // Scrolling DOWN/RIGHT: place item `effectivePadding` rows from bottom\n // The +1 converts from 0-indexed offset to correct position\n newOffset = Math.min(totalCount - visibleCount, selectedIndex - visibleCount + effectivePadding + 1)\n }\n\n // Clamp to valid range\n return Math.max(0, Math.min(newOffset, totalCount - visibleCount))\n}\n","/**\n * Silvery useInput Hook\n *\n * Handles keyboard input via the unified RuntimeContext.\n * Compatible with Ink's useInput API.\n *\n * No-ops when called outside a runtime (e.g., in createRenderer() tests where\n * RuntimeContext is absent). Components render without input handling, which\n * is correct for static rendering.\n * Use useRuntime() for components that need to detect interactive vs static mode.\n */\n\nimport { useContext, useEffect, useRef } from \"react\"\nimport { RuntimeContext } from \"../context\"\nimport { isModifierOnlyEvent, type InputHandler, type Key } from \"@silvery/ag/keys\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n// Re-export Key and InputHandler for consumers that import from useInput\nexport type { Key, InputHandler } from \"@silvery/ag/keys\"\n\n/**\n * Options for useInput hook.\n */\nexport interface UseInputOptions {\n /**\n * Enable or disable input handling.\n * Useful when there are multiple useInput hooks and you want to disable some.\n * @default true\n */\n isActive?: boolean\n\n /**\n * Callback for bracketed paste events.\n * When the terminal has bracketed paste mode enabled,\n * pasted text is delivered as a single string instead of\n * individual keystrokes.\n */\n onPaste?: (text: string) => void\n\n /**\n * Callback for key release events.\n * Requires Kitty protocol with REPORT_EVENTS flag enabled.\n * When provided, release events are dispatched here instead of being silently dropped.\n *\n * @example\n * ```tsx\n * useInput((input, key) => {\n * // Handle press/repeat events\n * }, {\n * onRelease: (input, key) => {\n * // Handle release events (e.g., stop scrolling, end drag)\n * },\n * })\n * ```\n */\n onRelease?: InputHandler\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/**\n * Hook for handling user input.\n *\n * No-ops if RuntimeContext is not provided (i.e., outside a runtime).\n * Components render normally without input handling in static mode.\n * Use useRuntime() for components that need to detect interactive vs static mode.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * useInput((input, key) => {\n * if (input === 'q') {\n * // Quit\n * }\n * if (key.upArrow) {\n * // Move up\n * }\n * }, {\n * onRelease: (input, key) => {\n * // Handle key release (requires Kitty REPORT_EVENTS)\n * },\n * });\n *\n * return <Text>Press q to quit</Text>;\n * }\n * ```\n */\nexport function useInput(inputHandler: InputHandler, options: UseInputOptions = {}): void {\n const rt = useContext(RuntimeContext)\n\n const { isActive = true, onPaste, onRelease } = options\n\n // Stable ref for the handler — avoids tearing down/recreating the\n // subscription on every render. Without this, rapid keystrokes between\n // effect cleanup and setup are lost.\n const handlerRef = useRef(inputHandler)\n handlerRef.current = inputHandler\n\n const onPasteRef = useRef(onPaste)\n onPasteRef.current = onPaste\n\n const onReleaseRef = useRef(onRelease)\n onReleaseRef.current = onRelease\n\n // Subscribe to input events via RuntimeContext\n // In static mode (no runtime), this is a no-op — components render\n // without input handling, which is correct for createRenderer() tests.\n useEffect(() => {\n if (!isActive || !rt) return\n\n return rt.on(\"input\", (input: string, key: Key) => {\n // Skip modifier-only keys (Cmd, Shift, Ctrl, Alt pressed alone).\n // These are handled by useModifierKeys, not useInput consumers.\n if (isModifierOnlyEvent(input, key)) return\n // Release events are dispatched to onRelease if provided,\n // otherwise silently dropped (handlers expect press-only semantics).\n if (key.eventType === \"release\") {\n onReleaseRef.current?.(input, key)\n return\n }\n const result = handlerRef.current(input, key)\n if (result === \"exit\") rt.exit()\n })\n }, [isActive, rt])\n\n // Subscribe to paste events via RuntimeContext\n useEffect(() => {\n if (!isActive || !rt) return\n\n return rt.on(\"paste\", (text: string) => {\n onPasteRef.current?.(text)\n })\n }, [isActive, rt])\n}\n","/**\n * Ring buffer for frozen scrollback items.\n *\n * Each item represents a rendered list entry (card, row, etc.) with its\n * ANSI snapshot, split rows, and plain-text rows for searching.\n * When total rows exceed capacity, the oldest items are evicted.\n */\n\nimport { stripAnsi } from \"./unicode\"\n\nexport interface HistoryItem {\n key: string | number\n ansi: string\n rows: string[]\n plainTextRows: string[]\n width: number\n}\n\nexport interface HistoryBuffer {\n push(item: HistoryItem): void\n readonly totalRows: number\n readonly itemCount: number\n getRows(startRow: number, count: number): string[]\n getPlainTextRows(startRow: number, count: number): string[]\n search(query: string): number[]\n getItemAtRow(row: number): { item: HistoryItem; localRow: number } | null\n clear(): void\n readonly capacity: number\n}\n\nexport function createHistoryItem(key: string | number, ansi: string, width: number): HistoryItem {\n const rows = ansi.split(\"\\n\")\n const plainTextRows = rows.map((r) => stripAnsi(r))\n return { key, ansi, rows, plainTextRows, width }\n}\n\nexport function createHistoryBuffer(capacity = 10_000): HistoryBuffer {\n // Store items in insertion order; evict from front when over budget.\n let items: HistoryItem[] = []\n let _totalRows = 0\n\n function evict(): void {\n while (_totalRows > capacity && items.length > 0) {\n const removed = items.shift()!\n _totalRows -= removed.rows.length\n }\n }\n\n /** Walk items to find which item contains the given document row. */\n function resolveRow(row: number): { itemIndex: number; localRow: number } | null {\n if (row < 0 || row >= _totalRows) return null\n let cumulative = 0\n for (let i = 0; i < items.length; i++) {\n const itemRows = items[i]!.rows.length\n if (row < cumulative + itemRows) {\n return { itemIndex: i, localRow: row - cumulative }\n }\n cumulative += itemRows\n }\n return null\n }\n\n return {\n push(item: HistoryItem): void {\n items.push(item)\n _totalRows += item.rows.length\n evict()\n },\n\n get totalRows(): number {\n return _totalRows\n },\n\n get itemCount(): number {\n return items.length\n },\n\n get capacity(): number {\n return capacity\n },\n\n getRows(startRow: number, count: number): string[] {\n const result: string[] = []\n for (let r = startRow; r < startRow + count; r++) {\n const resolved = resolveRow(r)\n if (resolved) {\n result.push(items[resolved.itemIndex]!.rows[resolved.localRow]!)\n } else {\n result.push(\"\")\n }\n }\n return result\n },\n\n getPlainTextRows(startRow: number, count: number): string[] {\n const result: string[] = []\n for (let r = startRow; r < startRow + count; r++) {\n const resolved = resolveRow(r)\n if (resolved) {\n result.push(items[resolved.itemIndex]!.plainTextRows[resolved.localRow]!)\n } else {\n result.push(\"\")\n }\n }\n return result\n },\n\n search(query: string): number[] {\n if (!query) return []\n const lowerQuery = query.toLowerCase()\n const matches: number[] = []\n let rowOffset = 0\n for (const item of items) {\n for (let r = 0; r < item.plainTextRows.length; r++) {\n if (item.plainTextRows[r]!.toLowerCase().includes(lowerQuery)) {\n matches.push(rowOffset + r)\n }\n }\n rowOffset += item.rows.length\n }\n return matches\n },\n\n getItemAtRow(row: number): { item: HistoryItem; localRow: number } | null {\n const resolved = resolveRow(row)\n if (!resolved) return null\n return { item: items[resolved.itemIndex]!, localRow: resolved.localRow }\n },\n\n clear(): void {\n items = []\n _totalRows = 0\n },\n }\n}\n","/**\n * Canonical row model spanning frozen history + live content.\n *\n * Frozen rows (from HistoryBuffer) occupy rows 0..frozenRows-1.\n * Live rows follow at frozenRows..totalRows-1.\n * All row indices are document-global.\n */\n\nimport type { HistoryBuffer } from \"./history-buffer\"\nimport type { SearchMatch } from \"./search-overlay\"\n\nexport interface LiveItemBlock {\n key: string | number\n itemIndex: number\n rows: string[]\n plainTextRows: string[]\n}\n\nexport interface DocumentSource {\n type: \"frozen\" | \"live\"\n itemKey?: string | number\n itemIndex?: number\n localRow: number\n}\n\nexport interface ListDocument {\n readonly totalRows: number\n readonly frozenRows: number\n readonly liveRows: number\n getRows(startRow: number, count: number): string[]\n getPlainTextRows(startRow: number, count: number): string[]\n getSource(row: number): DocumentSource | null\n search(query: string): SearchMatch[]\n}\n\nexport function createListDocument(history: HistoryBuffer, getLiveItems: () => LiveItemBlock[]): ListDocument {\n function liveRowCount(): number {\n let total = 0\n for (const block of getLiveItems()) {\n total += block.rows.length\n }\n return total\n }\n\n return {\n get totalRows(): number {\n return history.totalRows + liveRowCount()\n },\n\n get frozenRows(): number {\n return history.totalRows\n },\n\n get liveRows(): number {\n return liveRowCount()\n },\n\n getRows(startRow: number, count: number): string[] {\n const frozen = history.totalRows\n const result: string[] = []\n for (let r = startRow; r < startRow + count; r++) {\n if (r < 0 || r >= this.totalRows) {\n result.push(\"\")\n } else if (r < frozen) {\n result.push(...history.getRows(r, 1))\n } else {\n let liveRow = r - frozen\n let found = false\n for (const block of getLiveItems()) {\n if (liveRow < block.rows.length) {\n result.push(block.rows[liveRow]!)\n found = true\n break\n }\n liveRow -= block.rows.length\n }\n if (!found) result.push(\"\")\n }\n }\n return result\n },\n\n getPlainTextRows(startRow: number, count: number): string[] {\n const frozen = history.totalRows\n const result: string[] = []\n for (let r = startRow; r < startRow + count; r++) {\n if (r < 0 || r >= this.totalRows) {\n result.push(\"\")\n } else if (r < frozen) {\n result.push(...history.getPlainTextRows(r, 1))\n } else {\n let liveRow = r - frozen\n let found = false\n for (const block of getLiveItems()) {\n if (liveRow < block.plainTextRows.length) {\n result.push(block.plainTextRows[liveRow]!)\n found = true\n break\n }\n liveRow -= block.plainTextRows.length\n }\n if (!found) result.push(\"\")\n }\n }\n return result\n },\n\n getSource(row: number): DocumentSource | null {\n const frozen = history.totalRows\n if (row < 0 || row >= this.totalRows) return null\n if (row < frozen) {\n const hit = history.getItemAtRow(row)\n if (!hit) return null\n return {\n type: \"frozen\",\n itemKey: hit.item.key,\n localRow: hit.localRow,\n }\n }\n // Live: walk item blocks\n const liveItems = getLiveItems()\n let liveRow = row - frozen\n for (const block of liveItems) {\n if (liveRow < block.rows.length) {\n return { type: \"live\", itemIndex: block.itemIndex, localRow: liveRow }\n }\n liveRow -= block.rows.length\n }\n return null\n },\n\n search(query: string): SearchMatch[] {\n if (!query) return []\n const lowerQuery = query.toLowerCase()\n const matches: SearchMatch[] = []\n const frozen = history.totalRows\n\n // Search frozen rows\n const frozenRowMatches = history.search(query)\n for (const row of frozenRowMatches) {\n const plainRows = history.getPlainTextRows(row, 1)\n const line = plainRows[0]!.toLowerCase()\n let pos = line.indexOf(lowerQuery)\n while (pos !== -1) {\n matches.push({ row, startCol: pos, endCol: pos + query.length })\n pos = line.indexOf(lowerQuery, pos + 1)\n }\n }\n\n // Search live rows (walk item blocks)\n let rowOffset = 0\n for (const block of getLiveItems()) {\n for (let i = 0; i < block.plainTextRows.length; i++) {\n const line = block.plainTextRows[i]!.toLowerCase()\n let pos = line.indexOf(lowerQuery)\n while (pos !== -1) {\n matches.push({ row: frozen + rowOffset + i, startCol: pos, endCol: pos + query.length })\n pos = line.indexOf(lowerQuery, pos + 1)\n }\n }\n rowOffset += block.plainTextRows.length\n }\n\n return matches\n },\n }\n}\n","/**\n * Read/query facade over a ListDocument.\n *\n * Provides text extraction, search, hit-testing, and reveal for\n * pane-based scrollback UIs. Pure data — no React, no rendering.\n */\n\nimport type { ListDocument } from \"./list-document\"\nimport type { SearchMatch } from \"./search-overlay\"\nimport { stripAnsi } from \"./unicode\"\n\nexport interface SurfaceCapabilities {\n paneSafe: boolean\n searchableHistory: boolean\n selectableHistory: boolean\n overlayHistory: boolean\n}\n\nexport interface TextSurface {\n readonly id: string\n readonly document: ListDocument\n getText(startRow: number, startCol: number, endRow: number, endCol: number): string\n search(query: string): SearchMatch[]\n hitTest(viewportRow: number, viewportCol: number): { row: number; col: number } | null\n reveal(row: number): void\n /** Notify subscribers that content has changed (search results may be stale) */\n notifyContentChange(): void\n subscribe(listener: () => void): () => void\n readonly capabilities: SurfaceCapabilities\n}\n\nexport function createTextSurface(config: {\n id: string\n document: ListDocument\n viewportToDocument: (viewportRow: number) => number\n onReveal: (documentRow: number) => void\n capabilities: SurfaceCapabilities\n}): TextSurface {\n const listeners = new Set<() => void>()\n\n function notify(): void {\n for (const fn of listeners) fn()\n }\n\n return {\n get id(): string {\n return config.id\n },\n\n get document(): ListDocument {\n return config.document\n },\n\n getText(startRow: number, startCol: number, endRow: number, endCol: number): string {\n const rows = config.document.getRows(startRow, endRow - startRow + 1)\n const lines: string[] = []\n for (let i = 0; i < rows.length; i++) {\n const plain = stripAnsi(rows[i]!)\n const row = startRow + i\n if (row === startRow && row === endRow) {\n lines.push(plain.slice(startCol, endCol))\n } else if (row === startRow) {\n lines.push(plain.slice(startCol))\n } else if (row === endRow) {\n lines.push(plain.slice(0, endCol))\n } else {\n lines.push(plain)\n }\n }\n return lines.join(\"\\n\")\n },\n\n search(query: string): SearchMatch[] {\n return config.document.search(query)\n },\n\n hitTest(viewportRow: number, viewportCol: number): { row: number; col: number } | null {\n const docRow = config.viewportToDocument(viewportRow)\n if (docRow < 0 || docRow >= config.document.totalRows) return null\n return { row: docRow, col: viewportCol }\n },\n\n reveal(row: number): void {\n config.onReveal(row)\n notify()\n },\n\n notifyContentChange(): void {\n notify()\n },\n\n subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n\n get capabilities(): SurfaceCapabilities {\n return config.capabilities\n },\n }\n}\n","/**\n * Viewport Compositor - Merges frozen history rows with the live viewport.\n *\n * When a user scrolls up into history, the compositor provides the\n * history rows that should be shown. When at the tail (scrollOffset=0),\n * the live React-rendered content is shown instead.\n *\n * scrollOffset uses bottom-origin semantics: scrollOffset=N means\n * \"show rows starting at totalHistory - N from the tail\".\n *\n * This does NOT replace the React rendering pipeline. It provides\n * overlay data that the rendering layer can use when scrolled up.\n */\n\nimport type { HistoryBuffer } from \"./history-buffer\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ViewportCompositorConfig {\n /** The history buffer containing frozen items */\n history: HistoryBuffer\n /** Height of the viewport in rows */\n viewportHeight: number\n /** Current scroll offset into history (0 = at tail/live) */\n scrollOffset: number\n}\n\nexport interface ComposedViewport {\n /** History rows to overlay at top of viewport */\n overlayRows: string[]\n /** Number of top rows occupied by history */\n overlayRowCount: number\n /** Number of bottom rows for live content */\n liveRowsVisible: number\n /** Whether viewing history */\n isScrolledUp: boolean\n /** Total scrollable height */\n totalHeight: number\n}\n\n// ============================================================================\n// Compositor\n// ============================================================================\n\nexport function composeViewport(config: ViewportCompositorConfig): ComposedViewport {\n const { history, viewportHeight, scrollOffset } = config\n\n const totalHistory = history.totalRows\n\n if (scrollOffset <= 0 || totalHistory === 0) {\n return {\n overlayRows: [],\n overlayRowCount: 0,\n liveRowsVisible: viewportHeight,\n isScrolledUp: false,\n totalHeight: totalHistory + viewportHeight,\n }\n }\n\n // scrollOffset from tail: show rows starting at (totalHistory - clampedOffset)\n const clampedOffset = Math.min(scrollOffset, totalHistory)\n const startRow = Math.max(0, totalHistory - clampedOffset)\n const rowsToShow = Math.min(viewportHeight, totalHistory - startRow)\n const overlayRows = history.getRows(startRow, rowsToShow)\n const liveRowsVisible = viewportHeight - rowsToShow\n\n return {\n overlayRows,\n overlayRowCount: rowsToShow,\n liveRowsVisible,\n isScrolledUp: true,\n totalHeight: totalHistory + viewportHeight,\n }\n}\n","/**\n * Search overlay state machine for virtual inline mode.\n *\n * Pure TEA: (action, state) -> [state, effects[]]\n * Provides incremental search-as-you-type with match navigation.\n */\n\nexport interface SearchMatch {\n row: number\n startCol: number\n endCol: number\n}\n\nexport interface SearchState {\n active: boolean\n query: string\n matches: SearchMatch[]\n currentMatch: number // Index into matches, -1 when no matches\n cursorPosition: number // Cursor position within query string\n}\n\nexport type SearchAction =\n | { type: \"open\" }\n | { type: \"close\" }\n | { type: \"input\"; char: string }\n | { type: \"backspace\" }\n | { type: \"nextMatch\" } // Enter\n | { type: \"prevMatch\" } // Shift+Enter\n | { type: \"cursorLeft\" }\n | { type: \"cursorRight\" }\n | { type: \"cursorToStart\" } // Ctrl+A / Home\n | { type: \"cursorToEnd\" } // Ctrl+E / End\n | { type: \"deleteWordBack\" } // Ctrl+W / Alt+Backspace\n | { type: \"deleteToStart\" } // Ctrl+U\n\nexport type SearchEffect = { type: \"scrollTo\"; row: number } | { type: \"render\" }\n\nexport function createSearchState(): SearchState {\n return {\n active: false,\n query: \"\",\n matches: [],\n currentMatch: -1,\n cursorPosition: 0,\n }\n}\n\n/**\n * Update search state. searchFn is called when query changes to find matches.\n */\nexport function searchUpdate(\n action: SearchAction,\n state: SearchState,\n searchFn?: (query: string) => SearchMatch[],\n): [SearchState, SearchEffect[]] {\n switch (action.type) {\n case \"open\":\n return [\n { ...state, active: true, query: \"\", matches: [], currentMatch: -1, cursorPosition: 0 },\n [{ type: \"render\" }],\n ]\n\n case \"close\":\n return [createSearchState(), [{ type: \"render\" }]]\n\n case \"input\": {\n const query = state.query.slice(0, state.cursorPosition) + action.char + state.query.slice(state.cursorPosition)\n const cursorPosition = state.cursorPosition + 1\n const matches = searchFn ? searchFn(query) : []\n const currentMatch = matches.length > 0 ? 0 : -1\n const effects: SearchEffect[] = [{ type: \"render\" }]\n if (currentMatch >= 0) {\n effects.push({ type: \"scrollTo\", row: matches[0]!.row })\n }\n return [{ ...state, query, cursorPosition, matches, currentMatch }, effects]\n }\n\n case \"backspace\": {\n if (state.cursorPosition === 0) return [state, []]\n const query = state.query.slice(0, state.cursorPosition - 1) + state.query.slice(state.cursorPosition)\n const cursorPosition = state.cursorPosition - 1\n const matches = searchFn ? searchFn(query) : []\n const currentMatch = matches.length > 0 ? 0 : -1\n const effects: SearchEffect[] = [{ type: \"render\" }]\n if (currentMatch >= 0) {\n effects.push({ type: \"scrollTo\", row: matches[0]!.row })\n }\n return [{ ...state, query, cursorPosition, matches, currentMatch }, effects]\n }\n\n case \"nextMatch\": {\n if (state.matches.length === 0) return [state, []]\n const currentMatch = (state.currentMatch + 1) % state.matches.length\n return [\n { ...state, currentMatch },\n [{ type: \"scrollTo\", row: state.matches[currentMatch]!.row }, { type: \"render\" }],\n ]\n }\n\n case \"prevMatch\": {\n if (state.matches.length === 0) return [state, []]\n const currentMatch = (state.currentMatch - 1 + state.matches.length) % state.matches.length\n return [\n { ...state, currentMatch },\n [{ type: \"scrollTo\", row: state.matches[currentMatch]!.row }, { type: \"render\" }],\n ]\n }\n\n case \"cursorLeft\":\n if (state.cursorPosition === 0) return [state, []]\n return [{ ...state, cursorPosition: state.cursorPosition - 1 }, []]\n\n case \"cursorRight\":\n if (state.cursorPosition >= state.query.length) return [state, []]\n return [{ ...state, cursorPosition: state.cursorPosition + 1 }, []]\n\n case \"cursorToStart\":\n if (state.cursorPosition === 0) return [state, []]\n return [{ ...state, cursorPosition: 0 }, []]\n\n case \"cursorToEnd\":\n if (state.cursorPosition >= state.query.length) return [state, []]\n return [{ ...state, cursorPosition: state.query.length }, []]\n\n case \"deleteWordBack\": {\n if (state.cursorPosition === 0) return [state, []]\n // Find the start of the previous word (skip trailing whitespace, then non-whitespace)\n let pos = state.cursorPosition\n while (pos > 0 && /\\s/.test(state.query[pos - 1] ?? \"\")) pos--\n while (pos > 0 && !/\\s/.test(state.query[pos - 1] ?? \"\")) pos--\n const query = state.query.slice(0, pos) + state.query.slice(state.cursorPosition)\n const cursorPosition = pos\n const matches = searchFn ? searchFn(query) : []\n const currentMatch = matches.length > 0 ? 0 : -1\n const effects: SearchEffect[] = [{ type: \"render\" }]\n if (currentMatch >= 0) {\n effects.push({ type: \"scrollTo\", row: matches[0]!.row })\n }\n return [{ ...state, query, cursorPosition, matches, currentMatch }, effects]\n }\n\n case \"deleteToStart\": {\n if (state.cursorPosition === 0) return [state, []]\n const query = state.query.slice(state.cursorPosition)\n const cursorPosition = 0\n const matches = searchFn ? searchFn(query) : []\n const currentMatch = matches.length > 0 ? 0 : -1\n const effects: SearchEffect[] = [{ type: \"render\" }]\n if (currentMatch >= 0) {\n effects.push({ type: \"scrollTo\", row: matches[0]!.row })\n }\n return [{ ...state, query, cursorPosition, matches, currentMatch }, effects]\n }\n }\n}\n\n/**\n * Render the search bar as an ANSI string.\n * Format: \" / query [2/15]\" or \" / query [no matches]\"\n */\nexport function renderSearchBar(state: SearchState, cols: number): string {\n const prefix = \" / \"\n const matchInfo =\n state.matches.length > 0\n ? ` [${state.currentMatch + 1}/${state.matches.length}]`\n : state.query\n ? \" [no matches]\"\n : \"\"\n\n const content = prefix + state.query + matchInfo\n const padded = content.padEnd(cols)\n\n // Inverse video: ESC[7m ... ESC[27m\n return `\\x1b[7m${padded}\\x1b[27m`\n}\n","/**\n * Pane Manager - Pure functions for manipulating split layout trees.\n *\n * All functions are pure: they return new layout trees, never mutate.\n * The layout tree is a binary tree where leaves are panes and internal\n * nodes are splits (horizontal or vertical) with a ratio.\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type LayoutNode =\n | { type: \"leaf\"; id: string }\n | {\n type: \"split\"\n direction: \"horizontal\" | \"vertical\"\n ratio: number\n first: LayoutNode\n second: LayoutNode\n }\n\n// ============================================================================\n// Construction\n// ============================================================================\n\n/** Create a single-pane layout */\nexport function createLeaf(id: string): LayoutNode {\n return { type: \"leaf\", id }\n}\n\n// ============================================================================\n// Tree Queries\n// ============================================================================\n\n/** Get all leaf pane IDs in depth-first left-to-right order */\nexport function getPaneIds(layout: LayoutNode): string[] {\n if (layout.type === \"leaf\") return [layout.id]\n return [...getPaneIds(layout.first), ...getPaneIds(layout.second)]\n}\n\n/** Find the next/previous pane in tab order (depth-first left-to-right) */\nexport function getTabOrder(layout: LayoutNode): string[] {\n return getPaneIds(layout)\n}\n\n// ============================================================================\n// Tree Mutations (Pure)\n// ============================================================================\n\n/**\n * Split a pane into two. Returns new layout tree with the target pane split.\n * The original pane becomes the first child; the new pane becomes the second.\n */\nexport function splitPane(\n layout: LayoutNode,\n targetPaneId: string,\n direction: \"horizontal\" | \"vertical\",\n newPaneId: string,\n ratio = 0.5,\n): LayoutNode {\n const clampedRatio = clampRatio(ratio)\n\n if (layout.type === \"leaf\") {\n if (layout.id === targetPaneId) {\n return {\n type: \"split\",\n direction,\n ratio: clampedRatio,\n first: { type: \"leaf\", id: targetPaneId },\n second: { type: \"leaf\", id: newPaneId },\n }\n }\n return layout\n }\n\n // Recurse into children\n const newFirst = splitPane(layout.first, targetPaneId, direction, newPaneId, ratio)\n const newSecond = splitPane(layout.second, targetPaneId, direction, newPaneId, ratio)\n\n if (newFirst === layout.first && newSecond === layout.second) return layout\n\n return { ...layout, first: newFirst, second: newSecond }\n}\n\n/**\n * Remove a pane from the layout. The sibling takes the full space.\n * Returns null if the removed pane was the last one.\n */\nexport function removePane(layout: LayoutNode, paneId: string): LayoutNode | null {\n if (layout.type === \"leaf\") {\n return layout.id === paneId ? null : layout\n }\n\n // Check if either direct child is the target leaf\n if (layout.first.type === \"leaf\" && layout.first.id === paneId) {\n return layout.second\n }\n if (layout.second.type === \"leaf\" && layout.second.id === paneId) {\n return layout.first\n }\n\n // Recurse\n const newFirst = removePane(layout.first, paneId)\n const newSecond = removePane(layout.second, paneId)\n\n // If a subtree collapsed, promote the survivor\n if (newFirst === null) return newSecond\n if (newSecond === null) return newFirst\n\n if (newFirst === layout.first && newSecond === layout.second) return layout\n\n return { ...layout, first: newFirst, second: newSecond }\n}\n\n/** Swap two panes' positions in the layout */\nexport function swapPanes(layout: LayoutNode, paneId1: string, paneId2: string): LayoutNode {\n if (layout.type === \"leaf\") {\n if (layout.id === paneId1) return { type: \"leaf\", id: paneId2 }\n if (layout.id === paneId2) return { type: \"leaf\", id: paneId1 }\n return layout\n }\n\n const newFirst = swapPanes(layout.first, paneId1, paneId2)\n const newSecond = swapPanes(layout.second, paneId1, paneId2)\n\n if (newFirst === layout.first && newSecond === layout.second) return layout\n\n return { ...layout, first: newFirst, second: newSecond }\n}\n\n/**\n * Resize a split: adjust the ratio of the nearest ancestor split\n * that contains the target pane as its first child.\n * Positive delta = grow (first child gets more), negative = shrink.\n */\nexport function resizeSplit(layout: LayoutNode, paneId: string, delta: number): LayoutNode {\n if (layout.type === \"leaf\") return layout\n\n const firstIds = getPaneIds(layout.first)\n\n if (firstIds.includes(paneId)) {\n // Pane is in the first child — adjust this split's ratio\n const newRatio = clampRatio(layout.ratio + delta)\n if (newRatio === layout.ratio) return layout\n\n return { ...layout, ratio: newRatio }\n }\n\n const secondIds = getPaneIds(layout.second)\n\n if (secondIds.includes(paneId)) {\n // Pane is in the second child — shrink ratio (give less to first)\n const newRatio = clampRatio(layout.ratio - delta)\n if (newRatio === layout.ratio) return layout\n\n return { ...layout, ratio: newRatio }\n }\n\n return layout\n}\n\n// ============================================================================\n// Navigation\n// ============================================================================\n\n/**\n * Find the pane adjacent to the given pane in a direction.\n *\n * For left/right: looks for siblings in horizontal splits.\n * For up/down: looks for siblings in vertical splits.\n *\n * Returns null if no adjacent pane exists in that direction.\n */\nexport function findAdjacentPane(\n layout: LayoutNode,\n paneId: string,\n direction: \"left\" | \"right\" | \"up\" | \"down\",\n): string | null {\n const path = findPath(layout, paneId)\n if (!path) return null\n\n const splitDirection = direction === \"left\" || direction === \"right\" ? \"horizontal\" : \"vertical\"\n const goToSecond = direction === \"right\" || direction === \"down\"\n\n // Walk up the path looking for a relevant split\n for (let i = path.length - 1; i >= 0; i--) {\n const step = path[i]!\n if (step.node.type !== \"split\") continue\n if (step.node.direction !== splitDirection) continue\n\n // We came from 'first' and want to go to second (right/down)\n if (step.side === \"first\" && goToSecond) {\n return firstLeaf(step.node.second)\n }\n\n // We came from 'second' and want to go to first (left/up)\n if (step.side === \"second\" && !goToSecond) {\n return lastLeaf(step.node.first)\n }\n }\n\n return null\n}\n\n// ============================================================================\n// Internal Helpers\n// ============================================================================\n\nfunction clampRatio(ratio: number): number {\n return Math.max(0.1, Math.min(0.9, ratio))\n}\n\n/** Get the first (leftmost/topmost) leaf ID */\nfunction firstLeaf(node: LayoutNode): string {\n if (node.type === \"leaf\") return node.id\n return firstLeaf(node.first)\n}\n\n/** Get the last (rightmost/bottommost) leaf ID */\nfunction lastLeaf(node: LayoutNode): string {\n if (node.type === \"leaf\") return node.id\n return lastLeaf(node.second)\n}\n\ninterface PathStep {\n node: LayoutNode\n side: \"first\" | \"second\"\n}\n\n/** Find the path from root to a leaf, recording which side we took at each split */\nfunction findPath(layout: LayoutNode, paneId: string): PathStep[] | null {\n if (layout.type === \"leaf\") {\n return layout.id === paneId ? [] : null\n }\n\n const firstPath = findPath(layout.first, paneId)\n if (firstPath !== null) {\n return [{ node: layout, side: \"first\" }, ...firstPath]\n }\n\n const secondPath = findPath(layout.second, paneId)\n if (secondPath !== null) {\n return [{ node: layout, side: \"second\" }, ...secondPath]\n }\n\n return null\n}\n","/**\n * ANSI output constants and terminal control utilities.\n *\n * NOTE: Buffer rendering is handled by pipeline/output-phase.ts.\n * This file contains only terminal control sequences and constants.\n */\n\nimport { hostname } from \"node:os\"\nimport {\n enableMouse as _enableMouse,\n disableMouse as _disableMouse,\n enableKittyKeyboard as _enableKittyKeyboard,\n disableKittyKeyboard as _disableKittyKeyboard,\n} from \"@silvery/ansi\"\n\n// ============================================================================\n// ANSI Escape Codes\n// ============================================================================\n\nconst ESC = \"\\x1b\"\nconst CSI = `${ESC}[`\n\n// Cursor control\nconst CURSOR_HIDE = `${CSI}?25l`\nconst CURSOR_SHOW = `${CSI}?25h`\nconst CURSOR_HOME = `${CSI}H`\n\n// Synchronized Update Mode (DEC private mode 2026)\n// Tells the terminal to batch output and paint atomically, preventing tearing.\n// Supported by: Ghostty, Kitty, WezTerm, iTerm2, Foot, Alacritty 0.14+, tmux 3.2+\n// Terminals that don't support it safely ignore these sequences.\nconst SYNC_BEGIN = `${CSI}?2026h`\nconst SYNC_END = `${CSI}?2026l`\n\n// Style reset\nconst RESET = `${CSI}0m`\n\n// SGR (Select Graphic Rendition) codes\nconst SGR = {\n // Attributes\n bold: 1,\n dim: 2,\n italic: 3,\n underline: 4,\n blink: 5,\n inverse: 7,\n hidden: 8,\n strikethrough: 9,\n\n // Attribute resets\n boldOff: 22, // Also resets dim\n italicOff: 23,\n underlineOff: 24,\n blinkOff: 25,\n inverseOff: 27,\n hiddenOff: 28,\n strikethroughOff: 29,\n\n // Colors (foreground)\n fgDefault: 39,\n fgBlack: 30,\n fgRed: 31,\n fgGreen: 32,\n fgYellow: 33,\n fgBlue: 34,\n fgMagenta: 35,\n fgCyan: 36,\n fgWhite: 37,\n fgBrightBlack: 90,\n fgBrightRed: 91,\n fgBrightGreen: 92,\n fgBrightYellow: 93,\n fgBrightBlue: 94,\n fgBrightMagenta: 95,\n fgBrightCyan: 96,\n fgBrightWhite: 97,\n\n // Colors (background)\n bgDefault: 49,\n bgBlack: 40,\n bgRed: 41,\n bgGreen: 42,\n bgYellow: 43,\n bgBlue: 44,\n bgMagenta: 45,\n bgCyan: 46,\n bgWhite: 47,\n bgBrightBlack: 100,\n bgBrightRed: 101,\n bgBrightGreen: 102,\n bgBrightYellow: 103,\n bgBrightBlue: 104,\n bgBrightMagenta: 105,\n bgBrightCyan: 106,\n bgBrightWhite: 107,\n} as const\n\n// ============================================================================\n// Cursor Movement\n// ============================================================================\n\n/**\n * Generate ANSI sequence to move cursor to position.\n * Terminal positions are 1-indexed.\n */\nfunction moveCursor(x: number, y: number): string {\n return `${CSI}${y + 1};${x + 1}H`\n}\n\n/**\n * Generate ANSI sequence to move cursor up N lines.\n */\nfunction cursorUp(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}A`\n return `${CSI}${n}A`\n}\n\n/**\n * Generate ANSI sequence to move cursor down N lines.\n */\nfunction cursorDown(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}B`\n return `${CSI}${n}B`\n}\n\n/**\n * Generate ANSI sequence to move cursor right N columns.\n */\nfunction cursorRight(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}C`\n return `${CSI}${n}C`\n}\n\n/**\n * Generate ANSI sequence to move cursor left N columns.\n */\nfunction cursorLeft(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}D`\n return `${CSI}${n}D`\n}\n\n/**\n * Generate ANSI sequence to move cursor to column.\n */\nfunction cursorToColumn(x: number): string {\n return `${CSI}${x + 1}G`\n}\n\n// ============================================================================\n// Cursor Shape (DECSCUSR)\n// ============================================================================\n\n/**\n * Terminal cursor shape. Combined with blink parameter in setCursorStyle().\n */\nexport type CursorShape = \"block\" | \"underline\" | \"bar\"\n\nconst CURSOR_SHAPE_CODES: Record<CursorShape, { blink: number; steady: number }> = {\n block: { blink: 1, steady: 2 },\n underline: { blink: 3, steady: 4 },\n bar: { blink: 5, steady: 6 },\n}\n\n/**\n * Set the terminal cursor shape via DECSCUSR (CSI Ps SP q).\n *\n * Supported by: xterm, Ghostty, Kitty, WezTerm, iTerm2, Alacritty, foot.\n * Terminals that don't support it safely ignore the sequence.\n *\n * @param shape - \"block\", \"underline\", or \"bar\"\n * @param blink - Whether the cursor should blink (default: false)\n */\nexport function setCursorStyle(shape: CursorShape, blink = false): string {\n const code = blink ? CURSOR_SHAPE_CODES[shape].blink : CURSOR_SHAPE_CODES[shape].steady\n return `${CSI}${code} q`\n}\n\n/**\n * Reset the terminal cursor style to the terminal's default (DECSCUSR 0).\n */\nexport function resetCursorStyle(): string {\n return `${CSI}0 q`\n}\n\n// ============================================================================\n// Terminal Control\n// ============================================================================\n\n/**\n * Enter alternate screen buffer, clear screen, and hide cursor.\n * Cursor is hidden by default - applications must explicitly show it for text input.\n *\n * The clear screen (\\x1b[2J) and cursor home (\\x1b[H) are essential after entering\n * the alternate buffer to ensure a clean slate. Without this, the terminal may have\n * leftover content from previous sessions that causes rendering artifacts like\n * content appearing at wrong Y positions (bug km-x7ih).\n */\nexport function enterAlternateScreen(): string {\n return `${CSI}?1049h${CSI}2J${CURSOR_HOME}${CURSOR_HIDE}`\n}\n\n/**\n * Leave alternate screen buffer and restore cursor.\n * Includes SYNC_END as a safety belt — ensures synchronized update mode is\n * reset even if the process was interrupted mid-render. Sending SYNC_END\n * when not in sync mode is a harmless no-op.\n */\nexport function leaveAlternateScreen(): string {\n return `${SYNC_END}${CURSOR_SHOW}${CSI}?1049l`\n}\n\n// Re-export from @silvery/ansi — canonical implementations\nexport const enableMouse = _enableMouse\nexport const disableMouse = _disableMouse\n\n/**\n * Kitty keyboard protocol flags (bitfield).\n *\n * | Flag | Bit | Description |\n * | ---- | --- | ---------------------------------------------- |\n * | 1 | 0 | Disambiguate escape codes |\n * | 2 | 1 | Report event types (press/repeat/release) |\n * | 4 | 2 | Report alternate keys |\n * | 8 | 3 | Report all keys as escape codes |\n * | 16 | 4 | Report associated text |\n */\nexport const KittyFlags = {\n DISAMBIGUATE: 1,\n REPORT_EVENTS: 2,\n REPORT_ALTERNATE: 4,\n REPORT_ALL_KEYS: 8,\n REPORT_TEXT: 16,\n} as const\n\n/**\n * Enable Kitty keyboard protocol (push mode).\n * Sends CSI > flags u to opt into the specified modes.\n * Default flags=11 (DISAMBIGUATE | REPORT_EVENTS | REPORT_ALL_KEYS) —\n * enables modifier-only key reporting needed for useModifierKeys() Cmd tracking.\n * Supported: Ghostty, Kitty, WezTerm, foot. Ignored by unsupported terminals.\n *\n * @param flags Bitfield of KittyFlags (default: DISAMBIGUATE)\n */\nexport const enableKittyKeyboard = _enableKittyKeyboard\nexport function queryKittyKeyboard(): string {\n return `${CSI}?u`\n}\nexport const disableKittyKeyboard = _disableKittyKeyboard\n\n// ============================================================================\n// Terminal Notifications\n// ============================================================================\n\n/** BEL character — basic terminal bell/notification */\nexport const BEL = \"\\x07\"\n\n/** iTerm2 notification (OSC 9) */\nexport function notifyITerm2(message: string): string {\n return `${ESC}]9;${message}${BEL}`\n}\n\n/** Kitty notification (OSC 99) with optional title */\nexport function notifyKitty(message: string, opts?: { title?: string }): string {\n const params = opts?.title ? `;t=t;${opts.title}` : \"\"\n return `${ESC}]99;i=1:d=0${params};${message}${ESC}\\\\`\n}\n\n/**\n * Send a terminal notification using the best available method.\n *\n * Auto-detects terminal type via TERM_PROGRAM / TERM env vars:\n * - iTerm2 → OSC 9\n * - Kitty → OSC 99\n * - Others → BEL (audible/visual bell)\n */\nexport function notify(stdout: NodeJS.WriteStream, message: string, opts?: { title?: string }): void {\n const termProgram = process.env.TERM_PROGRAM ?? \"\"\n const term = process.env.TERM ?? \"\"\n\n if (termProgram === \"iTerm.app\") {\n stdout.write(notifyITerm2(message))\n } else if (term === \"xterm-kitty\") {\n stdout.write(notifyKitty(message, opts))\n } else {\n stdout.write(BEL)\n }\n}\n\n// ============================================================================\n// Window Title (OSC 0/2)\n// ============================================================================\n\n/**\n * Set the terminal window title using OSC 2 (window title only).\n * Does not affect icon title (tab name in some terminals).\n * Widely supported: xterm, Ghostty, iTerm2, Kitty, WezTerm, Alacritty, foot.\n */\nexport function setWindowTitle(stdout: NodeJS.WriteStream, title: string): void {\n stdout.write(`${ESC}]2;${title}${BEL}`)\n}\n\n/**\n * Set both the window title and icon title using OSC 0.\n * Some terminals treat OSC 0 as equivalent to OSC 2; others also change the\n * dock/taskbar icon name.\n */\nexport function setWindowAndIconTitle(stdout: NodeJS.WriteStream, title: string): void {\n stdout.write(`${ESC}]0;${title}${BEL}`)\n}\n\n/**\n * Reset the terminal window title by sending an empty OSC 2 sequence.\n * The terminal typically reverts to its default title (shell command, etc.).\n */\nexport function resetWindowTitle(stdout: NodeJS.WriteStream): void {\n stdout.write(`${ESC}]2;${BEL}`)\n}\n\n// ============================================================================\n// Directory Reporting\n// ============================================================================\n\n/** Report current working directory to the terminal via OSC 7.\n * Used by terminals (iTerm2, Ghostty, WezTerm) for tab/split directory inheritance.\n */\nexport function reportDirectory(stdout: NodeJS.WriteStream, path: string): void {\n // OSC 7 format: ESC ] 7 ; file://hostname/path BEL\n const host = hostname()\n const encoded = encodeURI(path).replace(/#/g, \"%23\")\n stdout.write(`${ESC}]7;file://${host}${encoded}${BEL}`)\n}\n\n// ============================================================================\n// Mouse Cursor Shape (OSC 22)\n// ============================================================================\n\n/**\n * Mouse cursor shape names for OSC 22.\n *\n * Uses X11/CSS cursor names. Supported by: Ghostty, Kitty (>=0.33), foot,\n * WezTerm (partial). Terminals that don't support OSC 22 safely ignore it.\n */\nexport type MouseCursorShape = \"default\" | \"text\" | \"pointer\" | \"crosshair\" | \"move\" | \"not-allowed\" | \"wait\" | \"help\"\n\n/**\n * Generate OSC 22 sequence to set the mouse cursor shape.\n *\n * @param shape - X11/CSS cursor name\n * @returns ANSI escape sequence string\n */\nexport function setMouseCursorShape(shape: MouseCursorShape): string {\n return `${ESC}]22;${shape}${BEL}`\n}\n\n/**\n * Generate OSC 22 sequence to reset mouse cursor to default.\n *\n * @returns ANSI escape sequence string\n */\nexport function resetMouseCursorShape(): string {\n return `${ESC}]22;default${BEL}`\n}\n\n// ============================================================================\n// Export Constants\n// ============================================================================\n\nexport const ANSI = {\n ESC,\n CSI,\n CURSOR_HIDE,\n CURSOR_SHOW,\n CURSOR_HOME,\n SYNC_BEGIN,\n SYNC_END,\n RESET,\n SGR,\n moveCursor,\n cursorUp,\n cursorDown,\n cursorLeft,\n cursorRight,\n cursorToColumn,\n} as const\n","/**\n * useExit — programmatic exit hook.\n *\n * Thin wrapper over useApp().exit that throws outside a runtime\n * (unlike useApp which returns no-ops in static mode).\n *\n * Prefer `return \"exit\"` from useInput handlers when possible.\n * Use useExit() for imperative exit from event handlers, timers, etc.\n */\n\nimport { useContext } from \"react\"\nimport { RuntimeContext } from \"../context\"\n\n/**\n * Returns a function that exits the app.\n * Throws if called outside a runtime (run(), createApp(), test renderer).\n */\nexport function useExit(): () => void {\n const rt = useContext(RuntimeContext)\n if (!rt) throw new Error(\"useExit must be used within run() or createApp()\")\n return rt.exit\n}\n","/**\n * Terminal scroll region (DECSTBM) utilities.\n *\n * Scroll regions tell the terminal to natively scroll content within\n * a defined row range, which is faster than re-rendering all cells.\n *\n * DECSTBM (DEC Set Top and Bottom Margins) is supported by most modern\n * terminal emulators: xterm, iTerm2, Kitty, Ghostty, WezTerm, etc.\n */\n\nconst ESC = \"\\x1b\"\n\n/** Set terminal scroll region (1-indexed top and bottom rows). */\nexport function setScrollRegion(stdout: NodeJS.WriteStream, top: number, bottom: number): void {\n stdout.write(`${ESC}[${top};${bottom}r`)\n}\n\n/** Reset scroll region to full terminal. */\nexport function resetScrollRegion(stdout: NodeJS.WriteStream): void {\n stdout.write(`${ESC}[r`)\n}\n\n/** Scroll content up by N lines within the current scroll region. */\nexport function scrollUp(stdout: NodeJS.WriteStream, lines: number = 1): void {\n stdout.write(`${ESC}[${lines}S`)\n}\n\n/** Scroll content down by N lines within the current scroll region. */\nexport function scrollDown(stdout: NodeJS.WriteStream, lines: number = 1): void {\n stdout.write(`${ESC}[${lines}T`)\n}\n\n/** Move cursor to a specific position (1-indexed row and column). */\nexport function moveCursor(stdout: NodeJS.WriteStream, row: number, col: number): void {\n stdout.write(`${ESC}[${row};${col}H`)\n}\n\nexport interface ScrollRegionConfig {\n /** Top row of the scrollable area (0-indexed). */\n top: number\n /** Bottom row of the scrollable area (0-indexed). */\n bottom: number\n /** Whether scroll region optimization is enabled. */\n enabled: boolean\n}\n\n/**\n * Detect if the terminal likely supports DECSTBM scroll regions.\n *\n * Most modern terminals do (xterm, iTerm2, Kitty, Ghostty, WezTerm, etc.)\n * but some (e.g., Linux console) may not handle them correctly.\n */\nexport function supportsScrollRegions(): boolean {\n const term = process.env.TERM ?? \"\"\n const termProgram = process.env.TERM_PROGRAM ?? \"\"\n\n // Known-good terminal programs\n if (termProgram === \"iTerm.app\" || termProgram === \"WezTerm\" || termProgram === \"ghostty\" || termProgram === \"vscode\")\n return true\n\n // Known-good TERM values\n if (term.includes(\"xterm\") || term.includes(\"screen\") || term.includes(\"tmux\") || term.includes(\"kitty\")) return true\n\n // Linux console doesn't support DECSTBM\n if (term === \"linux\") return false\n\n // Default: assume support for any term that's not empty\n return term !== \"\"\n}\n","/**\n * useCursor - Show and position the terminal's blinking cursor.\n *\n * Maps component-relative (col, row) to absolute terminal coordinates\n * using useScrollRect. Per-instance last-writer-wins: only one cursor\n * can be active at a time per silvery instance (the terminal has one hardware cursor).\n *\n * Cursor state is isolated per silvery instance via CursorContext. Each runtime\n * (run(), createApp(), test renderer) provides its own CursorProvider so\n * multiple silvery instances don't fight over cursor position.\n *\n * Falls back to module-level globals when no CursorProvider is present\n * (backward compatibility / deprecation path).\n */\n\nimport React, {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n type ReactNode,\n} from \"react\"\nimport type { CursorShape } from \"@silvery/ag-term/output\"\nimport type { BoxProps } from \"@silvery/ag/types\"\nimport { NodeContext } from \"@silvery/ag-react/context\"\nimport { useScrollRect, type Rect } from \"./useLayout\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CursorPosition {\n /** Column offset within the component (0-indexed) */\n col: number\n /** Row offset within the component (0-indexed) */\n row: number\n /** Whether the cursor should be visible. Default: true */\n visible?: boolean\n /** Terminal cursor shape (DECSCUSR). Default: terminal default */\n shape?: CursorShape\n}\n\nexport interface CursorState {\n /** Absolute terminal X position (0-indexed) */\n x: number\n /** Absolute terminal Y position (0-indexed) */\n y: number\n /** Whether cursor is visible */\n visible: boolean\n /** Terminal cursor shape (DECSCUSR) */\n shape?: CursorShape\n}\n\n// ============================================================================\n// Cursor Accessors — imperative interface for non-React consumers\n// ============================================================================\n\n/**\n * Imperative cursor accessors for non-React code (scheduler, output phase).\n * Created by createCursorStore() and threaded to consumers that can't use hooks.\n */\nexport interface CursorAccessors {\n getCursorState(): CursorState | null\n subscribeCursor(listener: () => void): () => void\n}\n\n// ============================================================================\n// Cursor Store — per-instance state + accessors\n// ============================================================================\n\nexport interface CursorStore {\n state: CursorState | null\n listeners: Set<() => void>\n accessors: CursorAccessors\n setCursorState(state: CursorState | null): void\n}\n\n/**\n * Create an isolated cursor store. Each silvery instance gets one.\n * Returns the store (for CursorProvider) and accessors (for scheduler/output).\n */\nexport function createCursorStore(): CursorStore {\n const store: CursorStore = {\n state: null,\n listeners: new Set(),\n accessors: null!,\n setCursorState(s: CursorState | null) {\n store.state = s\n for (const listener of store.listeners) listener()\n },\n }\n store.accessors = {\n getCursorState: () => store.state,\n subscribeCursor: (listener: () => void) => {\n store.listeners.add(listener)\n return () => {\n store.listeners.delete(listener)\n }\n },\n }\n return store\n}\n\n// ============================================================================\n// React Context\n// ============================================================================\n\nconst CursorCtx = createContext<CursorStore | null>(null)\n\n/**\n * Provider that gives its subtree an isolated cursor store.\n * Wrap your silvery app root in this to isolate cursor state per instance.\n */\nexport function CursorProvider({ store, children }: { store: CursorStore; children?: ReactNode }) {\n return React.createElement(CursorCtx.Provider, { value: store }, children)\n}\n\n// ============================================================================\n// Module-level Fallback (deprecated — for bare render() without provider)\n// ============================================================================\n\nlet _globalCursorState: CursorState | null = null\nlet _globalCursorListeners = new Set<() => void>()\n\nfunction globalSetCursorState(state: CursorState | null): void {\n _globalCursorState = state\n for (const listener of _globalCursorListeners) listener()\n}\n\nfunction globalGetCursorState(): CursorState | null {\n return _globalCursorState\n}\n\nfunction globalSubscribeCursor(listener: () => void): () => void {\n _globalCursorListeners.add(listener)\n return () => {\n _globalCursorListeners.delete(listener)\n }\n}\n\n/** For testing -- reset global fallback state between tests. */\nexport function resetCursorState(): void {\n _globalCursorState = null\n _globalCursorListeners = new Set()\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\n/**\n * Show and position the terminal's blinking cursor within this component.\n *\n * The cursor position is relative to the component's screen position.\n * Only one cursor can be active per silvery instance -- last caller with visible=true wins.\n *\n * Uses CursorContext if a CursorProvider is present (per-instance isolation).\n * Falls back to module-level globals otherwise (backward compat).\n */\nexport function useCursor(position: CursorPosition): void {\n const { col, row, visible = true, shape } = position\n const store = useContext(CursorCtx)\n const node = useContext(NodeContext)\n\n // Resolve set/get functions from context or global fallback\n const set = store ? store.setCursorState.bind(store) : globalSetCursorState\n const get = store ? store.accessors.getCursorState : globalGetCursorState\n\n // Compute content area offset from the parent node's border + padding.\n // useScrollRect provides the node's border-box rect, but cursor\n // col/row are relative to the content area (inside border + padding).\n const props = node?.props as BoxProps | undefined\n const padLeft = props?.paddingLeft ?? props?.paddingX ?? props?.padding ?? 0\n const padTop = props?.paddingTop ?? props?.paddingY ?? props?.padding ?? 0\n const borderLeft = props?.borderStyle ? 1 : 0\n const borderTop = props?.borderStyle ? 1 : 0\n const contentOffsetX = borderLeft + padLeft\n const contentOffsetY = borderTop + padTop\n\n // Keep current args in refs so the callback always reads fresh values\n const colRef = useRef(col)\n const rowRef = useRef(row)\n const visibleRef = useRef(visible)\n const shapeRef = useRef(shape)\n const setRef = useRef(set)\n const getRef = useRef(get)\n const lastRectRef = useRef<Rect | null>(null)\n const contentOffsetXRef = useRef(contentOffsetX)\n const contentOffsetYRef = useRef(contentOffsetY)\n colRef.current = col\n rowRef.current = row\n visibleRef.current = visible\n shapeRef.current = shape\n setRef.current = set\n getRef.current = get\n contentOffsetXRef.current = contentOffsetX\n contentOffsetYRef.current = contentOffsetY\n\n // Called synchronously during layout (useLayoutEffect) whenever\n // the component's screen position changes.\n useScrollRect(\n useCallback((rect) => {\n lastRectRef.current = rect\n if (!visibleRef.current) {\n return\n }\n setRef.current({\n x: rect.x + contentOffsetXRef.current + colRef.current,\n y: rect.y + contentOffsetYRef.current + rowRef.current,\n visible: true,\n shape: shapeRef.current,\n })\n }, []),\n )\n\n // When col/row/shape change without a layout change, update cursor\n // position from the last known screen rect. This handles the common case\n // where typing moves the cursor within a component but the component's\n // layout position stays the same (e.g., TextInput cursor moves on keystroke).\n useLayoutEffect(() => {\n const rect = lastRectRef.current\n if (!rect || !visible) return\n set({\n x: rect.x + contentOffsetX + col,\n y: rect.y + contentOffsetY + row,\n visible: true,\n shape,\n })\n }, [col, row, contentOffsetX, contentOffsetY, shape, visible, set])\n\n // On unmount or when visible becomes false, clear cursor state\n useEffect(() => {\n if (!visible) {\n // If we are hiding, clear state now\n const current = getRef.current()\n if (current) {\n setRef.current(null)\n }\n }\n\n return () => {\n // On unmount, clear cursor state\n setRef.current(null)\n }\n }, [visible])\n}\n\n// ============================================================================\n// Exports for scheduler integration\n// ============================================================================\n\n/**\n * @deprecated Use CursorAccessors from createCursorStore() instead.\n * These module-level functions are the global fallback for backward compat.\n */\nfunction getCursorState(): CursorState | null {\n return globalGetCursorState()\n}\n\nfunction subscribeCursor(listener: () => void): () => void {\n return globalSubscribeCursor(listener)\n}\n\nexport { getCursorState, subscribeCursor }\n","/**\n * ErrorBoundary — Built-in error boundary for silvery apps.\n *\n * Catches render errors in the component tree and displays a rich\n * error message with source location, code excerpt, and stack trace\n * using low-level silvery host elements (no dependency on Box/Text\n * from @silvery/ag-react/ui). This is the default root wrapper for all\n * silvery apps — createApp() and run() wrap the element tree with it\n * automatically.\n *\n * Uses `silvery-box` and `silvery-text` host elements directly to\n * avoid circular deps with higher-level component libraries.\n *\n * @packageDocumentation\n */\n\nimport * as fs from \"node:fs\"\nimport React, { Component } from \"react\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface SilveryErrorBoundaryProps {\n children?: React.ReactNode\n /** Called when an error is caught. Use for logging or cleanup. */\n onError?: (error: Error) => void\n}\n\ninterface SilveryErrorBoundaryState {\n error: Error | null\n}\n\n// ============================================================================\n// Stack parsing utilities\n// ============================================================================\n\n/**\n * Parse a stack line to extract function name, file, line, column.\n * Handles both `at Foo (file:line:col)` and `at file:line:col` formats.\n */\nfunction parseStackLine(line: string): { function?: string; file?: string; line?: number; column?: number } | null {\n const trimmed = line.trim()\n if (!trimmed.startsWith(\"at \")) return null\n\n const rest = trimmed.slice(3)\n // Match: functionName (file:line:col)\n const match1 = rest.match(/^(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)$/)\n if (match1) {\n return {\n function: match1[1],\n file: match1[2],\n line: Number(match1[3]),\n column: Number(match1[4]),\n }\n }\n // Match: file:line:col (no function name)\n const match2 = rest.match(/^(.+?):(\\d+):(\\d+)$/)\n if (match2) {\n return { file: match2[1], line: Number(match2[2]), column: Number(match2[3]) }\n }\n return null\n}\n\n/**\n * Clean up file path by removing cwd prefix and file:// protocol.\n */\nfunction cleanupPath(filePath: string | undefined): string | undefined {\n if (!filePath) return filePath\n let p = filePath\n const cwdPath = process.cwd()\n // Remove file:// protocol\n p = p.replace(/^file:\\/\\//, \"\")\n // Remove cwd prefix\n for (const prefix of [cwdPath, `/private${cwdPath}`]) {\n if (p.startsWith(`${prefix}/`)) {\n p = p.slice(prefix.length + 1)\n break\n }\n }\n return p\n}\n\n/**\n * Get source code excerpt around a line number (±3 lines).\n */\nfunction getCodeExcerpt(filePath: string, line: number): Array<{ line: number; value: string }> | null {\n try {\n if (!fs.existsSync(filePath)) return null\n const source = fs.readFileSync(filePath, \"utf8\")\n const lines = source.split(\"\\n\")\n const start = Math.max(0, line - 4)\n const end = Math.min(lines.length, line + 3)\n const result: Array<{ line: number; value: string }> = []\n for (let i = start; i < end; i++) {\n result.push({ line: i + 1, value: (lines[i] ?? \"\").replace(/\\t/g, \" \") })\n }\n return result\n } catch {\n return null\n }\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * Rich error boundary for silvery's runtime layer.\n *\n * Must be a class component (React limitation for error boundaries).\n * Renders error info using silvery-box/silvery-text host elements — no Box/Text dependency.\n * Shows: ERROR label, error message, file location, source code excerpt, and stack trace.\n */\nexport class SilveryErrorBoundary extends Component<SilveryErrorBoundaryProps, SilveryErrorBoundaryState> {\n override state: SilveryErrorBoundaryState = { error: null }\n\n static getDerivedStateFromError(error: Error): SilveryErrorBoundaryState {\n return { error }\n }\n\n override componentDidCatch(error: Error) {\n this.props.onError?.(error)\n }\n\n override render() {\n if (this.state.error) {\n const err = this.state.error\n const stack = err.stack ? err.stack.split(\"\\n\").slice(1) : []\n const origin = stack.length > 0 ? parseStackLine(stack[0]!) : null\n const filePath = cleanupPath(origin?.file)\n\n // Get source code excerpt\n let excerpt: Array<{ line: number; value: string }> | null = null\n let lineWidth = 0\n if (filePath && origin?.line) {\n excerpt = getCodeExcerpt(filePath, origin.line)\n if (excerpt) {\n for (const { line } of excerpt) {\n lineWidth = Math.max(lineWidth, String(line).length)\n }\n }\n }\n\n // Build the error display using silvery host elements\n // Format: padding=1 column layout with ERROR label, location, code, and stack\n const children: React.ReactNode[] = []\n\n // ERROR label + message\n children.push(\n React.createElement(\n \"silvery-box\",\n { key: \"header\" },\n React.createElement(\"silvery-text\", { backgroundColor: \"red\", color: \"white\" }, \" ERROR \"),\n React.createElement(\"silvery-text\", {}, ` ${err.message}`),\n ),\n )\n\n // File location\n if (filePath && origin) {\n children.push(\n React.createElement(\n \"silvery-box\",\n { key: \"location\", marginTop: 1 },\n React.createElement(\"silvery-text\", { dimColor: true }, `${filePath}:${origin.line}:${origin.column}`),\n ),\n )\n }\n\n // Source code excerpt\n if (excerpt && origin) {\n const codeLines = excerpt.map(({ line, value }) => {\n const lineNum = String(line).padStart(lineWidth, \" \")\n return React.createElement(\n \"silvery-box\",\n { key: `code-${line}` },\n React.createElement(\n \"silvery-text\",\n {\n dimColor: line !== origin.line,\n backgroundColor: line === origin.line ? \"red\" : undefined,\n color: line === origin.line ? \"white\" : undefined,\n },\n `${lineNum}:`,\n ),\n React.createElement(\n \"silvery-text\",\n {\n backgroundColor: line === origin.line ? \"red\" : undefined,\n color: line === origin.line ? \"white\" : undefined,\n },\n ` ${value}`,\n ),\n )\n })\n children.push(\n React.createElement(\"silvery-box\", { key: \"code\", marginTop: 1, flexDirection: \"column\" }, ...codeLines),\n )\n }\n\n // Stack trace\n if (stack.length > 0) {\n const stackLines = stack.map((line, i) => {\n const parsed = parseStackLine(line)\n if (!parsed) {\n return React.createElement(\n \"silvery-box\",\n { key: `stack-${i}` },\n React.createElement(\"silvery-text\", { dimColor: true }, `- ${line.trim()}`),\n )\n }\n const cleanFile = cleanupPath(parsed.file)\n return React.createElement(\n \"silvery-box\",\n { key: `stack-${i}` },\n React.createElement(\"silvery-text\", { dimColor: true }, \"- \"),\n React.createElement(\"silvery-text\", { dimColor: true, bold: true }, parsed.function ?? \"\"),\n React.createElement(\n \"silvery-text\",\n { dimColor: true, color: \"gray\" },\n ` (${cleanFile ?? \"\"}:${parsed.line}:${parsed.column})`,\n ),\n )\n })\n children.push(\n React.createElement(\"silvery-box\", { key: \"stack\", marginTop: 1, flexDirection: \"column\" }, ...stackLines),\n )\n }\n\n return React.createElement(\"silvery-box\", { flexDirection: \"column\", padding: 1 }, ...children)\n }\n return this.props.children\n }\n}\n","/**\n * Focus Queries — pure tree query functions for the silvery focus system.\n *\n * All functions are pure: no state, no React, no side effects.\n * They operate on the SilveryNode tree to resolve focusable elements,\n * tab order, spatial navigation targets, and explicit focus links.\n */\n\nimport type { AgNode, Rect } from \"./types\"\n\n// ============================================================================\n// Focusable Detection\n// ============================================================================\n\n/** Check if a node has the focusable prop set to true (or truthy). */\nfunction isFocusable(node: AgNode): boolean {\n if (node.hidden) return false\n const props = node.props as Record<string, unknown>\n return Boolean(props.focusable) && props.display !== \"none\"\n}\n\n/** Check if a node creates a focus scope (isolated Tab cycle). */\nfunction isFocusScope(node: AgNode): boolean {\n const props = node.props as Record<string, unknown>\n return Boolean(props.focusScope)\n}\n\n// ============================================================================\n// Tree Queries\n// ============================================================================\n\n/**\n * Walk up from node to nearest ancestor (or self) with focusable prop.\n * Useful for mouse clicks — find the focusable target from a deep text node.\n */\nexport function findFocusableAncestor(node: AgNode): AgNode | null {\n let current: AgNode | null = node\n while (current) {\n if (isFocusable(current)) return current\n current = current.parent\n }\n return null\n}\n\n/**\n * DFS traversal of focusable nodes in tab order, optionally scoped.\n *\n * When scope is provided, only nodes within that scope subtree are included.\n * If a focusScope node is encountered during traversal, its children are\n * skipped (they belong to a different scope), unless that scope IS the\n * provided scope node.\n */\nexport function getTabOrder(root: AgNode, scope?: AgNode): AgNode[] {\n const result: AgNode[] = []\n const walkRoot = scope ?? root\n\n function walk(node: AgNode): void {\n // Skip hidden nodes (Suspense) and display: none — entire subtree is excluded\n if (node.hidden) return\n const props = node.props as Record<string, unknown>\n if (props.display === \"none\") return\n\n // If this node is a focusScope boundary and it's NOT the walk root,\n // skip its children — they belong to a different Tab cycle.\n // The focusScope node itself may still be focusable (included below).\n if (node !== walkRoot && isFocusScope(node)) {\n // Include the scope node itself if it's focusable, but don't descend\n if (isFocusable(node)) {\n result.push(node)\n }\n return\n }\n\n if (isFocusable(node)) {\n result.push(node)\n }\n\n for (const child of node.children) {\n walk(child)\n }\n }\n\n walk(walkRoot)\n return result\n}\n\n/**\n * Walk up from a node to find the nearest ancestor (or self) with focusScope prop.\n * Returns the testID of the enclosing scope, or null if none found.\n */\nexport function findEnclosingScope(node: AgNode): string | null {\n let current: AgNode | null = node\n while (current) {\n if (isFocusScope(current)) {\n const props = current.props as Record<string, unknown>\n return typeof props.testID === \"string\" ? props.testID : null\n }\n current = current.parent\n }\n return null\n}\n\n/**\n * Find a node by testID in the subtree rooted at root.\n * DFS, returns the first match.\n */\nexport function findByTestID(root: AgNode, testID: string): AgNode | null {\n const props = root.props as Record<string, unknown>\n if (props.testID === testID) return root\n\n for (const child of root.children) {\n const found = findByTestID(child, testID)\n if (found) return found\n }\n return null\n}\n\n// ============================================================================\n// Spatial Navigation\n// ============================================================================\n\n/**\n * Compute center point of a Rect.\n */\nfunction rectCenter(rect: Rect): { cx: number; cy: number } {\n return {\n cx: rect.x + rect.width / 2,\n cy: rect.y + rect.height / 2,\n }\n}\n\n/**\n * Check if a candidate point falls within a 45-degree cone from source\n * in the given direction (tvOS-style spatial navigation).\n *\n * The cone extends from the center of the source rect in the specified\n * direction with a 45-degree half-angle (90-degree total aperture).\n */\nfunction isInCone(\n source: { cx: number; cy: number },\n candidate: { cx: number; cy: number },\n direction: \"up\" | \"down\" | \"left\" | \"right\",\n): boolean {\n const dx = candidate.cx - source.cx\n const dy = candidate.cy - source.cy\n\n // Must be in the correct general direction\n switch (direction) {\n case \"up\":\n if (dy >= 0) return false\n // Within 45-degree cone: |dx| <= |dy|\n return Math.abs(dx) <= Math.abs(dy)\n case \"down\":\n if (dy <= 0) return false\n return Math.abs(dx) <= Math.abs(dy)\n case \"left\":\n if (dx >= 0) return false\n return Math.abs(dy) <= Math.abs(dx)\n case \"right\":\n if (dx <= 0) return false\n return Math.abs(dy) <= Math.abs(dx)\n }\n}\n\n/**\n * Euclidean distance between two points.\n */\nfunction distance(a: { cx: number; cy: number }, b: { cx: number; cy: number }): number {\n const dx = a.cx - b.cx\n const dy = a.cy - b.cy\n return Math.sqrt(dx * dx + dy * dy)\n}\n\n/**\n * Find the nearest focusable candidate in a given direction using\n * 45-degree cone heuristic (tvOS-style spatial navigation).\n *\n * From the center of the source rect, draw a cone in the target direction.\n * Filter candidates whose center falls within the cone. Pick the closest\n * by Euclidean distance.\n *\n * @param from - The currently focused node\n * @param direction - Direction to search\n * @param candidates - All focusable nodes to consider\n * @param layoutFn - Function to get screen rect for a node (null if not laid out)\n */\nexport function findSpatialTarget(\n from: AgNode,\n direction: \"up\" | \"down\" | \"left\" | \"right\",\n candidates: AgNode[],\n layoutFn: (node: AgNode) => Rect | null,\n): AgNode | null {\n const sourceRect = layoutFn(from)\n if (!sourceRect) return null\n\n const source = rectCenter(sourceRect)\n\n let best: AgNode | null = null\n let bestDist = Infinity\n\n for (const candidate of candidates) {\n if (candidate === from) continue\n\n const candidateRect = layoutFn(candidate)\n if (!candidateRect) continue\n\n const target = rectCenter(candidateRect)\n\n if (!isInCone(source, target, direction)) continue\n\n const dist = distance(source, target)\n if (dist < bestDist) {\n bestDist = dist\n best = candidate\n }\n }\n\n return best\n}\n\n// ============================================================================\n// Explicit Focus Links\n// ============================================================================\n\n/**\n * Check if a node has an explicit nextFocus{Direction} override prop.\n *\n * These props allow components to declare explicit focus targets for\n * spatial navigation, overriding the cone heuristic.\n *\n * @param node - The node to check\n * @param direction - Direction string: \"up\", \"down\", \"left\", \"right\"\n * @returns The testID of the explicit target, or null\n */\nexport function getExplicitFocusLink(node: AgNode, direction: string): string | null {\n const props = node.props as Record<string, unknown>\n // Props follow the pattern: nextFocusUp, nextFocusDown, nextFocusLeft, nextFocusRight\n const propName = `nextFocus${direction.charAt(0).toUpperCase()}${direction.slice(1)}`\n const value = props[propName]\n return typeof value === \"string\" ? value : null\n}\n","/**\n * Interactive Signal Utilities\n *\n * Writer functions for per-node interactive state. State machines call these\n * during event processing to update hovered/armed/selected/focused/dropTarget\n * on AgNode instances.\n *\n * Each setter returns true if the value actually changed — callers can use\n * this for efficient dirty tracking (skip re-render if nothing changed).\n *\n * The InteractiveState object is lazily created on first write to avoid\n * allocating on nodes that never receive interactive events.\n */\n\nimport type { AgNode, InteractiveState } from \"./types\"\n\n// ============================================================================\n// Lazy Initialization\n// ============================================================================\n\n/**\n * Ensure a node has an InteractiveState object, creating one if needed.\n * Returns the (possibly newly created) state.\n */\nexport function ensureInteractiveState(node: AgNode): InteractiveState {\n if (!node.interactiveState) {\n node.interactiveState = {\n hovered: false,\n armed: false,\n selected: false,\n focused: false,\n dropTarget: false,\n }\n }\n return node.interactiveState\n}\n\n// ============================================================================\n// Individual Setters (return true if value changed)\n// ============================================================================\n\n/**\n * Set the hovered state. Returns true if the value changed.\n */\nexport function setHovered(node: AgNode, value: boolean): boolean {\n const state = ensureInteractiveState(node)\n if (state.hovered === value) return false\n state.hovered = value\n return true\n}\n\n/**\n * Set the armed state (pointer-down, awaiting click). Returns true if the value changed.\n */\nexport function setArmed(node: AgNode, value: boolean): boolean {\n const state = ensureInteractiveState(node)\n if (state.armed === value) return false\n state.armed = value\n return true\n}\n\n/**\n * Set the selected state. Returns true if the value changed.\n */\nexport function setSelected(node: AgNode, value: boolean): boolean {\n const state = ensureInteractiveState(node)\n if (state.selected === value) return false\n state.selected = value\n return true\n}\n\n/**\n * Set the focused state. Returns true if the value changed.\n */\nexport function setFocused(node: AgNode, value: boolean): boolean {\n const state = ensureInteractiveState(node)\n if (state.focused === value) return false\n state.focused = value\n return true\n}\n\n/**\n * Set the dropTarget state. Returns true if the value changed.\n */\nexport function setDropTarget(node: AgNode, value: boolean): boolean {\n const state = ensureInteractiveState(node)\n if (state.dropTarget === value) return false\n state.dropTarget = value\n return true\n}\n\n// ============================================================================\n// Batch Operations\n// ============================================================================\n\n/**\n * Clear all interactive state on a node.\n * Useful on pointer-up, focus-change, or when a node is removed from the tree.\n *\n * Sets the interactiveState reference to null to free the object.\n */\nexport function clearInteractiveState(node: AgNode): void {\n node.interactiveState = null\n}\n","/**\n * Focus Manager — standalone state container for the silvery focus system.\n *\n * Pure TypeScript, no React dependency. The subscribe/getSnapshot pattern\n * enables useSyncExternalStore in hooks.\n *\n * Replaces the flat focus list in context.ts (FocusContext with focusables Map).\n */\n\nimport type { AgNode, Rect } from \"./types\"\nimport {\n findByTestID,\n findFocusableAncestor,\n getTabOrder,\n findSpatialTarget,\n getExplicitFocusLink,\n} from \"./focus-queries\"\nimport { setFocused } from \"./interactive-signals\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type FocusOrigin = \"keyboard\" | \"mouse\" | \"programmatic\"\n\n/**\n * Callback fired when focus changes. Used by the runtime to dispatch\n * DOM-level focus/blur events without coupling FocusManager to the event system.\n *\n * @param oldNode - The node losing focus (null if nothing was focused)\n * @param newNode - The node gaining focus (null on blur)\n * @param origin - How focus was acquired\n */\nexport type FocusChangeCallback = (oldNode: AgNode | null, newNode: AgNode | null, origin: FocusOrigin | null) => void\n\nexport interface FocusSnapshot {\n activeId: string | null\n previousId: string | null\n focusOrigin: FocusOrigin | null\n scopeStack: readonly string[]\n /** The currently active peer scope (WPF FocusScope model) */\n activeScopeId: string | null\n}\n\nexport interface FocusManagerOptions {\n /** Called when focus changes — wire up event dispatch here */\n onFocusChange?: FocusChangeCallback\n}\n\n/**\n * Options for registering a hook-based (virtual) focusable.\n *\n * Hook focusables are registered via React hooks (e.g. `useFocus()` in the\n * Ink compat layer) rather than by the `focusable` prop on a tree node. They\n * participate in Tab cycling but don't have a backing `AgNode` — activeId\n * tracking is by id only, and `activeElement` is null when a hook focusable\n * is the active target.\n */\nexport interface HookFocusableOptions {\n /** Registration is inert when false — skipped in tab order, never reports focused */\n isActive?: boolean\n /** Focus this id when registered (only when isActive !== false) */\n autoFocus?: boolean\n}\n\nexport interface FocusManager {\n /** Currently focused node */\n readonly activeElement: AgNode | null\n /** testID of the currently focused node */\n readonly activeId: string | null\n /** Previously focused node */\n readonly previousElement: AgNode | null\n /** testID of the previously focused node */\n readonly previousId: string | null\n /** How focus was most recently acquired */\n readonly focusOrigin: FocusOrigin | null\n /** Stack of active focus scope IDs */\n readonly scopeStack: readonly string[]\n /** Map of scope ID -> last focused testID within that scope */\n readonly scopeMemory: Readonly<Record<string, string>>\n\n /** Focus a specific node */\n focus(node: AgNode, origin?: FocusOrigin): void\n /** Focus a node by testID (requires root for tree search) */\n focusById(id: string, root: AgNode, origin?: FocusOrigin): void\n /**\n * Focus a hook-registered (virtual) id directly without tree traversal.\n * Unlike `focusById`, this never needs a root — used by `useFocus()` hooks\n * that track focus by id only.\n */\n focusVirtualId(id: string, origin?: FocusOrigin): void\n /** Clear focus */\n blur(): void\n\n // ---- Hook-based (virtual) focusables ----\n\n /**\n * Register a hook-based focusable id (e.g. from `useFocus()` in Ink compat).\n *\n * Hook focusables form a flat list alongside the tree-based focusables.\n * `focusNext`/`focusPrev` interleave: tree focusables come first (document\n * order), then hook focusables (registration order). A single unified tab\n * cycle walks both.\n *\n * Returns an unregister callback (safe to call on effect cleanup).\n */\n registerHookFocusable(id: string, options?: HookFocusableOptions): () => void\n /** Update an existing hook-focusable's active state. */\n setHookFocusableActive(id: string, isActive: boolean): void\n /** Whether any hook focusables are currently registered. */\n readonly hasHookFocusables: boolean\n /**\n * Global focus enable (Ink compat). When false, `focusNext`/`focusPrev`\n * become no-ops for hook-registered focusables. Tree-based focusables\n * ignore this flag — apps using `useFocusable` are not affected.\n */\n readonly hookFocusEnabled: boolean\n setHookFocusEnabled(enabled: boolean): void\n\n /**\n * Handle a subtree being removed from the tree.\n * If the focused node (or previous node) is within the removed subtree,\n * clear the reference to prevent dead node retention and broken navigation.\n */\n handleSubtreeRemoved(removedRoot: AgNode): void\n\n /** Push a focus scope onto the stack */\n enterScope(scopeId: string): void\n /** Pop the current focus scope */\n exitScope(): void\n\n /** The currently active peer scope ID (WPF FocusScope model) */\n readonly activeScopeId: string | null\n /**\n * Activate a peer focus scope. Saves current focus in the old scope's memory,\n * switches to the new scope, and restores the remembered focus (or focuses\n * the first focusable element in the scope subtree).\n */\n activateScope(scopeId: string, root: AgNode): void\n\n /** Get the testID path from focused node to root */\n getFocusPath(root: AgNode): string[]\n /** Check if a subtree rooted at testID contains the focused node */\n hasFocusWithin(root: AgNode, testID: string): boolean\n\n /** Focus the next focusable node in tab order */\n focusNext(root: AgNode, scope?: AgNode): void\n /** Focus the previous focusable node in tab order */\n focusPrev(root: AgNode, scope?: AgNode): void\n /** Focus in a spatial direction (up/down/left/right) */\n focusDirection(\n root: AgNode,\n direction: \"up\" | \"down\" | \"left\" | \"right\",\n layoutFn?: (node: AgNode) => Rect | null,\n ): void\n\n /** Subscribe for React integration (useSyncExternalStore) */\n subscribe(listener: () => void): () => void\n /** Get immutable snapshot for useSyncExternalStore */\n getSnapshot(): FocusSnapshot\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Internal shape for a hook-registered focusable.\n * Order in the list reflects registration order (Ink-compatible tab order).\n */\ninterface HookFocusable {\n id: string\n isActive: boolean\n}\n\nexport function createFocusManager(options?: FocusManagerOptions): FocusManager {\n const onFocusChange = options?.onFocusChange\n\n // Internal state\n let activeElement: AgNode | null = null\n let activeId: string | null = null\n let previousElement: AgNode | null = null\n let previousId: string | null = null\n let focusOrigin: FocusOrigin | null = null\n const scopeStack: string[] = []\n const scopeMemory: Record<string, string> = {}\n let activeScopeId: string | null = null\n\n // Hook-registered focusables (flat list, Ink-style).\n const hookFocusables: HookFocusable[] = []\n // Global focus enable (Ink compat — `enableFocus()` / `disableFocus()` knob).\n let hookFocusEnabled = true\n\n // Subscriber management\n const listeners = new Set<() => void>()\n let snapshot: FocusSnapshot | null = null\n /** Counter incremented on every notify(); used by activateScope to detect inner notifications. */\n let notifyCount = 0\n\n function notify(): void {\n snapshot = null // Invalidate cached snapshot\n notifyCount++\n for (const listener of listeners) {\n listener()\n }\n }\n\n function getTestID(node: AgNode): string | null {\n const props = node.props as Record<string, unknown>\n return typeof props.testID === \"string\" ? props.testID : null\n }\n\n // ---- Focus operations ----\n\n function focus(node: AgNode, origin: FocusOrigin = \"programmatic\"): void {\n // Skip if already focused on this node\n if (activeElement === node) {\n // Still update origin if different\n if (focusOrigin !== origin) {\n focusOrigin = origin\n notify()\n }\n return\n }\n\n const oldElement = activeElement\n previousElement = activeElement\n previousId = activeId\n activeElement = node\n activeId = getTestID(node)\n focusOrigin = origin\n\n // Update interactive state on affected nodes\n if (oldElement) setFocused(oldElement, false)\n setFocused(node, true)\n\n // Remember this focus in the current scope\n if (activeId && scopeStack.length > 0) {\n scopeMemory[scopeStack[scopeStack.length - 1]!] = activeId\n }\n\n notify()\n\n // Fire focus change callback (after state is updated)\n onFocusChange?.(oldElement, node, origin)\n }\n\n function focusById(id: string, root: AgNode, origin: FocusOrigin = \"programmatic\"): void {\n const node = findByTestID(root, id)\n if (node) {\n // Walk up to the nearest focusable ancestor if the found node isn't focusable\n const focusable = findFocusableAncestor(node)\n if (focusable) {\n focus(focusable, origin)\n return\n }\n }\n // Virtual focus: set the ID without a DOM node. This enables named focus\n // targets (e.g. \"board-area\", \"detail-pane\") without requiring wrapper Boxes\n // that would disrupt layout.\n\n // Idempotent: skip if already virtually focused on this ID (prevents infinite\n // re-render loops when focus() is called during render).\n if (activeId === id && !activeElement) return\n\n const oldElement = activeElement\n previousElement = activeElement\n previousId = activeId\n activeElement = null\n activeId = id\n focusOrigin = origin\n\n // Update interactive state — old element loses focus, no new node to set\n if (oldElement) setFocused(oldElement, false)\n\n notify()\n\n // Fire focus change callback (old element blurs, no new node for virtual focus)\n onFocusChange?.(oldElement, null, origin)\n }\n\n function blur(): void {\n if (!activeElement && !activeId) return\n\n const oldElement = activeElement\n previousElement = activeElement\n previousId = activeId\n activeElement = null\n activeId = null\n focusOrigin = null\n\n // Update interactive state — old element loses focus\n if (oldElement) setFocused(oldElement, false)\n\n notify()\n\n // Fire focus change callback (after state is updated)\n onFocusChange?.(oldElement, null, null)\n }\n\n // ---- Hook-based (virtual) focusables ----\n\n /**\n * Set the focused target to a hook-registered id directly (no tree lookup).\n * activeElement becomes null (no backing node), activeId is the hook id.\n */\n function focusVirtualId(id: string, origin: FocusOrigin = \"programmatic\"): void {\n if (activeId === id && !activeElement) {\n if (focusOrigin !== origin) {\n focusOrigin = origin\n notify()\n }\n return\n }\n const oldElement = activeElement\n previousElement = activeElement\n previousId = activeId\n activeElement = null\n activeId = id\n focusOrigin = origin\n\n if (oldElement) setFocused(oldElement, false)\n\n notify()\n\n onFocusChange?.(oldElement, null, origin)\n }\n\n function unregisterHookFocusable(id: string): void {\n const idx = hookFocusables.findIndex((f) => f.id === id)\n if (idx === -1) return\n hookFocusables.splice(idx, 1)\n // Clear active focus if the removed id was focused.\n if (activeId === id && !activeElement) {\n previousId = activeId\n activeId = null\n focusOrigin = null\n notify()\n onFocusChange?.(null, null, null)\n return\n }\n notify()\n }\n\n function registerHookFocusable(id: string, opts: HookFocusableOptions = {}): () => void {\n const { isActive = true, autoFocus = false } = opts\n\n // Dedup by id — last registration wins (matches Ink behavior on re-mount).\n const existing = hookFocusables.findIndex((f) => f.id === id)\n if (existing !== -1) {\n // Skip notification if nothing changed (prevents render loops)\n if (hookFocusables[existing]!.isActive === isActive && !autoFocus) {\n return () => unregisterHookFocusable(id)\n }\n hookFocusables[existing] = { id, isActive }\n } else {\n hookFocusables.push({ id, isActive })\n }\n\n if (autoFocus && isActive && activeId === null) {\n focusVirtualId(id, \"programmatic\")\n } else {\n notify()\n }\n\n return () => unregisterHookFocusable(id)\n }\n\n function setHookFocusableActive(id: string, isActive: boolean): void {\n const entry = hookFocusables.find((f) => f.id === id)\n if (!entry) return\n if (entry.isActive === isActive) return\n entry.isActive = isActive\n // If the active virtual id was deactivated, clear it.\n if (!isActive && activeId === id && !activeElement) {\n previousId = activeId\n activeId = null\n focusOrigin = null\n notify()\n onFocusChange?.(null, null, null)\n return\n }\n notify()\n }\n\n function setHookFocusEnabled(enabled: boolean): void {\n if (hookFocusEnabled === enabled) return\n hookFocusEnabled = enabled\n notify()\n }\n\n // ---- Subtree removal ----\n\n /**\n * Check if a node is the given target or contains it as a descendant.\n */\n function subtreeContains(subtreeRoot: AgNode, target: AgNode): boolean {\n if (subtreeRoot === target) return true\n for (const child of subtreeRoot.children) {\n if (subtreeContains(child, target)) return true\n }\n return false\n }\n\n /**\n * Handle a subtree being removed from the tree. If the active or previous\n * element lives within the removed subtree, clear the dangling reference.\n * This prevents dead node retention and broken navigation (indexOf → -1).\n */\n function handleSubtreeRemoved(removedRoot: AgNode): void {\n let changed = false\n\n if (activeElement && subtreeContains(removedRoot, activeElement)) {\n const oldElement = activeElement\n // Clear interactive focus state before removing reference\n setFocused(oldElement, false)\n previousElement = activeElement\n previousId = activeId\n activeElement = null\n activeId = null\n focusOrigin = null\n changed = true\n onFocusChange?.(oldElement, null, null)\n }\n\n if (previousElement && subtreeContains(removedRoot, previousElement)) {\n previousElement = null\n previousId = null\n changed = true\n }\n\n if (changed) {\n notify()\n }\n }\n\n // ---- Scope management ----\n\n function enterScope(scopeId: string): void {\n scopeStack.push(scopeId)\n notify()\n }\n\n function exitScope(): void {\n const exited = scopeStack.pop()\n if (exited === undefined) return\n\n // Restore focus to the remembered element in the parent scope\n // (Caller is responsible for providing root to restore if needed)\n notify()\n }\n\n // ---- Peer scope activation (WPF FocusScope model) ----\n\n function activateScope(scopeId: string, root: AgNode): void {\n // Save current focus in the outgoing scope's memory\n if (activeScopeId && activeId) {\n scopeMemory[activeScopeId] = activeId\n }\n\n // Switch scope\n activeScopeId = scopeId\n\n // Restore focus: remembered element, or first focusable in scope.\n // Track whether notify() fired during focus/focusById to avoid double-notify.\n const countBefore = notifyCount\n const remembered = scopeMemory[scopeId]\n if (remembered) {\n focusById(remembered, root, \"programmatic\")\n } else {\n const scopeNode = findByTestID(root, scopeId)\n if (scopeNode) {\n const order = getTabOrder(root, scopeNode)\n if (order.length > 0) {\n focus(order[0]!, \"programmatic\")\n }\n }\n }\n\n // Only notify if focus/focusById didn't already notify.\n if (notifyCount === countBefore) {\n notify()\n }\n }\n\n // ---- Tree queries ----\n\n function getFocusPath(root: AgNode): string[] {\n if (!activeElement) return []\n\n const path: string[] = []\n let current: AgNode | null = activeElement\n while (current && current !== root.parent) {\n const id = getTestID(current)\n if (id) path.push(id)\n current = current.parent\n }\n return path\n }\n\n function hasFocusWithin(root: AgNode, testID: string): boolean {\n if (!activeElement) return false\n\n // Find the node with the given testID\n const target = findByTestID(root, testID)\n if (!target) return false\n\n // Walk up from activeElement to see if we pass through target\n let current: AgNode | null = activeElement\n while (current) {\n if (current === target) return true\n current = current.parent\n }\n return false\n }\n\n // ---- Navigation ----\n\n /**\n * Resolve the effective scope node for tab navigation.\n * If an explicit scope is provided, use it. Otherwise, if the scopeStack\n * is non-empty, find the topmost scope node in the tree by testID.\n */\n function resolveScope(root: AgNode, explicitScope?: AgNode): AgNode | undefined {\n if (explicitScope) return explicitScope\n\n if (scopeStack.length > 0) {\n const scopeId = scopeStack[scopeStack.length - 1]!\n const scopeNode = findByTestID(root, scopeId)\n if (scopeNode) return scopeNode\n }\n\n return undefined\n }\n\n /**\n * Unified tab entry — either a tree-based AgNode or a hook-registered id.\n * Tree focusables come first (document order), then hook focusables\n * (registration order). Inactive hook focusables are excluded.\n */\n type TabEntry = { kind: \"node\"; node: AgNode } | { kind: \"hook\"; id: string }\n\n function buildTabEntries(root: AgNode, scope?: AgNode): TabEntry[] {\n const effectiveScope = resolveScope(root, scope)\n const nodes = getTabOrder(root, effectiveScope)\n const entries: TabEntry[] = nodes.map((node) => ({ kind: \"node\", node }))\n // Hook focusables are only included when no explicit tree scope is active —\n // they're inherently scope-less. Skip inactive or when globally disabled.\n // Dedup: skip hook focusables whose id matches a tree node's testID —\n // the component is already in the tab order via the tree scan.\n if (!effectiveScope && hookFocusEnabled) {\n const treeIds = new Set(\n nodes.map((n) => (n.props as Record<string, unknown>).testID as string | undefined).filter(Boolean),\n )\n for (const entry of hookFocusables) {\n if (entry.isActive && !treeIds.has(entry.id)) entries.push({ kind: \"hook\", id: entry.id })\n }\n }\n return entries\n }\n\n function currentTabIndex(entries: TabEntry[]): number {\n if (activeElement) {\n for (let i = 0; i < entries.length; i++) {\n const e = entries[i]!\n if (e.kind === \"node\" && e.node === activeElement) return i\n }\n return -1\n }\n if (activeId) {\n for (let i = 0; i < entries.length; i++) {\n const e = entries[i]!\n if (e.kind === \"hook\" && e.id === activeId) return i\n }\n }\n return -1\n }\n\n function focusTabEntry(entry: TabEntry, origin: FocusOrigin): void {\n if (entry.kind === \"node\") {\n focus(entry.node, origin)\n } else {\n focusVirtualId(entry.id, origin)\n }\n }\n\n function focusNext(root: AgNode, scope?: AgNode): void {\n const entries = buildTabEntries(root, scope)\n if (entries.length === 0) return\n\n const currentIndex = currentTabIndex(entries)\n if (currentIndex === -1) {\n // Nothing focused — focus the first entry\n focusTabEntry(entries[0]!, \"keyboard\")\n return\n }\n\n const nextIndex = (currentIndex + 1) % entries.length\n focusTabEntry(entries[nextIndex]!, \"keyboard\")\n }\n\n function focusPrev(root: AgNode, scope?: AgNode): void {\n const entries = buildTabEntries(root, scope)\n if (entries.length === 0) return\n\n const currentIndex = currentTabIndex(entries)\n if (currentIndex === -1) {\n // Nothing focused — focus the last entry\n focusTabEntry(entries[entries.length - 1]!, \"keyboard\")\n return\n }\n\n const prevIndex = currentIndex <= 0 ? entries.length - 1 : currentIndex - 1\n focusTabEntry(entries[prevIndex]!, \"keyboard\")\n }\n\n function focusDirection(\n root: AgNode,\n direction: \"up\" | \"down\" | \"left\" | \"right\",\n layoutFn?: (node: AgNode) => Rect | null,\n ): void {\n if (!activeElement) return\n\n // Check for explicit focus link first\n const explicitTarget = getExplicitFocusLink(activeElement, direction)\n if (explicitTarget) {\n focusById(explicitTarget, root, \"keyboard\")\n return\n }\n\n // Fall back to spatial navigation\n const candidates = getTabOrder(root)\n const resolvedLayoutFn = layoutFn ?? ((node: AgNode) => node.scrollRect)\n const target = findSpatialTarget(activeElement, direction, candidates, resolvedLayoutFn)\n if (target) {\n focus(target, \"keyboard\")\n }\n }\n\n // ---- Subscribe/snapshot for useSyncExternalStore ----\n\n function subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n }\n\n function getSnapshot(): FocusSnapshot {\n if (!snapshot) {\n snapshot = {\n activeId,\n previousId,\n focusOrigin,\n scopeStack: [...scopeStack],\n activeScopeId,\n }\n }\n return snapshot\n }\n\n // ---- Public interface ----\n\n return {\n get activeElement() {\n return activeElement\n },\n get activeId() {\n return activeId\n },\n get previousElement() {\n return previousElement\n },\n get previousId() {\n return previousId\n },\n get focusOrigin() {\n return focusOrigin\n },\n get scopeStack() {\n return [...scopeStack] as readonly string[]\n },\n get scopeMemory() {\n return scopeMemory as Readonly<Record<string, string>>\n },\n get activeScopeId() {\n return activeScopeId\n },\n\n focus,\n focusById,\n focusVirtualId,\n blur,\n handleSubtreeRemoved,\n\n enterScope,\n exitScope,\n activateScope,\n\n getFocusPath,\n hasFocusWithin,\n\n focusNext,\n focusPrev,\n focusDirection,\n\n subscribe,\n getSnapshot,\n\n // Hook-based focusables (Ink compat)\n registerHookFocusable,\n setHookFocusableActive,\n get hasHookFocusables() {\n return hookFocusables.length > 0\n },\n get hookFocusEnabled() {\n return hookFocusEnabled\n },\n setHookFocusEnabled,\n }\n}\n","/**\n * Shared tree utilities for silvery event systems.\n *\n * Functions used by both focus-events.ts and mouse-events.ts.\n */\n\nimport type { AgNode, Rect } from \"./types.js\"\n\n/**\n * Collect the ancestor path from target to root (inclusive).\n */\nexport function getAncestorPath(node: AgNode): AgNode[] {\n const path: AgNode[] = []\n let current: AgNode | null = node\n while (current) {\n path.push(current)\n current = current.parent\n }\n return path\n}\n\n/**\n * Check if a point is inside a rect.\n */\nexport function pointInRect(x: number, y: number, rect: Rect): boolean {\n return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height\n}\n","/**\n * DOM-level Focus and Keyboard Events for silvery\n *\n * Provides React DOM-compatible focus/keyboard event infrastructure:\n * - SilveryKeyEvent / SilveryFocusEvent synthetic event objects\n * - Event dispatch with capture/target/bubble phases (key events)\n * - Event dispatch with target + bubble (focus events)\n *\n * Follows the same patterns as mouse-events.ts for consistency.\n */\n\nimport type { Key } from \"./keys\"\nimport { getAncestorPath } from \"./tree-utils.js\"\nimport type { AgNode } from \"./types\"\n\n// ============================================================================\n// Event Types\n// ============================================================================\n\n/**\n * Synthetic keyboard event, mirroring React.KeyboardEvent / DOM KeyboardEvent.\n */\nexport interface SilveryKeyEvent {\n /** The printable character, or \"\" for non-printable keys */\n key: string\n /** Raw terminal input string */\n input: string\n /** Modifier keys */\n ctrl: boolean\n meta: boolean\n shift: boolean\n super: boolean\n hyper: boolean\n /** Kitty event type */\n eventType?: \"press\" | \"repeat\" | \"release\"\n /** Deepest focusable node that received this event */\n target: AgNode\n /** Node whose handler is currently firing (changes during capture/bubble) */\n currentTarget: AgNode\n /** Stop event from propagating further */\n stopPropagation(): void\n /** Prevent default behavior */\n preventDefault(): void\n /** Whether stopPropagation() was called */\n readonly propagationStopped: boolean\n /** Whether preventDefault() was called */\n readonly defaultPrevented: boolean\n /** Raw parsed key data */\n nativeEvent: { input: string; key: Key }\n}\n\n/**\n * Synthetic focus event, mirroring React.FocusEvent / DOM FocusEvent.\n */\nexport interface SilveryFocusEvent {\n /** The node gaining or losing focus */\n target: AgNode\n /** The other node involved (losing focus on 'focus', gaining on 'blur') */\n relatedTarget: AgNode | null\n /** Event type */\n type: \"focus\" | \"blur\"\n /** Node whose handler is currently firing (changes during bubble) */\n currentTarget: AgNode\n /** Stop event from bubbling to parent nodes */\n stopPropagation(): void\n /** Whether stopPropagation() was called */\n readonly propagationStopped: boolean\n}\n\n// ============================================================================\n// Focus Event Handler Props (added to BoxProps)\n// ============================================================================\n\nexport interface FocusEventProps {\n /** Whether this node can receive focus */\n focusable?: boolean\n /** Whether this node should receive focus on mount */\n autoFocus?: boolean\n /** Whether this node creates a focus scope (focus trapping boundary) */\n focusScope?: boolean\n /** ID of the node to focus when pressing Up from this node */\n nextFocusUp?: string\n /** ID of the node to focus when pressing Down from this node */\n nextFocusDown?: string\n /** ID of the node to focus when pressing Left from this node */\n nextFocusLeft?: string\n /** ID of the node to focus when pressing Right from this node */\n nextFocusRight?: string\n /** Called when this node gains focus */\n onFocus?: (event: SilveryFocusEvent) => void\n /** Called when this node loses focus */\n onBlur?: (event: SilveryFocusEvent) => void\n /** Called on key down (bubble phase) */\n onKeyDown?: (event: SilveryKeyEvent, dispatch?: (msg: unknown) => void) => void\n /** Called on key up (bubble phase) */\n onKeyUp?: (event: SilveryKeyEvent, dispatch?: (msg: unknown) => void) => void\n /** Called on key down (capture phase — fires before target) */\n onKeyDownCapture?: (event: SilveryKeyEvent) => void\n}\n\n// ============================================================================\n// Event Factories\n// ============================================================================\n\n/**\n * Create a synthetic keyboard event.\n */\nexport function createKeyEvent(input: string, key: Key, target: AgNode): SilveryKeyEvent {\n let propagationStopped = false\n let defaultPrevented = false\n\n return {\n key: input,\n input,\n ctrl: key.ctrl,\n meta: key.meta,\n shift: key.shift,\n super: key.super,\n hyper: key.hyper,\n eventType: key.eventType,\n target,\n currentTarget: target,\n nativeEvent: { input, key },\n get propagationStopped() {\n return propagationStopped\n },\n get defaultPrevented() {\n return defaultPrevented\n },\n stopPropagation() {\n propagationStopped = true\n },\n preventDefault() {\n defaultPrevented = true\n },\n }\n}\n\n/**\n * Create a synthetic focus event.\n */\nexport function createFocusEvent(\n type: \"focus\" | \"blur\",\n target: AgNode,\n relatedTarget: AgNode | null,\n): SilveryFocusEvent {\n let propagationStopped = false\n\n return {\n type,\n target,\n relatedTarget,\n currentTarget: target,\n get propagationStopped() {\n return propagationStopped\n },\n stopPropagation() {\n propagationStopped = true\n },\n }\n}\n\n// ============================================================================\n// Tree Walking\n// ============================================================================\n\n// ============================================================================\n// Event Dispatch\n// ============================================================================\n\n/**\n * Dispatch a keyboard event through the render tree with DOM-style\n * capture/target/bubble phases.\n *\n * For press/repeat events:\n * 1. Capture phase: root → target (onKeyDownCapture props)\n * 2. Target phase: target's onKeyDown\n * 3. Bubble phase: target parent → root (onKeyDown props)\n *\n * For release events:\n * 1. Target phase: target's onKeyUp\n * 2. Bubble phase: target parent → root (onKeyUp props)\n * (No capture phase for keyUp — deliberate simplification; React DOM has onKeyUpCapture)\n *\n * stopPropagation() halts traversal at any phase.\n */\nexport function dispatchKeyEvent(event: SilveryKeyEvent, dispatch?: (msg: unknown) => void): void {\n const path = getAncestorPath(event.target)\n const mutableEvent = event as { currentTarget: AgNode }\n\n // Release events → onKeyUp (no capture phase — deliberate simplification; React DOM has onKeyUpCapture)\n const isRelease = event.eventType === \"release\"\n const handlerProp = isRelease ? \"onKeyUp\" : \"onKeyDown\"\n\n // Capture phase: root → target (onKeyDownCapture — press/repeat only)\n if (!isRelease) {\n for (let i = path.length - 1; i > 0; i--) {\n if (event.propagationStopped) return\n const node = path[i]!\n const handler = (node.props as Record<string, unknown>).onKeyDownCapture as\n | ((e: SilveryKeyEvent) => void)\n | undefined\n if (handler) {\n mutableEvent.currentTarget = node\n handler(event)\n }\n }\n }\n\n // Target phase\n if (!event.propagationStopped) {\n const target = path[0]!\n mutableEvent.currentTarget = target\n const handler = (target.props as Record<string, unknown>)[handlerProp] as\n | ((e: SilveryKeyEvent, d?: (msg: unknown) => void) => void)\n | undefined\n if (handler) {\n handler(event, dispatch)\n }\n }\n\n // Bubble phase: target parent → root\n for (let i = 1; i < path.length; i++) {\n if (event.propagationStopped) return\n const node = path[i]!\n const handler = (node.props as Record<string, unknown>)[handlerProp] as\n | ((e: SilveryKeyEvent, d?: (msg: unknown) => void) => void)\n | undefined\n if (handler) {\n mutableEvent.currentTarget = node\n handler(event, dispatch)\n }\n }\n}\n\n/**\n * Dispatch a focus event through the render tree.\n *\n * Fires onFocus/onBlur on the target, then bubbles to ancestors.\n */\nexport function dispatchFocusEvent(event: SilveryFocusEvent): void {\n const handlerProp = event.type === \"focus\" ? \"onFocus\" : \"onBlur\"\n const path = getAncestorPath(event.target)\n const mutableEvent = event as { currentTarget: AgNode }\n\n for (const node of path) {\n if (event.propagationStopped) break\n\n const handler = (node.props as Record<string, unknown>)[handlerProp] as ((e: SilveryFocusEvent) => void) | undefined\n if (handler) {\n mutableEvent.currentTarget = node\n handler(event)\n }\n }\n}\n","/**\n * Hit Registry Core — Pure logic for mouse hit testing.\n *\n * This module contains the React-free core of the hit registry:\n * types, registry class, z-index constants, and ID counter.\n *\n * React hooks and context live in ./hit-registry (which re-exports everything\n * from here plus adds useHitRegion, useHitRegionCallback, HitRegistryContext).\n *\n * The @silvery/ag-term barrel imports from this file to stay React-free.\n * Consumers who need React hooks should import from @silvery/ag-term/hit-registry.\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Target type for hit testing.\n * Each type represents a different clickable element in the UI.\n */\nexport interface HitTarget {\n /** The type of element that was clicked */\n type: \"node\" | \"fold-toggle\" | \"link\" | \"column-header\" | \"scroll-area\" | \"button\"\n /** Column index (for column-header, or items within a column) */\n colIndex?: number\n /** Card index within a column */\n cardIndex?: number\n /** Sub-item index within a card (e.g., checklist items) */\n subIndex?: number\n /** Node ID for node-specific targets */\n nodeId?: string\n /** URL for link targets */\n linkUrl?: string\n /** Custom action identifier */\n action?: string\n}\n\n/**\n * A registered hit region with position, size, target, and z-index.\n */\nexport interface HitRegion {\n /** X position on screen (0-indexed column) */\n x: number\n /** Y position on screen (0-indexed row) */\n y: number\n /** Width in columns */\n width: number\n /** Height in rows */\n height: number\n /** The target to return when this region is clicked */\n target: HitTarget\n /** Z-index for layering (higher values are on top) */\n zIndex: number\n}\n\n// ============================================================================\n// HitRegistry Class\n// ============================================================================\n\n/**\n * Registry for managing hit regions.\n *\n * Components register their screen regions with targets, and the registry\n * resolves mouse clicks to the appropriate target based on position and z-index.\n *\n * @example\n * ```typescript\n * const registry = new HitRegistry();\n *\n * // Register a card region\n * registry.register('card-1', {\n * x: 10, y: 5, width: 30, height: 8,\n * target: { type: 'node', nodeId: 'abc123' },\n * zIndex: 10\n * });\n *\n * // Hit test a click\n * const target = registry.hitTest(15, 7);\n * // Returns { type: 'node', nodeId: 'abc123' }\n * ```\n */\nexport class HitRegistry {\n private regions = new Map<string, HitRegion>()\n\n /**\n * Register a hit region with a unique ID.\n *\n * @param id - Unique identifier for the region (used for unregistration)\n * @param region - The region definition including position, size, target, and z-index\n */\n register(id: string, region: HitRegion): void {\n this.regions.set(id, region)\n }\n\n /**\n * Unregister a hit region by ID.\n *\n * @param id - The ID used when registering the region\n */\n unregister(id: string): void {\n this.regions.delete(id)\n }\n\n /**\n * Clear all registered regions.\n * Useful when the UI is completely redrawn.\n */\n clear(): void {\n this.regions.clear()\n }\n\n /**\n * Get the number of registered regions.\n * Useful for debugging.\n */\n get size(): number {\n return this.regions.size\n }\n\n /**\n * Test a screen position and return the highest z-index matching target.\n *\n * @param screenX - X position on screen (0-indexed column)\n * @param screenY - Y position on screen (0-indexed row)\n * @returns The target of the highest z-index region containing the point, or null if none\n */\n hitTest(screenX: number, screenY: number): HitTarget | null {\n let bestMatch: HitRegion | null = null\n\n for (const region of this.regions.values()) {\n // Check if point is within region bounds\n if (\n screenX >= region.x &&\n screenX < region.x + region.width &&\n screenY >= region.y &&\n screenY < region.y + region.height\n ) {\n // Keep the highest z-index match\n if (!bestMatch || region.zIndex > bestMatch.zIndex) {\n bestMatch = region\n }\n }\n }\n\n return bestMatch?.target ?? null\n }\n\n /**\n * Get all regions that contain a point, sorted by z-index (highest first).\n * Useful for debugging or when you need to know all overlapping elements.\n *\n * @param screenX - X position on screen (0-indexed column)\n * @param screenY - Y position on screen (0-indexed row)\n * @returns Array of matching regions, sorted by z-index descending\n */\n hitTestAll(screenX: number, screenY: number): HitRegion[] {\n const matches: HitRegion[] = []\n\n for (const region of this.regions.values()) {\n if (\n screenX >= region.x &&\n screenX < region.x + region.width &&\n screenY >= region.y &&\n screenY < region.y + region.height\n ) {\n matches.push(region)\n }\n }\n\n // Sort by z-index descending (highest first)\n return matches.sort((a, b) => b.zIndex - a.zIndex)\n }\n\n /**\n * Debug helper: get all registered regions.\n */\n getAllRegions(): Map<string, HitRegion> {\n return new Map(this.regions)\n }\n}\n\n// ============================================================================\n// ID Counter\n// ============================================================================\n\n/**\n * Generate a unique ID for hit region registration.\n */\nlet hitRegionIdCounter = 0\nexport function generateHitRegionId(): string {\n return `hit-${++hitRegionIdCounter}`\n}\n\n/**\n * Reset the ID counter (useful for testing).\n */\nexport function resetHitRegionIdCounter(): void {\n hitRegionIdCounter = 0\n}\n\n// ============================================================================\n// Z-Index Constants\n// ============================================================================\n\n/**\n * Recommended z-index values for different UI layers.\n */\nexport const Z_INDEX = {\n /** Background elements */\n BACKGROUND: 0,\n /** Column headers */\n COLUMN_HEADER: 5,\n /** Cards in the main view */\n CARD: 10,\n /** Fold toggles (above cards for easier clicking) */\n FOLD_TOGGLE: 15,\n /** Links within cards */\n LINK: 20,\n /** Floating elements */\n FLOATING: 50,\n /** Modal dialogs */\n DIALOG: 100,\n /** Dropdown menus */\n DROPDOWN: 150,\n /** Tooltips */\n TOOLTIP: 200,\n} as const\n","/**\n * Debug utilities for incremental render mismatch diagnostics.\n *\n * When SILVERY_STRICT detects a mismatch between incremental and fresh renders,\n * these utilities help identify the root cause by providing:\n * - Node attribution (which node owns the mismatched cell)\n * - Dirty flag state (what flags were set before render)\n * - Layout changes (prevLayout vs boxRect)\n * - Scroll context (offset changes, hidden items)\n */\n\nimport type { Cell } from \"@silvery/ag-term/buffer\"\nimport type { BoxProps, AgNode, Rect, TextProps } from \"@silvery/ag/types\"\nimport { isDirty, isAnyDirty, CONTENT_BIT, STYLE_PROPS_BIT, SUBTREE_BIT, CHILDREN_BIT } from \"@silvery/ag/epoch\"\nimport type { RenderPhaseStats } from \"@silvery/ag-term/pipeline/types\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Debug info about a node at a screen position */\nexport interface NodeDebugInfo {\n /** Node ID (if set via props.id) */\n id: string | undefined\n /** Node type (silvery-box, silvery-text, silvery-root) */\n type: string\n /** Path from root to this node (IDs or indices) */\n path: string\n /** Index within parent's children array */\n childIndex: number | null\n /** Dirty flags at time of mismatch */\n dirtyFlags: {\n contentDirty: boolean\n stylePropsDirty: boolean\n subtreeDirty: boolean\n childrenDirty: boolean\n layoutDirty: boolean\n }\n /** Layout info */\n layout: {\n prevLayout: Rect | null\n boxRect: Rect | null\n scrollRect: Rect | null\n layoutChanged: boolean\n }\n /** Scroll context (if this is a scroll container or inside one) */\n scroll?: {\n offset: number\n prevOffset: number\n offsetChanged: boolean\n contentHeight: number\n viewportHeight: number\n hiddenAbove: number\n hiddenBelow: number\n firstVisibleChild: number\n lastVisibleChild: number\n }\n /** Background color from props */\n backgroundColor: string | undefined\n /** Number of children */\n childCount: number\n /** Whether node is hidden (Suspense) */\n hidden: boolean\n}\n\n/** Full mismatch debug context */\nexport interface MismatchDebugContext {\n /** Screen position of the mismatch */\n position: { x: number; y: number }\n /** Cell values */\n cells: {\n incremental: Cell\n fresh: Cell\n }\n /** Render number */\n renderNum: number\n /** Node that owns this screen position (innermost) */\n node: NodeDebugInfo | null\n /** Scroll container ancestry (if any) */\n scrollAncestors: NodeDebugInfo[]\n /** All nodes whose scrollRect contains this position */\n containingNodes: NodeDebugInfo[]\n /** Fast-path analysis - why the node was likely skipped */\n fastPathAnalysis: string[]\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Find the innermost node at a screen position.\n */\nexport function findNodeAtPosition(root: AgNode, x: number, y: number): AgNode | null {\n let result: AgNode | null = null\n\n function visit(node: AgNode): void {\n const rect = node.scrollRect\n if (!rect) return\n\n // Check if position is within this node's scrollRect\n if (x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height) {\n result = node // This node contains the position\n\n // Check children (later children render on top of earlier ones)\n for (const child of node.children) {\n visit(child)\n }\n }\n }\n\n visit(root)\n return result\n}\n\n/**\n * Find all nodes whose scrollRect contains the given position.\n * Returns nodes from root to innermost (outermost first).\n */\nexport function findAllContainingNodes(root: AgNode, x: number, y: number): AgNode[] {\n const result: AgNode[] = []\n\n function visit(node: AgNode): void {\n const rect = node.scrollRect\n if (!rect) return\n\n if (x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height) {\n result.push(node)\n for (const child of node.children) {\n visit(child)\n }\n }\n }\n\n visit(root)\n return result\n}\n\n/**\n * Get the path from root to a node (for identification).\n */\nfunction getNodePath(node: AgNode): string {\n const parts: string[] = []\n let current: AgNode | null = node\n\n while (current) {\n const props = current.props as BoxProps & TextProps\n if (props.id) {\n parts.unshift(`#${props.id}`)\n } else if (current.parent) {\n const idx = current.parent.children.indexOf(current)\n parts.unshift(`[${idx}]`)\n } else {\n parts.unshift(\"root\")\n }\n current = current.parent\n }\n\n return parts.join(\" > \")\n}\n\n/**\n * Check if a rect changed (position or size).\n */\nfunction rectChanged(a: Rect | null, b: Rect | null): boolean {\n if (a === b) return false\n if (!a || !b) return true\n return a.x !== b.x || a.y !== b.y || a.width !== b.width || a.height !== b.height\n}\n\n/**\n * Extract debug info from a node.\n */\nexport function getNodeDebugInfo(node: AgNode): NodeDebugInfo {\n const props = node.props as BoxProps & TextProps\n\n // Get child index within parent\n let childIndex: number | null = null\n if (node.parent) {\n childIndex = node.parent.children.indexOf(node)\n }\n\n return {\n id: props.id,\n type: node.type,\n path: getNodePath(node),\n childIndex,\n dirtyFlags: {\n contentDirty: isDirty(node.dirtyBits, node.dirtyEpoch, CONTENT_BIT),\n stylePropsDirty: isDirty(node.dirtyBits, node.dirtyEpoch, STYLE_PROPS_BIT),\n subtreeDirty: isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT),\n childrenDirty: isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT),\n layoutDirty: node.layoutDirty,\n },\n layout: {\n prevLayout: node.prevLayout,\n boxRect: node.boxRect,\n scrollRect: node.scrollRect,\n layoutChanged: rectChanged(node.prevLayout, node.boxRect),\n },\n scroll: node.scrollState\n ? {\n offset: node.scrollState.offset,\n prevOffset: node.scrollState.prevOffset,\n offsetChanged: node.scrollState.offset !== node.scrollState.prevOffset,\n contentHeight: node.scrollState.contentHeight,\n viewportHeight: node.scrollState.viewportHeight,\n hiddenAbove: node.scrollState.hiddenAbove,\n hiddenBelow: node.scrollState.hiddenBelow,\n firstVisibleChild: node.scrollState.firstVisibleChild,\n lastVisibleChild: node.scrollState.lastVisibleChild,\n }\n : undefined,\n backgroundColor: props.backgroundColor,\n childCount: node.children.length,\n hidden: node.hidden ?? false,\n }\n}\n\n/**\n * Find scroll container ancestors for a node.\n */\nfunction findScrollAncestors(node: AgNode): AgNode[] {\n const result: AgNode[] = []\n let current = node.parent\n\n while (current) {\n if (current.scrollState) {\n result.push(current)\n }\n current = current.parent\n }\n\n return result\n}\n\n/**\n * Analyze why a node might have been incorrectly skipped by fast-path.\n */\nfunction analyzeFastPath(node: AgNode | null, scrollAncestors: AgNode[]): string[] {\n const analysis: string[] = []\n\n if (!node) {\n analysis.push(\"⚠ No node found at mismatch position - possible virtualization issue\")\n return analysis\n }\n\n const flags = node\n const allClean = !isAnyDirty(flags.dirtyBits, flags.dirtyEpoch) && !flags.layoutDirty\n\n if (allClean) {\n analysis.push(\"⚠ ALL DIRTY FLAGS FALSE - fast-path likely skipped this node\")\n }\n\n // Check if node is in a scroll container\n const scrollParent = scrollAncestors[0]\n if (scrollParent?.scrollState) {\n const ss = scrollParent.scrollState\n const childIndex = node.parent ? node.parent.children.indexOf(node) : -1\n\n // Check if this node SHOULD be in visible range\n const inVisibleRange = childIndex >= ss.firstVisibleChild && childIndex <= ss.lastVisibleChild\n if (!inVisibleRange && childIndex >= 0) {\n analysis.push(\n `⚠ Node index ${childIndex} is OUTSIDE visible range [${ss.firstVisibleChild}..${ss.lastVisibleChild}]`,\n )\n analysis.push(\" → Node should have been skipped, but mismatch suggests it should render\")\n } else if (inVisibleRange) {\n analysis.push(`✓ Node index ${childIndex} is in visible range [${ss.firstVisibleChild}..${ss.lastVisibleChild}]`)\n }\n\n // Check scroll offset\n if (ss.offset === ss.prevOffset) {\n analysis.push(\"✓ Scroll offset unchanged (fast-path enabled for children)\")\n } else {\n analysis.push(`⚠ Scroll offset CHANGED: ${ss.prevOffset} → ${ss.offset}`)\n }\n\n // Check if visible range might have changed\n if (ss.firstVisibleChild !== 0 || ss.lastVisibleChild !== scrollParent.children.length - 1) {\n analysis.push(\n ` Visible range is partial: [${ss.firstVisibleChild}..${ss.lastVisibleChild}] of ${scrollParent.children.length} children`,\n )\n analysis.push(\" → If visible range changed, newly visible children need rendering\")\n }\n }\n\n // Check prevLayout\n const layoutChanged = rectChanged(node.prevLayout, node.boxRect)\n if (!layoutChanged && node.prevLayout) {\n analysis.push(\"✓ Layout unchanged (prevLayout matches boxRect)\")\n } else if (!node.prevLayout) {\n analysis.push(\"⚠ prevLayout is NULL - node may never have been rendered before\")\n } else {\n analysis.push(\"⚠ Layout CHANGED but node still skipped - dirty flag not set?\")\n }\n\n // Check for sibling child position changes\n if (node.parent && node.parent.children.length > 1) {\n let siblingMoved = false\n for (const sibling of node.parent.children) {\n if (sibling !== node && sibling.boxRect && sibling.prevLayout) {\n if (sibling.boxRect.x !== sibling.prevLayout.x || sibling.boxRect.y !== sibling.prevLayout.y) {\n siblingMoved = true\n break\n }\n }\n }\n if (siblingMoved) {\n analysis.push(\"⚠ SIBLING POSITION CHANGED - parent should have detected this\")\n }\n }\n\n // Check hidden state\n if (node.hidden) {\n analysis.push(\"⚠ Node is HIDDEN (Suspense) - should not be rendered\")\n }\n\n return analysis\n}\n\n/**\n * Build full mismatch debug context.\n */\nexport function buildMismatchContext(\n root: AgNode,\n x: number,\n y: number,\n incrementalCell: Cell,\n freshCell: Cell,\n renderNum: number,\n): MismatchDebugContext {\n const innermost = findNodeAtPosition(root, x, y)\n const containing = findAllContainingNodes(root, x, y)\n const scrollAncestorNodes = innermost ? findScrollAncestors(innermost) : []\n\n return {\n position: { x, y },\n cells: {\n incremental: incrementalCell,\n fresh: freshCell,\n },\n renderNum,\n node: innermost ? getNodeDebugInfo(innermost) : null,\n scrollAncestors: scrollAncestorNodes.map(getNodeDebugInfo),\n containingNodes: containing.map(getNodeDebugInfo),\n fastPathAnalysis: analyzeFastPath(innermost, scrollAncestorNodes),\n }\n}\n\n/**\n * Format mismatch context as a human-readable string.\n *\n * @param ctx - The mismatch debug context (node attribution, dirty flags, scroll, fast-path)\n * @param renderPhaseStats - Optional render-phase instrumentation snapshot (auto-included by SILVERY_STRICT)\n */\nexport function formatMismatchContext(ctx: MismatchDebugContext, renderPhaseStats?: RenderPhaseStats): string {\n const lines: string[] = []\n\n // Header\n lines.push(`SILVERY_STRICT: MISMATCH at (${ctx.position.x}, ${ctx.position.y}) on render #${ctx.renderNum}`)\n lines.push(\"\")\n\n // Cell values\n const { incremental, fresh } = ctx.cells\n lines.push(\"CELL VALUES:\")\n lines.push(\n ` incremental: char=${JSON.stringify(incremental.char)} fg=${JSON.stringify(incremental.fg)} bg=${JSON.stringify(incremental.bg)} attrs=${JSON.stringify(incremental.attrs)}`,\n )\n lines.push(\n ` fresh: char=${JSON.stringify(fresh.char)} fg=${JSON.stringify(fresh.fg)} bg=${JSON.stringify(fresh.bg)} attrs=${JSON.stringify(fresh.attrs)}`,\n )\n lines.push(\"\")\n\n // Node attribution\n if (ctx.node) {\n lines.push(\"INNERMOST NODE:\")\n lines.push(` path: ${ctx.node.path}`)\n lines.push(` type: ${ctx.node.type}`)\n if (ctx.node.backgroundColor) {\n lines.push(` backgroundColor: ${ctx.node.backgroundColor}`)\n }\n lines.push(\"\")\n\n // Dirty flags\n const flags = ctx.node.dirtyFlags\n const activeFlags = Object.entries(flags)\n .filter(([, v]) => v)\n .map(([k]) => k)\n lines.push(\"DIRTY FLAGS:\")\n if (activeFlags.length > 0) {\n lines.push(` active: ${activeFlags.join(\", \")}`)\n } else {\n lines.push(\" active: (none - node was clean)\")\n }\n lines.push(\n ` all: contentDirty=${flags.contentDirty} stylePropsDirty=${flags.stylePropsDirty} subtreeDirty=${flags.subtreeDirty} childrenDirty=${flags.childrenDirty} layoutDirty=${flags.layoutDirty}`,\n )\n lines.push(\"\")\n\n // Layout info\n const { layout } = ctx.node\n lines.push(\"LAYOUT:\")\n if (layout.layoutChanged) {\n lines.push(\" ⚠ LAYOUT CHANGED:\")\n lines.push(` prevLayout: ${formatRect(layout.prevLayout)}`)\n lines.push(` boxRect: ${formatRect(layout.boxRect)}`)\n } else {\n lines.push(` boxRect: ${formatRect(layout.boxRect)}`)\n }\n lines.push(` scrollRect: ${formatRect(layout.scrollRect)}`)\n lines.push(\"\")\n\n // Scroll context\n if (ctx.node.scroll) {\n lines.push(\"SCROLL STATE (this node):\")\n formatScrollState(lines, ctx.node.scroll)\n lines.push(\"\")\n }\n } else {\n lines.push(\"INNERMOST NODE: (none found at this position)\")\n lines.push(\"\")\n }\n\n // Scroll ancestors\n if (ctx.scrollAncestors.length > 0) {\n lines.push(\"SCROLL ANCESTORS:\")\n for (const ancestor of ctx.scrollAncestors) {\n lines.push(` ${ancestor.path}:`)\n if (ancestor.scroll) {\n formatScrollState(lines, ancestor.scroll, \" \")\n }\n }\n lines.push(\"\")\n }\n\n // Containing nodes (for debugging layering issues)\n if (ctx.containingNodes.length > 1) {\n lines.push(\"ALL CONTAINING NODES (outermost to innermost):\")\n for (const node of ctx.containingNodes) {\n const flags = Object.entries(node.dirtyFlags)\n .filter(([, v]) => v)\n .map(([k]) => k.replace(\"Dirty\", \"\"))\n .join(\",\")\n const flagStr = flags ? ` [${flags}]` : \" [clean]\"\n const bgStr = node.backgroundColor ? ` bg=${node.backgroundColor}` : \"\"\n const childStr = node.childIndex !== null ? ` child[${node.childIndex}]` : \"\"\n lines.push(` ${node.path}${flagStr}${bgStr}${childStr}`)\n }\n lines.push(\"\")\n }\n\n // Fast-path analysis\n if (ctx.fastPathAnalysis.length > 0) {\n lines.push(\"FAST-PATH ANALYSIS:\")\n for (const line of ctx.fastPathAnalysis) {\n lines.push(` ${line}`)\n }\n lines.push(\"\")\n }\n\n // Render-phase instrumentation stats\n if (renderPhaseStats) {\n const s = renderPhaseStats\n lines.push(\"RENDER PHASE STATS:\")\n lines.push(` nodesVisited: ${s.nodesVisited} nodesRendered: ${s.nodesRendered} nodesSkipped: ${s.nodesSkipped}`)\n lines.push(` textNodes: ${s.textNodes} boxNodes: ${s.boxNodes} clearOps: ${s.clearOps}`)\n // Per-flag breakdown (why nodes weren't skipped)\n const flagLines: string[] = []\n if (s.noPrevBuffer) flagLines.push(`noPrevBuffer=${s.noPrevBuffer}`)\n if (s.flagContentDirty) flagLines.push(`contentDirty=${s.flagContentDirty}`)\n if (s.flagStylePropsDirty) flagLines.push(`stylePropsDirty=${s.flagStylePropsDirty}`)\n if (s.flagLayoutChanged) flagLines.push(`layoutChanged=${s.flagLayoutChanged}`)\n if (s.flagSubtreeDirty) flagLines.push(`subtreeDirty=${s.flagSubtreeDirty}`)\n if (s.flagChildrenDirty) flagLines.push(`childrenDirty=${s.flagChildrenDirty}`)\n if (s.flagChildPositionChanged) flagLines.push(`childPositionChanged=${s.flagChildPositionChanged}`)\n if (flagLines.length > 0) {\n lines.push(` render reasons: ${flagLines.join(\", \")}`)\n }\n // Scroll container diagnostics\n if (s.scrollContainerCount > 0) {\n lines.push(` scrollContainers: ${s.scrollContainerCount} viewportCleared: ${s.scrollViewportCleared}`)\n if (s.scrollClearReason) lines.push(` scrollClearReason: ${s.scrollClearReason}`)\n }\n // Normal container diagnostics\n if (s.normalChildrenRepaint > 0) {\n lines.push(` normalChildrenRepaint: ${s.normalChildrenRepaint}`)\n if (s.normalRepaintReason) lines.push(` normalRepaintReason: ${s.normalRepaintReason}`)\n }\n // Cascade diagnostics\n if (s.cascadeMinDepth < 999) {\n lines.push(` cascadeMinDepth: ${s.cascadeMinDepth}`)\n if (s.cascadeNodes) lines.push(` cascadeNodes: ${s.cascadeNodes}`)\n }\n lines.push(\"\")\n }\n\n return lines.join(\"\\n\")\n}\n\nfunction formatRect(rect: Rect | null): string {\n if (!rect) return \"(null)\"\n return `{x:${rect.x}, y:${rect.y}, w:${rect.width}, h:${rect.height}}`\n}\n\nfunction formatScrollState(lines: string[], scroll: NonNullable<NodeDebugInfo[\"scroll\"]>, indent = \" \"): void {\n if (scroll.offsetChanged) {\n lines.push(`${indent}⚠ SCROLL CHANGED: offset ${scroll.prevOffset} → ${scroll.offset}`)\n } else {\n lines.push(`${indent}offset: ${scroll.offset}`)\n }\n lines.push(\n `${indent}viewport: ${scroll.viewportHeight}/${scroll.contentHeight} (hidden: ▲${scroll.hiddenAbove} ▼${scroll.hiddenBelow})`,\n )\n lines.push(`${indent}visibleRange: [${scroll.firstVisibleChild}..${scroll.lastVisibleChild}]`)\n}\n","/**\n * Non-TTY Mode Support for Silvery\n *\n * Provides detection and rendering modes for non-interactive environments:\n * - Piped output (process.stdout.isTTY === false)\n * - CI environments\n * - TERM=dumb\n *\n * When in non-TTY mode, silvery avoids cursor positioning codes that garble\n * output in non-interactive environments.\n *\n * Modes:\n * - 'auto': Detect based on environment (default)\n * - 'tty': Force TTY mode (normal cursor positioning)\n * - 'line-by-line': Simple newline-separated output, no cursor movement\n * - 'static': Single output at end (no updates)\n * - 'plain': Strip all ANSI codes\n */\n\nimport { stripAnsi } from \"./unicode\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Non-TTY rendering mode.\n *\n * - 'auto': Auto-detect based on environment\n * - 'tty': Force TTY mode with cursor positioning\n * - 'line-by-line': Output lines without cursor repositioning\n * - 'static': Single final output only\n * - 'plain': Strip all ANSI escape codes\n */\nexport type NonTTYMode = \"auto\" | \"tty\" | \"line-by-line\" | \"static\" | \"plain\"\n\n/**\n * Options for non-TTY output.\n */\nexport interface NonTTYOptions {\n /** The rendering mode. Default: 'auto' */\n mode?: NonTTYMode\n /** Output stream to check for TTY status. Default: process.stdout */\n stdout?: NodeJS.WriteStream\n}\n\n/**\n * Resolved non-TTY mode after auto-detection.\n */\nexport type ResolvedNonTTYMode = Exclude<NonTTYMode, \"auto\">\n\n// ============================================================================\n// Detection\n// ============================================================================\n\n/**\n * Check if the environment is a TTY.\n *\n * Returns false if:\n * - stdout.isTTY is false or undefined\n * - TERM=dumb\n * - CI environment variables are set\n */\nexport function isTTY(stdout: NodeJS.WriteStream = process.stdout): boolean {\n // Check stdout.isTTY\n if (!stdout.isTTY) {\n return false\n }\n\n // Check TERM=dumb\n if (process.env.TERM === \"dumb\") {\n return false\n }\n\n // Check common CI environment variables\n if (\n process.env.CI ||\n process.env.GITHUB_ACTIONS ||\n process.env.GITLAB_CI ||\n process.env.JENKINS_URL ||\n process.env.BUILDKITE ||\n process.env.CIRCLECI ||\n process.env.TRAVIS\n ) {\n return false\n }\n\n return true\n}\n\n/**\n * Resolve the non-TTY mode based on options and environment.\n *\n * When mode is 'auto':\n * - If TTY detected: returns 'tty'\n * - If non-TTY detected: returns 'line-by-line'\n */\nexport function resolveNonTTYMode(options: NonTTYOptions = {}): ResolvedNonTTYMode {\n const { mode = \"auto\", stdout = process.stdout } = options\n\n if (mode !== \"auto\") {\n return mode\n }\n\n // Auto-detect based on environment\n return isTTY(stdout) ? \"tty\" : \"line-by-line\"\n}\n\n// Re-export stripAnsi from unicode.ts (canonical implementation)\nexport { stripAnsi } from \"./unicode\"\n\n// ============================================================================\n// Line-by-Line Output\n// ============================================================================\n\n/**\n * Convert buffer output to line-by-line format.\n *\n * Instead of using cursor positioning, outputs each line with a simple\n * carriage return and clear-to-end-of-line sequence.\n *\n * @param content The rendered content (may contain ANSI codes but no cursor positioning)\n * @param prevLineCount Number of lines in the previous frame (for clearing)\n * @returns Output string suitable for non-TTY rendering\n */\nexport function toLineByLineOutput(content: string, prevLineCount: number): string {\n const lines = content.split(\"\\n\")\n let output = \"\"\n\n // Move cursor up to overwrite previous content (if any)\n if (prevLineCount > 0) {\n // Move to start of first line\n output += \"\\r\"\n // Move up\n if (prevLineCount > 1) {\n output += `\\x1b[${prevLineCount - 1}A`\n }\n }\n\n // Output each line\n for (let i = 0; i < lines.length; i++) {\n if (i > 0) {\n output += \"\\n\"\n }\n output += lines[i]\n // Clear to end of line (removes leftover content from longer previous lines)\n output += \"\\x1b[K\"\n }\n\n // Clear any remaining lines from previous frame\n const extraLines = prevLineCount - lines.length\n if (extraLines > 0) {\n for (let i = 0; i < extraLines; i++) {\n output += \"\\n\\x1b[K\"\n }\n // Move cursor back up to end of content\n output += `\\x1b[${extraLines}A`\n }\n\n return output\n}\n\n/**\n * Convert buffer output to plain text format.\n *\n * Strips all ANSI codes and outputs simple newline-separated text.\n * No cursor movement or clearing.\n *\n * @param content The rendered content\n * @param prevLineCount Number of lines in the previous frame (unused in plain mode)\n * @returns Plain text output\n */\nexport function toPlainOutput(content: string, _prevLineCount: number): string {\n // Strip ANSI codes\n const plain = stripAnsi(content)\n\n // Trim trailing whitespace from each line but preserve structure\n const lines = plain.split(\"\\n\").map((line) => line.trimEnd())\n\n // Remove trailing empty lines\n while (lines.length > 0 && lines[lines.length - 1] === \"\") {\n lines.pop()\n }\n\n return lines.join(\"\\n\")\n}\n\n// ============================================================================\n// Output Helpers\n// ============================================================================\n\n/**\n * Create an output transformer based on the non-TTY mode.\n *\n * @param mode The resolved non-TTY mode\n * @returns A function that transforms output based on the mode\n */\nexport function createOutputTransformer(mode: ResolvedNonTTYMode): (content: string, prevLineCount: number) => string {\n switch (mode) {\n case \"tty\":\n // Pass through unchanged\n return (content) => content\n\n case \"line-by-line\":\n return toLineByLineOutput\n\n case \"static\":\n // For static mode, we return empty string for intermediate renders\n // The final render is handled by the caller\n return () => \"\"\n\n case \"plain\":\n return toPlainOutput\n }\n}\n\n/**\n * Count the number of lines in a string.\n */\nexport function countLines(str: string): number {\n if (!str) return 0\n return str.split(\"\\n\").length\n}\n","/**\n * Clipboard Backend Abstraction\n *\n * Pluggable clipboard system with support for multiple backends.\n * The default backend uses OSC 52 for terminal clipboard access.\n *\n * Architecture:\n * - ClipboardBackend: interface for clipboard read/write\n * - ClipboardData: multi-format clipboard content (text, markdown, html, internal)\n * - createOsc52Backend: OSC 52 terminal clipboard (default)\n * - createInternalClipboardBackend: in-memory store for rich app-internal paste\n * - createCompositeClipboard: fan-out writes to multiple backends\n *\n * OSC 52 Protocol:\n * - Copy: ESC ] 52 ; c ; <base64> BEL\n * - Query: ESC ] 52 ; c ; ? BEL\n * - Response: ESC ] 52 ; c ; <base64> BEL (or ST terminator)\n *\n * Supported by: Ghostty, Kitty, WezTerm, iTerm2, xterm, foot, tmux\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Multi-format clipboard content.\n *\n * Plain text is always present. Optional rich formats allow applications\n * to provide structured data for within-app paste without losing it\n * through the plain-text-only system clipboard.\n */\nexport interface ClipboardData {\n /** Plain text content (always present) */\n text: string\n /** Markdown representation */\n markdown?: string\n /** HTML representation */\n html?: string\n /** App-specific structured data (e.g., node tree for structured paste) */\n internal?: unknown\n}\n\n/**\n * Clipboard backend capabilities.\n *\n * `text` is always true — every backend supports plain text.\n * Rich format support is backend-dependent.\n */\nexport interface ClipboardCapabilities {\n readonly text: true\n readonly html?: boolean\n readonly markdown?: boolean\n readonly internal?: boolean\n}\n\n/**\n * Pluggable clipboard backend.\n *\n * Backends handle the transport of clipboard data to/from the system\n * or an in-memory store. The framework writes ClipboardData; the backend\n * decides what formats it can actually carry.\n */\nexport interface ClipboardBackend {\n /** Write clipboard data. Backends may ignore formats they don't support. */\n write(data: ClipboardData): void | Promise<void>\n /** Read clipboard contents as plain text. Not all backends support read. */\n read?(): Promise<string>\n /** What formats this backend supports */\n readonly capabilities: ClipboardCapabilities\n}\n\n// ============================================================================\n// Writable interface (avoid coupling to Node.js WriteStream)\n// ============================================================================\n\n/** Minimal writable interface for clipboard output */\ninterface Writable {\n write(data: string): boolean | void\n}\n\n// ============================================================================\n// OSC 52 Constants\n// ============================================================================\n\nconst ESC = \"\\x1b\"\nconst BEL = \"\\x07\"\n\n/** OSC 52 response prefix */\nconst OSC52_PREFIX = `${ESC}]52;c;`\n\n// ============================================================================\n// OSC 52 Backend\n// ============================================================================\n\n/**\n * Create an OSC 52 clipboard backend.\n *\n * Writes plain text to the system clipboard via the terminal's OSC 52 support.\n * Works across SSH sessions. Rich formats (markdown, html, internal) are\n * silently ignored — OSC 52 only carries plain text.\n *\n * Quirks:\n * - Some terminals limit payload size (~100KB)\n * - tmux requires `set -g set-clipboard on`\n * - Some terminals only support BEL terminator (not ST)\n */\nexport function createOsc52Backend(stdout: Writable): ClipboardBackend {\n return {\n write(data: ClipboardData): void {\n const base64 = Buffer.from(data.text, \"utf-8\").toString(\"base64\")\n stdout.write(`${ESC}]52;c;${base64}${BEL}`)\n },\n\n async read(): Promise<string> {\n // OSC 52 read requires async response parsing from stdin.\n // The query is sent here; the caller must parse the response\n // from the terminal input stream using parseClipboardResponse().\n stdout.write(`${ESC}]52;c;?${BEL}`)\n // Note: actual response arrives asynchronously via stdin.\n // This is a limitation of the terminal protocol — true async\n // read requires coordination with the input parser.\n return \"\"\n },\n\n capabilities: { text: true },\n }\n}\n\n// ============================================================================\n// Internal Clipboard Backend\n// ============================================================================\n\n/**\n * In-memory clipboard store for within-app paste.\n *\n * Stores the full ClipboardData including rich formats that OSC 52 can't carry.\n * Used alongside OSC 52 so plain text goes to the system clipboard while\n * rich data is available for internal paste operations.\n */\nexport function createInternalClipboardBackend(): ClipboardBackend & {\n /** Get the stored clipboard data, or null if empty */\n getData(): ClipboardData | null\n /** Get the timestamp of the last write */\n getTimestamp(): number\n} {\n let stored: ClipboardData | null = null\n let timestamp = 0\n\n return {\n write(data: ClipboardData): void {\n stored = { ...data }\n timestamp = Date.now()\n },\n\n async read(): Promise<string> {\n return stored?.text ?? \"\"\n },\n\n getData(): ClipboardData | null {\n return stored ? { ...stored } : null\n },\n\n getTimestamp(): number {\n return timestamp\n },\n\n capabilities: { text: true, html: true, markdown: true, internal: true },\n }\n}\n\n// ============================================================================\n// Composite Clipboard\n// ============================================================================\n\n/**\n * Create a composite clipboard that writes to multiple backends.\n *\n * Writes fan out to all backends. Reads come from the first backend\n * that supports read (in order). This lets you do OSC 52 + internal\n * store simultaneously: plain text goes to system clipboard, rich\n * data stays in memory for structured paste.\n */\nexport function createCompositeClipboard(...backends: ClipboardBackend[]): ClipboardBackend {\n return {\n write(data: ClipboardData): void | Promise<void> {\n const promises: Promise<void>[] = []\n for (const backend of backends) {\n const result = backend.write(data)\n if (result instanceof Promise) {\n promises.push(result)\n }\n }\n if (promises.length > 0) {\n return Promise.all(promises).then(() => undefined)\n }\n },\n\n async read(): Promise<string> {\n for (const backend of backends) {\n if (backend.read) {\n const text = await backend.read()\n if (text) return text\n }\n }\n return \"\"\n },\n\n capabilities: {\n text: true,\n html: backends.some((b) => b.capabilities.html) || undefined,\n markdown: backends.some((b) => b.capabilities.markdown) || undefined,\n internal: backends.some((b) => b.capabilities.internal) || undefined,\n },\n }\n}\n\n// ============================================================================\n// Backwards-compatible API (delegates to OSC 52)\n// ============================================================================\n\n/**\n * Copy text to the system clipboard via OSC 52.\n * Encodes the text as base64 and writes the OSC 52 sequence to stdout.\n *\n * @deprecated Use createOsc52Backend() for new code.\n */\nexport function copyToClipboard(stdout: NodeJS.WriteStream, text: string): void {\n const base64 = Buffer.from(text).toString(\"base64\")\n stdout.write(`${ESC}]52;c;${base64}${BEL}`)\n}\n\n/**\n * Request clipboard contents via OSC 52.\n * Writes the OSC 52 query sequence. The terminal will respond with\n * an OSC 52 response containing the clipboard contents as base64.\n * Use parseClipboardResponse() to decode the response.\n *\n * @deprecated Use createOsc52Backend() for new code.\n */\nexport function requestClipboard(stdout: NodeJS.WriteStream): void {\n stdout.write(`${ESC}]52;c;?${BEL}`)\n}\n\n// ============================================================================\n// Response Parsing\n// ============================================================================\n\n/**\n * Parse an OSC 52 clipboard response and decode the base64 content.\n *\n * Returns the decoded clipboard text, or null if the input is not\n * an OSC 52 clipboard response.\n *\n * Handles both BEL (\\x07) and ST (ESC \\) terminators.\n */\nexport function parseClipboardResponse(input: string): string | null {\n const prefixIdx = input.indexOf(OSC52_PREFIX)\n if (prefixIdx === -1) return null\n\n const contentStart = prefixIdx + OSC52_PREFIX.length\n\n // Reject the query marker — it's not a response\n if (input[contentStart] === \"?\") return null\n\n // Find terminator: BEL (\\x07) or ST (ESC \\)\n let contentEnd = input.indexOf(BEL, contentStart)\n if (contentEnd === -1) {\n contentEnd = input.indexOf(`${ESC}\\\\`, contentStart)\n }\n if (contentEnd === -1) return null\n\n const base64 = input.slice(contentStart, contentEnd)\n return Buffer.from(base64, \"base64\").toString(\"utf-8\")\n}\n","/**\n * Silvery Render Scheduler\n *\n * Batches rapid state updates to prevent flicker and improve performance.\n * Uses queueMicrotask for coalescing multiple synchronous state changes\n * into a single render pass.\n *\n * Features:\n * - Microtask-based batching (coalesces synchronous updates)\n * - Frame batching to prevent flicker\n * - Resize handling with debounce\n * - Clean shutdown\n */\n\nimport { appendFileSync } from \"node:fs\"\nimport { type Logger, createLogger } from \"loggily\"\nimport { type TerminalBuffer, bufferToText, cellEquals } from \"./buffer\"\nimport { buildMismatchContext, formatMismatchContext } from \"@silvery/test/debug-mismatch\"\nimport {\n type ResolvedNonTTYMode as ResolvedMode,\n countLines,\n createOutputTransformer,\n resolveNonTTYMode,\n stripAnsi,\n} from \"./non-tty\"\nimport { getCursorState as globalGetCursorState, type CursorAccessors } from \"@silvery/ag-react/hooks/useCursor\"\nimport { copyToClipboard as copyToClipboardImpl } from \"./clipboard\"\nimport { ANSI, notify as notifyTerminal, setCursorStyle, resetCursorStyle } from \"./output\"\nimport { executeRender, type PipelineConfig } from \"./pipeline\"\nimport type { RenderPhaseStats } from \"./pipeline/types\"\nimport type { AgNode } from \"@silvery/ag/types\"\n\nconst log = createLogger(\"silvery:scheduler\")\n\n/**\n * Whether synchronized update mode (DEC 2026) is enabled.\n *\n * Disabled by default due to a Ghostty rendering bug where incremental\n * cursor-positioned updates inside a sync region cause progressive visual\n * corruption. Works correctly in Kitty. Full renders (bufferToAnsi) work\n * fine with sync — only incremental diff output (changesToAnsi) triggers it.\n *\n * Set SILVERY_SYNC_UPDATE=1 to force-enable (e.g., for testing in Kitty).\n * TODO: Re-enable by default once the Ghostty bug is fixed.\n * See: https://github.com/ghostty-org/ghostty/discussions/11002\n */\nconst SYNC_UPDATE_ENABLED = process.env.SILVERY_SYNC_UPDATE === \"1\" || process.env.SILVERY_SYNC_UPDATE === \"true\"\n\n// ============================================================================\n// Errors\n// ============================================================================\n\n// Re-export from errors.ts (kept separate for React-free barrel imports)\nexport { IncrementalRenderMismatchError } from \"./errors\"\nimport { IncrementalRenderMismatchError } from \"./errors\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Non-TTY mode for rendering in non-interactive environments.\n */\nexport type NonTTYMode = \"auto\" | \"tty\" | \"line-by-line\" | \"static\" | \"plain\"\n\n/**\n * Resolved non-TTY mode after auto-detection.\n */\nexport type ResolvedNonTTYMode = Exclude<NonTTYMode, \"auto\">\n\nexport interface SchedulerOptions {\n /** stdout stream for writing output */\n stdout: NodeJS.WriteStream\n /** Root Silvery node */\n root: AgNode\n /** Debug mode - logs render timing */\n debug?: boolean\n /** Minimum time between frames in ms (default: 16 for ~60fps) */\n minFrameTime?: number\n /** Render mode: fullscreen (absolute positioning) or inline (relative positioning) */\n mode?: \"fullscreen\" | \"inline\"\n /**\n * Non-TTY mode for non-interactive environments (default: 'auto')\n *\n * - 'auto': Detect based on environment\n * - 'tty': Force TTY mode\n * - 'line-by-line': Simple line output\n * - 'static': Only output final frame\n * - 'plain': Strip all ANSI codes\n */\n nonTTYMode?: NonTTYMode\n /** Slow frame warning threshold in ms (default: 50). Set to 0 to disable. */\n slowFrameThreshold?: number\n /** Pipeline configuration (caps-scoped measurer + output phase) */\n pipelineConfig?: PipelineConfig\n /** Per-instance cursor accessors. Falls back to module-level globals if not provided. */\n cursorAccessors?: CursorAccessors\n /**\n * Custom output writer. When provided, all render output is routed through\n * this function instead of stdout.write(). Used by the output guard to\n * ensure only silvery's render pipeline writes to stdout in alt screen mode.\n */\n writeOutput?: (data: string) => boolean\n}\n\nexport interface RenderStats {\n /** Number of renders executed */\n renderCount: number\n /** Number of renders skipped (batched) */\n skippedCount: number\n /** Last render duration in ms */\n lastRenderTime: number\n /** Average render time in ms */\n avgRenderTime: number\n}\n\n// ============================================================================\n// RenderScheduler Class\n// ============================================================================\n\n/**\n * Schedules and batches render operations.\n *\n * Usage:\n * ```ts\n * const scheduler = new RenderScheduler({\n * stdout: process.stdout,\n * root: rootNode,\n * });\n *\n * // Schedule renders (automatically batched)\n * scheduler.scheduleRender();\n * scheduler.scheduleRender(); // This won't cause duplicate render\n *\n * // Force immediate render\n * scheduler.forceRender();\n *\n * // Clean shutdown\n * scheduler.dispose();\n * ```\n */\nexport class RenderScheduler {\n private stdout: NodeJS.WriteStream\n private root: AgNode\n private debugMode: boolean\n private minFrameTime: number\n private slowFrameThreshold: number\n private mode: \"fullscreen\" | \"inline\"\n private pipelineConfig?: PipelineConfig\n private getCursorState: () => import(\"@silvery/ag-react/hooks/useCursor\").CursorState | null\n private nonTTYMode: ResolvedMode\n private outputTransformer: (content: string, prevLineCount: number) => string\n private writeOutput: (data: string) => boolean\n private log: Logger\n\n /** Previous buffer for diffing */\n private prevBuffer: TerminalBuffer | null = null\n\n /** Line count of previous render (for non-TTY modes) */\n private prevLineCount = 0\n\n /** Accumulated output for static mode */\n private staticOutput = \"\"\n\n /** Is a render currently scheduled? */\n private renderScheduled = false\n\n /** Last render timestamp */\n private lastRenderTime = 0\n\n /** Pending frame timeout (for frame rate limiting) */\n private frameTimeout: ReturnType<typeof setTimeout> | null = null\n\n /** Resize listener cleanup */\n private resizeCleanup: (() => void) | null = null\n\n /** Render statistics */\n private stats: RenderStats = {\n renderCount: 0,\n skippedCount: 0,\n lastRenderTime: 0,\n avgRenderTime: 0,\n }\n\n /** Is the scheduler disposed? */\n private disposed = false\n\n /** Is the scheduler paused? When paused, renders are deferred until resume. */\n private paused = false\n\n /** Was a render requested while paused? */\n private pendingWhilePaused = false\n\n /**\n * Lines written to stdout between renders (inline mode only).\n * When useScrollback or other code writes to stdout, those lines\n * displace the terminal cursor. This offset is consumed on the next render.\n */\n private scrollbackOffset = 0\n\n constructor(options: SchedulerOptions) {\n this.stdout = options.stdout\n this.root = options.root\n this.debugMode = options.debug ?? false\n this.minFrameTime = options.minFrameTime ?? 16\n this.slowFrameThreshold = options.slowFrameThreshold ?? 50\n this.mode = options.mode ?? \"fullscreen\"\n this.pipelineConfig = options.pipelineConfig\n this.getCursorState = options.cursorAccessors?.getCursorState ?? globalGetCursorState\n this.writeOutput = options.writeOutput ?? ((data: string) => options.stdout.write(data))\n this.log = createLogger(\"silvery:scheduler\") as unknown as Logger\n\n // Resolve non-TTY mode based on environment\n this.nonTTYMode = resolveNonTTYMode({\n mode: options.nonTTYMode,\n stdout: this.stdout,\n })\n this.outputTransformer = createOutputTransformer(this.nonTTYMode)\n\n log.debug?.(`non-TTY mode resolved to: ${this.nonTTYMode}`)\n\n // Listen for terminal resize (only in TTY mode)\n if (this.nonTTYMode === \"tty\") {\n this.setupResizeListener()\n }\n }\n\n /**\n * Get the resolved non-TTY mode.\n */\n getNonTTYMode(): ResolvedMode {\n return this.nonTTYMode\n }\n\n // ==========================================================================\n // Public API\n // ==========================================================================\n\n /**\n * Schedule a render on the next microtask.\n *\n * Multiple calls within the same synchronous execution will be\n * coalesced into a single render.\n */\n scheduleRender(): void {\n if (this.disposed) return\n\n if (this.paused) {\n this.pendingWhilePaused = true\n return\n }\n\n if (this.renderScheduled) {\n this.stats.skippedCount++\n log.debug?.(`render skipped (batched), total: ${this.stats.skippedCount}`)\n return\n }\n\n this.renderScheduled = true\n log.debug?.(\"render scheduled\")\n\n // Use queueMicrotask for batching synchronous updates\n queueMicrotask(() => {\n this.renderScheduled = false\n\n if (this.disposed) return\n\n // Check frame rate limiting\n const now = Date.now()\n const timeSinceLastRender = now - this.lastRenderTime\n\n if (timeSinceLastRender < this.minFrameTime) {\n // Schedule for next frame\n log.debug?.(`frame limited, delay: ${this.minFrameTime - timeSinceLastRender}ms`)\n this.scheduleNextFrame(this.minFrameTime - timeSinceLastRender)\n } else {\n this.executeRender()\n }\n })\n }\n\n /**\n * Force an immediate render, bypassing batching.\n */\n forceRender(): void {\n if (this.disposed) return\n\n if (this.paused) {\n this.pendingWhilePaused = true\n return\n }\n\n // Cancel any pending scheduled render\n this.renderScheduled = false\n if (this.frameTimeout) {\n clearTimeout(this.frameTimeout)\n this.frameTimeout = null\n }\n\n this.executeRender()\n }\n\n /**\n * Get render statistics.\n */\n getStats(): RenderStats {\n return { ...this.stats }\n }\n\n /**\n * Report lines written to stdout between renders (inline mode only).\n * This adjusts cursor position tracking so the next render accounts\n * for the extra lines. Used by useScrollback to notify the scheduler\n * when it writes frozen items to stdout.\n */\n addScrollbackLines(lines: number): void {\n if (this.mode !== \"inline\" || lines <= 0) return\n this.scrollbackOffset += lines\n }\n\n /**\n * Send a terminal notification.\n *\n * Auto-detects terminal type and uses the best available method:\n * - iTerm2 → OSC 9\n * - Kitty → OSC 99\n * - Others → BEL\n */\n notify(message: string, opts?: { title?: string }): void {\n if (this.disposed) return\n // Route through writeOutput so the output guard allows it through\n const writable = { write: (data: string) => this.writeOutput(data) } as NodeJS.WriteStream\n notifyTerminal(writable, message, opts)\n }\n\n /**\n * Copy text to the system clipboard via OSC 52.\n * Works across SSH sessions in terminals that support it.\n */\n copyToClipboard(text: string): void {\n if (this.disposed) return\n // Route through writeOutput so the output guard allows it through\n const writable = { write: (data: string) => this.writeOutput(data) } as NodeJS.WriteStream\n copyToClipboardImpl(writable, text)\n }\n\n /**\n * Pause rendering. While paused, scheduled and forced renders are deferred.\n * Input handling continues normally. Call resume() to unpause and force a\n * full redraw. Used for screen-switching (alt screen ↔ normal screen).\n */\n pause(): void {\n if (this.disposed || this.paused) return\n this.paused = true\n this.pendingWhilePaused = false\n log.debug?.(\"scheduler paused\")\n }\n\n /**\n * Resume rendering after pause. Resets the previous buffer so the next\n * render outputs everything (full redraw), then forces an immediate render.\n */\n resume(): void {\n if (this.disposed || !this.paused) return\n this.paused = false\n log.debug?.(\"scheduler resumed\")\n\n // Reset buffer for full redraw (alt screen was switched)\n this.prevBuffer = null\n\n // If anything was deferred, render now\n if (this.pendingWhilePaused) {\n this.pendingWhilePaused = false\n this.executeRender()\n }\n }\n\n /**\n * Whether the scheduler is currently paused.\n */\n isPaused(): boolean {\n return this.paused\n }\n\n /**\n * Clear the terminal and reset buffer.\n */\n clear(): void {\n if (this.disposed) return\n\n // Clear screen and keep cursor hidden\n this.writeOutput(\"\\x1b[2J\\x1b[H\\x1b[?25l\")\n\n // Reset buffer so next render outputs everything\n this.prevBuffer = null\n }\n\n /**\n * Dispose the scheduler and clean up resources.\n */\n [Symbol.dispose](): void {\n this.dispose()\n }\n\n dispose(): void {\n if (this.disposed) return\n\n log.info?.(\n `dispose: renders=${this.stats.renderCount}, skipped=${this.stats.skippedCount}, avg=${Math.round(this.stats.avgRenderTime)}ms`,\n )\n this.disposed = true\n\n // Cancel pending renders\n this.renderScheduled = false\n if (this.frameTimeout) {\n clearTimeout(this.frameTimeout)\n this.frameTimeout = null\n }\n\n // Remove resize listener\n if (this.resizeCleanup) {\n this.resizeCleanup()\n this.resizeCleanup = null\n }\n\n // In static mode, output the final frame on dispose\n if (this.nonTTYMode === \"static\" && this.staticOutput) {\n this.writeOutput(this.staticOutput)\n this.writeOutput(\"\\n\")\n }\n }\n\n /**\n * Get the last rendered output (for static mode).\n * Returns the plain text output that would be written on dispose.\n */\n getStaticOutput(): string {\n return this.staticOutput\n }\n\n // ==========================================================================\n // Private Methods\n // ==========================================================================\n\n /**\n * Schedule render for next frame (frame rate limiting).\n */\n private scheduleNextFrame(delay: number): void {\n if (this.frameTimeout) return\n\n this.frameTimeout = setTimeout(() => {\n this.frameTimeout = null\n if (!this.disposed) {\n this.executeRender()\n }\n }, delay)\n }\n\n /**\n * Execute the actual render.\n */\n private executeRender(): void {\n using render = this.log.span(\"render\")\n const startTime = Date.now()\n\n try {\n // Get terminal dimensions\n const width = this.stdout.columns ?? 80\n // Inline mode: use NaN height so layout engine auto-sizes to content.\n // Fullscreen mode: use terminal rows as the constraint.\n const height = this.mode === \"inline\" ? NaN : (this.stdout.rows ?? 24)\n\n log.debug?.(`render #${this.stats.renderCount + 1}: ${width}x${height}, nonTTYMode=${this.nonTTYMode}`)\n\n // Run render pipeline\n const scrollbackOffset = this.scrollbackOffset\n this.scrollbackOffset = 0 // Consume the offset\n // For inline mode, pass cursor state into the pipeline so the output\n // phase can position the real terminal cursor at the useCursor() location.\n const inlineCursor = this.mode === \"inline\" ? this.getCursorState() : undefined\n const { output, buffer } = executeRender(\n this.root,\n width,\n height,\n this.prevBuffer,\n {\n mode: this.mode,\n scrollbackOffset,\n termRows: this.mode === \"inline\" ? (this.stdout.rows ?? 24) : undefined,\n cursorPos: inlineCursor,\n },\n this.pipelineConfig,\n )\n\n // Transform output based on non-TTY mode\n let transformedOutput: string\n if (this.nonTTYMode === \"tty\") {\n // Pass through unchanged\n transformedOutput = output\n } else if (this.nonTTYMode === \"static\") {\n // Store for final output, don't write yet\n this.staticOutput = stripAnsi(output)\n transformedOutput = \"\"\n } else {\n // Apply line-by-line or plain transformation\n transformedOutput = this.outputTransformer(output, this.prevLineCount)\n this.prevLineCount = countLines(output)\n }\n\n // Build cursor control suffix (position + show/hide).\n // This goes after rendered content so the terminal cursor lands\n // at the right spot after painting.\n let cursorSuffix = \"\"\n if (this.nonTTYMode === \"tty\") {\n const cursor = this.getCursorState()\n if (cursor?.visible) {\n const shapeSeq = cursor.shape ? setCursorStyle(cursor.shape) : resetCursorStyle()\n cursorSuffix = ANSI.moveCursor(cursor.x, cursor.y) + shapeSeq + ANSI.CURSOR_SHOW\n } else {\n cursorSuffix = ANSI.CURSOR_HIDE\n }\n }\n\n // Write output wrapped with synchronized update (DEC 2026) for TTY mode.\n // This tells the terminal to batch the output and paint atomically,\n // preventing tearing during rapid screen updates.\n if (transformedOutput.length > 0 || cursorSuffix.length > 0) {\n const fullOutput =\n this.nonTTYMode === \"tty\" && SYNC_UPDATE_ENABLED\n ? `${ANSI.SYNC_BEGIN}${transformedOutput}${cursorSuffix}${ANSI.SYNC_END}`\n : transformedOutput + cursorSuffix\n\n // Debug: log output sizes to detect potential pipe buffer splits\n if (log.debug) {\n const bytes = Buffer.byteLength(fullOutput)\n log.debug?.(\n `stdout.write: ${bytes} bytes (${transformedOutput.length} chars output + ${cursorSuffix.length} chars cursor)`,\n )\n if (bytes > 16384) {\n log.debug?.(\n `large output: ${bytes} bytes may exceed pipe buffer (16KB on macOS), risk of mid-sequence split`,\n )\n }\n }\n\n // Capture raw ANSI output to file for debugging garbled rendering\n const captureFile = process.env.SILVERY_CAPTURE_OUTPUT\n if (captureFile) {\n const fs = require(\"fs\")\n fs.appendFileSync(\n captureFile,\n `--- FRAME ${this.stats.renderCount + 1} (${Buffer.byteLength(fullOutput)} bytes) ---\\n`,\n )\n fs.appendFileSync(captureFile, fullOutput)\n fs.appendFileSync(captureFile, \"\\n\")\n }\n\n this.writeOutput(fullOutput)\n }\n\n // Save buffer for next diff\n this.prevBuffer = buffer\n\n // SILVERY_STRICT: compare incremental render against fresh render\n const strictEnv = process.env.SILVERY_STRICT\n const strictMode = strictEnv && strictEnv !== \"0\" && strictEnv !== \"false\"\n if (strictMode && this.stats.renderCount > 0) {\n const renderNum = this.stats.renderCount + 1\n const { buffer: freshBuffer } = executeRender(\n this.root,\n width,\n height,\n null,\n {\n mode: this.mode === \"fullscreen\" ? \"fullscreen\" : \"inline\",\n skipLayoutNotifications: true,\n },\n this.pipelineConfig,\n )\n let found = false\n for (let y = 0; y < buffer.height && !found; y++) {\n for (let x = 0; x < buffer.width && !found; x++) {\n const a = buffer.getCell(x, y)\n const b = freshBuffer.getCell(x, y)\n if (!cellEquals(a, b)) {\n found = true\n\n // Build rich debug context\n const ctx = buildMismatchContext(this.root, x, y, a, b, renderNum)\n\n // Capture render-phase instrumentation snapshot\n const renderPhaseStats: RenderPhaseStats | undefined = (globalThis as any).__silvery_content_detail\n ? structuredClone((globalThis as any).__silvery_content_detail)\n : undefined\n\n const debugInfo = formatMismatchContext(ctx, renderPhaseStats)\n\n // Include text output for full picture\n const incText = bufferToText(buffer)\n const freshText = bufferToText(freshBuffer)\n const msg = debugInfo + `--- incremental ---\\n${incText}\\n--- fresh ---\\n${freshText}`\n\n if (process.env.DEBUG_LOG) {\n appendFileSync(process.env.DEBUG_LOG, msg + \"\\n\")\n }\n log.error?.(msg)\n // Throw special error that won't be caught by general error handler\n throw new IncrementalRenderMismatchError(msg, {\n renderPhaseStats,\n mismatchContext: ctx,\n })\n }\n }\n }\n if (!found && process.env.DEBUG_LOG) {\n appendFileSync(process.env.DEBUG_LOG, `SILVERY_STRICT: render #${renderNum} OK\\n`)\n }\n }\n\n // Update stats\n const renderTime = Date.now() - startTime\n this.stats.renderCount++\n this.stats.lastRenderTime = renderTime\n this.stats.avgRenderTime =\n (this.stats.avgRenderTime * (this.stats.renderCount - 1) + renderTime) / this.stats.renderCount\n this.lastRenderTime = Date.now()\n\n // Record span data\n render.spanData.renderCount = this.stats.renderCount\n render.spanData.renderTime = renderTime\n render.spanData.bytes = transformedOutput.length\n\n log.debug?.(\n `render #${this.stats.renderCount} complete: ${renderTime}ms, output: ${transformedOutput.length} bytes`,\n )\n\n // First render is always slow (initialization); use 5x threshold for it\n const threshold = this.stats.renderCount <= 1 ? this.slowFrameThreshold * 5 : this.slowFrameThreshold\n if (threshold > 0 && renderTime > threshold) {\n log.debug?.(\n `slow frame: render #${this.stats.renderCount} took ${renderTime}ms (threshold: ${this.slowFrameThreshold}ms, bytes: ${transformedOutput.length})`,\n )\n }\n\n if (this.debugMode) {\n this.logDebug(`Render #${this.stats.renderCount} took ${renderTime}ms`)\n }\n } catch (error) {\n // Log and re-throw all render errors - the app should handle cleanup\n log.error?.(`render error: ${error}`)\n this.logError(\"Render error:\", error)\n throw error\n }\n }\n\n /**\n * Set up terminal resize listener.\n */\n private setupResizeListener(): void {\n let resizeTimeout: ReturnType<typeof setTimeout> | null = null\n\n const handleResize = () => {\n // Debounce resize events\n if (resizeTimeout) {\n clearTimeout(resizeTimeout)\n }\n\n resizeTimeout = setTimeout(() => {\n resizeTimeout = null\n\n // Reset buffer to force full redraw\n this.prevBuffer = null\n\n // Schedule render\n this.scheduleRender()\n }, 50) // 50ms debounce\n }\n\n this.stdout.on(\"resize\", handleResize)\n\n this.resizeCleanup = () => {\n this.stdout.off(\"resize\", handleResize)\n if (resizeTimeout) {\n clearTimeout(resizeTimeout)\n }\n }\n }\n\n /**\n * Log debug message (via loggily, not stderr — stderr corrupts alt screen).\n */\n private logDebug(message: string): void {\n log.debug?.(message)\n }\n\n /**\n * Log error message (via loggily, not stderr — stderr corrupts alt screen).\n */\n private logError(message: string, error: unknown): void {\n if (error instanceof Error) {\n log.error?.(`${message} ${error.stack ?? error.message}`)\n } else {\n log.error?.(`${message} ${String(error)}`)\n }\n }\n}\n\n// ============================================================================\n// Factory Function\n// ============================================================================\n\n/**\n * Create a render scheduler.\n *\n * @param options Scheduler options\n * @returns A new RenderScheduler instance\n */\nexport function createScheduler(options: SchedulerOptions): RenderScheduler {\n return new RenderScheduler(options)\n}\n\n// ============================================================================\n// Utility: Simple Render (for testing/debugging)\n// ============================================================================\n\n/**\n * Render once to a string (for testing).\n *\n * Does not batch or diff - just runs the pipeline and returns ANSI output.\n */\nexport function renderToString(root: AgNode, width: number, height: number): string {\n const { output } = executeRender(root, width, height, null)\n return output\n}\n","/**\n * TermDef Resolution\n *\n * Converts TermDef (minimal render config) into resolved values for rendering.\n * Handles auto-detection of events from stdin, dimension defaults, etc.\n */\n\nimport type { ColorLevel, Term } from \"./ansi/index\"\nimport type { Event, EventSource } from \"@silvery/ag/types\"\n\n// ============================================================================\n// Terminal-Specific Types (moved from @silvery/ag/types)\n// ============================================================================\n\n/**\n * Minimal surface for configuring render().\n *\n * TermDef provides a simple way to configure rendering without requiring\n * a full Term instance. It's useful for:\n * - Static rendering (just width/height, no events)\n * - Testing (mock dimensions and events)\n * - Quick scripts (auto-detect everything from stdin/stdout)\n *\n * The presence of `events` (or `stdin` which auto-creates events)\n * determines the render mode:\n * - No events → static mode (render until stable)\n * - Has events → interactive mode (render until exit() called)\n *\n * @example\n * ```tsx\n * // Static render with custom width\n * const output = await render(<App />, { width: 100 })\n *\n * // Interactive with stdin/stdout\n * await render(<App />, { stdin: process.stdin, stdout: process.stdout })\n *\n * // Custom events\n * await render(<App />, { events: myEventSource })\n * ```\n */\nexport interface TermDef {\n // -------------------------------------------------------------------------\n // Output Configuration\n // -------------------------------------------------------------------------\n\n /** Output stream (used for dimensions if not specified) */\n stdout?: NodeJS.WriteStream\n\n /** Width in columns (default: stdout?.columns ?? 80) */\n width?: number\n\n /** Height in rows (default: stdout?.rows ?? 24) */\n height?: number\n\n /** Color support (true=detect, false=none, or specific level) */\n colors?: boolean | ColorLevel | null\n\n // -------------------------------------------------------------------------\n // Input Configuration\n // -------------------------------------------------------------------------\n\n /**\n * Event source for interactive mode.\n *\n * When present, render runs until exit() is called.\n * When absent, render completes when UI is stable.\n */\n events?: AsyncIterable<Event> | EventSource\n\n /**\n * Standard input stream.\n *\n * When provided (and events is not), automatically creates input events\n * from stdin, enabling interactive mode.\n */\n stdin?: NodeJS.ReadStream\n}\n\n/**\n * Options passed to the render function.\n */\nexport interface RenderOptions {\n stdout?: NodeJS.WriteStream\n stdin?: NodeJS.ReadStream\n exitOnCtrlC?: boolean\n debug?: boolean\n}\n\n/**\n * The render instance returned by render().\n */\nexport interface RenderInstance {\n /** Re-render with new element */\n rerender: (element: unknown) => void\n /** Unmount and clean up */\n unmount: () => void\n /** Wait for render to complete */\n waitUntilExit: () => Promise<void>\n /** Clear terminal output */\n clear: () => void\n}\n\n// ============================================================================\n// Resolved TermDef\n// ============================================================================\n\n/**\n * Resolved values from a TermDef, ready for use by the render system.\n */\nexport interface ResolvedTermDef {\n /** Output stream (may be mock for static rendering) */\n stdout: NodeJS.WriteStream | null\n\n /** Width in columns */\n width: number\n\n /** Height in rows */\n height: number\n\n /** Color level (null = no colors) */\n colors: ColorLevel | null\n\n /** Event source (null = static mode) */\n events: AsyncIterable<Event> | null\n\n /** Whether this is static mode (no events = render until stable) */\n isStatic: boolean\n}\n\n// ============================================================================\n// Resolution Logic\n// ============================================================================\n\n/**\n * Default dimensions when not detectable.\n */\nconst DEFAULT_WIDTH = 80\nconst DEFAULT_HEIGHT = 24\n\n/**\n * Check if a value is a Term instance (duck typing).\n */\nexport function isTerm(value: unknown): value is Term {\n // Term can be a callable Proxy (typeof === 'function') or object\n if (!value || (typeof value !== \"object\" && typeof value !== \"function\")) {\n return false\n }\n const obj = value as Record<string, unknown>\n return (\n typeof obj.hasCursor === \"function\" &&\n typeof obj.hasInput === \"function\" &&\n typeof obj.hasColor === \"function\" &&\n typeof obj.write === \"function\"\n )\n}\n\n/**\n * Check if a value is a TermDef (not a Term).\n */\nexport function isTermDef(value: unknown): value is TermDef {\n if (!value || typeof value !== \"object\") return false\n // TermDef doesn't have hasCursor method\n const obj = value as Record<string, unknown>\n return typeof obj.hasCursor !== \"function\"\n}\n\n/**\n * Resolve a TermDef into concrete values.\n *\n * @param def - TermDef to resolve\n * @returns Resolved values ready for rendering\n */\nexport function resolveTermDef(def: TermDef): ResolvedTermDef {\n // Resolve dimensions\n const width = def.width ?? def.stdout?.columns ?? DEFAULT_WIDTH\n const height = def.height ?? def.stdout?.rows ?? DEFAULT_HEIGHT\n\n // Resolve colors\n let colors: ColorLevel | null = null\n if (def.colors === true) {\n // Auto-detect from stdout\n colors = detectColorLevel(def.stdout)\n } else if (def.colors === false || def.colors === null) {\n colors = null\n } else if (def.colors) {\n colors = def.colors\n } else {\n // Default: auto-detect\n colors = detectColorLevel(def.stdout)\n }\n\n // Resolve events\n let events: AsyncIterable<Event> | null = null\n if (def.events) {\n // Explicit events provided\n events = def.events\n } else if (def.stdin) {\n // Auto-create events from stdin\n events = createInputEvents(def.stdin)\n }\n\n return {\n stdout: def.stdout ?? null,\n width,\n height,\n colors,\n events,\n isStatic: events === null,\n }\n}\n\n/**\n * Resolve a Term instance into ResolvedTermDef.\n *\n * @param term - Term instance\n * @returns Resolved values\n */\nexport function resolveFromTerm(term: Term): ResolvedTermDef {\n return {\n stdout: term.stdout,\n width: term.cols ?? DEFAULT_WIDTH,\n height: term.rows ?? DEFAULT_HEIGHT,\n colors: term.hasColor(),\n // Term instances always have interactive capabilities\n events: createInputEvents(term.stdin),\n isStatic: false,\n }\n}\n\n// ============================================================================\n// Color Detection\n// ============================================================================\n\n/**\n * Detect color level from stdout stream.\n */\nfunction detectColorLevel(stdout?: NodeJS.WriteStream): ColorLevel | null {\n // Check environment variables\n if (process.env.NO_COLOR !== undefined) {\n return null\n }\n\n if (process.env.FORCE_COLOR !== undefined) {\n const level = Number.parseInt(process.env.FORCE_COLOR, 10)\n if (level === 0) return null\n if (level === 1) return \"basic\"\n if (level === 2) return \"256\"\n if (level >= 3) return \"truecolor\"\n return \"basic\"\n }\n\n // Check COLORTERM for truecolor\n if (process.env.COLORTERM === \"truecolor\" || process.env.COLORTERM === \"24bit\") {\n return \"truecolor\"\n }\n\n // Check if TTY\n if (!stdout?.isTTY) {\n return null\n }\n\n // Check TERM for 256 color support\n const term = process.env.TERM ?? \"\"\n if (term.includes(\"256color\") || term.includes(\"256\")) {\n return \"256\"\n }\n\n // Default to basic if TTY\n return \"basic\"\n}\n\n// ============================================================================\n// Input Events\n// ============================================================================\n\n/**\n * Create an async iterable of input events from a stdin stream.\n *\n * This enables interactive mode by providing a source of keyboard events.\n */\nexport function createInputEvents(stdin: NodeJS.ReadStream): AsyncIterable<Event> {\n return {\n [Symbol.asyncIterator](): AsyncIterator<Event> {\n const buffer: Event[] = []\n let resolveNext: ((value: IteratorResult<Event>) => void) | null = null\n let done = false\n\n // Set up stdin reading\n const handleData = (chunk: Buffer | string) => {\n const data = typeof chunk === \"string\" ? chunk : chunk.toString(\"utf8\")\n\n // Convert raw input to key events\n // This is simplified - real implementation would parse ANSI sequences\n for (const char of data) {\n const event: Event = {\n type: \"key\",\n key: char,\n ctrl: char.charCodeAt(0) < 32 && char !== \"\\r\" && char !== \"\\n\" && char !== \"\\t\",\n }\n\n if (resolveNext) {\n resolveNext({ value: event, done: false })\n resolveNext = null\n } else {\n buffer.push(event)\n }\n }\n }\n\n const handleEnd = () => {\n done = true\n if (resolveNext) {\n resolveNext({ value: undefined as unknown as Event, done: true })\n resolveNext = null\n }\n }\n\n // Only set up if stdin supports raw mode\n if (stdin.isTTY && typeof stdin.setRawMode === \"function\") {\n stdin.setEncoding(\"utf8\")\n stdin.on(\"data\", handleData)\n stdin.on(\"end\", handleEnd)\n }\n\n return {\n next(): Promise<IteratorResult<Event>> {\n // Return buffered event if available\n const buffered = buffer.shift()\n if (buffered) {\n return Promise.resolve({ value: buffered, done: false })\n }\n\n // If done, return done\n if (done) {\n return Promise.resolve({\n value: undefined as unknown as Event,\n done: true,\n })\n }\n\n // Wait for next event\n return new Promise((resolve) => {\n resolveNext = resolve\n })\n },\n\n return(): Promise<IteratorResult<Event>> {\n done = true\n stdin.off(\"data\", handleData)\n stdin.off(\"end\", handleEnd)\n return Promise.resolve({\n value: undefined as unknown as Event,\n done: true,\n })\n },\n }\n },\n }\n}\n","/**\n * OSC 4 Terminal Color Palette — re-exported from @silvery/ansi (canonical location).\n */\n\nexport { queryPaletteColor, queryMultiplePaletteColors, setPaletteColor, parsePaletteResponse } from \"@silvery/ansi\"\n","/**\n * OSC 133 Semantic Prompt Markers\n *\n * Shell integration protocol that marks prompts and commands in terminal output.\n * Terminals like iTerm2, Kitty, and WezTerm use these markers to provide\n * \"jump to previous/next prompt\" navigation (Cmd+Up/Cmd+Down in iTerm2).\n *\n * Protocol: OSC 133\n * - Prompt start: ESC ] 133 ; A BEL (before user input)\n * - Prompt end: ESC ] 133 ; B BEL (after user input, before command output)\n * - Command output start: ESC ] 133 ; C BEL (before command output)\n * - Command output end: ESC ] 133 ; D ; N BEL (after command output, N = exit code)\n *\n * For a chat-style app, each \"exchange\" (user prompt + assistant response) maps to:\n * - 133;A before the user's message\n * - 133;B after the user's message\n * - 133;C before the assistant's response\n * - 133;D;0 after the assistant's response\n *\n * Supported by: iTerm2, Kitty, WezTerm, foot, Ghostty\n */\n\nexport const OSC133 = {\n /** Mark prompt start (before user input) */\n promptStart: \"\\x1b]133;A\\x07\",\n /** Mark prompt end (after user input, before command output) */\n promptEnd: \"\\x1b]133;B\\x07\",\n /** Mark command output start */\n commandStart: \"\\x1b]133;C\\x07\",\n /** Mark command output end (exit code defaults to 0 = success) */\n commandEnd: (exitCode?: number) => `\\x1b]133;D;${exitCode ?? 0}\\x07`,\n} as const\n","/**\n * Kitty keyboard protocol detection.\n *\n * Sends CSI ? u and parses the response to determine whether the terminal\n * supports the Kitty keyboard protocol and which flags it reports.\n */\n\nimport { queryKittyKeyboard } from \"./output\"\n\nexport interface KittyDetectResult {\n /** Whether the terminal responded to the Kitty protocol query */\n supported: boolean\n /** Bitfield of KittyFlags the terminal reported supporting (0 if unsupported) */\n flags: number\n /** Any non-response data that was read during detection (regular input that arrived) */\n buffered?: string\n}\n\n/** Regex to match a Kitty keyboard query response: CSI ? <flags> u */\nconst KITTY_RESPONSE_RE = /\\x1b\\[\\?(\\d+)u/\n\n/**\n * Detect Kitty keyboard protocol support.\n *\n * Sends CSI ? u to the terminal and waits for a response.\n * Supported terminals respond with CSI ? flags u.\n * Unsupported terminals either ignore the query or echo it.\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin (should resolve with data or null on timeout)\n * @param timeoutMs How long to wait for response (default: 200ms)\n */\nexport async function detectKittySupport(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<KittyDetectResult> {\n write(queryKittyKeyboard())\n\n const data = await read(timeoutMs)\n if (data == null) {\n return { supported: false, flags: 0 }\n }\n\n const match = KITTY_RESPONSE_RE.exec(data)\n if (!match) {\n return { supported: false, flags: 0, buffered: data }\n }\n\n const flags = parseInt(match[1]!, 10)\n // Anything outside the matched response is buffered input\n const before = data.slice(0, match.index)\n const after = data.slice(match.index + match[0].length)\n const buffered = before + after\n return { supported: true, flags, buffered: buffered || undefined }\n}\n\n/**\n * Detect Kitty support using real stdin/stdout.\n * Convenience wrapper around detectKittySupport.\n */\nexport async function detectKittyFromStdio(\n stdout: { write: (s: string) => boolean | void },\n stdin: NodeJS.ReadStream,\n timeoutMs = 200,\n): Promise<KittyDetectResult> {\n const wasRaw = stdin.isRaw\n if (!wasRaw) stdin.setRawMode(true)\n\n try {\n const write = (s: string) => {\n stdout.write(s)\n }\n\n const read = (ms: number): Promise<string | null> =>\n new Promise((resolve) => {\n const timer = setTimeout(() => {\n stdin.removeListener(\"data\", onData)\n resolve(null)\n }, ms)\n\n function onData(chunk: Buffer) {\n clearTimeout(timer)\n stdin.removeListener(\"data\", onData)\n resolve(chunk.toString())\n }\n\n stdin.on(\"data\", onData)\n })\n\n return await detectKittySupport(write, read, timeoutMs)\n } finally {\n if (!wasRaw) stdin.setRawMode(false)\n }\n}\n","/**\n * Terminal Capability Test\n *\n * Renders labeled test patterns for each terminal feature.\n * Run in any terminal to visually verify what it supports.\n *\n * Usage:\n * import { runTermtest } from \"@silvery/ag-react\"\n * runTermtest() // all sections\n * runTermtest({ sections: [\"emoji\", \"colors\"] }) // specific sections\n *\n * Compare output across terminals to build/verify profiles.\n */\n\nimport { detectTerminalCaps } from \"./terminal-caps\"\n\nconst ESC = \"\\x1b\"\nconst CSI = `${ESC}[`\nconst RESET = `${CSI}0m`\n\nfunction sgr(...codes: (string | number)[]): string {\n return `${CSI}${codes.join(\";\")}m`\n}\n\nfunction sectionHeader(title: string): string {\n return `\\n${sgr(1)}═══ ${title} ═══${RESET}\\n`\n}\n\nfunction row(label: string, content: string): string {\n return ` ${label.padEnd(24)} ${content}${RESET}`\n}\n\n/** Available test sections */\nexport const TERMTEST_SECTIONS = [\n \"sgr\",\n \"underline\",\n \"colors\",\n \"256\",\n \"truecolor\",\n \"unicode\",\n \"emoji\",\n \"borders\",\n \"inverse\",\n \"profile\",\n] as const\n\nexport type TermtestSection = (typeof TERMTEST_SECTIONS)[number]\n\nexport interface TermtestOptions {\n /** Writable stream (defaults to process.stdout) */\n output?: { write(s: string): boolean }\n /** Show only these sections. Omit or empty = all sections. */\n sections?: TermtestSection[]\n}\n\n/**\n * Run the terminal capability test.\n * Pass section names to filter: `runTermtest({ sections: [\"emoji\"] })`\n */\nexport function runTermtest(options?: TermtestOptions): void {\n const w = options?.output ?? process.stdout\n const filter = options?.sections\n const show = (s: TermtestSection) => !filter || filter.length === 0 || filter.includes(s)\n\n const caps = detectTerminalCaps()\n\n w.write(`\\n${sgr(1)}Terminal Capability Test${RESET}\\n`)\n w.write(` Program: ${caps.program || \"(unknown)\"}\\n`)\n w.write(` TERM: ${caps.term || \"(unknown)\"}\\n`)\n w.write(` COLORTERM: ${process.env.COLORTERM || \"(unset)\"}\\n`)\n w.write(` Detected: color=${caps.colorLevel} dark=${caps.darkBackground} nerdfont=${caps.nerdfont}\\n`)\n w.write(` Underline: styles=${caps.underlineStyles} color=${caps.underlineColor}\\n`)\n w.write(` Emoji wide: ${caps.textEmojiWide}\\n`)\n\n if (show(\"sgr\")) {\n w.write(sectionHeader(\"SGR Text Attributes\"))\n w.write(row(\"Bold\", `${sgr(1)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Dim\", `${sgr(2)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Italic\", `${sgr(3)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Underline\", `${sgr(4)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Strikethrough\", `${sgr(9)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Inverse\", `${sgr(7)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Blink\", `${sgr(5)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Bold+Italic\", `${sgr(1, 3)}The quick brown fox${RESET}`) + \"\\n\")\n }\n\n if (show(\"underline\")) {\n w.write(sectionHeader(\"SGR 4:x Underline Styles (Terminal.app BREAKS here)\"))\n w.write(row(\"4:1 Single\", `${CSI}4:1mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"4:2 Double\", `${CSI}4:2mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"4:3 Curly\", `${CSI}4:3mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"4:4 Dotted\", `${CSI}4:4mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"4:5 Dashed\", `${CSI}4:5mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(\n row(\"After 4:x (clean?)\", `${CSI}4:3m${RESET}This text should be normal — if garbled, 4:x corrupted SGR state`) +\n \"\\n\",\n )\n\n w.write(sectionHeader(\"SGR 58 Underline Color (Terminal.app BREAKS here)\"))\n w.write(row(\"58;5;1 Red UL\", `${sgr(4)}${CSI}58;5;1mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"58;5;2 Green UL\", `${sgr(4)}${CSI}58;5;2mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"58;5;4 Blue UL\", `${sgr(4)}${CSI}58;5;4mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"58;2;R;G;B TC UL\", `${sgr(4)}${CSI}58;2;255;128;0mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(\n row(\n \"After SGR 58 (clean?)\",\n `${sgr(4)}${CSI}58;5;1m${RESET}This text should be normal — if garbled, 58 corrupted SGR state`,\n ) + \"\\n\",\n )\n }\n\n if (show(\"colors\")) {\n w.write(sectionHeader(\"ANSI 16 Colors\"))\n const colorNames = [\"Black\", \"Red\", \"Green\", \"Yellow\", \"Blue\", \"Magenta\", \"Cyan\", \"White\"]\n let fgLine = \" FG: \"\n for (let i = 0; i < 8; i++) fgLine += `${sgr(30 + i)} ${colorNames[i]}${RESET}`\n w.write(fgLine + \"\\n\")\n let fgBrLine = \" Br: \"\n for (let i = 0; i < 8; i++) fgBrLine += `${sgr(90 + i)} ${colorNames[i]}${RESET}`\n w.write(fgBrLine + \"\\n\")\n let bgLine = \" BG: \"\n for (let i = 0; i < 8; i++) bgLine += `${sgr(40 + i)} ${colorNames[i]} ${RESET}`\n w.write(bgLine + \"\\n\")\n let bgBrLine = \" BrBG:\"\n for (let i = 0; i < 8; i++) bgBrLine += `${sgr(100 + i)} ${colorNames[i]} ${RESET}`\n w.write(bgBrLine + \"\\n\")\n }\n\n if (show(\"256\")) {\n w.write(sectionHeader(\"256 Colors (indices 0-15, 16-231, 232-255)\"))\n let stdLine = \" 0-15: \"\n for (let i = 0; i < 16; i++) stdLine += `${CSI}48;5;${i}m ${RESET}`\n w.write(stdLine + \"\\n\")\n let cubeLine = \" Cube: \"\n for (let i = 16; i < 52; i++) cubeLine += `${CSI}48;5;${i}m ${RESET}`\n w.write(cubeLine + \"\\n\")\n let grayLine = \" Gray: \"\n for (let i = 232; i < 256; i++) grayLine += `${CSI}48;5;${i}m ${RESET}`\n w.write(grayLine + \"\\n\")\n }\n\n if (show(\"truecolor\")) {\n w.write(sectionHeader(\"Truecolor (38;2;R;G;B / 48;2;R;G;B)\"))\n let tcLine = \" Gradient: \"\n for (let i = 0; i < 40; i++) {\n const r = Math.round((i / 39) * 255)\n const g = Math.round(((39 - i) / 39) * 128)\n tcLine += `${CSI}48;2;${r};${g};80m ${RESET}`\n }\n w.write(tcLine + \"\\n\")\n w.write(row(\"If solid blocks →\", \"Truecolor NOT supported (256-color fallback)\") + \"\\n\")\n }\n\n if (show(\"unicode\")) {\n w.write(sectionHeader(\"Unicode, Emoji, PUA (Nerd Fonts)\"))\n w.write(row(\"ASCII\", \"Hello World! 0123456789\") + \"\\n\")\n w.write(row(\"Latin Extended\", \"àéîõü ñ ß ø å\") + \"\\n\")\n w.write(row(\"CJK\", \"你好世界 日本語 한국어\") + \"\\n\")\n w.write(row(\"Box Drawing\", \"┌─┬─┐ ╔═╦═╗ ╭─╮\") + \"\\n\")\n w.write(row(\"Block Elements\", \"▀▄█▌▐░▒▓\") + \"\\n\")\n w.write(row(\"Symbols\", \"● ○ ◉ ▶ ◀ ⚠ ✓ ✗ ⋮ §\") + \"\\n\")\n w.write(row(\"Emoji\", \"🎉 🚀 📁 📄 ⭐ 🔥 👍\") + \"\\n\")\n w.write(row(\"Nerd Font PUA\", \"\\uF114 folder \\uF0F6 file \\uE0B0 arrow \\uF013 gear\") + \"\\n\")\n w.write(row(\"If PUA = boxes →\", \"Nerd Fonts not installed\") + \"\\n\")\n }\n\n if (show(\"emoji\")) {\n // Each test line places a character then fills to exactly 10 visible columns.\n // If the _'s don't align with the ruler, the terminal's character width\n // disagrees with the expected width (shown in parentheses).\n w.write(sectionHeader(\"Emoji Width Alignment (_'s should align with ruler)\"))\n w.write(\" Ruler: |1234567890|\\n\")\n w.write(\" ASCII 'A': |A_________| (w=1)\\n\")\n w.write(\" CJK '你': |你________| (w=2)\\n\")\n w.write(\" Flag 🇨🇦: |🇨🇦________| (w=2)\\n\")\n w.write(\" Flag 🇺🇸: |🇺🇸________| (w=2)\\n\")\n w.write(\" Emoji 📁: |📁________| (w=2)\\n\")\n w.write(\" Emoji 📄: |📄________| (w=2)\\n\")\n w.write(\" Emoji 📋: |📋________| (w=2)\\n\")\n w.write(\" Emoji ⚠️: |⚠️________| (w=2)\\n\")\n w.write(\" Text ⚠: |⚠_________| (w=1 text, 2 emoji)\\n\")\n w.write(\" Text ⭐: |⭐________| (w=1 text, 2 emoji)\\n\")\n w.write(\" Text ☑: |☑_________| (w=1 text, 2 emoji)\\n\")\n w.write(\" Emoji 🏠: |🏠________| (w=2)\\n\")\n w.write(\" Emoji 👓: |👓________| (w=2)\\n\")\n w.write(\" ZWJ 👨🏻💻: |👨🏻💻________| (w=2)\\n\")\n w.write(\" Arrow →: |→_________| (w=1)\\n\")\n w.write(\" Arrow ▸: |▸_________| (w=1)\\n\")\n w.write(\" Circle ○: |○_________| (w=1)\\n\")\n w.write(\" Square □: |□_________| (w=1)\\n\")\n w.write(\" Check ✓: |✓_________| (w=1)\\n\")\n }\n\n if (show(\"borders\")) {\n w.write(sectionHeader(\"Box Drawing Borders\"))\n w.write(\" ┌──────────┐ ╔══════════╗ ╭──────────╮\\n\")\n w.write(\" │ single │ ║ double ║ │ round │\\n\")\n w.write(\" └──────────┘ ╚══════════╝ ╰──────────╯\\n\")\n }\n\n if (show(\"inverse\")) {\n w.write(sectionHeader(\"Inverse + Background (potential artifact source)\"))\n w.write(row(\"Red FG + Inverse\", `${sgr(31, 7)}This should have red background${RESET}`) + \"\\n\")\n w.write(row(\"Cyan BG + White FG\", `${sgr(46, 37)}Cyan background, white text${RESET}`) + \"\\n\")\n w.write(row(\"Black BG + White FG\", `${sgr(40, 97)}Black bg, bright white text${RESET}`) + \"\\n\")\n w.write(row(\"White BG + Black FG\", `${sgr(107, 30)}White bg, black text${RESET}`) + \"\\n\")\n\n w.write(sectionHeader(\"Reset Sanity Check\"))\n w.write(\" This line should be completely normal with no formatting artifacts.\\n\")\n w.write(\" If you see colors, underlines, or other styling above, the terminal\\n\")\n w.write(\" failed to process an SGR reset (\\\\x1b[0m) correctly.\\n\")\n }\n\n if (show(\"profile\")) {\n w.write(sectionHeader(\"Detected Terminal Profile\"))\n const entries = Object.entries(caps) as [string, unknown][]\n for (const [key, value] of entries) {\n const indicator = value === true ? \"✓\" : value === false ? \"✗\" : String(value)\n w.write(` ${key.padEnd(20)} ${indicator}\\n`)\n }\n }\n\n w.write(\"\\n\")\n}\n","/**\n * Measurer composition layer.\n *\n * Creates term-scoped measurers and pipeline configs.\n * Bridges ansi Term with silvery measurement capabilities.\n */\n\nimport type { Term, TerminalCaps } from \"./ansi/index\"\nimport { createWidthMeasurer, type Measurer } from \"./unicode\"\nimport { createOutputPhase } from \"./pipeline/output-phase\"\nimport type { PipelineConfig } from \"./pipeline\"\n\nexport type { Measurer } from \"./unicode\"\n\n/**\n * Term extended with measurement capabilities.\n */\nexport interface MeasuredTerm extends Term, Measurer {}\n\n/**\n * Extend a Term with measurement capabilities.\n *\n * Creates a width measurer from the term's caps and adds measurement\n * methods (displayWidth, graphemeWidth, wrapText, etc.) to the term.\n */\nexport function withMeasurer(term: Term): MeasuredTerm {\n const caps = term.caps\n const measurer = createWidthMeasurer(\n caps ? { textEmojiWide: caps.textEmojiWide, textSizingEnabled: caps.textSizingSupported } : {},\n )\n\n return Object.create(term, {\n textEmojiWide: { get: () => measurer.textEmojiWide, enumerable: true },\n textSizingEnabled: { get: () => measurer.textSizingEnabled, enumerable: true },\n displayWidth: { value: measurer.displayWidth.bind(measurer), enumerable: true },\n displayWidthAnsi: { value: measurer.displayWidthAnsi.bind(measurer), enumerable: true },\n graphemeWidth: { value: measurer.graphemeWidth.bind(measurer), enumerable: true },\n wrapText: { value: measurer.wrapText.bind(measurer), enumerable: true },\n sliceByWidth: { value: measurer.sliceByWidth.bind(measurer), enumerable: true },\n sliceByWidthFromEnd: { value: measurer.sliceByWidthFromEnd.bind(measurer), enumerable: true },\n }) as MeasuredTerm\n}\n\n/**\n * Create a pipeline configuration from caps and/or measurer.\n *\n * This is the single factory for PipelineConfig -- use it instead of\n * manually constructing { measurer, outputPhaseFn }.\n *\n * @param options.caps - Terminal capabilities (for output phase SGR generation)\n * @param options.measurer - Explicit measurer (if omitted, created from caps)\n */\nexport function createPipeline(\n options: {\n caps?: TerminalCaps\n measurer?: Measurer\n } = {},\n): PipelineConfig {\n const { caps, measurer: explicitMeasurer } = options\n const measurer =\n explicitMeasurer ??\n createWidthMeasurer(caps ? { textEmojiWide: caps.textEmojiWide, textSizingEnabled: caps.textSizingSupported } : {})\n const outputPhaseFn = createOutputPhase(\n caps\n ? {\n underlineStyles: caps.underlineStyles,\n underlineColor: caps.underlineColor,\n colorLevel: caps.colorLevel,\n }\n : {},\n measurer,\n )\n return { measurer, outputPhaseFn }\n}\n","/**\n * CSI 6n Cursor Position Query\n *\n * Queries the terminal for the current cursor position using the standard\n * Device Status Report (DSR) mechanism.\n *\n * Protocol:\n * - Query: CSI 6 n (\\x1b[6n)\n * - Response: CSI {row} ; {col} R (\\x1b[{row};{col}R)\n *\n * Row and column are 1-indexed in the protocol response.\n *\n * Supported by: virtually all terminals (VT100+)\n */\n\n/** Regex to match a CPR response: CSI row ; col R */\nconst CPR_RESPONSE_RE = /\\x1b\\[(\\d+);(\\d+)R/\n\n/**\n * Query the terminal cursor position.\n *\n * Sends CSI 6n and parses the CPR response.\n * Returns 1-indexed row and column.\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin (resolves with data or null on timeout)\n * @param timeoutMs How long to wait for response (default: 200ms)\n */\nexport async function queryCursorPosition(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<{ row: number; col: number } | null> {\n write(\"\\x1b[6n\")\n\n const data = await read(timeoutMs)\n if (data == null) return null\n\n const match = CPR_RESPONSE_RE.exec(data)\n if (!match) return null\n\n return {\n row: parseInt(match[1]!, 10),\n col: parseInt(match[2]!, 10),\n }\n}\n\n/**\n * Query cursor position using real stdin/stdout.\n * Convenience wrapper around queryCursorPosition.\n */\nexport async function queryCursorFromStdio(\n stdout: { write: (s: string) => boolean | void },\n stdin: NodeJS.ReadStream,\n timeoutMs = 200,\n): Promise<{ row: number; col: number } | null> {\n const wasRaw = stdin.isRaw\n if (!wasRaw) stdin.setRawMode(true)\n\n try {\n const write = (s: string) => {\n stdout.write(s)\n }\n\n const read = (ms: number): Promise<string | null> =>\n new Promise((resolve) => {\n const timer = setTimeout(() => {\n stdin.removeListener(\"data\", onData)\n resolve(null)\n }, ms)\n\n function onData(chunk: Buffer) {\n clearTimeout(timer)\n stdin.removeListener(\"data\", onData)\n resolve(chunk.toString())\n }\n\n stdin.on(\"data\", onData)\n })\n\n return await queryCursorPosition(write, read, timeoutMs)\n } finally {\n if (!wasRaw) stdin.setRawMode(false)\n }\n}\n","/**\n * OSC 10/11/12 Terminal Color Queries — re-exported from @silvery/ansi (canonical location).\n */\n\nexport {\n queryForegroundColor,\n queryBackgroundColor,\n queryCursorColor,\n setForegroundColor,\n setBackgroundColor,\n setCursorColor,\n resetForegroundColor,\n resetBackgroundColor,\n resetCursorColor,\n detectColorScheme,\n} from \"@silvery/ansi\"\n","/**\n * Device Attributes (DA1/DA2/DA3) + XTVERSION Queries\n *\n * Provides functions to query terminal identity and capabilities using\n * the standard VT device attribute escape sequences.\n *\n * Protocols:\n *\n * DA1 (Primary Device Attributes):\n * Query: CSI c (\\x1b[c)\n * Response: CSI ? Ps ; Ps ; ... c\n *\n * DA2 (Secondary Device Attributes):\n * Query: CSI > c (\\x1b[>c)\n * Response: CSI > Pt ; Pv ; Pc c\n * Where Pt=terminal type, Pv=firmware version, Pc=ROM cartridge id\n *\n * DA3 (Tertiary Device Attributes):\n * Query: CSI = c (\\x1b[=c)\n * Response: DCS ! | hex-encoded-id ST (\\x1bP!|{hex}\\x1b\\\\)\n *\n * XTVERSION (Terminal Name + Version):\n * Query: CSI > 0 q (\\x1b[>0q)\n * Response: DCS > | name(version) ST (\\x1bP>|{text}\\x1b\\\\)\n *\n * Supported by: xterm, Ghostty, Kitty, WezTerm, foot, VTE-based terminals\n */\n\n/** Regex for DA1 response: CSI ? params c */\nconst DA1_RESPONSE_RE = /\\x1b\\[\\?([\\d;]+)c/\n\n/** Regex for DA2 response: CSI > params c */\nconst DA2_RESPONSE_RE = /\\x1b\\[>([\\d;]+)c/\n\n/** Regex for DA3 response: DCS ! | hex ST */\nconst DA3_RESPONSE_RE = /\\x1bP!\\|([0-9a-fA-F]*)\\x1b\\\\/\n\n/** Regex for XTVERSION response: DCS > | text ST */\nconst XTVERSION_RESPONSE_RE = /\\x1bP>\\|([^\\x1b]*)\\x1b\\\\/\n\n// ============================================================================\n// DA1 — Primary Device Attributes\n// ============================================================================\n\n/**\n * Query primary device attributes (DA1).\n *\n * Returns the list of attribute parameters the terminal reports.\n * Common params: 1=132-cols, 4=sixel, 6=selective-erase, 22=ANSI-color\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param timeoutMs How long to wait for response (default: 200ms)\n */\nexport async function queryPrimaryDA(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<{ params: number[] } | null> {\n write(\"\\x1b[c\")\n\n const data = await read(timeoutMs)\n if (data == null) return null\n\n const match = DA1_RESPONSE_RE.exec(data)\n if (!match) return null\n\n const params = match[1]!.split(\";\").map((s) => parseInt(s, 10))\n return { params }\n}\n\n// ============================================================================\n// DA2 — Secondary Device Attributes\n// ============================================================================\n\n/**\n * Query secondary device attributes (DA2).\n *\n * Returns terminal type, firmware version, and ROM cartridge id.\n * Common type values: 0=VT100, 1=VT220, 41=xterm, 65=VT500\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param timeoutMs How long to wait for response (default: 200ms)\n */\nexport async function querySecondaryDA(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<{ type: number; version: number; id: number } | null> {\n write(\"\\x1b[>c\")\n\n const data = await read(timeoutMs)\n if (data == null) return null\n\n const match = DA2_RESPONSE_RE.exec(data)\n if (!match) return null\n\n const parts = match[1]!.split(\";\")\n if (parts.length < 3) return null\n\n return {\n type: parseInt(parts[0]!, 10),\n version: parseInt(parts[1]!, 10),\n id: parseInt(parts[2]!, 10),\n }\n}\n\n// ============================================================================\n// DA3 — Tertiary Device Attributes\n// ============================================================================\n\n/**\n * Query tertiary device attributes (DA3).\n *\n * Returns a hex-encoded unit ID string. Decode with Buffer.from(hex, 'hex').\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param timeoutMs How long to wait for response (default: 200ms)\n */\nexport async function queryTertiaryDA(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<string | null> {\n write(\"\\x1b[=c\")\n\n const data = await read(timeoutMs)\n if (data == null) return null\n\n const match = DA3_RESPONSE_RE.exec(data)\n if (!match) return null\n\n return match[1]!\n}\n\n// ============================================================================\n// XTVERSION — Terminal Name + Version\n// ============================================================================\n\n/**\n * Query the terminal name and version via XTVERSION.\n *\n * Returns the version string as reported by the terminal, e.g.:\n * - \"xterm(388)\"\n * - \"tmux 3.4\"\n * - \"WezTerm 20230712-072601-f4abf8fd\"\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param timeoutMs How long to wait for response (default: 200ms)\n */\nexport async function queryTerminalVersion(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<string | null> {\n write(\"\\x1b[>0q\")\n\n const data = await read(timeoutMs)\n if (data == null) return null\n\n const match = XTVERSION_RESPONSE_RE.exec(data)\n if (!match) return null\n\n return match[1]!\n}\n\n// ============================================================================\n// Combined Query\n// ============================================================================\n\n/** Combined device attributes result. */\nexport interface DeviceAttributes {\n da1: { params: number[] } | null\n da2: { type: number; version: number; id: number } | null\n version: string | null\n}\n\n/**\n * Query all device attributes: DA1, DA2, and XTVERSION.\n *\n * Convenience wrapper that queries all three sequentially.\n * DA3 is omitted from the combined query as it's rarely needed.\n *\n * @param stdout Writable stream (e.g., process.stdout)\n * @param stdin Readable stream (e.g., process.stdin)\n * @param timeoutMs Per-query timeout (default: 200ms)\n */\nexport async function queryDeviceAttributes(\n stdout: { write: (s: string) => boolean | void },\n stdin: NodeJS.ReadStream,\n timeoutMs = 200,\n): Promise<DeviceAttributes> {\n const wasRaw = stdin.isRaw\n if (!wasRaw) stdin.setRawMode(true)\n\n try {\n const write = (s: string) => {\n stdout.write(s)\n }\n\n const read = (ms: number): Promise<string | null> =>\n new Promise((resolve) => {\n const timer = setTimeout(() => {\n stdin.removeListener(\"data\", onData)\n resolve(null)\n }, ms)\n\n function onData(chunk: Buffer) {\n clearTimeout(timer)\n stdin.removeListener(\"data\", onData)\n resolve(chunk.toString())\n }\n\n stdin.on(\"data\", onData)\n })\n\n const da1 = await queryPrimaryDA(write, read, timeoutMs)\n const da2 = await querySecondaryDA(write, read, timeoutMs)\n const version = await queryTerminalVersion(write, read, timeoutMs)\n\n return { da1, da2, version }\n } finally {\n if (!wasRaw) stdin.setRawMode(false)\n }\n}\n","/**\n * DECRQM — DEC Private Mode Query\n *\n * Queries the terminal for the state of DEC private modes.\n *\n * Protocol:\n * - Query: CSI ? {mode} $ p\n * - Response: CSI ? {mode} ; {Ps} $ y\n *\n * Where Ps is:\n * 1 = set (mode is enabled)\n * 2 = reset (mode is disabled)\n * 0 = not recognized (unknown mode)\n * 3 = permanently set\n * 4 = permanently reset\n *\n * We normalize 3→\"set\" and 4→\"reset\" for simplicity.\n *\n * Supported by: xterm, Ghostty, Kitty, WezTerm, foot, VTE-based terminals\n */\n\n/** Regex for DECRPM response: CSI ? mode ; Ps $ y */\nconst DECRPM_RESPONSE_RE = /\\x1b\\[\\?(\\d+);(\\d+)\\$y/\n\n/** Well-known DEC private mode constants. */\nexport const DecMode = {\n /** DEC cursor visible (DECTCEM) */\n CURSOR_VISIBLE: 25,\n /** Alternate screen buffer (DECSET 1049) */\n ALT_SCREEN: 1049,\n /** Normal mouse tracking (X10) */\n MOUSE_TRACKING: 1000,\n /** Bracketed paste mode */\n BRACKETED_PASTE: 2004,\n /** Synchronized output */\n SYNC_OUTPUT: 2026,\n /** Focus reporting */\n FOCUS_REPORTING: 1004,\n} as const\n\ntype ModeState = \"set\" | \"reset\" | \"unknown\"\n\n/**\n * Query the state of a single DEC private mode.\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param mode DEC private mode number (e.g., DecMode.ALT_SCREEN)\n * @param timeoutMs How long to wait for response (default: 200ms)\n * @returns \"set\", \"reset\", or \"unknown\"\n */\nexport async function queryMode(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n mode: number,\n timeoutMs = 200,\n): Promise<ModeState> {\n write(`\\x1b[?${mode}$p`)\n\n const data = await read(timeoutMs)\n if (data == null) return \"unknown\"\n\n const match = DECRPM_RESPONSE_RE.exec(data)\n if (!match) return \"unknown\"\n\n const reportedMode = parseInt(match[1]!, 10)\n if (reportedMode !== mode) return \"unknown\"\n\n const ps = parseInt(match[2]!, 10)\n switch (ps) {\n case 1:\n case 3:\n return \"set\"\n case 2:\n case 4:\n return \"reset\"\n default:\n return \"unknown\"\n }\n}\n\n/**\n * Query the state of multiple DEC private modes.\n *\n * Queries each mode sequentially and returns a Map of results.\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param modes Array of DEC private mode numbers\n * @param timeoutMs Per-query timeout (default: 200ms)\n */\nexport async function queryModes(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n modes: number[],\n timeoutMs = 200,\n): Promise<Map<number, ModeState>> {\n const results = new Map<number, ModeState>()\n\n for (const mode of modes) {\n const state = await queryMode(write, read, mode, timeoutMs)\n results.set(mode, state)\n }\n\n return results\n}\n","/**\n * CSI 14t/18t — Terminal Pixel and Text Area Size Queries\n *\n * Queries the terminal for window dimensions in pixels and characters.\n *\n * Protocols:\n *\n * Text Area Pixels (CSI 14t):\n * Query: CSI 14 t\n * Response: CSI 4 ; height ; width t\n *\n * Text Area Size in Characters (CSI 18t):\n * Query: CSI 18 t\n * Response: CSI 8 ; rows ; cols t\n *\n * Cell size can be derived by dividing pixel dimensions by character dimensions.\n *\n * Supported by: xterm, Ghostty, Kitty, WezTerm, foot, iTerm2\n */\n\n/** Regex for CSI 4 ; height ; width t (pixel size response) */\nconst PIXEL_RESPONSE_RE = /\\x1b\\[4;(\\d+);(\\d+)t/\n\n/** Regex for CSI 8 ; rows ; cols t (text area size response) */\nconst TEXT_AREA_RESPONSE_RE = /\\x1b\\[8;(\\d+);(\\d+)t/\n\n// ============================================================================\n// Pixel Size Query\n// ============================================================================\n\n/**\n * Query the terminal text area size in pixels.\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param timeoutMs How long to wait for response (default: 200ms)\n * @returns Width and height in pixels, or null on timeout/unsupported\n */\nexport async function queryTextAreaPixels(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<{ width: number; height: number } | null> {\n write(\"\\x1b[14t\")\n\n const data = await read(timeoutMs)\n if (data == null) return null\n\n const match = PIXEL_RESPONSE_RE.exec(data)\n if (!match) return null\n\n return {\n height: parseInt(match[1]!, 10),\n width: parseInt(match[2]!, 10),\n }\n}\n\n// ============================================================================\n// Text Area Size Query (characters)\n// ============================================================================\n\n/**\n * Query the terminal text area size in characters (rows x columns).\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param timeoutMs How long to wait for response (default: 200ms)\n * @returns Rows and columns, or null on timeout/unsupported\n */\nexport async function queryTextAreaSize(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<{ cols: number; rows: number } | null> {\n write(\"\\x1b[18t\")\n\n const data = await read(timeoutMs)\n if (data == null) return null\n\n const match = TEXT_AREA_RESPONSE_RE.exec(data)\n if (!match) return null\n\n return {\n rows: parseInt(match[1]!, 10),\n cols: parseInt(match[2]!, 10),\n }\n}\n\n// ============================================================================\n// Cell Size (derived)\n// ============================================================================\n\n/**\n * Query the terminal cell size in pixels by querying both pixel\n * dimensions and character dimensions, then dividing.\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param timeoutMs Per-query timeout (default: 200ms)\n * @returns Cell width and height in pixels, or null if either query fails\n */\nexport async function queryCellSize(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<{ width: number; height: number } | null> {\n const pixels = await queryTextAreaPixels(write, read, timeoutMs)\n if (pixels == null) return null\n\n const size = await queryTextAreaSize(write, read, timeoutMs)\n if (size == null) return null\n\n if (size.cols === 0 || size.rows === 0) return null\n\n return {\n width: pixels.width / size.cols,\n height: pixels.height / size.rows,\n }\n}\n","/**\n * Canvas Render Adapter\n *\n * Two modes:\n * - **monospace** (default): Layout in cell units (cols x rows), convert to pixels at draw time.\n * - **proportional** (monospace: false): Layout in pixel units, draw at pixel coordinates directly.\n * Uses canvas measureText() for real font metrics. Enables proportional fonts (Inter, SF Pro, etc.)\n */\n\nimport type {\n BorderChars,\n RenderAdapter,\n RenderBuffer,\n RenderStyle,\n TextMeasureResult,\n TextMeasureStyle,\n TextMeasurer,\n} from \"../render-adapter\"\nimport type { Measurer } from \"../unicode\"\nimport { wrapTextWithMeasurer, stripAnsi } from \"../unicode\"\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nexport interface CanvasAdapterConfig {\n /** Font size in pixels (default: 14) */\n fontSize?: number\n /** Font family (default: 'monospace') */\n fontFamily?: string\n /** Line height multiplier (default: 1.2) */\n lineHeight?: number\n /** Background color (default: '#1e1e1e') */\n backgroundColor?: string\n /** Default foreground color (default: '#d4d4d4') */\n foregroundColor?: string\n /** Monospace mode (default: true). When false, uses proportional font measurement. */\n monospace?: boolean\n /** Device pixel ratio for sharp rendering on HiDPI displays (default: 1) */\n dpr?: number\n}\n\nconst DEFAULT_CONFIG: Required<CanvasAdapterConfig> = {\n fontSize: 14,\n fontFamily: \"monospace\",\n lineHeight: 1.2,\n backgroundColor: \"#1e1e1e\",\n foregroundColor: \"#d4d4d4\",\n monospace: true,\n dpr: 1,\n}\n\n// ============================================================================\n// Border Characters (same as terminal for consistency)\n// ============================================================================\n\nconst BORDER_CHARS: Record<string, BorderChars> = {\n single: {\n topLeft: \"┌\",\n topRight: \"┐\",\n bottomLeft: \"└\",\n bottomRight: \"┘\",\n horizontal: \"─\",\n vertical: \"│\",\n },\n double: {\n topLeft: \"╔\",\n topRight: \"╗\",\n bottomLeft: \"╚\",\n bottomRight: \"╝\",\n horizontal: \"═\",\n vertical: \"║\",\n },\n round: {\n topLeft: \"╭\",\n topRight: \"╮\",\n bottomLeft: \"╰\",\n bottomRight: \"╯\",\n horizontal: \"─\",\n vertical: \"│\",\n },\n bold: {\n topLeft: \"┏\",\n topRight: \"┓\",\n bottomLeft: \"┗\",\n bottomRight: \"┛\",\n horizontal: \"━\",\n vertical: \"┃\",\n },\n}\n\n// ============================================================================\n// Canvas Measurer\n// ============================================================================\n\n/** Create a scratch canvas 2D context for text measurement. */\nfunction createMeasureContext(\n fontSize: number,\n fontFamily: string,\n): CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D {\n // Prefer document.createElement(\"canvas\") over OffscreenCanvas for measurement —\n // OffscreenCanvas may not have access to web fonts loaded via <link> or @font-face.\n // A regular canvas element shares the document's font loading state.\n const canvas =\n typeof document !== \"undefined\"\n ? document.createElement(\"canvas\")\n : typeof OffscreenCanvas !== \"undefined\"\n ? new OffscreenCanvas(1, 1)\n : null\n if (!canvas) throw new Error(\"Canvas not available for text measurement\")\n const ctx = canvas.getContext(\"2d\")\n if (!ctx) throw new Error(\"Could not get 2d context for measurement\")\n ;(ctx as CanvasRenderingContext2D).font = `${fontSize}px ${fontFamily}`\n return ctx as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D\n}\n\nfunction createCanvasMeasurer(config: Required<CanvasAdapterConfig>): TextMeasurer {\n if (config.monospace) {\n return {\n measureText(text: string, _style?: TextMeasureStyle): TextMeasureResult {\n return { width: text.length, height: 1 }\n },\n getLineHeight(_style?: TextMeasureStyle): number {\n return 1\n },\n }\n }\n\n const lineHeightPx = config.fontSize * config.lineHeight\n const ctx = createMeasureContext(config.fontSize, config.fontFamily)\n\n return {\n measureText(text: string, _style?: TextMeasureStyle): TextMeasureResult {\n return { width: ctx.measureText(text).width, height: lineHeightPx }\n },\n getLineHeight(_style?: TextMeasureStyle): number {\n return lineHeightPx\n },\n }\n}\n\n// ============================================================================\n// Proportional (Pixel) Measurer\n// ============================================================================\n\n/** Font config for pixel measurement — only what's needed. */\nexport interface CanvasPixelMeasurerConfig {\n fontSize: number\n fontFamily: string\n lineHeight: number\n}\n\n/**\n * Create a pipeline Measurer that uses canvas font metrics for pixel-accurate widths.\n * All measurements return pixel values. Wrapping reuses silvery's wrapTextWithMeasurer.\n */\nexport function createCanvasPixelMeasurer(config: CanvasPixelMeasurerConfig): Measurer {\n const ctx = createMeasureContext(config.fontSize, config.fontFamily)\n const lineHeightPx = config.fontSize * config.lineHeight\n\n // Simple cache with full eviction at capacity (5000 entries, mostly single graphemes)\n const cache = new Map<string, number>()\n\n function pixelWidth(text: string): number {\n if (text.length === 0) return 0\n const cached = cache.get(text)\n if (cached !== undefined) return cached\n if (cache.size >= 5000) cache.clear()\n const w = ctx.measureText(text).width\n cache.set(text, w)\n return w\n }\n\n // Use Intl.Segmenter for proper grapheme iteration (emoji, CJK, combining marks)\n const segmenter = new Intl.Segmenter(undefined, { granularity: \"grapheme\" })\n\n function sliceGraphemes(text: string, maxWidth: number, fromEnd: boolean): string {\n const stripped = stripAnsi(text)\n if (pixelWidth(stripped) <= maxWidth) return text\n const graphemes = [...segmenter.segment(stripped)].map((s) => s.segment)\n let width = 0\n if (fromEnd) {\n for (let i = graphemes.length - 1; i >= 0; i--) {\n const gw = pixelWidth(graphemes[i]!)\n if (width + gw > maxWidth) return graphemes.slice(i + 1).join(\"\")\n width += gw\n }\n return stripped\n }\n for (let i = 0; i < graphemes.length; i++) {\n const gw = pixelWidth(graphemes[i]!)\n if (width + gw > maxWidth) return graphemes.slice(0, i).join(\"\")\n width += gw\n }\n return stripped\n }\n\n const measurer: Measurer = {\n textEmojiWide: false,\n textSizingEnabled: false,\n lineHeight: lineHeightPx,\n displayWidth(text: string): number {\n return pixelWidth(stripAnsi(text))\n },\n displayWidthAnsi(text: string): number {\n return pixelWidth(stripAnsi(text))\n },\n graphemeWidth(grapheme: string): number {\n return pixelWidth(grapheme)\n },\n wrapText(text: string, width: number, trim?: boolean, hard?: boolean): string[] {\n return wrapTextWithMeasurer(text, width, measurer, trim ?? false, hard ?? false)\n },\n sliceByWidth(text: string, maxWidth: number): string {\n return sliceGraphemes(text, maxWidth, false)\n },\n sliceByWidthFromEnd(text: string, maxWidth: number): string {\n return sliceGraphemes(text, maxWidth, true)\n },\n }\n\n return measurer\n}\n\n// ============================================================================\n// Color Conversion\n// ============================================================================\n\n// ANSI 256-color palette (standard 16 colors)\nconst ANSI_COLORS: Record<string, string> = {\n black: \"#000000\",\n red: \"#cd0000\",\n green: \"#00cd00\",\n yellow: \"#cdcd00\",\n blue: \"#0000ee\",\n magenta: \"#cd00cd\",\n cyan: \"#00cdcd\",\n white: \"#e5e5e5\",\n gray: \"#7f7f7f\",\n grey: \"#7f7f7f\",\n brightBlack: \"#7f7f7f\",\n brightRed: \"#ff0000\",\n brightGreen: \"#00ff00\",\n brightYellow: \"#ffff00\",\n brightBlue: \"#5c5cff\",\n brightMagenta: \"#ff00ff\",\n brightCyan: \"#00ffff\",\n brightWhite: \"#ffffff\",\n}\n\nfunction resolveColor(color: string | undefined, fallback: string): string {\n if (!color) return fallback\n\n // Already a CSS color (hex, rgb, etc.)\n if (color.startsWith(\"#\") || color.startsWith(\"rgb\")) {\n return color\n }\n\n // Named ANSI color\n const named = ANSI_COLORS[color.toLowerCase()]\n if (named) return named\n\n // Pass through (might be a CSS color name like 'cyan')\n return color\n}\n\n// ============================================================================\n// Canvas Render Buffer\n// ============================================================================\n\nexport class CanvasRenderBuffer implements RenderBuffer {\n readonly width: number\n readonly height: number\n readonly canvas: OffscreenCanvas | HTMLCanvasElement\n private ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D\n private config: Required<CanvasAdapterConfig>\n\n // Cell-to-pixel conversion factors\n private readonly charWidth: number\n private readonly cellHeight: number\n\n constructor(width: number, height: number, config: Required<CanvasAdapterConfig>) {\n this.width = width\n this.height = height\n this.config = config\n\n const dpr = config.dpr\n\n let cssWidth: number\n let cssHeight: number\n\n if (config.monospace) {\n // Monospace: layout in cell units, convert to pixels at draw time\n this.charWidth = config.fontSize * 0.6\n this.cellHeight = config.fontSize * config.lineHeight\n cssWidth = width * this.charWidth\n cssHeight = height * this.cellHeight\n } else {\n // Proportional: layout already in pixels, no conversion needed\n this.charWidth = 1\n this.cellHeight = 1\n cssWidth = width\n cssHeight = height\n }\n\n // Create canvas at native resolution (CSS pixels * DPR) for sharp HiDPI rendering.\n // The context transform scales all drawing by DPR, so coordinates stay in CSS pixels.\n const nativeWidth = Math.ceil(cssWidth * dpr)\n const nativeHeight = Math.ceil(cssHeight * dpr)\n\n if (typeof OffscreenCanvas !== \"undefined\") {\n this.canvas = new OffscreenCanvas(nativeWidth, nativeHeight)\n } else if (typeof document !== \"undefined\") {\n this.canvas = document.createElement(\"canvas\")\n this.canvas.width = nativeWidth\n this.canvas.height = nativeHeight\n } else {\n throw new Error(\"Canvas not available\")\n }\n\n const ctx = this.canvas.getContext(\"2d\")\n if (!ctx) throw new Error(\"Could not get 2d context\")\n this.ctx = ctx as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D\n\n // Scale context so all drawing uses CSS pixel coordinates\n if (dpr !== 1) {\n ;(this.ctx as CanvasRenderingContext2D).setTransform(dpr, 0, 0, dpr, 0, 0)\n }\n\n // Initialize with background\n this.ctx.fillStyle = config.backgroundColor\n this.ctx.fillRect(0, 0, cssWidth, cssHeight)\n }\n\n fillRect(x: number, y: number, width: number, height: number, style: RenderStyle): void {\n if (style.bg) {\n // Convert cell coordinates to pixel coordinates\n const px = x * this.charWidth\n const py = y * this.cellHeight\n const pw = width * this.charWidth\n const ph = height * this.cellHeight\n this.ctx.fillStyle = resolveColor(style.bg, this.config.backgroundColor)\n this.ctx.fillRect(px, py, pw, ph)\n }\n }\n\n drawText(x: number, y: number, text: string, style: RenderStyle): void {\n // Convert cell coordinates to pixel coordinates\n const px = x * this.charWidth\n\n const attrs = style.attrs ?? {}\n\n // Build font string\n const weight = attrs.bold ? \"bold\" : \"normal\"\n const fontStyle = attrs.italic ? \"italic\" : \"normal\"\n this.ctx.font = `${fontStyle} ${weight} ${this.config.fontSize}px ${this.config.fontFamily}`\n\n let py = y * this.cellHeight\n\n // Set colors\n this.ctx.fillStyle = resolveColor(style.fg, this.config.foregroundColor)\n\n if (!this.config.monospace) {\n // Match CSS line-height centering exactly.\n // CSS positions text by placing the alphabetic baseline at:\n // y + halfLeading + fontAscent\n // where halfLeading = (lineHeight - contentArea) / 2\n // and contentArea = fontBoundingBoxAscent + fontBoundingBoxDescent.\n // fontBoundingBox metrics are font-wide (not glyph-specific), matching CSS.\n const metrics = this.ctx.measureText(\"Hg\")\n const fontAscent = metrics.fontBoundingBoxAscent\n const fontDescent = metrics.fontBoundingBoxDescent\n const contentArea = fontAscent + fontDescent\n const lineHeightPx = this.config.fontSize * this.config.lineHeight\n const halfLeading = (lineHeightPx - contentArea) / 2\n this.ctx.textBaseline = \"alphabetic\"\n py += halfLeading + fontAscent\n } else {\n this.ctx.textBaseline = \"top\"\n }\n\n // Draw text\n this.ctx.fillText(text, px, py)\n\n // Handle underline\n if (attrs.underline) {\n this.drawUnderline(px, py, text, style)\n }\n\n // Handle strikethrough\n if (attrs.strikethrough) {\n const metrics = this.ctx.measureText(text)\n const textWidth = metrics.width\n const strikeY = py + this.config.fontSize * 0.5\n\n this.ctx.strokeStyle = resolveColor(style.fg, this.config.foregroundColor)\n this.ctx.lineWidth = 1\n this.ctx.beginPath()\n this.ctx.moveTo(px, strikeY)\n this.ctx.lineTo(px + textWidth, strikeY)\n this.ctx.stroke()\n }\n }\n\n /**\n * Draw underline decorations at pixel coordinates.\n * Note: px, py are already in pixel coordinates.\n */\n private drawUnderline(px: number, py: number, text: string, style: RenderStyle): void {\n const attrs = style.attrs ?? {}\n const metrics = this.ctx.measureText(text)\n const textWidth = metrics.width\n const underlineY = py + this.config.fontSize * 0.9\n\n const underlineColor = resolveColor(attrs.underlineColor ?? style.fg, this.config.foregroundColor)\n\n this.ctx.strokeStyle = underlineColor\n this.ctx.lineWidth = 1\n\n const underlineStyle = attrs.underlineStyle ?? \"single\"\n\n switch (underlineStyle) {\n case \"double\":\n // Two parallel lines\n this.ctx.beginPath()\n this.ctx.moveTo(px, underlineY - 1)\n this.ctx.lineTo(px + textWidth, underlineY - 1)\n this.ctx.moveTo(px, underlineY + 1)\n this.ctx.lineTo(px + textWidth, underlineY + 1)\n this.ctx.stroke()\n break\n\n case \"curly\":\n // Wavy line using bezier curves\n this.ctx.beginPath()\n this.ctx.moveTo(px, underlineY)\n const waveLength = 4\n const amplitude = 2\n for (let wx = 0; wx < textWidth; wx += waveLength * 2) {\n this.ctx.quadraticCurveTo(px + wx + waveLength / 2, underlineY - amplitude, px + wx + waveLength, underlineY)\n this.ctx.quadraticCurveTo(\n px + wx + (waveLength * 3) / 2,\n underlineY + amplitude,\n px + wx + waveLength * 2,\n underlineY,\n )\n }\n this.ctx.stroke()\n break\n\n case \"dotted\":\n this.ctx.setLineDash([2, 2])\n this.ctx.beginPath()\n this.ctx.moveTo(px, underlineY)\n this.ctx.lineTo(px + textWidth, underlineY)\n this.ctx.stroke()\n this.ctx.setLineDash([])\n break\n\n case \"dashed\":\n this.ctx.setLineDash([4, 2])\n this.ctx.beginPath()\n this.ctx.moveTo(px, underlineY)\n this.ctx.lineTo(px + textWidth, underlineY)\n this.ctx.stroke()\n this.ctx.setLineDash([])\n break\n\n default: // 'single'\n this.ctx.beginPath()\n this.ctx.moveTo(px, underlineY)\n this.ctx.lineTo(px + textWidth, underlineY)\n this.ctx.stroke()\n }\n }\n\n drawChar(x: number, y: number, char: string, style: RenderStyle): void {\n // For canvas, drawChar is essentially drawText for single chars\n this.drawText(x, y, char, style)\n }\n\n inBounds(x: number, y: number): boolean {\n return x >= 0 && x < this.width && y >= 0 && y < this.height\n }\n\n fillRoundedRect(\n x: number,\n y: number,\n width: number,\n height: number,\n radius: number,\n fill: string | undefined,\n stroke: string | undefined,\n lineWidth = 1,\n ): void {\n const px = x * this.charWidth\n const py = y * this.cellHeight\n const pw = width * this.charWidth\n const ph = height * this.cellHeight\n const r = Math.min(radius, pw / 2, ph / 2)\n\n this.ctx.beginPath()\n this.ctx.moveTo(px + r, py)\n this.ctx.lineTo(px + pw - r, py)\n this.ctx.quadraticCurveTo(px + pw, py, px + pw, py + r)\n this.ctx.lineTo(px + pw, py + ph - r)\n this.ctx.quadraticCurveTo(px + pw, py + ph, px + pw - r, py + ph)\n this.ctx.lineTo(px + r, py + ph)\n this.ctx.quadraticCurveTo(px, py + ph, px, py + ph - r)\n this.ctx.lineTo(px, py + r)\n this.ctx.quadraticCurveTo(px, py, px + r, py)\n this.ctx.closePath()\n\n if (fill) {\n this.ctx.fillStyle = fill\n this.ctx.fill()\n }\n if (stroke) {\n this.ctx.strokeStyle = stroke\n this.ctx.lineWidth = lineWidth\n this.ctx.stroke()\n }\n }\n}\n\n// ============================================================================\n// Canvas Adapter Factory\n// ============================================================================\n\nexport function createCanvasAdapter(config: CanvasAdapterConfig = {}): RenderAdapter {\n const cfg = { ...DEFAULT_CONFIG, ...config }\n const measurer = createCanvasMeasurer(cfg)\n\n return {\n name: \"canvas\",\n measurer,\n\n createBuffer(width: number, height: number): RenderBuffer {\n return new CanvasRenderBuffer(width, height, cfg)\n },\n\n flush(_buffer: RenderBuffer, _prevBuffer: RenderBuffer | null): void {\n // Canvas draws directly to the buffer during render.\n // The caller (renderToCanvas) copies the buffer to the visible canvas.\n },\n\n getBorderChars(style: string): BorderChars {\n return BORDER_CHARS[style] ?? BORDER_CHARS.single!\n },\n }\n}\n","/**\n * DOM Render Adapter\n *\n * Implements the RenderAdapter interface for browser DOM output.\n * Uses a line-based approach: one <div> per row, <span> elements for styled text runs.\n * The layout engine operates in cell units (columns x rows). This adapter\n * converts cell coordinates to pixel coordinates when rendering to the DOM,\n * using charWidth (fontSize * 0.6) and cellHeight (fontSize * lineHeight).\n *\n * Advantages over Canvas:\n * - Native text selection and copying\n * - Screen reader accessibility\n * - Browser font rendering (subpixel antialiasing, ligatures)\n * - CSS integration (theming, hover states)\n * - DevTools inspection\n *\n * Architecture follows xterm.js DOM renderer approach.\n * @see https://github.com/xtermjs/xterm.js/issues/3271\n */\n\nimport type {\n BorderChars,\n RenderAdapter,\n RenderBuffer,\n RenderStyle,\n TextMeasureResult,\n TextMeasureStyle,\n TextMeasurer,\n} from \"../render-adapter\"\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nexport interface DOMAdapterConfig {\n /** Font size in pixels (default: 14) */\n fontSize?: number\n /** Font family (default: 'monospace') */\n fontFamily?: string\n /** Line height multiplier (default: 1.2) */\n lineHeight?: number\n /** Background color (default: '#1e1e1e') */\n backgroundColor?: string\n /** Default foreground color (default: '#d4d4d4') */\n foregroundColor?: string\n /** CSS class prefix (default: 'silvery') */\n classPrefix?: string\n}\n\nconst DEFAULT_CONFIG: Required<DOMAdapterConfig> = {\n fontSize: 14,\n fontFamily: \"monospace\",\n lineHeight: 1.2,\n backgroundColor: \"#1e1e1e\",\n foregroundColor: \"#d4d4d4\",\n classPrefix: \"silvery\",\n}\n\n// ============================================================================\n// Border Characters (same as terminal/canvas for consistency)\n// ============================================================================\n\nconst BORDER_CHARS: Record<string, BorderChars> = {\n single: {\n topLeft: \"┌\",\n topRight: \"┐\",\n bottomLeft: \"└\",\n bottomRight: \"┘\",\n horizontal: \"─\",\n vertical: \"│\",\n },\n double: {\n topLeft: \"╔\",\n topRight: \"╗\",\n bottomLeft: \"╚\",\n bottomRight: \"╝\",\n horizontal: \"═\",\n vertical: \"║\",\n },\n round: {\n topLeft: \"╭\",\n topRight: \"╮\",\n bottomLeft: \"╰\",\n bottomRight: \"╯\",\n horizontal: \"─\",\n vertical: \"│\",\n },\n bold: {\n topLeft: \"┏\",\n topRight: \"┓\",\n bottomLeft: \"┗\",\n bottomRight: \"┛\",\n horizontal: \"━\",\n vertical: \"┃\",\n },\n}\n\n// ============================================================================\n// Color Conversion\n// ============================================================================\n\nconst ANSI_COLORS: Record<string, string> = {\n black: \"#000000\",\n red: \"#cd0000\",\n green: \"#00cd00\",\n yellow: \"#cdcd00\",\n blue: \"#0000ee\",\n magenta: \"#cd00cd\",\n cyan: \"#00cdcd\",\n white: \"#e5e5e5\",\n gray: \"#7f7f7f\",\n grey: \"#7f7f7f\",\n brightblack: \"#7f7f7f\",\n brightred: \"#ff0000\",\n brightgreen: \"#00ff00\",\n brightyellow: \"#ffff00\",\n brightblue: \"#5c5cff\",\n brightmagenta: \"#ff00ff\",\n brightcyan: \"#00ffff\",\n brightwhite: \"#ffffff\",\n}\n\nfunction resolveColor(color: string | undefined, fallback: string): string {\n if (!color) return fallback\n if (color.startsWith(\"#\") || color.startsWith(\"rgb\")) return color\n const named = ANSI_COLORS[color.toLowerCase()]\n return named ?? color\n}\n\n// ============================================================================\n// DOM Measurer\n// ============================================================================\n\nfunction createDOMMeasurer(_config: Required<DOMAdapterConfig>): TextMeasurer {\n // The layout engine operates in cell units (columns x rows), matching the\n // terminal convention. For monospace fonts, text width = character count\n // and line height = 1 row.\n return {\n measureText(text: string, _style?: TextMeasureStyle): TextMeasureResult {\n // For monospace fonts, width is simply the character count (one cell per char)\n return {\n width: text.length,\n height: 1,\n }\n },\n\n getLineHeight(_style?: TextMeasureStyle): number {\n return 1\n },\n }\n}\n\n// ============================================================================\n// Styled Text Run\n// ============================================================================\n\ninterface TextRun {\n text: string\n style: RenderStyle\n x: number\n}\n\n// ============================================================================\n// DOM Render Buffer\n// ============================================================================\n\nexport class DOMRenderBuffer implements RenderBuffer {\n readonly width: number\n readonly height: number\n\n private config: Required<DOMAdapterConfig>\n private lines: Map<number, TextRun[]>\n private backgrounds: Map<string, { x: number; y: number; w: number; h: number; color: string }>\n\n // Cell-to-pixel conversion factors\n private readonly charWidth: number\n private readonly cellHeight: number\n\n // Container element (set when flushing)\n private container: HTMLElement | null = null\n\n constructor(width: number, height: number, config: Required<DOMAdapterConfig>) {\n this.width = width\n this.height = height\n this.config = config\n this.lines = new Map()\n this.backgrounds = new Map()\n\n // Compute cell dimensions for coordinate conversion.\n // Width/height are in cell units (cols/rows); rendering converts to pixels.\n this.charWidth = config.fontSize * 0.6\n this.cellHeight = config.fontSize * config.lineHeight\n }\n\n /**\n * Set the container element for rendering.\n */\n setContainer(container: HTMLElement): void {\n this.container = container\n }\n\n /**\n * Get the container element.\n */\n getContainer(): HTMLElement | null {\n return this.container\n }\n\n fillRect(x: number, y: number, width: number, height: number, style: RenderStyle): void {\n if (style.bg) {\n const key = `${x},${y},${width},${height}`\n this.backgrounds.set(key, {\n x,\n y,\n w: width,\n h: height,\n color: resolveColor(style.bg, this.config.backgroundColor),\n })\n }\n }\n\n drawText(x: number, y: number, text: string, style: RenderStyle): void {\n if (!this.lines.has(y)) {\n this.lines.set(y, [])\n }\n this.lines.get(y)!.push({ text, style, x })\n }\n\n drawChar(x: number, y: number, char: string, style: RenderStyle): void {\n this.drawText(x, y, char, style)\n }\n\n inBounds(x: number, y: number): boolean {\n return x >= 0 && x < this.width && y >= 0 && y < this.height\n }\n\n /**\n * Render the buffer to the container element.\n * Coordinates in the buffer are in cell units (cols/rows).\n * This method converts them to pixel coordinates for DOM positioning.\n */\n render(): void {\n if (!this.container) {\n throw new Error(\"DOMRenderBuffer: No container set. Call setContainer() first.\")\n }\n\n const container = this.container\n const cw = this.charWidth\n const ch = this.cellHeight\n\n // Container dimensions in pixels (convert cell units back to pixels)\n const containerWidthPx = this.width * cw\n const containerHeightPx = this.height * ch\n\n // Clear previous content\n container.innerHTML = \"\"\n\n // Set container styles\n container.style.cssText = `\n\t\t\tposition: relative;\n\t\t\tfont-family: ${this.config.fontFamily};\n\t\t\tfont-size: ${this.config.fontSize}px;\n\t\t\tline-height: ${this.config.lineHeight};\n\t\t\tbackground-color: ${this.config.backgroundColor};\n\t\t\tcolor: ${this.config.foregroundColor};\n\t\t\twhite-space: pre;\n\t\t\toverflow: hidden;\n\t\t\twidth: ${containerWidthPx}px;\n\t\t\theight: ${containerHeightPx}px;\n\t\t`\n\n // Render background rectangles (convert cell coords to pixels)\n for (const bg of this.backgrounds.values()) {\n const bgDiv = document.createElement(\"div\")\n bgDiv.className = `${this.config.classPrefix}-bg`\n bgDiv.style.cssText = `\n\t\t\t\tposition: absolute;\n\t\t\t\tleft: ${bg.x * cw}px;\n\t\t\t\ttop: ${bg.y * ch}px;\n\t\t\t\twidth: ${bg.w * cw}px;\n\t\t\t\theight: ${bg.h * ch}px;\n\t\t\t\tbackground-color: ${bg.color};\n\t\t\t`\n container.appendChild(bgDiv)\n }\n\n // Render text lines (convert cell coords to pixels)\n const sortedLines = Array.from(this.lines.entries()).sort((a, b) => a[0] - b[0])\n\n for (const [y, runs] of sortedLines) {\n const lineDiv = document.createElement(\"div\")\n lineDiv.className = `${this.config.classPrefix}-line`\n lineDiv.style.cssText = `\n\t\t\t\tposition: absolute;\n\t\t\t\tleft: 0;\n\t\t\t\ttop: ${y * ch}px;\n\t\t\t\theight: ${ch}px;\n\t\t\t\twhite-space: pre;\n\t\t\t`\n\n // Sort runs by x position\n const sortedRuns = runs.sort((a, b) => a.x - b.x)\n\n for (const run of sortedRuns) {\n const span = document.createElement(\"span\")\n span.className = `${this.config.classPrefix}-text`\n span.textContent = run.text\n\n // Apply styles (convert cell x to pixel x)\n const styles: string[] = [`position: absolute`, `left: ${run.x * cw}px`]\n\n if (run.style.fg) {\n styles.push(`color: ${resolveColor(run.style.fg, this.config.foregroundColor)}`)\n }\n if (run.style.bg) {\n styles.push(`background-color: ${resolveColor(run.style.bg, this.config.backgroundColor)}`)\n }\n\n const attrs = run.style.attrs\n if (attrs) {\n if (attrs.bold) styles.push(\"font-weight: bold\")\n if (attrs.dim) styles.push(\"opacity: 0.5\")\n if (attrs.italic) styles.push(\"font-style: italic\")\n\n // Underline handling\n if (attrs.underline || attrs.underlineStyle) {\n const underlineStyle = attrs.underlineStyle ?? \"single\"\n const underlineColor = attrs.underlineColor\n ? resolveColor(attrs.underlineColor, this.config.foregroundColor)\n : \"currentColor\"\n\n switch (underlineStyle) {\n case \"double\":\n styles.push(`text-decoration: underline double ${underlineColor}`)\n break\n case \"curly\":\n styles.push(`text-decoration: underline wavy ${underlineColor}`)\n break\n case \"dotted\":\n styles.push(`text-decoration: underline dotted ${underlineColor}`)\n break\n case \"dashed\":\n styles.push(`text-decoration: underline dashed ${underlineColor}`)\n break\n default:\n styles.push(`text-decoration: underline solid ${underlineColor}`)\n }\n }\n\n if (attrs.strikethrough) {\n const existing = styles.find((s) => s.startsWith(\"text-decoration:\"))\n if (existing) {\n const idx = styles.indexOf(existing)\n styles[idx] = existing.replace(\"underline\", \"underline line-through\")\n } else {\n styles.push(\"text-decoration: line-through\")\n }\n }\n\n if (attrs.inverse) {\n // Swap foreground/background\n const fg = run.style.fg\n ? resolveColor(run.style.fg, this.config.foregroundColor)\n : this.config.foregroundColor\n const bg = run.style.bg\n ? resolveColor(run.style.bg, this.config.backgroundColor)\n : this.config.backgroundColor\n styles.push(`color: ${bg}`, `background-color: ${fg}`)\n }\n }\n\n span.style.cssText = styles.join(\"; \")\n lineDiv.appendChild(span)\n }\n\n container.appendChild(lineDiv)\n }\n }\n\n /**\n * Clear the buffer.\n */\n clear(): void {\n this.lines.clear()\n this.backgrounds.clear()\n }\n}\n\n// ============================================================================\n// DOM Adapter Factory\n// ============================================================================\n\nexport function createDOMAdapter(config: DOMAdapterConfig = {}): RenderAdapter {\n const cfg = { ...DEFAULT_CONFIG, ...config }\n const measurer = createDOMMeasurer(cfg)\n\n return {\n name: \"dom\",\n measurer,\n\n createBuffer(width: number, height: number): RenderBuffer {\n return new DOMRenderBuffer(width, height, cfg)\n },\n\n flush(buffer: RenderBuffer, _prevBuffer: RenderBuffer | null): void {\n // DOM buffer renders directly when render() is called\n const domBuffer = buffer as DOMRenderBuffer\n if (domBuffer.getContainer()) {\n domBuffer.render()\n }\n },\n\n getBorderChars(style: string): BorderChars {\n return BORDER_CHARS[style] ?? BORDER_CHARS.single!\n },\n }\n}\n\n// ============================================================================\n// Inject Global Styles (Optional)\n// ============================================================================\n\nlet stylesInjected = false\n\n/**\n * Inject global CSS styles for silvery DOM rendering.\n * Call once at application startup if you want default styling.\n */\nexport function injectDOMStyles(classPrefix = \"silvery\"): void {\n if (stylesInjected || typeof document === \"undefined\") return\n\n const style = document.createElement(\"style\")\n style.textContent = `\n\t\t.${classPrefix}-container {\n\t\t\tfont-family: monospace;\n\t\t\twhite-space: pre;\n\t\t\toverflow: hidden;\n\t\t}\n\t\t.${classPrefix}-line {\n\t\t\twhite-space: pre;\n\t\t}\n\t\t.${classPrefix}-text {\n\t\t\twhite-space: pre;\n\t\t}\n\t\t/* Selection styling */\n\t\t.${classPrefix}-text::selection {\n\t\t\tbackground-color: rgba(100, 150, 255, 0.3);\n\t\t}\n\t`\n document.head.appendChild(style)\n stylesInjected = true\n}\n","/**\n * DOM-level Mouse Events for silvery\n *\n * Provides React DOM-compatible mouse event infrastructure:\n * - SilveryMouseEvent / SilveryWheelEvent synthetic event objects\n * - Tree-based hit testing using scrollRect (replaces manual HitRegistry)\n * - Event dispatch with bubbling (target → root, stopPropagation support)\n * - Double-click detection (300ms / 2-cell threshold)\n * - mouseenter/mouseleave tracking (no bubble, like DOM spec)\n */\n\nimport { createLogger } from \"loggily\"\nimport type { FocusManager } from \"@silvery/ag/focus-manager\"\nimport { findFocusableAncestor } from \"@silvery/ag/focus-queries\"\nimport type { ParsedMouse } from \"./mouse\"\nimport { getAncestorPath, pointInRect } from \"@silvery/ag/tree-utils\"\nimport type { AgNode, Rect, UserSelect } from \"@silvery/ag/types\"\nimport type { SelectionScope } from \"@silvery/headless/selection\"\nimport { setHovered, setArmed } from \"@silvery/ag/interactive-signals\"\n\n// Re-export canonical types from ag (avoid duplicate type definitions)\nexport type { SilveryMouseEvent, SilveryWheelEvent } from \"@silvery/ag/mouse-event-types\"\nimport type { SilveryMouseEvent, SilveryWheelEvent } from \"@silvery/ag/mouse-event-types\"\n\nconst mouseLog = createLogger(\"silvery:mouse\")\n\n// ============================================================================\n// Mouse Event Handler Props — canonical location is @silvery/ag\n// ============================================================================\n\nimport type { MouseEventProps } from \"@silvery/ag/mouse-event-types\"\n\n// ============================================================================\n// Event Factory\n// ============================================================================\n\n/**\n * Create a synthetic mouse event.\n *\n * Modifier keys are merged from two sources:\n * - SGR mouse protocol: reports Ctrl, Alt/Meta, Shift (reliable)\n * - Keyboard tracking: reports Super/Cmd, Hyper, CapsLock, NumLock (via Kitty protocol)\n *\n * `metaKey` = keyboard-tracked Super (Cmd on macOS). SGR \"meta\" maps to `altKey`.\n */\nexport function createMouseEvent(\n type: SilveryMouseEvent[\"type\"],\n x: number,\n y: number,\n target: AgNode,\n parsed: ParsedMouse,\n keyboardMods?: KeyboardModifierState,\n): SilveryMouseEvent {\n let propagationStopped = false\n let defaultPrevented = false\n const metaKey = keyboardMods?.super ?? false\n if (type === \"click\" || type === \"mousedown\") {\n mouseLog.debug?.(`createMouseEvent(${type}) metaKey=${metaKey} keyboardMods.super=${keyboardMods?.super}`)\n }\n\n return {\n type,\n clientX: x,\n clientY: y,\n button: parsed.button,\n altKey: parsed.meta,\n ctrlKey: parsed.ctrl,\n metaKey,\n shiftKey: parsed.shift,\n target,\n currentTarget: target,\n nativeEvent: parsed,\n get propagationStopped() {\n return propagationStopped\n },\n get defaultPrevented() {\n return defaultPrevented\n },\n stopPropagation() {\n propagationStopped = true\n },\n preventDefault() {\n defaultPrevented = true\n },\n }\n}\n\n/**\n * Create a synthetic wheel event.\n */\nexport function createWheelEvent(\n x: number,\n y: number,\n target: AgNode,\n parsed: ParsedMouse,\n keyboardMods?: KeyboardModifierState,\n): SilveryWheelEvent {\n const base = createMouseEvent(\"wheel\", x, y, target, parsed, keyboardMods) as SilveryWheelEvent\n base.deltaY = parsed.delta ?? 0\n base.deltaX = 0\n return base\n}\n\n// ============================================================================\n// Hit Testing\n// ============================================================================\n\n/**\n * Tree-based hit test: find the deepest node whose scrollRect contains (x, y).\n * Uses reverse child order (last sibling wins = highest z-order, like DOM).\n * Respects overflow:hidden clipping.\n */\nexport function hitTest(node: AgNode, x: number, y: number): AgNode | null {\n const rect = node.scrollRect\n if (!rect) return null\n\n // Check if point is within this node's bounds\n if (!pointInRect(x, y, rect)) return null\n\n // pointerEvents=\"none\" makes this node and its subtree invisible to hit testing\n const props = node.props as { overflow?: string; pointerEvents?: string }\n if (props.pointerEvents === \"none\") return null\n\n // Check overflow clipping — if overflow is \"hidden\" or \"scroll\",\n // children outside this node's rect are not hittable\n const clips = props.overflow === \"hidden\" || props.overflow === \"scroll\"\n\n // DFS: check children in reverse order (last child = top z-order, like DOM)\n for (let i = node.children.length - 1; i >= 0; i--) {\n const child = node.children[i]!\n // If parent clips, skip children whose scrollRect doesn't overlap parent\n if (clips) {\n const childRect = child.scrollRect\n if (childRect && !pointInRect(x, y, rect)) {\n continue\n }\n }\n const hit = hitTest(child, x, y)\n if (hit) return hit\n }\n\n // Check virtual text children with inlineRects (nested Text inside Text).\n // These don't have scrollRect/layoutNode, so standard DFS misses them.\n if (node.type === \"silvery-text\") {\n for (let i = node.children.length - 1; i >= 0; i--) {\n const child = node.children[i]!\n if (child.inlineRects) {\n for (const inlineRect of child.inlineRects) {\n if (pointInRect(x, y, inlineRect)) return child\n }\n }\n }\n }\n\n // No child matched — this node is the target (if it has a scrollRect)\n return node\n}\n\n// ============================================================================\n// Selection Hit Testing\n// ============================================================================\n\n/**\n * Resolve the effective userSelect value for a node.\n * \"auto\" inherits from parent; root defaults to \"text\".\n */\nexport function resolveUserSelect(node: AgNode): \"none\" | \"text\" | \"contain\" {\n let current: AgNode | null = node\n while (current) {\n const props = current.props as { userSelect?: UserSelect }\n const value = props.userSelect\n if (value === \"none\" || value === \"text\" || value === \"contain\") return value\n // \"auto\" or undefined — walk up\n current = current.parent\n }\n // Root default is \"text\"\n return \"text\"\n}\n\n/**\n * Selection hit test: find the deepest node whose text is selectable at (x, y).\n *\n * Unlike pointer hitTest, this:\n * - Ignores pointerEvents (a node with pointerEvents=\"none\" can still be selectable)\n * - Respects userSelect (a node with userSelect=\"none\" is not a selection target)\n */\nexport function selectionHitTest(node: AgNode, x: number, y: number): AgNode | null {\n const rect = node.scrollRect\n if (!rect) return null\n\n if (!pointInRect(x, y, rect)) return null\n\n // userSelect=\"none\" blocks this subtree from selection hit testing\n // But only if explicitly \"none\" — \"auto\" inherits and root defaults to \"text\"\n const props = node.props as { overflow?: string; userSelect?: UserSelect }\n const resolved = resolveUserSelect(node)\n if (resolved === \"none\") return null\n\n // Check overflow clipping (same as pointer hitTest)\n const clips = props.overflow === \"hidden\" || props.overflow === \"scroll\"\n\n // DFS: check children in reverse order (last child = top z-order)\n for (let i = node.children.length - 1; i >= 0; i--) {\n const child = node.children[i]!\n if (clips) {\n const childRect = child.scrollRect\n if (childRect && !pointInRect(x, y, rect)) {\n continue\n }\n }\n const hit = selectionHitTest(child, x, y)\n if (hit) return hit\n }\n\n // Check virtual text children with inlineRects\n if (node.type === \"silvery-text\") {\n for (let i = node.children.length - 1; i >= 0; i--) {\n const child = node.children[i]!\n if (child.inlineRects) {\n for (const inlineRect of child.inlineRects) {\n if (pointInRect(x, y, inlineRect)) return child\n }\n }\n }\n }\n\n return node\n}\n\n/**\n * Find the contain boundary for a node.\n * Walks up to the nearest `userSelect=\"contain\"` ancestor and returns its scrollRect\n * as a SelectionScope. Returns null if no contain boundary exists.\n */\nexport function findContainBoundary(node: AgNode): SelectionScope | null {\n let current: AgNode | null = node\n while (current) {\n const props = current.props as { userSelect?: UserSelect }\n if (props.userSelect === \"contain\") {\n const rect = current.scrollRect\n if (rect) {\n return {\n top: rect.y,\n bottom: rect.y + rect.height - 1,\n left: rect.x,\n right: rect.x + rect.width - 1,\n }\n }\n }\n current = current.parent\n }\n return null\n}\n\n// ============================================================================\n// Draggable Resolution\n// ============================================================================\n\n/**\n * Check if a node has draggable=true.\n * Unlike userSelect, draggable is NOT inherited — only the exact node is checked.\n * Ancestors' draggable prop has no effect on children.\n */\nexport function resolveNodeDraggable(node: AgNode | null): boolean {\n if (!node) return false\n const props = node.props as { draggable?: boolean }\n return props.draggable === true\n}\n\n// ============================================================================\n// Event Dispatch\n// ============================================================================\n\n/** Map event type to the handler prop name */\nconst EVENT_HANDLER_MAP: Record<string, string & keyof MouseEventProps> = {\n click: \"onClick\",\n dblclick: \"onDoubleClick\",\n mousedown: \"onMouseDown\",\n mouseup: \"onMouseUp\",\n mousemove: \"onMouseMove\",\n mouseenter: \"onMouseEnter\",\n mouseleave: \"onMouseLeave\",\n wheel: \"onWheel\",\n}\n\n/**\n * Dispatch a mouse event through the render tree with DOM-style bubbling.\n *\n * Bubbles from target → root, calling the appropriate handler on each node.\n * stopPropagation() halts bubbling. mouseenter/mouseleave do NOT bubble (DOM spec).\n */\nexport function dispatchMouseEvent(event: SilveryMouseEvent): void {\n const handlerProp = EVENT_HANDLER_MAP[event.type]\n if (!handlerProp) return\n\n // mouseenter/mouseleave don't bubble (DOM spec)\n const noBubble = event.type === \"mouseenter\" || event.type === \"mouseleave\"\n\n if (noBubble) {\n // Only fire on the target itself\n const handler = (event.target.props as Record<string, unknown>)[handlerProp] as\n | ((e: SilveryMouseEvent) => void)\n | undefined\n if (handler) {\n const mutableEvent = event as { currentTarget: AgNode }\n mutableEvent.currentTarget = event.target\n handler(event)\n }\n return\n }\n\n // Bubble phase: fire from target up to root\n const path = getAncestorPath(event.target)\n for (const node of path) {\n if (event.propagationStopped) break\n\n const handler = (node.props as Record<string, unknown>)[handlerProp] as ((e: SilveryMouseEvent) => void) | undefined\n if (handler) {\n const mutableEvent = event as { currentTarget: AgNode }\n mutableEvent.currentTarget = node\n handler(event)\n }\n }\n}\n\n// ============================================================================\n// Double-Click Detection\n// ============================================================================\n\nexport interface DoubleClickState {\n lastClickTime: number\n lastClickX: number\n lastClickY: number\n lastClickButton: number\n}\n\nexport function createDoubleClickState(): DoubleClickState {\n return {\n lastClickTime: 0,\n lastClickX: -999,\n lastClickY: -999,\n lastClickButton: -1,\n }\n}\n\nconst DOUBLE_CLICK_TIME_MS = 300\nconst DOUBLE_CLICK_DISTANCE = 2\n\n/**\n * Check if a click qualifies as a double-click, given the previous click state.\n * Updates the state for the next check.\n * Returns true if this is a double-click.\n */\nexport function checkDoubleClick(\n state: DoubleClickState,\n x: number,\n y: number,\n button: number,\n now: number = Date.now(),\n): boolean {\n const timeDelta = now - state.lastClickTime\n const dx = Math.abs(x - state.lastClickX)\n const dy = Math.abs(y - state.lastClickY)\n const sameButton = button === state.lastClickButton\n\n const isDouble =\n sameButton && timeDelta <= DOUBLE_CLICK_TIME_MS && dx <= DOUBLE_CLICK_DISTANCE && dy <= DOUBLE_CLICK_DISTANCE\n\n // Update state\n state.lastClickTime = now\n state.lastClickX = x\n state.lastClickY = y\n state.lastClickButton = button\n\n // If double-click, reset so triple-click doesn't register as another double\n if (isDouble) {\n state.lastClickTime = 0\n }\n\n return isDouble\n}\n\n// ============================================================================\n// Mouse Enter/Leave Tracking\n// ============================================================================\n\n/**\n * Compute mouseenter/mouseleave transitions between two ancestor paths.\n *\n * Returns { entered, left } — arrays of nodes that were entered or left.\n * Mirrors the DOM spec: fire mouseleave on nodes in prevPath not in nextPath,\n * and mouseenter on nodes in nextPath not in prevPath.\n */\nexport function computeEnterLeave(prevPath: AgNode[], nextPath: AgNode[]): { entered: AgNode[]; left: AgNode[] } {\n const prevSet = new Set(prevPath)\n const nextSet = new Set(nextPath)\n\n const entered = nextPath.filter((n) => !prevSet.has(n))\n const left = prevPath.filter((n) => !nextSet.has(n))\n\n return { entered, left }\n}\n\n// ============================================================================\n// High-Level Mouse Event Processor\n// ============================================================================\n\n/**\n * Options for creating a mouse event processor.\n */\nexport interface MouseEventProcessorOptions {\n /** Optional focus manager — enables click-to-focus behavior.\n * On mousedown, the deepest focusable ancestor of the hit target is focused. */\n focusManager?: FocusManager\n}\n\n/**\n * State for the mouse event processor.\n */\n/**\n * Keyboard modifier state tracked from Kitty protocol key events.\n * Merged into mouse events to provide accurate modifier detection\n * (SGR mouse protocol reports Ctrl/Alt/Shift but NOT Cmd/Super).\n */\nexport interface KeyboardModifierState {\n super: boolean\n hyper: boolean\n capsLock: boolean\n numLock: boolean\n}\n\nexport interface MouseEventProcessorState {\n doubleClick: DoubleClickState\n /** Previous hover path (for enter/leave tracking) */\n hoverPath: AgNode[]\n /** Whether the left button is currently down (for click detection) */\n mouseDownTarget: AgNode | null\n /** Optional focus manager for click-to-focus */\n focusManager?: FocusManager\n /** Modifier state from Kitty keyboard events, merged into mouse events */\n keyboardModifiers: KeyboardModifierState\n}\n\nexport function createMouseEventProcessor(options?: MouseEventProcessorOptions): MouseEventProcessorState {\n return {\n doubleClick: createDoubleClickState(),\n hoverPath: [],\n mouseDownTarget: null,\n focusManager: options?.focusManager,\n keyboardModifiers: { super: false, hyper: false, capsLock: false, numLock: false },\n }\n}\n\n/**\n * Update keyboard modifier state from a parsed key event.\n * Call this for every keyboard event so mouse events can include accurate modifiers.\n */\nexport function updateKeyboardModifiers(\n state: MouseEventProcessorState,\n key: {\n super?: boolean\n hyper?: boolean\n capsLock?: boolean\n numLock?: boolean\n eventType?: string\n },\n): void {\n // On key release events, clear the modifier. On press/repeat, set it.\n const isRelease = key.eventType === \"release\"\n const prevSuper = state.keyboardModifiers.super\n if (key.super !== undefined) state.keyboardModifiers.super = isRelease ? false : key.super\n if (key.hyper !== undefined) state.keyboardModifiers.hyper = isRelease ? false : key.hyper\n if (key.capsLock !== undefined) state.keyboardModifiers.capsLock = key.capsLock\n if (key.numLock !== undefined) state.keyboardModifiers.numLock = key.numLock\n if (state.keyboardModifiers.super !== prevSuper) {\n mouseLog.debug?.(\n `keyboardModifiers.super: ${prevSuper} → ${state.keyboardModifiers.super} (key.super=${key.super}, eventType=${key.eventType})`,\n )\n }\n}\n\n/**\n * Process a raw ParsedMouse event and dispatch DOM-level events on the render tree.\n *\n * Call this for every SGR mouse event received. It handles:\n * - mousedown / mouseup\n * - click (on mouseup if same target as mousedown)\n * - dblclick (based on timing)\n * - mousemove + mouseenter/mouseleave\n * - wheel\n */\nexport function processMouseEvent(state: MouseEventProcessorState, parsed: ParsedMouse, root: AgNode): boolean {\n const { x, y, action } = parsed\n const target = hitTest(root, x, y)\n if (action === \"move\") {\n const nodeType = target?.type ?? \"null\"\n const nodeId = target ? ((target.props as Record<string, unknown>).id ?? \"\") : \"\"\n // Check entire ancestor path for onMouseEnter\n let enterAncestor = \"\"\n if (target) {\n let n: AgNode | null = target\n while (n) {\n if (\"onMouseEnter\" in (n.props as Record<string, unknown>)) {\n enterAncestor = `${n.type}#${(n.props as Record<string, unknown>).id ?? \"\"}`\n break\n }\n n = n.parent\n }\n }\n const newPath = target ? getAncestorPath(target) : []\n const { entered } = computeEnterLeave(state.hoverPath, newPath)\n mouseLog.debug?.(\n `move x=${x} y=${y} target=${nodeType}#${nodeId} enterAncestor=${enterAncestor || \"none\"} entered=${entered.length} prevPath=${state.hoverPath.length}`,\n )\n }\n if (!target) return false\n let defaultPrevented = false\n\n if (action === \"down\") {\n state.mouseDownTarget = target\n\n // Set armed state on the target node\n setArmed(target, true)\n\n // Click-to-focus: find nearest focusable ancestor and focus it\n if (state.focusManager) {\n const focusable = findFocusableAncestor(target)\n if (focusable) {\n state.focusManager.focus(focusable, \"mouse\")\n }\n }\n\n const event = createMouseEvent(\"mousedown\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(event)\n if (event.defaultPrevented) defaultPrevented = true\n } else if (action === \"up\") {\n // Clear armed state on the mousedown target\n if (state.mouseDownTarget) {\n setArmed(state.mouseDownTarget, false)\n }\n\n const event = createMouseEvent(\"mouseup\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(event)\n\n // Click = mouseup on the same node (or ancestor) where mousedown happened\n // DOM actually fires click even if up is on a different element, but the target\n // is the nearest common ancestor. For simplicity, we fire click on the up target\n // if mousedown was on the same target or a descendant.\n if (state.mouseDownTarget) {\n const clickEvent = createMouseEvent(\"click\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(clickEvent)\n if (clickEvent.defaultPrevented) defaultPrevented = true\n\n // Check for double-click\n const isDouble = checkDoubleClick(state.doubleClick, x, y, parsed.button)\n if (isDouble) {\n const dblEvent = createMouseEvent(\"dblclick\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(dblEvent)\n if (dblEvent.defaultPrevented) defaultPrevented = true\n }\n }\n\n state.mouseDownTarget = null\n } else if (action === \"move\") {\n const event = createMouseEvent(\"mousemove\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(event)\n\n // Compute enter/leave transitions\n const newPath = getAncestorPath(target)\n const { entered, left } = computeEnterLeave(state.hoverPath, newPath)\n\n // Fire mouseleave on nodes that were left (reverse order = deepest first)\n for (const node of left) {\n setHovered(node, false)\n const leaveEvent = createMouseEvent(\"mouseleave\", x, y, node, parsed, state.keyboardModifiers)\n dispatchMouseEvent(leaveEvent)\n }\n\n // Fire mouseenter on newly entered nodes (forward order = shallowest first)\n for (const node of entered.reverse()) {\n setHovered(node, true)\n const enterEvent = createMouseEvent(\"mouseenter\", x, y, node, parsed, state.keyboardModifiers)\n dispatchMouseEvent(enterEvent)\n }\n\n state.hoverPath = newPath\n } else if (action === \"wheel\") {\n const event = createWheelEvent(x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(event)\n if (event.defaultPrevented) defaultPrevented = true\n }\n return defaultPrevented\n}\n","/**\n * silvery Inspector — Debug introspection for rendering pipeline.\n *\n * Activate with SILVERY_DEV=1 env var or by calling enableInspector().\n * Outputs debug info to stderr or a log file (never to the TUI stdout).\n *\n * Features:\n * - Component tree dump (with layout rects)\n * - Focus path display\n * - Render stats (frame time, dirty rows, cell changes)\n * - Dirty region visualization\n *\n * This is DISTINCT from React DevTools (devtools.ts). This inspector provides\n * silvery-specific introspection: render pipeline stats, focus tree, dirty regions,\n * layout info.\n */\n\nimport type { createWriteStream as createWriteStreamType } from \"node:fs\"\nimport type { RenderStats } from \"./scheduler\"\nimport type { AgNode } from \"@silvery/ag/types\"\nimport { isDirty, CONTENT_BIT, STYLE_PROPS_BIT, BG_BIT, SUBTREE_BIT, CHILDREN_BIT } from \"@silvery/ag/epoch\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface InspectorOptions {\n /** Output stream (default: process.stderr) */\n output?: NodeJS.WritableStream\n /** Log file path (overrides output stream) */\n logFile?: string\n /** Include layout rects in tree dump */\n showLayout?: boolean\n /** Include style info in tree dump */\n showStyles?: boolean\n}\n\n// =============================================================================\n// State\n// =============================================================================\n\nlet inspectorEnabled = false\nlet inspectorOutput: NodeJS.WritableStream = process.stderr\n\n// =============================================================================\n// Public API\n// =============================================================================\n\n/** Enable the silvery inspector. */\nexport function enableInspector(options?: InspectorOptions): void {\n inspectorEnabled = true\n if (options?.logFile) {\n // Dynamic require to avoid pulling in fs for non-inspector users\n const fs: { createWriteStream: typeof createWriteStreamType } = require(\"node:fs\")\n inspectorOutput = fs.createWriteStream(options.logFile, { flags: \"a\" })\n } else if (options?.output) {\n inspectorOutput = options.output\n } else {\n inspectorOutput = process.stderr\n }\n}\n\n/** Disable the inspector. */\nexport function disableInspector(): void {\n inspectorEnabled = false\n}\n\n/** Check if inspector is active. */\nexport function isInspectorEnabled(): boolean {\n return inspectorEnabled\n}\n\n/**\n * Log render stats after each frame.\n *\n * Called by the scheduler after executeRender completes. When the inspector\n * is disabled this is a no-op (zero overhead).\n */\nexport function inspectFrame(stats: RenderStats): void {\n if (!inspectorEnabled) return\n const line =\n `[silvery] frame #${stats.renderCount} ` +\n `${stats.lastRenderTime.toFixed(1)}ms ` +\n `avg=${stats.avgRenderTime.toFixed(1)}ms ` +\n `skipped=${stats.skippedCount}\\n`\n inspectorOutput.write(line)\n}\n\n/**\n * Dump the component tree structure as indented text.\n *\n * Walks the SilveryNode tree and formats each node with its type, testID,\n * layout rect, and dirty flags.\n */\nexport function inspectTree(rootNode: AgNode, options?: { depth?: number; showLayout?: boolean }): string {\n const maxDepth = options?.depth ?? 10\n const showLayout = options?.showLayout ?? true\n const lines: string[] = []\n\n function walk(node: AgNode, indent: number): void {\n if (indent > maxDepth) return\n\n const prefix = \" \".repeat(indent)\n const type = node.type\n const testID = (node.props as Record<string, unknown>)?.testID\n const idStr = testID ? ` #${testID}` : \"\"\n\n // Layout rect from computed layout node or boxRect\n let rectStr = \"\"\n if (showLayout) {\n if (node.boxRect) {\n const r = node.boxRect\n rectStr = ` [${r.x},${r.y} ${r.width}x${r.height}]`\n } else if (node.layoutNode) {\n const ln = node.layoutNode\n rectStr = ` [${ln.getComputedLeft()},${ln.getComputedTop()} ${ln.getComputedWidth()}x${ln.getComputedHeight()}]`\n }\n }\n\n // Dirty flags\n const dirtyFlags: string[] = []\n if (node.layoutDirty) dirtyFlags.push(\"layout\")\n if (isDirty(node.dirtyBits, node.dirtyEpoch, CONTENT_BIT)) dirtyFlags.push(\"content\")\n if (isDirty(node.dirtyBits, node.dirtyEpoch, STYLE_PROPS_BIT)) dirtyFlags.push(\"paint\")\n if (isDirty(node.dirtyBits, node.dirtyEpoch, BG_BIT)) dirtyFlags.push(\"bg\")\n if (isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT)) dirtyFlags.push(\"subtree\")\n if (isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT)) dirtyFlags.push(\"children\")\n const dirtyStr = dirtyFlags.length > 0 ? ` dirty=[${dirtyFlags.join(\",\")}]` : \"\"\n\n // Text content (for text nodes)\n const textStr = node.textContent\n ? ` \"${node.textContent.slice(0, 30)}${node.textContent.length > 30 ? \"...\" : \"\"}\"`\n : \"\"\n\n lines.push(`${prefix}${type}${idStr}${rectStr}${dirtyStr}${textStr}`)\n\n for (const child of node.children) {\n walk(child, indent + 1)\n }\n }\n\n walk(rootNode, 0)\n return lines.join(\"\\n\")\n}\n\n/**\n * Auto-enable if SILVERY_DEV=1 is set.\n *\n * Call this at startup to respect the environment variable convention.\n */\nexport function autoEnableInspector(): void {\n if (process.env.SILVERY_DEV === \"1\" || process.env.SILVERY_DEV === \"true\") {\n const logFile = process.env.SILVERY_DEV_LOG\n enableInspector(logFile ? { logFile } : undefined)\n }\n}\n","/**\n * @silvery/core — TEA runtime, React reconciler, store, and effect system.\n *\n * Portable core that doesn't depend on terminal specifics. Can target\n * terminal, canvas, DOM, or any custom render backend.\n *\n * @packageDocumentation\n */\n\n/**\n * silvery/core — Pure functions and types, NO React dependency.\n *\n * This sub-path export provides:\n * - TEA (The Elm Architecture) types: SilveryModel, SilveryMsg, Effect, Sub, Plugin\n * - Focus manager: createFocusManager + types\n * - Focus events: event factories + dispatch functions\n * - Focus queries: tree query functions\n * - Plugin composition: compose() for middleware-style plugin chaining\n *\n * Everything here is pure TypeScript — no React import anywhere in the\n * dependency graph. Safe for use in non-React contexts (CLI tools, tests,\n * server-side logic).\n *\n * @packageDocumentation\n */\n\n// =============================================================================\n// TEA Types (The Elm Architecture)\n// =============================================================================\n\nimport type { FocusOrigin } from \"../focus-manager.js\"\nexport type { FocusOrigin } from \"../focus-manager.js\"\n\n/**\n * The model type that silvery manages for focus state.\n *\n * Applications extend this with their own model fields.\n * The store's update function receives the full model and returns\n * a new model + effects tuple.\n */\nexport interface SilveryModel {\n focus: {\n activeId: string | null\n previousId: string | null\n origin: FocusOrigin | null\n scopeStack: string[]\n scopeMemory: Record<string, string>\n }\n}\n\n/**\n * Direction type used in spatial navigation messages.\n */\nexport type Direction = \"up\" | \"down\" | \"left\" | \"right\"\n\n/**\n * Message types that silvery understands.\n *\n * Applications can extend this union with their own message types.\n * The store's update function pattern-matches on `type` to decide\n * how to update the model.\n */\nexport type SilveryMsg =\n | { type: \"focus\"; nodeId: string; origin?: FocusOrigin }\n | { type: \"blur\" }\n | { type: \"focus-next\" }\n | { type: \"focus-prev\" }\n | { type: \"focus-direction\"; direction: Direction }\n | { type: \"scope-enter\"; scopeId: string }\n | { type: \"scope-exit\" }\n | {\n type: \"term:key\"\n key: string\n input: string\n ctrl: boolean\n meta: boolean\n shift: boolean\n }\n | {\n type: \"term:mouse\"\n action: \"down\" | \"up\" | \"move\" | \"scroll\"\n x: number\n y: number\n button: number\n }\n | { type: \"term:resize\"; cols: number; rows: number }\n\n/**\n * Effect commands returned by update functions.\n *\n * Effects are declarative descriptions of side effects. The store\n * executes them after the model update, keeping the update function pure.\n *\n * - `none`: No effect (useful as a default)\n * - `batch`: Multiple effects to execute\n * - `dispatch`: Queue another message (no re-entrant dispatch)\n */\nexport type Effect = { type: \"none\" } | { type: \"batch\"; effects: Effect[] } | { type: \"dispatch\"; msg: SilveryMsg }\n\n/**\n * Subscription descriptor (for future use).\n *\n * Subscriptions represent long-running side effects (timers, event listeners)\n * that produce messages over time. The store manages their lifecycle.\n */\nexport type Sub = {\n type: \"none\"\n}\n\n// =============================================================================\n// Effect Constructors\n// =============================================================================\n\n/** No-op effect. */\nexport const none: Effect = { type: \"none\" }\n\n/** Batch multiple effects. */\nexport function batch(...effects: Effect[]): Effect {\n // Flatten nested batches and filter out none effects\n const flat: Effect[] = []\n for (const e of effects) {\n if (e.type === \"none\") continue\n if (e.type === \"batch\") {\n flat.push(...e.effects)\n } else {\n flat.push(e)\n }\n }\n if (flat.length === 0) return none\n if (flat.length === 1) return flat[0]!\n return { type: \"batch\", effects: flat }\n}\n\n/** Queue a message dispatch as an effect. */\nexport function dispatch(msg: SilveryMsg): Effect {\n return { type: \"dispatch\", msg }\n}\n\n// =============================================================================\n// Plugin Type (Middleware Composition)\n// =============================================================================\n\n/**\n * A plugin wraps an update function, adding behavior before/after/around it.\n *\n * Plugins compose via `compose()` — the outermost plugin runs first on\n * message receive, but the innermost (original) update runs first for\n * model updates. This is the standard middleware pattern.\n *\n * @example\n * ```ts\n * const logging: Plugin<MyModel, MyMsg> = (inner) => (msg, model) => {\n * console.log('msg:', msg.type)\n * const result = inner(msg, model)\n * console.log('new model:', result[0])\n * return result\n * }\n * ```\n */\nexport type Plugin<Model, Msg> = (\n innerUpdate: (msg: Msg, model: Model) => [Model, Effect[]],\n) => (msg: Msg, model: Model) => [Model, Effect[]]\n\n/**\n * Compose multiple plugins into a single update function wrapper.\n *\n * Plugins are applied right-to-left (innermost first), so the first\n * plugin in the array is the outermost wrapper — it sees messages first\n * and model changes last.\n *\n * @example\n * ```ts\n * const update = compose(logging, focusNav, spatialNav)(baseUpdate)\n * // Equivalent to: logging(focusNav(spatialNav(baseUpdate)))\n * ```\n */\nexport function compose<Model, Msg>(...plugins: Plugin<Model, Msg>[]): Plugin<Model, Msg> {\n return (innerUpdate) => {\n let update = innerUpdate\n // Apply right-to-left so first plugin is outermost\n for (let i = plugins.length - 1; i >= 0; i--) {\n update = plugins[i]!(update)\n }\n return update\n }\n}\n\n// =============================================================================\n// Focus Manager (pure, no React)\n// =============================================================================\n\nexport { createFocusManager } from \"../focus-manager.js\"\nexport type { FocusManager, FocusManagerOptions, FocusChangeCallback, FocusSnapshot } from \"../focus-manager.js\"\n\n// =============================================================================\n// Focus Events (pure, no React)\n// =============================================================================\n\nexport { createKeyEvent, createFocusEvent, dispatchKeyEvent, dispatchFocusEvent } from \"../focus-events.js\"\nexport type { SilveryKeyEvent, SilveryFocusEvent, FocusEventProps } from \"../focus-events.js\"\n\n// =============================================================================\n// Focus Queries (pure, no React)\n// =============================================================================\n\nexport {\n findFocusableAncestor,\n getTabOrder,\n findByTestID,\n findSpatialTarget,\n getExplicitFocusLink,\n} from \"@silvery/ag/focus-queries\"\n\n// =============================================================================\n// Slices (ops-as-data helper)\n// =============================================================================\n\nexport { createSlice } from \"./slice.js\"\nexport type { Slice, SliceWithInit, InferOp } from \"./slice.js\"\n\n// =============================================================================\n// Shared Types (pure)\n// =============================================================================\n\nexport type { AgNode, Rect } from \"../types.js\"\n","/**\n * silvery/store — TEA-style state container with effects.\n *\n * Wraps FocusManager into a dispatch/subscribe loop following\n * The Elm Architecture (TEA): Model + Msg -> [Model, Effect[]].\n *\n * The store provides:\n * - `dispatch(msg)` — send a message, run update, execute effects\n * - `getModel()` — current model snapshot\n * - `subscribe(listener)` — for useSyncExternalStore integration\n * - `getSnapshot(selector)` — selector-based access\n *\n * Effects are executed after each update cycle. `dispatch` effects\n * are queued (not re-entrant) to prevent stack overflow.\n *\n * @packageDocumentation\n */\n\nimport type { Effect, SilveryModel, SilveryMsg, Plugin } from \"../core\"\nimport { none } from \"../core\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Configuration for creating a store.\n */\nexport interface StoreConfig<Model extends SilveryModel, Msg extends SilveryMsg> {\n /** Initialize model and effects. Called once on store creation. */\n init: () => [Model, Effect[]]\n /** Pure update function: (msg, model) -> [newModel, effects] */\n update: (msg: Msg, model: Model) => [Model, Effect[]]\n}\n\n/**\n * The store API returned by createStore.\n */\nexport interface StoreApi<Model extends SilveryModel, Msg extends SilveryMsg> {\n /** Send a message through the update function. */\n dispatch(msg: Msg): void\n /** Get the current model. */\n getModel(): Model\n /** Subscribe to model changes. Returns unsubscribe function. */\n subscribe(listener: () => void): () => void\n /** Get a derived value from the model via selector. */\n getSnapshot<T>(selector: (model: Model) => T): T\n}\n\n// =============================================================================\n// Built-in Plugins\n// =============================================================================\n\n/**\n * Plugin that handles focus-related messages by updating the focus slice.\n *\n * Handles: focus, blur, scope-enter, scope-exit.\n * Passes focus-next, focus-prev, focus-direction through (they need\n * the node tree, which the store doesn't have — those are handled\n * at the React integration layer).\n */\nexport function withFocusManagement<Model extends SilveryModel, Msg extends SilveryMsg>(): Plugin<Model, Msg> {\n return (innerUpdate) => (msg, model) => {\n switch (msg.type) {\n case \"focus\": {\n const focusMsg = msg as Extract<SilveryMsg, { type: \"focus\" }>\n const newModel = {\n ...model,\n focus: {\n ...model.focus,\n previousId: model.focus.activeId,\n activeId: focusMsg.nodeId,\n origin: focusMsg.origin ?? \"programmatic\",\n // Remember in current scope\n scopeMemory:\n model.focus.scopeStack.length > 0\n ? {\n ...model.focus.scopeMemory,\n [model.focus.scopeStack[model.focus.scopeStack.length - 1]!]: focusMsg.nodeId,\n }\n : model.focus.scopeMemory,\n },\n }\n return [newModel as Model, [none]]\n }\n\n case \"blur\": {\n const newModel = {\n ...model,\n focus: {\n ...model.focus,\n previousId: model.focus.activeId,\n activeId: null,\n origin: null,\n },\n }\n return [newModel as Model, [none]]\n }\n\n case \"scope-enter\": {\n const scopeMsg = msg as Extract<SilveryMsg, { type: \"scope-enter\" }>\n const newModel = {\n ...model,\n focus: {\n ...model.focus,\n scopeStack: [...model.focus.scopeStack, scopeMsg.scopeId],\n },\n }\n return [newModel as Model, [none]]\n }\n\n case \"scope-exit\": {\n const newModel = {\n ...model,\n focus: {\n ...model.focus,\n scopeStack: model.focus.scopeStack.slice(0, -1),\n },\n }\n return [newModel as Model, [none]]\n }\n\n default:\n return innerUpdate(msg, model)\n }\n }\n}\n\n// =============================================================================\n// Default Update\n// =============================================================================\n\n/**\n * The default silvery update function.\n *\n * Returns the model unchanged with no effects for any unhandled message.\n * Compose with plugins to add behavior.\n */\nexport function silveryUpdate<Model extends SilveryModel, Msg extends SilveryMsg>(\n _msg: Msg,\n model: Model,\n): [Model, Effect[]] {\n return [model, [none]]\n}\n\n// =============================================================================\n// Default Init\n// =============================================================================\n\n/**\n * Create a default initial SilveryModel.\n */\nexport function defaultInit(): [SilveryModel, Effect[]] {\n return [\n {\n focus: {\n activeId: null,\n previousId: null,\n origin: null,\n scopeStack: [],\n scopeMemory: {},\n },\n },\n [none],\n ]\n}\n\n// =============================================================================\n// Store Factory\n// =============================================================================\n\n/**\n * Create a TEA-style store.\n *\n * The store manages model state and effect execution. Messages are\n * dispatched through the update function, which returns a new model\n * and a list of effects. Effects are executed after each update cycle.\n *\n * Dispatch effects are queued to prevent re-entrant dispatch:\n * if dispatching msg A triggers effect dispatch(B), B is queued and\n * processed after A's full update cycle completes.\n *\n * @example\n * ```ts\n * import { createStore, withFocusManagement, silveryUpdate } from '@silvery/create/store'\n * import { compose } from '@silvery/create/core'\n *\n * const store = createStore({\n * init: () => [{ focus: { activeId: null, ... }, count: 0 }, []],\n * update: compose(withFocusManagement())(silveryUpdate),\n * })\n *\n * store.dispatch({ type: 'focus', nodeId: 'btn1' })\n * console.log(store.getModel().focus.activeId) // 'btn1'\n * ```\n */\nexport function createStore<Model extends SilveryModel, Msg extends SilveryMsg>(\n config: StoreConfig<Model, Msg>,\n): StoreApi<Model, Msg> {\n // Initialize\n const [initialModel, initialEffects] = config.init()\n let model = initialModel\n\n // Subscriber management\n const listeners = new Set<() => void>()\n\n function notify(): void {\n for (const listener of listeners) {\n listener()\n }\n }\n\n // Effect execution with queue for dispatch effects\n let isDispatching = false\n const dispatchQueue: Msg[] = []\n\n function executeEffects(effects: Effect[]): void {\n for (const effect of effects) {\n executeEffect(effect)\n }\n }\n\n function executeEffect(effect: Effect): void {\n switch (effect.type) {\n case \"none\":\n break\n case \"batch\":\n executeEffects(effect.effects)\n break\n case \"dispatch\":\n // Queue dispatch effects to prevent re-entrant dispatch\n dispatchQueue.push(effect.msg as Msg)\n break\n }\n }\n\n function dispatch(msg: Msg): void {\n if (isDispatching) {\n // Queue if we're already in a dispatch cycle\n dispatchQueue.push(msg)\n return\n }\n\n isDispatching = true\n try {\n // Run update\n const [newModel, effects] = config.update(msg, model)\n const changed = newModel !== model\n model = newModel\n\n // Execute effects (may queue more dispatches)\n executeEffects(effects)\n\n // Notify subscribers if model changed\n if (changed) {\n notify()\n }\n\n // Process queued dispatches\n while (dispatchQueue.length > 0) {\n const queued = dispatchQueue.shift()!\n const [nextModel, nextEffects] = config.update(queued, model)\n const nextChanged = nextModel !== model\n model = nextModel\n executeEffects(nextEffects)\n if (nextChanged) {\n notify()\n }\n }\n } finally {\n isDispatching = false\n }\n }\n\n function getModel(): Model {\n return model\n }\n\n function subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n }\n\n function getSnapshot<T>(selector: (model: Model) => T): T {\n return selector(model)\n }\n\n // Execute initial effects and drain any queued dispatches\n executeEffects(initialEffects)\n while (dispatchQueue.length > 0) {\n const queued = dispatchQueue.shift()!\n const [nextModel, nextEffects] = config.update(queued, model)\n model = nextModel\n executeEffects(nextEffects)\n // No notify during init — no subscribers yet\n }\n\n return {\n dispatch,\n getModel,\n subscribe,\n getSnapshot,\n }\n}\n","/**\n * AsyncIterable stream helpers for event-driven TUI architecture.\n *\n * These are pure functions over AsyncIterables - no EventEmitters, no callbacks.\n * All helpers properly handle cleanup via return() on early break.\n *\n * @example\n * ```typescript\n * const keys = term.keys()\n * const resizes = term.resizes()\n *\n * // Merge multiple sources\n * const events = merge(\n * map(keys, k => ({ type: 'key', ...k })),\n * map(resizes, r => ({ type: 'resize', ...r }))\n * )\n *\n * // Consume until ctrl+c\n * for await (const event of events) {\n * if (event.type === 'key' && event.key === 'ctrl+c') break\n * }\n * ```\n */\n\n/**\n * Merge multiple AsyncIterables into one.\n *\n * Values are emitted in arrival order (first-come). When all sources complete,\n * the merged iterable completes. If any source throws, the error propagates\n * and remaining sources are cleaned up.\n *\n * IMPORTANT: Each call to merge() creates a fresh iterable. Don't share\n * the same merged iterable between multiple consumers.\n *\n * @example\n * ```typescript\n * const merged = merge(keys, resizes, ticks)\n * for await (const event of merged) {\n * // Process events from any source\n * }\n * ```\n */\nexport async function* merge<T>(...sources: AsyncIterable<T>[]): AsyncGenerator<T, void, undefined> {\n if (sources.length === 0) return\n\n // Track active iterators and their pending promises\n const iterators = sources.map((source) => source[Symbol.asyncIterator]())\n const pending = new Map<number, Promise<{ index: number; result: IteratorResult<T, unknown> }>>()\n\n async function nextWithIndex(idx: number): Promise<{ index: number; result: IteratorResult<T, unknown> }> {\n const iterator = iterators[idx]\n if (!iterator) throw new Error(`No iterator at index ${idx}`)\n const result = await iterator.next()\n return { index: idx, result }\n }\n\n // Start all iterators\n for (let i = 0; i < iterators.length; i++) {\n pending.set(i, nextWithIndex(i))\n }\n\n try {\n while (pending.size > 0) {\n // Race all pending promises\n const { index, result } = await Promise.race(pending.values())\n\n if (result.done) {\n // This source is exhausted, remove it\n pending.delete(index)\n } else {\n // Yield the value and request next from this source\n yield result.value\n pending.set(index, nextWithIndex(index))\n }\n }\n } finally {\n // Clean up all iterators on early exit or error\n await Promise.all(iterators.map((it) => (it.return ? it.return() : Promise.resolve())))\n }\n}\n\n/**\n * Transform each value from an AsyncIterable.\n *\n * @example\n * ```typescript\n * const keyEvents = map(keys, k => ({ type: 'key' as const, key: k }))\n * ```\n */\nexport async function* map<T, U>(source: AsyncIterable<T>, fn: (value: T) => U): AsyncGenerator<U, void, undefined> {\n const iterator = source[Symbol.asyncIterator]()\n try {\n // Use the iterator directly to avoid double-iteration\n for await (const value of { [Symbol.asyncIterator]: () => iterator }) {\n yield fn(value)\n }\n } finally {\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Filter values from an AsyncIterable.\n *\n * @example\n * ```typescript\n * const letters = filter(keys, k => k.key.length === 1)\n * ```\n */\nexport async function* filter<T>(\n source: AsyncIterable<T>,\n predicate: (value: T) => boolean,\n): AsyncGenerator<T, void, undefined> {\n const iterator = source[Symbol.asyncIterator]()\n try {\n for await (const value of { [Symbol.asyncIterator]: () => iterator }) {\n if (predicate(value)) {\n yield value\n }\n }\n } finally {\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Filter and transform in one pass (type narrowing).\n *\n * @example\n * ```typescript\n * const keyEvents = filterMap(events, e =>\n * e.type === 'key' ? e : undefined\n * )\n * ```\n */\nexport async function* filterMap<T, U>(\n source: AsyncIterable<T>,\n fn: (value: T) => U | undefined,\n): AsyncGenerator<U, void, undefined> {\n const iterator = source[Symbol.asyncIterator]()\n try {\n for await (const value of { [Symbol.asyncIterator]: () => iterator }) {\n const mapped = fn(value)\n if (mapped !== undefined) {\n yield mapped\n }\n }\n } finally {\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Take values until an AbortSignal fires.\n *\n * When the signal aborts, the iterator completes gracefully (no error thrown).\n * The source iterator is properly cleaned up.\n *\n * @example\n * ```typescript\n * const controller = new AbortController()\n * const events = takeUntil(allEvents, controller.signal)\n *\n * // Later: controller.abort() will end the iteration\n * ```\n */\nexport async function* takeUntil<T>(source: AsyncIterable<T>, signal: AbortSignal): AsyncGenerator<T, void, undefined> {\n if (signal.aborted) return\n\n const iterator = source[Symbol.asyncIterator]()\n\n // Create a promise that resolves when signal aborts\n let abortResolve: () => void\n const abortPromise = new Promise<void>((resolve) => {\n abortResolve = resolve\n })\n const onAbort = () => abortResolve()\n signal.addEventListener(\"abort\", onAbort, { once: true })\n\n try {\n while (!signal.aborted) {\n // Race between next value and abort\n const result = await Promise.race([\n iterator.next(),\n abortPromise.then(() => ({ done: true, value: undefined }) as const),\n ])\n\n if (result.done) break\n yield result.value as T\n }\n } finally {\n signal.removeEventListener(\"abort\", onAbort)\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Take the first n values from an AsyncIterable.\n *\n * @example\n * ```typescript\n * const firstThree = take(events, 3)\n * ```\n */\nexport async function* take<T>(source: AsyncIterable<T>, count: number): AsyncGenerator<T, void, undefined> {\n if (count <= 0) return\n\n const iterator = source[Symbol.asyncIterator]()\n let taken = 0\n\n try {\n for await (const value of { [Symbol.asyncIterator]: () => iterator }) {\n yield value\n taken++\n if (taken >= count) break\n }\n } finally {\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Create an AsyncIterable from an array (useful for testing).\n *\n * @example\n * ```typescript\n * const events = fromArray([\n * { type: 'key', key: 'j' },\n * { type: 'key', key: 'k' },\n * ])\n * ```\n */\nexport async function* fromArray<T>(items: T[]): AsyncGenerator<T, void, undefined> {\n for (const item of items) {\n yield item\n }\n}\n\n/**\n * Create an AsyncIterable that yields after a delay (useful for testing).\n *\n * @example\n * ```typescript\n * const delayed = fromArrayWithDelay([1, 2, 3], 100) // 100ms between each\n * ```\n */\nexport async function* fromArrayWithDelay<T>(items: T[], delayMs: number): AsyncGenerator<T, void, undefined> {\n for (const item of items) {\n await new Promise((resolve) => setTimeout(resolve, delayMs))\n yield item\n }\n}\n\n/**\n * Throttle high-frequency sources.\n *\n * Emits the first value immediately, then ignores values for the specified\n * duration. After the duration, the next value is emitted and the cycle repeats.\n *\n * @example\n * ```typescript\n * const throttled = throttle(mouseMoves, 16) // ~60fps\n * ```\n */\nexport async function* throttle<T>(source: AsyncIterable<T>, ms: number): AsyncGenerator<T, void, undefined> {\n const iterator = source[Symbol.asyncIterator]()\n let lastEmit = 0\n\n try {\n for await (const value of { [Symbol.asyncIterator]: () => iterator }) {\n const now = Date.now()\n if (now - lastEmit >= ms) {\n lastEmit = now\n yield value\n }\n }\n } finally {\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Debounce values - only emit after source is quiet for specified duration.\n *\n * NOTE: With pull-based AsyncIterables, true debouncing is complex. This\n * implementation collects all values and only yields the final value after\n * the source completes and quiet period passes. For real-time debouncing,\n * consider using a push-based pattern or EventEmitter.\n *\n * @example\n * ```typescript\n * const debounced = debounce(searchInput, 300) // Yields last value after source ends + delay\n * ```\n */\nexport async function* debounce<T>(source: AsyncIterable<T>, ms: number): AsyncGenerator<T, void, undefined> {\n const iterator = source[Symbol.asyncIterator]()\n let last: { value: T } | undefined\n\n try {\n for await (const value of { [Symbol.asyncIterator]: () => iterator }) {\n last = { value }\n }\n\n if (last) {\n await new Promise((resolve) => setTimeout(resolve, ms))\n yield last.value\n }\n } finally {\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Collect values into batches of specified size.\n *\n * @throws {Error} If size is not positive\n *\n * @example\n * ```typescript\n * const batched = batch(events, 10) // Emit arrays of 10 events\n * ```\n */\nexport function batch<T>(source: AsyncIterable<T>, size: number): AsyncGenerator<T[], void, undefined> {\n if (size <= 0) throw new Error(\"Batch size must be positive\")\n return batchImpl(source, size)\n}\n\nasync function* batchImpl<T>(source: AsyncIterable<T>, size: number): AsyncGenerator<T[], void, undefined> {\n const iterator = source[Symbol.asyncIterator]()\n let buffer: T[] = []\n\n try {\n for await (const value of { [Symbol.asyncIterator]: () => iterator }) {\n buffer.push(value)\n if (buffer.length >= size) {\n yield buffer\n buffer = []\n }\n }\n // Emit remaining items\n if (buffer.length > 0) {\n yield buffer\n }\n } finally {\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Concatenate multiple AsyncIterables in sequence.\n *\n * @example\n * ```typescript\n * const all = concat(header, body, footer)\n * ```\n */\nexport async function* concat<T>(...sources: AsyncIterable<T>[]): AsyncGenerator<T, void, undefined> {\n for (const source of sources) {\n yield* source\n }\n}\n\n/**\n * Zip multiple AsyncIterables together.\n * Completes when the shortest source completes.\n *\n * @example\n * ```typescript\n * const pairs = zip(keys, timestamps) // [key, timestamp][]\n * ```\n */\nexport async function* zip<T extends unknown[]>(\n ...sources: { [K in keyof T]: AsyncIterable<T[K]> }\n): AsyncGenerator<T, void, undefined> {\n const iterators = sources.map((source) => source[Symbol.asyncIterator]())\n\n try {\n while (true) {\n const results = await Promise.all(iterators.map((it) => it.next()))\n\n // If any source is done, we're done\n if (results.some((r) => r.done)) break\n\n yield results.map((r) => r.value) as T\n }\n } finally {\n await Promise.all(iterators.map((it) => (it.return ? it.return() : Promise.resolve())))\n }\n}\n","/**\n * Capability Registry — symbol-keyed service registry for cross-feature communication.\n *\n * Each createApp instance gets its own registry. Interaction features use it to\n * discover each other (e.g., selection looks up clipboard capability).\n *\n * @internal Not exported from the public barrel.\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** Symbol-keyed service registry for cross-feature communication. */\nexport interface CapabilityRegistry {\n /** Register a capability. Overwrites any existing registration for the same key. */\n register<T>(key: symbol, capability: T): void\n\n /** Look up a capability. Returns undefined if not registered. */\n get<T>(key: symbol): T | undefined\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/** Create a new capability registry (one per app instance). */\nexport function createCapabilityRegistry(): CapabilityRegistry {\n const capabilities = new Map<symbol, unknown>()\n\n return {\n register<T>(key: symbol, capability: T): void {\n capabilities.set(key, capability)\n },\n\n get<T>(key: symbol): T | undefined {\n return capabilities.get(key) as T | undefined\n },\n }\n}\n","/**\n * Capability symbols — well-known keys for the CapabilityRegistry.\n *\n * Features register themselves under these symbols so other parts\n * of the composition chain can discover and interact with them.\n *\n * @internal Not exported from the public barrel.\n */\n\n/** Selection feature: text selection state + mouse handling. */\nexport const SELECTION_CAPABILITY = Symbol.for(\"silvery.selection\")\n\n/** Clipboard feature: copy/paste via OSC 52 or other backends. */\nexport const CLIPBOARD_CAPABILITY = Symbol.for(\"silvery.clipboard\")\n\n/** Copy-mode feature: keyboard-driven selection (Esc+v). */\nexport const COPY_MODE_CAPABILITY = Symbol.for(\"silvery.copy-mode\")\n\n/** Find feature: text search (Ctrl+F). */\nexport const FIND_CAPABILITY = Symbol.for(\"silvery.find\")\n\n/** Drag feature: drag-and-drop state + mouse handling. */\nexport const DRAG_CAPABILITY = Symbol.for(\"silvery.drag\")\n\n/** Input router: priority-based event dispatch for interaction features. */\nexport const INPUT_ROUTER = Symbol.for(\"silvery.input-router\")\n\n/** Color scheme detector: Mode 2031 dark/light reactive detection. */\nexport const COLOR_SCHEME_CAPABILITY = Symbol.for(\"silvery.color-scheme\")\n","/**\n * Selection state machine — pure TEA `(action, state) → [state, effects[]]`.\n *\n * Buffer-level text selection (like native terminal selection).\n * Operates on terminal buffer coordinates, not the React tree.\n *\n * Supports three granularity levels:\n * - character: default drag selection\n * - word: double-click then drag extends by words\n * - line: triple-click then drag extends by lines\n */\n\nimport type { TerminalBuffer, RowMetadata } from \"@silvery/ag-term/buffer\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface SelectionPosition {\n col: number\n row: number\n}\n\nexport interface SelectionRange {\n anchor: SelectionPosition\n head: SelectionPosition\n}\n\n/**\n * Rectangular boundary for contain-scoped selection.\n * Derived from the nearest `userSelect=\"contain\"` ancestor's scrollRect.\n */\nexport interface SelectionScope {\n top: number\n bottom: number\n left: number\n right: number\n}\n\nexport type SelectionGranularity = \"character\" | \"word\" | \"line\"\n\nexport interface TerminalSelectionState {\n range: SelectionRange | null\n /** True while mouse button is held */\n selecting: boolean\n /** Who initiated the selection */\n source: \"mouse\" | \"keyboard\" | null\n /** Current selection granularity */\n granularity: SelectionGranularity\n /** Contain boundary — selection range is clamped to this rect */\n scope: SelectionScope | null\n}\n\nexport type SelectionAction =\n | { type: \"start\"; col: number; row: number; scope?: SelectionScope | null; source?: \"mouse\" | \"keyboard\" }\n | {\n type: \"startWord\"\n col: number\n row: number\n buffer: TerminalBuffer\n scope?: SelectionScope | null\n source?: \"mouse\" | \"keyboard\"\n }\n | {\n type: \"startLine\"\n col: number\n row: number\n buffer: TerminalBuffer\n scope?: SelectionScope | null\n source?: \"mouse\" | \"keyboard\"\n }\n | { type: \"extend\"; col: number; row: number; buffer?: TerminalBuffer }\n | { type: \"finish\" }\n | { type: \"clear\" }\n\nexport type SelectionEffect = { type: \"copy\"; text: string } | { type: \"render\" }\n\n// ============================================================================\n// Word Boundary Detection\n// ============================================================================\n\n/**\n * Check if a character is a word character (not whitespace/punctuation).\n * Word chars: letters, digits, underscore (matching \\w).\n */\nfunction isWordChar(ch: string): boolean {\n return /\\w/.test(ch)\n}\n\n/**\n * Find word boundaries at a given column in the buffer row.\n * Returns { startCol, endCol } inclusive of the word.\n * If the position is on whitespace/punctuation, selects that single char.\n */\nexport function findWordBoundary(\n buffer: TerminalBuffer,\n col: number,\n row: number,\n): { startCol: number; endCol: number } {\n const width = buffer.width\n const ch = buffer.getCell(col, row).char\n\n if (isWordChar(ch)) {\n // Walk backwards to find word start\n let startCol = col\n while (startCol > 0 && isWordChar(buffer.getCell(startCol - 1, row).char)) {\n startCol--\n }\n // Walk forwards to find word end\n let endCol = col\n while (endCol < width - 1 && isWordChar(buffer.getCell(endCol + 1, row).char)) {\n endCol++\n }\n return { startCol, endCol }\n }\n\n // Non-word char: select just that character\n return { startCol: col, endCol: col }\n}\n\n// ============================================================================\n// Line Boundary Detection\n// ============================================================================\n\n/**\n * Find line boundaries for a given row.\n * Returns { startCol, endCol } spanning from first content to last content.\n * If the row is empty, returns { startCol: 0, endCol: width - 1 }.\n */\nexport function findLineBoundary(buffer: TerminalBuffer, row: number): { startCol: number; endCol: number } {\n const width = buffer.width\n\n // Find last non-space column\n let endCol = width - 1\n while (endCol > 0 && buffer.getCell(endCol, row).char.trim() === \"\") {\n endCol--\n }\n\n // Find first non-space column\n let startCol = 0\n while (startCol < endCol && buffer.getCell(startCol, row).char.trim() === \"\") {\n startCol++\n }\n\n // If entirely blank (including when startCol == endCol and that cell is blank), select the full row\n if (startCol >= endCol && buffer.getCell(startCol, row).char.trim() === \"\") {\n return { startCol: 0, endCol: width - 1 }\n }\n\n return { startCol, endCol }\n}\n\n// ============================================================================\n// Granularity-Aware Extend\n// ============================================================================\n\n/**\n * Extend the head position according to the current granularity.\n * For word granularity: snaps to word boundaries.\n * For line granularity: snaps to line boundaries.\n */\nfunction extendByGranularity(\n col: number,\n row: number,\n anchor: SelectionPosition,\n granularity: SelectionGranularity,\n buffer?: TerminalBuffer,\n): SelectionPosition {\n if (granularity === \"character\" || !buffer) {\n return { col, row }\n }\n\n if (granularity === \"word\") {\n const { startCol, endCol } = findWordBoundary(buffer, col, row)\n // Extend towards the anchor direction\n const isForward = row > anchor.row || (row === anchor.row && col >= anchor.col)\n return isForward ? { col: endCol, row } : { col: startCol, row }\n }\n\n if (granularity === \"line\") {\n const { startCol, endCol } = findLineBoundary(buffer, row)\n const isForward = row > anchor.row || (row === anchor.row && col >= anchor.col)\n return isForward ? { col: endCol, row } : { col: startCol, row }\n }\n\n return { col, row }\n}\n\n// ============================================================================\n// State\n// ============================================================================\n\nexport function createTerminalSelectionState(): TerminalSelectionState {\n return { range: null, selecting: false, source: null, granularity: \"character\", scope: null }\n}\n\n// ============================================================================\n// Update\n// ============================================================================\n\n/**\n * Clamp a position to a scope boundary.\n */\nfunction clampToScope(col: number, row: number, scope: SelectionScope | null): SelectionPosition {\n if (!scope) return { col, row }\n return {\n col: Math.max(scope.left, Math.min(scope.right, col)),\n row: Math.max(scope.top, Math.min(scope.bottom, row)),\n }\n}\n\nexport function terminalSelectionUpdate(\n action: SelectionAction,\n state: TerminalSelectionState,\n): [TerminalSelectionState, SelectionEffect[]] {\n switch (action.type) {\n case \"start\": {\n const scope = action.scope ?? null\n const pos = clampToScope(action.col, action.row, scope)\n return [\n {\n range: { anchor: pos, head: pos },\n selecting: true,\n source: action.source ?? \"mouse\",\n granularity: \"character\",\n scope,\n },\n [{ type: \"render\" }],\n ]\n }\n\n case \"startWord\": {\n const scope = action.scope ?? null\n const { startCol, endCol } = findWordBoundary(action.buffer, action.col, action.row)\n const anchorPos = clampToScope(startCol, action.row, scope)\n const headPos = clampToScope(endCol, action.row, scope)\n return [\n {\n range: { anchor: anchorPos, head: headPos },\n selecting: true,\n source: action.source ?? \"mouse\",\n granularity: \"word\",\n scope,\n },\n [{ type: \"render\" }],\n ]\n }\n\n case \"startLine\": {\n const scope = action.scope ?? null\n const { startCol, endCol } = findLineBoundary(action.buffer, action.row)\n const anchorPos = clampToScope(startCol, action.row, scope)\n const headPos = clampToScope(endCol, action.row, scope)\n return [\n {\n range: { anchor: anchorPos, head: headPos },\n selecting: true,\n source: action.source ?? \"mouse\",\n granularity: \"line\",\n scope,\n },\n [{ type: \"render\" }],\n ]\n }\n\n case \"extend\": {\n if (!state.selecting) return [state, []]\n const extended = extendByGranularity(\n action.col,\n action.row,\n state.range!.anchor,\n state.granularity,\n action.buffer,\n )\n const head = clampToScope(extended.col, extended.row, state.scope)\n return [\n {\n ...state,\n range: { anchor: state.range!.anchor, head },\n selecting: true,\n },\n [{ type: \"render\" }],\n ]\n }\n\n case \"finish\": {\n if (!state.range) return [{ ...state, selecting: false }, []]\n return [{ ...state, range: state.range, selecting: false }, []]\n }\n\n case \"clear\": {\n const hadRange = state.range !== null\n return [createTerminalSelectionState(), hadRange ? [{ type: \"render\" }] : []]\n }\n }\n}\n\n// ============================================================================\n// Range Normalization\n// ============================================================================\n\nexport function normalizeRange(range: SelectionRange): {\n startRow: number\n startCol: number\n endRow: number\n endCol: number\n} {\n const { anchor, head } = range\n\n if (anchor.row < head.row || (anchor.row === head.row && anchor.col <= head.col)) {\n return { startRow: anchor.row, startCol: anchor.col, endRow: head.row, endCol: head.col }\n }\n\n return { startRow: head.row, startCol: head.col, endRow: anchor.row, endCol: anchor.col }\n}\n\n// ============================================================================\n// Text Extraction\n// ============================================================================\n\nexport interface ExtractTextOptions {\n /** When true, skip cells that don't have SELECTABLE_FLAG set */\n respectSelectableFlag?: boolean\n /** Row metadata for soft-wrap handling and precise trailing space trimming */\n rowMetadata?: readonly RowMetadata[]\n /**\n * Contain scope. When set, every row's col range is clamped to\n * `[scope.left, scope.right]` so the extracted text cannot include cells\n * outside the `userSelect=\"contain\"` ancestor's rect — even across the\n * interior rows of a multi-row selection.\n */\n scope?: SelectionScope | null\n}\n\n/**\n * Extract text from a buffer within a selection range.\n *\n * Handles:\n * - Soft-wrap joining (via RowMetadata.softWrapped)\n * - Trailing space trimming (via RowMetadata.lastContentCol or content scan)\n * - Blank line preservation within selection\n * - Wide-char continuation cell skipping\n * - SELECTABLE_FLAG filtering (when respectSelectableFlag is true)\n * - Contain-scope clipping (when options.scope is set)\n */\nexport function extractText(buffer: TerminalBuffer, range: SelectionRange, options?: ExtractTextOptions): string {\n const { startRow, startCol, endRow, endCol } = normalizeRange(range)\n const respectSelectable = options?.respectSelectableFlag ?? false\n const rowMeta = options?.rowMetadata\n const scope = options?.scope\n\n const parts: string[] = []\n\n for (let row = startRow; row <= endRow; row++) {\n let colStart = row === startRow ? startCol : 0\n let colEnd = row === endRow ? endCol : buffer.width - 1\n // Clip to contain scope on every row, not just the anchor/head rows.\n // Without this, multi-row selections inside a contain ancestor would\n // still grab full-width cells on interior rows.\n if (scope) {\n colStart = Math.max(colStart, scope.left)\n colEnd = Math.min(colEnd, scope.right)\n if (colStart > colEnd) {\n // This row is entirely outside the scope — emit an empty line to\n // preserve row counts, then continue.\n const meta = rowMeta?.[row]\n if (!(meta?.softWrapped && row < endRow)) {\n parts.push(\"\")\n if (row < endRow) parts.push(\"\\n\")\n }\n continue\n }\n }\n\n let line = \"\"\n for (let col = colStart; col <= colEnd; col++) {\n // Skip wide-char continuation cells\n if (buffer.isCellContinuation(col, row)) continue\n\n // Skip non-selectable cells when flag checking is enabled\n if (respectSelectable && !buffer.isCellSelectable(col, row)) continue\n\n line += buffer.getCellChar(col, row)\n }\n\n // Trim trailing spaces using lastContentCol if available, otherwise fallback\n const meta = rowMeta?.[row]\n if (meta && meta.lastContentCol >= 0) {\n // Compute how much of the line is trailing whitespace\n // lastContentCol is the rightmost col with non-space content\n const effectiveEnd = row === endRow ? endCol : buffer.width - 1\n const trailingCols = effectiveEnd - meta.lastContentCol\n if (trailingCols > 0 && line.length > 0) {\n // Trim up to trailingCols chars of trailing spaces\n line = line.replace(/\\s+$/, \"\")\n }\n } else {\n line = line.replace(/\\s+$/, \"\")\n }\n\n // Preserve blank lines within selection (don't drop them)\n // but join soft-wrapped lines without a newline\n if (meta?.softWrapped && row < endRow) {\n parts.push(line)\n } else {\n parts.push(line)\n // Add newline separator unless this is the last row\n if (row < endRow) {\n parts.push(\"\\n\")\n }\n }\n }\n\n return parts.join(\"\")\n}\n","/**\n * SelectionFeature — service wrapping the headless selection machine.\n *\n * Connects the pure `terminalSelectionUpdate` state machine to ag-term's\n * buffer for text extraction, clipboard for copy-on-select, and the\n * input router's invalidate callback for render triggering.\n *\n * Mouse event handling:\n * - mousedown → start selection (character granularity)\n * - mousemove while selecting → extend selection range\n * - mouseup → finish selection, copy to clipboard if available\n *\n * The feature is created by withDomEvents and registered in the\n * CapabilityRegistry under SELECTION_CAPABILITY.\n */\n\nimport {\n terminalSelectionUpdate,\n createTerminalSelectionState,\n extractText,\n type TerminalSelectionState,\n type SelectionRange,\n type SelectionEffect,\n} from \"@silvery/headless/selection\"\nimport type { TerminalBuffer } from \"../buffer\"\nimport type { ClipboardCapability } from \"./clipboard-capability\"\nimport { extractHtml } from \"../extract-html\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Observable selection state + mouse handlers. */\nexport interface SelectionFeature {\n /** Current selection state (getter). */\n readonly state: TerminalSelectionState\n\n /** Subscribe to state changes. Returns an unsubscribe function. */\n subscribe(listener: () => void): () => void\n\n /** Handle mouse down — start selection. */\n handleMouseDown(col: number, row: number, altKey: boolean): void\n\n /** Handle mouse move — extend selection while dragging. */\n handleMouseMove(col: number, row: number): void\n\n /** Handle mouse up — finish selection, trigger copy. */\n handleMouseUp(col: number, row: number): void\n\n /** Programmatically set a selection range (or null to clear). */\n setRange(range: SelectionRange | null): void\n\n /** Clear the current selection. */\n clear(): void\n\n /** Clean up resources. */\n dispose(): void\n}\n\n/**\n * Options for creating a bridge SelectionFeature that delegates to\n * an external state owner (e.g., create-app's inline selection state).\n */\nexport interface SelectionBridgeOptions {\n /** Get the current selection state. */\n getState: () => TerminalSelectionState\n /** Subscribe to state changes. Returns an unsubscribe function. */\n subscribe: (listener: () => void) => () => void\n /** Set a selection range programmatically (used by copy-mode). */\n setRange: (range: SelectionRange | null) => void\n /** Clear the selection. */\n clear: () => void\n}\n\n/** Options for creating a SelectionFeature. */\nexport interface SelectionFeatureOptions {\n /** Terminal buffer to extract text from (required for mouse selection / copy). */\n buffer?: TerminalBuffer\n /** Optional clipboard capability for copy-on-select. */\n clipboard?: ClipboardCapability\n /** Callback to trigger a render pass after state changes. */\n invalidate: () => void\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Create a SelectionFeature that wraps the headless selection machine.\n *\n * The feature manages subscriptions, processes effects from the machine,\n * and coordinates with clipboard and render invalidation.\n */\nexport function createSelectionFeature(options: SelectionFeatureOptions): SelectionFeature {\n const { buffer, clipboard, invalidate } = options\n\n let selectionState = createTerminalSelectionState()\n const listeners = new Set<() => void>()\n\n function notifyListeners(): void {\n for (const listener of listeners) {\n listener()\n }\n }\n\n function processEffects(effects: SelectionEffect[], richRange?: SelectionRange | null): void {\n for (const effect of effects) {\n if (effect.type === \"render\") {\n invalidate()\n } else if (effect.type === \"copy\" && clipboard) {\n if (clipboard.copyRich && richRange && buffer) {\n const html = extractHtml(buffer, richRange)\n clipboard.copyRich(effect.text, html)\n } else {\n clipboard.copy(effect.text)\n }\n }\n }\n }\n\n function updateState(\n newState: TerminalSelectionState,\n effects: SelectionEffect[],\n richRange?: SelectionRange | null,\n ): void {\n selectionState = newState\n notifyListeners()\n processEffects(effects, richRange)\n }\n\n return {\n get state(): TerminalSelectionState {\n return selectionState\n },\n\n subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n\n handleMouseDown(col: number, row: number, _altKey: boolean): void {\n const [newState, effects] = terminalSelectionUpdate({ type: \"start\", col, row, source: \"mouse\" }, selectionState)\n updateState(newState, effects)\n },\n\n handleMouseMove(col: number, row: number): void {\n if (!selectionState.selecting) return\n const [newState, effects] = terminalSelectionUpdate({ type: \"extend\", col, row, buffer: buffer! }, selectionState)\n updateState(newState, effects)\n },\n\n handleMouseUp(_col: number, _row: number): void {\n if (!selectionState.selecting) return\n const [newState, effects] = terminalSelectionUpdate({ type: \"finish\" }, selectionState)\n\n // Extract text and copy to clipboard on mouse up\n const copyEffects = [...effects]\n if (newState.range && clipboard && buffer) {\n const text = extractText(buffer, newState.range)\n if (text.length > 0) {\n copyEffects.push({ type: \"copy\", text })\n }\n }\n\n updateState(newState, copyEffects, newState.range)\n },\n\n setRange(range: SelectionRange | null): void {\n if (range === null) {\n const [newState, effects] = terminalSelectionUpdate({ type: \"clear\" }, selectionState)\n updateState(newState, effects)\n } else {\n // Set range by starting at anchor and extending to head\n const [startState, startEffects] = terminalSelectionUpdate(\n { type: \"start\", col: range.anchor.col, row: range.anchor.row, source: \"keyboard\" },\n selectionState,\n )\n const [extendState, extendEffects] = terminalSelectionUpdate(\n { type: \"extend\", col: range.head.col, row: range.head.row },\n startState,\n )\n const [finishState, finishEffects] = terminalSelectionUpdate({ type: \"finish\" }, extendState)\n updateState(finishState, [...startEffects, ...extendEffects, ...finishEffects])\n }\n },\n\n clear(): void {\n const [newState, effects] = terminalSelectionUpdate({ type: \"clear\" }, selectionState)\n updateState(newState, effects)\n },\n\n dispose(): void {\n selectionState = createTerminalSelectionState()\n listeners.clear()\n },\n }\n}\n\n// ============================================================================\n// Bridge Implementation\n// ============================================================================\n\n/**\n * Create a bridge SelectionFeature that delegates to an external state owner.\n *\n * Used by create-app to expose its inline selection state to React hooks\n * (useSelection) and copy-mode (which calls setRange/clear) without\n * duplicating the state machine. Mouse handlers are no-ops — the external\n * owner (create-app's event loop) handles mouse events directly.\n */\nexport function createSelectionBridge(options: SelectionBridgeOptions): SelectionFeature {\n return {\n get state(): TerminalSelectionState {\n return options.getState()\n },\n\n subscribe(listener: () => void): () => void {\n return options.subscribe(listener)\n },\n\n // Mouse handlers are no-ops — create-app handles mouse events directly.\n handleMouseDown(_col: number, _row: number, _altKey: boolean): void {},\n handleMouseMove(_col: number, _row: number): void {},\n handleMouseUp(_col: number, _row: number): void {},\n\n setRange(range: SelectionRange | null): void {\n options.setRange(range)\n },\n\n clear(): void {\n options.clear()\n },\n\n dispose(): void {},\n }\n}\n","/**\n * DEC Width Mode Detection (xterm patch #407)\n *\n * Queries the terminal for its character width settings using DECRQM\n * (DEC Private Mode Request). This replaces guesswork with definitive\n * answers from the terminal itself.\n *\n * Modes:\n * - 1020: UTF-8 mode\n * - 1021: CJK ambiguous width (1 or 2 cells)\n * - 1022: Emoji width (1 or 2 cells)\n * - 1023: Private-use area width (1 or 2 cells)\n *\n * Protocol:\n * - Query: CSI ? {mode} $ p (DECRQM)\n * - Response: CSI ? {mode} ; {Ps} $ y (DECRPM)\n *\n * Where Ps is:\n * 1 = set (enabled / wide / 2-cell)\n * 2 = reset (disabled / narrow / 1-cell)\n * 0 = not recognized\n * 3 = permanently set\n * 4 = permanently reset\n *\n * @see https://invisible-island.net/xterm/ctlseqs/ctlseqs.html\n */\n\n/** Well-known xterm width DEC mode numbers. */\nexport const WidthMode = {\n /** UTF-8 mode */\n UTF8: 1020,\n /** CJK ambiguous character width */\n CJK_WIDTH: 1021,\n /** Emoji width */\n EMOJI_WIDTH: 1022,\n /** Private-use area width */\n PRIVATE_USE_WIDTH: 1023,\n} as const\n\n/** Terminal-reported character width configuration. */\nexport interface TerminalWidthConfig {\n /** Whether terminal uses UTF-8 mode */\n utf8: boolean\n /** How terminal handles CJK ambiguous width (1 or 2) */\n cjkWidth: 1 | 2\n /** How terminal handles emoji width (1 or 2) */\n emojiWidth: 1 | 2\n /** How terminal handles private-use area width (1 or 2) */\n privateUseWidth: 1 | 2\n}\n\n/** Width detector with async detect() and cleanup. */\nexport interface WidthDetector {\n /** Detected configuration (null until detection completes) */\n readonly config: TerminalWidthConfig | null\n /** Query terminal for width settings */\n detect(): Promise<TerminalWidthConfig>\n /** Clean up resources */\n dispose(): void\n}\n\n/** Options for creating a width detector. */\nexport interface WidthDetectorOptions {\n /** Write data to the terminal */\n write: (data: string) => void\n /** Subscribe to terminal input data; returns unsubscribe function */\n onData: (handler: (data: string) => void) => () => void\n /** Per-mode timeout in milliseconds (default: 200) */\n timeoutMs?: number\n}\n\n/** Default configuration when detection fails or times out. */\nexport const DEFAULT_WIDTH_CONFIG: TerminalWidthConfig = {\n utf8: true,\n cjkWidth: 1,\n emojiWidth: 2,\n privateUseWidth: 1,\n}\n\n/** Regex for DECRPM response: CSI ? mode ; Ps $ y */\nconst DECRPM_RE = /\\x1b\\[\\?(\\d+);(\\d+)\\$y/g\n\n/**\n * Parse a DECRPM response value into a boolean (set/reset).\n * 1 and 3 (permanently set) = true, everything else = false.\n */\nfunction isSet(ps: number): boolean {\n return ps === 1 || ps === 3\n}\n\n/**\n * Query a single DEC width mode via DECRQM.\n * Returns the Ps value from the DECRPM response, or null on timeout.\n */\nfunction queryWidthMode(\n write: (data: string) => void,\n onData: (handler: (data: string) => void) => () => void,\n mode: number,\n timeoutMs: number,\n): Promise<number | null> {\n return new Promise<number | null>((resolve) => {\n let timer: ReturnType<typeof setTimeout> | null = null\n let unsubscribe: (() => void) | null = null\n let buffer = \"\"\n\n function cleanup() {\n if (timer !== null) {\n clearTimeout(timer)\n timer = null\n }\n if (unsubscribe !== null) {\n unsubscribe()\n unsubscribe = null\n }\n }\n\n unsubscribe = onData((data: string) => {\n buffer += data\n // Try to find DECRPM response for our mode\n DECRPM_RE.lastIndex = 0\n let match: RegExpExecArray | null\n while ((match = DECRPM_RE.exec(buffer)) !== null) {\n const reportedMode = parseInt(match[1]!, 10)\n if (reportedMode === mode) {\n const ps = parseInt(match[2]!, 10)\n cleanup()\n resolve(ps)\n return\n }\n }\n })\n\n timer = setTimeout(() => {\n cleanup()\n resolve(null)\n }, timeoutMs)\n\n // Send DECRQM query: CSI ? {mode} $ p\n write(`\\x1b[?${mode}$p`)\n })\n}\n\n/**\n * Apply detected width configuration to terminal capabilities.\n *\n * Maps DEC width modes to the existing TerminalCaps fields:\n * - emojiWidth=2 → textEmojiWide=true\n * - privateUseWidth=2 → textSizingSupported=true (PUA treated as 2-wide)\n *\n * Returns a new caps object with the detected overrides applied.\n * CJK width and UTF-8 mode are informational — they don't yet map to\n * caps fields but are available in the TerminalWidthConfig for consumers.\n */\nexport function applyWidthConfig<T extends { textEmojiWide: boolean; textSizingSupported: boolean }>(\n caps: T,\n config: TerminalWidthConfig,\n): T {\n return {\n ...caps,\n textEmojiWide: config.emojiWidth === 2,\n textSizingSupported: config.privateUseWidth === 2,\n }\n}\n\n/**\n * Create a width detector that queries the terminal for DEC modes 1020-1023.\n *\n * @example\n * ```ts\n * const detector = createWidthDetector({\n * write: (data) => process.stdout.write(data),\n * onData: (handler) => {\n * process.stdin.on('data', (chunk) => handler(chunk.toString()))\n * return () => process.stdin.removeListener('data', handler)\n * },\n * })\n *\n * const config = await detector.detect()\n * console.log(config.emojiWidth) // 1 or 2\n * detector.dispose()\n * ```\n */\nexport function createWidthDetector(options: WidthDetectorOptions): WidthDetector {\n const { write, onData, timeoutMs = 200 } = options\n let config: TerminalWidthConfig | null = null\n let disposed = false\n\n return {\n get config() {\n return config\n },\n\n async detect(): Promise<TerminalWidthConfig> {\n if (disposed) return config ?? { ...DEFAULT_WIDTH_CONFIG }\n if (config !== null) return config\n\n // Query all 4 modes sequentially (each waits for its response)\n const utf8Ps = await queryWidthMode(write, onData, WidthMode.UTF8, timeoutMs)\n const cjkPs = await queryWidthMode(write, onData, WidthMode.CJK_WIDTH, timeoutMs)\n const emojiPs = await queryWidthMode(write, onData, WidthMode.EMOJI_WIDTH, timeoutMs)\n const puaPs = await queryWidthMode(write, onData, WidthMode.PRIVATE_USE_WIDTH, timeoutMs)\n\n config = {\n utf8: utf8Ps !== null ? isSet(utf8Ps) : DEFAULT_WIDTH_CONFIG.utf8,\n cjkWidth: cjkPs !== null ? (isSet(cjkPs) ? 2 : 1) : DEFAULT_WIDTH_CONFIG.cjkWidth,\n emojiWidth: emojiPs !== null ? (isSet(emojiPs) ? 2 : 1) : DEFAULT_WIDTH_CONFIG.emojiWidth,\n privateUseWidth: puaPs !== null ? (isSet(puaPs) ? 2 : 1) : DEFAULT_WIDTH_CONFIG.privateUseWidth,\n }\n\n return config\n },\n\n dispose() {\n disposed = true\n },\n }\n}\n","/**\n * Selection style composition.\n *\n * Applies selection highlight as a cell-style transform in the rendering pipeline,\n * NOT as an ANSI overlay. Selection composes correctly with existing cell styles\n * (fg/bg/attrs), handles already-inverted content, and flows through the normal\n * diff/output renderer.\n *\n * Called after render phase, before output phase.\n */\n\nimport type { Color, TerminalBuffer } from \"./buffer\"\nimport { type SelectionRange, type SelectionScope, normalizeRange } from \"@silvery/headless/selection\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Selection theme tokens. If provided, these override the fallback fg/bg swap.\n */\nexport interface SelectionTheme {\n /** Foreground color for selected text */\n selectionFg?: Color\n /** Background color for selected text */\n selectionBg?: Color\n}\n\n/**\n * A single cell change produced by selection composition.\n * Used as a sparse overlay — only cells within the selection range are affected.\n */\nexport interface SelectionCellChange {\n col: number\n row: number\n fg: Color\n bg: Color\n}\n\n// ============================================================================\n// Style Composition\n// ============================================================================\n\n/**\n * Compute selection style changes for all cells within a selection range.\n *\n * Returns a sparse list of cell changes (fg/bg only). The caller applies these\n * to the buffer before the output phase diffs it. This approach:\n * - Composes correctly with existing cell styles\n * - Handles already-inverted content (swaps back to normal instead of double-inverting)\n * - Respects SELECTABLE_FLAG per cell (skip non-selectable)\n * - Works with the normal diff/output renderer (no separate ANSI pass)\n *\n * @param buffer The rendered buffer (post-render-phase)\n * @param selection Current selection range, or null\n * @param theme Optional selection theme colors\n * @param respectSelectableFlag When true, skip cells without SELECTABLE_FLAG\n */\nexport function composeSelectionCells(\n buffer: TerminalBuffer,\n selection: SelectionRange | null,\n theme?: SelectionTheme,\n respectSelectableFlag = false,\n scope?: SelectionScope | null,\n): SelectionCellChange[] {\n if (!selection) return []\n\n const { startRow, startCol, endRow, endCol } = normalizeRange(selection)\n const changes: SelectionCellChange[] = []\n\n for (let row = startRow; row <= endRow; row++) {\n let colStart = row === startRow ? startCol : 0\n let colEnd = row === endRow ? endCol : buffer.width - 1\n // Clip to contain scope on every row so selection highlight never paints\n // outside a `userSelect=\"contain\"` ancestor, even on interior rows.\n if (scope) {\n colStart = Math.max(colStart, scope.left)\n colEnd = Math.min(colEnd, scope.right)\n if (colStart > colEnd) continue\n }\n\n for (let col = colStart; col <= colEnd; col++) {\n // Skip continuation cells (second half of wide chars)\n if (buffer.isCellContinuation(col, row)) continue\n\n // Skip non-selectable cells when flag checking is enabled\n if (respectSelectableFlag && !buffer.isCellSelectable(col, row)) continue\n\n const cellFg = buffer.getCellFg(col, row)\n const cellBg = buffer.getCellBg(col, row)\n\n let newFg: Color\n let newBg: Color\n\n if (theme?.selectionBg != null) {\n // Use theme tokens\n newFg = theme.selectionFg ?? cellFg\n newBg = theme.selectionBg\n } else {\n // Fallback: swap fg/bg (handles already-inverted content correctly)\n // If fg is null (default), use a visible fallback\n newFg = cellBg\n newBg = cellFg\n }\n\n changes.push({ col, row, fg: newFg, bg: newBg })\n }\n }\n\n return changes\n}\n\n/**\n * Apply selection style changes to a buffer.\n *\n * Modifies the buffer in-place by setting fg/bg on affected cells.\n * Call this after the render phase and before the output phase.\n *\n * @param buffer The rendered buffer to modify\n * @param changes Cell changes from composeSelectionCells\n */\nexport function applySelectionToBuffer(buffer: TerminalBuffer, changes: SelectionCellChange[]): void {\n for (const change of changes) {\n const cell = buffer.getCell(change.col, change.row)\n buffer.setCell(change.col, change.row, {\n ...cell,\n fg: change.fg,\n bg: change.bg,\n })\n }\n}\n\n// ============================================================================\n// Legacy API (deprecated — kept for backwards compatibility)\n// ============================================================================\n\n/**\n * Generate ANSI sequences to render selection overlay (inverse video on selected cells).\n *\n * @deprecated Use composeSelectionCells + applySelectionToBuffer instead.\n * This approach re-emits characters with SGR 7m, which doesn't compose correctly\n * with existing cell styles. The new style composition approach modifies cell data\n * before the output phase, producing correct results.\n */\nexport function renderSelectionOverlay(\n selection: SelectionRange | null,\n buffer: TerminalBuffer,\n mode: \"fullscreen\" | \"inline\" = \"fullscreen\",\n scope?: SelectionScope | null,\n): string {\n if (!selection) return \"\"\n\n const { startRow, startCol, endRow, endCol } = normalizeRange(selection)\n let out = \"\"\n\n for (let row = startRow; row <= endRow; row++) {\n let colStart = row === startRow ? startCol : 0\n let colEnd = row === endRow ? endCol : buffer.width - 1\n // Clip to contain scope on every row (see composeSelectionCells above).\n if (scope) {\n colStart = Math.max(colStart, scope.left)\n colEnd = Math.min(colEnd, scope.right)\n }\n\n if (colStart > colEnd) continue\n\n if (mode === \"fullscreen\") {\n out += `\\x1b[${row + 1};${colStart + 1}H`\n }\n\n out += \"\\x1b[7m\"\n for (let col = colStart; col <= colEnd; col++) {\n out += buffer.getCell(col, row).char\n }\n out += \"\\x1b[27m\"\n }\n\n return out\n}\n","/**\n * Virtual scrollback buffer for storing historical rendered content.\n *\n * Used by virtual inline mode to maintain scrollable history while\n * rendering in altscreen (which normally has no scrollback).\n *\n * Implementation uses a circular buffer for O(1) push and bounded memory.\n */\n\nimport { stripAnsi } from \"./unicode\"\n\nexport interface VirtualScrollbackOptions {\n /** Maximum number of lines to store. Default: 10000 */\n maxLines?: number\n}\n\nexport interface VirtualScrollback {\n /** Push rendered lines into history */\n push(lines: string[]): void\n /** Get visible lines at a scroll offset. offset=0 = most recent (bottom) */\n getVisibleRows(offset: number, count: number): string[]\n /** Total number of lines stored */\n readonly totalLines: number\n /** Search for text across all stored lines. Returns indices of matching lines (0 = oldest). */\n search(query: string): number[]\n /** Clear all stored content */\n clear(): void\n}\n\nexport function createVirtualScrollback(options?: VirtualScrollbackOptions): VirtualScrollback {\n const maxLines = options?.maxLines ?? 10_000\n const ansiLines: string[] = new Array(maxLines)\n const plainLines: string[] = new Array(maxLines)\n let head = 0 // next write position\n let count = 0 // total lines stored (capped at maxLines)\n\n return {\n push(lines: string[]): void {\n for (const line of lines) {\n ansiLines[head] = line\n plainLines[head] = stripAnsi(line)\n head = (head + 1) % maxLines\n if (count < maxLines) count++\n }\n },\n\n getVisibleRows(offset: number, rowCount: number): string[] {\n const result: string[] = []\n for (let i = 0; i < rowCount; i++) {\n const logicalIndex = count - offset - rowCount + i\n if (logicalIndex < 0 || logicalIndex >= count) {\n result.push(\"\")\n } else {\n // Convert logical index (0=oldest) to physical position\n const physical = (head - count + logicalIndex + maxLines) % maxLines\n result.push(ansiLines[physical]!)\n }\n }\n return result\n },\n\n get totalLines(): number {\n return count\n },\n\n search(query: string): number[] {\n if (!query) return []\n const lowerQuery = query.toLowerCase()\n const matches: number[] = []\n for (let i = 0; i < count; i++) {\n // logical index i (0=oldest), convert to physical\n const physical = (head - count + i + maxLines) % maxLines\n if (plainLines[physical]!.toLowerCase().includes(lowerQuery)) {\n matches.push(i)\n }\n }\n return matches\n },\n\n clear(): void {\n head = 0\n count = 0\n },\n }\n}\n","/**\n * @silvery/ag-term — Terminal rendering target for silvery.\n *\n * Provides terminal buffer, pipeline, output, input protocols,\n * layout engine, render adapters, and Unicode text utilities.\n *\n * @packageDocumentation\n */\n\n// =============================================================================\n// Ag (tree + layout + render)\n// =============================================================================\n\nexport { createAg } from \"./ag\"\nexport type { Ag, CreateAgOptions, AgLayoutOptions, AgRenderOptions, AgRenderResult } from \"./ag\"\n\n// =============================================================================\n// Plugin Composition (era2a)\n// =============================================================================\n\nexport { create, pipe, from, withAg, withTerm } from \"./compose\"\nexport type { AppBase, AppWithAg, AppWithTerm, PipeBuilder } from \"./compose\"\nexport { withReact } from \"./compose-react\"\nexport { withTest, type AppWithTest } from \"./compose-test\"\n\n// =============================================================================\n// Buffer\n// =============================================================================\n\nexport type { TerminalBuffer, Color } from \"./buffer\"\nexport { colorEquals, DEFAULT_BG, isDefaultBg, createTextFrame } from \"./buffer\"\n\n// =============================================================================\n// Pipeline\n// =============================================================================\n\nexport {\n executeRender,\n silveryBenchStart,\n silveryBenchStop,\n silveryBenchReset,\n type PipelineConfig,\n type ExecuteRenderOptions,\n type SilveryBenchPhases,\n type SilveryBenchOutputDetail,\n} from \"./pipeline\"\nexport {\n outputPhase,\n setOutputCaps,\n createOutputPhase,\n type OutputPhaseFn,\n type OutputCaps,\n} from \"./pipeline/output-phase\"\nexport type { PipelineContext } from \"./pipeline/types\"\n\n// =============================================================================\n// App Types\n// =============================================================================\n\nexport type { App } from \"./app\"\nexport type { BoundTerm } from \"./bound-term\"\n\n// =============================================================================\n// Layout Engine\n// =============================================================================\n\nexport type { LayoutEngine, LayoutConstants } from \"./layout-engine\"\nexport type { LayoutNode, MeasureFunc, MeasureMode } from \"@silvery/ag/layout-types\"\n\n// =============================================================================\n// Render Adapter Types (RenderAdapter itself is internal — not exported)\n// =============================================================================\n\nexport type {\n RenderBuffer,\n RenderStyle,\n TextMeasurer,\n TextMeasureResult,\n TextMeasureStyle,\n BorderChars,\n} from \"./render-adapter\"\n\n// Canvas adapter\nexport { createCanvasAdapter, CanvasRenderBuffer } from \"./adapters/canvas-adapter\"\nexport type { CanvasAdapterConfig } from \"./adapters/canvas-adapter\"\n\n// DOM adapter\nexport { createDOMAdapter, DOMRenderBuffer, injectDOMStyles } from \"./adapters/dom-adapter\"\nexport type { DOMAdapterConfig } from \"./adapters/dom-adapter\"\n\n// =============================================================================\n// ANSI Sanitizer\n// =============================================================================\n\nexport {\n sanitizeAnsi,\n tokenizeAnsi,\n isCSISGR,\n extractColonSGRReplacements,\n createColonSGRTracker,\n} from \"./ansi-sanitize\"\nexport type { AnsiToken, ColonSGRReplacement } from \"./ansi-sanitize\"\n\n// =============================================================================\n// ANSI Escape Sequences / Output\n// =============================================================================\n\nexport {\n ANSI,\n BEL,\n enableMouse,\n disableMouse,\n KittyFlags,\n enableKittyKeyboard,\n disableKittyKeyboard,\n queryKittyKeyboard,\n notify,\n notifyITerm2,\n notifyKitty,\n reportDirectory,\n setWindowTitle,\n setWindowAndIconTitle,\n resetWindowTitle,\n setCursorStyle,\n resetCursorStyle,\n setMouseCursorShape,\n resetMouseCursorShape,\n} from \"./output\"\nexport type { CursorShape, MouseCursorShape } from \"./output\"\n\n// =============================================================================\n// Bracketed Paste\n// =============================================================================\n\nexport {\n enableBracketedPaste,\n disableBracketedPaste,\n parseBracketedPaste,\n createBracketedPasteEvent,\n createInternalPasteEvent,\n PASTE_START,\n PASTE_END,\n} from \"./bracketed-paste\"\nexport type { BracketedPasteResult, PasteEvent } from \"./bracketed-paste\"\n\n// =============================================================================\n// Clipboard\n// =============================================================================\n\nexport { copyToClipboard, requestClipboard, parseClipboardResponse } from \"./clipboard\"\nexport { createOsc52Backend, createInternalClipboardBackend, createCompositeClipboard } from \"./clipboard\"\nexport type { ClipboardData, ClipboardBackend, ClipboardCapabilities } from \"./clipboard\"\n\n// =============================================================================\n// Advanced Clipboard (OSC 5522)\n// =============================================================================\n\nexport {\n createAdvancedClipboard,\n parseOsc5522Response,\n parsePasteData,\n ENABLE_PASTE_EVENTS,\n DISABLE_PASTE_EVENTS,\n} from \"./ansi/advanced-clipboard\"\nexport type { AdvancedClipboard, AdvancedClipboardOptions, ClipboardEntry } from \"./ansi/advanced-clipboard\"\n\n// =============================================================================\n// OSC 4 Palette Color Query/Set\n// =============================================================================\n\nexport { queryPaletteColor, setPaletteColor, parsePaletteResponse, queryMultiplePaletteColors } from \"./osc-palette\"\n\n// =============================================================================\n// OSC 133 Semantic Prompt Markers\n// =============================================================================\n\nexport { OSC133 } from \"./osc-markers\"\n\n// =============================================================================\n// Kitty Protocol Detection\n// =============================================================================\n\nexport { detectKittySupport, detectKittyFromStdio, type KittyDetectResult } from \"./kitty-detect\"\n\n// =============================================================================\n// Kitty Protocol Manager\n// =============================================================================\n\nexport { createKittyManager, type KittyManager, type KittyManagerOptions } from \"./kitty-manager\"\n\n// =============================================================================\n// Terminal Capability Detection\n// =============================================================================\n\nexport { detectTerminalCaps, defaultCaps, type TerminalCaps } from \"./terminal-caps\"\n\n// =============================================================================\n// Terminal Capability Visual Test\n// =============================================================================\n\nexport { runTermtest, TERMTEST_SECTIONS, type TermtestSection, type TermtestOptions } from \"./termtest\"\n\n// =============================================================================\n// Text Sizing (OSC 66)\n// =============================================================================\n\nexport {\n textSized,\n textScaled,\n resetTextScale,\n isPrivateUseArea,\n isTextSizingLikelySupported,\n detectTextSizingSupport,\n} from \"./text-sizing\"\n\n// =============================================================================\n// CSI 6n Cursor Position Query\n// =============================================================================\n\nexport { queryCursorPosition, queryCursorFromStdio } from \"./cursor-query\"\n\n// =============================================================================\n// OSC 10/11/12 Terminal Color Queries\n// =============================================================================\n\nexport {\n queryForegroundColor,\n queryBackgroundColor,\n queryCursorColor,\n setForegroundColor,\n setBackgroundColor,\n setCursorColor,\n resetForegroundColor,\n resetBackgroundColor,\n resetCursorColor,\n detectColorScheme,\n} from \"./terminal-colors\"\n\n// =============================================================================\n// Color Scheme Detection (Mode 2031) — re-exported from @silvery/ansi\n// =============================================================================\n\nexport {\n createColorSchemeDetector,\n parseColorSchemeResponse,\n ENABLE_COLOR_SCHEME_REPORTING,\n DISABLE_COLOR_SCHEME_REPORTING,\n type ColorSchemeDetector,\n type ColorSchemeDetectorOptions,\n} from \"@silvery/ansi\"\n\n// =============================================================================\n// Width Detection (DEC 1020-1023)\n// =============================================================================\n\nexport {\n createWidthDetector,\n applyWidthConfig,\n DEFAULT_WIDTH_CONFIG,\n type WidthDetector,\n type TerminalWidthConfig,\n} from \"./ansi/width-detection\"\n\n// =============================================================================\n// DA1/DA2/DA3 + XTVERSION Device Attribute Queries\n// =============================================================================\n\nexport {\n queryPrimaryDA,\n querySecondaryDA,\n queryTertiaryDA,\n queryTerminalVersion,\n queryDeviceAttributes,\n type DeviceAttributes,\n} from \"./device-attrs\"\n\n// =============================================================================\n// Focus Reporting\n// =============================================================================\n\nexport { enableFocusReporting, disableFocusReporting, parseFocusEvent } from \"./focus-reporting\"\n\n// =============================================================================\n// DECRQM Mode Query\n// =============================================================================\n\nexport { queryMode, queryModes, DecMode } from \"./mode-query\"\n\n// DEC Width Mode Detection additional exports (WidthMode, WidthDetectorOptions)\nexport { WidthMode } from \"./ansi/width-detection\"\nexport type { WidthDetectorOptions } from \"./ansi/width-detection\"\n\n// =============================================================================\n// CSI 14t/18t Pixel and Text Area Size\n// =============================================================================\n\nexport { queryTextAreaPixels, queryTextAreaSize, queryCellSize } from \"./pixel-size\"\n\n// =============================================================================\n// TermDef Resolution\n// =============================================================================\n\n// TermDef and related terminal-specific types\nexport type {\n TermDef,\n RenderOptions as TermDefRenderOptions,\n RenderInstance as TermDefRenderInstance,\n} from \"./term-def\"\n// TermDef resolution — internal. Use createTerm() instead of TermDef.\n// isTerm and createInputEvents are still public utilities.\nexport { isTerm, createInputEvents } from \"./term-def\"\n\n// =============================================================================\n// Hit Registry (Mouse Support) — React-free core only\n// =============================================================================\n//\n// The barrel exports only the pure core (class, types, constants).\n// React hooks and context are available via @silvery/ag-term/hit-registry.\n//\n\nexport { HitRegistry, resetHitRegionIdCounter, Z_INDEX } from \"./hit-registry-core\"\nexport type { HitTarget, HitRegion } from \"./hit-registry-core\"\n\n// =============================================================================\n// Mouse Parsing (SGR mode 1006)\n// =============================================================================\n\nexport { parseMouseSequence, isMouseSequence, type ParsedMouse } from \"./mouse\"\n\n// =============================================================================\n// Mouse Events (DOM-level)\n// =============================================================================\n\nexport {\n hitTest,\n createMouseEvent,\n createWheelEvent,\n dispatchMouseEvent,\n processMouseEvent,\n createMouseEventProcessor,\n checkDoubleClick,\n createDoubleClickState,\n computeEnterLeave,\n resolveNodeDraggable,\n type SilveryMouseEvent,\n type SilveryWheelEvent,\n type MouseEventProcessorOptions,\n type MouseEventProcessorState,\n type KeyboardModifierState,\n updateKeyboardModifiers,\n} from \"./mouse-events\"\nexport type { MouseEventProps } from \"@silvery/ag/mouse-event-types\"\n\n// =============================================================================\n// Non-TTY Utilities\n// =============================================================================\n\nexport { isTTY, resolveNonTTYMode, stripAnsi } from \"./non-tty\"\nexport type { NonTTYOptions, ResolvedNonTTYMode } from \"./non-tty\"\n\n// =============================================================================\n// DevTools — available via @silvery/ag-term/devtools (not re-exported here to\n// keep this barrel React-free; devtools imports the React reconciler)\n// =============================================================================\n\n// =============================================================================\n// Inspector\n// =============================================================================\n\nexport {\n enableInspector,\n disableInspector,\n isInspectorEnabled,\n inspectTree,\n inspectFrame,\n autoEnableInspector,\n} from \"./inspector\"\nexport type { InspectorOptions } from \"./inspector\"\n\n// =============================================================================\n// Unicode Text Utilities\n// =============================================================================\n\nexport {\n // Measurement\n displayWidth,\n displayWidthAnsi,\n measureText,\n // Manipulation\n wrapText,\n truncateText,\n padText,\n constrainText,\n sliceByWidth,\n sliceByWidthRange,\n sliceByWidthFromEnd,\n // ANSI handling\n hasAnsi,\n parseAnsiText,\n stripAnsi as stripAnsiUnicode,\n truncateAnsi,\n // Grapheme operations\n splitGraphemes,\n graphemeCount,\n graphemeWidth,\n // Character detection\n isWideGrapheme,\n isZeroWidthGrapheme,\n isCJK,\n isLikelyEmoji,\n hasWideCharacters,\n hasZeroWidthCharacters,\n // Emoji presentation\n ensureEmojiPresentation,\n // Text sizing state\n setTextSizingEnabled,\n isTextSizingEnabled,\n // Text-presentation emoji width\n setTextEmojiWide,\n // Buffer writing\n writeTextToBuffer,\n writeTextTruncated,\n writeLinesToBuffer,\n // Utilities\n normalizeText,\n getFirstCodePoint,\n} from \"./unicode\"\nexport type { StyledSegment } from \"./unicode\"\n\n// Width measurer factory\nexport { createWidthMeasurer, createMeasurer, runWithMeasurer, type Measurer, type WidthMeasurer } from \"./unicode\"\n\n// Measurer composition (term + measurement)\nexport { withMeasurer, createPipeline, type MeasuredTerm } from \"./measurer\"\n\n// withRender plugin — available via @silvery/create/with-render (not re-exported\n// here to keep this barrel React-free; withRender's renderStatic() pulls React)\n\n// =============================================================================\n// Scroll Utilities\n// =============================================================================\n\nexport { calcEdgeBasedScrollOffset } from \"./scroll-utils\"\n\n// Scroll Region Optimization (DECSTBM)\nexport {\n setScrollRegion,\n resetScrollRegion,\n scrollUp,\n scrollDown,\n moveCursor,\n supportsScrollRegions,\n} from \"./scroll-region\"\nexport type { ScrollRegionConfig } from \"./scroll-region\"\n\n// =============================================================================\n// Pane Manager\n// =============================================================================\n\nexport type { LayoutNode as SplitLayoutNode } from \"./pane-manager\"\nexport {\n createLeaf,\n splitPane,\n removePane,\n getPaneIds,\n findAdjacentPane,\n resizeSplit,\n swapPanes,\n getTabOrder as getSplitTabOrder,\n} from \"./pane-manager\"\n\n// =============================================================================\n// Scheduler\n// =============================================================================\n\nexport { IncrementalRenderMismatchError } from \"./errors\"\n\n// =============================================================================\n// ANSI Primitives (merged from @silvery/ansi)\n// =============================================================================\n\n// Term factory and lazy instance\nexport { createTerm, term } from \"./ansi/index\"\nexport type { Term, StyleChain, TermEmulatorBackend } from \"./ansi/index\"\n\n// Console patching\nexport { patchConsole } from \"./ansi/index\"\nexport type { PatchedConsole, PatchConsoleOptions, ConsoleStats } from \"./ansi/index\"\n\n// Output guard (alt screen protection)\nexport { createOutputGuard } from \"./ansi/index\"\nexport type { OutputGuard, OutputGuardOptions } from \"./ansi/index\"\n\n// Types\nexport type {\n UnderlineStyle,\n RGB,\n ColorLevel,\n Color as AnsiColor,\n AnsiColorName,\n StyleOptions,\n ConsoleMethod,\n ConsoleEntry,\n CreateTermOptions,\n} from \"./ansi/index\"\n\n// Detection\nexport { detectCursor, detectInput, detectColor, detectUnicode, detectExtendedUnderline } from \"./ansi/index\"\n\n// Utilities\nexport { ANSI_REGEX, displayLength } from \"./ansi/index\"\n\n// Underline functions\nexport {\n underline as ansiUnderline,\n curlyUnderline,\n dottedUnderline,\n dashedUnderline,\n doubleUnderline,\n underlineColor,\n styledUnderline,\n} from \"./ansi/index\"\n\n// Hyperlinks\nexport { hyperlink } from \"./ansi/index\"\n\n// ANSI control helpers (re-exported as ansi* to avoid conflicts with term's own)\nexport {\n enterAltScreen,\n leaveAltScreen,\n clearScreen,\n clearLine,\n cursorTo,\n cursorHome,\n cursorHide,\n cursorShow,\n cursorStyle as ansiCursorStyle,\n setTitle,\n enableSyncUpdate,\n disableSyncUpdate,\n} from \"./ansi/index\"\n\n// Background override\nexport { BG_OVERRIDE_CODE, bgOverride } from \"./ansi/index\"\n\n// =============================================================================\n// Interactive Signals (re-exported from @silvery/ag)\n// =============================================================================\n\nexport {\n ensureInteractiveState,\n setHovered,\n setArmed,\n setSelected,\n setFocused,\n setDropTarget,\n clearInteractiveState,\n} from \"@silvery/ag/interactive-signals\"\n\n// =============================================================================\n// Selection (re-exported from @silvery/headless)\n// =============================================================================\n\nexport {\n terminalSelectionUpdate,\n createTerminalSelectionState,\n normalizeRange,\n extractText,\n findWordBoundary,\n findLineBoundary,\n type TerminalSelectionState,\n type SelectionRange,\n type SelectionPosition,\n type SelectionAction,\n type SelectionEffect,\n type SelectionGranularity,\n} from \"@silvery/headless/selection\"\n\nexport { renderSelectionOverlay } from \"./selection-renderer\"\nexport { extractHtml } from \"./extract-html\"\n\n// =============================================================================\n// Find (re-exported from @silvery/headless)\n// =============================================================================\n\nexport {\n findUpdate,\n createFindState,\n searchBuffer,\n type FindState,\n type FindMatch,\n type FindResult,\n type FindProvider,\n type FindAction,\n type FindEffect,\n} from \"@silvery/headless/find\"\n\n// FindFeature service\nexport { createFindFeature } from \"./find-feature\"\nexport type { FindFeature, FindFeatureOptions } from \"./find-feature\"\n\n// =============================================================================\n// Copy Mode (re-exported from @silvery/headless)\n// =============================================================================\n\nexport {\n copyModeUpdate,\n createCopyModeState,\n type CopyModeState,\n type CopyModePosition,\n type CopyModeBuffer,\n type CopyModeAction,\n type CopyModeEffect,\n} from \"@silvery/headless/copy-mode\"\n\n// =============================================================================\n// Pointer State Machine (re-exported from @silvery/headless)\n// =============================================================================\n\nexport {\n pointerStateUpdate,\n createPointerState,\n createPointerDoubleClickState,\n checkPointerDoubleClick,\n DRAG_THRESHOLD,\n type PointerState,\n type PointerAction,\n type PointerEffect,\n type Position as PointerPosition,\n type PointerDoubleClickState,\n} from \"@silvery/headless/pointer\"\n\n// =============================================================================\n// Drag Events\n// =============================================================================\n\nexport {\n createDragEvent,\n createDragState,\n isDropTarget,\n findDropTarget,\n type DragState,\n type DragEvent,\n type DragEventProps,\n} from \"./drag-events\"\n\n// =============================================================================\n// Virtual Scrollback\n// =============================================================================\n\nexport { createVirtualScrollback, type VirtualScrollback, type VirtualScrollbackOptions } from \"./virtual-scrollback\"\n\n// =============================================================================\n// History Buffer\n// =============================================================================\n\nexport { createHistoryBuffer, createHistoryItem, type HistoryItem, type HistoryBuffer } from \"./history-buffer\"\n\n// =============================================================================\n// Viewport Compositor\n// =============================================================================\n\nexport { composeViewport, type ViewportCompositorConfig, type ComposedViewport } from \"./viewport-compositor\"\n\n// =============================================================================\n// List Document\n// =============================================================================\n\nexport { createListDocument, type ListDocument, type DocumentSource, type LiveItemBlock } from \"./list-document\"\n\n// =============================================================================\n// Text Surface\n// =============================================================================\n\nexport { createTextSurface, type TextSurface, type SurfaceCapabilities } from \"./text-surface\"\n\n// =============================================================================\n// Search Overlay\n// =============================================================================\n\nexport {\n createSearchState,\n searchUpdate,\n renderSearchBar,\n type SearchState,\n type SearchMatch,\n type SearchAction,\n type SearchEffect,\n} from \"./search-overlay\"\n","/**\n * Pure layout function for silvery-loop.\n *\n * Takes a React element and dimensions, returns an immutable Buffer.\n * This is Layer 0 - no runtime, no events, just pure rendering.\n */\n\nimport { createTerm } from \"../ansi/index\"\nimport React, { type ReactElement } from \"react\"\nimport { bufferToStyledText, bufferToText } from \"../buffer\"\nimport { StdoutContext, StderrContext, TermContext } from \"@silvery/ag-react/context\"\nimport { ensureDefaultLayoutEngine, isLayoutEngineInitialized } from \"../layout-engine\"\nimport { executeRender } from \"../pipeline\"\nimport { createContainer, createFiberRoot, getContainerRoot, reconciler } from \"@silvery/ag-react/reconciler\"\nimport type { Buffer, Dims } from \"./types\"\n\n/**\n * Options for the layout function.\n */\nexport interface LayoutOptions {\n /** Skip layout notifications (for static renders). Default: true */\n skipLayoutNotifications?: boolean\n /** Strip ANSI codes for plain text output. Default: false */\n plain?: boolean\n}\n\n/**\n * Ensure layout engine is initialized.\n * Must be called before layout() in async contexts.\n */\nexport async function ensureLayoutEngine(): Promise<void> {\n if (!isLayoutEngineInitialized()) {\n await ensureDefaultLayoutEngine()\n }\n}\n\n/**\n * Pure layout function - renders a React element to a Buffer.\n *\n * IMPORTANT: Call ensureLayoutEngine() first in async contexts.\n * The layout engine must be initialized before calling this.\n *\n * @param element React element to render\n * @param dims Terminal dimensions\n * @param options Layout options\n * @returns Immutable Buffer with text, ansi, and nodes\n *\n * @example\n * ```typescript\n * import { layout, ensureLayoutEngine } from '@silvery/ag-term/runtime'\n *\n * await ensureLayoutEngine()\n * const buffer = layout(<Text>Hello</Text>, { cols: 80, rows: 24 })\n * console.log(buffer.text) // \"Hello\"\n * ```\n */\nexport function layout(element: ReactElement, dims: Dims, options: LayoutOptions = {}): Buffer {\n if (!isLayoutEngineInitialized()) {\n throw new Error(\"Layout engine not initialized. Call ensureLayoutEngine() first.\")\n }\n\n const { skipLayoutNotifications = true, plain = false } = options\n const { cols: width, rows: height } = dims\n\n // Create container for React reconciliation\n const container = createContainer(() => {})\n\n // Create fiber root\n const fiberRoot = createFiberRoot(container)\n\n // Create minimal mock stdout for components that use useStdout\n const mockStdout = {\n columns: width,\n rows: height,\n write: () => true,\n isTTY: false,\n on: () => mockStdout,\n off: () => mockStdout,\n once: () => mockStdout,\n removeListener: () => mockStdout,\n addListener: () => mockStdout,\n } as unknown as NodeJS.WriteStream\n\n // Create mock term for components that use useTerm()\n const mockTerm = createTerm({ color: plain ? null : \"truecolor\" })\n\n // Wrap with minimal contexts (no input handling needed)\n const wrapped = React.createElement(\n TermContext.Provider,\n { value: mockTerm },\n React.createElement(\n StdoutContext.Provider,\n {\n value: {\n stdout: mockStdout,\n write: () => {},\n },\n },\n React.createElement(\n StderrContext.Provider,\n {\n value: {\n stderr: process.stderr,\n write: (data: string) => {\n process.stderr.write(data)\n },\n },\n },\n element,\n ),\n ),\n )\n\n // Mount, render, and unmount - all without act warnings\n withoutActWarnings(() => {\n reconciler.updateContainerSync(wrapped, fiberRoot, null, null)\n reconciler.flushSyncWork()\n })\n\n // Execute render pipeline (skip layout notifications for static renders)\n const root = getContainerRoot(container)\n const { buffer: termBuffer } = executeRender(root, width, height, null, {\n skipLayoutNotifications,\n })\n\n // Get text representations\n const text = bufferToText(termBuffer)\n const ansi = bufferToStyledText(termBuffer)\n\n // Unmount (cleanup)\n withoutActWarnings(() => {\n reconciler.updateContainerSync(null, fiberRoot, null, null)\n reconciler.flushSyncWork()\n })\n\n return {\n text,\n ansi,\n nodes: root,\n _buffer: termBuffer,\n }\n}\n\n/**\n * Synchronous layout - assumes engine is already initialized.\n * Throws if engine not ready.\n */\nexport function layoutSync(element: ReactElement, dims: Dims, options: LayoutOptions = {}): Buffer {\n return layout(element, dims, options)\n}\n\n/**\n * Run a function with React act warnings disabled.\n * Used for static renders where we don't use act() and don't need layout feedback.\n */\nfunction withoutActWarnings(fn: () => void): void {\n const prev = (globalThis as any).IS_REACT_ACT_ENVIRONMENT\n ;(globalThis as any).IS_REACT_ACT_ENVIRONMENT = false\n try {\n fn()\n } finally {\n ;(globalThis as any).IS_REACT_ACT_ENVIRONMENT = prev\n }\n}\n","/**\n * Pure diff function for silvery-loop.\n *\n * Takes prev and next buffers, returns minimal ANSI patch.\n * This is an internal function used by the runtime.\n */\n\nimport { outputPhase } from \"../pipeline\"\nimport type { Buffer } from \"./types\"\n\n/**\n * Diff mode for ANSI output.\n */\nexport type DiffMode = \"fullscreen\" | \"inline\"\n\n/**\n * Compute the minimal ANSI diff between two buffers.\n *\n * @param prev Previous buffer (null on first render)\n * @param next Current buffer\n * @param mode Render mode (fullscreen or inline)\n * @returns ANSI escape sequence string to transform prev into next\n *\n * @example\n * ```typescript\n * import { diff, layout } from '@silvery/ag-term/runtime'\n *\n * const prev = layout(<Text>Hello</Text>, dims)\n * const next = layout(<Text>World</Text>, dims)\n * const patch = diff(prev, next)\n * process.stdout.write(patch)\n * ```\n */\nexport function diff(\n prev: Buffer | null,\n next: Buffer,\n mode: DiffMode = \"fullscreen\",\n scrollbackOffset = 0,\n termRows?: number,\n): string {\n const prevBuffer = prev?._buffer ?? null\n const nextBuffer = next._buffer\n\n return outputPhase(prevBuffer, nextBuffer, mode, scrollbackOffset, termRows)\n}\n\n/**\n * Render a buffer to ANSI string (no diff, full render).\n *\n * @param buffer Buffer to render\n * @param mode Render mode (fullscreen or inline)\n * @returns Full ANSI output\n */\nexport function render(buffer: Buffer, mode: DiffMode = \"fullscreen\"): string {\n return outputPhase(null, buffer._buffer, mode)\n}\n","import { type TerminalBuffer, bufferToStyledText, bufferToText } from \"../buffer\"\nimport type { AgNode } from \"@silvery/ag/types\"\nimport type { Buffer } from \"./types\"\n\nexport function createBuffer(termBuffer: TerminalBuffer, nodes: AgNode): Buffer {\n let _text: string | undefined\n let _ansi: string | undefined\n return {\n get text() {\n return (_text ??= bufferToText(termBuffer))\n },\n get ansi() {\n return (_ansi ??= bufferToStyledText(termBuffer))\n },\n nodes,\n _buffer: termBuffer,\n }\n}\n","/**\n * Create the silvery-loop runtime kernel.\n *\n * The runtime owns the event loop, diffing, and output. Users interact via:\n * - events() - AsyncIterable of all events (keys, resize, effects)\n * - schedule() - Queue effects for async execution\n * - render() - Output a buffer (diffing handled internally)\n *\n * NOTE: This runtime is designed for single-consumer use. Calling events()\n * multiple times concurrently will cause events to be split between consumers.\n * Each call returns a fresh AsyncIterable, but they share the underlying queue.\n *\n * @example\n * ```typescript\n * using runtime = createRuntime({ target: termTarget })\n *\n * for await (const event of runtime.events()) {\n * state = reducer(state, event)\n * runtime.render(layout(view(state), runtime.getDims()))\n * }\n * ```\n */\n\nimport { createOutputPhase } from \"../pipeline/output-phase\"\nimport { takeUntil } from \"@silvery/create/streams\"\nimport { diff } from \"./diff\"\nimport type { Buffer, Dims, Event, Runtime, RuntimeOptions } from \"./types\"\n\n// =============================================================================\n// Event Channel - unified async iterable for all internal events\n// =============================================================================\n\ninterface EventChannel {\n push(event: Event): void\n events(): AsyncIterable<Event>\n dispose(): void\n}\n\n/**\n * Create an event channel that bridges callbacks to AsyncIterable.\n *\n * This is the single point where callbacks (resize, effect completion)\n * are converted to the async iterable pattern. External sources like\n * keyboard events are already AsyncIterable and merged at a higher level.\n */\nfunction createEventChannel(signal: AbortSignal): EventChannel {\n const queue: Event[] = []\n let pendingResolve: ((event: Event | null) => void) | undefined\n let disposed = false\n\n // Resolve pending waiter on abort\n const onAbort = () => {\n if (pendingResolve) {\n pendingResolve(null)\n pendingResolve = undefined\n }\n }\n signal.addEventListener(\"abort\", onAbort, { once: true })\n\n return {\n push(event: Event): void {\n if (disposed || signal.aborted) return\n\n if (pendingResolve) {\n const r = pendingResolve\n pendingResolve = undefined\n r(event)\n } else {\n queue.push(event)\n }\n },\n\n events(): AsyncIterable<Event> {\n return {\n [Symbol.asyncIterator](): AsyncIterator<Event> {\n return {\n async next(): Promise<IteratorResult<Event>> {\n if (disposed || signal.aborted) {\n return { done: true, value: undefined }\n }\n\n // Return queued event if available\n if (queue.length > 0) {\n return { done: false, value: queue.shift()! }\n }\n\n // Wait for next event or abort\n const event = await new Promise<Event | null>((resolve) => {\n pendingResolve = resolve\n })\n\n if (event === null || disposed || signal.aborted) {\n return { done: true, value: undefined }\n }\n\n return { done: false, value: event }\n },\n }\n },\n }\n },\n\n dispose(): void {\n disposed = true\n signal.removeEventListener(\"abort\", onAbort)\n if (pendingResolve) {\n pendingResolve(null)\n pendingResolve = undefined\n }\n },\n }\n}\n\n// =============================================================================\n// Runtime Factory\n// =============================================================================\n\n/**\n * Create a runtime kernel.\n *\n * @param options Runtime configuration\n * @returns Runtime instance implementing Symbol.dispose\n */\nexport function createRuntime(options: RuntimeOptions): Runtime {\n const { target, signal: externalSignal, mode = \"fullscreen\" } = options\n\n // Inline mode needs persistent cursor tracking across frames.\n // If no outputPhaseFn provided, create one so prevCursorRow/prevOutputLines\n // persist between renders (bare diff() creates fresh state each call).\n const fallbackOutputPhase = mode === \"inline\" ? createOutputPhase({}) : undefined\n let outputPhaseFn = options.outputPhaseFn ?? fallbackOutputPhase\n\n // Internal abort controller for cleanup\n const controller = new AbortController()\n const signal = controller.signal\n\n // Wire external signal if provided - track for cleanup\n let externalAbortHandler: (() => void) | undefined\n if (externalSignal) {\n if (externalSignal.aborted) {\n controller.abort()\n } else {\n externalAbortHandler = () => controller.abort()\n externalSignal.addEventListener(\"abort\", externalAbortHandler, {\n once: true,\n })\n }\n }\n\n // Track previous buffer for diffing\n let prevBuffer: Buffer | null = null\n\n // Scrollback offset tracking (inline mode only)\n let scrollbackOffset = 0\n\n // Track if disposed\n let disposed = false\n\n // Unified event channel for resize and effect events\n const eventChannel = createEventChannel(signal)\n\n // Subscribe to resize events if supported\n let unsubscribeResize: (() => void) | undefined\n if (target.onResize) {\n unsubscribeResize = target.onResize((dims) => {\n eventChannel.push({ type: \"resize\", cols: dims.cols, rows: dims.rows })\n })\n }\n\n // Effect ID counter\n let effectId = 0\n\n return {\n events(): AsyncIterable<Event> {\n // Return channel events wrapped with takeUntil for cleanup\n return takeUntil(eventChannel.events(), signal)\n },\n\n schedule<T>(effect: () => Promise<T>, opts?: { signal?: AbortSignal }): void {\n if (disposed) return\n\n const id = `effect-${effectId++}`\n const effectSignal = opts?.signal\n\n // Check if already aborted\n if (effectSignal?.aborted) return\n\n // Execute effect asynchronously\n const execute = async () => {\n // Track abort handler for cleanup\n let abortHandler: (() => void) | undefined\n\n try {\n if (effectSignal) {\n // Create abort race with cleanup\n const aborted = new Promise<never>((_resolve, reject) => {\n abortHandler = () => reject(new Error(\"Effect aborted\"))\n effectSignal.addEventListener(\"abort\", abortHandler, {\n once: true,\n })\n })\n\n const result = await Promise.race([effect(), aborted])\n\n // Clean up abort listener after success\n if (abortHandler) {\n effectSignal.removeEventListener(\"abort\", abortHandler)\n }\n\n eventChannel.push({ type: \"effect\", id, result })\n } else {\n const result = await effect()\n eventChannel.push({ type: \"effect\", id, result })\n }\n } catch (error) {\n // Clean up abort listener on error too\n if (abortHandler && effectSignal) {\n effectSignal.removeEventListener(\"abort\", abortHandler)\n }\n\n // Check for abort by name (handles DOMException, AbortError, etc.)\n if (error instanceof Error && (error.message === \"Effect aborted\" || error.name === \"AbortError\")) {\n // Silently ignore aborted effects\n return\n }\n eventChannel.push({\n type: \"error\",\n error: error instanceof Error ? error : new Error(String(error)),\n })\n }\n }\n\n // Start immediately (microtask)\n queueMicrotask(() => {\n void execute()\n })\n },\n\n render(buffer: Buffer): void {\n if (disposed) return\n\n // Compute diff internally — pass terminal rows to cap output.\n // Inline mode: prevents scrollback corruption (cursor-up clamped at row 0).\n // Fullscreen mode: prevents buffer overflow that scrolls the terminal and\n // desynchronizes prevBuffer from actual terminal state (ghost pixel garble).\n const offset = scrollbackOffset\n scrollbackOffset = 0 // Consume the offset\n const termRows = target.getDims().rows\n\n // Use scoped output phase if provided (threads measurer/caps correctly),\n // otherwise fall back to raw diff() for backwards compatibility\n let patch: string\n if (outputPhaseFn) {\n const prevBuf = prevBuffer?._buffer ?? null\n const nextBuf = buffer._buffer\n patch = outputPhaseFn(prevBuf, nextBuf, mode, offset, termRows)\n } else {\n patch = diff(prevBuffer, buffer, mode, offset, termRows)\n }\n prevBuffer = buffer\n\n // Debug: capture raw ANSI output that's actually written to the terminal\n if (process.env.SILVERY_CAPTURE_RAW) {\n try {\n const fs = require(\"fs\")\n fs.appendFileSync(\"/tmp/silvery-runtime-raw.ansi\", patch)\n } catch {}\n }\n\n // Write to target\n target.write(patch)\n },\n\n addScrollbackLines(lines: number): void {\n if (mode !== \"inline\" || lines <= 0) return\n scrollbackOffset += lines\n },\n\n invalidate(): void {\n prevBuffer = null\n },\n\n setOutputPhaseFn(fn: RuntimeOptions[\"outputPhaseFn\"]): void {\n if (fn) outputPhaseFn = fn\n },\n\n resetInlineCursor(): void {\n // Reset inline cursor tracking — delegates to the output phase (either\n // the caller-provided one or the inline-mode fallback created above).\n const fn = outputPhaseFn as { resetInlineState?: () => void } | undefined\n fn?.resetInlineState?.()\n },\n\n getInlineCursorRow(): number {\n const fn = outputPhaseFn as { getInlineCursorRow?: () => number } | undefined\n return fn?.getInlineCursorRow?.() ?? -1\n },\n\n promoteScrollback(content: string, lines: number): void {\n const fn = outputPhaseFn as { promoteScrollback?: (c: string, l: number) => void } | undefined\n fn?.promoteScrollback?.(content, lines)\n },\n\n getDims(): Dims {\n return target.getDims()\n },\n\n [Symbol.dispose](): void {\n if (disposed) return\n disposed = true\n\n // Abort all pending operations\n controller.abort()\n\n // Remove external signal listener if still attached\n if (externalAbortHandler && externalSignal) {\n externalSignal.removeEventListener(\"abort\", externalAbortHandler)\n }\n\n // Unsubscribe from resize\n if (unsubscribeResize) {\n unsubscribeResize()\n }\n\n // Dispose event channel\n eventChannel.dispose()\n },\n }\n}\n","/**\n * Signal Store — Zustand StoreApi-compatible store backed by alien-signals.\n *\n * Drop-in replacement for Zustand's createStore(). Provides the same\n * StoreApi<T> interface so useApp()/StoreContext keep working unchanged.\n *\n * Also re-exports StateCreator for backward compatibility with code\n * that imported it from \"zustand\".\n */\n\nimport { signal } from \"alien-signals\"\n\n// =============================================================================\n// Types (matching Zustand's API surface)\n// =============================================================================\n\ntype SetStateInternal<T> = {\n (partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: false): void\n (state: T | ((state: T) => T), replace: true): void\n}\n\nexport interface StoreApi<T> {\n setState: SetStateInternal<T>\n getState: () => T\n getInitialState: () => T\n subscribe: (listener: (state: T, prevState: T) => void) => () => void\n}\n\nexport type StateCreator<\n T,\n Mis extends [StoreMutatorIdentifier, unknown][] = [],\n Mos extends [StoreMutatorIdentifier, unknown][] = [],\n U = T,\n> = ((setState: StoreApi<T>[\"setState\"], getState: StoreApi<T>[\"getState\"], store: StoreApi<T>) => U) & {\n $$storeMutators?: Mos\n}\n\n// Zustand compatibility stubs — unused but needed for type compat\nexport interface StoreMutators<_S, _A> {}\nexport type StoreMutatorIdentifier = keyof StoreMutators<unknown, unknown>\n\n// =============================================================================\n// createStore — signal-backed Zustand replacement\n// =============================================================================\n\nexport function createStore<T>(factory: StateCreator<T>): StoreApi<T> {\n const listeners = new Set<(state: T, prevState: T) => void>()\n const state$ = signal<T>(undefined as T)\n let initialState: T\n\n const setState: SetStateInternal<T> = (partial: unknown, replace?: boolean) => {\n const prev = state$()\n const raw =\n typeof partial === \"function\" ? (partial as (state: T) => T | Partial<T>)(prev) : (partial as T | Partial<T>)\n\n let next: T\n if (!replace && raw !== null && typeof raw === \"object\" && !Array.isArray(raw)) {\n next = { ...prev, ...(raw as Partial<T>) } as T\n } else {\n next = raw as T\n }\n\n if (Object.is(prev, next)) return\n\n state$(next)\n\n for (const listener of listeners) {\n listener(next, prev)\n }\n }\n\n const getState = (): T => state$()\n const getInitialState = (): T => initialState\n\n const subscribe = (listener: (state: T, prevState: T) => void): (() => void) => {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n }\n\n const api: StoreApi<T> = { setState, getState, getInitialState, subscribe }\n\n const created = factory(setState, getState, api)\n state$(created)\n initialState = created\n\n return api\n}\n","/**\n * Event handler composition for createApp runtime.\n *\n * Extracted from create-app.tsx to reduce nesting depth.\n * Contains: handler context creation, focus navigation dispatch,\n * mouse event dispatch, and key handler dispatch.\n *\n * All functions are pure or near-pure — they don't access the event loop's\n * mutable state (pendingRerender, isRendering, etc.), which stays in create-app.tsx.\n */\n\nimport type { StoreApi } from \"@silvery/create/signal-store\"\n\nimport { createKeyEvent, dispatchKeyEvent } from \"@silvery/ag/focus-events\"\nimport type { FocusManager } from \"@silvery/ag/focus-manager\"\nimport { findByTestID } from \"@silvery/ag/focus-queries\"\nimport { type MouseEventProcessorState, processMouseEvent, hitTest } from \"../mouse-events\"\nimport type { Container } from \"@silvery/ag-react/reconciler\"\nimport { getContainerRoot } from \"@silvery/ag-react/reconciler\"\nimport type { AgNode } from \"@silvery/ag/types\"\nimport type { Key } from \"./keys\"\nimport type { EventHandler, EventHandlerContext, EventHandlers } from \"./create-app\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Namespaced event from a provider.\n */\nexport interface NamespacedEvent {\n type: string\n provider: string\n event: string\n data: unknown\n}\n\n// ============================================================================\n// Handler Context\n// ============================================================================\n\n/**\n * Build the EventHandlerContext passed to user-defined event handlers.\n * Shared by runEventHandler() and press().\n *\n * When the store was created with `tea()` middleware, `dispatch` is\n * automatically wired from the store state.\n */\nexport function createHandlerContext<S>(\n store: StoreApi<S>,\n focusManager: FocusManager,\n container: Container,\n): EventHandlerContext<S> {\n // Detect tea() middleware: store state has a dispatch function\n const state = store.getState() as Record<string, unknown>\n const teaDispatch = typeof state.dispatch === \"function\" ? state.dispatch : undefined\n\n return {\n set: store.setState,\n get: store.getState,\n focusManager,\n focus(testID: string) {\n const root = getContainerRoot(container)\n focusManager.focusById(testID, root, \"programmatic\")\n },\n activateScope(scopeId: string) {\n const root = getContainerRoot(container)\n focusManager.activateScope(scopeId, root)\n },\n getFocusPath() {\n const root = getContainerRoot(container)\n return focusManager.getFocusPath(root)\n },\n dispatch: teaDispatch as EventHandlerContext<S>[\"dispatch\"],\n hitTest(x: number, y: number) {\n const root = getContainerRoot(container)\n return hitTest(root, x, y)\n },\n }\n}\n\n// ============================================================================\n// Focus Navigation\n// ============================================================================\n\n/**\n * Dispatch a key event through the focus system and handle default\n * focus navigation (Tab, Shift+Tab, Enter scope, Escape scope).\n *\n * Returns \"consumed\" if the focus system handled the event (caller should\n * render and return), or \"continue\" if the event should proceed to app handlers.\n */\nexport function handleFocusNavigation(\n input: string,\n parsedKey: Key,\n focusManager: FocusManager,\n container: Container,\n): \"consumed\" | \"continue\" {\n // Dispatch key event to focused node (capture + bubble phases)\n if (focusManager.activeElement) {\n const keyEvent = createKeyEvent(input, parsedKey, focusManager.activeElement)\n dispatchKeyEvent(keyEvent)\n\n // If focus system consumed the event, skip app handlers\n if (keyEvent.propagationStopped || keyEvent.defaultPrevented) {\n return \"consumed\"\n }\n }\n\n const root = getContainerRoot(container)\n\n // Tab: focus next (works even when nothing is focused — starts from first)\n if (parsedKey.tab && !parsedKey.shift) {\n focusManager.focusNext(root)\n return \"consumed\"\n }\n\n // Shift+Tab: focus previous (works even when nothing is focused — starts from last)\n if (parsedKey.tab && parsedKey.shift) {\n focusManager.focusPrev(root)\n return \"consumed\"\n }\n\n // Enter: if focused element has focusScope, enter that scope\n if (parsedKey.return && focusManager.activeElement) {\n const activeEl = focusManager.activeElement\n const props = activeEl.props as Record<string, unknown>\n const testID = typeof props.testID === \"string\" ? props.testID : null\n if (props.focusScope && testID) {\n focusManager.enterScope(testID)\n focusManager.focusNext(root, activeEl)\n return \"consumed\"\n }\n }\n\n // Escape: exit the current focus scope if one is open.\n //\n // Apps handle their own Escape routing via keybindings (close dialogs, exit\n // modes, etc.), so we only intercept Escape when there is an actual focus\n // scope to pop. Previously this also called focusManager.blur() as a\n // fallback, but that consumed Escape before app handlers could run — for\n // example preventing `console.close` from firing while the board has the\n // auto-focused \"board-area\" Box as activeElement. Apps that want the old\n // behaviour can implement it in their own key handler.\n if (parsedKey.escape) {\n if (focusManager.scopeStack.length > 0) {\n const scopeId = focusManager.scopeStack[focusManager.scopeStack.length - 1]!\n focusManager.exitScope()\n const scopeNode = findByTestID(root, scopeId)\n if (scopeNode) {\n focusManager.focus(scopeNode, \"keyboard\")\n }\n return \"consumed\"\n }\n }\n\n return \"continue\"\n}\n\n// ============================================================================\n// Mouse Event Dispatch\n// ============================================================================\n\n/**\n * Dispatch a DOM-level mouse event to the node tree.\n * Called from runEventHandler for mouse events.\n */\nexport function dispatchMouseEventToTree(\n event: NamespacedEvent,\n mouseEventState: MouseEventProcessorState,\n root: AgNode,\n): boolean {\n if (event.event !== \"mouse\" || !event.data) return false\n\n const mouseData = event.data as {\n button: number\n x: number\n y: number\n action: string\n delta?: number\n shift: boolean\n meta: boolean\n ctrl: boolean\n }\n\n return processMouseEvent(\n mouseEventState,\n {\n button: mouseData.button,\n x: mouseData.x,\n y: mouseData.y,\n action: mouseData.action as \"down\" | \"up\" | \"move\" | \"wheel\",\n delta: mouseData.delta,\n shift: mouseData.shift,\n meta: mouseData.meta,\n ctrl: mouseData.ctrl,\n },\n root,\n )\n}\n\n// ============================================================================\n// Event Handler Dispatch\n// ============================================================================\n\n/**\n * Invoke the namespaced handler for a single event (state mutation only, no render).\n * Returns true to continue, false to exit, or \"flush\" for a render barrier.\n *\n * Also dispatches DOM-level mouse events when applicable.\n */\nexport function invokeEventHandler<S>(\n event: NamespacedEvent,\n handlers: EventHandlers<S> | undefined,\n ctx: EventHandlerContext<S>,\n mouseEventState: MouseEventProcessorState,\n container: Container,\n): boolean | \"flush\" {\n // DOM-level mouse event dispatch FIRST — component handlers (onClick, etc.)\n // can call preventDefault() to suppress the app-level handler.\n const root = getContainerRoot(container)\n const prevented = dispatchMouseEventToTree(event, mouseEventState, root)\n\n // Skip app handler if a component called preventDefault()\n if (prevented) return true\n\n const namespacedHandler = handlers?.[event.type as keyof typeof handlers]\n\n if (namespacedHandler && typeof namespacedHandler === \"function\") {\n const result = (namespacedHandler as EventHandler<unknown, S>)(event.data, ctx)\n if (result === \"exit\") return false\n if (result === \"flush\") return \"flush\"\n }\n\n return true\n}\n\n/**\n * Dispatch a term:key event to app handlers (namespaced + legacy).\n * Returns \"exit\" if the handler signaled exit, undefined otherwise.\n */\nexport function dispatchKeyToHandlers<S>(\n input: string,\n parsedKey: Key,\n handlers: EventHandlers<S> | undefined,\n ctx: EventHandlerContext<S>,\n): \"exit\" | undefined {\n // Namespaced handler\n const namespacedHandler = handlers?.[\"term:key\" as keyof typeof handlers]\n if (namespacedHandler && typeof namespacedHandler === \"function\") {\n const result = (namespacedHandler as EventHandler<unknown, S>)({ input, key: parsedKey }, ctx)\n if (result === \"exit\") return \"exit\"\n }\n\n // Legacy handler\n if ((handlers as any)?.key) {\n const result = (handlers as any).key(input, parsedKey, ctx)\n if (result === \"exit\") return \"exit\"\n }\n\n return undefined\n}\n","/**\n * Terminal Lifecycle Events\n *\n * Handles suspend/resume (Ctrl+Z/SIGCONT) and interrupt (Ctrl+C) for TUI apps.\n * When stdin is in raw mode, the terminal does not generate SIGTSTP/SIGINT for\n * Ctrl+Z/Ctrl+C. This module intercepts the raw bytes and manages the full\n * terminal state save/restore cycle.\n *\n * Inspired by ncurses (endwin/refresh), bubbletea, and Textual.\n *\n * Protocols managed:\n * - Raw mode (stdin)\n * - Alternate screen buffer (DEC private mode 1049)\n * - Cursor visibility (DEC private mode 25)\n * - Mouse tracking (modes 1000, 1002, 1006)\n * - Kitty keyboard protocol (CSI > flags u / CSI < u)\n * - Bracketed paste (DEC private mode 2004)\n * - SGR attributes (reset via CSI 0 m)\n */\n\nimport { writeSync } from \"node:fs\"\nimport { enableKittyKeyboard, disableKittyKeyboard, enableMouse, disableMouse, resetCursorStyle } from \"../output\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Options for terminal lifecycle event handling.\n */\nexport interface TerminalLifecycleOptions {\n /** Handle Ctrl+Z by suspending the process. Default: true */\n suspendOnCtrlZ?: boolean\n /** Handle Ctrl+C by exiting the process. Default: true */\n exitOnCtrlC?: boolean\n /** Called before suspend. Return false to prevent. */\n onSuspend?: () => boolean | void\n /** Called after resume from suspend. */\n onResume?: () => void\n /** Called on Ctrl+C. Return false to prevent exit. */\n onInterrupt?: () => boolean | void\n}\n\n/**\n * Snapshot of terminal protocol state for save/restore across suspend/resume.\n */\nexport interface TerminalState {\n rawMode: boolean\n alternateScreen: boolean\n cursorHidden: boolean\n mouseEnabled: boolean\n kittyEnabled: boolean\n kittyFlags: number\n bracketedPaste: boolean\n focusReporting: boolean\n}\n\n// ============================================================================\n// State Capture\n// ============================================================================\n\n/**\n * Capture the current terminal protocol state.\n *\n * This builds a TerminalState from the options passed to run()/createApp(),\n * since terminal state is not directly queryable from the OS.\n */\nexport function captureTerminalState(opts: {\n alternateScreen?: boolean\n cursorHidden?: boolean\n mouse?: boolean\n kitty?: boolean\n kittyFlags?: number\n bracketedPaste?: boolean\n rawMode?: boolean\n focusReporting?: boolean\n}): TerminalState {\n return {\n rawMode: opts.rawMode ?? true,\n alternateScreen: opts.alternateScreen ?? false,\n cursorHidden: opts.cursorHidden ?? true,\n mouseEnabled: opts.mouse ?? false,\n kittyEnabled: opts.kitty ?? false,\n kittyFlags: opts.kittyFlags ?? 11, // DISAMBIGUATE(1) | REPORT_EVENTS(2) | REPORT_ALL_KEYS(8)\n bracketedPaste: opts.bracketedPaste ?? false,\n focusReporting: opts.focusReporting ?? false,\n }\n}\n\n// ============================================================================\n// Restore (before suspend / on exit)\n// ============================================================================\n\n/**\n * Restore terminal to normal state before suspending or exiting.\n *\n * Uses writeSync for reliability during signal handling (async write\n * may not complete before the process suspends).\n *\n * Order matters: disable protocols first, then show cursor, then exit\n * alternate screen, then disable raw mode.\n */\nexport function restoreTerminalState(stdout: NodeJS.WriteStream, stdin: NodeJS.ReadStream): void {\n // Step 1: Stop consuming stdin — prevent processing of in-flight events\n try {\n stdin.removeAllListeners(\"data\")\n stdin.pause()\n } catch {\n // Ignore — stdin may be closed\n }\n\n // Step 2: Send all protocol disable sequences\n const sequences = [\n \"\\x1b[0m\", // Reset SGR attributes\n \"\\x1b[?1004l\", // Disable focus reporting\n disableMouse(), // Disable all mouse tracking modes\n disableKittyKeyboard(), // Pop Kitty keyboard protocol\n \"\\x1b[?2004l\", // Disable bracketed paste\n resetCursorStyle(), // Reset cursor shape to terminal default (DECSCUSR 0)\n \"\\x1b[?25h\", // Show cursor\n \"\\x1b[?1049l\", // Exit alternate screen\n ].join(\"\")\n\n // Use writeSync for reliability during signal handlers — but only when stdout\n // is the real process.stdout. Mock stdouts have fd:1 which bypasses the mock.\n if (stdout === process.stdout) {\n try {\n writeSync((stdout as unknown as { fd: number }).fd, sequences)\n } catch {\n try {\n stdout.write(sequences)\n } catch {\n // Terminal may already be gone (e.g., SSH disconnect)\n }\n }\n } else {\n try {\n stdout.write(sequences)\n } catch {\n // Terminal may already be gone\n }\n }\n\n // Step 3: Drain in-flight stdin bytes — the terminal may have queued events\n // (Kitty key release, SGR mouse) before processing our disable sequences.\n // Discard them so they don't leak to the shell prompt.\n try {\n stdin.resume()\n while (stdin.read() !== null) {\n // discard buffered data\n }\n stdin.pause()\n } catch {\n // Ignore — best-effort drain\n }\n\n // Step 4: Disable raw mode on stdin\n if (stdin.isTTY && stdin.isRaw) {\n try {\n stdin.setRawMode(false)\n } catch {\n // Ignore - stdin may already be closed\n }\n }\n}\n\n// ============================================================================\n// Resume (after SIGCONT)\n// ============================================================================\n\n/**\n * Re-enter TUI mode after resuming from suspend (SIGCONT).\n *\n * Restores all protocols that were active before suspend, in the correct\n * order: raw mode first, then alternate screen, then protocols, then\n * trigger a full redraw via synthetic resize.\n */\nexport function resumeTerminalState(state: TerminalState, stdout: NodeJS.WriteStream, stdin: NodeJS.ReadStream): void {\n // Re-enable raw mode first (needed to receive key input)\n if (state.rawMode && stdin.isTTY) {\n try {\n stdin.setRawMode(true)\n stdin.resume()\n } catch {\n // Ignore - may fail if stdin is closed\n }\n }\n\n // Build the sequence of escape codes to restore TUI state\n const sequences: string[] = []\n\n if (state.alternateScreen) {\n sequences.push(\"\\x1b[?1049h\") // Enter alternate screen\n }\n\n // Clear screen and home cursor (always needed after resume to get a clean slate)\n sequences.push(\"\\x1b[2J\\x1b[H\")\n\n if (state.cursorHidden) {\n sequences.push(\"\\x1b[?25l\") // Hide cursor\n }\n\n if (state.kittyEnabled) {\n sequences.push(enableKittyKeyboard(state.kittyFlags as 1))\n }\n\n if (state.mouseEnabled) {\n sequences.push(enableMouse())\n }\n\n if (state.bracketedPaste) {\n sequences.push(\"\\x1b[?2004h\") // Enable bracketed paste\n }\n\n if (state.focusReporting) {\n sequences.push(\"\\x1b[?1004h\") // Enable focus reporting\n }\n\n // Write all sequences — use writeSync only for real process.stdout\n const joined = sequences.join(\"\")\n if (stdout === process.stdout) {\n try {\n writeSync((stdout as unknown as { fd: number }).fd, joined)\n } catch {\n try {\n stdout.write(joined)\n } catch {\n // Terminal may be gone\n }\n }\n } else {\n try {\n stdout.write(joined)\n } catch {\n // Terminal may be gone\n }\n }\n\n // Emit synthetic resize to trigger full redraw.\n // The screen was cleared, so the runtime needs to render a complete frame.\n stdout.emit(\"resize\")\n}\n\n// ============================================================================\n// Suspend Flow\n// ============================================================================\n\n/**\n * Execute the full suspend flow: save state, restore terminal, SIGTSTP,\n * and set up SIGCONT handler to resume.\n *\n * @param state - Terminal state snapshot to restore on resume\n * @param stdout - Output stream\n * @param stdin - Input stream\n * @param onResume - Optional callback after resume\n */\nexport function performSuspend(\n state: TerminalState,\n stdout: NodeJS.WriteStream,\n stdin: NodeJS.ReadStream,\n onResume?: () => void,\n): void {\n // Restore terminal to normal\n restoreTerminalState(stdout, stdin)\n\n // Register one-time SIGCONT handler BEFORE sending SIGTSTP\n process.once(\"SIGCONT\", () => {\n // Re-enter TUI mode\n resumeTerminalState(state, stdout, stdin)\n onResume?.()\n })\n\n // Actually suspend the process\n process.kill(process.pid, \"SIGTSTP\")\n}\n\n// ============================================================================\n// Raw byte constants\n// ============================================================================\n\n/** Ctrl+C raw byte (ETX - End of Text) */\nexport const CTRL_C = \"\\x03\"\n\n/** Ctrl+Z raw byte (SUB - Substitute) */\nexport const CTRL_Z = \"\\x1a\"\n","/**\n * Keypress performance instrumentation.\n *\n * Zero-overhead when TRACE is not set — all logging uses optional chaining.\n * When TRACE=silvery:perf is set, emits span timing for each keypress cycle\n * and a summary on exit.\n *\n * @example\n * ```bash\n * TRACE=silvery:perf bun km view ~/vault\n * # → SPAN silvery:perf:keypress (5ms) {key: \"j\"}\n * # → on exit: keypress summary: 42 presses, mean=4.2ms, p95=12.1ms, max=18.3ms, overruns=2\n * ```\n */\n\nimport { createLogger } from \"loggily\"\n\n/** Exported for ?. chaining in hot paths: `perfLog.span?.(\"keypress\", { key })` */\nexport const perfLog = createLogger(\"silvery:perf\")\n\n// ============================================================================\n// Budget tracking (only active when spans are created)\n// ============================================================================\n\nlet samples: Array<{ key: string; durationMs: number }> | null = null\nlet budgetOverruns = 0\n\n/**\n * Record a completed keypress and check budget.\n * Only records when tracing is active (samples array initialized by startTracking).\n * Call after the keypress cycle completes (render done).\n */\nexport function checkBudget(key: string, durationMs: number, budgetMs = 16) {\n if (samples) {\n samples.push({ key, durationMs })\n }\n if (durationMs > budgetMs) {\n budgetOverruns++\n perfLog.warn?.(`keypress over budget: ${key} took ${durationMs.toFixed(1)}ms (budget: ${budgetMs}ms)`)\n }\n}\n\n/** Call once when first span is created to start accumulating samples. */\nexport function startTracking() {\n if (!samples) samples = []\n}\n\n// ============================================================================\n// Exit summary\n// ============================================================================\n\n/**\n * Log a summary of all recorded keypress spans.\n *\n * Call when the app unmounts/exits. Only produces output when TRACE is\n * enabled and at least one span was recorded.\n */\nexport function logExitSummary() {\n if (!samples || samples.length === 0) return\n\n const durations = samples.map((s) => s.durationMs).sort((a, b) => a - b)\n const total = samples.length\n const mean = durations.reduce((sum, d) => sum + d, 0) / total\n const p95Index = Math.min(Math.floor(total * 0.95), total - 1)\n const p95 = durations[p95Index]!\n const max = durations[total - 1]!\n\n perfLog.info?.(\n `keypress summary: ${total} presses, mean=${mean.toFixed(1)}ms, p95=${p95.toFixed(1)}ms, max=${max.toFixed(1)}ms, overruns=${budgetOverruns}`,\n )\n\n // Reset for potential reuse (tests)\n samples = null\n budgetOverruns = 0\n}\n\n/**\n * Reset internal state. Useful for tests to ensure clean state between runs.\n */\nexport function resetPerfState() {\n samples = null\n budgetOverruns = 0\n}\n\n/**\n * Get current sample count. Useful for tests.\n */\nexport function getSampleCount(): number {\n return samples?.length ?? 0\n}\n","/**\n * createApp() - Layer 3 entry point for silvery-loop\n *\n * Provides signal-backed store integration with unified providers.\n * Providers are stores (getState/subscribe) + event sources (events()).\n *\n * @example\n * ```tsx\n * import { createApp, useApp } from '@silvery/create/create-app'\n * import { createTermProvider } from '@silvery/ag-term/runtime'\n *\n * const app = createApp(\n * // Store factory\n * ({ term }) => (set, get) => ({\n * count: 0,\n * increment: () => set(s => ({ count: s.count + 1 })),\n * }),\n * // Event handlers - namespaced as 'provider:event'\n * {\n * 'term:key': ({ input, key }, { set }) => {\n * if (input === 'j') set(s => ({ count: s.count + 1 }))\n * if (input === 'q') return 'exit'\n * },\n * 'term:resize': ({ cols, rows }, { set }) => {\n * // handle resize\n * },\n * }\n * )\n *\n * function Counter() {\n * const count = useApp(s => s.count)\n * return <Text>Count: {count}</Text>\n * }\n *\n * const term = createTermProvider(process.stdin, process.stdout)\n * await app.run(<Counter />, { term })\n *\n * // Frame iteration:\n * for await (const frame of app.run(<Counter />, { term })) {\n * expect(frame.text).toContain('Count:')\n * }\n * ```\n */\n\nimport { writeSync } from \"node:fs\"\nimport process from \"node:process\"\nimport React, { createContext, useContext, useEffect, useRef, type ReactElement } from \"react\"\nimport { type StateCreator, type StoreApi, createStore } from \"@silvery/create/signal-store\"\n\nimport { createTerm } from \"@silvery/ag-term/ansi\"\nimport {\n CacheBackendContext,\n CapabilityRegistryContext,\n FocusManagerContext,\n RuntimeContext,\n type RuntimeContextValue,\n StdoutContext,\n StderrContext,\n TermContext,\n} from \"@silvery/ag-react/context\"\nimport { SilveryErrorBoundary } from \"@silvery/ag-react/error-boundary\"\nimport { createFocusManager } from \"@silvery/ag/focus-manager\"\nimport { createCursorStore, CursorProvider } from \"@silvery/ag-react/hooks/useCursor\"\nimport { createFocusEvent, dispatchFocusEvent } from \"@silvery/ag/focus-events\"\nimport { executeRender } from \"@silvery/ag-term/pipeline\"\nimport { createAg, type Ag } from \"@silvery/ag-term/ag\"\nimport { createPipeline } from \"@silvery/ag-term/measurer\"\nimport {\n isTextSizingLikelySupported,\n detectTextSizingSupport,\n getCachedProbeResult,\n} from \"@silvery/ag-term/text-sizing\"\nimport { createWidthDetector, applyWidthConfig } from \"@silvery/ag-term\"\nimport { IncrementalRenderMismatchError } from \"@silvery/ag-term/scheduler\"\nimport { isAnyDirty } from \"@silvery/ag/epoch\"\nimport {\n createContainer,\n createFiberRoot,\n getContainerRoot,\n reconciler,\n setOnNodeRemoved,\n} from \"@silvery/ag-react/reconciler\"\nimport { map, merge, takeUntil } from \"@silvery/create/streams\"\nimport { createBuffer } from \"@silvery/ag-term/runtime/create-buffer\"\nimport { createRuntime } from \"@silvery/ag-term/runtime/create-runtime\"\nimport {\n createHandlerContext,\n dispatchKeyToHandlers,\n handleFocusNavigation,\n invokeEventHandler,\n type NamespacedEvent,\n} from \"@silvery/ag-term/runtime/event-handlers\"\nimport { keyToAnsi, keyToKittyAnsi, isModifierOnlyEvent } from \"@silvery/ag/keys\"\nimport { parseKey, type Key } from \"@silvery/ag-term/runtime/keys\"\nimport { ensureLayoutEngine } from \"@silvery/ag-term/runtime/layout\"\nimport {\n createMouseEventProcessor,\n updateKeyboardModifiers,\n findContainBoundary,\n selectionHitTest,\n} from \"@silvery/ag-term/mouse-events\"\nimport {\n enableKittyKeyboard,\n disableKittyKeyboard,\n KittyFlags,\n enableMouse,\n disableMouse,\n resetCursorStyle,\n enterAlternateScreen,\n leaveAlternateScreen,\n} from \"@silvery/ag-term/output\"\nimport { enableFocusReporting } from \"@silvery/ag-term/focus-reporting\"\nimport { detectKittyFromStdio } from \"@silvery/ag-term/kitty-detect\"\nimport { captureTerminalState, performSuspend } from \"@silvery/ag-term/runtime/terminal-lifecycle\"\nimport { type TermProvider, createTermProvider } from \"@silvery/ag-term/runtime/term-provider\"\nimport type { Buffer, Dims, Provider, RenderTarget } from \"@silvery/ag-term/runtime/types\"\nimport { createTerminalSelectionState, terminalSelectionUpdate, extractText } from \"@silvery/headless/selection\"\nimport { createSelectionBridge, type SelectionFeature } from \"@silvery/ag-term/features/selection\"\nimport { renderSelectionOverlay } from \"@silvery/ag-term/selection-renderer\"\nimport { createCapabilityRegistry, type CapabilityRegistry } from \"@silvery/create/internal/capability-registry\"\nimport { SELECTION_CAPABILITY } from \"@silvery/create/internal/capabilities\"\nimport { createVirtualScrollback } from \"@silvery/ag-term/virtual-scrollback\"\nimport { createSearchState, searchUpdate, renderSearchBar, type SearchMatch } from \"@silvery/ag-term/search-overlay\"\nimport { createOutputGuard, type OutputGuard } from \"@silvery/ag-term/ansi/output-guard\"\nimport { perfLog, checkBudget, logExitSummary, startTracking } from \"@silvery/ag-term/runtime/perf\"\nimport { createLogger } from \"loggily\"\n\nconst log = createLogger(\"silvery:app\")\n\n// ============================================================================\n// Feature-detection flags — hoisted to module scope.\n//\n// These env var checks were historically evaluated on every doRender() call,\n// adding ~10μs/frame overhead to production renders. They are all static for\n// the lifetime of the process, so we compute them once at module load.\n//\n// When the instrumentation flag is off (the common case), branches guarded by\n// these constants are dead-code eliminated by V8's optimizer — turning them\n// into no-ops on the hot path.\n// ============================================================================\nconst ENV = typeof process !== \"undefined\" ? process.env : undefined\nconst NO_INCREMENTAL = ENV?.SILVERY_NO_INCREMENTAL === \"1\"\nconst STRICT_MODE = (() => {\n const v = ENV?.SILVERY_STRICT\n return !!v && v !== \"0\" && v !== \"false\"\n})()\nconst CELL_DEBUG = (() => {\n const v = ENV?.SILVERY_CELL_DEBUG\n if (!v || !v.includes(\",\")) return null\n const [cx, cy] = v.split(\",\").map(Number)\n if (!Number.isFinite(cx) || !Number.isFinite(cy)) return null\n return { x: cx, y: cy }\n})()\n// INSTRUMENTED = any diagnostic is on. When false, the per-frame resets of\n// diagnostic globals can be skipped entirely — they are only consumed by the\n// STRICT/CELL_DEBUG paths. This is the primary hot-path win: when no\n// instrumentation is active (production), doRender skips ~8 global ops/frame.\nconst INSTRUMENTED = STRICT_MODE || CELL_DEBUG !== null\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Check if value is a Provider with events (full interface).\n */\nfunction isFullProvider(value: unknown): value is Provider<unknown, Record<string, unknown>> {\n if (value === null || value === undefined) return false\n // Term is a Proxy wrapping chalk, so typeof is \"function\" not \"object\"\n if (typeof value !== \"object\" && typeof value !== \"function\") return false\n return (\n \"getState\" in value &&\n \"subscribe\" in value &&\n \"events\" in value &&\n typeof (value as Provider).getState === \"function\" &&\n typeof (value as Provider).subscribe === \"function\" &&\n typeof (value as Provider).events === \"function\"\n )\n}\n\n/**\n * Check if value is a basic Provider (just getState/subscribe, Zustand-compatible).\n */\nfunction isBasicProvider(value: unknown): value is {\n getState(): unknown\n subscribe(l: (s: unknown) => void): () => void\n} {\n if (value === null || value === undefined) return false\n // Term is a Proxy wrapping chalk, so typeof is \"function\" not \"object\"\n if (typeof value !== \"object\" && typeof value !== \"function\") return false\n return (\n \"getState\" in value &&\n \"subscribe\" in value &&\n typeof (value as { getState: unknown }).getState === \"function\" &&\n typeof (value as { subscribe: unknown }).subscribe === \"function\"\n )\n}\n\n/**\n * Event handler context passed to handlers.\n *\n * When the store uses `tea()` middleware, `dispatch` is available with the\n * correct Op type inferred from the store. For non-tea stores it's `undefined`.\n */\nexport interface EventHandlerContext<S> {\n set: StoreApi<S>[\"setState\"]\n get: StoreApi<S>[\"getState\"]\n /** The tree-based focus manager */\n focusManager: import(\"@silvery/create/focus-manager\").FocusManager\n /** Convenience: focus a node by testID */\n focus(testID: string): void\n /** Activate a peer focus scope (saves/restores focus per scope) */\n activateScope(scopeId: string): void\n /** Get the focus path from focused node to root */\n getFocusPath(): string[]\n /**\n * Dispatch an operation through the tea() reducer.\n *\n * Available when the store was created with `tea()` middleware from `silvery/tea`.\n * Type-safe: the Op type is inferred from the store's TeaSlice.\n * For non-tea stores, this is `undefined`.\n */\n dispatch?: \"dispatch\" extends keyof S ? S[\"dispatch\"] : undefined\n /** Hit-test the render tree at (x, y). Returns the deepest SilveryNode at that point, or null. */\n hitTest(x: number, y: number): import(\"@silvery/create/types\").AgNode | null\n}\n\n/**\n * Generic event handler function.\n * Return 'exit' to exit the app.\n */\nexport type EventHandler<T, S> = (data: T, ctx: EventHandlerContext<S>) => void | \"exit\" | \"flush\"\n\n/**\n * Event handlers map.\n * Keys are namespaced as 'provider:event' (e.g., 'term:key', 'term:resize').\n */\nexport type EventHandlers<S> = {\n [event: `${string}:${string}`]: EventHandler<unknown, S> | undefined\n}\n\n/**\n * Options for app.run().\n */\nexport interface AppRunOptions {\n /** Terminal dimensions (default: from process.stdout) */\n cols?: number\n rows?: number\n /** Standard output (default: process.stdout) */\n stdout?: NodeJS.WriteStream\n /** Standard input (default: process.stdin) */\n stdin?: NodeJS.ReadStream\n /**\n * Plain writable sink for ANSI output. Headless mode with active output.\n * Requires cols and rows. Input via handle.press().\n */\n writable?: { write(data: string): void }\n /**\n * Subscribe to resize events in headless mode.\n * Called with a handler that should be invoked when dimensions change.\n * Returns an unsubscribe function.\n */\n onResize?: (handler: (dims: { cols: number; rows: number }) => void) => () => void\n /** Abort signal for external cleanup */\n signal?: AbortSignal\n /** Enter alternate screen buffer (clean slate, restore on exit). Default: false */\n alternateScreen?: boolean\n /** Use Kitty keyboard protocol encoding for press(). Default: false */\n kittyMode?: boolean\n /**\n * Enable Kitty keyboard protocol.\n * - `true`: auto-detect and enable with DISAMBIGUATE flag (1)\n * - number: enable with specific KittyFlags bitfield\n * - `false`/undefined: don't enable (default)\n */\n kitty?: boolean | number\n /**\n * Enable SGR mouse tracking (mode 1006).\n * When true, enables mouse events and disables on cleanup.\n * Default: false\n */\n mouse?: boolean\n /**\n * Enable virtual inline mode: alt screen with virtual scrollback buffer.\n * Provides scrollable history + search (Ctrl+F) while using fullscreen rendering.\n * Default: false\n */\n virtualInline?: boolean\n /**\n * Handle Ctrl+Z by suspending the process (save terminal state,\n * send SIGTSTP, restore on SIGCONT). Default: true\n */\n suspendOnCtrlZ?: boolean\n /**\n * Handle Ctrl+C by restoring terminal and exiting.\n * Default: true\n */\n exitOnCtrlC?: boolean\n /** Called before suspend. Return false to prevent. */\n onSuspend?: () => boolean | void\n /** Called after resume from suspend. */\n onResume?: () => void\n /** Called on Ctrl+C. Return false to prevent exit. */\n onInterrupt?: () => boolean | void\n /**\n * Enable Kitty text sizing protocol (OSC 66) for PUA characters.\n * When enabled, nerdfont/powerline icons are measured as 2-wide and\n * wrapped in OSC 66 sequences so the terminal renders them at the\n * correct width.\n * - `true`: force enable\n * - `\"auto\"`: use heuristic, then probe to verify (progressive enhancement)\n * - `\"probe\"`: start disabled, probe async, enable on confirmation\n * - `false`/undefined: disabled (default)\n */\n textSizing?: boolean | \"auto\" | \"probe\"\n /**\n * Enable DEC width mode detection (modes 1020-1023).\n * Queries the terminal for its actual character width settings (emoji,\n * CJK, private-use area) and updates the measurer accordingly.\n * - `true`: always run width detection probe\n * - `\"auto\"`: run probe when caps are provided (default for real terminals)\n * - `false`/undefined: disabled (default)\n */\n widthDetection?: boolean | \"auto\"\n /**\n * Enable terminal focus reporting (CSI ?1004h).\n * When enabled, the terminal sends focus-in/focus-out events that are\n * dispatched as 'term:focus' events with `{ focused: boolean }`.\n * Default: false\n */\n focusReporting?: boolean\n /**\n * Enable buffer-level text selection via mouse drag.\n * When enabled, left mouse drag selects text, and mouse up copies\n * selected text to clipboard via OSC 52.\n * Default: true when mouse is enabled\n */\n selection?: boolean\n /**\n * Terminal capabilities for width measurement and output suppression.\n * When provided, configures the render pipeline to use these caps\n * (scoped width measurer + output phase). Typically from term.caps.\n */\n caps?: import(\"@silvery/ag-term/terminal-caps\").TerminalCaps\n /**\n * Guard stdout/stderr in alt screen mode. When true (the default for\n * alternateScreen), intercepts process.stdout.write and process.stderr.write\n * so that only silvery's render pipeline can write to stdout. Non-silvery\n * stderr writes are redirected to DEBUG_LOG if set, otherwise suppressed.\n * This prevents display corruption from libraries that write directly to\n * process.stdout/stderr (e.g., loggily, debug).\n *\n * - `true`: enable output guard (default when alternateScreen is true)\n * - `false`: disable output guard\n */\n guardOutput?: boolean\n /**\n * Root component that wraps the element tree with additional providers.\n * Set by plugins (e.g., withInk) via the `app.Root` pattern.\n * The Root component receives children and wraps them with providers.\n */\n Root?: React.ComponentType<{ children: React.ReactNode }>\n /**\n * Capability registry from the composition layer (e.g., withDomEvents, withTerminal).\n * When provided, exposed to React components via CapabilityRegistryContext so\n * hooks like useSelection() can discover interaction features.\n */\n capabilityRegistry?: import(\"@silvery/ag-react/context\").CapabilityLookup\n /** Providers and plain values to inject */\n [key: string]: unknown\n}\n\n/**\n * Handle returned by app.run().\n *\n * Also AsyncIterable<Buffer> — iterate to get frames after each event:\n * ```typescript\n * for await (const frame of app.run(<App />)) {\n * expect(frame.text).toContain('expected')\n * }\n * ```\n */\nexport interface AppHandle<S> {\n /** Current rendered text (no ANSI) */\n readonly text: string\n /** Live reconciler root node (for locator queries) */\n readonly root: import(\"@silvery/ag/types\").AgNode\n /** Current terminal buffer (cell-level access) */\n readonly buffer: import(\"@silvery/ag-term/buffer\").TerminalBuffer | null\n /** Access to the Zustand store */\n readonly store: StoreApi<S>\n /** Wait until the app exits */\n waitUntilExit(): Promise<void>\n /** Unmount and cleanup */\n unmount(): void\n /** Dispose (alias for unmount) — enables `using` */\n [Symbol.dispose](): void\n /** Send a key press (simulates term:key event) */\n press(key: string): Promise<void>\n /** Iterate frames yielded after each event */\n [Symbol.asyncIterator](): AsyncIterator<Buffer>\n}\n\n/**\n * App definition returned by createApp().\n */\nexport interface AppDefinition<S> {\n run(element: ReactElement, options?: AppRunOptions): AppRunner<S>\n}\n\n/**\n * Result of app.run() — both a Promise<AppHandle> and an AsyncIterable<Buffer>.\n *\n * - `await app.run(el)` → AppHandle (backward compat)\n * - `for await (const frame of app.run(el))` → iterate frames\n */\nexport interface AppRunner<S> extends AsyncIterable<Buffer>, PromiseLike<AppHandle<S>> {}\n\n// ============================================================================\n// Store Context\n// ============================================================================\n\nexport const StoreContext = createContext<StoreApi<unknown> | null>(null)\n\n/**\n * Hook for accessing app state with selectors.\n *\n * @example\n * ```tsx\n * const count = useApp(s => s.count)\n * const { count, increment } = useApp(s => ({ count: s.count, increment: s.increment }))\n * ```\n */\nexport function useApp<S, T>(selector: (state: S) => T): T {\n const store = useContext(StoreContext) as StoreApi<S> | null\n if (!store) throw new Error(\"useApp must be used within createApp().run()\")\n\n const [state, setState] = React.useState(() => selector(store.getState()))\n const selectorRef = useRef(selector)\n selectorRef.current = selector\n\n useEffect(() => {\n return store.subscribe((newState) => {\n const next = selectorRef.current(newState)\n // Only update if the selected value actually changed (avoids\n // unnecessary re-renders when unrelated store slices change)\n setState((prev) => (Object.is(prev, next) ? prev : next))\n })\n }, [store])\n\n return state\n}\n\n/**\n * Shallow comparison for plain objects.\n * Returns true if objects have same keys with Object.is() equal values.\n */\nfunction shallowEqual<T>(a: T, b: T): boolean {\n if (Object.is(a, b)) return true\n if (typeof a !== \"object\" || typeof b !== \"object\" || a === null || b === null) {\n return false\n }\n const keysA = Object.keys(a as Record<string, unknown>)\n const keysB = Object.keys(b as Record<string, unknown>)\n if (keysA.length !== keysB.length) return false\n for (const key of keysA) {\n if (!Object.is((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])) {\n return false\n }\n }\n return true\n}\n\n/**\n * Hook for accessing app state with shallow comparison.\n *\n * Like useApp, but uses shallow object comparison instead of Object.is().\n * Use when your selector returns a new object on each call — this prevents\n * re-renders when all individual fields are unchanged.\n *\n * @example\n * ```tsx\n * const { cursor, mode } = useAppShallow(s => ({\n * cursor: s.cursorNodeId,\n * mode: s.viewMode,\n * }))\n * ```\n */\nexport function useAppShallow<S, T>(selector: (state: S) => T): T {\n const store = useContext(StoreContext) as StoreApi<S> | null\n if (!store) throw new Error(\"useAppShallow must be used within createApp().run()\")\n\n const [state, setState] = React.useState(() => selector(store.getState()))\n const selectorRef = useRef(selector)\n selectorRef.current = selector\n\n useEffect(() => {\n return store.subscribe((newState) => {\n const next = selectorRef.current(newState)\n setState((prev) => (shallowEqual(prev, next) ? prev : next))\n })\n }, [store])\n\n return state\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Create an app with Zustand store and provider integration.\n *\n * This is Layer 3 - it provides:\n * - Zustand store with fine-grained subscriptions\n * - Providers as unified stores + event sources\n * - Event handlers namespaced as 'provider:event'\n *\n * @param factory Store factory function that receives providers\n * @param handlers Optional event handlers (namespaced as 'provider:event')\n */\nexport function createApp<I extends Record<string, unknown>, S extends Record<string, unknown>>(\n factory: (inject: I) => StateCreator<S>,\n handlers?: EventHandlers<S & I>,\n): AppDefinition<S & I> {\n return {\n run(element: ReactElement, options: AppRunOptions = {}): AppRunner<S & I> {\n // Lazy-init: the actual setup happens once, on first access\n let handlePromise: Promise<AppHandle<S & I>> | null = null\n\n const init = (): Promise<AppHandle<S & I>> => {\n if (handlePromise) return handlePromise\n handlePromise = initApp(factory, handlers, element, options)\n return handlePromise\n }\n\n return {\n // PromiseLike — makes `await app.run(el)` work\n then<TResult1 = AppHandle<S & I>, TResult2 = never>(\n onfulfilled?: ((value: AppHandle<S & I>) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return init().then(onfulfilled, onrejected)\n },\n\n // AsyncIterable — makes `for await (const frame of app.run(el))` work\n [Symbol.asyncIterator](): AsyncIterator<Buffer> {\n let handle: AppHandle<S & I> | null = null\n let iterator: AsyncIterator<Buffer> | null = null\n let started = false\n\n return {\n async next(): Promise<IteratorResult<Buffer>> {\n if (!started) {\n started = true\n handle = await init()\n iterator = handle[Symbol.asyncIterator]()\n }\n return iterator!.next()\n },\n async return(): Promise<IteratorResult<Buffer>> {\n if (handle) handle.unmount()\n return { done: true, value: undefined as unknown as Buffer }\n },\n }\n },\n }\n },\n }\n}\n\n/**\n * Initialize the app — extracted from run() for clarity.\n */\nasync function initApp<I extends Record<string, unknown>, S extends Record<string, unknown>>(\n factory: (inject: I) => StateCreator<S>,\n handlers: EventHandlers<S & I> | undefined,\n element: ReactElement,\n options: AppRunOptions,\n): Promise<AppHandle<S & I>> {\n const {\n cols: explicitCols,\n rows: explicitRows,\n stdout: explicitStdout,\n stdin = process.stdin,\n signal: externalSignal,\n alternateScreen = false,\n kittyMode: explicitKittyMode,\n kitty: kittyOption,\n mouse: mouseOption = false,\n virtualInline: virtualInlineOption = false,\n suspendOnCtrlZ: suspendOption = true,\n exitOnCtrlC: exitOnCtrlCOption = true,\n onSuspend: onSuspendHook,\n onResume: onResumeHook,\n onInterrupt: onInterruptHook,\n textSizing: textSizingOption,\n widthDetection: widthDetectionOption,\n focusReporting: focusReportingOption = false,\n selection: selectionOption,\n caps: capsOption,\n guardOutput: guardOutputOption,\n Root: RootComponent,\n capabilityRegistry: capabilityRegistryOption,\n writable: explicitWritable,\n onResize: explicitOnResize,\n ...injectValues\n } = options\n\n // Derive kitty mode for press(): use explicit kittyMode if set, otherwise\n // auto-enable when kitty protocol is active (so press() encodes modifier keys correctly)\n const useKittyMode = explicitKittyMode ?? !!kittyOption\n\n const headless = (explicitCols != null && explicitRows != null && !explicitStdout) || explicitWritable != null\n const cols = explicitCols ?? process.stdout.columns ?? 80\n const rows = explicitRows ?? process.stdout.rows ?? 24\n const stdout = explicitStdout ?? process.stdout\n\n // Output guard: created after protocol setup (see below).\n // Only guard when using real process.stdout — mock stdouts don't benefit from\n // the guard (which patches process.stdout.write), and it would route render\n // output to the real stdout instead of the mock.\n const isRealStdout = stdout === process.stdout\n const shouldGuardOutput = guardOutputOption ?? (alternateScreen && !headless && isRealStdout)\n let outputGuard: OutputGuard | null = null\n\n // Initialize layout engine\n await ensureLayoutEngine()\n\n // Create abort controller for cleanup\n const controller = new AbortController()\n const signal = controller.signal\n\n // Wire external signal\n if (externalSignal) {\n if (externalSignal.aborted) {\n controller.abort()\n } else {\n externalSignal.addEventListener(\"abort\", () => controller.abort(), {\n once: true,\n })\n }\n }\n\n // Separate providers from plain values\n const providers: Record<string, Provider<unknown, Record<string, unknown>>> = {}\n const plainValues: Record<string, unknown> = {}\n const providerCleanups: (() => void)[] = []\n\n // Create term provider if not provided\n let termProvider: TermProvider | null = null\n if (!(\"term\" in injectValues) || !isFullProvider(injectValues.term)) {\n // In headless mode, provide mock streams so termProvider doesn't touch real stdin/stdout.\n // When onResize is provided, the mock supports resize events so the term provider\n // picks up dimension changes and triggers re-renders through the event loop.\n const resizeListeners = new Set<() => void>()\n const termStdout = headless\n ? ({\n columns: cols,\n rows,\n write: () => true,\n isTTY: false,\n on(event: string, handler: () => void) {\n if (event === \"resize\") resizeListeners.add(handler)\n return termStdout\n },\n off(event: string, handler: () => void) {\n if (event === \"resize\") resizeListeners.delete(handler)\n return termStdout\n },\n } as unknown as NodeJS.WriteStream)\n : stdout\n const termStdin = headless\n ? ({\n isTTY: false,\n on: () => termStdin,\n off: () => termStdin,\n setRawMode: () => {},\n resume: () => {},\n pause: () => {},\n setEncoding: () => {},\n } as unknown as NodeJS.ReadStream)\n : stdin\n termProvider = createTermProvider(termStdin, termStdout, { cols, rows })\n providers.term = termProvider as unknown as Provider<unknown, Record<string, unknown>>\n providerCleanups.push(() => termProvider![Symbol.dispose]())\n\n // Wire onResize to the mock termStdout so the term provider sees resize events.\n // This updates:\n // 1. currentDims — so getDims() returns correct values for doRender()\n // 2. mock termStdout columns/rows — so the term provider reads correct dimensions\n // 3. mock termStdout resize listeners — triggers term:resize through the provider's\n // event stream → event loop → doRender()\n if (headless && explicitOnResize) {\n const unsub = explicitOnResize((dims) => {\n currentDims = dims\n ;(termStdout as { columns: number; rows: number }).columns = dims.cols\n ;(termStdout as { columns: number; rows: number }).rows = dims.rows\n for (const listener of resizeListeners) listener()\n })\n providerCleanups.push(unsub)\n }\n }\n\n // Categorize injected values\n for (const [name, value] of Object.entries(injectValues)) {\n if (isFullProvider(value)) {\n providers[name] = value\n } else {\n plainValues[name] = value\n }\n }\n\n // Build inject object (providers + plain values)\n const inject = { ...providers, ...plainValues } as I\n\n // Subscribe to provider state changes\n const stateUnsubscribes: (() => void)[] = []\n\n // Create store\n const store = createStore<S & I>((set, get, api) => {\n // Get base state from factory\n const baseState = factory(inject)(\n set as StoreApi<S>[\"setState\"],\n get as StoreApi<S>[\"getState\"],\n api as StoreApi<S>,\n )\n\n // Merge provider references into state (for access via selectors)\n const mergedState: Record<string, unknown> = { ...baseState }\n\n for (const [name, provider] of Object.entries(providers)) {\n mergedState[name] = provider\n\n // Subscribe to provider state changes (basic providers only)\n if (isBasicProvider(provider)) {\n const unsub = provider.subscribe((_providerState) => {\n // Could flatten provider state here if desired\n // For now, just trigger a re-check\n })\n stateUnsubscribes.push(unsub)\n }\n }\n\n // Add plain values\n for (const [name, value] of Object.entries(plainValues)) {\n mergedState[name] = value\n }\n\n return mergedState as S & I\n })\n\n // Track current dimensions\n let currentDims: Dims = { cols, rows }\n\n // Subscribe to stdout resize events so currentDims stays in sync.\n // In headless mode this is handled by explicitOnResize above.\n // In non-headless mode, stdout resize events update currentDims directly\n // and notify mockTerm subscribers (so useSyncExternalStore re-renders).\n if (!headless) {\n const onStdoutResize = () => {\n currentDims = {\n cols: stdout.columns || 80,\n rows: stdout.rows || 24,\n }\n for (const listener of mockTermSubscribers) listener(currentDims)\n }\n stdout.on(\"resize\", onStdoutResize)\n providerCleanups.push(() => stdout.off(\"resize\", onStdoutResize))\n }\n\n let shouldExit = false\n let renderPaused = false\n let isRendering = false // Re-entrancy guard for store subscription\n let inEventHandler = false // True during processEvent/press — suppresses subscription renders\n let pendingRerender = false // Deferred render flag for re-entrancy\n\n // ========================================================================\n // ANSI Trace: SILVERY_TRACE=1 logs all stdout writes with decoded sequences\n // ========================================================================\n const _ansiTrace = !headless && process.env?.SILVERY_TRACE === \"1\"\n\n let _traceSeq = 0\n const _traceStart = performance.now()\n let _origStdoutWrite: typeof process.stdout.write | undefined\n\n if (_ansiTrace) {\n const fs =\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\") as typeof import(\"node:fs\")\n fs.writeFileSync(\"/tmp/silvery-trace.log\", `=== SILVERY TRACE START ===\\n`)\n\n _origStdoutWrite = stdout.write.bind(stdout) as typeof stdout.write\n\n const symbolize = (s: string): string =>\n s\n .replace(/\\x1b\\[\\?1049h/g, \"⟨ALT_ON⟩\")\n .replace(/\\x1b\\[\\?1049l/g, \"⟨ALT_OFF⟩\")\n .replace(/\\x1b\\[2J/g, \"⟨CLEAR⟩\")\n .replace(/\\x1b\\[H/g, \"⟨HOME⟩\")\n .replace(/\\x1b\\[\\?25l/g, \"⟨CUR_HIDE⟩\")\n .replace(/\\x1b\\[\\?25h/g, \"⟨CUR_SHOW⟩\")\n .replace(/\\x1b\\[\\?2026h/g, \"⟨SYNC_ON⟩\")\n .replace(/\\x1b\\[\\?2026l/g, \"⟨SYNC_OFF⟩\")\n .replace(/\\x1b\\[\\?2004h/g, \"⟨BPASTE_ON⟩\")\n .replace(/\\x1b\\[\\?2004l/g, \"⟨BPASTE_OFF⟩\")\n .replace(/\\x1b\\[0m/g, \"⟨RST⟩\")\n .replace(/\\x1b\\[(\\d+);(\\d+)H/g, \"⟨GO $1,$2⟩\")\n .replace(/\\x1b\\[38;5;(\\d+)m/g, \"⟨F$1⟩\")\n .replace(/\\x1b\\[48;5;(\\d+)m/g, \"⟨B$1⟩\")\n .replace(/\\x1b\\[38;2;(\\d+);(\\d+);(\\d+)m/g, \"⟨FR$1,$2,$3⟩\")\n .replace(/\\x1b\\[48;2;(\\d+);(\\d+);(\\d+)m/g, \"⟨BR$1,$2,$3⟩\")\n .replace(/\\x1b\\[1m/g, \"⟨BOLD⟩\")\n .replace(/\\x1b\\[2m/g, \"⟨DIM⟩\")\n .replace(/\\x1b\\[3m/g, \"⟨ITAL⟩\")\n .replace(/\\x1b\\[4m/g, \"⟨UL⟩\")\n .replace(/\\x1b\\[7m/g, \"⟨INV⟩\")\n .replace(/\\x1b\\[22m/g, \"⟨/BOLD⟩\")\n .replace(/\\x1b\\[23m/g, \"⟨/ITAL⟩\")\n .replace(/\\x1b\\[24m/g, \"⟨/UL⟩\")\n .replace(/\\x1b\\[27m/g, \"⟨/INV⟩\")\n .replace(/\\x1b\\[39m/g, \"⟨/FG⟩\")\n .replace(/\\x1b\\[49m/g, \"⟨/BG⟩\")\n // Catch remaining CSI sequences\n .replace(/\\x1b\\[([0-9;]*)([A-Za-z])/g, \"⟨CSI $1$2⟩\")\n // Catch remaining ESC sequences\n .replace(/\\x1b([^\\[])/, \"⟨ESC $1⟩\")\n\n const traceWrite = function (this: typeof stdout, chunk: unknown, ...args: unknown[]): boolean {\n const str = typeof chunk === \"string\" ? chunk : String(chunk)\n const seq = ++_traceSeq\n const ms = (performance.now() - _traceStart).toFixed(0)\n const decoded = symbolize(str)\n // Truncate for readability but keep enough to identify content\n const preview =\n decoded.length > 400 ? decoded.slice(0, 200) + ` ...[${decoded.length}ch]... ` + decoded.slice(-100) : decoded\n fs.appendFileSync(\n \"/tmp/silvery-trace.log\",\n `[${String(seq).padStart(4, \"0\")}] +${ms}ms (${str.length}b): ${preview}\\n`,\n )\n return (_origStdoutWrite as Function).call(this, chunk, ...args)\n } as typeof stdout.write\n\n stdout.write = traceWrite\n // Restore original stdout.write on cleanup (providerCleanups runs during cleanup())\n providerCleanups.push(() => {\n if (_origStdoutWrite) stdout.write = _origStdoutWrite\n })\n }\n\n // Create render target\n const target: RenderTarget = headless\n ? {\n write(frame: string) {\n if (explicitWritable) explicitWritable.write(frame)\n },\n getDims: () => currentDims,\n }\n : {\n write(frame: string): void {\n if (_perfLog) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `TARGET.write: ${frame.length} bytes (paused=${renderPaused})\\n`,\n )\n }\n if (!renderPaused) {\n if (outputGuard) {\n outputGuard.writeStdout(frame)\n } else {\n stdout.write(frame)\n }\n }\n },\n getDims(): Dims {\n return currentDims\n },\n onResize(handler: (dims: Dims) => void): () => void {\n const onResize = () => {\n currentDims = {\n cols: stdout.columns || 80,\n rows: stdout.rows || 24,\n }\n handler(currentDims)\n }\n stdout.on(\"resize\", onResize)\n return () => stdout.off(\"resize\", onResize)\n },\n }\n\n // Resolve textSizing from caps + option\n // For \"auto\": use heuristic first, probe to verify if heuristic says yes\n // For \"probe\": start disabled, probe async to determine\n // For true/false: use directly\n const heuristicSupported = capsOption?.textSizingSupported ?? isTextSizingLikelySupported()\n const shouldProbe = textSizingOption === \"probe\" || (textSizingOption === \"auto\" && heuristicSupported)\n // If we have a cached probe result, use it immediately instead of probing again\n const cachedProbe = shouldProbe ? getCachedProbeResult() : undefined\n let textSizingEnabled: boolean\n if (textSizingOption === true) {\n textSizingEnabled = true\n } else if (textSizingOption === \"probe\") {\n // \"probe\": start disabled unless cache says supported\n textSizingEnabled = cachedProbe?.supported ?? false\n } else if (textSizingOption === \"auto\") {\n if (cachedProbe !== undefined) {\n // Cache available: use definitive probe result\n textSizingEnabled = cachedProbe.supported\n } else {\n // No cache: use heuristic for first render, probe will verify\n textSizingEnabled = heuristicSupported\n }\n } else {\n textSizingEnabled = false\n }\n\n // Whether we still need to run the async probe (no cache hit)\n const needsProbe = shouldProbe && cachedProbe === undefined && !headless\n\n // Resolve width detection: \"auto\" enables when caps are provided and not headless\n const needsWidthDetection =\n !headless && (widthDetectionOption === true || (widthDetectionOption === \"auto\" && capsOption != null))\n\n // Track effective caps — may be updated by width detection and text sizing probes\n let effectiveCaps = capsOption ? { ...capsOption, textSizingSupported: textSizingEnabled } : undefined\n\n // Create pipeline config from caps (scoped width measurer + output phase)\n // Use `let` because the pipeline may be recreated after a probe changes textSizing\n let pipelineConfig = effectiveCaps ? createPipeline({ caps: effectiveCaps }) : undefined\n\n // Create runtime (pass scoped output phase to ensure measurer/caps are threaded)\n // mode must match alternateScreen: inline apps (alternateScreen=false) need\n // inline output phase rendering (relative cursor) + scrollback offset tracking.\n const runtime = createRuntime({\n target,\n signal,\n mode: alternateScreen ? \"fullscreen\" : \"inline\",\n outputPhaseFn: pipelineConfig?.outputPhaseFn,\n })\n\n // Cleanup state\n let cleanedUp = false\n let storeUnsubscribeFn: (() => void) | null = null\n // Track protocol state for cleanup and suspend/resume\n let kittyEnabled = false\n const defaultKittyFlags = KittyFlags.DISAMBIGUATE | KittyFlags.REPORT_EVENTS | KittyFlags.REPORT_ALL_KEYS\n let kittyFlags: number = defaultKittyFlags\n let mouseEnabled = false\n let focusReportingEnabled = false\n // Selection requires explicit opt-in — don't hijack mouse clicks by default\n const selectionEnabled = selectionOption ?? false\n let selectionState = createTerminalSelectionState()\n\n // --- Selection bridge ---\n // Listeners for the bridge's subscribe mechanism (used by useSelection)\n const selectionListeners = new Set<() => void>()\n\n /** Notify useSelection() subscribers that selection state changed. */\n function notifySelectionListeners(): void {\n for (const listener of selectionListeners) {\n listener()\n }\n }\n\n // Capability registry: use provided one or create our own so the bridge\n // can be registered and useSelection() works even without withDomEvents().\n const capabilityRegistry: CapabilityRegistry =\n (capabilityRegistryOption as CapabilityRegistry | undefined) ?? createCapabilityRegistry()\n\n // The bridge exposes create-app's selection state via the SelectionFeature\n // interface. React hooks (useSelection) and copy-mode read/write through it.\n let selectionBridge: SelectionFeature | undefined\n if (selectionEnabled) {\n selectionBridge = createSelectionBridge({\n getState: () => selectionState,\n subscribe: (listener) => {\n selectionListeners.add(listener)\n return () => {\n selectionListeners.delete(listener)\n }\n },\n setRange: (range) => {\n if (range === null) {\n const [next] = terminalSelectionUpdate({ type: \"clear\" }, selectionState)\n selectionState = next\n } else {\n // Start at anchor, extend to head, finish\n const [s1] = terminalSelectionUpdate(\n { type: \"start\", col: range.anchor.col, row: range.anchor.row, source: \"keyboard\" },\n selectionState,\n )\n const [s2] = terminalSelectionUpdate({ type: \"extend\", col: range.head.col, row: range.head.row }, s1)\n const [s3] = terminalSelectionUpdate({ type: \"finish\" }, s2)\n selectionState = s3\n }\n notifySelectionListeners()\n // Force re-render to show/clear overlay\n if (currentBuffer) {\n runtime.invalidate()\n }\n },\n clear: () => {\n const [next] = terminalSelectionUpdate({ type: \"clear\" }, selectionState)\n selectionState = next\n notifySelectionListeners()\n if (currentBuffer) {\n runtime.invalidate()\n }\n },\n })\n capabilityRegistry.register(SELECTION_CAPABILITY, selectionBridge)\n }\n\n // Virtual inline mode state\n const scrollback = virtualInlineOption ? createVirtualScrollback() : null\n let virtualScrollOffset = 0 // 0 = live (bottom), >0 = scrolled up\n let searchState = createSearchState()\n\n // Focus manager (tree-based focus system) with event dispatch wiring\n const focusManager = createFocusManager({\n onFocusChange(oldNode, newNode, _origin) {\n // Dispatch blur event on the old element\n if (oldNode) {\n const blurEvent = createFocusEvent(\"blur\", oldNode, newNode)\n dispatchFocusEvent(blurEvent)\n }\n // Dispatch focus event on the new element\n if (newNode) {\n const focusEvent = createFocusEvent(\"focus\", newNode, oldNode)\n dispatchFocusEvent(focusEvent)\n }\n },\n })\n\n // Wire up focus cleanup on node removal — when React unmounts a subtree,\n // the host-config calls this to clear focus if the active element was removed.\n setOnNodeRemoved((removedNode) => focusManager.handleSubtreeRemoved(removedNode))\n\n // Per-instance cursor state (replaces module-level globals)\n const cursorStore = createCursorStore()\n\n // Mouse event processor for DOM-level dispatch (with click-to-focus)\n const mouseEventState = createMouseEventProcessor({ focusManager })\n\n // Cleanup function - idempotent, can be called from exit() or finally\n const cleanup = () => {\n if (cleanedUp) return\n cleanedUp = true\n\n // Log keypress performance summary before teardown (only emits when TRACE was active)\n logExitSummary()\n\n // Unmount React tree first — this runs effect cleanups (clears intervals,\n // cancels subscriptions) before we tear down the infrastructure.\n try {\n reconciler.updateContainerSync(null, fiberRoot, null, () => {})\n reconciler.flushSyncWork()\n } catch {\n // Ignore — component tree may already be partially torn down\n }\n\n // Unregister node removal hook\n setOnNodeRemoved(null)\n\n // Unsubscribe from store\n if (storeUnsubscribeFn) {\n storeUnsubscribeFn()\n }\n\n // Unsubscribe from provider state changes\n stateUnsubscribes.forEach((unsub) => {\n try {\n unsub()\n } catch {\n // Ignore\n }\n })\n\n // Dispose output guard BEFORE terminal protocol cleanup — restores original\n // stdout/stderr write methods so the cleanup sequences go through unimpeded.\n if (outputGuard) {\n outputGuard.dispose()\n outputGuard = null\n }\n\n // === Terminal protocol cleanup ===\n //\n // Order is critical to avoid escape sequence leaks on exit:\n //\n // 1. Stop consuming stdin — remove data listeners so no more events process\n // 2. Send all protocol disable sequences via writeSync (synchronous, reliable)\n // 3. Drain any in-flight stdin bytes (terminal may have queued events before\n // processing our disable sequences — especially Kitty key release events)\n // 4. Disable raw mode and pause stdin\n //\n // Without the drain, Kitty release events (e.g., CSI 113;1:3u for 'q' release)\n // and SGR mouse events appear as garbled text on the shell prompt after exit.\n\n if (!headless && stdin.isTTY) {\n // Step 1: Stop consuming stdin — prevent any more event processing\n stdin.removeAllListeners(\"data\")\n stdin.pause()\n\n // Step 2: Send ALL protocol disable sequences unconditionally.\n // Sending a disable for an inactive protocol is harmless, and unconditional\n // cleanup is more robust than tracking enable/disable state.\n const sequences = [\n \"\\x1b[?1004l\", // Disable focus reporting\n disableMouse(), // Disable SGR mouse tracking (modes 1003, 1006)\n disableKittyKeyboard(), // Pop Kitty keyboard protocol\n \"\\x1b[?2004l\", // Disable bracketed paste\n \"\\x1b[0m\", // Reset SGR attributes\n resetCursorStyle(), // Reset cursor shape to terminal default (DECSCUSR 0)\n \"\\x1b[?25h\", // Show cursor\n alternateScreen ? \"\\x1b[?1049l\" : \"\", // Exit alternate screen\n ].join(\"\")\n\n // Use writeSync for reliability — async write may not flush before exit.\n // For mock/test stdouts, writeSync(fd) bypasses the mock, so fall back.\n const isRealStdout = stdout === process.stdout\n if (isRealStdout) {\n try {\n writeSync((stdout as unknown as { fd: number }).fd, sequences)\n } catch {\n try {\n stdout.write(sequences)\n } catch {\n /* terminal may be gone */\n }\n }\n\n // Step 3: Drain in-flight stdin bytes. The terminal may have already\n // queued events (Kitty release, mouse moves) before processing our\n // disable sequences. Read and discard them so they don't leak to shell.\n //\n // Known limitation: stdin.read() only gets Node's internal buffer.\n // Late-arriving bytes (Kitty release of 'q') in the kernel TTY buffer\n // may leak to the shell as garbled text (e.g., \"3;1:3u\").\n // See bead km-silvery.exit-kitty-leak for investigation.\n try {\n stdin.resume()\n while (stdin.read() !== null) {\n /* discard Node-buffered data */\n }\n stdin.pause()\n } catch {\n // Drain failed — best-effort, continue cleanup\n }\n } else {\n try {\n stdout.write(sequences)\n } catch {\n /* terminal may be gone */\n }\n }\n\n // Step 4: Disable raw mode\n try {\n stdin.setRawMode(false)\n } catch {\n // Ignore — stdin may be closed\n }\n } else if (!headless) {\n // Non-TTY cleanup: just send disable sequences\n const sequences = [\n \"\\x1b[?1004l\",\n disableMouse(),\n disableKittyKeyboard(),\n \"\\x1b[?2004l\",\n \"\\x1b[0m\",\n resetCursorStyle(),\n \"\\x1b[?25h\",\n alternateScreen ? \"\\x1b[?1049l\" : \"\",\n ].join(\"\")\n try {\n stdout.write(sequences)\n } catch {\n /* terminal may be gone */\n }\n }\n\n // Cleanup providers — stdin is already cleaned up above for TTY,\n // but provider cleanup handles other resources (resize listeners, etc.)\n providerCleanups.forEach((fn) => {\n try {\n fn()\n } catch {\n // Ignore\n }\n })\n\n // Dispose runtime\n runtime[Symbol.dispose]()\n }\n\n let exit: () => void // eslint-disable-line prefer-const -- forward declaration, assigned once at L1403\n\n // Create SilveryNode container.\n // onRender fires during React's resetAfterCommit — inside the commit phase.\n // Calling doRender from there would be re-entrant (doRender calls updateContainerSync\n // which triggers commit which calls onRender again). Always defer via microtask.\n // Without this callback, setInterval/setTimeout-driven setState never flushes to terminal.\n const container = createContainer(() => {\n if (shouldExit) return\n if (inEventHandler) {\n // During processEvent/press: just flag, caller's flush loop handles it.\n pendingRerender = true\n return\n }\n // Always defer — onRender fires during React commit, re-entry is unsafe.\n if (!pendingRerender) {\n pendingRerender = true\n queueMicrotask(() => {\n if (!pendingRerender) return\n pendingRerender = false\n if (!shouldExit && !isRendering) {\n isRendering = true\n try {\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n } finally {\n isRendering = false\n }\n }\n })\n }\n })\n\n // Create React fiber root\n const fiberRoot = createFiberRoot(container)\n\n // Track current buffer for text access\n let currentBuffer: Buffer\n\n // Create mock stdout for contexts\n const mockStdout = {\n columns: cols,\n rows: rows,\n write: () => true,\n isTTY: false,\n on: () => mockStdout,\n off: () => mockStdout,\n once: () => mockStdout,\n removeListener: () => mockStdout,\n addListener: () => mockStdout,\n } as unknown as NodeJS.WriteStream\n\n // Create mock term — override getState to return the app's actual dimensions\n // rather than process.stdout dimensions (which may differ in test/emulator contexts).\n // Also override subscribe to notify listeners on resize so useSyncExternalStore\n // (used by useTerm/useWindowSize) triggers re-renders when dimensions change.\n const baseMockTerm = createTerm({ color: \"truecolor\" })\n const mockTermSubscribers = new Set<(state: { cols: number; rows: number }) => void>()\n const mockTerm = Object.create(baseMockTerm, {\n getState: { value: (): { cols: number; rows: number } => currentDims },\n subscribe: {\n value: (listener: (state: { cols: number; rows: number }) => void): (() => void) => {\n mockTermSubscribers.add(listener)\n return () => mockTermSubscribers.delete(listener)\n },\n },\n }) as typeof baseMockTerm\n\n // RuntimeContext input listeners — allows components using hooks/useInput\n // (TextInput, TextArea, SelectList etc.) to work inside createApp apps.\n //\n // V1r apply chain: ordered dispatch, focus lane before fallback, explicit handled.\n // Raw Sets replaced with arrays for ordered iteration.\n const runtimeInputListeners: Array<(input: string, key: Key) => void> = []\n const runtimePasteListeners: Array<(text: string) => void> = []\n const runtimeFocusListeners: Array<(focused: boolean) => void> = []\n\n // Typed event bus — supports view → runtime events via emit()\n const runtimeEventListeners = new Map<string, Array<Function>>()\n runtimeEventListeners.set(\"input\", runtimeInputListeners as unknown as Array<Function>)\n runtimeEventListeners.set(\"paste\", runtimePasteListeners as unknown as Array<Function>)\n runtimeEventListeners.set(\"focus\", runtimeFocusListeners as unknown as Array<Function>)\n\n const runtimeContextValue: RuntimeContextValue = {\n on(event, handler) {\n let listeners = runtimeEventListeners.get(event)\n if (!listeners) {\n listeners = []\n runtimeEventListeners.set(event, listeners)\n }\n listeners.push(handler)\n return () => {\n const idx = listeners!.indexOf(handler)\n if (idx >= 0) listeners!.splice(idx, 1)\n }\n },\n emit(event, ...args) {\n const listeners = runtimeEventListeners.get(event)\n if (listeners) {\n for (const listener of listeners) {\n listener(...args)\n }\n }\n },\n exit: () => exit(),\n }\n\n // Wrap element with all required providers\n // SilveryErrorBoundary is always the outermost wrapper — catches render errors gracefully.\n // If a Root component is provided (e.g., from withInk), wrap the element with it\n // inside silvery's contexts so it can access Term, Stdout, FocusManager, Runtime.\n const Root = RootComponent ?? React.Fragment\n // Cache backend selection:\n // - inline: \"terminal\" — items promoted to real terminal scrollback\n // - fullscreen + virtualInline: \"virtual\" — items stored in HistoryBuffer,\n // viewable via virtual scroll overlay\n // - plain fullscreen: \"retain\" — items cached but kept in the render tree\n // (no scrollback to display unmounted items, virtualizer handles windowing)\n const cacheBackend = !alternateScreen ? \"terminal\" : virtualInlineOption ? \"virtual\" : \"retain\"\n const wrappedElement = (\n <SilveryErrorBoundary>\n <CursorProvider store={cursorStore}>\n <CacheBackendContext.Provider value={cacheBackend}>\n <TermContext.Provider value={mockTerm}>\n <StdoutContext.Provider\n value={{\n stdout: mockStdout,\n write: () => {},\n notifyScrollback: (lines: number) => runtime.addScrollbackLines(lines),\n promoteScrollback: (content: string, lines: number) => runtime.promoteScrollback(content, lines),\n resetInlineCursor: () => runtime.resetInlineCursor(),\n getInlineCursorRow: () => runtime.getInlineCursorRow(),\n }}\n >\n <StderrContext.Provider\n value={{\n stderr: process.stderr,\n write: (data: string) => {\n process.stderr.write(data)\n },\n }}\n >\n <FocusManagerContext.Provider value={focusManager}>\n <RuntimeContext.Provider value={runtimeContextValue}>\n <CapabilityRegistryContext.Provider value={capabilityRegistry}>\n <Root>\n <StoreContext.Provider value={store as StoreApi<unknown>}>{element}</StoreContext.Provider>\n </Root>\n </CapabilityRegistryContext.Provider>\n </RuntimeContext.Provider>\n </FocusManagerContext.Provider>\n </StderrContext.Provider>\n </StdoutContext.Provider>\n </TermContext.Provider>\n </CacheBackendContext.Provider>\n </CursorProvider>\n </SilveryErrorBoundary>\n )\n\n // Performance instrumentation — count renders per event\n let _renderCount = 0\n let _eventStart = 0\n const _perfLog = typeof process !== \"undefined\" && process.env?.DEBUG?.includes(\"silvery:perf\")\n\n // Incremental rendering via long-lived Ag instance.\n // The Ag manages its own prevBuffer for incremental rendering.\n // Set SILVERY_NO_INCREMENTAL=1 to disable (for debugging blank screen issues).\n // _noIncremental aliases the module-level NO_INCREMENTAL constant for readability.\n const _noIncremental = NO_INCREMENTAL\n\n // Long-lived Ag instance — created lazily on first doRender() after reconciler\n // produces the root node. Reused across all subsequent frames, avoiding per-frame\n // pipeline state allocation. The Ag manages its own prevBuffer for incremental\n // content rendering.\n let _ag: Ag | null = null\n // Track the last TerminalBuffer for dimension-change detection (the Ag manages\n // prevBuffer internally, but we need the dimensions for resize detection).\n let _lastTermBuffer: import(\"@silvery/ag-term/buffer\").TerminalBuffer | null = null\n\n // Helper to render and get text\n function doRender(): Buffer {\n _renderCount++\n if (_ansiTrace) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-trace.log\",\n `--- doRender #${_renderCount} (ag=${_ag ? \"reuse\" : \"create\"}, incremental=${!_noIncremental}) ---\\n`,\n )\n }\n const renderStart = performance.now()\n\n // Phase A: React reconciliation\n reconciler.updateContainerSync(wrappedElement, fiberRoot, null, () => {})\n reconciler.flushSyncWork()\n const reconcileMs = performance.now() - renderStart\n\n // Bench instrumentation: accumulate reconcile time. The pipeline accumulator\n // (set by silveryBenchStart) catches measure/layout/content/output; reconcile\n // lives outside pipeline/index.ts so we add it here.\n {\n const acc = (globalThis as any).__silvery_bench_phases\n if (acc) acc.reconcile += reconcileMs\n }\n\n // Phase B: Render pipeline (incremental when prevBuffer available)\n const pipelineStart = performance.now()\n const rootNode = getContainerRoot(container)\n const dims = runtime.getDims()\n\n const isInline = !alternateScreen\n\n // Create or reuse long-lived Ag instance. Created lazily because the root\n // AgNode is produced by the React reconciler in Phase A above.\n if (!_ag) {\n _ag = createAg(rootNode, { measurer: pipelineConfig?.measurer })\n }\n\n // Invalidate prevBuffer on dimension change (resize).\n // Both Ag-level (ag.resetBuffer()) and runtime-level (runtime.invalidate())\n // must be cleared — otherwise the ANSI diff compares different-sized buffers.\n //\n // In inline mode, only WIDTH changes trigger invalidation. Height changes are\n // normal (content grows/shrinks as items are added/frozen) and are handled\n // incrementally by the output phase. Invalidating on height causes the runtime's\n // prevBuffer to be null, which triggers the first-render clear path with \\x1b[J\n // — wiping the entire visible screen including shell prompt content above the app.\n if (_ag) {\n // Check dimension changes. On first render there's no prevBuffer to compare.\n const lastBuffer = _lastTermBuffer\n if (lastBuffer) {\n const widthChanged = dims.cols !== lastBuffer.width\n const heightChanged = !isInline && dims.rows !== lastBuffer.height\n if (widthChanged || heightChanged) {\n _ag.resetBuffer()\n runtime.invalidate()\n }\n }\n }\n\n // Clear diagnostic arrays before the render so we capture only this render's data.\n // INSTRUMENTED is hoisted from env vars at module load — when no diagnostic is\n // active (the hot path), all three global resets and the cell-debug setup\n // constant-fold out of the frame.\n if (INSTRUMENTED) {\n ;(globalThis as any).__silvery_content_all = undefined\n ;(globalThis as any).__silvery_node_trace = undefined\n // Cell debug: enable during real incremental render for SILVERY_STRICT diagnosis.\n // Set SILVERY_CELL_DEBUG=x,y to trace which nodes cover a specific cell.\n // The log is captured during the render and included in any mismatch error.\n ;(globalThis as any).__silvery_cell_debug =\n CELL_DEBUG !== null ? { x: CELL_DEBUG.x, y: CELL_DEBUG.y, log: [] as string[] } : undefined\n }\n\n // Early return: if reconciliation produced no dirty flags on the tree,\n // skip the pipeline entirely. This avoids cloning prevBuffer (which\n // resets dirty rows to 0), preserving the row-level dirty markers that\n // the runtime diff needs to detect actual changes.\n // Exception: dimension changes require re-layout even without dirty flags.\n const rootHasDirty = rootNode.layoutDirty || isAnyDirty(rootNode.dirtyBits, rootNode.dirtyEpoch)\n const dimsChanged =\n _lastTermBuffer != null && (dims.cols !== _lastTermBuffer.width || dims.rows !== _lastTermBuffer.height)\n if (!rootHasDirty && !dimsChanged && _lastTermBuffer && currentBuffer) {\n return currentBuffer\n }\n\n // When SILVERY_NO_INCREMENTAL is set, force fresh render every frame\n if (_noIncremental) {\n _ag.resetBuffer()\n }\n\n // Run layout + content render via the long-lived Ag instance.\n // The Ag manages prevBuffer internally for incremental rendering.\n // Output phase is NOT run here — the runtime handles it separately.\n _ag.layout(dims)\n const { buffer: termBuffer, prevBuffer: agPrevBuffer } = _ag.render()\n _lastTermBuffer = termBuffer\n const wasIncremental = !_noIncremental && agPrevBuffer !== null\n const pipelineMs = performance.now() - pipelineStart\n\n // Expose timing for diagnostics (previously done by executeRenderCore).\n // Output timing is 0 here — the runtime handles the output phase separately.\n ;(globalThis as any).__silvery_last_pipeline = {\n layout: pipelineMs,\n output: 0,\n total: pipelineMs,\n incremental: wasIncremental,\n }\n ;(globalThis as any).__silvery_render_count = ((globalThis as any).__silvery_render_count ?? 0) + 1\n\n // Bench instrumentation: accumulate pipeline-level timing.\n // ag.ts handles measure/layout/content accumulation; we add total here.\n {\n const acc = (globalThis as any).__silvery_bench_phases\n if (acc) {\n acc.total += pipelineMs\n acc.pipelineCalls += 1\n }\n }\n\n // SILVERY_STRICT: compare incremental render against fresh render.\n // createApp bypasses Scheduler/Renderer which have this check built-in,\n // so we add it here to catch incremental rendering bugs at runtime.\n // STRICT_MODE is hoisted to module scope — the env var is read once at load.\n if (STRICT_MODE && wasIncremental) {\n const { buffer: freshBuffer } = executeRender(\n rootNode,\n dims.cols,\n dims.rows,\n null,\n {\n skipLayoutNotifications: true,\n skipScrollStateUpdates: true,\n },\n pipelineConfig,\n )\n const { cellEquals, bufferToText } =\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"@silvery/ag-term/buffer\") as typeof import(\"@silvery/ag-term/buffer\")\n for (let y = 0; y < termBuffer.height; y++) {\n for (let x = 0; x < termBuffer.width; x++) {\n const a = termBuffer.getCell(x, y)\n const b = freshBuffer.getCell(x, y)\n if (!cellEquals(a, b)) {\n // Use cell debug log collected during the real incremental render\n let cellDebugInfo = \"\"\n const savedCellDbg = (globalThis as any).__silvery_cell_debug as\n | { x: number; y: number; log: string[] }\n | undefined\n if (savedCellDbg && savedCellDbg.x === x && savedCellDbg.y === y && savedCellDbg.log.length > 0) {\n cellDebugInfo = `\\nCELL DEBUG (${savedCellDbg.log.length} entries for (${x},${y})):\\n${savedCellDbg.log.join(\"\\n\")}\\n`\n } else if (savedCellDbg && savedCellDbg.x === x && savedCellDbg.y === y) {\n cellDebugInfo = `\\nCELL DEBUG: No nodes cover (${x},${y}) during incremental render\\n`\n } else {\n cellDebugInfo = `\\nCELL DEBUG: Target cell (${x},${y}) differs from debug cell (${savedCellDbg?.x},${savedCellDbg?.y})\\n`\n }\n\n // Re-run fresh render with write trap to capture what writes to the mismatched cell\n let trapInfo = \"\"\n const trap = { x, y, log: [] as string[] }\n ;(globalThis as any).__silvery_write_trap = trap\n try {\n executeRender(\n rootNode,\n dims.cols,\n dims.rows,\n null,\n {\n skipLayoutNotifications: true,\n skipScrollStateUpdates: true,\n },\n pipelineConfig,\n )\n } catch {\n // ignore\n }\n ;(globalThis as any).__silvery_write_trap = null\n if (trap.log.length > 0) {\n trapInfo = `\\nWRITE TRAP (${trap.log.length} writes to (${x},${y})):\\n${trap.log.join(\"\\n\")}\\n`\n } else {\n trapInfo = `\\nWRITE TRAP: NO WRITES to (${x},${y})\\n`\n }\n const incText = bufferToText(termBuffer)\n const freshText = bufferToText(freshBuffer)\n const cellStr = (c: typeof a) =>\n `char=${JSON.stringify(c.char)} fg=${c.fg} bg=${c.bg} ulColor=${c.underlineColor} wide=${c.wide} cont=${c.continuation} attrs={bold=${c.attrs.bold},dim=${c.attrs.dim},italic=${c.attrs.italic},ul=${c.attrs.underline},ulStyle=${c.attrs.underlineStyle},blink=${c.attrs.blink},inv=${c.attrs.inverse},hidden=${c.attrs.hidden},strike=${c.attrs.strikethrough}}`\n // Dump render phase stats for diagnosis\n const contentAll = (globalThis as any).__silvery_content_all as unknown[]\n const statsStr = contentAll\n ? `\\n--- render phase stats (${contentAll.length} calls) ---\\n` +\n contentAll\n .map(\n (s: any, i: number) =>\n ` #${i}: visited=${s.nodesVisited} rendered=${s.nodesRendered} skipped=${s.nodesSkipped} ` +\n `clearOps=${s.clearOps} cascade=\"${s.cascadeNodes}\" ` +\n `flags={C=${s.flagContentDirty} P=${s.flagStylePropsDirty} L=${s.flagLayoutChanged} ` +\n `S=${s.flagSubtreeDirty} Ch=${s.flagChildrenDirty} CP=${s.flagChildPositionChanged} AL=${s.flagAncestorLayoutChanged} noPrev=${s.noPrevBuffer}} ` +\n `scroll={containers=${s.scrollContainerCount} cleared=${s.scrollViewportCleared} reason=\"${s.scrollClearReason}\"} ` +\n `normalRepaint=\"${s.normalRepaintReason}\" ` +\n `prevBuf={null=${s._prevBufferNull} dimMismatch=${s._prevBufferDimMismatch} hasPrev=${s._hasPrevBuffer} ` +\n `layout=${s._layoutW}x${s._layoutH} prev=${s._prevW}x${s._prevH}}`,\n )\n .join(\"\\n\")\n : \"\"\n const msg =\n `SILVERY_STRICT (createApp): MISMATCH at (${x}, ${y}) on render #${_renderCount}\\n` +\n ` incremental: ${cellStr(a)}\\n` +\n ` fresh: ${cellStr(b)}` +\n statsStr +\n // Per-node trace\n (() => {\n const traces = (globalThis as any).__silvery_node_trace as unknown[][] | undefined\n if (!traces || traces.length === 0) return \"\"\n let out = \"\\n--- node trace ---\"\n for (let ti = 0; ti < traces.length; ti++) {\n out += `\\n renderPhase #${ti}:`\n for (const t of traces[ti] as any[]) {\n out += `\\n ${t.decision} ${t.id}(${t.type})@${t.depth} rect=${t.rect} prev=${t.prevLayout}`\n out += ` hasPrev=${t.hasPrev} ancClr=${t.ancestorCleared} flags=[${t.flags}] layout∆=${t.layoutChanged}`\n if (t.decision === \"RENDER\") {\n out += ` caa=${t.contentAreaAffected} crc=${t.contentRegionCleared} cnfr=${t.childrenNeedFreshRender}`\n out += ` childPrev=${t.childHasPrev} childAnc=${t.childAncestorCleared} skipBg=${t.skipBgFill} bg=${t.bgColor ?? \"none\"}`\n }\n }\n }\n return out\n })() +\n cellDebugInfo +\n trapInfo +\n `\\n--- incremental ---\\n${incText}\\n--- fresh ---\\n${freshText}`\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\"/tmp/silvery-perf.log\", msg + \"\\n\")\n // Also throw to make it visible\n throw new IncrementalRenderMismatchError(msg)\n }\n }\n }\n if (_perfLog) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `SILVERY_STRICT (createApp): render #${_renderCount} OK\\n`,\n )\n }\n }\n\n const buf = createBuffer(termBuffer, rootNode)\n if (_perfLog) {\n const renderDuration = performance.now() - renderStart\n const phases = (globalThis as any).__silvery_last_pipeline\n const detail = (globalThis as any).__silvery_content_detail\n const phaseStr = phases\n ? ` [measure=${phases.measure.toFixed(1)} layout=${phases.layout.toFixed(1)} content=${phases.content.toFixed(1)} output=${phases.output.toFixed(1)}]`\n : \"\"\n const detailStr = detail\n ? ` {visited=${detail.nodesVisited} rendered=${detail.nodesRendered} skipped=${detail.nodesSkipped} noPrev=${detail.noPrevBuffer ?? 0} dirty=${detail.flagContentDirty ?? 0} paint=${detail.flagStylePropsDirty ?? 0} layoutChg=${detail.flagLayoutChanged ?? 0} subtree=${detail.flagSubtreeDirty ?? 0} children=${detail.flagChildrenDirty ?? 0} childPos=${detail.flagChildPositionChanged ?? 0} scroll=${detail.scrollContainerCount ?? 0}/${detail.scrollViewportCleared ?? 0}${detail.scrollClearReason ? `(${detail.scrollClearReason})` : \"\"}}${detail.cascadeNodes ? ` CASCADE[minDepth=${detail.cascadeMinDepth} ${detail.cascadeNodes}]` : \"\"}`\n : \"\"\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `doRender #${_renderCount}: ${renderDuration.toFixed(1)}ms (reconcile=${reconcileMs.toFixed(1)}ms pipeline=${pipelineMs.toFixed(1)}ms ${dims.cols}x${dims.rows})${phaseStr}${detailStr}\\n`,\n )\n }\n return buf\n }\n\n // Initial render\n if (_ansiTrace) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\"/tmp/silvery-trace.log\", \"=== INITIAL RENDER ===\\n\")\n }\n currentBuffer = doRender()\n\n // Enter alternate screen if requested, then clear and hide cursor\n if (!headless) {\n if (_ansiTrace) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\"/tmp/silvery-trace.log\", \"=== ALT SCREEN + CLEAR ===\\n\")\n }\n if (alternateScreen) {\n stdout.write(\"\\x1b[?1049h\")\n stdout.write(\"\\x1b[2J\\x1b[H\")\n }\n stdout.write(\"\\x1b[?25l\")\n\n // Kitty keyboard protocol\n if (kittyOption != null && kittyOption !== false) {\n if (kittyOption === true) {\n // Auto-detect: probe terminal, enable if supported\n const result = await detectKittyFromStdio(stdout, stdin as NodeJS.ReadStream)\n if (result.supported) {\n stdout.write(enableKittyKeyboard(defaultKittyFlags))\n kittyEnabled = true\n kittyFlags = defaultKittyFlags\n }\n } else {\n // Explicit flags — enable directly without detection\n stdout.write(enableKittyKeyboard(kittyOption as 1))\n kittyEnabled = true\n kittyFlags = kittyOption as number\n }\n } else if (kittyOption == null) {\n // No option specified: legacy behavior — always enable Kitty with full fidelity\n stdout.write(enableKittyKeyboard(defaultKittyFlags))\n kittyEnabled = true\n kittyFlags = defaultKittyFlags\n }\n\n // Mouse tracking\n if (mouseOption) {\n stdout.write(enableMouse())\n mouseEnabled = true\n }\n\n // Focus reporting is deferred to after the event loop starts (see below).\n // Enabling it here would cause the terminal's immediate CSI I/O response\n // to arrive before the input parser's stdin listener is attached, leaking\n // raw escape sequences to the screen.\n }\n if (_ansiTrace) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\"/tmp/silvery-trace.log\", \"=== RUNTIME.RENDER (initial) ===\\n\")\n }\n runtime.render(currentBuffer)\n if (_perfLog) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `STARTUP: initial render done (render #${_renderCount}, incremental=${!_noIncremental})\\n`,\n )\n }\n\n // Activate output guard after protocol setup and initial render are done.\n // This intercepts process.stdout/stderr writes so that only silvery's\n // render pipeline can write to stdout — all other writes are suppressed\n // (stdout) or redirected to DEBUG_LOG (stderr).\n if (shouldGuardOutput) {\n outputGuard = createOutputGuard()\n }\n\n // Assign pause/resume now that doRender and runtime are available.\n // Update runtimeContextValue in-place so useApp()/useRuntime() sees the latest values.\n if (!headless) {\n runtimeContextValue.pause = () => {\n renderPaused = true\n // Temporarily dispose the output guard so console-mode writes\n // (e.g., log dump) reach the terminal directly.\n if (outputGuard) {\n outputGuard.dispose()\n outputGuard = null\n }\n if (alternateScreen) stdout.write(leaveAlternateScreen())\n }\n runtimeContextValue.resume = () => {\n if (alternateScreen) stdout.write(enterAlternateScreen())\n renderPaused = false\n // Re-create the output guard (disposed during pause)\n if (shouldGuardOutput && !outputGuard) {\n outputGuard = createOutputGuard()\n }\n // Reset diff state so next render outputs a full frame.\n // The screen was cleared when entering console mode, so\n // incremental diffing would produce an incomplete frame.\n runtime.invalidate()\n _ag?.resetBuffer()\n // Force full re-render to restore display, but only if we're not\n // already inside a doRender() call (e.g. when resume() is called\n // from a React effect cleanup during reconciliation).\n if (!isRendering) {\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n }\n // If isRendering is true, the outer doRender()/runtime.render() will\n // handle the re-render after effects complete, with renderPaused=false.\n }\n }\n\n // Exit promise\n let exitResolve: () => void\n let exitResolved = false\n const exitPromise = new Promise<void>((resolve) => {\n exitResolve = () => {\n if (!exitResolved) {\n exitResolved = true\n resolve()\n }\n }\n })\n\n // Now define exit function (needs exitResolve and cleanup)\n //\n // When called from within the event pump (key handler returns \"exit\"),\n // we send protocol disable sequences immediately but defer the full\n // cleanup (drain + raw mode) to the pump's finally block. This gives\n // the event loop time to receive late-arriving bytes (e.g., Kitty\n // keyboard release events) before we hand stdin back to the shell.\n //\n // When called from outside the pump (signal handler, direct call),\n // we do sync cleanup immediately (best-effort).\n exit = () => {\n if (shouldExit) return // Already exiting\n shouldExit = true\n\n // Immediately disable protocols that generate async responses.\n // This is the earliest possible moment — before the terminal\n // sends any more events in response to the exit key.\n if (!headless && stdout.isTTY) {\n const earlyDisable = [\n disableKittyKeyboard(), // Stop Kitty release events\n disableMouse(), // Stop mouse events\n \"\\x1b[?1004l\", // Stop focus reporting\n ].join(\"\")\n try {\n writeSync((stdout as unknown as { fd: number }).fd, earlyDisable)\n } catch {\n try {\n stdout.write(earlyDisable)\n } catch {\n /* terminal may be gone */\n }\n }\n }\n\n controller.abort()\n\n // If we're inside the event pump, defer cleanup — the pump's\n // finally block will call cleanupAfterDrain() with an async drain.\n // If we're outside (signal handler, etc.), do sync cleanup now.\n if (!inEventHandler) {\n cleanup()\n exitResolve()\n }\n // else: pump's finally block handles cleanup + exitResolve\n }\n runtimeContextValue.exit = exit\n\n // Frame listeners for async iteration\n let frameResolve: ((buffer: Buffer) => void) | null = null\n let framesDone = false\n\n // Notify frame listeners\n function emitFrame(buf: Buffer) {\n if (frameResolve) {\n const resolve = frameResolve\n frameResolve = null\n resolve(buf)\n }\n }\n\n // Subscribe to store for re-renders.\n //\n // Three cases:\n // 1. inEventHandler=true (during processEvent/press): ONLY flag pendingRerender.\n // The caller's flush loop will handle all deferred renders. No microtask.\n // 2. isRendering=true (during doRender effects): defer via pendingRerender flag.\n // Queue a microtask to render after the current render completes — but only\n // if NOT in an event handler (the flush loop handles it).\n // 3. Neither: render immediately (standalone setState from timeout/interval).\n storeUnsubscribeFn = store.subscribe(() => {\n if (shouldExit) return\n if (_ansiTrace) {\n const _case = inEventHandler ? \"1:event\" : isRendering ? \"2:rendering\" : \"3:standalone\"\n const stack = new Error().stack?.split(\"\\n\").slice(1, 5).join(\"\\n\") ?? \"\"\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-trace.log\",\n `=== SUBSCRIPTION (case ${_case}, render #${_renderCount + 1}) ===\\n${stack}\\n`,\n )\n }\n if (inEventHandler) {\n // During processEvent/press: just flag, caller's flush loop handles it.\n pendingRerender = true\n return\n }\n if (isRendering) {\n // During doRender (outside event handler): defer to microtask.\n if (!pendingRerender) {\n pendingRerender = true\n queueMicrotask(() => {\n if (!pendingRerender) return\n pendingRerender = false\n if (!shouldExit && !isRendering) {\n if (_perfLog) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `SUBSCRIPTION: deferred microtask render (case 2, render #${_renderCount + 1})\\n`,\n )\n }\n isRendering = true\n try {\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n } finally {\n isRendering = false\n }\n }\n })\n }\n return\n }\n if (_perfLog) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `SUBSCRIPTION: immediate render (case 3, render #${_renderCount + 1})\\n`,\n )\n }\n isRendering = true\n try {\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n } finally {\n isRendering = false\n }\n })\n\n // Create namespaced event streams from all providers\n function createProviderEventStream(\n name: string,\n provider: Provider<unknown, Record<string, unknown>>,\n ): AsyncIterable<NamespacedEvent> {\n return map(provider.events(), (event) => ({\n type: `${name}:${String(event.type)}`,\n provider: name,\n event: String(event.type),\n data: event.data,\n }))\n }\n\n /**\n * Write selection overlay to stdout after a render.\n * Appends inverse-video ANSI sequences over selected cells.\n */\n function writeSelectionOverlay(): void {\n if (!selectionEnabled || !selectionState.range || !currentBuffer) return\n const mode = alternateScreen ? \"fullscreen\" : \"inline\"\n const overlay = renderSelectionOverlay(selectionState.range, currentBuffer._buffer, mode, selectionState.scope)\n if (overlay) target.write(overlay)\n }\n\n /**\n * Push the current rendered frame to the virtual scrollback buffer.\n */\n function pushToScrollback(): void {\n if (!scrollback || !currentBuffer) return\n const lines = currentBuffer.text.split(\"\\n\")\n scrollback.push(lines)\n }\n\n /**\n * Render the virtual scrollback view (historical content) to the terminal.\n * When scrolled up, replaces the live app content with historical rows.\n */\n function renderVirtualScrollbackView(): void {\n if (!scrollback || virtualScrollOffset <= 0) return\n const dims = target.getDims()\n const rows = scrollback.getVisibleRows(virtualScrollOffset, dims.rows)\n\n // Clear screen and write rows using absolute positioning\n let out = \"\"\n for (let row = 0; row < rows.length; row++) {\n out += `\\x1b[${row + 1};1H\\x1b[2K${rows[row] ?? \"\"}`\n }\n\n // Scroll indicator at top-right\n const indicator = ` ↑ ${virtualScrollOffset} lines `\n const indicatorCol = Math.max(1, dims.cols - indicator.length + 1)\n out += `\\x1b[1;${indicatorCol}H\\x1b[7m${indicator}\\x1b[27m`\n\n target.write(out)\n }\n\n /**\n * Render search highlights for the current match with inverse video.\n */\n function renderSearchHighlights(): void {\n if (!searchState.active || searchState.currentMatch < 0) return\n const match = searchState.matches[searchState.currentMatch]\n if (!match) return\n\n const dims = target.getDims()\n // Calculate the screen row of the current match\n let screenRow: number\n if (scrollback && virtualScrollOffset > 0) {\n // In scrollback view: calculate relative position\n const totalLines = scrollback.totalLines\n const firstVisibleLine = totalLines - virtualScrollOffset - dims.rows\n screenRow = match.row - firstVisibleLine\n } else {\n screenRow = match.row\n }\n\n if (screenRow < 0 || screenRow >= dims.rows) return\n\n // Move to match position and render with inverse\n let out = `\\x1b[${screenRow + 1};${match.startCol + 1}H\\x1b[7m`\n // Emit the match text (we know the query length)\n for (let col = match.startCol; col <= match.endCol; col++) {\n if (currentBuffer && virtualScrollOffset <= 0) {\n out += currentBuffer._buffer.getCell(col, screenRow).char\n } else {\n out += searchState.query[col - match.startCol] ?? \" \"\n }\n }\n out += \"\\x1b[27m\"\n target.write(out)\n }\n\n /**\n * Render the search bar at the bottom of the screen.\n */\n function renderSearchBarOverlay(): void {\n if (!searchState.active) return\n const dims = target.getDims()\n const bar = renderSearchBar(searchState, dims.cols)\n // Position at the last row\n target.write(`\\x1b[${dims.rows};1H${bar}`)\n }\n\n /**\n * Search function for virtual scrollback — converts line matches to SearchMatch[].\n */\n function searchScrollback(query: string): SearchMatch[] {\n if (!scrollback || !query) return []\n const matchingLines = scrollback.search(query)\n const lowerQuery = query.toLowerCase()\n const matches: SearchMatch[] = []\n for (const lineIdx of matchingLines) {\n // Find exact column positions by getting the line text\n const rows = scrollback.getVisibleRows(scrollback.totalLines - lineIdx - 1, 1)\n const line = rows[0] ?? \"\"\n // Strip ANSI for column matching\n const plain = line.replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, \"\")\n let col = plain.toLowerCase().indexOf(lowerQuery)\n while (col !== -1) {\n matches.push({ row: lineIdx, startCol: col, endCol: col + query.length - 1 })\n col = plain.toLowerCase().indexOf(lowerQuery, col + 1)\n }\n }\n return matches\n }\n\n /**\n * Run a single event's handler (state mutation only, no render).\n * Returns true if processing should continue, false if app should exit.\n *\n * Intercepts mouse events for selection and virtual inline mode.\n */\n function runEventHandler(event: NamespacedEvent): boolean | \"flush\" {\n // Virtual inline: intercept search key events\n if (scrollback && searchState.active && event.type === \"term:key\") {\n const data = event.data as { input: string; key: Key }\n if (data.key.escape) {\n const [next] = searchUpdate({ type: \"close\" }, searchState)\n searchState = next\n virtualScrollOffset = 0 // Return to live view\n return true // Consume\n }\n if (data.key.return && !data.key.shift) {\n const [next, effects] = searchUpdate({ type: \"nextMatch\" }, searchState, searchScrollback)\n searchState = next\n for (const eff of effects) {\n if (eff.type === \"scrollTo\") {\n virtualScrollOffset = Math.max(0, scrollback.totalLines - eff.row - target.getDims().rows)\n }\n }\n return true\n }\n if (data.key.return && data.key.shift) {\n const [next, effects] = searchUpdate({ type: \"prevMatch\" }, searchState, searchScrollback)\n searchState = next\n for (const eff of effects) {\n if (eff.type === \"scrollTo\") {\n virtualScrollOffset = Math.max(0, scrollback.totalLines - eff.row - target.getDims().rows)\n }\n }\n return true\n }\n if (data.key.backspace) {\n const [next, effects] = searchUpdate({ type: \"backspace\" }, searchState, searchScrollback)\n searchState = next\n for (const eff of effects) {\n if (eff.type === \"scrollTo\") {\n virtualScrollOffset = Math.max(0, scrollback.totalLines - eff.row - target.getDims().rows)\n }\n }\n return true\n }\n if (data.key.leftArrow) {\n const [next] = searchUpdate({ type: \"cursorLeft\" }, searchState)\n searchState = next\n return true\n }\n if (data.key.rightArrow) {\n const [next] = searchUpdate({ type: \"cursorRight\" }, searchState)\n searchState = next\n return true\n }\n if (data.input && !data.key.ctrl && !data.key.meta) {\n const [next, effects] = searchUpdate({ type: \"input\", char: data.input }, searchState, searchScrollback)\n searchState = next\n for (const eff of effects) {\n if (eff.type === \"scrollTo\") {\n virtualScrollOffset = Math.max(0, scrollback.totalLines - eff.row - target.getDims().rows)\n }\n }\n return true\n }\n }\n\n // Virtual inline: Ctrl+F opens search\n if (scrollback && event.type === \"term:key\") {\n const data = event.data as { input: string; key: Key }\n if (data.input === \"f\" && data.key.ctrl) {\n const [next] = searchUpdate({ type: \"open\" }, searchState)\n searchState = next\n return true\n }\n }\n\n // Virtual inline: intercept wheel events for scrolling\n if (scrollback && event.event === \"mouse\" && event.data) {\n const mouseData = event.data as {\n button: number\n x: number\n y: number\n action: string\n delta?: number\n }\n if (mouseData.action === \"wheel\") {\n const scrollLines = 3\n if (mouseData.delta && mouseData.delta < 0) {\n // Scroll up (into history)\n virtualScrollOffset = Math.min(\n virtualScrollOffset + scrollLines,\n Math.max(0, scrollback.totalLines - target.getDims().rows),\n )\n } else {\n // Scroll down (toward live)\n virtualScrollOffset = Math.max(0, virtualScrollOffset - scrollLines)\n }\n return true // Consume wheel events\n }\n }\n\n // Selection: intercept mouse events\n if (selectionEnabled && event.event === \"mouse\" && event.data) {\n const mouseData = event.data as {\n button: number\n x: number\n y: number\n action: string\n }\n\n // Left button (button 0) drag for selection\n if (mouseData.button === 0) {\n if (mouseData.action === \"down\") {\n // Clear any existing selection first, then start new\n if (selectionState.range) {\n const [cleared] = terminalSelectionUpdate({ type: \"clear\" }, selectionState)\n selectionState = cleared\n }\n // Resolve contain boundary from the node under the cursor.\n // If the click lands inside a `userSelect=\"contain\"` subtree, the selection\n // range is clamped to that ancestor's scrollRect so drags can't leak into\n // adjacent siblings. selectionHitTest uses the selection-aware walk\n // (respects userSelect=\"none\" subtrees) rather than pointer hit test.\n const agRoot = getContainerRoot(container)\n const hit = agRoot ? selectionHitTest(agRoot, mouseData.x, mouseData.y) : null\n const scope = hit ? findContainBoundary(hit) : null\n const [next] = terminalSelectionUpdate(\n { type: \"start\", col: mouseData.x, row: mouseData.y, scope },\n selectionState,\n )\n selectionState = next\n notifySelectionListeners()\n // Force full re-render to clear old overlay (incremental render won't\n // overwrite the inverse-video ANSI the overlay wrote directly to stdout)\n if (currentBuffer) {\n runtime.invalidate()\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n writeSelectionOverlay()\n }\n // Don't consume — let the component tree also handle mousedown (for click-to-focus etc.)\n } else if (mouseData.action === \"move\" && selectionState.selecting) {\n const [next] = terminalSelectionUpdate({ type: \"extend\", col: mouseData.x, row: mouseData.y }, selectionState)\n selectionState = next\n notifySelectionListeners()\n // Re-render overlay to show updated selection\n if (currentBuffer) {\n runtime.render(currentBuffer)\n writeSelectionOverlay()\n }\n // Consume move events during selection — don't dispatch to component tree\n return true\n } else if (mouseData.action === \"up\" && selectionState.selecting) {\n const [next] = terminalSelectionUpdate({ type: \"finish\" }, selectionState)\n selectionState = next\n notifySelectionListeners()\n\n // Copy selected text via OSC 52\n if (next.range && currentBuffer) {\n const text = extractText(currentBuffer._buffer, next.range, { scope: next.scope })\n if (text.length > 0) {\n const base64 = globalThis.Buffer.from(text).toString(\"base64\")\n target.write(`\\x1b]52;c;${base64}\\x07`)\n }\n }\n // Re-render overlay with final selection\n if (currentBuffer) {\n runtime.render(currentBuffer)\n writeSelectionOverlay()\n }\n // Don't consume — let click handler run\n }\n }\n }\n\n // Selection: clear on any keypress\n if (selectionEnabled && event.type === \"term:key\" && selectionState.range) {\n const [next] = terminalSelectionUpdate({ type: \"clear\" }, selectionState)\n selectionState = next\n notifySelectionListeners()\n // Force full re-render to remove overlay\n if (currentBuffer) {\n runtime.invalidate()\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n }\n }\n\n // When scrolled up in virtual inline mode, don't dispatch events to component tree\n // (except for search which is handled above)\n if (scrollback && virtualScrollOffset > 0 && event.type === \"term:key\") {\n // Any non-search keypress returns to live view\n virtualScrollOffset = 0\n return true\n }\n\n const ctx = createHandlerContext(store, focusManager, container)\n return invokeEventHandler(event, handlers, ctx, mouseEventState, container)\n }\n\n /**\n * Process a batch of events — run all handlers, then render once.\n *\n * This is the key optimization for press-and-hold / auto-repeat keys.\n * When events arrive faster than renders (e.g., 30/sec auto-repeat vs\n * 50ms renders), we batch all pending handlers into a single render pass.\n *\n * For a batch of 3 'j' presses: handler1 → handler2 → handler3 → render.\n * The cursor moves 3 positions, but we only pay one render cost.\n */\n async function processEventBatch(events: NamespacedEvent[]): Promise<Buffer | null> {\n if (shouldExit || events.length === 0) return null\n _renderCount = 0\n _eventStart = performance.now()\n\n // Keypress performance span — wraps the entire batch cycle.\n // perfLog.span?.() short-circuits all argument evaluation when TRACE is off.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n using _perfSpan = perfLog.span?.(\n \"keypress\",\n (() => {\n startTracking()\n const keyEvents = events.filter((e) => e.type === \"term:key\")\n return {\n key:\n keyEvents.length > 0\n ? keyEvents.map((e) => (e.data as { input: string }).input).join(\",\")\n : (events[0]?.type ?? \"unknown\"),\n }\n })(),\n )\n\n // Intercept lifecycle keys (Ctrl+Z, Ctrl+C) BEFORE they reach app handlers.\n // These must be handled at the runtime level, not by individual components.\n if (!headless) {\n for (let i = events.length - 1; i >= 0; i--) {\n const event = events[i]!\n if (event.type !== \"term:key\") continue\n const data = event.data as { input: string; key: Key }\n\n // Ctrl+Z: suspend (parseKey returns input=\"z\" with key.ctrl=true)\n if (data.input === \"z\" && data.key.ctrl && suspendOption) {\n const prevented = onSuspendHook?.() === false\n if (!prevented) {\n // Remove this event from the batch\n events.splice(i, 1)\n const state = captureTerminalState({\n alternateScreen,\n cursorHidden: true,\n mouse: mouseEnabled,\n kitty: kittyEnabled,\n kittyFlags,\n bracketedPaste: true,\n rawMode: true,\n focusReporting: focusReportingEnabled,\n })\n performSuspend(state, stdout, stdin, () => {\n // After resume, trigger a full re-render\n runtime.invalidate()\n onResumeHook?.()\n })\n } else {\n events.splice(i, 1)\n }\n }\n\n // Ctrl+C: exit (parseKey returns input=\"c\" with key.ctrl=true)\n if (data.input === \"c\" && data.key.ctrl && exitOnCtrlCOption) {\n const prevented = onInterruptHook?.() === false\n if (!prevented) {\n exit()\n return null\n }\n events.splice(i, 1)\n }\n }\n if (events.length === 0) return null\n }\n\n // Suppress subscription renders — the flush loop below handles everything.\n inEventHandler = true\n isRendering = true\n\n // Input pipeline Stage 3: Event Loop — see docs/guide/input-architecture.md\n //\n // Event precedence (plugin-centric model):\n // 1. Raw: modifier tracking + keyboard state (always fires)\n // 2. Focused: focus tree dispatch via handleFocusNavigation (consumes if handled)\n // 3. Fallback: RuntimeContext listeners (useInput — only unhandled events)\n // 4. App handler (TEA update / commands)\n //\n // This ensures focused components (modals, TextInput) get events BEFORE global\n // hooks (useInput). A modal's onKeyDown for Escape fires before useInput's quit.\n for (const event of events) {\n if (event.type === \"term:key\") {\n const { input, key: parsedKey } = event.data as { input: string; key: Key }\n\n // Raw lane: Always update keyboard modifier state (Super/Cmd, Hyper) for\n // mouse events. SGR mouse protocol can't report these — Kitty fills the gap.\n updateKeyboardModifiers(mouseEventState, parsedKey)\n\n // Release and modifier-only events: bridge to RuntimeContext (useModifierKeys\n // needs them) but skip focus dispatch and app handlers.\n if (parsedKey.eventType === \"release\" || isModifierOnlyEvent(input, parsedKey)) {\n for (const listener of runtimeInputListeners) {\n listener(input, parsedKey)\n }\n if (shouldExit) {\n inEventHandler = false\n return null\n }\n continue\n }\n\n // Focused lane: dispatch through focus tree BEFORE useInput.\n // If a focused component handles the event (stopPropagation/preventDefault),\n // useInput never sees it — focused components have priority.\n let focusConsumed = false\n if (focusManager.activeElement) {\n const focusResult = handleFocusNavigation(input, parsedKey, focusManager, container)\n focusConsumed = focusResult === \"consumed\"\n }\n\n // Fallback lane: bridge to RuntimeContext listeners (useInput) only if\n // the focus tree didn't consume the event.\n if (!focusConsumed) {\n for (const listener of runtimeInputListeners) {\n listener(input, parsedKey)\n }\n }\n } else if (event.type === \"term:paste\") {\n const { text } = event.data as { text: string }\n for (const listener of runtimePasteListeners) {\n listener(text)\n }\n } else if (event.type === \"term:focus\") {\n const { focused } = event.data as { focused: boolean }\n for (const listener of runtimeFocusListeners) {\n listener(focused)\n }\n }\n\n // If a listener called exit() (e.g., useInput handler returned \"exit\"),\n // stop processing events immediately — don't render or flush.\n if (shouldExit) {\n inEventHandler = false\n return null\n }\n\n // Skip key events already handled: release/modifier-only were continued above,\n // focus-consumed events still reach the app handler for render barriers.\n if (event.type === \"term:key\") {\n const { input, key: k } = event.data as { input: string; key: Key }\n if (k.eventType === \"release\") continue\n if (isModifierOnlyEvent(input, k)) continue\n }\n\n const result = runEventHandler(event)\n if (result === false) {\n isRendering = false\n inEventHandler = false\n exit()\n return null\n }\n\n // Render barrier: if handler requested flush, render now before next event.\n // This ensures newly mounted components (e.g., InlineEditField) have their\n // refs set up before the next event handler runs.\n //\n // IMPORTANT: runtime.render() must be called here to keep the runtime's\n // prevBuffer in sync with the Ag's internal prevBuffer. Without this,\n // the post-batch doRender's dirty-row tracking would be stale relative\n // to runtime.prevBuffer, causing diffBuffers() to skip all rows and\n // produce an empty diff (0 bytes output).\n if (result === \"flush\") {\n pendingRerender = false\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n // Flush effects so mounted components can set up refs\n await Promise.resolve()\n if (pendingRerender) {\n pendingRerender = false\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n }\n }\n }\n\n // Clear deferred renders from handlers' setState calls — the explicit\n // doRender below picks up all state changes in one pass.\n pendingRerender = false\n\n // Explicit render — batches all handler state changes + flushes effects\n try {\n currentBuffer = doRender()\n } finally {\n isRendering = false\n }\n\n // Flush deferred re-renders from effects.\n // React's passive effects (useEffect) are scheduled during doRender\n // but flushed at the START of the next doRender (flushPassiveEffects).\n // The await drains the microtask queue so React's internally-queued\n // effect flush runs. Since inEventHandler=true, any setState from\n // effects just sets pendingRerender (no microtask render).\n let flushCount = 0\n const maxFlushes = 5\n while (flushCount < maxFlushes) {\n await Promise.resolve() // Drain microtask queue → passive effects flush\n if (!pendingRerender) break\n pendingRerender = false\n isRendering = true\n try {\n currentBuffer = doRender()\n } finally {\n isRendering = false\n }\n flushCount++\n }\n\n // The render phase's dirty rows are relative to the Ag's internal prevBuffer.\n // But runtime.render() diffs against its own prevBuffer, which may differ\n // when: (a) multiple doRender calls shifted the Ag's prevBuffer ahead, or\n // (b) the Z chord timeout causes the zoom render to arrive as a deferred\n // event where intermediate renders have updated the Ag's prevBuffer.\n // Always mark all rows dirty to ensure runtime.render() does a full diff.\n // The cost is negligible (diffBuffers still skips identical rows via\n // rowMetadataEquals/rowCharsEquals pre-check), but correctness is guaranteed.\n currentBuffer._buffer.markAllRowsDirty()\n\n inEventHandler = false\n const runtimeStart = performance.now()\n runtime.render(currentBuffer)\n // Post-render: push to scrollback, overlay selection/search\n pushToScrollback()\n if (virtualScrollOffset > 0) {\n renderVirtualScrollbackView()\n }\n writeSelectionOverlay()\n renderSearchHighlights()\n renderSearchBarOverlay()\n const runtimeMs = performance.now() - runtimeStart\n if (_perfLog) {\n const totalMs = performance.now() - _eventStart\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `EVENT batch(${events.length} ${events[0]?.type}): ${totalMs.toFixed(1)}ms total, ${_renderCount} doRender() calls, runtime.render=${runtimeMs.toFixed(1)}ms\\n---\\n`,\n )\n }\n // Budget check — warn if batch took longer than one frame (16ms)\n if (_perfSpan) {\n checkBudget(events[0]?.type ?? \"batch\", performance.now() - _eventStart)\n }\n return currentBuffer\n }\n\n // Start event loop\n //\n // Event coalescing: when events arrive faster than renders, we batch\n // consecutive handler calls into a single render pass. This prevents\n // the \"event backlog\" problem where auto-repeat keys queue up faster\n // than they can be rendered (e.g., 30/sec auto-repeat vs 50ms renders).\n //\n // Strategy: collect events into a shared queue, run all pending handlers,\n // render once. This means pressing and holding 'j' processes 2-3 cursor\n // moves per render instead of 1, keeping up with auto-repeat.\n const eventQueue: NamespacedEvent[] = []\n let eventQueueResolve: (() => void) | null = null\n\n const eventLoop = async () => {\n // Merge all provider event streams\n const providerEventStreams = Object.entries(providers).map(([name, provider]) =>\n createProviderEventStream(name, provider),\n )\n\n const allEvents = merge(...providerEventStreams)\n\n // Pump events from async iterable into the shared queue\n const pumpEvents = async () => {\n try {\n for await (const event of takeUntil(allEvents, signal)) {\n eventQueue.push(event)\n if (eventQueueResolve) {\n const resolve = eventQueueResolve\n eventQueueResolve = null\n resolve()\n }\n if (shouldExit) break\n }\n } finally {\n // Signal end of events\n if (eventQueueResolve) {\n const resolve = eventQueueResolve\n eventQueueResolve = null\n resolve()\n }\n }\n }\n\n // Run text sizing probe BEFORE stdin is consumed by the input parser.\n // The probe writes a test sequence to stdout and reads the CPR response\n // from stdin. This must happen before pumpEvents() attaches the stdin\n // data listener, otherwise the CPR response would be consumed as a key event.\n if (needsProbe) {\n try {\n // Set up temporary raw mode + stdin listener for probe\n const wasRaw = stdin.isRaw\n if (stdin.isTTY && !wasRaw) {\n stdin.setRawMode(true)\n stdin.resume()\n stdin.setEncoding(\"utf8\")\n }\n\n const probeRead = (): Promise<string> =>\n new Promise<string>((resolve) => {\n const onData = (data: string) => {\n stdin.off(\"data\", onData)\n resolve(data as string)\n }\n stdin.on(\"data\", onData)\n })\n\n const probeResult = await detectTextSizingSupport(\n (data) => (outputGuard ? outputGuard.writeStdout(data) : stdout.write(data)),\n probeRead,\n 500, // Short timeout — probe should be fast\n )\n\n // If probe result differs from initial heuristic, recreate pipeline\n if (probeResult.supported !== textSizingEnabled) {\n textSizingEnabled = probeResult.supported\n if (effectiveCaps) {\n effectiveCaps = { ...effectiveCaps, textSizingSupported: textSizingEnabled }\n pipelineConfig = createPipeline({ caps: effectiveCaps })\n // Update runtime's output phase to use the new measurer\n runtime.setOutputPhaseFn(pipelineConfig.outputPhaseFn)\n }\n // Invalidate pipeline and runtime diff state for full redraw.\n // Recreate Ag with updated measurer (text sizing support changed).\n _ag = null\n runtime.invalidate()\n // Force full re-render with updated measurer\n if (!isRendering) {\n isRendering = true\n try {\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n } finally {\n isRendering = false\n }\n }\n }\n\n // Restore raw mode if we changed it (pumpEvents will set it again)\n if (stdin.isTTY && !wasRaw) {\n stdin.setRawMode(false)\n stdin.pause()\n }\n } catch {\n // Probe failed — keep current textSizing setting (safe fallback)\n }\n }\n\n // Run DEC width detection probe BEFORE stdin is consumed by the input parser.\n // Queries DEC modes 1020-1023 for emoji/CJK/PUA width settings.\n // Must happen before pumpEvents() for the same reason as text-sizing probe.\n if (needsWidthDetection) {\n try {\n const wasRaw = stdin.isRaw\n if (stdin.isTTY && !wasRaw) {\n stdin.setRawMode(true)\n stdin.resume()\n stdin.setEncoding(\"utf8\")\n }\n\n const stdinHandlers: Array<(data: string) => void> = []\n const stdinListener = (data: string) => {\n for (const handler of stdinHandlers) handler(data)\n }\n stdin.on(\"data\", stdinListener)\n\n const detector = createWidthDetector({\n write: (data) => (outputGuard ? outputGuard.writeStdout(data) : stdout.write(data)),\n onData: (handler) => {\n stdinHandlers.push(handler)\n return () => {\n const idx = stdinHandlers.indexOf(handler)\n if (idx >= 0) stdinHandlers.splice(idx, 1)\n }\n },\n timeoutMs: 200,\n })\n\n const widthConfig = await detector.detect()\n detector.dispose()\n stdin.off(\"data\", stdinListener)\n\n // Apply detected width config to caps and recreate pipeline if changed\n if (effectiveCaps) {\n const updatedCaps = applyWidthConfig(effectiveCaps, widthConfig)\n const capsChanged =\n updatedCaps.textEmojiWide !== effectiveCaps.textEmojiWide ||\n updatedCaps.textSizingSupported !== effectiveCaps.textSizingSupported\n if (capsChanged) {\n effectiveCaps = updatedCaps\n pipelineConfig = createPipeline({ caps: effectiveCaps })\n runtime.setOutputPhaseFn(pipelineConfig.outputPhaseFn)\n // Recreate Ag with updated measurer (caps changed text sizing/emoji width)\n _ag = null\n runtime.invalidate()\n if (!isRendering) {\n isRendering = true\n try {\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n } finally {\n isRendering = false\n }\n }\n }\n }\n\n if (stdin.isTTY && !wasRaw) {\n stdin.setRawMode(false)\n stdin.pause()\n }\n } catch {\n // Width detection failed — keep default caps (safe fallback)\n }\n }\n\n // Start pump in background — this synchronously runs the term-provider\n // generator body, which attaches the stdin data listener. After this call,\n // stdin is being consumed, so terminal responses won't leak as raw text.\n pumpEvents().catch((err: unknown) => log.error?.(`pumpEvents failed: ${err}`))\n\n // Enable focus reporting NOW — after stdin listener is attached.\n // Must be deferred from the init phase because the terminal's immediate\n // CSI I/O response would leak before the input parser was ready.\n if (focusReportingOption && !focusReportingEnabled) {\n enableFocusReporting((s) => (outputGuard ? outputGuard.writeStdout(s) : stdout.write(s)))\n focusReportingEnabled = true\n }\n\n try {\n while (!shouldExit && !signal.aborted) {\n // Wait for at least one event\n if (eventQueue.length === 0) {\n await new Promise<void>((resolve) => {\n eventQueueResolve = resolve\n signal.addEventListener(\"abort\", () => resolve(), { once: true })\n })\n }\n\n if (shouldExit || signal.aborted) break\n if (eventQueue.length === 0) continue\n\n // Drain-then-render: yield to the event loop repeatedly so the pump\n // (async-iterator chain: term-provider → merge → map → takeUntil →\n // pumpEvents) can push ALL pending events into eventQueue before we\n // process the batch. Each hop through the async iterator pipeline\n // costs several microtask ticks per event, so a single\n // `Promise.resolve()` yield is not enough to drain a burst of 10+\n // events from the term-provider's internal queue. We use\n // `setImmediate` (which runs after ALL pending microtasks) so that a\n // full async-iterator round-trip has time to complete. Then we loop\n // until the queue is stable across two consecutive yields, meaning\n // the pipeline has delivered everything it had ready.\n //\n // This ensures rapid keypresses (e.g., jumping from fold level 1 to\n // 10, or OS auto-repeat buffering \"jjjjj...\") coalesce into ONE\n // render cycle instead of N.\n //\n // Safety: bounded by maxDrainSpins to prevent pathological stalls\n // if an event source is producing faster than we can drain. Under\n // realistic auto-repeat (30-60 keys/sec), events arrive in a short\n // burst then go quiet — maxDrainSpins=32 is plenty of headroom.\n const maxDrainSpins = 32\n let drainSpins = 0\n const yieldToEventLoop = () => new Promise<void>((resolve) => setImmediate(resolve))\n // First mandatory yield — lets events already in-flight land.\n await yieldToEventLoop()\n let prevLen = eventQueue.length\n while (drainSpins < maxDrainSpins) {\n // eslint-disable-next-line no-await-in-loop -- intentional: sequential yields drain the async iterator pipeline\n await yieldToEventLoop()\n const curLen = eventQueue.length\n if (curLen === prevLen) break\n prevLen = curLen\n drainSpins++\n }\n if (_perfLog) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `DRAIN: spins=${drainSpins}, batch=${eventQueue.length}\\n`,\n )\n }\n // Expose diagnostic counters on globalThis for test assertions.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const _g = globalThis as any\n _g.__silvery_last_drain_spins = drainSpins\n _g.__silvery_last_batch_size = eventQueue.length\n _g.__silvery_batch_count = (_g.__silvery_batch_count ?? 0) + 1\n\n // Process all pending events — run handlers without rendering\n const buf = await processEventBatch(eventQueue.splice(0))\n if (buf) emitFrame(buf)\n }\n } finally {\n // Mark frames as done and notify waiters\n framesDone = true\n if (frameResolve) {\n const resolve = frameResolve\n frameResolve = null\n // Signal completion — resolve with a sentinel that next() will detect\n resolve(null as unknown as Buffer)\n }\n\n // Async drain: give the event loop 1 tick + 15ms to receive any\n // late-arriving bytes (Kitty release events, mouse events) that were\n // in the kernel TTY buffer when we sent the disable sequences.\n // This is the async path — signal handlers use the sync fallback in exit().\n if (shouldExit && !cleanedUp && !headless && stdin.isTTY) {\n try {\n // Remove data listener but keep raw mode on — we're still consuming\n stdin.removeAllListeners(\"data\")\n stdin.resume()\n // Let the event loop tick to deliver kernel-buffered bytes\n await new Promise((resolve) => setTimeout(resolve, 15))\n // Drain whatever arrived\n while (stdin.read() !== null) {\n /* discard late arrivals */\n }\n stdin.pause()\n } catch {\n // Best-effort — continue to cleanup\n }\n }\n\n // Cleanup and resolve exit promise\n cleanup()\n exitResolve()\n }\n }\n\n // Start loop in background\n eventLoop().catch((err: unknown) => log.error?.(`eventLoop failed: ${err}`))\n\n // Return handle with async iteration\n const handle: AppHandle<S & I> = {\n get text() {\n return currentBuffer.text\n },\n get root() {\n return getContainerRoot(container)\n },\n get buffer() {\n return currentBuffer?._buffer ?? null\n },\n get store() {\n return store\n },\n waitUntilExit() {\n return exitPromise\n },\n unmount() {\n exit()\n },\n [Symbol.dispose]() {\n exit()\n },\n async press(rawKey: string) {\n // perfLog.span is always defined; the cost of performance.now() is negligible.\n const pressStart = performance.now()\n // Convert named keys to ANSI bytes (Kitty protocol when enabled)\n const ansiKey = useKittyMode ? keyToKittyAnsi(rawKey) : keyToAnsi(rawKey)\n const [input, parsedKey] = parseKey(ansiKey)\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n using _perfSpan = perfLog.span?.(\n \"keypress\",\n (() => {\n startTracking()\n return { key: input || rawKey }\n })(),\n )\n\n // Intercept lifecycle keys (Ctrl+C) — same as processEventBatch but for\n // headless/press() path. parseKey returns input=\"c\" with key.ctrl=true\n // for Ctrl+C (not the raw \"\\x03\" byte).\n if (input === \"c\" && parsedKey.ctrl && exitOnCtrlCOption) {\n const prevented = onInterruptHook?.() === false\n if (!prevented) {\n exit()\n return\n }\n }\n\n // Bridge to RuntimeContext listeners (useInput consumers)\n for (const listener of runtimeInputListeners) {\n listener(input, parsedKey)\n }\n\n // Suppress subscription renders — flush loop below handles everything.\n inEventHandler = true\n isRendering = true\n\n // Focus system: dispatch key event and handle default navigation\n const focusResult = handleFocusNavigation(input, parsedKey, focusManager, container)\n if (focusResult === \"consumed\") {\n pendingRerender = false\n isRendering = false\n inEventHandler = false\n doRender()\n await Promise.resolve()\n if (_perfSpan) checkBudget(input || rawKey, performance.now() - pressStart)\n return\n }\n\n // Dispatch to app handlers (namespaced + legacy)\n const handlerCtx = createHandlerContext(store, focusManager, container)\n if (dispatchKeyToHandlers(input, parsedKey, handlers, handlerCtx) === \"exit\") {\n isRendering = false\n inEventHandler = false\n exit()\n return\n }\n\n // Clear deferred renders — explicit render below batches all changes\n pendingRerender = false\n\n // Trigger re-render (batches handler state changes + flushes effects)\n try {\n currentBuffer = doRender()\n } finally {\n isRendering = false\n }\n // Flush deferred re-renders from effects.\n // await drains microtask queue → React passive effects flush.\n // Since inEventHandler=true, setState from effects just flags\n // pendingRerender (no microtask render).\n let flushCount = 0\n const maxFlushes = 5\n while (flushCount < maxFlushes) {\n await Promise.resolve()\n if (!pendingRerender) break\n pendingRerender = false\n isRendering = true\n try {\n currentBuffer = doRender()\n } finally {\n isRendering = false\n }\n flushCount++\n }\n // Mark all rows dirty — same safety net as processEventBatch (line 2443).\n // When the effect flush loop ran additional doRender calls, the final buffer's\n // dirty rows are relative to the Ag's internal prevBuffer (which advanced),\n // not the runtime's prevBuffer (which is from the last runtime.render()).\n // Without this, diffBuffers skips rows that changed relative to runtime's\n // prevBuffer but aren't marked dirty → garbled output.\n if (flushCount > 0) {\n currentBuffer._buffer.markAllRowsDirty()\n }\n inEventHandler = false\n runtime.render(currentBuffer)\n if (_perfSpan) checkBudget(input || rawKey, performance.now() - pressStart)\n },\n\n [Symbol.asyncIterator](): AsyncIterator<Buffer> {\n return {\n async next(): Promise<IteratorResult<Buffer>> {\n if (framesDone || shouldExit) {\n return { done: true, value: undefined as unknown as Buffer }\n }\n\n // Wait for next frame from event loop\n const buf = await new Promise<Buffer>((resolve) => {\n // If already done, resolve immediately\n if (framesDone || shouldExit) {\n resolve(null as unknown as Buffer)\n return\n }\n frameResolve = resolve\n })\n\n // null sentinel means done\n if (!buf) {\n return { done: true, value: undefined as unknown as Buffer }\n }\n\n return { done: false, value: buf }\n },\n async return(): Promise<IteratorResult<Buffer>> {\n exit()\n return { done: true, value: undefined as unknown as Buffer }\n },\n }\n },\n }\n\n return handle\n}\n","/**\n * usePasteCallback — subscribe to bracketed paste events.\n *\n * Simple callback-based paste hook for run() apps.\n * For component composition with PasteProvider, use usePaste() instead.\n *\n * @example\n * ```tsx\n * usePasteCallback((text) => {\n * insertText(text)\n * })\n * ```\n */\n\nimport { useContext, useEffect, useRef } from \"react\"\nimport { RuntimeContext } from \"../context\"\n\nexport type PasteCallback = (text: string) => void\n\nexport function usePasteCallback(handler: PasteCallback): void {\n const rt = useContext(RuntimeContext)\n\n const handlerRef = useRef(handler)\n handlerRef.current = handler\n\n useEffect(() => {\n if (!rt) return\n return rt.on(\"paste\", (text: string) => {\n handlerRef.current(text)\n })\n }, [rt])\n}\n","/**\n * run() - Layer 2 entry point for silvery-loop\n *\n * Thin wrapper over createApp() for simple React apps with keyboard input.\n * Use this when you want React component state (useState, useEffect)\n * with simple keyboard input via useInput().\n *\n * For stores and providers, use createApp() (Layer 3) directly.\n *\n * @example\n * ```tsx\n * import { run, useInput } from '@silvery/ag-term/runtime'\n *\n * function Counter() {\n * const [count, setCount] = useState(0)\n *\n * useInput((input, key) => {\n * if (input === 'j') setCount(c => c + 1)\n * if (key.upArrow) setCount(c => c + 1)\n * if (input === 'q') return 'exit'\n * })\n *\n * return <Text>Count: {count}</Text>\n * }\n *\n * await run(<Counter />)\n * ```\n */\n\nimport React, { type ReactElement } from \"react\"\n\nimport { createApp } from \"./create-app\"\nimport type { Term } from \"../ansi/term\"\nimport { detectTerminalCaps } from \"../terminal-caps\"\nimport { detectTheme } from \"@silvery/theme/detect\"\nimport { ThemeProvider } from \"@silvery/theme/ThemeContext\"\n\n// Re-export types from keys.ts\nexport type { Key, InputHandler } from \"./keys\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Options for run().\n *\n * run() auto-detects terminal capabilities and enables features by default.\n * Pass explicit values to override. For the full list of capabilities detected,\n * see {@link detectTerminalCaps} in terminal-caps.ts.\n *\n * **Mouse tracking note:** When `mouse` is enabled (the default), the terminal\n * captures mouse events and native text selection (copy/paste) requires holding\n * Shift (or Option on macOS in some terminals). Set `mouse: false` to restore\n * native copy/paste behavior.\n */\nexport interface RunOptions {\n /** Terminal dimensions (default: from process.stdout) */\n cols?: number\n rows?: number\n /** Standard output (default: process.stdout) */\n stdout?: NodeJS.WriteStream\n /** Standard input (default: process.stdin) */\n stdin?: NodeJS.ReadStream\n /**\n * Plain writable sink for ANSI output. Headless mode with active output.\n * Requires cols and rows. Input via handle.press().\n */\n writable?: { write(data: string): void }\n /** Abort signal for external cleanup */\n signal?: AbortSignal\n /**\n * Enable Kitty keyboard protocol for unambiguous key identification\n * (Cmd ⌘, Hyper ✦ modifiers, key release events).\n * - `true`: enable with DISAMBIGUATE flag (1)\n * - number: enable with specific KittyFlags bitfield\n * - `false`: don't enable\n * - Default: auto-detected from terminal (enabled for Ghostty, Kitty, WezTerm, foot)\n */\n kitty?: boolean | number\n /**\n * Enable SGR mouse tracking (mode 1006) for click, scroll, and drag events.\n * When enabled, native text selection requires holding Shift (or Option on macOS)\n * and native terminal scrolling is disabled.\n * Default: `true` in fullscreen mode, `false` in inline mode (where content\n * lives in terminal scrollback and natural scrolling is expected).\n */\n mouse?: boolean\n /**\n * Render mode:\n * - `\"fullscreen\"` — alt screen buffer (default)\n * - `\"inline\"` — scrollback-compatible, no alt screen\n * - `\"virtualInline\"` — alt screen with virtual scrollback (scrollable history + search)\n */\n mode?: \"fullscreen\" | \"inline\" | \"virtualInline\"\n /**\n * Enable Kitty text sizing protocol (OSC 66) for PUA characters.\n * Ensures nerdfont/powerline icons are measured and rendered at the correct width.\n * - `true`: force enable\n * - `\"auto\"`: use heuristic, then probe to verify (progressive enhancement)\n * - `\"probe\"`: start disabled, probe async, enable on confirmation\n * - `false`: disabled\n * - Default: \"auto\"\n */\n textSizing?: boolean | \"auto\" | \"probe\"\n /**\n * Enable DEC width mode detection (modes 1020-1023).\n * Queries the terminal for emoji/CJK/PUA width settings at startup.\n * - `true`: always run width detection probe\n * - `\"auto\"`: run probe when caps are provided (default)\n * - `false`: disabled\n * Default: \"auto\"\n */\n widthDetection?: boolean | \"auto\"\n /**\n * Enable terminal focus reporting (CSI ?1004h).\n * Dispatches 'term:focus' events with `{ focused: boolean }`.\n * Default: true\n */\n focusReporting?: boolean\n /**\n * Terminal capabilities for width measurement and output suppression.\n * Default: auto-detected via detectTerminalCaps()\n */\n caps?: import(\"../terminal-caps.js\").TerminalCaps\n /**\n * Handle Ctrl+Z by suspending the process. Default: true\n */\n suspendOnCtrlZ?: boolean\n /**\n * Handle Ctrl+C by restoring terminal and exiting. Default: true\n */\n exitOnCtrlC?: boolean\n /** Called before suspend. Return false to prevent. */\n onSuspend?: () => boolean | void\n /** Called after resume from suspend. */\n onResume?: () => void\n /** Called on Ctrl+C. Return false to prevent exit. */\n onInterrupt?: () => boolean | void\n}\n\n/**\n * Handle returned by run() for controlling the app.\n */\nexport interface RunHandle {\n /** Current rendered text (no ANSI) */\n readonly text: string\n /** Live reconciler root node (for locator queries) */\n readonly root: import(\"@silvery/ag/types\").AgNode\n /** Current terminal buffer (cell-level access) */\n readonly buffer: import(\"@silvery/ag-term/buffer\").TerminalBuffer | null\n /** Wait until the app exits */\n waitUntilExit(): Promise<void>\n /** Unmount and cleanup */\n unmount(): void\n /** Dispose (alias for unmount) — enables `using` */\n [Symbol.dispose](): void\n /** Send a key press */\n press(key: string): Promise<void>\n}\n\n// ============================================================================\n// Hooks (Layer 2 — uses RuntimeContext, works in both run() and createApp())\n// ============================================================================\n\n// All hooks re-exported from ag-react — single implementation, no duplication.\n// run.tsx has zero hook implementations. See km-silvery.zero-hooks-run.\nexport { useInput, type UseInputOptions } from \"@silvery/ag-react/hooks/useInput\"\nexport { useExit } from \"@silvery/ag-react/hooks/useExit\"\nexport {\n usePasteCallback as usePaste,\n type PasteCallback as PasteHandler,\n} from \"@silvery/ag-react/hooks/usePasteCallback\"\n\n// ============================================================================\n// run() — thin wrapper over createApp()\n// ============================================================================\n\n/**\n * Run a React component with the silvery-loop runtime.\n *\n * Accepts either a Term instance or RunOptions:\n * - `run(<App />, term)` — Term handles streams, createApp handles rendering\n * - `run(<App />, { cols, rows, ... })` — classic options API\n *\n * Internally delegates to createApp() with an empty store.\n * For stores and providers, use createApp() directly.\n */\nexport async function run(element: ReactElement, term: Term, termOptions?: Partial<RunOptions>): Promise<RunHandle>\nexport async function run(element: ReactElement, options?: RunOptions): Promise<RunHandle>\nexport async function run(\n element: ReactElement,\n optionsOrTerm: RunOptions | Term = {},\n termOptions?: Partial<RunOptions>,\n): Promise<RunHandle> {\n // Term path: pass Term as provider + its streams, auto-enable from Term caps\n if (isTerm(optionsOrTerm)) {\n const term = optionsOrTerm as Term\n const emulator = (term as unknown as Record<string, unknown>)._emulator as { feed(data: string): void } | undefined\n\n // Emulator-backed term: non-headless mode with stdout routing to emulator.\n // Create a mock stdin that forwards sendInput() data to the term provider's\n // input parser, so events flow through the full createApp pipeline.\n if (emulator) {\n const { EventEmitter } = await import(\"node:events\")\n const stdinEmitter = new EventEmitter()\n const mockStdin = Object.assign(stdinEmitter, {\n isTTY: true,\n isRaw: false,\n fd: 0,\n setRawMode(_mode: boolean) {\n mockStdin.isRaw = _mode\n return mockStdin\n },\n read() {\n return null\n },\n resume() {\n return mockStdin\n },\n pause() {\n return mockStdin\n },\n ref() {\n return mockStdin\n },\n unref() {\n return mockStdin\n },\n setEncoding() {\n return mockStdin\n },\n }) as unknown as NodeJS.ReadStream\n\n // Wire sendInput: when term.sendInput(data) is called, emit on mock stdin\n // so the term provider's parser processes it through the real pipeline.\n // The mixed-proxy's set/defineProperty traps forward to termBase,\n // so this override replaces the original sendInput with one that\n // feeds the mock stdin instead of the internal event queue.\n if ((term as any).sendInput) {\n ;(term as any).sendInput = (data: string) => {\n stdinEmitter.emit(\"data\", data)\n }\n }\n\n // Resolve alternateScreen from termOptions.mode (if provided).\n // The mode prop is consumed at the run() level for the options path,\n // but in the Term path it needs explicit conversion.\n const termMode = termOptions?.mode\n const altScreen = termMode === \"inline\" ? false : true\n\n const app = createApp(() => () => ({}))\n const handle = await app.run(element, {\n alternateScreen: altScreen,\n ...termOptions,\n stdin: mockStdin,\n stdout: term.stdout, // Feeds emulator — protocol escapes reach the emulator\n guardOutput: false, // Don't monkeypatch process.stdout in test/emulator context\n cols: term.cols ?? 80,\n rows: term.rows ?? 24,\n })\n return wrapHandle(handle)\n }\n\n // Real terminal: full setup\n const caps = term.caps ?? detectTerminalCaps()\n // Detect terminal colors via OSC — must happen before alt screen\n const theme = await detectTheme()\n const themed = <ThemeProvider theme={theme}>{element}</ThemeProvider>\n const app = createApp(() => () => ({}))\n const handle = await app.run(themed, {\n term,\n stdout: term.stdout,\n stdin: term.stdin,\n cols: term.cols ?? undefined,\n rows: term.rows ?? undefined,\n caps,\n alternateScreen: true,\n kitty: caps.kittyKeyboard,\n mouse: true,\n focusReporting: true,\n textSizing: \"auto\",\n widthDetection: \"auto\",\n })\n return wrapHandle(handle)\n }\n\n // Options path: auto-detect caps and derive defaults\n const { mode, ...rest } = optionsOrTerm as RunOptions\n const caps = rest.caps ?? detectTerminalCaps()\n const headless = rest.writable != null || (rest.cols != null && rest.rows != null && !rest.stdout)\n // Detect terminal colors via OSC — must happen before alt screen (skipped for headless)\n const themed = headless\n ? element\n : await detectTheme().then((theme) => <ThemeProvider theme={theme}>{element}</ThemeProvider>)\n const app = createApp(() => () => ({}))\n const handle = await app.run(themed, {\n ...rest,\n caps,\n alternateScreen: mode !== \"inline\",\n virtualInline: mode === \"virtualInline\",\n kitty: rest.kitty ?? caps.kittyKeyboard,\n mouse: rest.mouse ?? mode !== \"inline\",\n focusReporting: rest.focusReporting ?? mode !== \"inline\",\n textSizing: rest.textSizing ?? \"auto\",\n widthDetection: rest.widthDetection ?? \"auto\",\n })\n return wrapHandle(handle)\n}\n\n/** Duck-type check: Term has getState and events as functions.\n * Note: Term is a Proxy wrapping chalk, so typeof is \"function\" not \"object\". */\nfunction isTerm(obj: unknown): obj is Term {\n if (obj == null) return false\n if (typeof obj !== \"object\" && typeof obj !== \"function\") return false\n const o = obj as Record<string, unknown>\n return typeof o.getState === \"function\" && typeof o.events === \"function\"\n}\n\n/** Wrap AppHandle as RunHandle (subset of the full handle). */\nfunction wrapHandle(handle: {\n readonly text: string\n readonly root: import(\"@silvery/ag/types\").AgNode\n readonly buffer: import(\"@silvery/ag-term/buffer\").TerminalBuffer | null\n waitUntilExit(): Promise<void>\n unmount(): void\n [Symbol.dispose](): void\n press(key: string): Promise<void>\n}): RunHandle {\n return {\n get text() {\n return handle.text\n },\n get root() {\n return handle.root\n },\n get buffer() {\n return handle.buffer\n },\n waitUntilExit: () => handle.waitUntilExit(),\n unmount: () => handle.unmount(),\n [Symbol.dispose]: () => handle[Symbol.dispose](),\n press: (key: string) => handle.press(key),\n }\n}\n","/**\n * Time/tick source for silvery-loop.\n *\n * Creates an AsyncIterable that yields at regular intervals.\n * Used for animations, spinners, progress bars, etc.\n */\n\n/**\n * Create a tick source that yields at regular intervals.\n *\n * The tick source respects AbortSignal for cleanup and stops\n * when the signal is aborted.\n *\n * @param intervalMs Interval between ticks in milliseconds\n * @param signal Optional AbortSignal to stop the tick source\n * @returns AsyncIterable that yields tick numbers (0, 1, 2, ...)\n *\n * @example\n * ```typescript\n * const controller = new AbortController()\n * const ticks = createTick(100, controller.signal)\n *\n * for await (const tick of ticks) {\n * console.log(`Tick ${tick}`)\n * if (tick >= 10) controller.abort()\n * }\n * ```\n */\nexport function createTick(intervalMs: number, signal?: AbortSignal): AsyncIterable<number> {\n return {\n [Symbol.asyncIterator]: () => createTickIterator(intervalMs, signal),\n }\n}\n\n/**\n * Create the actual tick iterator.\n */\nfunction createTickIterator(intervalMs: number, signal?: AbortSignal): AsyncIterator<number> {\n let count = 0\n let timer: ReturnType<typeof setTimeout> | undefined\n let pendingResolve: ((result: IteratorResult<number>) => void) | undefined\n let done = false\n\n // Handle abort signal\n const onAbort = () => {\n done = true\n if (timer) {\n clearTimeout(timer)\n timer = undefined\n }\n if (pendingResolve) {\n pendingResolve({ done: true, value: undefined })\n pendingResolve = undefined\n }\n }\n\n if (signal) {\n if (signal.aborted) {\n done = true\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true })\n }\n }\n\n return {\n async next(): Promise<IteratorResult<number>> {\n if (done) {\n return { done: true, value: undefined }\n }\n\n return new Promise<IteratorResult<number>>((resolve, _reject) => {\n pendingResolve = resolve\n\n timer = setTimeout(() => {\n if (!done) {\n const value = count++\n pendingResolve = undefined\n resolve({ done: false, value })\n }\n }, intervalMs)\n })\n },\n\n async return(): Promise<IteratorResult<number>> {\n done = true\n if (timer) {\n clearTimeout(timer)\n timer = undefined\n }\n if (signal) {\n signal.removeEventListener(\"abort\", onAbort)\n }\n if (pendingResolve) {\n pendingResolve({ done: true, value: undefined })\n pendingResolve = undefined\n }\n return { done: true, value: undefined }\n },\n }\n}\n\n/**\n * Create a tick source that yields at approximately 60fps (~16ms).\n *\n * @param signal Optional AbortSignal to stop the tick source\n * @returns AsyncIterable that yields frame numbers\n */\nexport function createFrameTick(signal?: AbortSignal): AsyncIterable<number> {\n return createTick(16, signal)\n}\n\n/**\n * Create a tick source that yields once per second.\n *\n * @param signal Optional AbortSignal to stop the tick source\n * @returns AsyncIterable that yields second counts\n */\nexport function createSecondTick(signal?: AbortSignal): AsyncIterable<number> {\n return createTick(1000, signal)\n}\n\n/**\n * Create a tick source with adaptive timing based on render performance.\n *\n * This is useful for maintaining a target frame rate while allowing\n * for slower frames when needed.\n *\n * @param targetFps Target frames per second (default: 60)\n * @param signal Optional AbortSignal to stop the tick source\n * @returns AsyncIterable with timing information\n */\nexport function createAdaptiveTick(\n targetFps = 60,\n signal?: AbortSignal,\n): AsyncIterable<{ tick: number; elapsed: number; delta: number }> {\n const targetMs = 1000 / targetFps\n let lastTime = Date.now()\n let tick = 0\n\n return {\n [Symbol.asyncIterator]: () => {\n let done = false\n let timer: ReturnType<typeof setTimeout> | undefined\n let pendingResolve:\n | ((\n result: IteratorResult<{\n tick: number\n elapsed: number\n delta: number\n }>,\n ) => void)\n | undefined\n\n const onAbort = () => {\n done = true\n if (timer) {\n clearTimeout(timer)\n timer = undefined\n }\n if (pendingResolve) {\n pendingResolve({ done: true, value: undefined })\n pendingResolve = undefined\n }\n }\n\n if (signal) {\n if (signal.aborted) {\n done = true\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true })\n }\n }\n\n return {\n async next(): Promise<IteratorResult<{ tick: number; elapsed: number; delta: number }>> {\n if (done) {\n return { done: true, value: undefined }\n }\n\n return new Promise((resolve) => {\n pendingResolve = resolve\n const now = Date.now()\n const elapsed = now - lastTime\n const delay = Math.max(0, targetMs - elapsed)\n\n timer = setTimeout(() => {\n if (!done) {\n const currentTime = Date.now()\n const delta = currentTime - lastTime\n lastTime = currentTime\n pendingResolve = undefined\n resolve({\n done: false,\n value: { tick: tick++, elapsed: currentTime, delta },\n })\n }\n }, delay)\n })\n },\n\n async return(): Promise<IteratorResult<{ tick: number; elapsed: number; delta: number }>> {\n done = true\n if (timer) {\n clearTimeout(timer)\n timer = undefined\n }\n if (signal) {\n signal.removeEventListener(\"abort\", onAbort)\n }\n if (pendingResolve) {\n pendingResolve({ done: true, value: undefined })\n pendingResolve = undefined\n }\n return { done: true, value: undefined }\n },\n }\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,SAAgB,0BACd,eACA,eACA,cACA,YACA,UAAU,GACF;AAER,KAAI,cAAc,aAAc,QAAO;CAKvC,MAAM,mBAAmB,UAAU,KAAK,eAAe,IAAI;CAG3D,MAAM,eAAe;CACrB,MAAM,aAAa,gBAAgB,eAAe;CAGlD,MAAM,cAAc,eAAe;CACnC,MAAM,YAAY,aAAa;CAE/B,IAAI,YAAY;AAEhB,KAAI,gBAAgB,YAElB,aAAY,KAAK,IAAI,GAAG,gBAAgB,iBAAiB;UAEzD,qBAAqB,KACrB,kBAAkB,eAClB,gBAAgB,KAKhB,eAAe,QAOf,aAAY,KAAK,IAAI,GAAG,gBAAgB,QAAQ;UACvC,gBAAgB,UAGzB,aAAY,KAAK,IAAI,aAAa,cAAc,gBAAgB,eAAe,mBAAmB,EAAE;AAItG,QAAO,KAAK,IAAI,GAAG,KAAK,IAAI,WAAW,aAAa,aAAa,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACHpE,SAAgB,SAAS,cAA4B,UAA2B,EAAE,EAAQ;CACxF,MAAM,KAAK,WAAW,eAAe;CAErC,MAAM,EAAE,WAAW,MAAM,SAAS,cAAc;CAKhD,MAAM,aAAa,OAAO,aAAa;AACvC,YAAW,UAAU;CAErB,MAAM,aAAa,OAAO,QAAQ;AAClC,YAAW,UAAU;CAErB,MAAM,eAAe,OAAO,UAAU;AACtC,cAAa,UAAU;AAKvB,iBAAgB;AACd,MAAI,CAAC,YAAY,CAAC,GAAI;AAEtB,SAAO,GAAG,GAAG,UAAU,OAAe,QAAa;AAGjD,OAAI,oBAAoB,OAAO,IAAI,CAAE;AAGrC,OAAI,IAAI,cAAc,WAAW;AAC/B,iBAAa,UAAU,OAAO,IAAI;AAClC;;AAGF,OADe,WAAW,QAAQ,OAAO,IAAI,KAC9B,OAAQ,IAAG,MAAM;IAChC;IACD,CAAC,UAAU,GAAG,CAAC;AAGlB,iBAAgB;AACd,MAAI,CAAC,YAAY,CAAC,GAAI;AAEtB,SAAO,GAAG,GAAG,UAAU,SAAiB;AACtC,cAAW,UAAU,KAAK;IAC1B;IACD,CAAC,UAAU,GAAG,CAAC;;;;;;;;;;;AC3GpB,SAAgB,kBAAkB,KAAsB,MAAc,OAA4B;CAChG,MAAM,OAAO,KAAK,MAAM,KAAK;AAE7B,QAAO;EAAE;EAAK;EAAM;EAAM,eADJ,KAAK,KAAK,MAAM,UAAU,EAAE,CAAC;EACV;EAAO;;AAGlD,SAAgB,oBAAoB,WAAW,KAAuB;CAEpE,IAAI,QAAuB,EAAE;CAC7B,IAAI,aAAa;CAEjB,SAAS,QAAc;AACrB,SAAO,aAAa,YAAY,MAAM,SAAS,GAAG;GAChD,MAAM,UAAU,MAAM,OAAO;AAC7B,iBAAc,QAAQ,KAAK;;;;CAK/B,SAAS,WAAW,KAA6D;AAC/E,MAAI,MAAM,KAAK,OAAO,WAAY,QAAO;EACzC,IAAI,aAAa;AACjB,OAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,MAAM,WAAW,MAAM,GAAI,KAAK;AAChC,OAAI,MAAM,aAAa,SACrB,QAAO;IAAE,WAAW;IAAG,UAAU,MAAM;IAAY;AAErD,iBAAc;;AAEhB,SAAO;;AAGT,QAAO;EACL,KAAK,MAAyB;AAC5B,SAAM,KAAK,KAAK;AAChB,iBAAc,KAAK,KAAK;AACxB,UAAO;;EAGT,IAAI,YAAoB;AACtB,UAAO;;EAGT,IAAI,YAAoB;AACtB,UAAO,MAAM;;EAGf,IAAI,WAAmB;AACrB,UAAO;;EAGT,QAAQ,UAAkB,OAAyB;GACjD,MAAM,SAAmB,EAAE;AAC3B,QAAK,IAAI,IAAI,UAAU,IAAI,WAAW,OAAO,KAAK;IAChD,MAAM,WAAW,WAAW,EAAE;AAC9B,QAAI,SACF,QAAO,KAAK,MAAM,SAAS,WAAY,KAAK,SAAS,UAAW;QAEhE,QAAO,KAAK,GAAG;;AAGnB,UAAO;;EAGT,iBAAiB,UAAkB,OAAyB;GAC1D,MAAM,SAAmB,EAAE;AAC3B,QAAK,IAAI,IAAI,UAAU,IAAI,WAAW,OAAO,KAAK;IAChD,MAAM,WAAW,WAAW,EAAE;AAC9B,QAAI,SACF,QAAO,KAAK,MAAM,SAAS,WAAY,cAAc,SAAS,UAAW;QAEzE,QAAO,KAAK,GAAG;;AAGnB,UAAO;;EAGT,OAAO,OAAyB;AAC9B,OAAI,CAAC,MAAO,QAAO,EAAE;GACrB,MAAM,aAAa,MAAM,aAAa;GACtC,MAAM,UAAoB,EAAE;GAC5B,IAAI,YAAY;AAChB,QAAK,MAAM,QAAQ,OAAO;AACxB,SAAK,IAAI,IAAI,GAAG,IAAI,KAAK,cAAc,QAAQ,IAC7C,KAAI,KAAK,cAAc,GAAI,aAAa,CAAC,SAAS,WAAW,CAC3D,SAAQ,KAAK,YAAY,EAAE;AAG/B,iBAAa,KAAK,KAAK;;AAEzB,UAAO;;EAGT,aAAa,KAA6D;GACxE,MAAM,WAAW,WAAW,IAAI;AAChC,OAAI,CAAC,SAAU,QAAO;AACtB,UAAO;IAAE,MAAM,MAAM,SAAS;IAAa,UAAU,SAAS;IAAU;;EAG1E,QAAc;AACZ,WAAQ,EAAE;AACV,gBAAa;;EAEhB;;;;AClGH,SAAgB,mBAAmB,SAAwB,cAAmD;CAC5G,SAAS,eAAuB;EAC9B,IAAI,QAAQ;AACZ,OAAK,MAAM,SAAS,cAAc,CAChC,UAAS,MAAM,KAAK;AAEtB,SAAO;;AAGT,QAAO;EACL,IAAI,YAAoB;AACtB,UAAO,QAAQ,YAAY,cAAc;;EAG3C,IAAI,aAAqB;AACvB,UAAO,QAAQ;;EAGjB,IAAI,WAAmB;AACrB,UAAO,cAAc;;EAGvB,QAAQ,UAAkB,OAAyB;GACjD,MAAM,SAAS,QAAQ;GACvB,MAAM,SAAmB,EAAE;AAC3B,QAAK,IAAI,IAAI,UAAU,IAAI,WAAW,OAAO,IAC3C,KAAI,IAAI,KAAK,KAAK,KAAK,UACrB,QAAO,KAAK,GAAG;YACN,IAAI,OACb,QAAO,KAAK,GAAG,QAAQ,QAAQ,GAAG,EAAE,CAAC;QAChC;IACL,IAAI,UAAU,IAAI;IAClB,IAAI,QAAQ;AACZ,SAAK,MAAM,SAAS,cAAc,EAAE;AAClC,SAAI,UAAU,MAAM,KAAK,QAAQ;AAC/B,aAAO,KAAK,MAAM,KAAK,SAAU;AACjC,cAAQ;AACR;;AAEF,gBAAW,MAAM,KAAK;;AAExB,QAAI,CAAC,MAAO,QAAO,KAAK,GAAG;;AAG/B,UAAO;;EAGT,iBAAiB,UAAkB,OAAyB;GAC1D,MAAM,SAAS,QAAQ;GACvB,MAAM,SAAmB,EAAE;AAC3B,QAAK,IAAI,IAAI,UAAU,IAAI,WAAW,OAAO,IAC3C,KAAI,IAAI,KAAK,KAAK,KAAK,UACrB,QAAO,KAAK,GAAG;YACN,IAAI,OACb,QAAO,KAAK,GAAG,QAAQ,iBAAiB,GAAG,EAAE,CAAC;QACzC;IACL,IAAI,UAAU,IAAI;IAClB,IAAI,QAAQ;AACZ,SAAK,MAAM,SAAS,cAAc,EAAE;AAClC,SAAI,UAAU,MAAM,cAAc,QAAQ;AACxC,aAAO,KAAK,MAAM,cAAc,SAAU;AAC1C,cAAQ;AACR;;AAEF,gBAAW,MAAM,cAAc;;AAEjC,QAAI,CAAC,MAAO,QAAO,KAAK,GAAG;;AAG/B,UAAO;;EAGT,UAAU,KAAoC;GAC5C,MAAM,SAAS,QAAQ;AACvB,OAAI,MAAM,KAAK,OAAO,KAAK,UAAW,QAAO;AAC7C,OAAI,MAAM,QAAQ;IAChB,MAAM,MAAM,QAAQ,aAAa,IAAI;AACrC,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO;KACL,MAAM;KACN,SAAS,IAAI,KAAK;KAClB,UAAU,IAAI;KACf;;GAGH,MAAM,YAAY,cAAc;GAChC,IAAI,UAAU,MAAM;AACpB,QAAK,MAAM,SAAS,WAAW;AAC7B,QAAI,UAAU,MAAM,KAAK,OACvB,QAAO;KAAE,MAAM;KAAQ,WAAW,MAAM;KAAW,UAAU;KAAS;AAExE,eAAW,MAAM,KAAK;;AAExB,UAAO;;EAGT,OAAO,OAA8B;AACnC,OAAI,CAAC,MAAO,QAAO,EAAE;GACrB,MAAM,aAAa,MAAM,aAAa;GACtC,MAAM,UAAyB,EAAE;GACjC,MAAM,SAAS,QAAQ;GAGvB,MAAM,mBAAmB,QAAQ,OAAO,MAAM;AAC9C,QAAK,MAAM,OAAO,kBAAkB;IAElC,MAAM,OADY,QAAQ,iBAAiB,KAAK,EAAE,CAC3B,GAAI,aAAa;IACxC,IAAI,MAAM,KAAK,QAAQ,WAAW;AAClC,WAAO,QAAQ,IAAI;AACjB,aAAQ,KAAK;MAAE;MAAK,UAAU;MAAK,QAAQ,MAAM,MAAM;MAAQ,CAAC;AAChE,WAAM,KAAK,QAAQ,YAAY,MAAM,EAAE;;;GAK3C,IAAI,YAAY;AAChB,QAAK,MAAM,SAAS,cAAc,EAAE;AAClC,SAAK,IAAI,IAAI,GAAG,IAAI,MAAM,cAAc,QAAQ,KAAK;KACnD,MAAM,OAAO,MAAM,cAAc,GAAI,aAAa;KAClD,IAAI,MAAM,KAAK,QAAQ,WAAW;AAClC,YAAO,QAAQ,IAAI;AACjB,cAAQ,KAAK;OAAE,KAAK,SAAS,YAAY;OAAG,UAAU;OAAK,QAAQ,MAAM,MAAM;OAAQ,CAAC;AACxF,YAAM,KAAK,QAAQ,YAAY,MAAM,EAAE;;;AAG3C,iBAAa,MAAM,cAAc;;AAGnC,UAAO;;EAEV;;;;ACtIH,SAAgB,kBAAkB,QAMlB;CACd,MAAM,4BAAY,IAAI,KAAiB;CAEvC,SAAS,SAAe;AACtB,OAAK,MAAM,MAAM,UAAW,KAAI;;AAGlC,QAAO;EACL,IAAI,KAAa;AACf,UAAO,OAAO;;EAGhB,IAAI,WAAyB;AAC3B,UAAO,OAAO;;EAGhB,QAAQ,UAAkB,UAAkB,QAAgB,QAAwB;GAClF,MAAM,OAAO,OAAO,SAAS,QAAQ,UAAU,SAAS,WAAW,EAAE;GACrE,MAAM,QAAkB,EAAE;AAC1B,QAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;IACpC,MAAM,QAAQ,UAAU,KAAK,GAAI;IACjC,MAAM,MAAM,WAAW;AACvB,QAAI,QAAQ,YAAY,QAAQ,OAC9B,OAAM,KAAK,MAAM,MAAM,UAAU,OAAO,CAAC;aAChC,QAAQ,SACjB,OAAM,KAAK,MAAM,MAAM,SAAS,CAAC;aACxB,QAAQ,OACjB,OAAM,KAAK,MAAM,MAAM,GAAG,OAAO,CAAC;QAElC,OAAM,KAAK,MAAM;;AAGrB,UAAO,MAAM,KAAK,KAAK;;EAGzB,OAAO,OAA8B;AACnC,UAAO,OAAO,SAAS,OAAO,MAAM;;EAGtC,QAAQ,aAAqB,aAA0D;GACrF,MAAM,SAAS,OAAO,mBAAmB,YAAY;AACrD,OAAI,SAAS,KAAK,UAAU,OAAO,SAAS,UAAW,QAAO;AAC9D,UAAO;IAAE,KAAK;IAAQ,KAAK;IAAa;;EAG1C,OAAO,KAAmB;AACxB,UAAO,SAAS,IAAI;AACpB,WAAQ;;EAGV,sBAA4B;AAC1B,WAAQ;;EAGV,UAAU,UAAkC;AAC1C,aAAU,IAAI,SAAS;AACvB,gBAAa;AACX,cAAU,OAAO,SAAS;;;EAI9B,IAAI,eAAoC;AACtC,UAAO,OAAO;;EAEjB;;;;ACvDH,SAAgB,gBAAgB,QAAoD;CAClF,MAAM,EAAE,SAAS,gBAAgB,iBAAiB;CAElD,MAAM,eAAe,QAAQ;AAE7B,KAAI,gBAAgB,KAAK,iBAAiB,EACxC,QAAO;EACL,aAAa,EAAE;EACf,iBAAiB;EACjB,iBAAiB;EACjB,cAAc;EACd,aAAa,eAAe;EAC7B;CAKH,MAAM,WAAW,KAAK,IAAI,GAAG,eADP,KAAK,IAAI,cAAc,aAAa,CACA;CAC1D,MAAM,aAAa,KAAK,IAAI,gBAAgB,eAAe,SAAS;AAIpE,QAAO;EACL,aAJkB,QAAQ,QAAQ,UAAU,WAAW;EAKvD,iBAAiB;EACjB,iBALsB,iBAAiB;EAMvC,cAAc;EACd,aAAa,eAAe;EAC7B;;;;ACrCH,SAAgB,oBAAiC;AAC/C,QAAO;EACL,QAAQ;EACR,OAAO;EACP,SAAS,EAAE;EACX,cAAc;EACd,gBAAgB;EACjB;;;;;AAMH,SAAgB,aACd,QACA,OACA,UAC+B;AAC/B,SAAQ,OAAO,MAAf;EACE,KAAK,OACH,QAAO,CACL;GAAE,GAAG;GAAO,QAAQ;GAAM,OAAO;GAAI,SAAS,EAAE;GAAE,cAAc;GAAI,gBAAgB;GAAG,EACvF,CAAC,EAAE,MAAM,UAAU,CAAC,CACrB;EAEH,KAAK,QACH,QAAO,CAAC,mBAAmB,EAAE,CAAC,EAAE,MAAM,UAAU,CAAC,CAAC;EAEpD,KAAK,SAAS;GACZ,MAAM,QAAQ,MAAM,MAAM,MAAM,GAAG,MAAM,eAAe,GAAG,OAAO,OAAO,MAAM,MAAM,MAAM,MAAM,eAAe;GAChH,MAAM,iBAAiB,MAAM,iBAAiB;GAC9C,MAAM,UAAU,WAAW,SAAS,MAAM,GAAG,EAAE;GAC/C,MAAM,eAAe,QAAQ,SAAS,IAAI,IAAI;GAC9C,MAAM,UAA0B,CAAC,EAAE,MAAM,UAAU,CAAC;AACpD,OAAI,gBAAgB,EAClB,SAAQ,KAAK;IAAE,MAAM;IAAY,KAAK,QAAQ,GAAI;IAAK,CAAC;AAE1D,UAAO,CAAC;IAAE,GAAG;IAAO;IAAO;IAAgB;IAAS;IAAc,EAAE,QAAQ;;EAG9E,KAAK,aAAa;AAChB,OAAI,MAAM,mBAAmB,EAAG,QAAO,CAAC,OAAO,EAAE,CAAC;GAClD,MAAM,QAAQ,MAAM,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,GAAG,MAAM,MAAM,MAAM,MAAM,eAAe;GACtG,MAAM,iBAAiB,MAAM,iBAAiB;GAC9C,MAAM,UAAU,WAAW,SAAS,MAAM,GAAG,EAAE;GAC/C,MAAM,eAAe,QAAQ,SAAS,IAAI,IAAI;GAC9C,MAAM,UAA0B,CAAC,EAAE,MAAM,UAAU,CAAC;AACpD,OAAI,gBAAgB,EAClB,SAAQ,KAAK;IAAE,MAAM;IAAY,KAAK,QAAQ,GAAI;IAAK,CAAC;AAE1D,UAAO,CAAC;IAAE,GAAG;IAAO;IAAO;IAAgB;IAAS;IAAc,EAAE,QAAQ;;EAG9E,KAAK,aAAa;AAChB,OAAI,MAAM,QAAQ,WAAW,EAAG,QAAO,CAAC,OAAO,EAAE,CAAC;GAClD,MAAM,gBAAgB,MAAM,eAAe,KAAK,MAAM,QAAQ;AAC9D,UAAO,CACL;IAAE,GAAG;IAAO;IAAc,EAC1B,CAAC;IAAE,MAAM;IAAY,KAAK,MAAM,QAAQ,cAAe;IAAK,EAAE,EAAE,MAAM,UAAU,CAAC,CAClF;;EAGH,KAAK,aAAa;AAChB,OAAI,MAAM,QAAQ,WAAW,EAAG,QAAO,CAAC,OAAO,EAAE,CAAC;GAClD,MAAM,gBAAgB,MAAM,eAAe,IAAI,MAAM,QAAQ,UAAU,MAAM,QAAQ;AACrF,UAAO,CACL;IAAE,GAAG;IAAO;IAAc,EAC1B,CAAC;IAAE,MAAM;IAAY,KAAK,MAAM,QAAQ,cAAe;IAAK,EAAE,EAAE,MAAM,UAAU,CAAC,CAClF;;EAGH,KAAK;AACH,OAAI,MAAM,mBAAmB,EAAG,QAAO,CAAC,OAAO,EAAE,CAAC;AAClD,UAAO,CAAC;IAAE,GAAG;IAAO,gBAAgB,MAAM,iBAAiB;IAAG,EAAE,EAAE,CAAC;EAErE,KAAK;AACH,OAAI,MAAM,kBAAkB,MAAM,MAAM,OAAQ,QAAO,CAAC,OAAO,EAAE,CAAC;AAClE,UAAO,CAAC;IAAE,GAAG;IAAO,gBAAgB,MAAM,iBAAiB;IAAG,EAAE,EAAE,CAAC;EAErE,KAAK;AACH,OAAI,MAAM,mBAAmB,EAAG,QAAO,CAAC,OAAO,EAAE,CAAC;AAClD,UAAO,CAAC;IAAE,GAAG;IAAO,gBAAgB;IAAG,EAAE,EAAE,CAAC;EAE9C,KAAK;AACH,OAAI,MAAM,kBAAkB,MAAM,MAAM,OAAQ,QAAO,CAAC,OAAO,EAAE,CAAC;AAClE,UAAO,CAAC;IAAE,GAAG;IAAO,gBAAgB,MAAM,MAAM;IAAQ,EAAE,EAAE,CAAC;EAE/D,KAAK,kBAAkB;AACrB,OAAI,MAAM,mBAAmB,EAAG,QAAO,CAAC,OAAO,EAAE,CAAC;GAElD,IAAI,MAAM,MAAM;AAChB,UAAO,MAAM,KAAK,KAAK,KAAK,MAAM,MAAM,MAAM,MAAM,GAAG,CAAE;AACzD,UAAO,MAAM,KAAK,CAAC,KAAK,KAAK,MAAM,MAAM,MAAM,MAAM,GAAG,CAAE;GAC1D,MAAM,QAAQ,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,MAAM,MAAM,MAAM,MAAM,eAAe;GACjF,MAAM,iBAAiB;GACvB,MAAM,UAAU,WAAW,SAAS,MAAM,GAAG,EAAE;GAC/C,MAAM,eAAe,QAAQ,SAAS,IAAI,IAAI;GAC9C,MAAM,UAA0B,CAAC,EAAE,MAAM,UAAU,CAAC;AACpD,OAAI,gBAAgB,EAClB,SAAQ,KAAK;IAAE,MAAM;IAAY,KAAK,QAAQ,GAAI;IAAK,CAAC;AAE1D,UAAO,CAAC;IAAE,GAAG;IAAO;IAAO;IAAgB;IAAS;IAAc,EAAE,QAAQ;;EAG9E,KAAK,iBAAiB;AACpB,OAAI,MAAM,mBAAmB,EAAG,QAAO,CAAC,OAAO,EAAE,CAAC;GAClD,MAAM,QAAQ,MAAM,MAAM,MAAM,MAAM,eAAe;GACrD,MAAM,iBAAiB;GACvB,MAAM,UAAU,WAAW,SAAS,MAAM,GAAG,EAAE;GAC/C,MAAM,eAAe,QAAQ,SAAS,IAAI,IAAI;GAC9C,MAAM,UAA0B,CAAC,EAAE,MAAM,UAAU,CAAC;AACpD,OAAI,gBAAgB,EAClB,SAAQ,KAAK;IAAE,MAAM;IAAY,KAAK,QAAQ,GAAI;IAAK,CAAC;AAE1D,UAAO,CAAC;IAAE,GAAG;IAAO;IAAO;IAAgB;IAAS;IAAc,EAAE,QAAQ;;;;;;;;AASlF,SAAgB,gBAAgB,OAAoB,MAAsB;CACxE,MAAM,SAAS;CACf,MAAM,YACJ,MAAM,QAAQ,SAAS,IACnB,MAAM,MAAM,eAAe,EAAE,GAAG,MAAM,QAAQ,OAAO,KACrD,MAAM,QACJ,mBACA;AAMR,QAAO,WAJS,SAAS,MAAM,QAAQ,WAChB,OAAO,KAAK,CAGX;;;;;AClJ1B,SAAgB,WAAW,IAAwB;AACjD,QAAO;EAAE,MAAM;EAAQ;EAAI;;;AAQ7B,SAAgB,WAAW,QAA8B;AACvD,KAAI,OAAO,SAAS,OAAQ,QAAO,CAAC,OAAO,GAAG;AAC9C,QAAO,CAAC,GAAG,WAAW,OAAO,MAAM,EAAE,GAAG,WAAW,OAAO,OAAO,CAAC;;;AAIpE,SAAgBA,cAAY,QAA8B;AACxD,QAAO,WAAW,OAAO;;;;;;AAW3B,SAAgB,UACd,QACA,cACA,WACA,WACA,QAAQ,IACI;CACZ,MAAM,eAAe,WAAW,MAAM;AAEtC,KAAI,OAAO,SAAS,QAAQ;AAC1B,MAAI,OAAO,OAAO,aAChB,QAAO;GACL,MAAM;GACN;GACA,OAAO;GACP,OAAO;IAAE,MAAM;IAAQ,IAAI;IAAc;GACzC,QAAQ;IAAE,MAAM;IAAQ,IAAI;IAAW;GACxC;AAEH,SAAO;;CAIT,MAAM,WAAW,UAAU,OAAO,OAAO,cAAc,WAAW,WAAW,MAAM;CACnF,MAAM,YAAY,UAAU,OAAO,QAAQ,cAAc,WAAW,WAAW,MAAM;AAErF,KAAI,aAAa,OAAO,SAAS,cAAc,OAAO,OAAQ,QAAO;AAErE,QAAO;EAAE,GAAG;EAAQ,OAAO;EAAU,QAAQ;EAAW;;;;;;AAO1D,SAAgB,WAAW,QAAoB,QAAmC;AAChF,KAAI,OAAO,SAAS,OAClB,QAAO,OAAO,OAAO,SAAS,OAAO;AAIvC,KAAI,OAAO,MAAM,SAAS,UAAU,OAAO,MAAM,OAAO,OACtD,QAAO,OAAO;AAEhB,KAAI,OAAO,OAAO,SAAS,UAAU,OAAO,OAAO,OAAO,OACxD,QAAO,OAAO;CAIhB,MAAM,WAAW,WAAW,OAAO,OAAO,OAAO;CACjD,MAAM,YAAY,WAAW,OAAO,QAAQ,OAAO;AAGnD,KAAI,aAAa,KAAM,QAAO;AAC9B,KAAI,cAAc,KAAM,QAAO;AAE/B,KAAI,aAAa,OAAO,SAAS,cAAc,OAAO,OAAQ,QAAO;AAErE,QAAO;EAAE,GAAG;EAAQ,OAAO;EAAU,QAAQ;EAAW;;;AAI1D,SAAgB,UAAU,QAAoB,SAAiB,SAA6B;AAC1F,KAAI,OAAO,SAAS,QAAQ;AAC1B,MAAI,OAAO,OAAO,QAAS,QAAO;GAAE,MAAM;GAAQ,IAAI;GAAS;AAC/D,MAAI,OAAO,OAAO,QAAS,QAAO;GAAE,MAAM;GAAQ,IAAI;GAAS;AAC/D,SAAO;;CAGT,MAAM,WAAW,UAAU,OAAO,OAAO,SAAS,QAAQ;CAC1D,MAAM,YAAY,UAAU,OAAO,QAAQ,SAAS,QAAQ;AAE5D,KAAI,aAAa,OAAO,SAAS,cAAc,OAAO,OAAQ,QAAO;AAErE,QAAO;EAAE,GAAG;EAAQ,OAAO;EAAU,QAAQ;EAAW;;;;;;;AAQ1D,SAAgB,YAAY,QAAoB,QAAgB,OAA2B;AACzF,KAAI,OAAO,SAAS,OAAQ,QAAO;AAInC,KAFiB,WAAW,OAAO,MAAM,CAE5B,SAAS,OAAO,EAAE;EAE7B,MAAM,WAAW,WAAW,OAAO,QAAQ,MAAM;AACjD,MAAI,aAAa,OAAO,MAAO,QAAO;AAEtC,SAAO;GAAE,GAAG;GAAQ,OAAO;GAAU;;AAKvC,KAFkB,WAAW,OAAO,OAAO,CAE7B,SAAS,OAAO,EAAE;EAE9B,MAAM,WAAW,WAAW,OAAO,QAAQ,MAAM;AACjD,MAAI,aAAa,OAAO,MAAO,QAAO;AAEtC,SAAO;GAAE,GAAG;GAAQ,OAAO;GAAU;;AAGvC,QAAO;;;;;;;;;;AAeT,SAAgB,iBACd,QACA,QACA,WACe;CACf,MAAM,OAAO,SAAS,QAAQ,OAAO;AACrC,KAAI,CAAC,KAAM,QAAO;CAElB,MAAM,iBAAiB,cAAc,UAAU,cAAc,UAAU,eAAe;CACtF,MAAM,aAAa,cAAc,WAAW,cAAc;AAG1D,MAAK,IAAI,IAAI,KAAK,SAAS,GAAG,KAAK,GAAG,KAAK;EACzC,MAAM,OAAO,KAAK;AAClB,MAAI,KAAK,KAAK,SAAS,QAAS;AAChC,MAAI,KAAK,KAAK,cAAc,eAAgB;AAG5C,MAAI,KAAK,SAAS,WAAW,WAC3B,QAAO,UAAU,KAAK,KAAK,OAAO;AAIpC,MAAI,KAAK,SAAS,YAAY,CAAC,WAC7B,QAAO,SAAS,KAAK,KAAK,MAAM;;AAIpC,QAAO;;AAOT,SAAS,WAAW,OAAuB;AACzC,QAAO,KAAK,IAAI,IAAK,KAAK,IAAI,IAAK,MAAM,CAAC;;;AAI5C,SAAS,UAAU,MAA0B;AAC3C,KAAI,KAAK,SAAS,OAAQ,QAAO,KAAK;AACtC,QAAO,UAAU,KAAK,MAAM;;;AAI9B,SAAS,SAAS,MAA0B;AAC1C,KAAI,KAAK,SAAS,OAAQ,QAAO,KAAK;AACtC,QAAO,SAAS,KAAK,OAAO;;;AAS9B,SAAS,SAAS,QAAoB,QAAmC;AACvE,KAAI,OAAO,SAAS,OAClB,QAAO,OAAO,OAAO,SAAS,EAAE,GAAG;CAGrC,MAAM,YAAY,SAAS,OAAO,OAAO,OAAO;AAChD,KAAI,cAAc,KAChB,QAAO,CAAC;EAAE,MAAM;EAAQ,MAAM;EAAS,EAAE,GAAG,UAAU;CAGxD,MAAM,aAAa,SAAS,OAAO,QAAQ,OAAO;AAClD,KAAI,eAAe,KACjB,QAAO,CAAC;EAAE,MAAM;EAAQ,MAAM;EAAU,EAAE,GAAG,WAAW;AAG1D,QAAO;;;;;;;;;;UCzOa;AAMtB,MAAMC,QAAM;AACZ,MAAMC,QAAM,GAAGD,MAAI;AAGnB,MAAM,cAAc,GAAGC,MAAI;AAC3B,MAAM,cAAc,GAAGA,MAAI;AAC3B,MAAM,cAAc,GAAGA,MAAI;AAM3B,MAAM,aAAa,GAAGA,MAAI;AAC1B,MAAM,WAAW,GAAGA,MAAI;AAGxB,MAAMC,UAAQ,GAAGD,MAAI;AAGrB,MAAM,MAAM;CAEV,MAAM;CACN,KAAK;CACL,QAAQ;CACR,WAAW;CACX,OAAO;CACP,SAAS;CACT,QAAQ;CACR,eAAe;CAGf,SAAS;CACT,WAAW;CACX,cAAc;CACd,UAAU;CACV,YAAY;CACZ,WAAW;CACX,kBAAkB;CAGlB,WAAW;CACX,SAAS;CACT,OAAO;CACP,SAAS;CACT,UAAU;CACV,QAAQ;CACR,WAAW;CACX,QAAQ;CACR,SAAS;CACT,eAAe;CACf,aAAa;CACb,eAAe;CACf,gBAAgB;CAChB,cAAc;CACd,iBAAiB;CACjB,cAAc;CACd,eAAe;CAGf,WAAW;CACX,SAAS;CACT,OAAO;CACP,SAAS;CACT,UAAU;CACV,QAAQ;CACR,WAAW;CACX,QAAQ;CACR,SAAS;CACT,eAAe;CACf,aAAa;CACb,eAAe;CACf,gBAAgB;CAChB,cAAc;CACd,iBAAiB;CACjB,cAAc;CACd,eAAe;CAChB;;;;;AAUD,SAASE,aAAW,GAAW,GAAmB;AAChD,QAAO,GAAGF,QAAM,IAAI,EAAE,GAAG,IAAI,EAAE;;;;;AAMjC,SAAS,SAAS,GAAmB;AACnC,KAAI,KAAK,EAAG,QAAO;AACnB,KAAI,MAAM,EAAG,QAAO,GAAGA,MAAI;AAC3B,QAAO,GAAGA,QAAM,EAAE;;;;;AAMpB,SAAS,WAAW,GAAmB;AACrC,KAAI,KAAK,EAAG,QAAO;AACnB,KAAI,MAAM,EAAG,QAAO,GAAGA,MAAI;AAC3B,QAAO,GAAGA,QAAM,EAAE;;;;;AAMpB,SAAS,YAAY,GAAmB;AACtC,KAAI,KAAK,EAAG,QAAO;AACnB,KAAI,MAAM,EAAG,QAAO,GAAGA,MAAI;AAC3B,QAAO,GAAGA,QAAM,EAAE;;;;;AAMpB,SAAS,WAAW,GAAmB;AACrC,KAAI,KAAK,EAAG,QAAO;AACnB,KAAI,MAAM,EAAG,QAAO,GAAGA,MAAI;AAC3B,QAAO,GAAGA,QAAM,EAAE;;;;;AAMpB,SAAS,eAAe,GAAmB;AACzC,QAAO,GAAGA,QAAM,IAAI,EAAE;;AAYxB,MAAM,qBAA6E;CACjF,OAAO;EAAE,OAAO;EAAG,QAAQ;EAAG;CAC9B,WAAW;EAAE,OAAO;EAAG,QAAQ;EAAG;CAClC,KAAK;EAAE,OAAO;EAAG,QAAQ;EAAG;CAC7B;;;;;;;;;;AAWD,SAAgB,eAAe,OAAoB,QAAQ,OAAe;AAExE,QAAO,GAAGA,QADG,QAAQ,mBAAmB,OAAO,QAAQ,mBAAmB,OAAO,OAC5D;;;;;AAMvB,SAAgB,mBAA2B;AACzC,QAAO,GAAGA,MAAI;;;;;;;;;;;AAgBhB,SAAgB,uBAA+B;AAC7C,QAAO,GAAGA,MAAI,QAAQA,MAAI,IAAI,cAAc;;;;;;;;AAS9C,SAAgB,uBAA+B;AAC7C,QAAO,GAAG,WAAW,cAAcA,MAAI;;AAIzC,MAAa,cAAcG;AAC3B,MAAa,eAAeC;;;;;;;;;;;;AAa5B,MAAa,aAAa;CACxB,cAAc;CACd,eAAe;CACf,kBAAkB;CAClB,iBAAiB;CACjB,aAAa;CACd;;;;;;;;;;AAWD,MAAa,sBAAsBC;AACnC,SAAgB,qBAA6B;AAC3C,QAAO,GAAGL,MAAI;;AAEhB,MAAa,uBAAuBM;;AAOpC,MAAaC,QAAM;;AAGnB,SAAgB,aAAa,SAAyB;AACpD,QAAO,GAAGR,MAAI,KAAK;;;AAIrB,SAAgB,YAAY,SAAiB,MAAmC;AAE9E,QAAO,GAAGA,MAAI,aADC,MAAM,QAAQ,QAAQ,KAAK,UAAU,GAClB,GAAG,UAAUA,MAAI;;;;;;;;;;AAWrD,SAAgB,OAAO,QAA4B,SAAiB,MAAiC;CACnG,MAAM,cAAc,QAAQ,IAAI,gBAAgB;CAChD,MAAM,OAAO,QAAQ,IAAI,QAAQ;AAEjC,KAAI,gBAAgB,YAClB,QAAO,MAAM,aAAa,QAAQ,CAAC;UAC1B,SAAS,cAClB,QAAO,MAAM,YAAY,SAAS,KAAK,CAAC;KAExC,QAAO,MAAA,OAAU;;;;;;;AAarB,SAAgB,eAAe,QAA4B,OAAqB;AAC9E,QAAO,MAAM,GAAGA,MAAI,KAAK,SAAc;;;;;;;AAQzC,SAAgB,sBAAsB,QAA4B,OAAqB;AACrF,QAAO,MAAM,GAAGA,MAAI,KAAK,SAAc;;;;;;AAOzC,SAAgB,iBAAiB,QAAkC;AACjE,QAAO,MAAM,GAAGA,MAAI,MAAW;;;;;AAUjC,SAAgB,gBAAgB,QAA4B,MAAoB;CAE9E,MAAM,OAAO,UAAU;CACvB,MAAM,UAAU,UAAU,KAAK,CAAC,QAAQ,MAAM,MAAM;AACpD,QAAO,MAAM,GAAGA,MAAI,YAAY,OAAO,WAAgB;;;;;;;;AAqBzD,SAAgB,oBAAoB,OAAiC;AACnE,QAAO,GAAGA,MAAI,MAAM;;;;;;;AAQtB,SAAgB,wBAAgC;AAC9C,QAAO,GAAGA,MAAI;;AAOhB,MAAa,OAAO;CAClB,KAAA;CACA,KAAA;CACA;CACA;CACA;CACA;CACA;CACA,OAAA;CACA;CACA,YAAA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;;;;;;;AClXD,SAAgB,UAAsB;CACpC,MAAM,KAAK,WAAW,eAAe;AACrC,KAAI,CAAC,GAAI,OAAM,IAAI,MAAM,mDAAmD;AAC5E,QAAO,GAAG;;;;;;;;;;;;;ACVZ,MAAMS,QAAM;;AAGZ,SAAgB,gBAAgB,QAA4B,KAAa,QAAsB;AAC7F,QAAO,MAAM,GAAGA,MAAI,GAAG,IAAI,GAAG,OAAO,GAAG;;;AAI1C,SAAgB,kBAAkB,QAAkC;AAClE,QAAO,MAAM,GAAGA,MAAI,IAAI;;;AAI1B,SAAgB,SAAS,QAA4B,QAAgB,GAAS;AAC5E,QAAO,MAAM,GAAGA,MAAI,GAAG,MAAM,GAAG;;;AAIlC,SAAgB,WAAW,QAA4B,QAAgB,GAAS;AAC9E,QAAO,MAAM,GAAGA,MAAI,GAAG,MAAM,GAAG;;;AAIlC,SAAgB,WAAW,QAA4B,KAAa,KAAmB;AACrF,QAAO,MAAM,GAAGA,MAAI,GAAG,IAAI,GAAG,IAAI,GAAG;;;;;;;;AAkBvC,SAAgB,wBAAiC;CAC/C,MAAM,OAAO,QAAQ,IAAI,QAAQ;CACjC,MAAM,cAAc,QAAQ,IAAI,gBAAgB;AAGhD,KAAI,gBAAgB,eAAe,gBAAgB,aAAa,gBAAgB,aAAa,gBAAgB,SAC3G,QAAO;AAGT,KAAI,KAAK,SAAS,QAAQ,IAAI,KAAK,SAAS,SAAS,IAAI,KAAK,SAAS,OAAO,IAAI,KAAK,SAAS,QAAQ,CAAE,QAAO;AAGjH,KAAI,SAAS,QAAS,QAAO;AAG7B,QAAO,SAAS;;;;;;;;;;;;;;;;;;;;;;ACgBlB,SAAgB,oBAAiC;CAC/C,MAAM,QAAqB;EACzB,OAAO;EACP,2BAAW,IAAI,KAAK;EACpB,WAAW;EACX,eAAe,GAAuB;AACpC,SAAM,QAAQ;AACd,QAAK,MAAM,YAAY,MAAM,UAAW,WAAU;;EAErD;AACD,OAAM,YAAY;EAChB,sBAAsB,MAAM;EAC5B,kBAAkB,aAAyB;AACzC,SAAM,UAAU,IAAI,SAAS;AAC7B,gBAAa;AACX,UAAM,UAAU,OAAO,SAAS;;;EAGrC;AACD,QAAO;;AAOT,MAAM,YAAY,cAAkC,KAAK;;;;;AAMzD,SAAgB,eAAe,EAAE,OAAO,YAA0D;AAChG,QAAO,MAAM,cAAc,UAAU,UAAU,EAAE,OAAO,OAAO,EAAE,SAAS;;AAO5E,IAAI,qBAAyC;AAC7C,IAAI,yCAAyB,IAAI,KAAiB;AAElD,SAAS,qBAAqB,OAAiC;AAC7D,sBAAqB;AACrB,MAAK,MAAM,YAAY,uBAAwB,WAAU;;AAG3D,SAAS,uBAA2C;AAClD,QAAO;;AAGT,SAAS,sBAAsB,UAAkC;AAC/D,wBAAuB,IAAI,SAAS;AACpC,cAAa;AACX,yBAAuB,OAAO,SAAS;;;;AAK3C,SAAgB,mBAAyB;AACvC,sBAAqB;AACrB,0CAAyB,IAAI,KAAK;;;;;;;;;;;AAgBpC,SAAgB,UAAU,UAAgC;CACxD,MAAM,EAAE,KAAK,KAAK,UAAU,MAAM,UAAU;CAC5C,MAAM,QAAQ,WAAW,UAAU;CACnC,MAAM,OAAO,WAAW,YAAY;CAGpC,MAAM,MAAM,QAAQ,MAAM,eAAe,KAAK,MAAM,GAAG;CACvD,MAAM,MAAM,QAAQ,MAAM,UAAU,iBAAiB;CAKrD,MAAM,QAAQ,MAAM;CACpB,MAAM,UAAU,OAAO,eAAe,OAAO,YAAY,OAAO,WAAW;CAC3E,MAAM,SAAS,OAAO,cAAc,OAAO,YAAY,OAAO,WAAW;CACzE,MAAM,aAAa,OAAO,cAAc,IAAI;CAC5C,MAAM,YAAY,OAAO,cAAc,IAAI;CAC3C,MAAM,iBAAiB,aAAa;CACpC,MAAM,iBAAiB,YAAY;CAGnC,MAAM,SAAS,OAAO,IAAI;CAC1B,MAAM,SAAS,OAAO,IAAI;CAC1B,MAAM,aAAa,OAAO,QAAQ;CAClC,MAAM,WAAW,OAAO,MAAM;CAC9B,MAAM,SAAS,OAAO,IAAI;CAC1B,MAAM,SAAS,OAAO,IAAI;CAC1B,MAAM,cAAc,OAAoB,KAAK;CAC7C,MAAM,oBAAoB,OAAO,eAAe;CAChD,MAAM,oBAAoB,OAAO,eAAe;AAChD,QAAO,UAAU;AACjB,QAAO,UAAU;AACjB,YAAW,UAAU;AACrB,UAAS,UAAU;AACnB,QAAO,UAAU;AACjB,QAAO,UAAU;AACjB,mBAAkB,UAAU;AAC5B,mBAAkB,UAAU;AAI5B,eACE,aAAa,SAAS;AACpB,cAAY,UAAU;AACtB,MAAI,CAAC,WAAW,QACd;AAEF,SAAO,QAAQ;GACb,GAAG,KAAK,IAAI,kBAAkB,UAAU,OAAO;GAC/C,GAAG,KAAK,IAAI,kBAAkB,UAAU,OAAO;GAC/C,SAAS;GACT,OAAO,SAAS;GACjB,CAAC;IACD,EAAE,CAAC,CACP;AAMD,uBAAsB;EACpB,MAAM,OAAO,YAAY;AACzB,MAAI,CAAC,QAAQ,CAAC,QAAS;AACvB,MAAI;GACF,GAAG,KAAK,IAAI,iBAAiB;GAC7B,GAAG,KAAK,IAAI,iBAAiB;GAC7B,SAAS;GACT;GACD,CAAC;IACD;EAAC;EAAK;EAAK;EAAgB;EAAgB;EAAO;EAAS;EAAI,CAAC;AAGnE,iBAAgB;AACd,MAAI,CAAC;OAEa,OAAO,SAAS,CAE9B,QAAO,QAAQ,KAAK;;AAIxB,eAAa;AAEX,UAAO,QAAQ,KAAK;;IAErB,CAAC,QAAQ,CAAC;;;;;;AAWf,SAAS,iBAAqC;AAC5C,QAAO,sBAAsB;;AAG/B,SAAS,gBAAgB,UAAkC;AACzD,QAAO,sBAAsB,SAAS;;;;;;;;;;;;;;;;;;;;;;;AC7NxC,SAAS,eAAe,MAA2F;CACjH,MAAM,UAAU,KAAK,MAAM;AAC3B,KAAI,CAAC,QAAQ,WAAW,MAAM,CAAE,QAAO;CAEvC,MAAM,OAAO,QAAQ,MAAM,EAAE;CAE7B,MAAM,SAAS,KAAK,MAAM,kCAAkC;AAC5D,KAAI,OACF,QAAO;EACL,UAAU,OAAO;EACjB,MAAM,OAAO;EACb,MAAM,OAAO,OAAO,GAAG;EACvB,QAAQ,OAAO,OAAO,GAAG;EAC1B;CAGH,MAAM,SAAS,KAAK,MAAM,sBAAsB;AAChD,KAAI,OACF,QAAO;EAAE,MAAM,OAAO;EAAI,MAAM,OAAO,OAAO,GAAG;EAAE,QAAQ,OAAO,OAAO,GAAG;EAAE;AAEhF,QAAO;;;;;AAMT,SAAS,YAAY,UAAkD;AACrE,KAAI,CAAC,SAAU,QAAO;CACtB,IAAI,IAAI;CACR,MAAM,UAAU,QAAQ,KAAK;AAE7B,KAAI,EAAE,QAAQ,cAAc,GAAG;AAE/B,MAAK,MAAM,UAAU,CAAC,SAAS,WAAW,UAAU,CAClD,KAAI,EAAE,WAAW,GAAG,OAAO,GAAG,EAAE;AAC9B,MAAI,EAAE,MAAM,OAAO,SAAS,EAAE;AAC9B;;AAGJ,QAAO;;;;;AAMT,SAAS,eAAe,UAAkB,MAA6D;AACrG,KAAI;AACF,MAAI,CAAC,GAAG,WAAW,SAAS,CAAE,QAAO;EAErC,MAAM,QADS,GAAG,aAAa,UAAU,OAAO,CAC3B,MAAM,KAAK;EAChC,MAAM,QAAQ,KAAK,IAAI,GAAG,OAAO,EAAE;EACnC,MAAM,MAAM,KAAK,IAAI,MAAM,QAAQ,OAAO,EAAE;EAC5C,MAAM,SAAiD,EAAE;AACzD,OAAK,IAAI,IAAI,OAAO,IAAI,KAAK,IAC3B,QAAO,KAAK;GAAE,MAAM,IAAI;GAAG,QAAQ,MAAM,MAAM,IAAI,QAAQ,OAAO,KAAK;GAAE,CAAC;AAE5E,SAAO;SACD;AACN,SAAO;;;;;;;;;;AAeX,IAAa,uBAAb,cAA0C,UAAgE;CACxG,QAA4C,EAAE,OAAO,MAAM;CAE3D,OAAO,yBAAyB,OAAyC;AACvE,SAAO,EAAE,OAAO;;CAGlB,kBAA2B,OAAc;AACvC,OAAK,MAAM,UAAU,MAAM;;CAG7B,SAAkB;AAChB,MAAI,KAAK,MAAM,OAAO;GACpB,MAAM,MAAM,KAAK,MAAM;GACvB,MAAM,QAAQ,IAAI,QAAQ,IAAI,MAAM,MAAM,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE;GAC7D,MAAM,SAAS,MAAM,SAAS,IAAI,eAAe,MAAM,GAAI,GAAG;GAC9D,MAAM,WAAW,YAAY,QAAQ,KAAK;GAG1C,IAAI,UAAyD;GAC7D,IAAI,YAAY;AAChB,OAAI,YAAY,QAAQ,MAAM;AAC5B,cAAU,eAAe,UAAU,OAAO,KAAK;AAC/C,QAAI,QACF,MAAK,MAAM,EAAE,UAAU,QACrB,aAAY,KAAK,IAAI,WAAW,OAAO,KAAK,CAAC,OAAO;;GAO1D,MAAM,WAA8B,EAAE;AAGtC,YAAS,KACP,MAAM,cACJ,eACA,EAAE,KAAK,UAAU,EACjB,MAAM,cAAc,gBAAgB;IAAE,iBAAiB;IAAO,OAAO;IAAS,EAAE,UAAU,EAC1F,MAAM,cAAc,gBAAgB,EAAE,EAAE,IAAI,IAAI,UAAU,CAC3D,CACF;AAGD,OAAI,YAAY,OACd,UAAS,KACP,MAAM,cACJ,eACA;IAAE,KAAK;IAAY,WAAW;IAAG,EACjC,MAAM,cAAc,gBAAgB,EAAE,UAAU,MAAM,EAAE,GAAG,SAAS,GAAG,OAAO,KAAK,GAAG,OAAO,SAAS,CACvG,CACF;AAIH,OAAI,WAAW,QAAQ;IACrB,MAAM,YAAY,QAAQ,KAAK,EAAE,MAAM,YAAY;KACjD,MAAM,UAAU,OAAO,KAAK,CAAC,SAAS,WAAW,IAAI;AACrD,YAAO,MAAM,cACX,eACA,EAAE,KAAK,QAAQ,QAAQ,EACvB,MAAM,cACJ,gBACA;MACE,UAAU,SAAS,OAAO;MAC1B,iBAAiB,SAAS,OAAO,OAAO,QAAQ,KAAA;MAChD,OAAO,SAAS,OAAO,OAAO,UAAU,KAAA;MACzC,EACD,GAAG,QAAQ,GACZ,EACD,MAAM,cACJ,gBACA;MACE,iBAAiB,SAAS,OAAO,OAAO,QAAQ,KAAA;MAChD,OAAO,SAAS,OAAO,OAAO,UAAU,KAAA;MACzC,EACD,IAAI,QACL,CACF;MACD;AACF,aAAS,KACP,MAAM,cAAc,eAAe;KAAE,KAAK;KAAQ,WAAW;KAAG,eAAe;KAAU,EAAE,GAAG,UAAU,CACzG;;AAIH,OAAI,MAAM,SAAS,GAAG;IACpB,MAAM,aAAa,MAAM,KAAK,MAAM,MAAM;KACxC,MAAM,SAAS,eAAe,KAAK;AACnC,SAAI,CAAC,OACH,QAAO,MAAM,cACX,eACA,EAAE,KAAK,SAAS,KAAK,EACrB,MAAM,cAAc,gBAAgB,EAAE,UAAU,MAAM,EAAE,KAAK,KAAK,MAAM,GAAG,CAC5E;KAEH,MAAM,YAAY,YAAY,OAAO,KAAK;AAC1C,YAAO,MAAM,cACX,eACA,EAAE,KAAK,SAAS,KAAK,EACrB,MAAM,cAAc,gBAAgB,EAAE,UAAU,MAAM,EAAE,KAAK,EAC7D,MAAM,cAAc,gBAAgB;MAAE,UAAU;MAAM,MAAM;MAAM,EAAE,OAAO,YAAY,GAAG,EAC1F,MAAM,cACJ,gBACA;MAAE,UAAU;MAAM,OAAO;MAAQ,EACjC,KAAK,aAAa,GAAG,GAAG,OAAO,KAAK,GAAG,OAAO,OAAO,GACtD,CACF;MACD;AACF,aAAS,KACP,MAAM,cAAc,eAAe;KAAE,KAAK;KAAS,WAAW;KAAG,eAAe;KAAU,EAAE,GAAG,WAAW,CAC3G;;AAGH,UAAO,MAAM,cAAc,eAAe;IAAE,eAAe;IAAU,SAAS;IAAG,EAAE,GAAG,SAAS;;AAEjG,SAAO,KAAK,MAAM;;;;;;ACxNtB,SAAS,YAAY,MAAuB;AAC1C,KAAI,KAAK,OAAQ,QAAO;CACxB,MAAM,QAAQ,KAAK;AACnB,QAAO,QAAQ,MAAM,UAAU,IAAI,MAAM,YAAY;;;AAIvD,SAAS,aAAa,MAAuB;CAC3C,MAAM,QAAQ,KAAK;AACnB,QAAO,QAAQ,MAAM,WAAW;;;;;;AAWlC,SAAgB,sBAAsB,MAA6B;CACjE,IAAI,UAAyB;AAC7B,QAAO,SAAS;AACd,MAAI,YAAY,QAAQ,CAAE,QAAO;AACjC,YAAU,QAAQ;;AAEpB,QAAO;;;;;;;;;;AAWT,SAAgB,YAAY,MAAc,OAA0B;CAClE,MAAM,SAAmB,EAAE;CAC3B,MAAM,WAAW,SAAS;CAE1B,SAAS,KAAK,MAAoB;AAEhC,MAAI,KAAK,OAAQ;AAEjB,MADc,KAAK,MACT,YAAY,OAAQ;AAK9B,MAAI,SAAS,YAAY,aAAa,KAAK,EAAE;AAE3C,OAAI,YAAY,KAAK,CACnB,QAAO,KAAK,KAAK;AAEnB;;AAGF,MAAI,YAAY,KAAK,CACnB,QAAO,KAAK,KAAK;AAGnB,OAAK,MAAM,SAAS,KAAK,SACvB,MAAK,MAAM;;AAIf,MAAK,SAAS;AACd,QAAO;;;;;;AAOT,SAAgB,mBAAmB,MAA6B;CAC9D,IAAI,UAAyB;AAC7B,QAAO,SAAS;AACd,MAAI,aAAa,QAAQ,EAAE;GACzB,MAAM,QAAQ,QAAQ;AACtB,UAAO,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;;AAE3D,YAAU,QAAQ;;AAEpB,QAAO;;;;;;AAOT,SAAgB,aAAa,MAAc,QAA+B;AAExE,KADc,KAAK,MACT,WAAW,OAAQ,QAAO;AAEpC,MAAK,MAAM,SAAS,KAAK,UAAU;EACjC,MAAM,QAAQ,aAAa,OAAO,OAAO;AACzC,MAAI,MAAO,QAAO;;AAEpB,QAAO;;;;;AAUT,SAAS,WAAW,MAAwC;AAC1D,QAAO;EACL,IAAI,KAAK,IAAI,KAAK,QAAQ;EAC1B,IAAI,KAAK,IAAI,KAAK,SAAS;EAC5B;;;;;;;;;AAUH,SAAS,SACP,QACA,WACA,WACS;CACT,MAAM,KAAK,UAAU,KAAK,OAAO;CACjC,MAAM,KAAK,UAAU,KAAK,OAAO;AAGjC,SAAQ,WAAR;EACE,KAAK;AACH,OAAI,MAAM,EAAG,QAAO;AAEpB,UAAO,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG;EACrC,KAAK;AACH,OAAI,MAAM,EAAG,QAAO;AACpB,UAAO,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG;EACrC,KAAK;AACH,OAAI,MAAM,EAAG,QAAO;AACpB,UAAO,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG;EACrC,KAAK;AACH,OAAI,MAAM,EAAG,QAAO;AACpB,UAAO,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG;;;;;;AAOzC,SAAS,SAAS,GAA+B,GAAuC;CACtF,MAAM,KAAK,EAAE,KAAK,EAAE;CACpB,MAAM,KAAK,EAAE,KAAK,EAAE;AACpB,QAAO,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;;;;;;;;;;;;;;;AAgBrC,SAAgB,kBACd,MACA,WACA,YACA,UACe;CACf,MAAM,aAAa,SAAS,KAAK;AACjC,KAAI,CAAC,WAAY,QAAO;CAExB,MAAM,SAAS,WAAW,WAAW;CAErC,IAAI,OAAsB;CAC1B,IAAI,WAAW;AAEf,MAAK,MAAM,aAAa,YAAY;AAClC,MAAI,cAAc,KAAM;EAExB,MAAM,gBAAgB,SAAS,UAAU;AACzC,MAAI,CAAC,cAAe;EAEpB,MAAM,SAAS,WAAW,cAAc;AAExC,MAAI,CAAC,SAAS,QAAQ,QAAQ,UAAU,CAAE;EAE1C,MAAM,OAAO,SAAS,QAAQ,OAAO;AACrC,MAAI,OAAO,UAAU;AACnB,cAAW;AACX,UAAO;;;AAIX,QAAO;;;;;;;;;;;;AAiBT,SAAgB,qBAAqB,MAAc,WAAkC;CAInF,MAAM,QAHQ,KAAK,MAEF,YAAY,UAAU,OAAO,EAAE,CAAC,aAAa,GAAG,UAAU,MAAM,EAAE;AAEnF,QAAO,OAAO,UAAU,WAAW,QAAQ;;;;;;;;ACvN7C,SAAgB,uBAAuB,MAAgC;AACrE,KAAI,CAAC,KAAK,iBACR,MAAK,mBAAmB;EACtB,SAAS;EACT,OAAO;EACP,UAAU;EACV,SAAS;EACT,YAAY;EACb;AAEH,QAAO,KAAK;;;;;AAUd,SAAgB,WAAW,MAAc,OAAyB;CAChE,MAAM,QAAQ,uBAAuB,KAAK;AAC1C,KAAI,MAAM,YAAY,MAAO,QAAO;AACpC,OAAM,UAAU;AAChB,QAAO;;;;;AAMT,SAAgB,SAAS,MAAc,OAAyB;CAC9D,MAAM,QAAQ,uBAAuB,KAAK;AAC1C,KAAI,MAAM,UAAU,MAAO,QAAO;AAClC,OAAM,QAAQ;AACd,QAAO;;;;;AAgBT,SAAgB,WAAW,MAAc,OAAyB;CAChE,MAAM,QAAQ,uBAAuB,KAAK;AAC1C,KAAI,MAAM,YAAY,MAAO,QAAO;AACpC,OAAM,UAAU;AAChB,QAAO;;;;ACiGT,SAAgB,mBAAmB,SAA6C;CAC9E,MAAM,gBAAgB,SAAS;CAG/B,IAAI,gBAA+B;CACnC,IAAI,WAA0B;CAC9B,IAAI,kBAAiC;CACrC,IAAI,aAA4B;CAChC,IAAI,cAAkC;CACtC,MAAM,aAAuB,EAAE;CAC/B,MAAM,cAAsC,EAAE;CAC9C,IAAI,gBAA+B;CAGnC,MAAM,iBAAkC,EAAE;CAE1C,IAAI,mBAAmB;CAGvB,MAAM,4BAAY,IAAI,KAAiB;CACvC,IAAI,WAAiC;;CAErC,IAAI,cAAc;CAElB,SAAS,SAAe;AACtB,aAAW;AACX;AACA,OAAK,MAAM,YAAY,UACrB,WAAU;;CAId,SAAS,UAAU,MAA6B;EAC9C,MAAM,QAAQ,KAAK;AACnB,SAAO,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;;CAK3D,SAAS,MAAM,MAAc,SAAsB,gBAAsB;AAEvE,MAAI,kBAAkB,MAAM;AAE1B,OAAI,gBAAgB,QAAQ;AAC1B,kBAAc;AACd,YAAQ;;AAEV;;EAGF,MAAM,aAAa;AACnB,oBAAkB;AAClB,eAAa;AACb,kBAAgB;AAChB,aAAW,UAAU,KAAK;AAC1B,gBAAc;AAGd,MAAI,WAAY,YAAW,YAAY,MAAM;AAC7C,aAAW,MAAM,KAAK;AAGtB,MAAI,YAAY,WAAW,SAAS,EAClC,aAAY,WAAW,WAAW,SAAS,MAAO;AAGpD,UAAQ;AAGR,kBAAgB,YAAY,MAAM,OAAO;;CAG3C,SAAS,UAAU,IAAY,MAAc,SAAsB,gBAAsB;EACvF,MAAM,OAAO,aAAa,MAAM,GAAG;AACnC,MAAI,MAAM;GAER,MAAM,YAAY,sBAAsB,KAAK;AAC7C,OAAI,WAAW;AACb,UAAM,WAAW,OAAO;AACxB;;;AASJ,MAAI,aAAa,MAAM,CAAC,cAAe;EAEvC,MAAM,aAAa;AACnB,oBAAkB;AAClB,eAAa;AACb,kBAAgB;AAChB,aAAW;AACX,gBAAc;AAGd,MAAI,WAAY,YAAW,YAAY,MAAM;AAE7C,UAAQ;AAGR,kBAAgB,YAAY,MAAM,OAAO;;CAG3C,SAAS,OAAa;AACpB,MAAI,CAAC,iBAAiB,CAAC,SAAU;EAEjC,MAAM,aAAa;AACnB,oBAAkB;AAClB,eAAa;AACb,kBAAgB;AAChB,aAAW;AACX,gBAAc;AAGd,MAAI,WAAY,YAAW,YAAY,MAAM;AAE7C,UAAQ;AAGR,kBAAgB,YAAY,MAAM,KAAK;;;;;;CASzC,SAAS,eAAe,IAAY,SAAsB,gBAAsB;AAC9E,MAAI,aAAa,MAAM,CAAC,eAAe;AACrC,OAAI,gBAAgB,QAAQ;AAC1B,kBAAc;AACd,YAAQ;;AAEV;;EAEF,MAAM,aAAa;AACnB,oBAAkB;AAClB,eAAa;AACb,kBAAgB;AAChB,aAAW;AACX,gBAAc;AAEd,MAAI,WAAY,YAAW,YAAY,MAAM;AAE7C,UAAQ;AAER,kBAAgB,YAAY,MAAM,OAAO;;CAG3C,SAAS,wBAAwB,IAAkB;EACjD,MAAM,MAAM,eAAe,WAAW,MAAM,EAAE,OAAO,GAAG;AACxD,MAAI,QAAQ,GAAI;AAChB,iBAAe,OAAO,KAAK,EAAE;AAE7B,MAAI,aAAa,MAAM,CAAC,eAAe;AACrC,gBAAa;AACb,cAAW;AACX,iBAAc;AACd,WAAQ;AACR,mBAAgB,MAAM,MAAM,KAAK;AACjC;;AAEF,UAAQ;;CAGV,SAAS,sBAAsB,IAAY,OAA6B,EAAE,EAAc;EACtF,MAAM,EAAE,WAAW,MAAM,YAAY,UAAU;EAG/C,MAAM,WAAW,eAAe,WAAW,MAAM,EAAE,OAAO,GAAG;AAC7D,MAAI,aAAa,IAAI;AAEnB,OAAI,eAAe,UAAW,aAAa,YAAY,CAAC,UACtD,cAAa,wBAAwB,GAAG;AAE1C,kBAAe,YAAY;IAAE;IAAI;IAAU;QAE3C,gBAAe,KAAK;GAAE;GAAI;GAAU,CAAC;AAGvC,MAAI,aAAa,YAAY,aAAa,KACxC,gBAAe,IAAI,eAAe;MAElC,SAAQ;AAGV,eAAa,wBAAwB,GAAG;;CAG1C,SAAS,uBAAuB,IAAY,UAAyB;EACnE,MAAM,QAAQ,eAAe,MAAM,MAAM,EAAE,OAAO,GAAG;AACrD,MAAI,CAAC,MAAO;AACZ,MAAI,MAAM,aAAa,SAAU;AACjC,QAAM,WAAW;AAEjB,MAAI,CAAC,YAAY,aAAa,MAAM,CAAC,eAAe;AAClD,gBAAa;AACb,cAAW;AACX,iBAAc;AACd,WAAQ;AACR,mBAAgB,MAAM,MAAM,KAAK;AACjC;;AAEF,UAAQ;;CAGV,SAAS,oBAAoB,SAAwB;AACnD,MAAI,qBAAqB,QAAS;AAClC,qBAAmB;AACnB,UAAQ;;;;;CAQV,SAAS,gBAAgB,aAAqB,QAAyB;AACrE,MAAI,gBAAgB,OAAQ,QAAO;AACnC,OAAK,MAAM,SAAS,YAAY,SAC9B,KAAI,gBAAgB,OAAO,OAAO,CAAE,QAAO;AAE7C,SAAO;;;;;;;CAQT,SAAS,qBAAqB,aAA2B;EACvD,IAAI,UAAU;AAEd,MAAI,iBAAiB,gBAAgB,aAAa,cAAc,EAAE;GAChE,MAAM,aAAa;AAEnB,cAAW,YAAY,MAAM;AAC7B,qBAAkB;AAClB,gBAAa;AACb,mBAAgB;AAChB,cAAW;AACX,iBAAc;AACd,aAAU;AACV,mBAAgB,YAAY,MAAM,KAAK;;AAGzC,MAAI,mBAAmB,gBAAgB,aAAa,gBAAgB,EAAE;AACpE,qBAAkB;AAClB,gBAAa;AACb,aAAU;;AAGZ,MAAI,QACF,SAAQ;;CAMZ,SAAS,WAAW,SAAuB;AACzC,aAAW,KAAK,QAAQ;AACxB,UAAQ;;CAGV,SAAS,YAAkB;AAEzB,MADe,WAAW,KAAK,KAChB,KAAA,EAAW;AAI1B,UAAQ;;CAKV,SAAS,cAAc,SAAiB,MAAoB;AAE1D,MAAI,iBAAiB,SACnB,aAAY,iBAAiB;AAI/B,kBAAgB;EAIhB,MAAM,cAAc;EACpB,MAAM,aAAa,YAAY;AAC/B,MAAI,WACF,WAAU,YAAY,MAAM,eAAe;OACtC;GACL,MAAM,YAAY,aAAa,MAAM,QAAQ;AAC7C,OAAI,WAAW;IACb,MAAM,QAAQ,YAAY,MAAM,UAAU;AAC1C,QAAI,MAAM,SAAS,EACjB,OAAM,MAAM,IAAK,eAAe;;;AAMtC,MAAI,gBAAgB,YAClB,SAAQ;;CAMZ,SAAS,aAAa,MAAwB;AAC5C,MAAI,CAAC,cAAe,QAAO,EAAE;EAE7B,MAAM,OAAiB,EAAE;EACzB,IAAI,UAAyB;AAC7B,SAAO,WAAW,YAAY,KAAK,QAAQ;GACzC,MAAM,KAAK,UAAU,QAAQ;AAC7B,OAAI,GAAI,MAAK,KAAK,GAAG;AACrB,aAAU,QAAQ;;AAEpB,SAAO;;CAGT,SAAS,eAAe,MAAc,QAAyB;AAC7D,MAAI,CAAC,cAAe,QAAO;EAG3B,MAAM,SAAS,aAAa,MAAM,OAAO;AACzC,MAAI,CAAC,OAAQ,QAAO;EAGpB,IAAI,UAAyB;AAC7B,SAAO,SAAS;AACd,OAAI,YAAY,OAAQ,QAAO;AAC/B,aAAU,QAAQ;;AAEpB,SAAO;;;;;;;CAUT,SAAS,aAAa,MAAc,eAA4C;AAC9E,MAAI,cAAe,QAAO;AAE1B,MAAI,WAAW,SAAS,GAAG;GACzB,MAAM,UAAU,WAAW,WAAW,SAAS;GAC/C,MAAM,YAAY,aAAa,MAAM,QAAQ;AAC7C,OAAI,UAAW,QAAO;;;CAa1B,SAAS,gBAAgB,MAAc,OAA4B;EACjE,MAAM,iBAAiB,aAAa,MAAM,MAAM;EAChD,MAAM,QAAQ,YAAY,MAAM,eAAe;EAC/C,MAAM,UAAsB,MAAM,KAAK,UAAU;GAAE,MAAM;GAAQ;GAAM,EAAE;AAKzE,MAAI,CAAC,kBAAkB,kBAAkB;GACvC,MAAM,UAAU,IAAI,IAClB,MAAM,KAAK,MAAO,EAAE,MAAkC,OAA6B,CAAC,OAAO,QAAQ,CACpG;AACD,QAAK,MAAM,SAAS,eAClB,KAAI,MAAM,YAAY,CAAC,QAAQ,IAAI,MAAM,GAAG,CAAE,SAAQ,KAAK;IAAE,MAAM;IAAQ,IAAI,MAAM;IAAI,CAAC;;AAG9F,SAAO;;CAGT,SAAS,gBAAgB,SAA6B;AACpD,MAAI,eAAe;AACjB,QAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;IACvC,MAAM,IAAI,QAAQ;AAClB,QAAI,EAAE,SAAS,UAAU,EAAE,SAAS,cAAe,QAAO;;AAE5D,UAAO;;AAET,MAAI,SACF,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;GACvC,MAAM,IAAI,QAAQ;AAClB,OAAI,EAAE,SAAS,UAAU,EAAE,OAAO,SAAU,QAAO;;AAGvD,SAAO;;CAGT,SAAS,cAAc,OAAiB,QAA2B;AACjE,MAAI,MAAM,SAAS,OACjB,OAAM,MAAM,MAAM,OAAO;MAEzB,gBAAe,MAAM,IAAI,OAAO;;CAIpC,SAAS,UAAU,MAAc,OAAsB;EACrD,MAAM,UAAU,gBAAgB,MAAM,MAAM;AAC5C,MAAI,QAAQ,WAAW,EAAG;EAE1B,MAAM,eAAe,gBAAgB,QAAQ;AAC7C,MAAI,iBAAiB,IAAI;AAEvB,iBAAc,QAAQ,IAAK,WAAW;AACtC;;AAIF,gBAAc,SADK,eAAe,KAAK,QAAQ,SACZ,WAAW;;CAGhD,SAAS,UAAU,MAAc,OAAsB;EACrD,MAAM,UAAU,gBAAgB,MAAM,MAAM;AAC5C,MAAI,QAAQ,WAAW,EAAG;EAE1B,MAAM,eAAe,gBAAgB,QAAQ;AAC7C,MAAI,iBAAiB,IAAI;AAEvB,iBAAc,QAAQ,QAAQ,SAAS,IAAK,WAAW;AACvD;;AAIF,gBAAc,QADI,gBAAgB,IAAI,QAAQ,SAAS,IAAI,eAAe,IACvC,WAAW;;CAGhD,SAAS,eACP,MACA,WACA,UACM;AACN,MAAI,CAAC,cAAe;EAGpB,MAAM,iBAAiB,qBAAqB,eAAe,UAAU;AACrE,MAAI,gBAAgB;AAClB,aAAU,gBAAgB,MAAM,WAAW;AAC3C;;EAIF,MAAM,aAAa,YAAY,KAAK;EAEpC,MAAM,SAAS,kBAAkB,eAAe,WAAW,YADlC,cAAc,SAAiB,KAAK,YAC2B;AACxF,MAAI,OACF,OAAM,QAAQ,WAAW;;CAM7B,SAAS,UAAU,UAAkC;AACnD,YAAU,IAAI,SAAS;AACvB,eAAa;AACX,aAAU,OAAO,SAAS;;;CAI9B,SAAS,cAA6B;AACpC,MAAI,CAAC,SACH,YAAW;GACT;GACA;GACA;GACA,YAAY,CAAC,GAAG,WAAW;GAC3B;GACD;AAEH,SAAO;;AAKT,QAAO;EACL,IAAI,gBAAgB;AAClB,UAAO;;EAET,IAAI,WAAW;AACb,UAAO;;EAET,IAAI,kBAAkB;AACpB,UAAO;;EAET,IAAI,aAAa;AACf,UAAO;;EAET,IAAI,cAAc;AAChB,UAAO;;EAET,IAAI,aAAa;AACf,UAAO,CAAC,GAAG,WAAW;;EAExB,IAAI,cAAc;AAChB,UAAO;;EAET,IAAI,gBAAgB;AAClB,UAAO;;EAGT;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EAEA;EACA;EAGA;EACA;EACA,IAAI,oBAAoB;AACtB,UAAO,eAAe,SAAS;;EAEjC,IAAI,mBAAmB;AACrB,UAAO;;EAET;EACD;;;;;;;ACpsBH,SAAgB,gBAAgB,MAAwB;CACtD,MAAM,OAAiB,EAAE;CACzB,IAAI,UAAyB;AAC7B,QAAO,SAAS;AACd,OAAK,KAAK,QAAQ;AAClB,YAAU,QAAQ;;AAEpB,QAAO;;;;;AAMT,SAAgB,YAAY,GAAW,GAAW,MAAqB;AACrE,QAAO,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK,SAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK;;;;;;;ACkFpF,SAAgB,eAAe,OAAe,KAAU,QAAiC;CACvF,IAAI,qBAAqB;CACzB,IAAI,mBAAmB;AAEvB,QAAO;EACL,KAAK;EACL;EACA,MAAM,IAAI;EACV,MAAM,IAAI;EACV,OAAO,IAAI;EACX,OAAO,IAAI;EACX,OAAO,IAAI;EACX,WAAW,IAAI;EACf;EACA,eAAe;EACf,aAAa;GAAE;GAAO;GAAK;EAC3B,IAAI,qBAAqB;AACvB,UAAO;;EAET,IAAI,mBAAmB;AACrB,UAAO;;EAET,kBAAkB;AAChB,wBAAqB;;EAEvB,iBAAiB;AACf,sBAAmB;;EAEtB;;;;;AAMH,SAAgB,iBACd,MACA,QACA,eACmB;CACnB,IAAI,qBAAqB;AAEzB,QAAO;EACL;EACA;EACA;EACA,eAAe;EACf,IAAI,qBAAqB;AACvB,UAAO;;EAET,kBAAkB;AAChB,wBAAqB;;EAExB;;;;;;;;;;;;;;;;;;AA2BH,SAAgB,iBAAiB,OAAwB,UAAyC;CAChG,MAAM,OAAO,gBAAgB,MAAM,OAAO;CAC1C,MAAM,eAAe;CAGrB,MAAM,YAAY,MAAM,cAAc;CACtC,MAAM,cAAc,YAAY,YAAY;AAG5C,KAAI,CAAC,UACH,MAAK,IAAI,IAAI,KAAK,SAAS,GAAG,IAAI,GAAG,KAAK;AACxC,MAAI,MAAM,mBAAoB;EAC9B,MAAM,OAAO,KAAK;EAClB,MAAM,UAAW,KAAK,MAAkC;AAGxD,MAAI,SAAS;AACX,gBAAa,gBAAgB;AAC7B,WAAQ,MAAM;;;AAMpB,KAAI,CAAC,MAAM,oBAAoB;EAC7B,MAAM,SAAS,KAAK;AACpB,eAAa,gBAAgB;EAC7B,MAAM,UAAW,OAAO,MAAkC;AAG1D,MAAI,QACF,SAAQ,OAAO,SAAS;;AAK5B,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,MAAI,MAAM,mBAAoB;EAC9B,MAAM,OAAO,KAAK;EAClB,MAAM,UAAW,KAAK,MAAkC;AAGxD,MAAI,SAAS;AACX,gBAAa,gBAAgB;AAC7B,WAAQ,OAAO,SAAS;;;;;;;;;AAU9B,SAAgB,mBAAmB,OAAgC;CACjE,MAAM,cAAc,MAAM,SAAS,UAAU,YAAY;CACzD,MAAM,OAAO,gBAAgB,MAAM,OAAO;CAC1C,MAAM,eAAe;AAErB,MAAK,MAAM,QAAQ,MAAM;AACvB,MAAI,MAAM,mBAAoB;EAE9B,MAAM,UAAW,KAAK,MAAkC;AACxD,MAAI,SAAS;AACX,gBAAa,gBAAgB;AAC7B,WAAQ,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzKpB,IAAa,cAAb,MAAyB;CACvB,0BAAkB,IAAI,KAAwB;;;;;;;CAQ9C,SAAS,IAAY,QAAyB;AAC5C,OAAK,QAAQ,IAAI,IAAI,OAAO;;;;;;;CAQ9B,WAAW,IAAkB;AAC3B,OAAK,QAAQ,OAAO,GAAG;;;;;;CAOzB,QAAc;AACZ,OAAK,QAAQ,OAAO;;;;;;CAOtB,IAAI,OAAe;AACjB,SAAO,KAAK,QAAQ;;;;;;;;;CAUtB,QAAQ,SAAiB,SAAmC;EAC1D,IAAI,YAA8B;AAElC,OAAK,MAAM,UAAU,KAAK,QAAQ,QAAQ,CAExC,KACE,WAAW,OAAO,KAClB,UAAU,OAAO,IAAI,OAAO,SAC5B,WAAW,OAAO,KAClB,UAAU,OAAO,IAAI,OAAO;OAGxB,CAAC,aAAa,OAAO,SAAS,UAAU,OAC1C,aAAY;;AAKlB,SAAO,WAAW,UAAU;;;;;;;;;;CAW9B,WAAW,SAAiB,SAA8B;EACxD,MAAM,UAAuB,EAAE;AAE/B,OAAK,MAAM,UAAU,KAAK,QAAQ,QAAQ,CACxC,KACE,WAAW,OAAO,KAClB,UAAU,OAAO,IAAI,OAAO,SAC5B,WAAW,OAAO,KAClB,UAAU,OAAO,IAAI,OAAO,OAE5B,SAAQ,KAAK,OAAO;AAKxB,SAAO,QAAQ,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,OAAO;;;;;CAMpD,gBAAwC;AACtC,SAAO,IAAI,IAAI,KAAK,QAAQ;;;;;;AAWhC,IAAI,qBAAqB;AACzB,SAAgB,sBAA8B;AAC5C,QAAO,OAAO,EAAE;;;;;AAMlB,SAAgB,0BAAgC;AAC9C,sBAAqB;;;;;AAUvB,MAAa,UAAU;CAErB,YAAY;CAEZ,eAAe;CAEf,MAAM;CAEN,aAAa;CAEb,MAAM;CAEN,UAAU;CAEV,QAAQ;CAER,UAAU;CAEV,SAAS;CACV;;;;;;ACtID,SAAgB,mBAAmB,MAAc,GAAW,GAA0B;CACpF,IAAI,SAAwB;CAE5B,SAAS,MAAM,MAAoB;EACjC,MAAM,OAAO,KAAK;AAClB,MAAI,CAAC,KAAM;AAGX,MAAI,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK,SAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK,QAAQ;AACrF,YAAS;AAGT,QAAK,MAAM,SAAS,KAAK,SACvB,OAAM,MAAM;;;AAKlB,OAAM,KAAK;AACX,QAAO;;;;;;AAOT,SAAgB,uBAAuB,MAAc,GAAW,GAAqB;CACnF,MAAM,SAAmB,EAAE;CAE3B,SAAS,MAAM,MAAoB;EACjC,MAAM,OAAO,KAAK;AAClB,MAAI,CAAC,KAAM;AAEX,MAAI,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK,SAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK,QAAQ;AACrF,UAAO,KAAK,KAAK;AACjB,QAAK,MAAM,SAAS,KAAK,SACvB,OAAM,MAAM;;;AAKlB,OAAM,KAAK;AACX,QAAO;;;;;AAMT,SAAS,YAAY,MAAsB;CACzC,MAAM,QAAkB,EAAE;CAC1B,IAAI,UAAyB;AAE7B,QAAO,SAAS;EACd,MAAM,QAAQ,QAAQ;AACtB,MAAI,MAAM,GACR,OAAM,QAAQ,IAAI,MAAM,KAAK;WACpB,QAAQ,QAAQ;GACzB,MAAM,MAAM,QAAQ,OAAO,SAAS,QAAQ,QAAQ;AACpD,SAAM,QAAQ,IAAI,IAAI,GAAG;QAEzB,OAAM,QAAQ,OAAO;AAEvB,YAAU,QAAQ;;AAGpB,QAAO,MAAM,KAAK,MAAM;;;;;AAM1B,SAAS,YAAY,GAAgB,GAAyB;AAC5D,KAAI,MAAM,EAAG,QAAO;AACpB,KAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AACrB,QAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE;;;;;AAM7E,SAAgB,iBAAiB,MAA6B;CAC5D,MAAM,QAAQ,KAAK;CAGnB,IAAI,aAA4B;AAChC,KAAI,KAAK,OACP,cAAa,KAAK,OAAO,SAAS,QAAQ,KAAK;AAGjD,QAAO;EACL,IAAI,MAAM;EACV,MAAM,KAAK;EACX,MAAM,YAAY,KAAK;EACvB;EACA,YAAY;GACV,cAAc,QAAQ,KAAK,WAAW,KAAK,YAAA,EAAwB;GACnE,iBAAiB,QAAQ,KAAK,WAAW,KAAK,YAAA,EAA4B;GAC1E,cAAc,QAAQ,KAAK,WAAW,KAAK,YAAA,GAAwB;GACnE,eAAe,QAAQ,KAAK,WAAW,KAAK,YAAA,EAAyB;GACrE,aAAa,KAAK;GACnB;EACD,QAAQ;GACN,YAAY,KAAK;GACjB,SAAS,KAAK;GACd,YAAY,KAAK;GACjB,eAAe,YAAY,KAAK,YAAY,KAAK,QAAQ;GAC1D;EACD,QAAQ,KAAK,cACT;GACE,QAAQ,KAAK,YAAY;GACzB,YAAY,KAAK,YAAY;GAC7B,eAAe,KAAK,YAAY,WAAW,KAAK,YAAY;GAC5D,eAAe,KAAK,YAAY;GAChC,gBAAgB,KAAK,YAAY;GACjC,aAAa,KAAK,YAAY;GAC9B,aAAa,KAAK,YAAY;GAC9B,mBAAmB,KAAK,YAAY;GACpC,kBAAkB,KAAK,YAAY;GACpC,GACD,KAAA;EACJ,iBAAiB,MAAM;EACvB,YAAY,KAAK,SAAS;EAC1B,QAAQ,KAAK,UAAU;EACxB;;;;;AAMH,SAAS,oBAAoB,MAAwB;CACnD,MAAM,SAAmB,EAAE;CAC3B,IAAI,UAAU,KAAK;AAEnB,QAAO,SAAS;AACd,MAAI,QAAQ,YACV,QAAO,KAAK,QAAQ;AAEtB,YAAU,QAAQ;;AAGpB,QAAO;;;;;AAMT,SAAS,gBAAgB,MAAqB,iBAAqC;CACjF,MAAM,WAAqB,EAAE;AAE7B,KAAI,CAAC,MAAM;AACT,WAAS,KAAK,uEAAuE;AACrF,SAAO;;CAGT,MAAM,QAAQ;AAGd,KAFiB,CAAC,WAAW,MAAM,WAAW,MAAM,WAAW,IAAI,CAAC,MAAM,YAGxE,UAAS,KAAK,+DAA+D;CAI/E,MAAM,eAAe,gBAAgB;AACrC,KAAI,cAAc,aAAa;EAC7B,MAAM,KAAK,aAAa;EACxB,MAAM,aAAa,KAAK,SAAS,KAAK,OAAO,SAAS,QAAQ,KAAK,GAAG;EAGtE,MAAM,iBAAiB,cAAc,GAAG,qBAAqB,cAAc,GAAG;AAC9E,MAAI,CAAC,kBAAkB,cAAc,GAAG;AACtC,YAAS,KACP,gBAAgB,WAAW,6BAA6B,GAAG,kBAAkB,IAAI,GAAG,iBAAiB,GACtG;AACD,YAAS,KAAK,4EAA4E;aACjF,eACT,UAAS,KAAK,gBAAgB,WAAW,wBAAwB,GAAG,kBAAkB,IAAI,GAAG,iBAAiB,GAAG;AAInH,MAAI,GAAG,WAAW,GAAG,WACnB,UAAS,KAAK,6DAA6D;MAE3E,UAAS,KAAK,4BAA4B,GAAG,WAAW,KAAK,GAAG,SAAS;AAI3E,MAAI,GAAG,sBAAsB,KAAK,GAAG,qBAAqB,aAAa,SAAS,SAAS,GAAG;AAC1F,YAAS,KACP,gCAAgC,GAAG,kBAAkB,IAAI,GAAG,iBAAiB,OAAO,aAAa,SAAS,OAAO,WAClH;AACD,YAAS,KAAK,sEAAsE;;;AAMxF,KAAI,CADkB,YAAY,KAAK,YAAY,KAAK,QAAQ,IAC1C,KAAK,WACzB,UAAS,KAAK,kDAAkD;UACvD,CAAC,KAAK,WACf,UAAS,KAAK,kEAAkE;KAEhF,UAAS,KAAK,gEAAgE;AAIhF,KAAI,KAAK,UAAU,KAAK,OAAO,SAAS,SAAS,GAAG;EAClD,IAAI,eAAe;AACnB,OAAK,MAAM,WAAW,KAAK,OAAO,SAChC,KAAI,YAAY,QAAQ,QAAQ,WAAW,QAAQ;OAC7C,QAAQ,QAAQ,MAAM,QAAQ,WAAW,KAAK,QAAQ,QAAQ,MAAM,QAAQ,WAAW,GAAG;AAC5F,mBAAe;AACf;;;AAIN,MAAI,aACF,UAAS,KAAK,gEAAgE;;AAKlF,KAAI,KAAK,OACP,UAAS,KAAK,uDAAuD;AAGvE,QAAO;;;;;AAMT,SAAgB,qBACd,MACA,GACA,GACA,iBACA,WACA,WACsB;CACtB,MAAM,YAAY,mBAAmB,MAAM,GAAG,EAAE;CAChD,MAAM,aAAa,uBAAuB,MAAM,GAAG,EAAE;CACrD,MAAM,sBAAsB,YAAY,oBAAoB,UAAU,GAAG,EAAE;AAE3E,QAAO;EACL,UAAU;GAAE;GAAG;GAAG;EAClB,OAAO;GACL,aAAa;GACb,OAAO;GACR;EACD;EACA,MAAM,YAAY,iBAAiB,UAAU,GAAG;EAChD,iBAAiB,oBAAoB,IAAI,iBAAiB;EAC1D,iBAAiB,WAAW,IAAI,iBAAiB;EACjD,kBAAkB,gBAAgB,WAAW,oBAAoB;EAClE;;;;;;;;AASH,SAAgB,sBAAsB,KAA2B,kBAA6C;CAC5G,MAAM,QAAkB,EAAE;AAG1B,OAAM,KAAK,gCAAgC,IAAI,SAAS,EAAE,IAAI,IAAI,SAAS,EAAE,eAAe,IAAI,YAAY;AAC5G,OAAM,KAAK,GAAG;CAGd,MAAM,EAAE,aAAa,UAAU,IAAI;AACnC,OAAM,KAAK,eAAe;AAC1B,OAAM,KACJ,uBAAuB,KAAK,UAAU,YAAY,KAAK,CAAC,MAAM,KAAK,UAAU,YAAY,GAAG,CAAC,MAAM,KAAK,UAAU,YAAY,GAAG,CAAC,SAAS,KAAK,UAAU,YAAY,MAAM,GAC7K;AACD,OAAM,KACJ,uBAAuB,KAAK,UAAU,MAAM,KAAK,CAAC,MAAM,KAAK,UAAU,MAAM,GAAG,CAAC,MAAM,KAAK,UAAU,MAAM,GAAG,CAAC,SAAS,KAAK,UAAU,MAAM,MAAM,GACrJ;AACD,OAAM,KAAK,GAAG;AAGd,KAAI,IAAI,MAAM;AACZ,QAAM,KAAK,kBAAkB;AAC7B,QAAM,KAAK,WAAW,IAAI,KAAK,OAAO;AACtC,QAAM,KAAK,WAAW,IAAI,KAAK,OAAO;AACtC,MAAI,IAAI,KAAK,gBACX,OAAM,KAAK,sBAAsB,IAAI,KAAK,kBAAkB;AAE9D,QAAM,KAAK,GAAG;EAGd,MAAM,QAAQ,IAAI,KAAK;EACvB,MAAM,cAAc,OAAO,QAAQ,MAAM,CACtC,QAAQ,GAAG,OAAO,EAAE,CACpB,KAAK,CAAC,OAAO,EAAE;AAClB,QAAM,KAAK,eAAe;AAC1B,MAAI,YAAY,SAAS,EACvB,OAAM,KAAK,aAAa,YAAY,KAAK,KAAK,GAAG;MAEjD,OAAM,KAAK,oCAAoC;AAEjD,QAAM,KACJ,uBAAuB,MAAM,aAAa,mBAAmB,MAAM,gBAAgB,gBAAgB,MAAM,aAAa,iBAAiB,MAAM,cAAc,eAAe,MAAM,cACjL;AACD,QAAM,KAAK,GAAG;EAGd,MAAM,EAAE,WAAW,IAAI;AACvB,QAAM,KAAK,UAAU;AACrB,MAAI,OAAO,eAAe;AACxB,SAAM,KAAK,sBAAsB;AACjC,SAAM,KAAK,mBAAmB,WAAW,OAAO,WAAW,GAAG;AAC9D,SAAM,KAAK,gBAAgB,WAAW,OAAO,QAAQ,GAAG;QAExD,OAAM,KAAK,cAAc,WAAW,OAAO,QAAQ,GAAG;AAExD,QAAM,KAAK,iBAAiB,WAAW,OAAO,WAAW,GAAG;AAC5D,QAAM,KAAK,GAAG;AAGd,MAAI,IAAI,KAAK,QAAQ;AACnB,SAAM,KAAK,4BAA4B;AACvC,qBAAkB,OAAO,IAAI,KAAK,OAAO;AACzC,SAAM,KAAK,GAAG;;QAEX;AACL,QAAM,KAAK,gDAAgD;AAC3D,QAAM,KAAK,GAAG;;AAIhB,KAAI,IAAI,gBAAgB,SAAS,GAAG;AAClC,QAAM,KAAK,oBAAoB;AAC/B,OAAK,MAAM,YAAY,IAAI,iBAAiB;AAC1C,SAAM,KAAK,KAAK,SAAS,KAAK,GAAG;AACjC,OAAI,SAAS,OACX,mBAAkB,OAAO,SAAS,QAAQ,OAAO;;AAGrD,QAAM,KAAK,GAAG;;AAIhB,KAAI,IAAI,gBAAgB,SAAS,GAAG;AAClC,QAAM,KAAK,iDAAiD;AAC5D,OAAK,MAAM,QAAQ,IAAI,iBAAiB;GACtC,MAAM,QAAQ,OAAO,QAAQ,KAAK,WAAW,CAC1C,QAAQ,GAAG,OAAO,EAAE,CACpB,KAAK,CAAC,OAAO,EAAE,QAAQ,SAAS,GAAG,CAAC,CACpC,KAAK,IAAI;GACZ,MAAM,UAAU,QAAQ,KAAK,MAAM,KAAK;GACxC,MAAM,QAAQ,KAAK,kBAAkB,OAAO,KAAK,oBAAoB;GACrE,MAAM,WAAW,KAAK,eAAe,OAAO,UAAU,KAAK,WAAW,KAAK;AAC3E,SAAM,KAAK,KAAK,KAAK,OAAO,UAAU,QAAQ,WAAW;;AAE3D,QAAM,KAAK,GAAG;;AAIhB,KAAI,IAAI,iBAAiB,SAAS,GAAG;AACnC,QAAM,KAAK,sBAAsB;AACjC,OAAK,MAAM,QAAQ,IAAI,iBACrB,OAAM,KAAK,KAAK,OAAO;AAEzB,QAAM,KAAK,GAAG;;AAIhB,KAAI,kBAAkB;EACpB,MAAM,IAAI;AACV,QAAM,KAAK,sBAAsB;AACjC,QAAM,KAAK,mBAAmB,EAAE,aAAa,mBAAmB,EAAE,cAAc,kBAAkB,EAAE,eAAe;AACnH,QAAM,KAAK,gBAAgB,EAAE,UAAU,cAAc,EAAE,SAAS,cAAc,EAAE,WAAW;EAE3F,MAAM,YAAsB,EAAE;AAC9B,MAAI,EAAE,aAAc,WAAU,KAAK,gBAAgB,EAAE,eAAe;AACpE,MAAI,EAAE,iBAAkB,WAAU,KAAK,gBAAgB,EAAE,mBAAmB;AAC5E,MAAI,EAAE,oBAAqB,WAAU,KAAK,mBAAmB,EAAE,sBAAsB;AACrF,MAAI,EAAE,kBAAmB,WAAU,KAAK,iBAAiB,EAAE,oBAAoB;AAC/E,MAAI,EAAE,iBAAkB,WAAU,KAAK,gBAAgB,EAAE,mBAAmB;AAC5E,MAAI,EAAE,kBAAmB,WAAU,KAAK,iBAAiB,EAAE,oBAAoB;AAC/E,MAAI,EAAE,yBAA0B,WAAU,KAAK,wBAAwB,EAAE,2BAA2B;AACpG,MAAI,UAAU,SAAS,EACrB,OAAM,KAAK,qBAAqB,UAAU,KAAK,KAAK,GAAG;AAGzD,MAAI,EAAE,uBAAuB,GAAG;AAC9B,SAAM,KAAK,uBAAuB,EAAE,qBAAqB,qBAAqB,EAAE,wBAAwB;AACxG,OAAI,EAAE,kBAAmB,OAAM,KAAK,wBAAwB,EAAE,oBAAoB;;AAGpF,MAAI,EAAE,wBAAwB,GAAG;AAC/B,SAAM,KAAK,4BAA4B,EAAE,wBAAwB;AACjE,OAAI,EAAE,oBAAqB,OAAM,KAAK,0BAA0B,EAAE,sBAAsB;;AAG1F,MAAI,EAAE,kBAAkB,KAAK;AAC3B,SAAM,KAAK,sBAAsB,EAAE,kBAAkB;AACrD,OAAI,EAAE,aAAc,OAAM,KAAK,mBAAmB,EAAE,eAAe;;AAErE,QAAM,KAAK,GAAG;;AAGhB,QAAO,MAAM,KAAK,KAAK;;AAGzB,SAAS,WAAW,MAA2B;AAC7C,KAAI,CAAC,KAAM,QAAO;AAClB,QAAO,MAAM,KAAK,EAAE,MAAM,KAAK,EAAE,MAAM,KAAK,MAAM,MAAM,KAAK,OAAO;;AAGtE,SAAS,kBAAkB,OAAiB,QAA8C,SAAS,MAAY;AAC7G,KAAI,OAAO,cACT,OAAM,KAAK,GAAG,OAAO,2BAA2B,OAAO,WAAW,KAAK,OAAO,SAAS;KAEvF,OAAM,KAAK,GAAG,OAAO,UAAU,OAAO,SAAS;AAEjD,OAAM,KACJ,GAAG,OAAO,YAAY,OAAO,eAAe,GAAG,OAAO,cAAc,aAAa,OAAO,YAAY,IAAI,OAAO,YAAY,GAC5H;AACD,OAAM,KAAK,GAAG,OAAO,iBAAiB,OAAO,kBAAkB,IAAI,OAAO,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACnchG,SAAgB,MAAM,SAA6B,QAAQ,QAAiB;AAE1E,KAAI,CAAC,OAAO,MACV,QAAO;AAIT,KAAI,QAAQ,IAAI,SAAS,OACvB,QAAO;AAIT,KACE,QAAQ,IAAI,MACZ,QAAQ,IAAI,kBACZ,QAAQ,IAAI,aACZ,QAAQ,IAAI,eACZ,QAAQ,IAAI,aACZ,QAAQ,IAAI,YACZ,QAAQ,IAAI,OAEZ,QAAO;AAGT,QAAO;;;;;;;;;AAUT,SAAgB,kBAAkB,UAAyB,EAAE,EAAsB;CACjF,MAAM,EAAE,OAAO,QAAQ,SAAS,QAAQ,WAAW;AAEnD,KAAI,SAAS,OACX,QAAO;AAIT,QAAO,MAAM,OAAO,GAAG,QAAQ;;;;;;;;;;;;AAoBjC,SAAgB,mBAAmB,SAAiB,eAA+B;CACjF,MAAM,QAAQ,QAAQ,MAAM,KAAK;CACjC,IAAI,SAAS;AAGb,KAAI,gBAAgB,GAAG;AAErB,YAAU;AAEV,MAAI,gBAAgB,EAClB,WAAU,QAAQ,gBAAgB,EAAE;;AAKxC,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,MAAI,IAAI,EACN,WAAU;AAEZ,YAAU,MAAM;AAEhB,YAAU;;CAIZ,MAAM,aAAa,gBAAgB,MAAM;AACzC,KAAI,aAAa,GAAG;AAClB,OAAK,IAAI,IAAI,GAAG,IAAI,YAAY,IAC9B,WAAU;AAGZ,YAAU,QAAQ,WAAW;;AAG/B,QAAO;;;;;;;;;;;;AAaT,SAAgB,cAAc,SAAiB,gBAAgC;CAK7E,MAAM,QAHQ,UAAU,QAAQ,CAGZ,MAAM,KAAK,CAAC,KAAK,SAAS,KAAK,SAAS,CAAC;AAG7D,QAAO,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,OAAO,GACrD,OAAM,KAAK;AAGb,QAAO,MAAM,KAAK,KAAK;;;;;;;;AAazB,SAAgB,wBAAwB,MAA8E;AACpH,SAAQ,MAAR;EACE,KAAK,MAEH,SAAQ,YAAY;EAEtB,KAAK,eACH,QAAO;EAET,KAAK,SAGH,cAAa;EAEf,KAAK,QACH,QAAO;;;;;;AAOb,SAAgB,WAAW,KAAqB;AAC9C,KAAI,CAAC,IAAK,QAAO;AACjB,QAAO,IAAI,MAAM,KAAK,CAAC;;;;ACxIzB,MAAM,MAAM;AACZ,MAAM,MAAM;;AAGZ,MAAM,eAAe,GAAG,IAAI;;;;;;;AA0I5B,SAAgB,gBAAgB,QAA4B,MAAoB;CAC9E,MAAM,SAAS,OAAO,KAAK,KAAK,CAAC,SAAS,SAAS;AACnD,QAAO,MAAM,GAAG,IAAI,QAAQ,SAAS,MAAM;;;;;;;;;;AAW7C,SAAgB,iBAAiB,QAAkC;AACjE,QAAO,MAAM,GAAG,IAAI,SAAS,MAAM;;;;;;;;;;AAerC,SAAgB,uBAAuB,OAA8B;CACnE,MAAM,YAAY,MAAM,QAAQ,aAAa;AAC7C,KAAI,cAAc,GAAI,QAAO;CAE7B,MAAM,eAAe,YAAY,aAAa;AAG9C,KAAI,MAAM,kBAAkB,IAAK,QAAO;CAGxC,IAAI,aAAa,MAAM,QAAQ,KAAK,aAAa;AACjD,KAAI,eAAe,GACjB,cAAa,MAAM,QAAQ,GAAG,IAAI,KAAK,aAAa;AAEtD,KAAI,eAAe,GAAI,QAAO;CAE9B,MAAM,SAAS,MAAM,MAAM,cAAc,WAAW;AACpD,QAAO,OAAO,KAAK,QAAQ,SAAS,CAAC,SAAS,QAAQ;;;;;;;;;;;;;;;;;aCjQgB;AAgBxE,MAAMC,QAAM,aAAa,oBAAoB;;;;;;;;;;;;;AAc7C,MAAM,sBAAsB,QAAQ,IAAI,wBAAwB,OAAO,QAAQ,IAAI,wBAAwB;;;;;;;;;;;;;;;;;;;;;;AA+F3G,IAAa,kBAAb,MAA6B;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;CAGA,aAA4C;;CAG5C,gBAAwB;;CAGxB,eAAuB;;CAGvB,kBAA0B;;CAG1B,iBAAyB;;CAGzB,eAA6D;;CAG7D,gBAA6C;;CAG7C,QAA6B;EAC3B,aAAa;EACb,cAAc;EACd,gBAAgB;EAChB,eAAe;EAChB;;CAGD,WAAmB;;CAGnB,SAAiB;;CAGjB,qBAA6B;;;;;;CAO7B,mBAA2B;CAE3B,YAAY,SAA2B;AACrC,OAAK,SAAS,QAAQ;AACtB,OAAK,OAAO,QAAQ;AACpB,OAAK,YAAY,QAAQ,SAAS;AAClC,OAAK,eAAe,QAAQ,gBAAgB;AAC5C,OAAK,qBAAqB,QAAQ,sBAAsB;AACxD,OAAK,OAAO,QAAQ,QAAQ;AAC5B,OAAK,iBAAiB,QAAQ;AAC9B,OAAK,iBAAiB,QAAQ,iBAAiB,kBAAkBC;AACjE,OAAK,cAAc,QAAQ,iBAAiB,SAAiB,QAAQ,OAAO,MAAM,KAAK;AACvF,OAAK,MAAM,aAAa,oBAAoB;AAG5C,OAAK,aAAa,kBAAkB;GAClC,MAAM,QAAQ;GACd,QAAQ,KAAK;GACd,CAAC;AACF,OAAK,oBAAoB,wBAAwB,KAAK,WAAW;AAEjE,QAAI,QAAQ,6BAA6B,KAAK,aAAa;AAG3D,MAAI,KAAK,eAAe,MACtB,MAAK,qBAAqB;;;;;CAO9B,gBAA8B;AAC5B,SAAO,KAAK;;;;;;;;CAad,iBAAuB;AACrB,MAAI,KAAK,SAAU;AAEnB,MAAI,KAAK,QAAQ;AACf,QAAK,qBAAqB;AAC1B;;AAGF,MAAI,KAAK,iBAAiB;AACxB,QAAK,MAAM;AACX,SAAI,QAAQ,oCAAoC,KAAK,MAAM,eAAe;AAC1E;;AAGF,OAAK,kBAAkB;AACvB,QAAI,QAAQ,mBAAmB;AAG/B,uBAAqB;AACnB,QAAK,kBAAkB;AAEvB,OAAI,KAAK,SAAU;GAInB,MAAM,sBADM,KAAK,KAAK,GACY,KAAK;AAEvC,OAAI,sBAAsB,KAAK,cAAc;AAE3C,UAAI,QAAQ,yBAAyB,KAAK,eAAe,oBAAoB,IAAI;AACjF,SAAK,kBAAkB,KAAK,eAAe,oBAAoB;SAE/D,MAAK,eAAe;IAEtB;;;;;CAMJ,cAAoB;AAClB,MAAI,KAAK,SAAU;AAEnB,MAAI,KAAK,QAAQ;AACf,QAAK,qBAAqB;AAC1B;;AAIF,OAAK,kBAAkB;AACvB,MAAI,KAAK,cAAc;AACrB,gBAAa,KAAK,aAAa;AAC/B,QAAK,eAAe;;AAGtB,OAAK,eAAe;;;;;CAMtB,WAAwB;AACtB,SAAO,EAAE,GAAG,KAAK,OAAO;;;;;;;;CAS1B,mBAAmB,OAAqB;AACtC,MAAI,KAAK,SAAS,YAAY,SAAS,EAAG;AAC1C,OAAK,oBAAoB;;;;;;;;;;CAW3B,OAAO,SAAiB,MAAiC;AACvD,MAAI,KAAK,SAAU;AAGnB,SADiB,EAAE,QAAQ,SAAiB,KAAK,YAAY,KAAK,EAAE,EAC3C,SAAS,KAAK;;;;;;CAOzC,gBAAgB,MAAoB;AAClC,MAAI,KAAK,SAAU;AAGnB,kBADiB,EAAE,QAAQ,SAAiB,KAAK,YAAY,KAAK,EAAE,EACtC,KAAK;;;;;;;CAQrC,QAAc;AACZ,MAAI,KAAK,YAAY,KAAK,OAAQ;AAClC,OAAK,SAAS;AACd,OAAK,qBAAqB;AAC1B,QAAI,QAAQ,mBAAmB;;;;;;CAOjC,SAAe;AACb,MAAI,KAAK,YAAY,CAAC,KAAK,OAAQ;AACnC,OAAK,SAAS;AACd,QAAI,QAAQ,oBAAoB;AAGhC,OAAK,aAAa;AAGlB,MAAI,KAAK,oBAAoB;AAC3B,QAAK,qBAAqB;AAC1B,QAAK,eAAe;;;;;;CAOxB,WAAoB;AAClB,SAAO,KAAK;;;;;CAMd,QAAc;AACZ,MAAI,KAAK,SAAU;AAGnB,OAAK,YAAY,yBAAyB;AAG1C,OAAK,aAAa;;;;;CAMpB,CAAC,OAAO,WAAiB;AACvB,OAAK,SAAS;;CAGhB,UAAgB;AACd,MAAI,KAAK,SAAU;AAEnB,QAAI,OACF,oBAAoB,KAAK,MAAM,YAAY,YAAY,KAAK,MAAM,aAAa,QAAQ,KAAK,MAAM,KAAK,MAAM,cAAc,CAAC,IAC7H;AACD,OAAK,WAAW;AAGhB,OAAK,kBAAkB;AACvB,MAAI,KAAK,cAAc;AACrB,gBAAa,KAAK,aAAa;AAC/B,QAAK,eAAe;;AAItB,MAAI,KAAK,eAAe;AACtB,QAAK,eAAe;AACpB,QAAK,gBAAgB;;AAIvB,MAAI,KAAK,eAAe,YAAY,KAAK,cAAc;AACrD,QAAK,YAAY,KAAK,aAAa;AACnC,QAAK,YAAY,KAAK;;;;;;;CAQ1B,kBAA0B;AACxB,SAAO,KAAK;;;;;CAUd,kBAA0B,OAAqB;AAC7C,MAAI,KAAK,aAAc;AAEvB,OAAK,eAAe,iBAAiB;AACnC,QAAK,eAAe;AACpB,OAAI,CAAC,KAAK,SACR,MAAK,eAAe;KAErB,MAAM;;;;;CAMX,gBAA8B;;;GAC5B,MAAM,SAAA,YAAA,EAAS,KAAK,IAAI,KAAK,SAAS,CAAA;GACtC,MAAM,YAAY,KAAK,KAAK;AAE5B,OAAI;IAEF,MAAM,QAAQ,KAAK,OAAO,WAAW;IAGrC,MAAM,SAAS,KAAK,SAAS,WAAW,MAAO,KAAK,OAAO,QAAQ;AAEnE,UAAI,QAAQ,WAAW,KAAK,MAAM,cAAc,EAAE,IAAI,MAAM,GAAG,OAAO,eAAe,KAAK,aAAa;IAGvG,MAAM,mBAAmB,KAAK;AAC9B,SAAK,mBAAmB;IAGxB,MAAM,eAAe,KAAK,SAAS,WAAW,KAAK,gBAAgB,GAAG,KAAA;IACtE,MAAM,EAAE,QAAQ,WAAW,cACzB,KAAK,MACL,OACA,QACA,KAAK,YACL;KACE,MAAM,KAAK;KACX;KACA,UAAU,KAAK,SAAS,WAAY,KAAK,OAAO,QAAQ,KAAM,KAAA;KAC9D,WAAW;KACZ,EACD,KAAK,eACN;IAGD,IAAI;AACJ,QAAI,KAAK,eAAe,MAEtB,qBAAoB;aACX,KAAK,eAAe,UAAU;AAEvC,UAAK,eAAe,UAAU,OAAO;AACrC,yBAAoB;WACf;AAEL,yBAAoB,KAAK,kBAAkB,QAAQ,KAAK,cAAc;AACtE,UAAK,gBAAgB,WAAW,OAAO;;IAMzC,IAAI,eAAe;AACnB,QAAI,KAAK,eAAe,OAAO;KAC7B,MAAM,SAAS,KAAK,gBAAgB;AACpC,SAAI,QAAQ,SAAS;MACnB,MAAM,WAAW,OAAO,QAAQ,eAAe,OAAO,MAAM,GAAG,kBAAkB;AACjF,qBAAe,KAAK,WAAW,OAAO,GAAG,OAAO,EAAE,GAAG,WAAW,KAAK;WAErE,gBAAe,KAAK;;AAOxB,QAAI,kBAAkB,SAAS,KAAK,aAAa,SAAS,GAAG;KAC3D,MAAM,aACJ,KAAK,eAAe,SAAS,sBACzB,GAAG,KAAK,aAAa,oBAAoB,eAAe,KAAK,aAC7D,oBAAoB;AAG1B,SAAID,MAAI,OAAO;MACb,MAAM,QAAQ,OAAO,WAAW,WAAW;AAC3C,YAAI,QACF,iBAAiB,MAAM,UAAU,kBAAkB,OAAO,kBAAkB,aAAa,OAAO,gBACjG;AACD,UAAI,QAAQ,MACV,OAAI,QACF,iBAAiB,MAAM,2EACxB;;KAKL,MAAM,cAAc,QAAQ,IAAI;AAChC,SAAI,aAAa;MACf,MAAM,KAAA,UAAa,KAAK;AACxB,SAAG,eACD,aACA,aAAa,KAAK,MAAM,cAAc,EAAE,IAAI,OAAO,WAAW,WAAW,CAAC,eAC3E;AACD,SAAG,eAAe,aAAa,WAAW;AAC1C,SAAG,eAAe,aAAa,KAAK;;AAGtC,UAAK,YAAY,WAAW;;AAI9B,SAAK,aAAa;IAGlB,MAAM,YAAY,QAAQ,IAAI;AAE9B,QADmB,aAAa,cAAc,OAAO,cAAc,WACjD,KAAK,MAAM,cAAc,GAAG;KAC5C,MAAM,YAAY,KAAK,MAAM,cAAc;KAC3C,MAAM,EAAE,QAAQ,gBAAgB,cAC9B,KAAK,MACL,OACA,QACA,MACA;MACE,MAAM,KAAK,SAAS,eAAe,eAAe;MAClD,yBAAyB;MAC1B,EACD,KAAK,eACN;KACD,IAAI,QAAQ;AACZ,UAAK,IAAI,IAAI,GAAG,IAAI,OAAO,UAAU,CAAC,OAAO,IAC3C,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,SAAS,CAAC,OAAO,KAAK;MAC/C,MAAM,IAAI,OAAO,QAAQ,GAAG,EAAE;MAC9B,MAAM,IAAI,YAAY,QAAQ,GAAG,EAAE;AACnC,UAAI,CAAC,WAAW,GAAG,EAAE,EAAE;AACrB,eAAQ;OAGR,MAAM,MAAM,qBAAqB,KAAK,MAAM,GAAG,GAAG,GAAG,GAAG,UAAU;OAGlE,MAAM,mBAAkD,WAAmB,2BACvE,gBAAiB,WAAmB,yBAAyB,GAC7D,KAAA;OAOJ,MAAM,MALY,sBAAsB,KAAK,iBAAiB,GAKtC,wBAFR,aAAa,OAAO,CAEoB,mBADtC,aAAa,YAAY;AAG3C,WAAI,QAAQ,IAAI,UACd,gBAAe,QAAQ,IAAI,WAAW,MAAM,KAAK;AAEnD,aAAI,QAAQ,IAAI;AAEhB,aAAM,IAAI,+BAA+B,KAAK;QAC5C;QACA,iBAAiB;QAClB,CAAC;;;AAIR,SAAI,CAAC,SAAS,QAAQ,IAAI,UACxB,gBAAe,QAAQ,IAAI,WAAW,2BAA2B,UAAU,OAAO;;IAKtF,MAAM,aAAa,KAAK,KAAK,GAAG;AAChC,SAAK,MAAM;AACX,SAAK,MAAM,iBAAiB;AAC5B,SAAK,MAAM,iBACR,KAAK,MAAM,iBAAiB,KAAK,MAAM,cAAc,KAAK,cAAc,KAAK,MAAM;AACtF,SAAK,iBAAiB,KAAK,KAAK;AAGhC,WAAO,SAAS,cAAc,KAAK,MAAM;AACzC,WAAO,SAAS,aAAa;AAC7B,WAAO,SAAS,QAAQ,kBAAkB;AAE1C,UAAI,QACF,WAAW,KAAK,MAAM,YAAY,aAAa,WAAW,cAAc,kBAAkB,OAAO,QAClG;IAGD,MAAM,YAAY,KAAK,MAAM,eAAe,IAAI,KAAK,qBAAqB,IAAI,KAAK;AACnF,QAAI,YAAY,KAAK,aAAa,UAChC,OAAI,QACF,uBAAuB,KAAK,MAAM,YAAY,QAAQ,WAAW,iBAAiB,KAAK,mBAAmB,aAAa,kBAAkB,OAAO,GACjJ;AAGH,QAAI,KAAK,UACP,MAAK,SAAS,WAAW,KAAK,MAAM,YAAY,QAAQ,WAAW,IAAI;YAElE,OAAO;AAEd,UAAI,QAAQ,iBAAiB,QAAQ;AACrC,SAAK,SAAS,iBAAiB,MAAM;AACrC,UAAM;;;;;;;;;;;CAOV,sBAAoC;EAClC,IAAI,gBAAsD;EAE1D,MAAM,qBAAqB;AAEzB,OAAI,cACF,cAAa,cAAc;AAG7B,mBAAgB,iBAAiB;AAC/B,oBAAgB;AAGhB,SAAK,aAAa;AAGlB,SAAK,gBAAgB;MACpB,GAAG;;AAGR,OAAK,OAAO,GAAG,UAAU,aAAa;AAEtC,OAAK,sBAAsB;AACzB,QAAK,OAAO,IAAI,UAAU,aAAa;AACvC,OAAI,cACF,cAAa,cAAc;;;;;;CAQjC,SAAiB,SAAuB;AACtC,QAAI,QAAQ,QAAQ;;;;;CAMtB,SAAiB,SAAiB,OAAsB;AACtD,MAAI,iBAAiB,MACnB,OAAI,QAAQ,GAAG,QAAQ,GAAG,MAAM,SAAS,MAAM,UAAU;MAEzD,OAAI,QAAQ,GAAG,QAAQ,GAAG,OAAO,MAAM,GAAG;;;;;;;;ACtjBhD,MAAM,gBAAgB;AACtB,MAAM,iBAAiB;;;;AAKvB,SAAgBE,SAAO,OAA+B;AAEpD,KAAI,CAAC,SAAU,OAAO,UAAU,YAAY,OAAO,UAAU,WAC3D,QAAO;CAET,MAAM,MAAM;AACZ,QACE,OAAO,IAAI,cAAc,cACzB,OAAO,IAAI,aAAa,cACxB,OAAO,IAAI,aAAa,cACxB,OAAO,IAAI,UAAU;;;;;AAOzB,SAAgB,UAAU,OAAkC;AAC1D,KAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAGhD,QAAO,OADK,MACM,cAAc;;;;;;;;AASlC,SAAgB,eAAe,KAA+B;CAE5D,MAAM,QAAQ,IAAI,SAAS,IAAI,QAAQ,WAAW;CAClD,MAAM,SAAS,IAAI,UAAU,IAAI,QAAQ,QAAQ;CAGjD,IAAI,SAA4B;AAChC,KAAI,IAAI,WAAW,KAEjB,UAAS,iBAAiB,IAAI,OAAO;UAC5B,IAAI,WAAW,SAAS,IAAI,WAAW,KAChD,UAAS;UACA,IAAI,OACb,UAAS,IAAI;KAGb,UAAS,iBAAiB,IAAI,OAAO;CAIvC,IAAI,SAAsC;AAC1C,KAAI,IAAI,OAEN,UAAS,IAAI;UACJ,IAAI,MAEb,UAAS,kBAAkB,IAAI,MAAM;AAGvC,QAAO;EACL,QAAQ,IAAI,UAAU;EACtB;EACA;EACA;EACA;EACA,UAAU,WAAW;EACtB;;;;;;;;AASH,SAAgB,gBAAgB,MAA6B;AAC3D,QAAO;EACL,QAAQ,KAAK;EACb,OAAO,KAAK,QAAQ;EACpB,QAAQ,KAAK,QAAQ;EACrB,QAAQ,KAAK,UAAU;EAEvB,QAAQ,kBAAkB,KAAK,MAAM;EACrC,UAAU;EACX;;;;;AAUH,SAAS,iBAAiB,QAAgD;AAExE,KAAI,QAAQ,IAAI,aAAa,KAAA,EAC3B,QAAO;AAGT,KAAI,QAAQ,IAAI,gBAAgB,KAAA,GAAW;EACzC,MAAM,QAAQ,OAAO,SAAS,QAAQ,IAAI,aAAa,GAAG;AAC1D,MAAI,UAAU,EAAG,QAAO;AACxB,MAAI,UAAU,EAAG,QAAO;AACxB,MAAI,UAAU,EAAG,QAAO;AACxB,MAAI,SAAS,EAAG,QAAO;AACvB,SAAO;;AAIT,KAAI,QAAQ,IAAI,cAAc,eAAe,QAAQ,IAAI,cAAc,QACrE,QAAO;AAIT,KAAI,CAAC,QAAQ,MACX,QAAO;CAIT,MAAM,OAAO,QAAQ,IAAI,QAAQ;AACjC,KAAI,KAAK,SAAS,WAAW,IAAI,KAAK,SAAS,MAAM,CACnD,QAAO;AAIT,QAAO;;;;;;;AAYT,SAAgB,kBAAkB,OAAgD;AAChF,QAAO,EACL,CAAC,OAAO,iBAAuC;EAC7C,MAAM,SAAkB,EAAE;EAC1B,IAAI,cAA+D;EACnE,IAAI,OAAO;EAGX,MAAM,cAAc,UAA2B;GAC7C,MAAM,OAAO,OAAO,UAAU,WAAW,QAAQ,MAAM,SAAS,OAAO;AAIvE,QAAK,MAAM,QAAQ,MAAM;IACvB,MAAM,QAAe;KACnB,MAAM;KACN,KAAK;KACL,MAAM,KAAK,WAAW,EAAE,GAAG,MAAM,SAAS,QAAQ,SAAS,QAAQ,SAAS;KAC7E;AAED,QAAI,aAAa;AACf,iBAAY;MAAE,OAAO;MAAO,MAAM;MAAO,CAAC;AAC1C,mBAAc;UAEd,QAAO,KAAK,MAAM;;;EAKxB,MAAM,kBAAkB;AACtB,UAAO;AACP,OAAI,aAAa;AACf,gBAAY;KAAE,OAAO,KAAA;KAA+B,MAAM;KAAM,CAAC;AACjE,kBAAc;;;AAKlB,MAAI,MAAM,SAAS,OAAO,MAAM,eAAe,YAAY;AACzD,SAAM,YAAY,OAAO;AACzB,SAAM,GAAG,QAAQ,WAAW;AAC5B,SAAM,GAAG,OAAO,UAAU;;AAG5B,SAAO;GACL,OAAuC;IAErC,MAAM,WAAW,OAAO,OAAO;AAC/B,QAAI,SACF,QAAO,QAAQ,QAAQ;KAAE,OAAO;KAAU,MAAM;KAAO,CAAC;AAI1D,QAAI,KACF,QAAO,QAAQ,QAAQ;KACrB,OAAO,KAAA;KACP,MAAM;KACP,CAAC;AAIJ,WAAO,IAAI,SAAS,YAAY;AAC9B,mBAAc;MACd;;GAGJ,SAAyC;AACvC,WAAO;AACP,UAAM,IAAI,QAAQ,WAAW;AAC7B,UAAM,IAAI,OAAO,UAAU;AAC3B,WAAO,QAAQ,QAAQ;KACrB,OAAO,KAAA;KACP,MAAM;KACP,CAAC;;GAEL;IAEJ;;;;UCjWiH;;;;;;;;;;;;;;;;;;;;;;;;ACkBpH,MAAa,SAAS;CAEpB,aAAa;CAEb,WAAW;CAEX,cAAc;CAEd,aAAa,aAAsB,cAAc,YAAY,EAAE;CAChE;;;;;;;;;;ACZD,MAAM,oBAAoB;;;;;;;;;;;;AAa1B,eAAsB,mBACpB,OACA,MACA,YAAY,KACgB;AAC5B,OAAM,oBAAoB,CAAC;CAE3B,MAAM,OAAO,MAAM,KAAK,UAAU;AAClC,KAAI,QAAQ,KACV,QAAO;EAAE,WAAW;EAAO,OAAO;EAAG;CAGvC,MAAM,QAAQ,kBAAkB,KAAK,KAAK;AAC1C,KAAI,CAAC,MACH,QAAO;EAAE,WAAW;EAAO,OAAO;EAAG,UAAU;EAAM;AAQvD,QAAO;EAAE,WAAW;EAAM,OALZ,SAAS,MAAM,IAAK,GAAG;EAKJ,UAHlB,KAAK,MAAM,GAAG,MAAM,MAAM,GAC3B,KAAK,MAAM,MAAM,QAAQ,MAAM,GAAG,OAAO,IAEA,KAAA;EAAW;;;;;;AAOpE,eAAsB,qBACpB,QACA,OACA,YAAY,KACgB;CAC5B,MAAM,SAAS,MAAM;AACrB,KAAI,CAAC,OAAQ,OAAM,WAAW,KAAK;AAEnC,KAAI;EACF,MAAM,SAAS,MAAc;AAC3B,UAAO,MAAM,EAAE;;EAGjB,MAAM,QAAQ,OACZ,IAAI,SAAS,YAAY;GACvB,MAAM,QAAQ,iBAAiB;AAC7B,UAAM,eAAe,QAAQ,OAAO;AACpC,YAAQ,KAAK;MACZ,GAAG;GAEN,SAAS,OAAO,OAAe;AAC7B,iBAAa,MAAM;AACnB,UAAM,eAAe,QAAQ,OAAO;AACpC,YAAQ,MAAM,UAAU,CAAC;;AAG3B,SAAM,GAAG,QAAQ,OAAO;IACxB;AAEJ,SAAO,MAAM,mBAAmB,OAAO,MAAM,UAAU;WAC/C;AACR,MAAI,CAAC,OAAQ,OAAM,WAAW,MAAM;;;;;;;;;;;;;;;;;;AC3ExC,MAAM,MAAM;AACZ,MAAM,QAAQ,GAAG,IAAI;AAErB,SAAS,IAAI,GAAG,OAAoC;AAClD,QAAO,GAAG,MAAM,MAAM,KAAK,IAAI,CAAC;;AAGlC,SAAS,cAAc,OAAuB;AAC5C,QAAO,KAAK,IAAI,EAAE,CAAC,MAAM,MAAM,MAAM,MAAM;;AAG7C,SAAS,IAAI,OAAe,SAAyB;AACnD,QAAO,KAAK,MAAM,OAAO,GAAG,CAAC,GAAG,UAAU;;;AAI5C,MAAa,oBAAoB;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;AAeD,SAAgB,YAAY,SAAiC;CAC3D,MAAM,IAAI,SAAS,UAAU,QAAQ;CACrC,MAAM,SAAS,SAAS;CACxB,MAAM,QAAQ,MAAuB,CAAC,UAAU,OAAO,WAAW,KAAK,OAAO,SAAS,EAAE;CAEzF,MAAM,OAAO,oBAAoB;AAEjC,GAAE,MAAM,KAAK,IAAI,EAAE,CAAC,0BAA0B,MAAM,IAAI;AACxD,GAAE,MAAM,cAAc,KAAK,WAAW,YAAY,IAAI;AACtD,GAAE,MAAM,WAAW,KAAK,QAAQ,YAAY,IAAI;AAChD,GAAE,MAAM,gBAAgB,QAAQ,IAAI,aAAa,UAAU,IAAI;AAC/D,GAAE,MAAM,qBAAqB,KAAK,WAAW,QAAQ,KAAK,eAAe,YAAY,KAAK,SAAS,IAAI;AACvG,GAAE,MAAM,uBAAuB,KAAK,gBAAgB,SAAS,KAAK,eAAe,IAAI;AACrF,GAAE,MAAM,iBAAiB,KAAK,cAAc,IAAI;AAEhD,KAAI,KAAK,MAAM,EAAE;AACf,IAAE,MAAM,cAAc,sBAAsB,CAAC;AAC7C,IAAE,MAAM,IAAI,QAAQ,GAAG,IAAI,EAAE,CAAC,qBAAqB,QAAQ,GAAG,KAAK;AACnE,IAAE,MAAM,IAAI,OAAO,GAAG,IAAI,EAAE,CAAC,qBAAqB,QAAQ,GAAG,KAAK;AAClE,IAAE,MAAM,IAAI,UAAU,GAAG,IAAI,EAAE,CAAC,qBAAqB,QAAQ,GAAG,KAAK;AACrE,IAAE,MAAM,IAAI,aAAa,GAAG,IAAI,EAAE,CAAC,qBAAqB,QAAQ,GAAG,KAAK;AACxE,IAAE,MAAM,IAAI,iBAAiB,GAAG,IAAI,EAAE,CAAC,qBAAqB,QAAQ,GAAG,KAAK;AAC5E,IAAE,MAAM,IAAI,WAAW,GAAG,IAAI,EAAE,CAAC,qBAAqB,QAAQ,GAAG,KAAK;AACtE,IAAE,MAAM,IAAI,SAAS,GAAG,IAAI,EAAE,CAAC,qBAAqB,QAAQ,GAAG,KAAK;AACpE,IAAE,MAAM,IAAI,eAAe,GAAG,IAAI,GAAG,EAAE,CAAC,qBAAqB,QAAQ,GAAG,KAAK;;AAG/E,KAAI,KAAK,YAAY,EAAE;AACrB,IAAE,MAAM,cAAc,sDAAsD,CAAC;AAC7E,IAAE,MAAM,IAAI,cAAc,GAAG,IAAI,yBAAyB,QAAQ,GAAG,KAAK;AAC1E,IAAE,MAAM,IAAI,cAAc,GAAG,IAAI,yBAAyB,QAAQ,GAAG,KAAK;AAC1E,IAAE,MAAM,IAAI,aAAa,GAAG,IAAI,yBAAyB,QAAQ,GAAG,KAAK;AACzE,IAAE,MAAM,IAAI,cAAc,GAAG,IAAI,yBAAyB,QAAQ,GAAG,KAAK;AAC1E,IAAE,MAAM,IAAI,cAAc,GAAG,IAAI,yBAAyB,QAAQ,GAAG,KAAK;AAC1E,IAAE,MACA,IAAI,sBAAsB,GAAG,IAAI,MAAM,MAAM,kEAAkE,GAC7G,KACH;AAED,IAAE,MAAM,cAAc,oDAAoD,CAAC;AAC3E,IAAE,MAAM,IAAI,iBAAiB,GAAG,IAAI,EAAE,GAAG,IAAI,4BAA4B,QAAQ,GAAG,KAAK;AACzF,IAAE,MAAM,IAAI,mBAAmB,GAAG,IAAI,EAAE,GAAG,IAAI,4BAA4B,QAAQ,GAAG,KAAK;AAC3F,IAAE,MAAM,IAAI,kBAAkB,GAAG,IAAI,EAAE,GAAG,IAAI,4BAA4B,QAAQ,GAAG,KAAK;AAC1F,IAAE,MAAM,IAAI,oBAAoB,GAAG,IAAI,EAAE,GAAG,IAAI,oCAAoC,QAAQ,GAAG,KAAK;AACpG,IAAE,MACA,IACE,yBACA,GAAG,IAAI,EAAE,GAAG,IAAI,SAAS,MAAM,iEAChC,GAAG,KACL;;AAGH,KAAI,KAAK,SAAS,EAAE;AAClB,IAAE,MAAM,cAAc,iBAAiB,CAAC;EACxC,MAAM,aAAa;GAAC;GAAS;GAAO;GAAS;GAAU;GAAQ;GAAW;GAAQ;GAAQ;EAC1F,IAAI,SAAS;AACb,OAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IAAK,WAAU,GAAG,IAAI,KAAK,EAAE,CAAC,GAAG,WAAW,KAAK;AACxE,IAAE,MAAM,SAAS,KAAK;EACtB,IAAI,WAAW;AACf,OAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IAAK,aAAY,GAAG,IAAI,KAAK,EAAE,CAAC,GAAG,WAAW,KAAK;AAC1E,IAAE,MAAM,WAAW,KAAK;EACxB,IAAI,SAAS;AACb,OAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IAAK,WAAU,GAAG,IAAI,KAAK,EAAE,CAAC,GAAG,WAAW,GAAG,GAAG;AACzE,IAAE,MAAM,SAAS,KAAK;EACtB,IAAI,WAAW;AACf,OAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IAAK,aAAY,GAAG,IAAI,MAAM,EAAE,CAAC,GAAG,WAAW,GAAG,GAAG;AAC5E,IAAE,MAAM,WAAW,KAAK;;AAG1B,KAAI,KAAK,MAAM,EAAE;AACf,IAAE,MAAM,cAAc,6CAA6C,CAAC;EACpE,IAAI,UAAU;AACd,OAAK,IAAI,IAAI,GAAG,IAAI,IAAI,IAAK,YAAW,GAAG,IAAI,OAAO,EAAE,KAAK;AAC7D,IAAE,MAAM,UAAU,KAAK;EACvB,IAAI,WAAW;AACf,OAAK,IAAI,IAAI,IAAI,IAAI,IAAI,IAAK,aAAY,GAAG,IAAI,OAAO,EAAE,IAAI;AAC9D,IAAE,MAAM,WAAW,KAAK;EACxB,IAAI,WAAW;AACf,OAAK,IAAI,IAAI,KAAK,IAAI,KAAK,IAAK,aAAY,GAAG,IAAI,OAAO,EAAE,IAAI;AAChE,IAAE,MAAM,WAAW,KAAK;;AAG1B,KAAI,KAAK,YAAY,EAAE;AACrB,IAAE,MAAM,cAAc,sCAAsC,CAAC;EAC7D,IAAI,SAAS;AACb,OAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;GAC3B,MAAM,IAAI,KAAK,MAAO,IAAI,KAAM,IAAI;GACpC,MAAM,IAAI,KAAK,OAAQ,KAAK,KAAK,KAAM,IAAI;AAC3C,aAAU,GAAG,IAAI,OAAO,EAAE,GAAG,EAAE,OAAO;;AAExC,IAAE,MAAM,SAAS,KAAK;AACtB,IAAE,MAAM,IAAI,qBAAqB,+CAA+C,GAAG,KAAK;;AAG1F,KAAI,KAAK,UAAU,EAAE;AACnB,IAAE,MAAM,cAAc,mCAAmC,CAAC;AAC1D,IAAE,MAAM,IAAI,SAAS,0BAA0B,GAAG,KAAK;AACvD,IAAE,MAAM,IAAI,kBAAkB,gBAAgB,GAAG,KAAK;AACtD,IAAE,MAAM,IAAI,OAAO,eAAe,GAAG,KAAK;AAC1C,IAAE,MAAM,IAAI,eAAe,kBAAkB,GAAG,KAAK;AACrD,IAAE,MAAM,IAAI,kBAAkB,WAAW,GAAG,KAAK;AACjD,IAAE,MAAM,IAAI,WAAW,sBAAsB,GAAG,KAAK;AACrD,IAAE,MAAM,IAAI,SAAS,sBAAsB,GAAG,KAAK;AACnD,IAAE,MAAM,IAAI,iBAAiB,oCAAwD,GAAG,KAAK;AAC7F,IAAE,MAAM,IAAI,oBAAoB,2BAA2B,GAAG,KAAK;;AAGrE,KAAI,KAAK,QAAQ,EAAE;AAIjB,IAAE,MAAM,cAAc,sDAAsD,CAAC;AAC7E,IAAE,MAAM,8BAA8B;AACtC,IAAE,MAAM,oCAAoC;AAC5C,IAAE,MAAM,kCAAkC;AAC1C,IAAE,MAAM,uCAAuC;AAC/C,IAAE,MAAM,uCAAuC;AAC/C,IAAE,MAAM,oCAAoC;AAC5C,IAAE,MAAM,oCAAoC;AAC5C,IAAE,MAAM,oCAAoC;AAC5C,IAAE,MAAM,oCAAoC;AAC5C,IAAE,MAAM,kDAAkD;AAC1D,IAAE,MAAM,iDAAiD;AACzD,IAAE,MAAM,kDAAkD;AAC1D,IAAE,MAAM,oCAAoC;AAC5C,IAAE,MAAM,oCAAoC;AAC5C,IAAE,MAAM,2CAA2C;AACnD,IAAE,MAAM,oCAAoC;AAC5C,IAAE,MAAM,oCAAoC;AAC5C,IAAE,MAAM,oCAAoC;AAC5C,IAAE,MAAM,oCAAoC;AAC5C,IAAE,MAAM,oCAAoC;;AAG9C,KAAI,KAAK,UAAU,EAAE;AACnB,IAAE,MAAM,cAAc,sBAAsB,CAAC;AAC7C,IAAE,MAAM,+CAA+C;AACvD,IAAE,MAAM,+CAA+C;AACvD,IAAE,MAAM,+CAA+C;;AAGzD,KAAI,KAAK,UAAU,EAAE;AACnB,IAAE,MAAM,cAAc,mDAAmD,CAAC;AAC1E,IAAE,MAAM,IAAI,oBAAoB,GAAG,IAAI,IAAI,EAAE,CAAC,iCAAiC,QAAQ,GAAG,KAAK;AAC/F,IAAE,MAAM,IAAI,sBAAsB,GAAG,IAAI,IAAI,GAAG,CAAC,6BAA6B,QAAQ,GAAG,KAAK;AAC9F,IAAE,MAAM,IAAI,uBAAuB,GAAG,IAAI,IAAI,GAAG,CAAC,6BAA6B,QAAQ,GAAG,KAAK;AAC/F,IAAE,MAAM,IAAI,uBAAuB,GAAG,IAAI,KAAK,GAAG,CAAC,sBAAsB,QAAQ,GAAG,KAAK;AAEzF,IAAE,MAAM,cAAc,qBAAqB,CAAC;AAC5C,IAAE,MAAM,0EAA0E;AAClF,IAAE,MAAM,0EAA0E;AAClF,IAAE,MAAM,2DAA2D;;AAGrE,KAAI,KAAK,UAAU,EAAE;AACnB,IAAE,MAAM,cAAc,4BAA4B,CAAC;EACnD,MAAM,UAAU,OAAO,QAAQ,KAAK;AACpC,OAAK,MAAM,CAAC,KAAK,UAAU,SAAS;GAClC,MAAM,YAAY,UAAU,OAAO,MAAM,UAAU,QAAQ,MAAM,OAAO,MAAM;AAC9E,KAAE,MAAM,KAAK,IAAI,OAAO,GAAG,CAAC,GAAG,UAAU,IAAI;;;AAIjD,GAAE,MAAM,KAAK;;;;;;;;;;ACrMf,SAAgB,aAAa,MAA0B;CACrD,MAAM,OAAO,KAAK;CAClB,MAAM,WAAW,oBACf,OAAO;EAAE,eAAe,KAAK;EAAe,mBAAmB,KAAK;EAAqB,GAAG,EAAE,CAC/F;AAED,QAAO,OAAO,OAAO,MAAM;EACzB,eAAe;GAAE,WAAW,SAAS;GAAe,YAAY;GAAM;EACtE,mBAAmB;GAAE,WAAW,SAAS;GAAmB,YAAY;GAAM;EAC9E,cAAc;GAAE,OAAO,SAAS,aAAa,KAAK,SAAS;GAAE,YAAY;GAAM;EAC/E,kBAAkB;GAAE,OAAO,SAAS,iBAAiB,KAAK,SAAS;GAAE,YAAY;GAAM;EACvF,eAAe;GAAE,OAAO,SAAS,cAAc,KAAK,SAAS;GAAE,YAAY;GAAM;EACjF,UAAU;GAAE,OAAO,SAAS,SAAS,KAAK,SAAS;GAAE,YAAY;GAAM;EACvE,cAAc;GAAE,OAAO,SAAS,aAAa,KAAK,SAAS;GAAE,YAAY;GAAM;EAC/E,qBAAqB;GAAE,OAAO,SAAS,oBAAoB,KAAK,SAAS;GAAE,YAAY;GAAM;EAC9F,CAAC;;;;;;;;;;;AAYJ,SAAgB,eACd,UAGI,EAAE,EACU;CAChB,MAAM,EAAE,MAAM,UAAU,qBAAqB;CAC7C,MAAM,WACJ,oBACA,oBAAoB,OAAO;EAAE,eAAe,KAAK;EAAe,mBAAmB,KAAK;EAAqB,GAAG,EAAE,CAAC;AAWrH,QAAO;EAAE;EAAU,eAVG,kBACpB,OACI;GACE,iBAAiB,KAAK;GACtB,gBAAgB,KAAK;GACrB,YAAY,KAAK;GAClB,GACD,EAAE,EACN,SACD;EACiC;;;;;;;;;;;;;;;;;;;ACxDpC,MAAM,kBAAkB;;;;;;;;;;;AAYxB,eAAsB,oBACpB,OACA,MACA,YAAY,KACkC;AAC9C,OAAM,UAAU;CAEhB,MAAM,OAAO,MAAM,KAAK,UAAU;AAClC,KAAI,QAAQ,KAAM,QAAO;CAEzB,MAAM,QAAQ,gBAAgB,KAAK,KAAK;AACxC,KAAI,CAAC,MAAO,QAAO;AAEnB,QAAO;EACL,KAAK,SAAS,MAAM,IAAK,GAAG;EAC5B,KAAK,SAAS,MAAM,IAAK,GAAG;EAC7B;;;;;;AAOH,eAAsB,qBACpB,QACA,OACA,YAAY,KACkC;CAC9C,MAAM,SAAS,MAAM;AACrB,KAAI,CAAC,OAAQ,OAAM,WAAW,KAAK;AAEnC,KAAI;EACF,MAAM,SAAS,MAAc;AAC3B,UAAO,MAAM,EAAE;;EAGjB,MAAM,QAAQ,OACZ,IAAI,SAAS,YAAY;GACvB,MAAM,QAAQ,iBAAiB;AAC7B,UAAM,eAAe,QAAQ,OAAO;AACpC,YAAQ,KAAK;MACZ,GAAG;GAEN,SAAS,OAAO,OAAe;AAC7B,iBAAa,MAAM;AACnB,UAAM,eAAe,QAAQ,OAAO;AACpC,YAAQ,MAAM,UAAU,CAAC;;AAG3B,SAAM,GAAG,QAAQ,OAAO;IACxB;AAEJ,SAAO,MAAM,oBAAoB,OAAO,MAAM,UAAU;WAChD;AACR,MAAI,CAAC,OAAQ,OAAM,WAAW,MAAM;;;;;UCnElB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACctB,MAAM,kBAAkB;;AAGxB,MAAM,kBAAkB;;AAGxB,MAAM,kBAAkB;;AAGxB,MAAM,wBAAwB;;;;;;;;;;;AAgB9B,eAAsB,eACpB,OACA,MACA,YAAY,KAC0B;AACtC,OAAM,SAAS;CAEf,MAAM,OAAO,MAAM,KAAK,UAAU;AAClC,KAAI,QAAQ,KAAM,QAAO;CAEzB,MAAM,QAAQ,gBAAgB,KAAK,KAAK;AACxC,KAAI,CAAC,MAAO,QAAO;AAGnB,QAAO,EAAE,QADM,MAAM,GAAI,MAAM,IAAI,CAAC,KAAK,MAAM,SAAS,GAAG,GAAG,CAAC,EAC9C;;;;;;;;;;;;AAiBnB,eAAsB,iBACpB,OACA,MACA,YAAY,KACmD;AAC/D,OAAM,UAAU;CAEhB,MAAM,OAAO,MAAM,KAAK,UAAU;AAClC,KAAI,QAAQ,KAAM,QAAO;CAEzB,MAAM,QAAQ,gBAAgB,KAAK,KAAK;AACxC,KAAI,CAAC,MAAO,QAAO;CAEnB,MAAM,QAAQ,MAAM,GAAI,MAAM,IAAI;AAClC,KAAI,MAAM,SAAS,EAAG,QAAO;AAE7B,QAAO;EACL,MAAM,SAAS,MAAM,IAAK,GAAG;EAC7B,SAAS,SAAS,MAAM,IAAK,GAAG;EAChC,IAAI,SAAS,MAAM,IAAK,GAAG;EAC5B;;;;;;;;;;;AAgBH,eAAsB,gBACpB,OACA,MACA,YAAY,KACY;AACxB,OAAM,UAAU;CAEhB,MAAM,OAAO,MAAM,KAAK,UAAU;AAClC,KAAI,QAAQ,KAAM,QAAO;CAEzB,MAAM,QAAQ,gBAAgB,KAAK,KAAK;AACxC,KAAI,CAAC,MAAO,QAAO;AAEnB,QAAO,MAAM;;;;;;;;;;;;;;AAmBf,eAAsB,qBACpB,OACA,MACA,YAAY,KACY;AACxB,OAAM,WAAW;CAEjB,MAAM,OAAO,MAAM,KAAK,UAAU;AAClC,KAAI,QAAQ,KAAM,QAAO;CAEzB,MAAM,QAAQ,sBAAsB,KAAK,KAAK;AAC9C,KAAI,CAAC,MAAO,QAAO;AAEnB,QAAO,MAAM;;;;;;;;;;;;AAwBf,eAAsB,sBACpB,QACA,OACA,YAAY,KACe;CAC3B,MAAM,SAAS,MAAM;AACrB,KAAI,CAAC,OAAQ,OAAM,WAAW,KAAK;AAEnC,KAAI;EACF,MAAM,SAAS,MAAc;AAC3B,UAAO,MAAM,EAAE;;EAGjB,MAAM,QAAQ,OACZ,IAAI,SAAS,YAAY;GACvB,MAAM,QAAQ,iBAAiB;AAC7B,UAAM,eAAe,QAAQ,OAAO;AACpC,YAAQ,KAAK;MACZ,GAAG;GAEN,SAAS,OAAO,OAAe;AAC7B,iBAAa,MAAM;AACnB,UAAM,eAAe,QAAQ,OAAO;AACpC,YAAQ,MAAM,UAAU,CAAC;;AAG3B,SAAM,GAAG,QAAQ,OAAO;IACxB;AAMJ,SAAO;GAAE,KAJG,MAAM,eAAe,OAAO,MAAM,UAAU;GAI1C,KAHF,MAAM,iBAAiB,OAAO,MAAM,UAAU;GAGvC,SAFH,MAAM,qBAAqB,OAAO,MAAM,UAAU;GAEtC;WACpB;AACR,MAAI,CAAC,OAAQ,OAAM,WAAW,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;AC3MxC,MAAM,qBAAqB;;AAG3B,MAAa,UAAU;CAErB,gBAAgB;CAEhB,YAAY;CAEZ,gBAAgB;CAEhB,iBAAiB;CAEjB,aAAa;CAEb,iBAAiB;CAClB;;;;;;;;;;AAaD,eAAsB,UACpB,OACA,MACA,MACA,YAAY,KACQ;AACpB,OAAM,SAAS,KAAK,IAAI;CAExB,MAAM,OAAO,MAAM,KAAK,UAAU;AAClC,KAAI,QAAQ,KAAM,QAAO;CAEzB,MAAM,QAAQ,mBAAmB,KAAK,KAAK;AAC3C,KAAI,CAAC,MAAO,QAAO;AAGnB,KADqB,SAAS,MAAM,IAAK,GAAG,KACvB,KAAM,QAAO;AAGlC,SADW,SAAS,MAAM,IAAK,GAAG,EAClC;EACE,KAAK;EACL,KAAK,EACH,QAAO;EACT,KAAK;EACL,KAAK,EACH,QAAO;EACT,QACE,QAAO;;;;;;;;;;;;;AAcb,eAAsB,WACpB,OACA,MACA,OACA,YAAY,KACqB;CACjC,MAAM,0BAAU,IAAI,KAAwB;AAE5C,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,QAAQ,MAAM,UAAU,OAAO,MAAM,MAAM,UAAU;AAC3D,UAAQ,IAAI,MAAM,MAAM;;AAG1B,QAAO;;;;;;;;;;;;;;;;;;;;;;;;ACnFT,MAAM,oBAAoB;;AAG1B,MAAM,wBAAwB;;;;;;;;;AAc9B,eAAsB,oBACpB,OACA,MACA,YAAY,KACuC;AACnD,OAAM,WAAW;CAEjB,MAAM,OAAO,MAAM,KAAK,UAAU;AAClC,KAAI,QAAQ,KAAM,QAAO;CAEzB,MAAM,QAAQ,kBAAkB,KAAK,KAAK;AAC1C,KAAI,CAAC,MAAO,QAAO;AAEnB,QAAO;EACL,QAAQ,SAAS,MAAM,IAAK,GAAG;EAC/B,OAAO,SAAS,MAAM,IAAK,GAAG;EAC/B;;;;;;;;;;AAeH,eAAsB,kBACpB,OACA,MACA,YAAY,KACoC;AAChD,OAAM,WAAW;CAEjB,MAAM,OAAO,MAAM,KAAK,UAAU;AAClC,KAAI,QAAQ,KAAM,QAAO;CAEzB,MAAM,QAAQ,sBAAsB,KAAK,KAAK;AAC9C,KAAI,CAAC,MAAO,QAAO;AAEnB,QAAO;EACL,MAAM,SAAS,MAAM,IAAK,GAAG;EAC7B,MAAM,SAAS,MAAM,IAAK,GAAG;EAC9B;;;;;;;;;;;AAgBH,eAAsB,cACpB,OACA,MACA,YAAY,KACuC;CACnD,MAAM,SAAS,MAAM,oBAAoB,OAAO,MAAM,UAAU;AAChE,KAAI,UAAU,KAAM,QAAO;CAE3B,MAAM,OAAO,MAAM,kBAAkB,OAAO,MAAM,UAAU;AAC5D,KAAI,QAAQ,KAAM,QAAO;AAEzB,KAAI,KAAK,SAAS,KAAK,KAAK,SAAS,EAAG,QAAO;AAE/C,QAAO;EACL,OAAO,OAAO,QAAQ,KAAK;EAC3B,QAAQ,OAAO,SAAS,KAAK;EAC9B;;;;AC3EH,MAAMC,mBAAgD;CACpD,UAAU;CACV,YAAY;CACZ,YAAY;CACZ,iBAAiB;CACjB,iBAAiB;CACjB,WAAW;CACX,KAAK;CACN;AAMD,MAAMC,iBAA4C;CAChD,QAAQ;EACN,SAAS;EACT,UAAU;EACV,YAAY;EACZ,aAAa;EACb,YAAY;EACZ,UAAU;EACX;CACD,QAAQ;EACN,SAAS;EACT,UAAU;EACV,YAAY;EACZ,aAAa;EACb,YAAY;EACZ,UAAU;EACX;CACD,OAAO;EACL,SAAS;EACT,UAAU;EACV,YAAY;EACZ,aAAa;EACb,YAAY;EACZ,UAAU;EACX;CACD,MAAM;EACJ,SAAS;EACT,UAAU;EACV,YAAY;EACZ,aAAa;EACb,YAAY;EACZ,UAAU;EACX;CACF;;AAOD,SAAS,qBACP,UACA,YAC8D;CAI9D,MAAM,SACJ,OAAO,aAAa,cAChB,SAAS,cAAc,SAAS,GAChC,OAAO,oBAAoB,cACzB,IAAI,gBAAgB,GAAG,EAAE,GACzB;AACR,KAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,4CAA4C;CACzE,MAAM,MAAM,OAAO,WAAW,KAAK;AACnC,KAAI,CAAC,IAAK,OAAM,IAAI,MAAM,2CAA2C;AACnE,KAAiC,OAAO,GAAG,SAAS,KAAK;AAC3D,QAAO;;AAGT,SAAS,qBAAqB,QAAqD;AACjF,KAAI,OAAO,UACT,QAAO;EACL,YAAY,MAAc,QAA8C;AACtE,UAAO;IAAE,OAAO,KAAK;IAAQ,QAAQ;IAAG;;EAE1C,cAAc,QAAmC;AAC/C,UAAO;;EAEV;CAGH,MAAM,eAAe,OAAO,WAAW,OAAO;CAC9C,MAAM,MAAM,qBAAqB,OAAO,UAAU,OAAO,WAAW;AAEpE,QAAO;EACL,YAAY,MAAc,QAA8C;AACtE,UAAO;IAAE,OAAO,IAAI,YAAY,KAAK,CAAC;IAAO,QAAQ;IAAc;;EAErE,cAAc,QAAmC;AAC/C,UAAO;;EAEV;;AA2FH,MAAMC,gBAAsC;CAC1C,OAAO;CACP,KAAK;CACL,OAAO;CACP,QAAQ;CACR,MAAM;CACN,SAAS;CACT,MAAM;CACN,OAAO;CACP,MAAM;CACN,MAAM;CACN,aAAa;CACb,WAAW;CACX,aAAa;CACb,cAAc;CACd,YAAY;CACZ,eAAe;CACf,YAAY;CACZ,aAAa;CACd;AAED,SAASC,eAAa,OAA2B,UAA0B;AACzE,KAAI,CAAC,MAAO,QAAO;AAGnB,KAAI,MAAM,WAAW,IAAI,IAAI,MAAM,WAAW,MAAM,CAClD,QAAO;CAIT,MAAM,QAAQD,cAAY,MAAM,aAAa;AAC7C,KAAI,MAAO,QAAO;AAGlB,QAAO;;AAOT,IAAa,qBAAb,MAAwD;CACtD;CACA;CACA;CACA;CACA;CAGA;CACA;CAEA,YAAY,OAAe,QAAgB,QAAuC;AAChF,OAAK,QAAQ;AACb,OAAK,SAAS;AACd,OAAK,SAAS;EAEd,MAAM,MAAM,OAAO;EAEnB,IAAI;EACJ,IAAI;AAEJ,MAAI,OAAO,WAAW;AAEpB,QAAK,YAAY,OAAO,WAAW;AACnC,QAAK,aAAa,OAAO,WAAW,OAAO;AAC3C,cAAW,QAAQ,KAAK;AACxB,eAAY,SAAS,KAAK;SACrB;AAEL,QAAK,YAAY;AACjB,QAAK,aAAa;AAClB,cAAW;AACX,eAAY;;EAKd,MAAM,cAAc,KAAK,KAAK,WAAW,IAAI;EAC7C,MAAM,eAAe,KAAK,KAAK,YAAY,IAAI;AAE/C,MAAI,OAAO,oBAAoB,YAC7B,MAAK,SAAS,IAAI,gBAAgB,aAAa,aAAa;WACnD,OAAO,aAAa,aAAa;AAC1C,QAAK,SAAS,SAAS,cAAc,SAAS;AAC9C,QAAK,OAAO,QAAQ;AACpB,QAAK,OAAO,SAAS;QAErB,OAAM,IAAI,MAAM,uBAAuB;EAGzC,MAAM,MAAM,KAAK,OAAO,WAAW,KAAK;AACxC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,2BAA2B;AACrD,OAAK,MAAM;AAGX,MAAI,QAAQ,EACR,MAAK,IAAiC,aAAa,KAAK,GAAG,GAAG,KAAK,GAAG,EAAE;AAI5E,OAAK,IAAI,YAAY,OAAO;AAC5B,OAAK,IAAI,SAAS,GAAG,GAAG,UAAU,UAAU;;CAG9C,SAAS,GAAW,GAAW,OAAe,QAAgB,OAA0B;AACtF,MAAI,MAAM,IAAI;GAEZ,MAAM,KAAK,IAAI,KAAK;GACpB,MAAM,KAAK,IAAI,KAAK;GACpB,MAAM,KAAK,QAAQ,KAAK;GACxB,MAAM,KAAK,SAAS,KAAK;AACzB,QAAK,IAAI,YAAYC,eAAa,MAAM,IAAI,KAAK,OAAO,gBAAgB;AACxE,QAAK,IAAI,SAAS,IAAI,IAAI,IAAI,GAAG;;;CAIrC,SAAS,GAAW,GAAW,MAAc,OAA0B;EAErE,MAAM,KAAK,IAAI,KAAK;EAEpB,MAAM,QAAQ,MAAM,SAAS,EAAE;EAG/B,MAAM,SAAS,MAAM,OAAO,SAAS;EACrC,MAAM,YAAY,MAAM,SAAS,WAAW;AAC5C,OAAK,IAAI,OAAO,GAAG,UAAU,GAAG,OAAO,GAAG,KAAK,OAAO,SAAS,KAAK,KAAK,OAAO;EAEhF,IAAI,KAAK,IAAI,KAAK;AAGlB,OAAK,IAAI,YAAYA,eAAa,MAAM,IAAI,KAAK,OAAO,gBAAgB;AAExE,MAAI,CAAC,KAAK,OAAO,WAAW;GAO1B,MAAM,UAAU,KAAK,IAAI,YAAY,KAAK;GAC1C,MAAM,aAAa,QAAQ;GAE3B,MAAM,cAAc,aADA,QAAQ;GAG5B,MAAM,eADe,KAAK,OAAO,WAAW,KAAK,OAAO,aACpB,eAAe;AACnD,QAAK,IAAI,eAAe;AACxB,SAAM,cAAc;QAEpB,MAAK,IAAI,eAAe;AAI1B,OAAK,IAAI,SAAS,MAAM,IAAI,GAAG;AAG/B,MAAI,MAAM,UACR,MAAK,cAAc,IAAI,IAAI,MAAM,MAAM;AAIzC,MAAI,MAAM,eAAe;GAEvB,MAAM,YADU,KAAK,IAAI,YAAY,KAAK,CAChB;GAC1B,MAAM,UAAU,KAAK,KAAK,OAAO,WAAW;AAE5C,QAAK,IAAI,cAAcA,eAAa,MAAM,IAAI,KAAK,OAAO,gBAAgB;AAC1E,QAAK,IAAI,YAAY;AACrB,QAAK,IAAI,WAAW;AACpB,QAAK,IAAI,OAAO,IAAI,QAAQ;AAC5B,QAAK,IAAI,OAAO,KAAK,WAAW,QAAQ;AACxC,QAAK,IAAI,QAAQ;;;;;;;CAQrB,cAAsB,IAAY,IAAY,MAAc,OAA0B;EACpF,MAAM,QAAQ,MAAM,SAAS,EAAE;EAE/B,MAAM,YADU,KAAK,IAAI,YAAY,KAAK,CAChB;EAC1B,MAAM,aAAa,KAAK,KAAK,OAAO,WAAW;EAE/C,MAAM,iBAAiBA,eAAa,MAAM,kBAAkB,MAAM,IAAI,KAAK,OAAO,gBAAgB;AAElG,OAAK,IAAI,cAAc;AACvB,OAAK,IAAI,YAAY;AAIrB,UAFuB,MAAM,kBAAkB,UAE/C;GACE,KAAK;AAEH,SAAK,IAAI,WAAW;AACpB,SAAK,IAAI,OAAO,IAAI,aAAa,EAAE;AACnC,SAAK,IAAI,OAAO,KAAK,WAAW,aAAa,EAAE;AAC/C,SAAK,IAAI,OAAO,IAAI,aAAa,EAAE;AACnC,SAAK,IAAI,OAAO,KAAK,WAAW,aAAa,EAAE;AAC/C,SAAK,IAAI,QAAQ;AACjB;GAEF,KAAK;AAEH,SAAK,IAAI,WAAW;AACpB,SAAK,IAAI,OAAO,IAAI,WAAW;IAC/B,MAAM,aAAa;IACnB,MAAM,YAAY;AAClB,SAAK,IAAI,KAAK,GAAG,KAAK,WAAW,MAAM,aAAa,GAAG;AACrD,UAAK,IAAI,iBAAiB,KAAK,KAAK,aAAa,GAAG,aAAa,WAAW,KAAK,KAAK,YAAY,WAAW;AAC7G,UAAK,IAAI,iBACP,KAAK,KAAM,aAAa,IAAK,GAC7B,aAAa,WACb,KAAK,KAAK,aAAa,GACvB,WACD;;AAEH,SAAK,IAAI,QAAQ;AACjB;GAEF,KAAK;AACH,SAAK,IAAI,YAAY,CAAC,GAAG,EAAE,CAAC;AAC5B,SAAK,IAAI,WAAW;AACpB,SAAK,IAAI,OAAO,IAAI,WAAW;AAC/B,SAAK,IAAI,OAAO,KAAK,WAAW,WAAW;AAC3C,SAAK,IAAI,QAAQ;AACjB,SAAK,IAAI,YAAY,EAAE,CAAC;AACxB;GAEF,KAAK;AACH,SAAK,IAAI,YAAY,CAAC,GAAG,EAAE,CAAC;AAC5B,SAAK,IAAI,WAAW;AACpB,SAAK,IAAI,OAAO,IAAI,WAAW;AAC/B,SAAK,IAAI,OAAO,KAAK,WAAW,WAAW;AAC3C,SAAK,IAAI,QAAQ;AACjB,SAAK,IAAI,YAAY,EAAE,CAAC;AACxB;GAEF;AACE,SAAK,IAAI,WAAW;AACpB,SAAK,IAAI,OAAO,IAAI,WAAW;AAC/B,SAAK,IAAI,OAAO,KAAK,WAAW,WAAW;AAC3C,SAAK,IAAI,QAAQ;;;CAIvB,SAAS,GAAW,GAAW,MAAc,OAA0B;AAErE,OAAK,SAAS,GAAG,GAAG,MAAM,MAAM;;CAGlC,SAAS,GAAW,GAAoB;AACtC,SAAO,KAAK,KAAK,IAAI,KAAK,SAAS,KAAK,KAAK,IAAI,KAAK;;CAGxD,gBACE,GACA,GACA,OACA,QACA,QACA,MACA,QACA,YAAY,GACN;EACN,MAAM,KAAK,IAAI,KAAK;EACpB,MAAM,KAAK,IAAI,KAAK;EACpB,MAAM,KAAK,QAAQ,KAAK;EACxB,MAAM,KAAK,SAAS,KAAK;EACzB,MAAM,IAAI,KAAK,IAAI,QAAQ,KAAK,GAAG,KAAK,EAAE;AAE1C,OAAK,IAAI,WAAW;AACpB,OAAK,IAAI,OAAO,KAAK,GAAG,GAAG;AAC3B,OAAK,IAAI,OAAO,KAAK,KAAK,GAAG,GAAG;AAChC,OAAK,IAAI,iBAAiB,KAAK,IAAI,IAAI,KAAK,IAAI,KAAK,EAAE;AACvD,OAAK,IAAI,OAAO,KAAK,IAAI,KAAK,KAAK,EAAE;AACrC,OAAK,IAAI,iBAAiB,KAAK,IAAI,KAAK,IAAI,KAAK,KAAK,GAAG,KAAK,GAAG;AACjE,OAAK,IAAI,OAAO,KAAK,GAAG,KAAK,GAAG;AAChC,OAAK,IAAI,iBAAiB,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE;AACvD,OAAK,IAAI,OAAO,IAAI,KAAK,EAAE;AAC3B,OAAK,IAAI,iBAAiB,IAAI,IAAI,KAAK,GAAG,GAAG;AAC7C,OAAK,IAAI,WAAW;AAEpB,MAAI,MAAM;AACR,QAAK,IAAI,YAAY;AACrB,QAAK,IAAI,MAAM;;AAEjB,MAAI,QAAQ;AACV,QAAK,IAAI,cAAc;AACvB,QAAK,IAAI,YAAY;AACrB,QAAK,IAAI,QAAQ;;;;AASvB,SAAgB,oBAAoB,SAA8B,EAAE,EAAiB;CACnF,MAAM,MAAM;EAAE,GAAGH;EAAgB,GAAG;EAAQ;AAG5C,QAAO;EACL,MAAM;EACN,UAJe,qBAAqB,IAAI;EAMxC,aAAa,OAAe,QAA8B;AACxD,UAAO,IAAI,mBAAmB,OAAO,QAAQ,IAAI;;EAGnD,MAAM,SAAuB,aAAwC;EAKrE,eAAe,OAA4B;AACzC,UAAOC,eAAa,UAAUA,eAAa;;EAE9C;;;;ACpfH,MAAM,iBAA6C;CACjD,UAAU;CACV,YAAY;CACZ,YAAY;CACZ,iBAAiB;CACjB,iBAAiB;CACjB,aAAa;CACd;AAMD,MAAM,eAA4C;CAChD,QAAQ;EACN,SAAS;EACT,UAAU;EACV,YAAY;EACZ,aAAa;EACb,YAAY;EACZ,UAAU;EACX;CACD,QAAQ;EACN,SAAS;EACT,UAAU;EACV,YAAY;EACZ,aAAa;EACb,YAAY;EACZ,UAAU;EACX;CACD,OAAO;EACL,SAAS;EACT,UAAU;EACV,YAAY;EACZ,aAAa;EACb,YAAY;EACZ,UAAU;EACX;CACD,MAAM;EACJ,SAAS;EACT,UAAU;EACV,YAAY;EACZ,aAAa;EACb,YAAY;EACZ,UAAU;EACX;CACF;AAMD,MAAM,cAAsC;CAC1C,OAAO;CACP,KAAK;CACL,OAAO;CACP,QAAQ;CACR,MAAM;CACN,SAAS;CACT,MAAM;CACN,OAAO;CACP,MAAM;CACN,MAAM;CACN,aAAa;CACb,WAAW;CACX,aAAa;CACb,cAAc;CACd,YAAY;CACZ,eAAe;CACf,YAAY;CACZ,aAAa;CACd;AAED,SAAS,aAAa,OAA2B,UAA0B;AACzE,KAAI,CAAC,MAAO,QAAO;AACnB,KAAI,MAAM,WAAW,IAAI,IAAI,MAAM,WAAW,MAAM,CAAE,QAAO;AAE7D,QADc,YAAY,MAAM,aAAa,KAC7B;;AAOlB,SAAS,kBAAkB,SAAmD;AAI5E,QAAO;EACL,YAAY,MAAc,QAA8C;AAEtE,UAAO;IACL,OAAO,KAAK;IACZ,QAAQ;IACT;;EAGH,cAAc,QAAmC;AAC/C,UAAO;;EAEV;;AAiBH,IAAa,kBAAb,MAAqD;CACnD;CACA;CAEA;CACA;CACA;CAGA;CACA;CAGA,YAAwC;CAExC,YAAY,OAAe,QAAgB,QAAoC;AAC7E,OAAK,QAAQ;AACb,OAAK,SAAS;AACd,OAAK,SAAS;AACd,OAAK,wBAAQ,IAAI,KAAK;AACtB,OAAK,8BAAc,IAAI,KAAK;AAI5B,OAAK,YAAY,OAAO,WAAW;AACnC,OAAK,aAAa,OAAO,WAAW,OAAO;;;;;CAM7C,aAAa,WAA8B;AACzC,OAAK,YAAY;;;;;CAMnB,eAAmC;AACjC,SAAO,KAAK;;CAGd,SAAS,GAAW,GAAW,OAAe,QAAgB,OAA0B;AACtF,MAAI,MAAM,IAAI;GACZ,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,GAAG;AAClC,QAAK,YAAY,IAAI,KAAK;IACxB;IACA;IACA,GAAG;IACH,GAAG;IACH,OAAO,aAAa,MAAM,IAAI,KAAK,OAAO,gBAAgB;IAC3D,CAAC;;;CAIN,SAAS,GAAW,GAAW,MAAc,OAA0B;AACrE,MAAI,CAAC,KAAK,MAAM,IAAI,EAAE,CACpB,MAAK,MAAM,IAAI,GAAG,EAAE,CAAC;AAEvB,OAAK,MAAM,IAAI,EAAE,CAAE,KAAK;GAAE;GAAM;GAAO;GAAG,CAAC;;CAG7C,SAAS,GAAW,GAAW,MAAc,OAA0B;AACrE,OAAK,SAAS,GAAG,GAAG,MAAM,MAAM;;CAGlC,SAAS,GAAW,GAAoB;AACtC,SAAO,KAAK,KAAK,IAAI,KAAK,SAAS,KAAK,KAAK,IAAI,KAAK;;;;;;;CAQxD,SAAe;AACb,MAAI,CAAC,KAAK,UACR,OAAM,IAAI,MAAM,gEAAgE;EAGlF,MAAM,YAAY,KAAK;EACvB,MAAM,KAAK,KAAK;EAChB,MAAM,KAAK,KAAK;EAGhB,MAAM,mBAAmB,KAAK,QAAQ;EACtC,MAAM,oBAAoB,KAAK,SAAS;AAGxC,YAAU,YAAY;AAGtB,YAAU,MAAM,UAAU;;kBAEZ,KAAK,OAAO,WAAW;gBACzB,KAAK,OAAO,SAAS;kBACnB,KAAK,OAAO,WAAW;uBAClB,KAAK,OAAO,gBAAgB;YACvC,KAAK,OAAO,gBAAgB;;;YAG5B,iBAAiB;aAChB,kBAAkB;;AAI3B,OAAK,MAAM,MAAM,KAAK,YAAY,QAAQ,EAAE;GAC1C,MAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,SAAM,YAAY,GAAG,KAAK,OAAO,YAAY;AAC7C,SAAM,MAAM,UAAU;;YAEhB,GAAG,IAAI,GAAG;WACX,GAAG,IAAI,GAAG;aACR,GAAG,IAAI,GAAG;cACT,GAAG,IAAI,GAAG;wBACA,GAAG,MAAM;;AAE3B,aAAU,YAAY,MAAM;;EAI9B,MAAM,cAAc,MAAM,KAAK,KAAK,MAAM,SAAS,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG;AAEhF,OAAK,MAAM,CAAC,GAAG,SAAS,aAAa;GACnC,MAAM,UAAU,SAAS,cAAc,MAAM;AAC7C,WAAQ,YAAY,GAAG,KAAK,OAAO,YAAY;AAC/C,WAAQ,MAAM,UAAU;;;WAGnB,IAAI,GAAG;cACJ,GAAG;;;GAKX,MAAM,aAAa,KAAK,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,EAAE;AAEjD,QAAK,MAAM,OAAO,YAAY;IAC5B,MAAM,OAAO,SAAS,cAAc,OAAO;AAC3C,SAAK,YAAY,GAAG,KAAK,OAAO,YAAY;AAC5C,SAAK,cAAc,IAAI;IAGvB,MAAM,SAAmB,CAAC,sBAAsB,SAAS,IAAI,IAAI,GAAG,IAAI;AAExE,QAAI,IAAI,MAAM,GACZ,QAAO,KAAK,UAAU,aAAa,IAAI,MAAM,IAAI,KAAK,OAAO,gBAAgB,GAAG;AAElF,QAAI,IAAI,MAAM,GACZ,QAAO,KAAK,qBAAqB,aAAa,IAAI,MAAM,IAAI,KAAK,OAAO,gBAAgB,GAAG;IAG7F,MAAM,QAAQ,IAAI,MAAM;AACxB,QAAI,OAAO;AACT,SAAI,MAAM,KAAM,QAAO,KAAK,oBAAoB;AAChD,SAAI,MAAM,IAAK,QAAO,KAAK,eAAe;AAC1C,SAAI,MAAM,OAAQ,QAAO,KAAK,qBAAqB;AAGnD,SAAI,MAAM,aAAa,MAAM,gBAAgB;MAC3C,MAAM,iBAAiB,MAAM,kBAAkB;MAC/C,MAAM,iBAAiB,MAAM,iBACzB,aAAa,MAAM,gBAAgB,KAAK,OAAO,gBAAgB,GAC/D;AAEJ,cAAQ,gBAAR;OACE,KAAK;AACH,eAAO,KAAK,qCAAqC,iBAAiB;AAClE;OACF,KAAK;AACH,eAAO,KAAK,mCAAmC,iBAAiB;AAChE;OACF,KAAK;AACH,eAAO,KAAK,qCAAqC,iBAAiB;AAClE;OACF,KAAK;AACH,eAAO,KAAK,qCAAqC,iBAAiB;AAClE;OACF,QACE,QAAO,KAAK,oCAAoC,iBAAiB;;;AAIvE,SAAI,MAAM,eAAe;MACvB,MAAM,WAAW,OAAO,MAAM,MAAM,EAAE,WAAW,mBAAmB,CAAC;AACrE,UAAI,UAAU;OACZ,MAAM,MAAM,OAAO,QAAQ,SAAS;AACpC,cAAO,OAAO,SAAS,QAAQ,aAAa,yBAAyB;YAErE,QAAO,KAAK,gCAAgC;;AAIhD,SAAI,MAAM,SAAS;MAEjB,MAAM,KAAK,IAAI,MAAM,KACjB,aAAa,IAAI,MAAM,IAAI,KAAK,OAAO,gBAAgB,GACvD,KAAK,OAAO;MAChB,MAAM,KAAK,IAAI,MAAM,KACjB,aAAa,IAAI,MAAM,IAAI,KAAK,OAAO,gBAAgB,GACvD,KAAK,OAAO;AAChB,aAAO,KAAK,UAAU,MAAM,qBAAqB,KAAK;;;AAI1D,SAAK,MAAM,UAAU,OAAO,KAAK,KAAK;AACtC,YAAQ,YAAY,KAAK;;AAG3B,aAAU,YAAY,QAAQ;;;;;;CAOlC,QAAc;AACZ,OAAK,MAAM,OAAO;AAClB,OAAK,YAAY,OAAO;;;AAQ5B,SAAgB,iBAAiB,SAA2B,EAAE,EAAiB;CAC7E,MAAM,MAAM;EAAE,GAAG;EAAgB,GAAG;EAAQ;AAG5C,QAAO;EACL,MAAM;EACN,UAJe,kBAAkB,IAAI;EAMrC,aAAa,OAAe,QAA8B;AACxD,UAAO,IAAI,gBAAgB,OAAO,QAAQ,IAAI;;EAGhD,MAAM,QAAsB,aAAwC;GAElE,MAAM,YAAY;AAClB,OAAI,UAAU,cAAc,CAC1B,WAAU,QAAQ;;EAItB,eAAe,OAA4B;AACzC,UAAO,aAAa,UAAU,aAAa;;EAE9C;;AAOH,IAAI,iBAAiB;;;;;AAMrB,SAAgB,gBAAgB,cAAc,WAAiB;AAC7D,KAAI,kBAAkB,OAAO,aAAa,YAAa;CAEvD,MAAM,QAAQ,SAAS,cAAc,QAAQ;AAC7C,OAAM,cAAc;KACjB,YAAY;;;;;KAKZ,YAAY;;;KAGZ,YAAY;;;;KAIZ,YAAY;;;;AAIf,UAAS,KAAK,YAAY,MAAM;AAChC,kBAAiB;;;;;;;;;;;;;;AC1anB,MAAM,WAAW,aAAa,gBAAgB;;;;;;;;;;AAqB9C,SAAgB,iBACd,MACA,GACA,GACA,QACA,QACA,cACmB;CACnB,IAAI,qBAAqB;CACzB,IAAI,mBAAmB;CACvB,MAAM,UAAU,cAAc,SAAS;AACvC,KAAI,SAAS,WAAW,SAAS,YAC/B,UAAS,QAAQ,oBAAoB,KAAK,YAAY,QAAQ,sBAAsB,cAAc,QAAQ;AAG5G,QAAO;EACL;EACA,SAAS;EACT,SAAS;EACT,QAAQ,OAAO;EACf,QAAQ,OAAO;EACf,SAAS,OAAO;EAChB;EACA,UAAU,OAAO;EACjB;EACA,eAAe;EACf,aAAa;EACb,IAAI,qBAAqB;AACvB,UAAO;;EAET,IAAI,mBAAmB;AACrB,UAAO;;EAET,kBAAkB;AAChB,wBAAqB;;EAEvB,iBAAiB;AACf,sBAAmB;;EAEtB;;;;;AAMH,SAAgB,iBACd,GACA,GACA,QACA,QACA,cACmB;CACnB,MAAM,OAAO,iBAAiB,SAAS,GAAG,GAAG,QAAQ,QAAQ,aAAa;AAC1E,MAAK,SAAS,OAAO,SAAS;AAC9B,MAAK,SAAS;AACd,QAAO;;;;;;;AAYT,SAAgB,QAAQ,MAAc,GAAW,GAA0B;CACzE,MAAM,OAAO,KAAK;AAClB,KAAI,CAAC,KAAM,QAAO;AAGlB,KAAI,CAAC,YAAY,GAAG,GAAG,KAAK,CAAE,QAAO;CAGrC,MAAM,QAAQ,KAAK;AACnB,KAAI,MAAM,kBAAkB,OAAQ,QAAO;CAI3C,MAAM,QAAQ,MAAM,aAAa,YAAY,MAAM,aAAa;AAGhE,MAAK,IAAI,IAAI,KAAK,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;EAClD,MAAM,QAAQ,KAAK,SAAS;AAE5B,MAAI;OACgB,MAAM,cACP,CAAC,YAAY,GAAG,GAAG,KAAK,CACvC;;EAGJ,MAAM,MAAM,QAAQ,OAAO,GAAG,EAAE;AAChC,MAAI,IAAK,QAAO;;AAKlB,KAAI,KAAK,SAAS,eAChB,MAAK,IAAI,IAAI,KAAK,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;EAClD,MAAM,QAAQ,KAAK,SAAS;AAC5B,MAAI,MAAM;QACH,MAAM,cAAc,MAAM,YAC7B,KAAI,YAAY,GAAG,GAAG,WAAW,CAAE,QAAO;;;AAOlD,QAAO;;;;;;AAWT,SAAgB,kBAAkB,MAA2C;CAC3E,IAAI,UAAyB;AAC7B,QAAO,SAAS;EAEd,MAAM,QADQ,QAAQ,MACF;AACpB,MAAI,UAAU,UAAU,UAAU,UAAU,UAAU,UAAW,QAAO;AAExE,YAAU,QAAQ;;AAGpB,QAAO;;;;;;;;;AAUT,SAAgB,iBAAiB,MAAc,GAAW,GAA0B;CAClF,MAAM,OAAO,KAAK;AAClB,KAAI,CAAC,KAAM,QAAO;AAElB,KAAI,CAAC,YAAY,GAAG,GAAG,KAAK,CAAE,QAAO;CAIrC,MAAM,QAAQ,KAAK;AAEnB,KADiB,kBAAkB,KAAK,KACvB,OAAQ,QAAO;CAGhC,MAAM,QAAQ,MAAM,aAAa,YAAY,MAAM,aAAa;AAGhE,MAAK,IAAI,IAAI,KAAK,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;EAClD,MAAM,QAAQ,KAAK,SAAS;AAC5B,MAAI;OACgB,MAAM,cACP,CAAC,YAAY,GAAG,GAAG,KAAK,CACvC;;EAGJ,MAAM,MAAM,iBAAiB,OAAO,GAAG,EAAE;AACzC,MAAI,IAAK,QAAO;;AAIlB,KAAI,KAAK,SAAS,eAChB,MAAK,IAAI,IAAI,KAAK,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;EAClD,MAAM,QAAQ,KAAK,SAAS;AAC5B,MAAI,MAAM;QACH,MAAM,cAAc,MAAM,YAC7B,KAAI,YAAY,GAAG,GAAG,WAAW,CAAE,QAAO;;;AAMlD,QAAO;;;;;;;AAQT,SAAgB,oBAAoB,MAAqC;CACvE,IAAI,UAAyB;AAC7B,QAAO,SAAS;AAEd,MADc,QAAQ,MACZ,eAAe,WAAW;GAClC,MAAM,OAAO,QAAQ;AACrB,OAAI,KACF,QAAO;IACL,KAAK,KAAK;IACV,QAAQ,KAAK,IAAI,KAAK,SAAS;IAC/B,MAAM,KAAK;IACX,OAAO,KAAK,IAAI,KAAK,QAAQ;IAC9B;;AAGL,YAAU,QAAQ;;AAEpB,QAAO;;;AAuBT,MAAM,oBAAoE;CACxE,OAAO;CACP,UAAU;CACV,WAAW;CACX,SAAS;CACT,WAAW;CACX,YAAY;CACZ,YAAY;CACZ,OAAO;CACR;;;;;;;AAQD,SAAgB,mBAAmB,OAAgC;CACjE,MAAM,cAAc,kBAAkB,MAAM;AAC5C,KAAI,CAAC,YAAa;AAKlB,KAFiB,MAAM,SAAS,gBAAgB,MAAM,SAAS,cAEjD;EAEZ,MAAM,UAAW,MAAM,OAAO,MAAkC;AAGhE,MAAI,SAAS;GACX,MAAM,eAAe;AACrB,gBAAa,gBAAgB,MAAM;AACnC,WAAQ,MAAM;;AAEhB;;CAIF,MAAM,OAAO,gBAAgB,MAAM,OAAO;AAC1C,MAAK,MAAM,QAAQ,MAAM;AACvB,MAAI,MAAM,mBAAoB;EAE9B,MAAM,UAAW,KAAK,MAAkC;AACxD,MAAI,SAAS;GACX,MAAM,eAAe;AACrB,gBAAa,gBAAgB;AAC7B,WAAQ,MAAM;;;;AAgBpB,SAAgB,yBAA2C;AACzD,QAAO;EACL,eAAe;EACf,YAAY;EACZ,YAAY;EACZ,iBAAiB;EAClB;;AAGH,MAAM,uBAAuB;AAC7B,MAAM,wBAAwB;;;;;;AAO9B,SAAgB,iBACd,OACA,GACA,GACA,QACA,MAAc,KAAK,KAAK,EACf;CACT,MAAM,YAAY,MAAM,MAAM;CAC9B,MAAM,KAAK,KAAK,IAAI,IAAI,MAAM,WAAW;CACzC,MAAM,KAAK,KAAK,IAAI,IAAI,MAAM,WAAW;CAGzC,MAAM,WAFa,WAAW,MAAM,mBAGpB,aAAa,wBAAwB,MAAM,yBAAyB,MAAM;AAG1F,OAAM,gBAAgB;AACtB,OAAM,aAAa;AACnB,OAAM,aAAa;AACnB,OAAM,kBAAkB;AAGxB,KAAI,SACF,OAAM,gBAAgB;AAGxB,QAAO;;;;;;;;;AAcT,SAAgB,kBAAkB,UAAoB,UAA2D;CAC/G,MAAM,UAAU,IAAI,IAAI,SAAS;CACjC,MAAM,UAAU,IAAI,IAAI,SAAS;AAKjC,QAAO;EAAE,SAHO,SAAS,QAAQ,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;EAGrC,MAFL,SAAS,QAAQ,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;EAE5B;;AA2C1B,SAAgB,0BAA0B,SAAgE;AACxG,QAAO;EACL,aAAa,wBAAwB;EACrC,WAAW,EAAE;EACb,iBAAiB;EACjB,cAAc,SAAS;EACvB,mBAAmB;GAAE,OAAO;GAAO,OAAO;GAAO,UAAU;GAAO,SAAS;GAAO;EACnF;;;;;;AAOH,SAAgB,wBACd,OACA,KAOM;CAEN,MAAM,YAAY,IAAI,cAAc;CACpC,MAAM,YAAY,MAAM,kBAAkB;AAC1C,KAAI,IAAI,UAAU,KAAA,EAAW,OAAM,kBAAkB,QAAQ,YAAY,QAAQ,IAAI;AACrF,KAAI,IAAI,UAAU,KAAA,EAAW,OAAM,kBAAkB,QAAQ,YAAY,QAAQ,IAAI;AACrF,KAAI,IAAI,aAAa,KAAA,EAAW,OAAM,kBAAkB,WAAW,IAAI;AACvE,KAAI,IAAI,YAAY,KAAA,EAAW,OAAM,kBAAkB,UAAU,IAAI;AACrE,KAAI,MAAM,kBAAkB,UAAU,UACpC,UAAS,QACP,4BAA4B,UAAU,KAAK,MAAM,kBAAkB,MAAM,cAAc,IAAI,MAAM,cAAc,IAAI,UAAU,GAC9H;;;;;;;;;;;;AAcL,SAAgB,kBAAkB,OAAiC,QAAqB,MAAuB;CAC7G,MAAM,EAAE,GAAG,GAAG,WAAW;CACzB,MAAM,SAAS,QAAQ,MAAM,GAAG,EAAE;AAClC,KAAI,WAAW,QAAQ;EACrB,MAAM,WAAW,QAAQ,QAAQ;EACjC,MAAM,SAAS,SAAW,OAAO,MAAkC,MAAM,KAAM;EAE/E,IAAI,gBAAgB;AACpB,MAAI,QAAQ;GACV,IAAI,IAAmB;AACvB,UAAO,GAAG;AACR,QAAI,kBAAmB,EAAE,OAAmC;AAC1D,qBAAgB,GAAG,EAAE,KAAK,GAAI,EAAE,MAAkC,MAAM;AACxE;;AAEF,QAAI,EAAE;;;EAGV,MAAM,UAAU,SAAS,gBAAgB,OAAO,GAAG,EAAE;EACrD,MAAM,EAAE,YAAY,kBAAkB,MAAM,WAAW,QAAQ;AAC/D,WAAS,QACP,UAAU,EAAE,KAAK,EAAE,UAAU,SAAS,GAAG,OAAO,iBAAiB,iBAAiB,OAAO,WAAW,QAAQ,OAAO,YAAY,MAAM,UAAU,SAChJ;;AAEH,KAAI,CAAC,OAAQ,QAAO;CACpB,IAAI,mBAAmB;AAEvB,KAAI,WAAW,QAAQ;AACrB,QAAM,kBAAkB;AAGxB,WAAS,QAAQ,KAAK;AAGtB,MAAI,MAAM,cAAc;GACtB,MAAM,YAAY,sBAAsB,OAAO;AAC/C,OAAI,UACF,OAAM,aAAa,MAAM,WAAW,QAAQ;;EAIhD,MAAM,QAAQ,iBAAiB,aAAa,GAAG,GAAG,QAAQ,QAAQ,MAAM,kBAAkB;AAC1F,qBAAmB,MAAM;AACzB,MAAI,MAAM,iBAAkB,oBAAmB;YACtC,WAAW,MAAM;AAE1B,MAAI,MAAM,gBACR,UAAS,MAAM,iBAAiB,MAAM;AAIxC,qBADc,iBAAiB,WAAW,GAAG,GAAG,QAAQ,QAAQ,MAAM,kBAAkB,CAC/D;AAMzB,MAAI,MAAM,iBAAiB;GACzB,MAAM,aAAa,iBAAiB,SAAS,GAAG,GAAG,QAAQ,QAAQ,MAAM,kBAAkB;AAC3F,sBAAmB,WAAW;AAC9B,OAAI,WAAW,iBAAkB,oBAAmB;AAIpD,OADiB,iBAAiB,MAAM,aAAa,GAAG,GAAG,OAAO,OAAO,EAC3D;IACZ,MAAM,WAAW,iBAAiB,YAAY,GAAG,GAAG,QAAQ,QAAQ,MAAM,kBAAkB;AAC5F,uBAAmB,SAAS;AAC5B,QAAI,SAAS,iBAAkB,oBAAmB;;;AAItD,QAAM,kBAAkB;YACf,WAAW,QAAQ;AAE5B,qBADc,iBAAiB,aAAa,GAAG,GAAG,QAAQ,QAAQ,MAAM,kBAAkB,CACjE;EAGzB,MAAM,UAAU,gBAAgB,OAAO;EACvC,MAAM,EAAE,SAAS,SAAS,kBAAkB,MAAM,WAAW,QAAQ;AAGrE,OAAK,MAAM,QAAQ,MAAM;AACvB,cAAW,MAAM,MAAM;AAEvB,sBADmB,iBAAiB,cAAc,GAAG,GAAG,MAAM,QAAQ,MAAM,kBAAkB,CAChE;;AAIhC,OAAK,MAAM,QAAQ,QAAQ,SAAS,EAAE;AACpC,cAAW,MAAM,KAAK;AAEtB,sBADmB,iBAAiB,cAAc,GAAG,GAAG,MAAM,QAAQ,MAAM,kBAAkB,CAChE;;AAGhC,QAAM,YAAY;YACT,WAAW,SAAS;EAC7B,MAAM,QAAQ,iBAAiB,GAAG,GAAG,QAAQ,QAAQ,MAAM,kBAAkB;AAC7E,qBAAmB,MAAM;AACzB,MAAI,MAAM,iBAAkB,oBAAmB;;AAEjD,QAAO;;;;ACtiBT,IAAI,mBAAmB;AACvB,IAAI,kBAAyC,QAAQ;;AAOrD,SAAgB,gBAAgB,SAAkC;AAChE,oBAAmB;AACnB,KAAI,SAAS,QAGX,mBAAA,UADwE,UAAU,CAC7D,kBAAkB,QAAQ,SAAS,EAAE,OAAO,KAAK,CAAC;UAC9D,SAAS,OAClB,mBAAkB,QAAQ;KAE1B,mBAAkB,QAAQ;;;AAK9B,SAAgB,mBAAyB;AACvC,oBAAmB;;;AAIrB,SAAgB,qBAA8B;AAC5C,QAAO;;;;;;;;AAST,SAAgB,aAAa,OAA0B;AACrD,KAAI,CAAC,iBAAkB;CACvB,MAAM,OACJ,oBAAoB,MAAM,YAAY,GACnC,MAAM,eAAe,QAAQ,EAAE,CAAC,SAC5B,MAAM,cAAc,QAAQ,EAAE,CAAC,aAC3B,MAAM,aAAa;AAChC,iBAAgB,MAAM,KAAK;;;;;;;;AAS7B,SAAgB,YAAY,UAAkB,SAA4D;CACxG,MAAM,WAAW,SAAS,SAAS;CACnC,MAAM,aAAa,SAAS,cAAc;CAC1C,MAAM,QAAkB,EAAE;CAE1B,SAAS,KAAK,MAAc,QAAsB;AAChD,MAAI,SAAS,SAAU;EAEvB,MAAM,SAAS,KAAK,OAAO,OAAO;EAClC,MAAM,OAAO,KAAK;EAClB,MAAM,SAAU,KAAK,OAAmC;EACxD,MAAM,QAAQ,SAAS,KAAK,WAAW;EAGvC,IAAI,UAAU;AACd,MAAI;OACE,KAAK,SAAS;IAChB,MAAM,IAAI,KAAK;AACf,cAAU,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,EAAE,OAAO;cACxC,KAAK,YAAY;IAC1B,MAAM,KAAK,KAAK;AAChB,cAAU,KAAK,GAAG,iBAAiB,CAAC,GAAG,GAAG,gBAAgB,CAAC,GAAG,GAAG,kBAAkB,CAAC,GAAG,GAAG,mBAAmB,CAAC;;;EAKlH,MAAM,aAAuB,EAAE;AAC/B,MAAI,KAAK,YAAa,YAAW,KAAK,SAAS;AAC/C,MAAI,QAAQ,KAAK,WAAW,KAAK,YAAA,EAAwB,CAAE,YAAW,KAAK,UAAU;AACrF,MAAI,QAAQ,KAAK,WAAW,KAAK,YAAA,EAA4B,CAAE,YAAW,KAAK,QAAQ;AACvF,MAAI,QAAQ,KAAK,WAAW,KAAK,YAAA,EAAmB,CAAE,YAAW,KAAK,KAAK;AAC3E,MAAI,QAAQ,KAAK,WAAW,KAAK,YAAA,GAAwB,CAAE,YAAW,KAAK,UAAU;AACrF,MAAI,QAAQ,KAAK,WAAW,KAAK,YAAA,EAAyB,CAAE,YAAW,KAAK,WAAW;EACvF,MAAM,WAAW,WAAW,SAAS,IAAI,WAAW,WAAW,KAAK,IAAI,CAAC,KAAK;EAG9E,MAAM,UAAU,KAAK,cACjB,KAAK,KAAK,YAAY,MAAM,GAAG,GAAG,GAAG,KAAK,YAAY,SAAS,KAAK,QAAQ,GAAG,KAC/E;AAEJ,QAAM,KAAK,GAAG,SAAS,OAAO,QAAQ,UAAU,WAAW,UAAU;AAErE,OAAK,MAAM,SAAS,KAAK,SACvB,MAAK,OAAO,SAAS,EAAE;;AAI3B,MAAK,UAAU,EAAE;AACjB,QAAO,MAAM,KAAK,KAAK;;;;;;;AAQzB,SAAgB,sBAA4B;AAC1C,KAAI,QAAQ,IAAI,gBAAgB,OAAO,QAAQ,IAAI,gBAAgB,QAAQ;EACzE,MAAM,UAAU,QAAQ,IAAI;AAC5B,kBAAgB,UAAU,EAAE,SAAS,GAAG,KAAA,EAAU;;;;;;ACvCtD,MAAa,OAAe,EAAE,MAAM,QAAQ;;;;;;;;;;;ACrD5C,SAAgB,sBAA8F;AAC5G,SAAQ,iBAAiB,KAAK,UAAU;AACtC,UAAQ,IAAI,MAAZ;GACE,KAAK,SAAS;IACZ,MAAM,WAAW;AAkBjB,WAAO,CAjBU;KACf,GAAG;KACH,OAAO;MACL,GAAG,MAAM;MACT,YAAY,MAAM,MAAM;MACxB,UAAU,SAAS;MACnB,QAAQ,SAAS,UAAU;MAE3B,aACE,MAAM,MAAM,WAAW,SAAS,IAC5B;OACE,GAAG,MAAM,MAAM;QACd,MAAM,MAAM,WAAW,MAAM,MAAM,WAAW,SAAS,KAAM,SAAS;OACxE,GACD,MAAM,MAAM;MACnB;KACF,EAC0B,CAAC,KAAK,CAAC;;GAGpC,KAAK,OAUH,QAAO,CATU;IACf,GAAG;IACH,OAAO;KACL,GAAG,MAAM;KACT,YAAY,MAAM,MAAM;KACxB,UAAU;KACV,QAAQ;KACT;IACF,EAC0B,CAAC,KAAK,CAAC;GAGpC,KAAK,eAAe;IAClB,MAAM,WAAW;AAQjB,WAAO,CAPU;KACf,GAAG;KACH,OAAO;MACL,GAAG,MAAM;MACT,YAAY,CAAC,GAAG,MAAM,MAAM,YAAY,SAAS,QAAQ;MAC1D;KACF,EAC0B,CAAC,KAAK,CAAC;;GAGpC,KAAK,aAQH,QAAO,CAPU;IACf,GAAG;IACH,OAAO;KACL,GAAG,MAAM;KACT,YAAY,MAAM,MAAM,WAAW,MAAM,GAAG,GAAG;KAChD;IACF,EAC0B,CAAC,KAAK,CAAC;GAGpC,QACE,QAAO,YAAY,KAAK,MAAM;;;;;;;;;;AAetC,SAAgB,cACd,MACA,OACmB;AACnB,QAAO,CAAC,OAAO,CAAC,KAAK,CAAC;;;;;AAUxB,SAAgB,cAAwC;AACtD,QAAO,CACL,EACE,OAAO;EACL,UAAU;EACV,YAAY;EACZ,QAAQ;EACR,YAAY,EAAE;EACd,aAAa,EAAE;EAChB,EACF,EACD,CAAC,KAAK,CACP;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCH,SAAgBG,cACd,QACsB;CAEtB,MAAM,CAAC,cAAc,kBAAkB,OAAO,MAAM;CACpD,IAAI,QAAQ;CAGZ,MAAM,4BAAY,IAAI,KAAiB;CAEvC,SAAS,SAAe;AACtB,OAAK,MAAM,YAAY,UACrB,WAAU;;CAKd,IAAI,gBAAgB;CACpB,MAAM,gBAAuB,EAAE;CAE/B,SAAS,eAAe,SAAyB;AAC/C,OAAK,MAAM,UAAU,QACnB,eAAc,OAAO;;CAIzB,SAAS,cAAc,QAAsB;AAC3C,UAAQ,OAAO,MAAf;GACE,KAAK,OACH;GACF,KAAK;AACH,mBAAe,OAAO,QAAQ;AAC9B;GACF,KAAK;AAEH,kBAAc,KAAK,OAAO,IAAW;AACrC;;;CAIN,SAAS,SAAS,KAAgB;AAChC,MAAI,eAAe;AAEjB,iBAAc,KAAK,IAAI;AACvB;;AAGF,kBAAgB;AAChB,MAAI;GAEF,MAAM,CAAC,UAAU,WAAW,OAAO,OAAO,KAAK,MAAM;GACrD,MAAM,UAAU,aAAa;AAC7B,WAAQ;AAGR,kBAAe,QAAQ;AAGvB,OAAI,QACF,SAAQ;AAIV,UAAO,cAAc,SAAS,GAAG;IAC/B,MAAM,SAAS,cAAc,OAAO;IACpC,MAAM,CAAC,WAAW,eAAe,OAAO,OAAO,QAAQ,MAAM;IAC7D,MAAM,cAAc,cAAc;AAClC,YAAQ;AACR,mBAAe,YAAY;AAC3B,QAAI,YACF,SAAQ;;YAGJ;AACR,mBAAgB;;;CAIpB,SAAS,WAAkB;AACzB,SAAO;;CAGT,SAAS,UAAU,UAAkC;AACnD,YAAU,IAAI,SAAS;AACvB,eAAa;AACX,aAAU,OAAO,SAAS;;;CAI9B,SAAS,YAAe,UAAkC;AACxD,SAAO,SAAS,MAAM;;AAIxB,gBAAe,eAAe;AAC9B,QAAO,cAAc,SAAS,GAAG;EAC/B,MAAM,SAAS,cAAc,OAAO;EACpC,MAAM,CAAC,WAAW,eAAe,OAAO,OAAO,QAAQ,MAAM;AAC7D,UAAQ;AACR,iBAAe,YAAY;;AAI7B,QAAO;EACL;EACA;EACA;EACA;EACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtQH,gBAAuB,MAAS,GAAG,SAAiE;AAClG,KAAI,QAAQ,WAAW,EAAG;CAG1B,MAAM,YAAY,QAAQ,KAAK,WAAW,OAAO,OAAO,gBAAgB,CAAC;CACzE,MAAM,0BAAU,IAAI,KAA6E;CAEjG,eAAe,cAAc,KAA6E;EACxG,MAAM,WAAW,UAAU;AAC3B,MAAI,CAAC,SAAU,OAAM,IAAI,MAAM,wBAAwB,MAAM;AAE7D,SAAO;GAAE,OAAO;GAAK,QADN,MAAM,SAAS,MAAM;GACP;;AAI/B,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,IACpC,SAAQ,IAAI,GAAG,cAAc,EAAE,CAAC;AAGlC,KAAI;AACF,SAAO,QAAQ,OAAO,GAAG;GAEvB,MAAM,EAAE,OAAO,WAAW,MAAM,QAAQ,KAAK,QAAQ,QAAQ,CAAC;AAE9D,OAAI,OAAO,KAET,SAAQ,OAAO,MAAM;QAChB;AAEL,UAAM,OAAO;AACb,YAAQ,IAAI,OAAO,cAAc,MAAM,CAAC;;;WAGpC;AAER,QAAM,QAAQ,IAAI,UAAU,KAAK,OAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,SAAS,CAAE,CAAC;;;;;;;;;;;AAY3F,gBAAuB,IAAU,QAA0B,IAAyD;CAClH,MAAM,WAAW,OAAO,OAAO,gBAAgB;AAC/C,KAAI;AAEF,aAAW,MAAM,SAAS,GAAG,OAAO,sBAAsB,UAAU,CAClE,OAAM,GAAG,MAAM;WAET;AACR,MAAI,SAAS,OACX,OAAM,SAAS,QAAQ;;;;;;;;;;;AAa7B,gBAAuB,OACrB,QACA,WACoC;CACpC,MAAM,WAAW,OAAO,OAAO,gBAAgB;AAC/C,KAAI;AACF,aAAW,MAAM,SAAS,GAAG,OAAO,sBAAsB,UAAU,CAClE,KAAI,UAAU,MAAM,CAClB,OAAM;WAGF;AACR,MAAI,SAAS,OACX,OAAM,SAAS,QAAQ;;;;;;;;;;;;;AAe7B,gBAAuB,UACrB,QACA,IACoC;CACpC,MAAM,WAAW,OAAO,OAAO,gBAAgB;AAC/C,KAAI;AACF,aAAW,MAAM,SAAS,GAAG,OAAO,sBAAsB,UAAU,EAAE;GACpE,MAAM,SAAS,GAAG,MAAM;AACxB,OAAI,WAAW,KAAA,EACb,OAAM;;WAGF;AACR,MAAI,SAAS,OACX,OAAM,SAAS,QAAQ;;;;;;;;;;;;;;;;;AAmB7B,gBAAuB,UAAa,QAA0B,QAAyD;AACrH,KAAI,OAAO,QAAS;CAEpB,MAAM,WAAW,OAAO,OAAO,gBAAgB;CAG/C,IAAI;CACJ,MAAM,eAAe,IAAI,SAAe,YAAY;AAClD,iBAAe;GACf;CACF,MAAM,gBAAgB,cAAc;AACpC,QAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;AAEzD,KAAI;AACF,SAAO,CAAC,OAAO,SAAS;GAEtB,MAAM,SAAS,MAAM,QAAQ,KAAK,CAChC,SAAS,MAAM,EACf,aAAa,YAAY;IAAE,MAAM;IAAM,OAAO,KAAA;IAAW,EAAW,CACrE,CAAC;AAEF,OAAI,OAAO,KAAM;AACjB,SAAM,OAAO;;WAEP;AACR,SAAO,oBAAoB,SAAS,QAAQ;AAC5C,MAAI,SAAS,OACX,OAAM,SAAS,QAAQ;;;;;;;;;;;AAa7B,gBAAuB,KAAQ,QAA0B,OAAmD;AAC1G,KAAI,SAAS,EAAG;CAEhB,MAAM,WAAW,OAAO,OAAO,gBAAgB;CAC/C,IAAI,QAAQ;AAEZ,KAAI;AACF,aAAW,MAAM,SAAS,GAAG,OAAO,sBAAsB,UAAU,EAAE;AACpE,SAAM;AACN;AACA,OAAI,SAAS,MAAO;;WAEd;AACR,MAAI,SAAS,OACX,OAAM,SAAS,QAAQ;;;;;;;;;;;;;;AAgB7B,gBAAuB,UAAa,OAAgD;AAClF,MAAK,MAAM,QAAQ,MACjB,OAAM;;;;;;;;;;AAYV,gBAAuB,mBAAsB,OAAY,SAAqD;AAC5G,MAAK,MAAM,QAAQ,OAAO;AACxB,QAAM,IAAI,SAAS,YAAY,WAAW,SAAS,QAAQ,CAAC;AAC5D,QAAM;;;;;;;;;;;;;;AAeV,gBAAuB,SAAY,QAA0B,IAAgD;CAC3G,MAAM,WAAW,OAAO,OAAO,gBAAgB;CAC/C,IAAI,WAAW;AAEf,KAAI;AACF,aAAW,MAAM,SAAS,GAAG,OAAO,sBAAsB,UAAU,EAAE;GACpE,MAAM,MAAM,KAAK,KAAK;AACtB,OAAI,MAAM,YAAY,IAAI;AACxB,eAAW;AACX,UAAM;;;WAGF;AACR,MAAI,SAAS,OACX,OAAM,SAAS,QAAQ;;;;;;;;;;;;;;;;AAkB7B,gBAAuB,SAAY,QAA0B,IAAgD;CAC3G,MAAM,WAAW,OAAO,OAAO,gBAAgB;CAC/C,IAAI;AAEJ,KAAI;AACF,aAAW,MAAM,SAAS,GAAG,OAAO,sBAAsB,UAAU,CAClE,QAAO,EAAE,OAAO;AAGlB,MAAI,MAAM;AACR,SAAM,IAAI,SAAS,YAAY,WAAW,SAAS,GAAG,CAAC;AACvD,SAAM,KAAK;;WAEL;AACR,MAAI,SAAS,OACX,OAAM,SAAS,QAAQ;;;;;;;;;;;;;AAe7B,SAAgB,MAAS,QAA0B,MAAoD;AACrG,KAAI,QAAQ,EAAG,OAAM,IAAI,MAAM,8BAA8B;AAC7D,QAAO,UAAU,QAAQ,KAAK;;AAGhC,gBAAgB,UAAa,QAA0B,MAAoD;CACzG,MAAM,WAAW,OAAO,OAAO,gBAAgB;CAC/C,IAAI,SAAc,EAAE;AAEpB,KAAI;AACF,aAAW,MAAM,SAAS,GAAG,OAAO,sBAAsB,UAAU,EAAE;AACpE,UAAO,KAAK,MAAM;AAClB,OAAI,OAAO,UAAU,MAAM;AACzB,UAAM;AACN,aAAS,EAAE;;;AAIf,MAAI,OAAO,SAAS,EAClB,OAAM;WAEA;AACR,MAAI,SAAS,OACX,OAAM,SAAS,QAAQ;;;;;;;;;;;AAa7B,gBAAuB,OAAU,GAAG,SAAiE;AACnG,MAAK,MAAM,UAAU,QACnB,QAAO;;;;;;;;;;;AAaX,gBAAuB,IACrB,GAAG,SACiC;CACpC,MAAM,YAAY,QAAQ,KAAK,WAAW,OAAO,OAAO,gBAAgB,CAAC;AAEzE,KAAI;AACF,SAAO,MAAM;GACX,MAAM,UAAU,MAAM,QAAQ,IAAI,UAAU,KAAK,OAAO,GAAG,MAAM,CAAC,CAAC;AAGnE,OAAI,QAAQ,MAAM,MAAM,EAAE,KAAK,CAAE;AAEjC,SAAM,QAAQ,KAAK,MAAM,EAAE,MAAM;;WAE3B;AACR,QAAM,QAAQ,IAAI,UAAU,KAAK,OAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,SAAS,CAAE,CAAC;;;;;;ACvX3F,SAAgB,2BAA+C;CAC7D,MAAM,+BAAe,IAAI,KAAsB;AAE/C,QAAO;EACL,SAAY,KAAa,YAAqB;AAC5C,gBAAa,IAAI,KAAK,WAAW;;EAGnC,IAAO,KAA4B;AACjC,UAAO,aAAa,IAAI,IAAI;;EAE/B;;;;;;;;;;;;;AC5BH,MAAa,uBAAuB,OAAO,IAAI,oBAAoB;;;;;;;AC2EnE,SAAS,WAAW,IAAqB;AACvC,QAAO,KAAK,KAAK,GAAG;;;;;;;AAQtB,SAAgB,iBACd,QACA,KACA,KACsC;CACtC,MAAM,QAAQ,OAAO;CACrB,MAAM,KAAK,OAAO,QAAQ,KAAK,IAAI,CAAC;AAEpC,KAAI,WAAW,GAAG,EAAE;EAElB,IAAI,WAAW;AACf,SAAO,WAAW,KAAK,WAAW,OAAO,QAAQ,WAAW,GAAG,IAAI,CAAC,KAAK,CACvE;EAGF,IAAI,SAAS;AACb,SAAO,SAAS,QAAQ,KAAK,WAAW,OAAO,QAAQ,SAAS,GAAG,IAAI,CAAC,KAAK,CAC3E;AAEF,SAAO;GAAE;GAAU;GAAQ;;AAI7B,QAAO;EAAE,UAAU;EAAK,QAAQ;EAAK;;;;;;;AAYvC,SAAgB,iBAAiB,QAAwB,KAAmD;CAC1G,MAAM,QAAQ,OAAO;CAGrB,IAAI,SAAS,QAAQ;AACrB,QAAO,SAAS,KAAK,OAAO,QAAQ,QAAQ,IAAI,CAAC,KAAK,MAAM,KAAK,GAC/D;CAIF,IAAI,WAAW;AACf,QAAO,WAAW,UAAU,OAAO,QAAQ,UAAU,IAAI,CAAC,KAAK,MAAM,KAAK,GACxE;AAIF,KAAI,YAAY,UAAU,OAAO,QAAQ,UAAU,IAAI,CAAC,KAAK,MAAM,KAAK,GACtE,QAAO;EAAE,UAAU;EAAG,QAAQ,QAAQ;EAAG;AAG3C,QAAO;EAAE;EAAU;EAAQ;;;;;;;AAY7B,SAAS,oBACP,KACA,KACA,QACA,aACA,QACmB;AACnB,KAAI,gBAAgB,eAAe,CAAC,OAClC,QAAO;EAAE;EAAK;EAAK;AAGrB,KAAI,gBAAgB,QAAQ;EAC1B,MAAM,EAAE,UAAU,WAAW,iBAAiB,QAAQ,KAAK,IAAI;AAG/D,SADkB,MAAM,OAAO,OAAQ,QAAQ,OAAO,OAAO,OAAO,OAAO,MACxD;GAAE,KAAK;GAAQ;GAAK,GAAG;GAAE,KAAK;GAAU;GAAK;;AAGlE,KAAI,gBAAgB,QAAQ;EAC1B,MAAM,EAAE,UAAU,WAAW,iBAAiB,QAAQ,IAAI;AAE1D,SADkB,MAAM,OAAO,OAAQ,QAAQ,OAAO,OAAO,OAAO,OAAO,MACxD;GAAE,KAAK;GAAQ;GAAK,GAAG;GAAE,KAAK;GAAU;GAAK;;AAGlE,QAAO;EAAE;EAAK;EAAK;;AAOrB,SAAgB,+BAAuD;AACrE,QAAO;EAAE,OAAO;EAAM,WAAW;EAAO,QAAQ;EAAM,aAAa;EAAa,OAAO;EAAM;;;;;AAU/F,SAAS,aAAa,KAAa,KAAa,OAAiD;AAC/F,KAAI,CAAC,MAAO,QAAO;EAAE;EAAK;EAAK;AAC/B,QAAO;EACL,KAAK,KAAK,IAAI,MAAM,MAAM,KAAK,IAAI,MAAM,OAAO,IAAI,CAAC;EACrD,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,QAAQ,IAAI,CAAC;EACtD;;AAGH,SAAgB,wBACd,QACA,OAC6C;AAC7C,SAAQ,OAAO,MAAf;EACE,KAAK,SAAS;GACZ,MAAM,QAAQ,OAAO,SAAS;GAC9B,MAAM,MAAM,aAAa,OAAO,KAAK,OAAO,KAAK,MAAM;AACvD,UAAO,CACL;IACE,OAAO;KAAE,QAAQ;KAAK,MAAM;KAAK;IACjC,WAAW;IACX,QAAQ,OAAO,UAAU;IACzB,aAAa;IACb;IACD,EACD,CAAC,EAAE,MAAM,UAAU,CAAC,CACrB;;EAGH,KAAK,aAAa;GAChB,MAAM,QAAQ,OAAO,SAAS;GAC9B,MAAM,EAAE,UAAU,WAAW,iBAAiB,OAAO,QAAQ,OAAO,KAAK,OAAO,IAAI;AAGpF,UAAO,CACL;IACE,OAAO;KAAE,QAJK,aAAa,UAAU,OAAO,KAAK,MAAM;KAI3B,MAHhB,aAAa,QAAQ,OAAO,KAAK,MAAM;KAGR;IAC3C,WAAW;IACX,QAAQ,OAAO,UAAU;IACzB,aAAa;IACb;IACD,EACD,CAAC,EAAE,MAAM,UAAU,CAAC,CACrB;;EAGH,KAAK,aAAa;GAChB,MAAM,QAAQ,OAAO,SAAS;GAC9B,MAAM,EAAE,UAAU,WAAW,iBAAiB,OAAO,QAAQ,OAAO,IAAI;AAGxE,UAAO,CACL;IACE,OAAO;KAAE,QAJK,aAAa,UAAU,OAAO,KAAK,MAAM;KAI3B,MAHhB,aAAa,QAAQ,OAAO,KAAK,MAAM;KAGR;IAC3C,WAAW;IACX,QAAQ,OAAO,UAAU;IACzB,aAAa;IACb;IACD,EACD,CAAC,EAAE,MAAM,UAAU,CAAC,CACrB;;EAGH,KAAK,UAAU;AACb,OAAI,CAAC,MAAM,UAAW,QAAO,CAAC,OAAO,EAAE,CAAC;GACxC,MAAM,WAAW,oBACf,OAAO,KACP,OAAO,KACP,MAAM,MAAO,QACb,MAAM,aACN,OAAO,OACR;GACD,MAAM,OAAO,aAAa,SAAS,KAAK,SAAS,KAAK,MAAM,MAAM;AAClE,UAAO,CACL;IACE,GAAG;IACH,OAAO;KAAE,QAAQ,MAAM,MAAO;KAAQ;KAAM;IAC5C,WAAW;IACZ,EACD,CAAC,EAAE,MAAM,UAAU,CAAC,CACrB;;EAGH,KAAK;AACH,OAAI,CAAC,MAAM,MAAO,QAAO,CAAC;IAAE,GAAG;IAAO,WAAW;IAAO,EAAE,EAAE,CAAC;AAC7D,UAAO,CAAC;IAAE,GAAG;IAAO,OAAO,MAAM;IAAO,WAAW;IAAO,EAAE,EAAE,CAAC;EAGjE,KAAK,SAAS;GACZ,MAAM,WAAW,MAAM,UAAU;AACjC,UAAO,CAAC,8BAA8B,EAAE,WAAW,CAAC,EAAE,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC;;;;AASnF,SAAgB,eAAe,OAK7B;CACA,MAAM,EAAE,QAAQ,SAAS;AAEzB,KAAI,OAAO,MAAM,KAAK,OAAQ,OAAO,QAAQ,KAAK,OAAO,OAAO,OAAO,KAAK,IAC1E,QAAO;EAAE,UAAU,OAAO;EAAK,UAAU,OAAO;EAAK,QAAQ,KAAK;EAAK,QAAQ,KAAK;EAAK;AAG3F,QAAO;EAAE,UAAU,KAAK;EAAK,UAAU,KAAK;EAAK,QAAQ,OAAO;EAAK,QAAQ,OAAO;EAAK;;;;;;;;;;;;;AAgC3F,SAAgB,YAAY,QAAwB,OAAuB,SAAsC;CAC/G,MAAM,EAAE,UAAU,UAAU,QAAQ,WAAW,eAAe,MAAM;CACpE,MAAM,oBAAoB,SAAS,yBAAyB;CAC5D,MAAM,UAAU,SAAS;CACzB,MAAM,QAAQ,SAAS;CAEvB,MAAM,QAAkB,EAAE;AAE1B,MAAK,IAAI,MAAM,UAAU,OAAO,QAAQ,OAAO;EAC7C,IAAI,WAAW,QAAQ,WAAW,WAAW;EAC7C,IAAI,SAAS,QAAQ,SAAS,SAAS,OAAO,QAAQ;AAItD,MAAI,OAAO;AACT,cAAW,KAAK,IAAI,UAAU,MAAM,KAAK;AACzC,YAAS,KAAK,IAAI,QAAQ,MAAM,MAAM;AACtC,OAAI,WAAW,QAAQ;AAIrB,QAAI,GADS,UAAU,OACX,eAAe,MAAM,SAAS;AACxC,WAAM,KAAK,GAAG;AACd,SAAI,MAAM,OAAQ,OAAM,KAAK,KAAK;;AAEpC;;;EAIJ,IAAI,OAAO;AACX,OAAK,IAAI,MAAM,UAAU,OAAO,QAAQ,OAAO;AAE7C,OAAI,OAAO,mBAAmB,KAAK,IAAI,CAAE;AAGzC,OAAI,qBAAqB,CAAC,OAAO,iBAAiB,KAAK,IAAI,CAAE;AAE7D,WAAQ,OAAO,YAAY,KAAK,IAAI;;EAItC,MAAM,OAAO,UAAU;AACvB,MAAI,QAAQ,KAAK,kBAAkB;QAGZ,QAAQ,SAAS,SAAS,OAAO,QAAQ,KAC1B,KAAK,iBACtB,KAAK,KAAK,SAAS,EAEpC,QAAO,KAAK,QAAQ,QAAQ,GAAG;QAGjC,QAAO,KAAK,QAAQ,QAAQ,GAAG;AAKjC,MAAI,MAAM,eAAe,MAAM,OAC7B,OAAM,KAAK,KAAK;OACX;AACL,SAAM,KAAK,KAAK;AAEhB,OAAI,MAAM,OACR,OAAM,KAAK,KAAK;;;AAKtB,QAAO,MAAM,KAAK,GAAG;;;;;;;;;;;;ACxMvB,SAAgB,sBAAsB,SAAmD;AACvF,QAAO;EACL,IAAI,QAAgC;AAClC,UAAO,QAAQ,UAAU;;EAG3B,UAAU,UAAkC;AAC1C,UAAO,QAAQ,UAAU,SAAS;;EAIpC,gBAAgB,MAAc,MAAc,SAAwB;EACpE,gBAAgB,MAAc,MAAoB;EAClD,cAAc,MAAc,MAAoB;EAEhD,SAAS,OAAoC;AAC3C,WAAQ,SAAS,MAAM;;EAGzB,QAAc;AACZ,WAAQ,OAAO;;EAGjB,UAAgB;EACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACjNH,MAAa,YAAY;CAEvB,MAAM;CAEN,WAAW;CAEX,aAAa;CAEb,mBAAmB;CACpB;;AAmCD,MAAa,uBAA4C;CACvD,MAAM;CACN,UAAU;CACV,YAAY;CACZ,iBAAiB;CAClB;;AAGD,MAAM,YAAY;;;;;AAMlB,SAAS,MAAM,IAAqB;AAClC,QAAO,OAAO,KAAK,OAAO;;;;;;AAO5B,SAAS,eACP,OACA,QACA,MACA,WACwB;AACxB,QAAO,IAAI,SAAwB,YAAY;EAC7C,IAAI,QAA8C;EAClD,IAAI,cAAmC;EACvC,IAAI,SAAS;EAEb,SAAS,UAAU;AACjB,OAAI,UAAU,MAAM;AAClB,iBAAa,MAAM;AACnB,YAAQ;;AAEV,OAAI,gBAAgB,MAAM;AACxB,iBAAa;AACb,kBAAc;;;AAIlB,gBAAc,QAAQ,SAAiB;AACrC,aAAU;AAEV,aAAU,YAAY;GACtB,IAAI;AACJ,WAAQ,QAAQ,UAAU,KAAK,OAAO,MAAM,KAE1C,KADqB,SAAS,MAAM,IAAK,GAAG,KACvB,MAAM;IACzB,MAAM,KAAK,SAAS,MAAM,IAAK,GAAG;AAClC,aAAS;AACT,YAAQ,GAAG;AACX;;IAGJ;AAEF,UAAQ,iBAAiB;AACvB,YAAS;AACT,WAAQ,KAAK;KACZ,UAAU;AAGb,QAAM,SAAS,KAAK,IAAI;GACxB;;;;;;;;;;;;;AAcJ,SAAgB,iBACd,MACA,QACG;AACH,QAAO;EACL,GAAG;EACH,eAAe,OAAO,eAAe;EACrC,qBAAqB,OAAO,oBAAoB;EACjD;;;;;;;;;;;;;;;;;;;;AAqBH,SAAgB,oBAAoB,SAA8C;CAChF,MAAM,EAAE,OAAO,QAAQ,YAAY,QAAQ;CAC3C,IAAI,SAAqC;CACzC,IAAI,WAAW;AAEf,QAAO;EACL,IAAI,SAAS;AACX,UAAO;;EAGT,MAAM,SAAuC;AAC3C,OAAI,SAAU,QAAO,UAAU,EAAE,GAAG,sBAAsB;AAC1D,OAAI,WAAW,KAAM,QAAO;GAG5B,MAAM,SAAS,MAAM,eAAe,OAAO,QAAQ,UAAU,MAAM,UAAU;GAC7E,MAAM,QAAQ,MAAM,eAAe,OAAO,QAAQ,UAAU,WAAW,UAAU;GACjF,MAAM,UAAU,MAAM,eAAe,OAAO,QAAQ,UAAU,aAAa,UAAU;GACrF,MAAM,QAAQ,MAAM,eAAe,OAAO,QAAQ,UAAU,mBAAmB,UAAU;AAEzF,YAAS;IACP,MAAM,WAAW,OAAO,MAAM,OAAO,GAAG,qBAAqB;IAC7D,UAAU,UAAU,OAAQ,MAAM,MAAM,GAAG,IAAI,IAAK,qBAAqB;IACzE,YAAY,YAAY,OAAQ,MAAM,QAAQ,GAAG,IAAI,IAAK,qBAAqB;IAC/E,iBAAiB,UAAU,OAAQ,MAAM,MAAM,GAAG,IAAI,IAAK,qBAAqB;IACjF;AAED,UAAO;;EAGT,UAAU;AACR,cAAW;;EAEd;;;;;;;;;;;;ACvEH,SAAgB,uBACd,WACA,QACA,OAAgC,cAChC,OACQ;AACR,KAAI,CAAC,UAAW,QAAO;CAEvB,MAAM,EAAE,UAAU,UAAU,QAAQ,WAAW,eAAe,UAAU;CACxE,IAAI,MAAM;AAEV,MAAK,IAAI,MAAM,UAAU,OAAO,QAAQ,OAAO;EAC7C,IAAI,WAAW,QAAQ,WAAW,WAAW;EAC7C,IAAI,SAAS,QAAQ,SAAS,SAAS,OAAO,QAAQ;AAEtD,MAAI,OAAO;AACT,cAAW,KAAK,IAAI,UAAU,MAAM,KAAK;AACzC,YAAS,KAAK,IAAI,QAAQ,MAAM,MAAM;;AAGxC,MAAI,WAAW,OAAQ;AAEvB,MAAI,SAAS,aACX,QAAO,QAAQ,MAAM,EAAE,GAAG,WAAW,EAAE;AAGzC,SAAO;AACP,OAAK,IAAI,MAAM,UAAU,OAAO,QAAQ,MACtC,QAAO,OAAO,QAAQ,KAAK,IAAI,CAAC;AAElC,SAAO;;AAGT,QAAO;;;;;;;;;;;;ACpJT,SAAgB,wBAAwB,SAAuD;CAC7F,MAAM,WAAW,SAAS,YAAY;CACtC,MAAM,YAAsB,IAAI,MAAM,SAAS;CAC/C,MAAM,aAAuB,IAAI,MAAM,SAAS;CAChD,IAAI,OAAO;CACX,IAAI,QAAQ;AAEZ,QAAO;EACL,KAAK,OAAuB;AAC1B,QAAK,MAAM,QAAQ,OAAO;AACxB,cAAU,QAAQ;AAClB,eAAW,QAAQ,UAAU,KAAK;AAClC,YAAQ,OAAO,KAAK;AACpB,QAAI,QAAQ,SAAU;;;EAI1B,eAAe,QAAgB,UAA4B;GACzD,MAAM,SAAmB,EAAE;AAC3B,QAAK,IAAI,IAAI,GAAG,IAAI,UAAU,KAAK;IACjC,MAAM,eAAe,QAAQ,SAAS,WAAW;AACjD,QAAI,eAAe,KAAK,gBAAgB,MACtC,QAAO,KAAK,GAAG;SACV;KAEL,MAAM,YAAY,OAAO,QAAQ,eAAe,YAAY;AAC5D,YAAO,KAAK,UAAU,UAAW;;;AAGrC,UAAO;;EAGT,IAAI,aAAqB;AACvB,UAAO;;EAGT,OAAO,OAAyB;AAC9B,OAAI,CAAC,MAAO,QAAO,EAAE;GACrB,MAAM,aAAa,MAAM,aAAa;GACtC,MAAM,UAAoB,EAAE;AAC5B,QAAK,IAAI,IAAI,GAAG,IAAI,OAAO,IAGzB,KAAI,YADc,OAAO,QAAQ,IAAI,YAAY,UACvB,aAAa,CAAC,SAAS,WAAW,CAC1D,SAAQ,KAAK,EAAE;AAGnB,UAAO;;EAGT,QAAc;AACZ,UAAO;AACP,WAAQ;;EAEX;;;;aCrD6E;UA2N1D;;;;;;;;;aChPsC;;;;;AAqB5D,eAAsB,qBAAoC;AACxD,KAAI,CAAC,2BAA2B,CAC9B,OAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;AAwBrC,SAAgB,OAAO,SAAuB,MAAY,UAAyB,EAAE,EAAU;AAC7F,KAAI,CAAC,2BAA2B,CAC9B,OAAM,IAAI,MAAM,kEAAkE;CAGpF,MAAM,EAAE,0BAA0B,MAAM,QAAQ,UAAU;CAC1D,MAAM,EAAE,MAAM,OAAO,MAAM,WAAW;CAGtC,MAAM,YAAY,sBAAsB,GAAG;CAG3C,MAAM,YAAY,gBAAgB,UAAU;CAG5C,MAAM,aAAa;EACjB,SAAS;EACT,MAAM;EACN,aAAa;EACb,OAAO;EACP,UAAU;EACV,WAAW;EACX,YAAY;EACZ,sBAAsB;EACtB,mBAAmB;EACpB;CAGD,MAAM,WAAW,WAAW,EAAE,OAAO,QAAQ,OAAO,aAAa,CAAC;CAGlE,MAAM,UAAU,MAAM,cACpB,YAAY,UACZ,EAAE,OAAO,UAAU,EACnB,MAAM,cACJ,cAAc,UACd,EACE,OAAO;EACL,QAAQ;EACR,aAAa;EACd,EACF,EACD,MAAM,cACJ,cAAc,UACd,EACE,OAAO;EACL,QAAQ,QAAQ;EAChB,QAAQ,SAAiB;AACvB,WAAQ,OAAO,MAAM,KAAK;;EAE7B,EACF,EACD,QACD,CACF,CACF;AAGD,0BAAyB;AACvB,aAAW,oBAAoB,SAAS,WAAW,MAAM,KAAK;AAC9D,aAAW,eAAe;GAC1B;CAGF,MAAM,OAAO,iBAAiB,UAAU;CACxC,MAAM,EAAE,QAAQ,eAAe,cAAc,MAAM,OAAO,QAAQ,MAAM,EACtE,yBACD,CAAC;CAGF,MAAM,OAAO,aAAa,WAAW;CACrC,MAAM,OAAO,mBAAmB,WAAW;AAG3C,0BAAyB;AACvB,aAAW,oBAAoB,MAAM,WAAW,MAAM,KAAK;AAC3D,aAAW,eAAe;GAC1B;AAEF,QAAO;EACL;EACA;EACA,OAAO;EACP,SAAS;EACV;;;;;;AAOH,SAAgB,WAAW,SAAuB,MAAY,UAAyB,EAAE,EAAU;AACjG,QAAO,OAAO,SAAS,MAAM,QAAQ;;;;;;AAOvC,SAAS,mBAAmB,IAAsB;CAChD,MAAM,OAAQ,WAAmB;AAC/B,YAAmB,2BAA2B;AAChD,KAAI;AACF,MAAI;WACI;AACN,aAAmB,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChIpD,SAAgB,KACd,MACA,MACA,OAAiB,cACjB,mBAAmB,GACnB,UACQ;CACR,MAAM,aAAa,MAAM,WAAW;CACpC,MAAM,aAAa,KAAK;AAExB,QAAO,YAAY,YAAY,YAAY,MAAM,kBAAkB,SAAS;;;;;;;;;AAU9E,SAAgB,OAAO,QAAgB,OAAiB,cAAsB;AAC5E,QAAO,YAAY,MAAM,OAAO,SAAS,KAAK;;;;aCtDiC;AAIjF,SAAgB,aAAa,YAA4B,OAAuB;CAC9E,IAAI;CACJ,IAAI;AACJ,QAAO;EACL,IAAI,OAAO;AACT,UAAQ,UAAU,aAAa,WAAW;;EAE5C,IAAI,OAAO;AACT,UAAQ,UAAU,mBAAmB,WAAW;;EAElD;EACA,SAAS;EACV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC6BH,SAAS,mBAAmB,QAAmC;CAC7D,MAAM,QAAiB,EAAE;CACzB,IAAI;CACJ,IAAI,WAAW;CAGf,MAAM,gBAAgB;AACpB,MAAI,gBAAgB;AAClB,kBAAe,KAAK;AACpB,oBAAiB,KAAA;;;AAGrB,QAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;AAEzD,QAAO;EACL,KAAK,OAAoB;AACvB,OAAI,YAAY,OAAO,QAAS;AAEhC,OAAI,gBAAgB;IAClB,MAAM,IAAI;AACV,qBAAiB,KAAA;AACjB,MAAE,MAAM;SAER,OAAM,KAAK,MAAM;;EAIrB,SAA+B;AAC7B,UAAO,EACL,CAAC,OAAO,iBAAuC;AAC7C,WAAO,EACL,MAAM,OAAuC;AAC3C,SAAI,YAAY,OAAO,QACrB,QAAO;MAAE,MAAM;MAAM,OAAO,KAAA;MAAW;AAIzC,SAAI,MAAM,SAAS,EACjB,QAAO;MAAE,MAAM;MAAO,OAAO,MAAM,OAAO;MAAG;KAI/C,MAAM,QAAQ,MAAM,IAAI,SAAuB,YAAY;AACzD,uBAAiB;OACjB;AAEF,SAAI,UAAU,QAAQ,YAAY,OAAO,QACvC,QAAO;MAAE,MAAM;MAAM,OAAO,KAAA;MAAW;AAGzC,YAAO;MAAE,MAAM;MAAO,OAAO;MAAO;OAEvC;MAEJ;;EAGH,UAAgB;AACd,cAAW;AACX,UAAO,oBAAoB,SAAS,QAAQ;AAC5C,OAAI,gBAAgB;AAClB,mBAAe,KAAK;AACpB,qBAAiB,KAAA;;;EAGtB;;;;;;;;AAaH,SAAgB,cAAc,SAAkC;CAC9D,MAAM,EAAE,QAAQ,QAAQ,gBAAgB,OAAO,iBAAiB;CAKhE,MAAM,sBAAsB,SAAS,WAAW,kBAAkB,EAAE,CAAC,GAAG,KAAA;CACxE,IAAI,gBAAgB,QAAQ,iBAAiB;CAG7C,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,SAAS,WAAW;CAG1B,IAAI;AACJ,KAAI,eACF,KAAI,eAAe,QACjB,YAAW,OAAO;MACb;AACL,+BAA6B,WAAW,OAAO;AAC/C,iBAAe,iBAAiB,SAAS,sBAAsB,EAC7D,MAAM,MACP,CAAC;;CAKN,IAAI,aAA4B;CAGhC,IAAI,mBAAmB;CAGvB,IAAI,WAAW;CAGf,MAAM,eAAe,mBAAmB,OAAO;CAG/C,IAAI;AACJ,KAAI,OAAO,SACT,qBAAoB,OAAO,UAAU,SAAS;AAC5C,eAAa,KAAK;GAAE,MAAM;GAAU,MAAM,KAAK;GAAM,MAAM,KAAK;GAAM,CAAC;GACvE;CAIJ,IAAI,WAAW;AAEf,QAAO;EACL,SAA+B;AAE7B,UAAO,UAAU,aAAa,QAAQ,EAAE,OAAO;;EAGjD,SAAY,QAA0B,MAAuC;AAC3E,OAAI,SAAU;GAEd,MAAM,KAAK,UAAU;GACrB,MAAM,eAAe,MAAM;AAG3B,OAAI,cAAc,QAAS;GAG3B,MAAM,UAAU,YAAY;IAE1B,IAAI;AAEJ,QAAI;AACF,SAAI,cAAc;MAEhB,MAAM,UAAU,IAAI,SAAgB,UAAU,WAAW;AACvD,4BAAqB,uBAAO,IAAI,MAAM,iBAAiB,CAAC;AACxD,oBAAa,iBAAiB,SAAS,cAAc,EACnD,MAAM,MACP,CAAC;QACF;MAEF,MAAM,SAAS,MAAM,QAAQ,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC;AAGtD,UAAI,aACF,cAAa,oBAAoB,SAAS,aAAa;AAGzD,mBAAa,KAAK;OAAE,MAAM;OAAU;OAAI;OAAQ,CAAC;YAC5C;MACL,MAAM,SAAS,MAAM,QAAQ;AAC7B,mBAAa,KAAK;OAAE,MAAM;OAAU;OAAI;OAAQ,CAAC;;aAE5C,OAAO;AAEd,SAAI,gBAAgB,aAClB,cAAa,oBAAoB,SAAS,aAAa;AAIzD,SAAI,iBAAiB,UAAU,MAAM,YAAY,oBAAoB,MAAM,SAAS,cAElF;AAEF,kBAAa,KAAK;MAChB,MAAM;MACN,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;MACjE,CAAC;;;AAKN,wBAAqB;AACd,aAAS;KACd;;EAGJ,OAAO,QAAsB;AAC3B,OAAI,SAAU;GAMd,MAAM,SAAS;AACf,sBAAmB;GACnB,MAAM,WAAW,OAAO,SAAS,CAAC;GAIlC,IAAI;AACJ,OAAI,eAAe;IACjB,MAAM,UAAU,YAAY,WAAW;IACvC,MAAM,UAAU,OAAO;AACvB,YAAQ,cAAc,SAAS,SAAS,MAAM,QAAQ,SAAS;SAE/D,SAAQ,KAAK,YAAY,QAAQ,MAAM,QAAQ,SAAS;AAE1D,gBAAa;AAGb,OAAI,QAAQ,IAAI,oBACd,KAAI;AAEF,cADmB,KAAK,CACrB,eAAe,iCAAiC,MAAM;WACnD;AAIV,UAAO,MAAM,MAAM;;EAGrB,mBAAmB,OAAqB;AACtC,OAAI,SAAS,YAAY,SAAS,EAAG;AACrC,uBAAoB;;EAGtB,aAAmB;AACjB,gBAAa;;EAGf,iBAAiB,IAA2C;AAC1D,OAAI,GAAI,iBAAgB;;EAG1B,oBAA0B;AAGb,kBACP,oBAAoB;;EAG1B,qBAA6B;AAE3B,UADW,eACA,sBAAsB,IAAI;;EAGvC,kBAAkB,SAAiB,OAAqB;AAC3C,kBACP,oBAAoB,SAAS,MAAM;;EAGzC,UAAgB;AACd,UAAO,OAAO,SAAS;;EAGzB,CAAC,OAAO,WAAiB;AACvB,OAAI,SAAU;AACd,cAAW;AAGX,cAAW,OAAO;AAGlB,OAAI,wBAAwB,eAC1B,gBAAe,oBAAoB,SAAS,qBAAqB;AAInE,OAAI,kBACF,oBAAmB;AAIrB,gBAAa,SAAS;;EAEzB;;;;;;;;;;;;;AC1RH,SAAgB,YAAe,SAAuC;CACpE,MAAM,4BAAY,IAAI,KAAuC;CAC7D,MAAM,SAAS,OAAU,KAAA,EAAe;CACxC,IAAI;CAEJ,MAAM,YAAiC,SAAkB,YAAsB;EAC7E,MAAM,OAAO,QAAQ;EACrB,MAAM,MACJ,OAAO,YAAY,aAAc,QAAyC,KAAK,GAAI;EAErF,IAAI;AACJ,MAAI,CAAC,WAAW,QAAQ,QAAQ,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,IAAI,CAC5E,QAAO;GAAE,GAAG;GAAM,GAAI;GAAoB;MAE1C,QAAO;AAGT,MAAI,OAAO,GAAG,MAAM,KAAK,CAAE;AAE3B,SAAO,KAAK;AAEZ,OAAK,MAAM,YAAY,UACrB,UAAS,MAAM,KAAK;;CAIxB,MAAM,iBAAoB,QAAQ;CAClC,MAAM,wBAA2B;CAEjC,MAAM,aAAa,aAA6D;AAC9E,YAAU,IAAI,SAAS;AACvB,eAAa;AACX,aAAU,OAAO,SAAS;;;CAI9B,MAAM,MAAmB;EAAE;EAAU;EAAU;EAAiB;EAAW;CAE3E,MAAM,UAAU,QAAQ,UAAU,UAAU,IAAI;AAChD,QAAO,QAAQ;AACf,gBAAe;AAEf,QAAO;;;;;;;;;;;ACvCT,SAAgB,qBACd,OACA,cACA,WACwB;CAExB,MAAM,QAAQ,MAAM,UAAU;CAC9B,MAAM,cAAc,OAAO,MAAM,aAAa,aAAa,MAAM,WAAW,KAAA;AAE5E,QAAO;EACL,KAAK,MAAM;EACX,KAAK,MAAM;EACX;EACA,MAAM,QAAgB;GACpB,MAAM,OAAO,iBAAiB,UAAU;AACxC,gBAAa,UAAU,QAAQ,MAAM,eAAe;;EAEtD,cAAc,SAAiB;GAC7B,MAAM,OAAO,iBAAiB,UAAU;AACxC,gBAAa,cAAc,SAAS,KAAK;;EAE3C,eAAe;GACb,MAAM,OAAO,iBAAiB,UAAU;AACxC,UAAO,aAAa,aAAa,KAAK;;EAExC,UAAU;EACV,QAAQ,GAAW,GAAW;AAE5B,UAAO,QADM,iBAAiB,UAAU,EACnB,GAAG,EAAE;;EAE7B;;;;;;;;;AAcH,SAAgB,sBACd,OACA,WACA,cACA,WACyB;AAEzB,KAAI,aAAa,eAAe;EAC9B,MAAM,WAAW,eAAe,OAAO,WAAW,aAAa,cAAc;AAC7E,mBAAiB,SAAS;AAG1B,MAAI,SAAS,sBAAsB,SAAS,iBAC1C,QAAO;;CAIX,MAAM,OAAO,iBAAiB,UAAU;AAGxC,KAAI,UAAU,OAAO,CAAC,UAAU,OAAO;AACrC,eAAa,UAAU,KAAK;AAC5B,SAAO;;AAIT,KAAI,UAAU,OAAO,UAAU,OAAO;AACpC,eAAa,UAAU,KAAK;AAC5B,SAAO;;AAIT,KAAI,UAAU,UAAU,aAAa,eAAe;EAClD,MAAM,WAAW,aAAa;EAC9B,MAAM,QAAQ,SAAS;EACvB,MAAM,SAAS,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;AACjE,MAAI,MAAM,cAAc,QAAQ;AAC9B,gBAAa,WAAW,OAAO;AAC/B,gBAAa,UAAU,MAAM,SAAS;AACtC,UAAO;;;AAaX,KAAI,UAAU;MACR,aAAa,WAAW,SAAS,GAAG;GACtC,MAAM,UAAU,aAAa,WAAW,aAAa,WAAW,SAAS;AACzE,gBAAa,WAAW;GACxB,MAAM,YAAY,aAAa,MAAM,QAAQ;AAC7C,OAAI,UACF,cAAa,MAAM,WAAW,WAAW;AAE3C,UAAO;;;AAIX,QAAO;;;;;;AAWT,SAAgB,yBACd,OACA,iBACA,MACS;AACT,KAAI,MAAM,UAAU,WAAW,CAAC,MAAM,KAAM,QAAO;CAEnD,MAAM,YAAY,MAAM;AAWxB,QAAO,kBACL,iBACA;EACE,QAAQ,UAAU;EAClB,GAAG,UAAU;EACb,GAAG,UAAU;EACb,QAAQ,UAAU;EAClB,OAAO,UAAU;EACjB,OAAO,UAAU;EACjB,MAAM,UAAU;EAChB,MAAM,UAAU;EACjB,EACD,KACD;;;;;;;;AAaH,SAAgB,mBACd,OACA,UACA,KACA,iBACA,WACmB;AAOnB,KAHkB,yBAAyB,OAAO,iBADrC,iBAAiB,UAAU,CACgC,CAGzD,QAAO;CAEtB,MAAM,oBAAoB,WAAW,MAAM;AAE3C,KAAI,qBAAqB,OAAO,sBAAsB,YAAY;EAChE,MAAM,SAAU,kBAA+C,MAAM,MAAM,IAAI;AAC/E,MAAI,WAAW,OAAQ,QAAO;AAC9B,MAAI,WAAW,QAAS,QAAO;;AAGjC,QAAO;;;;;;AAOT,SAAgB,sBACd,OACA,WACA,UACA,KACoB;CAEpB,MAAM,oBAAoB,WAAW;AACrC,KAAI,qBAAqB,OAAO,sBAAsB;MACpC,kBAA+C;GAAE;GAAO,KAAK;GAAW,EAAE,IAAI,KAC/E,OAAQ,QAAO;;AAIhC,KAAK,UAAkB;MACL,SAAiB,IAAI,OAAO,WAAW,IAAI,KAC5C,OAAQ,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9LlC,SAAgB,qBAAqB,MASnB;AAChB,QAAO;EACL,SAAS,KAAK,WAAW;EACzB,iBAAiB,KAAK,mBAAmB;EACzC,cAAc,KAAK,gBAAgB;EACnC,cAAc,KAAK,SAAS;EAC5B,cAAc,KAAK,SAAS;EAC5B,YAAY,KAAK,cAAc;EAC/B,gBAAgB,KAAK,kBAAkB;EACvC,gBAAgB,KAAK,kBAAkB;EACxC;;;;;;;;;;;AAgBH,SAAgB,qBAAqB,QAA4B,OAAgC;AAE/F,KAAI;AACF,QAAM,mBAAmB,OAAO;AAChC,QAAM,OAAO;SACP;CAKR,MAAM,YAAY;EAChB;EACA;EACA,cAAc;EACd,sBAAsB;EACtB;EACA,kBAAkB;EAClB;EACA;EACD,CAAC,KAAK,GAAG;AAIV,KAAI,WAAW,QAAQ,OACrB,KAAI;AACF,YAAW,OAAqC,IAAI,UAAU;SACxD;AACN,MAAI;AACF,UAAO,MAAM,UAAU;UACjB;;KAKV,KAAI;AACF,SAAO,MAAM,UAAU;SACjB;AAQV,KAAI;AACF,QAAM,QAAQ;AACd,SAAO,MAAM,MAAM,KAAK;AAGxB,QAAM,OAAO;SACP;AAKR,KAAI,MAAM,SAAS,MAAM,MACvB,KAAI;AACF,QAAM,WAAW,MAAM;SACjB;;;;;;;;;AAiBZ,SAAgB,oBAAoB,OAAsB,QAA4B,OAAgC;AAEpH,KAAI,MAAM,WAAW,MAAM,MACzB,KAAI;AACF,QAAM,WAAW,KAAK;AACtB,QAAM,QAAQ;SACR;CAMV,MAAM,YAAsB,EAAE;AAE9B,KAAI,MAAM,gBACR,WAAU,KAAK,cAAc;AAI/B,WAAU,KAAK,gBAAgB;AAE/B,KAAI,MAAM,aACR,WAAU,KAAK,YAAY;AAG7B,KAAI,MAAM,aACR,WAAU,KAAK,oBAAoB,MAAM,WAAgB,CAAC;AAG5D,KAAI,MAAM,aACR,WAAU,KAAK,aAAa,CAAC;AAG/B,KAAI,MAAM,eACR,WAAU,KAAK,cAAc;AAG/B,KAAI,MAAM,eACR,WAAU,KAAK,cAAc;CAI/B,MAAM,SAAS,UAAU,KAAK,GAAG;AACjC,KAAI,WAAW,QAAQ,OACrB,KAAI;AACF,YAAW,OAAqC,IAAI,OAAO;SACrD;AACN,MAAI;AACF,UAAO,MAAM,OAAO;UACd;;KAKV,KAAI;AACF,SAAO,MAAM,OAAO;SACd;AAOV,QAAO,KAAK,SAAS;;;;;;;;;;;AAgBvB,SAAgB,eACd,OACA,QACA,OACA,UACM;AAEN,sBAAqB,QAAQ,MAAM;AAGnC,SAAQ,KAAK,iBAAiB;AAE5B,sBAAoB,OAAO,QAAQ,MAAM;AACzC,cAAY;GACZ;AAGF,SAAQ,KAAK,QAAQ,KAAK,UAAU;;;AAQtC,MAAa,SAAS;;AAGtB,MAAa,SAAS;;;;;;;;;;;;;;;;;;AC1QtB,MAAa,UAAU,aAAa,eAAe;AAMnD,IAAI,UAA6D;AACjE,IAAI,iBAAiB;;;;;;AAOrB,SAAgB,YAAY,KAAa,YAAoB,WAAW,IAAI;AAC1E,KAAI,QACF,SAAQ,KAAK;EAAE;EAAK;EAAY,CAAC;AAEnC,KAAI,aAAa,UAAU;AACzB;AACA,UAAQ,OAAO,yBAAyB,IAAI,QAAQ,WAAW,QAAQ,EAAE,CAAC,cAAc,SAAS,KAAK;;;;AAK1G,SAAgB,gBAAgB;AAC9B,KAAI,CAAC,QAAS,WAAU,EAAE;;;;;;;;AAa5B,SAAgB,iBAAiB;AAC/B,KAAI,CAAC,WAAW,QAAQ,WAAW,EAAG;CAEtC,MAAM,YAAY,QAAQ,KAAK,MAAM,EAAE,WAAW,CAAC,MAAM,GAAG,MAAM,IAAI,EAAE;CACxE,MAAM,QAAQ,QAAQ;CACtB,MAAM,OAAO,UAAU,QAAQ,KAAK,MAAM,MAAM,GAAG,EAAE,GAAG;CAExD,MAAM,MAAM,UADK,KAAK,IAAI,KAAK,MAAM,QAAQ,IAAK,EAAE,QAAQ,EAAE;CAE9D,MAAM,MAAM,UAAU,QAAQ;AAE9B,SAAQ,OACN,qBAAqB,MAAM,iBAAiB,KAAK,QAAQ,EAAE,CAAC,UAAU,IAAI,QAAQ,EAAE,CAAC,UAAU,IAAI,QAAQ,EAAE,CAAC,eAAe,iBAC9H;AAGD,WAAU;AACV,kBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACsDnB,MAAM,MAAM,aAAa,cAAc;AAavC,MAAM,MAAM,OAAOC,cAAY,cAAcA,UAAQ,MAAM,KAAA;AAC3D,MAAM,iBAAiB,KAAK,2BAA2B;AACvD,MAAM,qBAAqB;CACzB,MAAM,IAAI,KAAK;AACf,QAAO,CAAC,CAAC,KAAK,MAAM,OAAO,MAAM;IAC/B;AACJ,MAAM,oBAAoB;CACxB,MAAM,IAAI,KAAK;AACf,KAAI,CAAC,KAAK,CAAC,EAAE,SAAS,IAAI,CAAE,QAAO;CACnC,MAAM,CAAC,IAAI,MAAM,EAAE,MAAM,IAAI,CAAC,IAAI,OAAO;AACzC,KAAI,CAAC,OAAO,SAAS,GAAG,IAAI,CAAC,OAAO,SAAS,GAAG,CAAE,QAAO;AACzD,QAAO;EAAE,GAAG;EAAI,GAAG;EAAI;IACrB;AAKJ,MAAM,eAAe,eAAe,eAAe;;;;AASnD,SAAS,eAAe,OAAqE;AAC3F,KAAI,UAAU,QAAQ,UAAU,KAAA,EAAW,QAAO;AAElD,KAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAAY,QAAO;AACrE,QACE,cAAc,SACd,eAAe,SACf,YAAY,SACZ,OAAQ,MAAmB,aAAa,cACxC,OAAQ,MAAmB,cAAc,cACzC,OAAQ,MAAmB,WAAW;;;;;AAO1C,SAAS,gBAAgB,OAGvB;AACA,KAAI,UAAU,QAAQ,UAAU,KAAA,EAAW,QAAO;AAElD,KAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAAY,QAAO;AACrE,QACE,cAAc,SACd,eAAe,SACf,OAAQ,MAAgC,aAAa,cACrD,OAAQ,MAAiC,cAAc;;AAoO3D,MAAa,eAAe,cAAwC,KAAK;;;;;;;;;;AAWzE,SAAgB,OAAa,UAA8B;CACzD,MAAM,QAAQ,WAAW,aAAa;AACtC,KAAI,CAAC,MAAO,OAAM,IAAI,MAAM,+CAA+C;CAE3E,MAAM,CAAC,OAAO,YAAY,MAAM,eAAe,SAAS,MAAM,UAAU,CAAC,CAAC;CAC1E,MAAM,cAAc,OAAO,SAAS;AACpC,aAAY,UAAU;AAEtB,iBAAgB;AACd,SAAO,MAAM,WAAW,aAAa;GACnC,MAAM,OAAO,YAAY,QAAQ,SAAS;AAG1C,aAAU,SAAU,OAAO,GAAG,MAAM,KAAK,GAAG,OAAO,KAAM;IACzD;IACD,CAAC,MAAM,CAAC;AAEX,QAAO;;;;;;AAOT,SAAS,aAAgB,GAAM,GAAe;AAC5C,KAAI,OAAO,GAAG,GAAG,EAAE,CAAE,QAAO;AAC5B,KAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,MAAM,QAAQ,MAAM,KACxE,QAAO;CAET,MAAM,QAAQ,OAAO,KAAK,EAA6B;CACvD,MAAM,QAAQ,OAAO,KAAK,EAA6B;AACvD,KAAI,MAAM,WAAW,MAAM,OAAQ,QAAO;AAC1C,MAAK,MAAM,OAAO,MAChB,KAAI,CAAC,OAAO,GAAI,EAA8B,MAAO,EAA8B,KAAK,CACtF,QAAO;AAGX,QAAO;;;;;;;;;;;;;;;;;AAkBT,SAAgB,cAAoB,UAA8B;CAChE,MAAM,QAAQ,WAAW,aAAa;AACtC,KAAI,CAAC,MAAO,OAAM,IAAI,MAAM,sDAAsD;CAElF,MAAM,CAAC,OAAO,YAAY,MAAM,eAAe,SAAS,MAAM,UAAU,CAAC,CAAC;CAC1E,MAAM,cAAc,OAAO,SAAS;AACpC,aAAY,UAAU;AAEtB,iBAAgB;AACd,SAAO,MAAM,WAAW,aAAa;GACnC,MAAM,OAAO,YAAY,QAAQ,SAAS;AAC1C,aAAU,SAAU,aAAa,MAAM,KAAK,GAAG,OAAO,KAAM;IAC5D;IACD,CAAC,MAAM,CAAC;AAEX,QAAO;;;;;;;;;;;;;AAkBT,SAAgB,UACd,SACA,UACsB;AACtB,QAAO,EACL,IAAI,SAAuB,UAAyB,EAAE,EAAoB;EAExE,IAAI,gBAAkD;EAEtD,MAAM,aAAwC;AAC5C,OAAI,cAAe,QAAO;AAC1B,mBAAgB,QAAQ,SAAS,UAAU,SAAS,QAAQ;AAC5D,UAAO;;AAGT,SAAO;GAEL,KACE,aACA,YAC8B;AAC9B,WAAO,MAAM,CAAC,KAAK,aAAa,WAAW;;GAI7C,CAAC,OAAO,iBAAwC;IAC9C,IAAI,SAAkC;IACtC,IAAI,WAAyC;IAC7C,IAAI,UAAU;AAEd,WAAO;KACL,MAAM,OAAwC;AAC5C,UAAI,CAAC,SAAS;AACZ,iBAAU;AACV,gBAAS,MAAM,MAAM;AACrB,kBAAW,OAAO,OAAO,gBAAgB;;AAE3C,aAAO,SAAU,MAAM;;KAEzB,MAAM,SAA0C;AAC9C,UAAI,OAAQ,QAAO,SAAS;AAC5B,aAAO;OAAE,MAAM;OAAM,OAAO,KAAA;OAAgC;;KAE/D;;GAEJ;IAEJ;;;;;AAMH,eAAe,QACb,SACA,UACA,SACA,SAC2B;CAC3B,MAAM,EACJ,MAAM,cACN,MAAM,cACN,QAAQ,gBACR,QAAQA,UAAQ,OAChB,QAAQ,gBACR,kBAAkB,OAClB,WAAW,mBACX,OAAO,aACP,OAAO,cAAc,OACrB,eAAe,sBAAsB,OACrC,gBAAgB,gBAAgB,MAChC,aAAa,oBAAoB,MACjC,WAAW,eACX,UAAU,cACV,aAAa,iBACb,YAAY,kBACZ,gBAAgB,sBAChB,gBAAgB,uBAAuB,OACvC,WAAW,iBACX,MAAM,YACN,aAAa,mBACb,MAAM,eACN,oBAAoB,0BACpB,UAAU,kBACV,UAAU,kBACV,GAAG,iBACD;CAIJ,MAAM,eAAe,qBAAqB,CAAC,CAAC;CAE5C,MAAM,WAAY,gBAAgB,QAAQ,gBAAgB,QAAQ,CAAC,kBAAmB,oBAAoB;CAC1G,MAAM,OAAO,gBAAgBA,UAAQ,OAAO,WAAW;CACvD,MAAM,OAAO,gBAAgBA,UAAQ,OAAO,QAAQ;CACpD,MAAM,SAAS,kBAAkBA,UAAQ;CAMzC,MAAM,eAAe,WAAWA,UAAQ;CACxC,MAAM,oBAAoB,sBAAsB,mBAAmB,CAAC,YAAY;CAChF,IAAI,cAAkC;AAGtC,OAAM,oBAAoB;CAG1B,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,SAAS,WAAW;AAG1B,KAAI,eACF,KAAI,eAAe,QACjB,YAAW,OAAO;KAElB,gBAAe,iBAAiB,eAAe,WAAW,OAAO,EAAE,EACjE,MAAM,MACP,CAAC;CAKN,MAAM,YAAwE,EAAE;CAChF,MAAM,cAAuC,EAAE;CAC/C,MAAM,mBAAmC,EAAE;CAG3C,IAAI,eAAoC;AACxC,KAAI,EAAE,UAAU,iBAAiB,CAAC,eAAe,aAAa,KAAK,EAAE;EAInE,MAAM,kCAAkB,IAAI,KAAiB;EAC7C,MAAM,aAAa,WACd;GACC,SAAS;GACT;GACA,aAAa;GACb,OAAO;GACP,GAAG,OAAe,SAAqB;AACrC,QAAI,UAAU,SAAU,iBAAgB,IAAI,QAAQ;AACpD,WAAO;;GAET,IAAI,OAAe,SAAqB;AACtC,QAAI,UAAU,SAAU,iBAAgB,OAAO,QAAQ;AACvD,WAAO;;GAEV,GACD;EACJ,MAAM,YAAY,WACb;GACC,OAAO;GACP,UAAU;GACV,WAAW;GACX,kBAAkB;GAClB,cAAc;GACd,aAAa;GACb,mBAAmB;GACpB,GACD;AACJ,iBAAe,mBAAmB,WAAW,YAAY;GAAE;GAAM;GAAM,CAAC;AACxE,YAAU,OAAO;AACjB,mBAAiB,WAAW,aAAc,OAAO,UAAU,CAAC;AAQ5D,MAAI,YAAY,kBAAkB;GAChC,MAAM,QAAQ,kBAAkB,SAAS;AACvC,kBAAc;AACZ,eAAiD,UAAU,KAAK;AAChE,eAAiD,OAAO,KAAK;AAC/D,SAAK,MAAM,YAAY,gBAAiB,WAAU;KAClD;AACF,oBAAiB,KAAK,MAAM;;;AAKhC,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,aAAa,CACtD,KAAI,eAAe,MAAM,CACvB,WAAU,QAAQ;KAElB,aAAY,QAAQ;CAKxB,MAAM,SAAS;EAAE,GAAG;EAAW,GAAG;EAAa;CAG/C,MAAM,oBAAoC,EAAE;CAG5C,MAAM,QAAQ,aAAoB,KAAK,KAAK,QAAQ;EASlD,MAAM,cAAuC,EAAE,GAP7B,QAAQ,OAAO,CAC/B,KACA,KACA,IACD,EAG4D;AAE7D,OAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,UAAU,EAAE;AACxD,eAAY,QAAQ;AAGpB,OAAI,gBAAgB,SAAS,EAAE;IAC7B,MAAM,QAAQ,SAAS,WAAW,mBAAmB,GAGnD;AACF,sBAAkB,KAAK,MAAM;;;AAKjC,OAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,YAAY,CACrD,aAAY,QAAQ;AAGtB,SAAO;GACP;CAGF,IAAI,cAAoB;EAAE;EAAM;EAAM;AAMtC,KAAI,CAAC,UAAU;EACb,MAAM,uBAAuB;AAC3B,iBAAc;IACZ,MAAM,OAAO,WAAW;IACxB,MAAM,OAAO,QAAQ;IACtB;AACD,QAAK,MAAM,YAAY,oBAAqB,UAAS,YAAY;;AAEnE,SAAO,GAAG,UAAU,eAAe;AACnC,mBAAiB,WAAW,OAAO,IAAI,UAAU,eAAe,CAAC;;CAGnE,IAAI,aAAa;CACjB,IAAI,eAAe;CACnB,IAAI,cAAc;CAClB,IAAI,iBAAiB;CACrB,IAAI,kBAAkB;CAKtB,MAAM,aAAa,CAAC,YAAYA,UAAQ,KAAK,kBAAkB;CAE/D,IAAI,YAAY;CAChB,MAAM,cAAc,YAAY,KAAK;CACrC,IAAI;AAEJ,KAAI,YAAY;EACd,MAAM,KAAA,UAEI,UAAU;AACpB,KAAG,cAAc,0BAA0B,gCAAgC;AAE3E,qBAAmB,OAAO,MAAM,KAAK,OAAO;EAE5C,MAAM,aAAa,MACjB,EACG,QAAQ,kBAAkB,WAAW,CACrC,QAAQ,kBAAkB,YAAY,CACtC,QAAQ,aAAa,UAAU,CAC/B,QAAQ,YAAY,SAAS,CAC7B,QAAQ,gBAAgB,aAAa,CACrC,QAAQ,gBAAgB,aAAa,CACrC,QAAQ,kBAAkB,YAAY,CACtC,QAAQ,kBAAkB,aAAa,CACvC,QAAQ,kBAAkB,cAAc,CACxC,QAAQ,kBAAkB,eAAe,CACzC,QAAQ,aAAa,QAAQ,CAC7B,QAAQ,uBAAuB,aAAa,CAC5C,QAAQ,sBAAsB,QAAQ,CACtC,QAAQ,sBAAsB,QAAQ,CACtC,QAAQ,kCAAkC,eAAe,CACzD,QAAQ,kCAAkC,eAAe,CACzD,QAAQ,aAAa,SAAS,CAC9B,QAAQ,aAAa,QAAQ,CAC7B,QAAQ,aAAa,SAAS,CAC9B,QAAQ,aAAa,OAAO,CAC5B,QAAQ,aAAa,QAAQ,CAC7B,QAAQ,cAAc,UAAU,CAChC,QAAQ,cAAc,UAAU,CAChC,QAAQ,cAAc,QAAQ,CAC9B,QAAQ,cAAc,SAAS,CAC/B,QAAQ,cAAc,QAAQ,CAC9B,QAAQ,cAAc,QAAQ,CAE9B,QAAQ,8BAA8B,aAAa,CAEnD,QAAQ,eAAe,WAAW;EAEvC,MAAM,aAAa,SAA+B,OAAgB,GAAG,MAA0B;GAC7F,MAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,OAAO,MAAM;GAC7D,MAAM,MAAM,EAAE;GACd,MAAM,MAAM,YAAY,KAAK,GAAG,aAAa,QAAQ,EAAE;GACvD,MAAM,UAAU,UAAU,IAAI;GAE9B,MAAM,UACJ,QAAQ,SAAS,MAAM,QAAQ,MAAM,GAAG,IAAI,GAAG,QAAQ,QAAQ,OAAO,WAAW,QAAQ,MAAM,KAAK,GAAG;AACzG,MAAG,eACD,0BACA,IAAI,OAAO,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,GAAG,MAAM,IAAI,OAAO,MAAM,QAAQ,IACzE;AACD,UAAQ,iBAA8B,KAAK,MAAM,OAAO,GAAG,KAAK;;AAGlE,SAAO,QAAQ;AAEf,mBAAiB,WAAW;AAC1B,OAAI,iBAAkB,QAAO,QAAQ;IACrC;;CAIJ,MAAM,SAAuB,WACzB;EACE,MAAM,OAAe;AACnB,OAAI,iBAAkB,kBAAiB,MAAM,MAAM;;EAErD,eAAe;EAChB,GACD;EACE,MAAM,OAAqB;AACzB,OAAI,SAEF,WAAQ,UAAU,CAAC,eACjB,yBACA,iBAAiB,MAAM,OAAO,iBAAiB,aAAa,KAC7D;AAEH,OAAI,CAAC,aACH,KAAI,YACF,aAAY,YAAY,MAAM;OAE9B,QAAO,MAAM,MAAM;;EAIzB,UAAgB;AACd,UAAO;;EAET,SAAS,SAA2C;GAClD,MAAM,iBAAiB;AACrB,kBAAc;KACZ,MAAM,OAAO,WAAW;KACxB,MAAM,OAAO,QAAQ;KACtB;AACD,YAAQ,YAAY;;AAEtB,UAAO,GAAG,UAAU,SAAS;AAC7B,gBAAa,OAAO,IAAI,UAAU,SAAS;;EAE9C;CAML,MAAM,qBAAqB,YAAY,uBAAuB,6BAA6B;CAC3F,MAAM,cAAc,qBAAqB,WAAY,qBAAqB,UAAU;CAEpF,MAAM,cAAc,cAAc,sBAAsB,GAAG,KAAA;CAC3D,IAAI;AACJ,KAAI,qBAAqB,KACvB,qBAAoB;UACX,qBAAqB,QAE9B,qBAAoB,aAAa,aAAa;UACrC,qBAAqB,OAC9B,KAAI,gBAAgB,KAAA,EAElB,qBAAoB,YAAY;KAGhC,qBAAoB;KAGtB,qBAAoB;CAItB,MAAM,aAAa,eAAe,gBAAgB,KAAA,KAAa,CAAC;CAGhE,MAAM,sBACJ,CAAC,aAAa,yBAAyB,QAAS,yBAAyB,UAAU,cAAc;CAGnG,IAAI,gBAAgB,aAAa;EAAE,GAAG;EAAY,qBAAqB;EAAmB,GAAG,KAAA;CAI7F,IAAI,iBAAiB,gBAAgB,eAAe,EAAE,MAAM,eAAe,CAAC,GAAG,KAAA;CAK/E,MAAM,UAAU,cAAc;EAC5B;EACA;EACA,MAAM,kBAAkB,eAAe;EACvC,eAAe,gBAAgB;EAChC,CAAC;CAGF,IAAI,YAAY;CAChB,IAAI,qBAA0C;CAE9C,IAAI,eAAe;CACnB,MAAM,oBAAoB,WAAW,eAAe,WAAW,gBAAgB,WAAW;CAC1F,IAAI,aAAqB;CACzB,IAAI,eAAe;CACnB,IAAI,wBAAwB;CAE5B,MAAM,mBAAmB,mBAAmB;CAC5C,IAAI,iBAAiB,8BAA8B;CAInD,MAAM,qCAAqB,IAAI,KAAiB;;CAGhD,SAAS,2BAAiC;AACxC,OAAK,MAAM,YAAY,mBACrB,WAAU;;CAMd,MAAM,qBACH,4BAA+D,0BAA0B;CAI5F,IAAI;AACJ,KAAI,kBAAkB;AACpB,oBAAkB,sBAAsB;GACtC,gBAAgB;GAChB,YAAY,aAAa;AACvB,uBAAmB,IAAI,SAAS;AAChC,iBAAa;AACX,wBAAmB,OAAO,SAAS;;;GAGvC,WAAW,UAAU;AACnB,QAAI,UAAU,MAAM;KAClB,MAAM,CAAC,QAAQ,wBAAwB,EAAE,MAAM,SAAS,EAAE,eAAe;AACzE,sBAAiB;WACZ;KAEL,MAAM,CAAC,MAAM,wBACX;MAAE,MAAM;MAAS,KAAK,MAAM,OAAO;MAAK,KAAK,MAAM,OAAO;MAAK,QAAQ;MAAY,EACnF,eACD;KACD,MAAM,CAAC,MAAM,wBAAwB;MAAE,MAAM;MAAU,KAAK,MAAM,KAAK;MAAK,KAAK,MAAM,KAAK;MAAK,EAAE,GAAG;KACtG,MAAM,CAAC,MAAM,wBAAwB,EAAE,MAAM,UAAU,EAAE,GAAG;AAC5D,sBAAiB;;AAEnB,8BAA0B;AAE1B,QAAI,cACF,SAAQ,YAAY;;GAGxB,aAAa;IACX,MAAM,CAAC,QAAQ,wBAAwB,EAAE,MAAM,SAAS,EAAE,eAAe;AACzE,qBAAiB;AACjB,8BAA0B;AAC1B,QAAI,cACF,SAAQ,YAAY;;GAGzB,CAAC;AACF,qBAAmB,SAAS,sBAAsB,gBAAgB;;CAIpE,MAAM,aAAa,sBAAsB,yBAAyB,GAAG;CACrE,IAAI,sBAAsB;CAC1B,IAAI,cAAc,mBAAmB;CAGrC,MAAM,eAAe,mBAAmB,EACtC,cAAc,SAAS,SAAS,SAAS;AAEvC,MAAI,QAEF,oBADkB,iBAAiB,QAAQ,SAAS,QAAQ,CAC/B;AAG/B,MAAI,QAEF,oBADmB,iBAAiB,SAAS,SAAS,QAAQ,CAChC;IAGnC,CAAC;AAIF,mBAAkB,gBAAgB,aAAa,qBAAqB,YAAY,CAAC;CAGjF,MAAM,cAAc,mBAAmB;CAGvC,MAAM,kBAAkB,0BAA0B,EAAE,cAAc,CAAC;CAGnE,MAAM,gBAAgB;AACpB,MAAI,UAAW;AACf,cAAY;AAGZ,kBAAgB;AAIhB,MAAI;AACF,cAAW,oBAAoB,MAAM,WAAW,YAAY,GAAG;AAC/D,cAAW,eAAe;UACpB;AAKR,mBAAiB,KAAK;AAGtB,MAAI,mBACF,qBAAoB;AAItB,oBAAkB,SAAS,UAAU;AACnC,OAAI;AACF,WAAO;WACD;IAGR;AAIF,MAAI,aAAa;AACf,eAAY,SAAS;AACrB,iBAAc;;AAgBhB,MAAI,CAAC,YAAY,MAAM,OAAO;AAE5B,SAAM,mBAAmB,OAAO;AAChC,SAAM,OAAO;GAKb,MAAM,YAAY;IAChB;IACA,cAAc;IACd,sBAAsB;IACtB;IACA;IACA,kBAAkB;IAClB;IACA,kBAAkB,gBAAgB;IACnC,CAAC,KAAK,GAAG;AAKV,OADqB,WAAWA,UAAQ,QACtB;AAChB,QAAI;AACF,eAAW,OAAqC,IAAI,UAAU;YACxD;AACN,SAAI;AACF,aAAO,MAAM,UAAU;aACjB;;AAaV,QAAI;AACF,WAAM,QAAQ;AACd,YAAO,MAAM,MAAM,KAAK;AAGxB,WAAM,OAAO;YACP;SAIR,KAAI;AACF,WAAO,MAAM,UAAU;WACjB;AAMV,OAAI;AACF,UAAM,WAAW,MAAM;WACjB;aAGC,CAAC,UAAU;GAEpB,MAAM,YAAY;IAChB;IACA,cAAc;IACd,sBAAsB;IACtB;IACA;IACA,kBAAkB;IAClB;IACA,kBAAkB,gBAAgB;IACnC,CAAC,KAAK,GAAG;AACV,OAAI;AACF,WAAO,MAAM,UAAU;WACjB;;AAOV,mBAAiB,SAAS,OAAO;AAC/B,OAAI;AACF,QAAI;WACE;IAGR;AAGF,UAAQ,OAAO,UAAU;;CAG3B,IAAI;CAOJ,MAAM,YAAY,sBAAsB;AACtC,MAAI,WAAY;AAChB,MAAI,gBAAgB;AAElB,qBAAkB;AAClB;;AAGF,MAAI,CAAC,iBAAiB;AACpB,qBAAkB;AAClB,wBAAqB;AACnB,QAAI,CAAC,gBAAiB;AACtB,sBAAkB;AAClB,QAAI,CAAC,cAAc,CAAC,aAAa;AAC/B,mBAAc;AACd,SAAI;AACF,sBAAgB,UAAU;AAC1B,cAAQ,OAAO,cAAc;eACrB;AACR,oBAAc;;;KAGlB;;GAEJ;CAGF,MAAM,YAAY,gBAAgB,UAAU;CAG5C,IAAI;CAGJ,MAAM,aAAa;EACjB,SAAS;EACH;EACN,aAAa;EACb,OAAO;EACP,UAAU;EACV,WAAW;EACX,YAAY;EACZ,sBAAsB;EACtB,mBAAmB;EACpB;CAMD,MAAM,eAAe,WAAW,EAAE,OAAO,aAAa,CAAC;CACvD,MAAM,sCAAsB,IAAI,KAAsD;CACtF,MAAM,WAAW,OAAO,OAAO,cAAc;EAC3C,UAAU,EAAE,aAA6C,aAAa;EACtE,WAAW,EACT,QAAQ,aAA4E;AAClF,uBAAoB,IAAI,SAAS;AACjC,gBAAa,oBAAoB,OAAO,SAAS;KAEpD;EACF,CAAC;CAOF,MAAM,wBAAkE,EAAE;CAC1E,MAAM,wBAAuD,EAAE;CAC/D,MAAM,wBAA2D,EAAE;CAGnE,MAAM,wCAAwB,IAAI,KAA8B;AAChE,uBAAsB,IAAI,SAAS,sBAAoD;AACvF,uBAAsB,IAAI,SAAS,sBAAoD;AACvF,uBAAsB,IAAI,SAAS,sBAAoD;CAEvF,MAAM,sBAA2C;EAC/C,GAAG,OAAO,SAAS;GACjB,IAAI,YAAY,sBAAsB,IAAI,MAAM;AAChD,OAAI,CAAC,WAAW;AACd,gBAAY,EAAE;AACd,0BAAsB,IAAI,OAAO,UAAU;;AAE7C,aAAU,KAAK,QAAQ;AACvB,gBAAa;IACX,MAAM,MAAM,UAAW,QAAQ,QAAQ;AACvC,QAAI,OAAO,EAAG,WAAW,OAAO,KAAK,EAAE;;;EAG3C,KAAK,OAAO,GAAG,MAAM;GACnB,MAAM,YAAY,sBAAsB,IAAI,MAAM;AAClD,OAAI,UACF,MAAK,MAAM,YAAY,UACrB,UAAS,GAAG,KAAK;;EAIvB,YAAY,MAAM;EACnB;CAMD,MAAM,OAAO,iBAAiB,MAAM;CAOpC,MAAM,eAAe,CAAC,kBAAkB,aAAa,sBAAsB,YAAY;CACvF,MAAM,iBACJ,oBAAC,sBAAD,EAAA,UACE,oBAAC,gBAAD;EAAgB,OAAO;YACrB,oBAAC,oBAAoB,UAArB;GAA8B,OAAO;aACnC,oBAAC,YAAY,UAAb;IAAsB,OAAO;cAC3B,oBAAC,cAAc,UAAf;KACE,OAAO;MACL,QAAQ;MACR,aAAa;MACb,mBAAmB,UAAkB,QAAQ,mBAAmB,MAAM;MACtE,oBAAoB,SAAiB,UAAkB,QAAQ,kBAAkB,SAAS,MAAM;MAChG,yBAAyB,QAAQ,mBAAmB;MACpD,0BAA0B,QAAQ,oBAAoB;MACvD;eAED,oBAAC,cAAc,UAAf;MACE,OAAO;OACL,QAAQA,UAAQ;OAChB,QAAQ,SAAiB;AACvB,kBAAQ,OAAO,MAAM,KAAK;;OAE7B;gBAED,oBAAC,oBAAoB,UAArB;OAA8B,OAAO;iBACnC,oBAAC,eAAe,UAAhB;QAAyB,OAAO;kBAC9B,oBAAC,0BAA0B,UAA3B;SAAoC,OAAO;mBACzC,oBAAC,MAAD,EAAA,UACE,oBAAC,aAAa,UAAd;UAAuB,OAAO;oBAA6B;UAAgC,CAAA,EACtF,CAAA;SAC4B,CAAA;QACb,CAAA;OACG,CAAA;MACR,CAAA;KACF,CAAA;IACJ,CAAA;GACM,CAAA;EAChB,CAAA,EACI,CAAA;CAIzB,IAAI,eAAe;CACnB,IAAI,cAAc;CAClB,MAAM,WAAW,OAAOA,cAAY,eAAeA,UAAQ,KAAK,OAAO,SAAS,eAAe;CAM/F,MAAM,iBAAiB;CAMvB,IAAI,MAAiB;CAGrB,IAAI,kBAA2E;CAG/E,SAAS,WAAmB;AAC1B;AACA,MAAI,WAEF,WAAQ,UAAU,CAAC,eACjB,0BACA,iBAAiB,aAAa,OAAO,MAAM,UAAU,SAAS,gBAAgB,CAAC,eAAe,SAC/F;EAEH,MAAM,cAAc,YAAY,KAAK;AAGrC,aAAW,oBAAoB,gBAAgB,WAAW,YAAY,GAAG;AACzE,aAAW,eAAe;EAC1B,MAAM,cAAc,YAAY,KAAK,GAAG;EAKxC;GACE,MAAM,MAAO,WAAmB;AAChC,OAAI,IAAK,KAAI,aAAa;;EAI5B,MAAM,gBAAgB,YAAY,KAAK;EACvC,MAAM,WAAW,iBAAiB,UAAU;EAC5C,MAAM,OAAO,QAAQ,SAAS;EAE9B,MAAM,WAAW,CAAC;AAIlB,MAAI,CAAC,IACH,OAAM,SAAS,UAAU,EAAE,UAAU,gBAAgB,UAAU,CAAC;AAYlE,MAAI,KAAK;GAEP,MAAM,aAAa;AACnB,OAAI,YAAY;IACd,MAAM,eAAe,KAAK,SAAS,WAAW;IAC9C,MAAM,gBAAgB,CAAC,YAAY,KAAK,SAAS,WAAW;AAC5D,QAAI,gBAAgB,eAAe;AACjC,SAAI,aAAa;AACjB,aAAQ,YAAY;;;;AAS1B,MAAI,cAAc;AACd,cAAmB,wBAAwB,KAAA;AAC3C,cAAmB,uBAAuB,KAAA;AAI1C,cAAmB,uBACnB,eAAe,OAAO;IAAE,GAAG,WAAW;IAAG,GAAG,WAAW;IAAG,KAAK,EAAE;IAAc,GAAG,KAAA;;EAQtF,MAAM,eAAe,SAAS,eAAe,WAAW,SAAS,WAAW,SAAS,WAAW;EAChG,MAAM,cACJ,mBAAmB,SAAS,KAAK,SAAS,gBAAgB,SAAS,KAAK,SAAS,gBAAgB;AACnG,MAAI,CAAC,gBAAgB,CAAC,eAAe,mBAAmB,cACtD,QAAO;AAIT,MAAI,eACF,KAAI,aAAa;AAMnB,MAAI,OAAO,KAAK;EAChB,MAAM,EAAE,QAAQ,YAAY,YAAY,iBAAiB,IAAI,QAAQ;AACrE,oBAAkB;EAClB,MAAM,iBAAiB,CAAC,kBAAkB,iBAAiB;EAC3D,MAAM,aAAa,YAAY,KAAK,GAAG;AAIrC,aAAmB,0BAA0B;GAC7C,QAAQ;GACR,QAAQ;GACR,OAAO;GACP,aAAa;GACd;AACC,aAAmB,0BAA2B,WAAmB,0BAA0B,KAAK;EAIlG;GACE,MAAM,MAAO,WAAmB;AAChC,OAAI,KAAK;AACP,QAAI,SAAS;AACb,QAAI,iBAAiB;;;AAQzB,MAAI,eAAe,gBAAgB;GACjC,MAAM,EAAE,QAAQ,gBAAgB,cAC9B,UACA,KAAK,MACL,KAAK,MACL,MACA;IACE,yBAAyB;IACzB,wBAAwB;IACzB,EACD,eACD;GACD,MAAM,EAAE,YAAY,kBAAA,aAAA,EAAA,aAAA,eAAA;AAGpB,QAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,IACrC,MAAK,IAAI,IAAI,GAAG,IAAI,WAAW,OAAO,KAAK;IACzC,MAAM,IAAI,WAAW,QAAQ,GAAG,EAAE;IAClC,MAAM,IAAI,YAAY,QAAQ,GAAG,EAAE;AACnC,QAAI,CAAC,WAAW,GAAG,EAAE,EAAE;KAErB,IAAI,gBAAgB;KACpB,MAAM,eAAgB,WAAmB;AAGzC,SAAI,gBAAgB,aAAa,MAAM,KAAK,aAAa,MAAM,KAAK,aAAa,IAAI,SAAS,EAC5F,iBAAgB,iBAAiB,aAAa,IAAI,OAAO,gBAAgB,EAAE,GAAG,EAAE,OAAO,aAAa,IAAI,KAAK,KAAK,CAAC;cAC1G,gBAAgB,aAAa,MAAM,KAAK,aAAa,MAAM,EACpE,iBAAgB,iCAAiC,EAAE,GAAG,EAAE;SAExD,iBAAgB,8BAA8B,EAAE,GAAG,EAAE,6BAA6B,cAAc,EAAE,GAAG,cAAc,EAAE;KAIvH,IAAI,WAAW;KACf,MAAM,OAAO;MAAE;MAAG;MAAG,KAAK,EAAE;MAAc;AACxC,gBAAmB,uBAAuB;AAC5C,SAAI;AACF,oBACE,UACA,KAAK,MACL,KAAK,MACL,MACA;OACE,yBAAyB;OACzB,wBAAwB;OACzB,EACD,eACD;aACK;AAGN,gBAAmB,uBAAuB;AAC5C,SAAI,KAAK,IAAI,SAAS,EACpB,YAAW,iBAAiB,KAAK,IAAI,OAAO,cAAc,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,KAAK,KAAK,CAAC;SAE5F,YAAW,+BAA+B,EAAE,GAAG,EAAE;KAEnD,MAAM,UAAU,aAAa,WAAW;KACxC,MAAM,YAAY,aAAa,YAAY;KAC3C,MAAM,WAAW,MACf,QAAQ,KAAK,UAAU,EAAE,KAAK,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,WAAW,EAAE,eAAe,QAAQ,EAAE,KAAK,QAAQ,EAAE,aAAa,eAAe,EAAE,MAAM,KAAK,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,OAAO,MAAM,EAAE,MAAM,UAAU,WAAW,EAAE,MAAM,eAAe,SAAS,EAAE,MAAM,MAAM,OAAO,EAAE,MAAM,QAAQ,UAAU,EAAE,MAAM,OAAO,UAAU,EAAE,MAAM,cAAc;KAElW,MAAM,aAAc,WAAmB;KACvC,MAAM,WAAW,aACb,6BAA6B,WAAW,OAAO,iBAC/C,WACG,KACE,GAAQ,MACP,MAAM,EAAE,YAAY,EAAE,aAAa,YAAY,EAAE,cAAc,WAAW,EAAE,aAAa,YAC7E,EAAE,SAAS,YAAY,EAAE,aAAa,aACtC,EAAE,iBAAiB,KAAK,EAAE,oBAAoB,KAAK,EAAE,kBAAkB,KAC9E,EAAE,iBAAiB,MAAM,EAAE,kBAAkB,MAAM,EAAE,yBAAyB,MAAM,EAAE,0BAA0B,UAAU,EAAE,aAAa,uBACxH,EAAE,qBAAqB,WAAW,EAAE,sBAAsB,WAAW,EAAE,kBAAkB,oBAC7F,EAAE,oBAAoB,kBACvB,EAAE,gBAAgB,eAAe,EAAE,uBAAuB,WAAW,EAAE,eAAe,UAC7F,EAAE,SAAS,GAAG,EAAE,SAAS,QAAQ,EAAE,OAAO,GAAG,EAAE,OAAO,GACnE,CACA,KAAK,KAAK,GACb;KACJ,MAAM,MACJ,4CAA4C,EAAE,IAAI,EAAE,eAAe,aAAa,mBAC9D,QAAQ,EAAE,CAAC,mBACX,QAAQ,EAAE,KAC5B,kBAEO;MACL,MAAM,SAAU,WAAmB;AACnC,UAAI,CAAC,UAAU,OAAO,WAAW,EAAG,QAAO;MAC3C,IAAI,MAAM;AACV,WAAK,IAAI,KAAK,GAAG,KAAK,OAAO,QAAQ,MAAM;AACzC,cAAO,oBAAoB,GAAG;AAC9B,YAAK,MAAM,KAAK,OAAO,KAAc;AACnC,eAAO,SAAS,EAAE,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,KAAK,IAAI,EAAE,MAAM,QAAQ,EAAE,KAAK,QAAQ,EAAE;AAClF,eAAO,YAAY,EAAE,QAAQ,UAAU,EAAE,gBAAgB,UAAU,EAAE,MAAM,YAAY,EAAE;AACzF,YAAI,EAAE,aAAa,UAAU;AAC3B,gBAAO,QAAQ,EAAE,oBAAoB,OAAO,EAAE,qBAAqB,QAAQ,EAAE;AAC7E,gBAAO,cAAc,EAAE,aAAa,YAAY,EAAE,qBAAqB,UAAU,EAAE,WAAW,MAAM,EAAE,WAAW;;;;AAIvH,aAAO;SACL,GACJ,gBACA,WACA,0BAA0B,QAAQ,mBAAmB;AAEvD,eAAQ,UAAU,CAAC,eAAe,yBAAyB,MAAM,KAAK;AAEtE,WAAM,IAAI,+BAA+B,IAAI;;;AAInD,OAAI,SAEF,WAAQ,UAAU,CAAC,eACjB,yBACA,uCAAuC,aAAa,OACrD;;EAIL,MAAM,MAAM,aAAa,YAAY,SAAS;AAC9C,MAAI,UAAU;GACZ,MAAM,iBAAiB,YAAY,KAAK,GAAG;GAC3C,MAAM,SAAU,WAAmB;GACnC,MAAM,SAAU,WAAmB;GACnC,MAAM,WAAW,SACb,aAAa,OAAO,QAAQ,QAAQ,EAAE,CAAC,UAAU,OAAO,OAAO,QAAQ,EAAE,CAAC,WAAW,OAAO,QAAQ,QAAQ,EAAE,CAAC,UAAU,OAAO,OAAO,QAAQ,EAAE,CAAC,KAClJ;GACJ,MAAM,YAAY,SACd,aAAa,OAAO,aAAa,YAAY,OAAO,cAAc,WAAW,OAAO,aAAa,UAAU,OAAO,gBAAgB,EAAE,SAAS,OAAO,oBAAoB,EAAE,SAAS,OAAO,uBAAuB,EAAE,aAAa,OAAO,qBAAqB,EAAE,WAAW,OAAO,oBAAoB,EAAE,YAAY,OAAO,qBAAqB,EAAE,YAAY,OAAO,4BAA4B,EAAE,UAAU,OAAO,wBAAwB,EAAE,GAAG,OAAO,yBAAyB,IAAI,OAAO,oBAAoB,IAAI,OAAO,kBAAkB,KAAK,GAAG,GAAG,OAAO,eAAe,qBAAqB,OAAO,gBAAgB,GAAG,OAAO,aAAa,KAAK,OACpnB;AAEJ,aAAQ,UAAU,CAAC,eACjB,yBACA,aAAa,aAAa,IAAI,eAAe,QAAQ,EAAE,CAAC,gBAAgB,YAAY,QAAQ,EAAE,CAAC,cAAc,WAAW,QAAQ,EAAE,CAAC,KAAK,KAAK,KAAK,GAAG,KAAK,KAAK,GAAG,WAAW,UAAU,IACxL;;AAEH,SAAO;;AAIT,KAAI,WAEF,WAAQ,UAAU,CAAC,eAAe,0BAA0B,2BAA2B;AAEzF,iBAAgB,UAAU;AAG1B,KAAI,CAAC,UAAU;AACb,MAAI,WAEF,WAAQ,UAAU,CAAC,eAAe,0BAA0B,+BAA+B;AAE7F,MAAI,iBAAiB;AACnB,UAAO,MAAM,cAAc;AAC3B,UAAO,MAAM,gBAAgB;;AAE/B,SAAO,MAAM,YAAY;AAGzB,MAAI,eAAe,QAAQ,gBAAgB,MACzC,KAAI,gBAAgB;QAEH,MAAM,qBAAqB,QAAQ,MAA2B,EAClE,WAAW;AACpB,WAAO,MAAM,oBAAoB,kBAAkB,CAAC;AACpD,mBAAe;AACf,iBAAa;;SAEV;AAEL,UAAO,MAAM,oBAAoB,YAAiB,CAAC;AACnD,kBAAe;AACf,gBAAa;;WAEN,eAAe,MAAM;AAE9B,UAAO,MAAM,oBAAoB,kBAAkB,CAAC;AACpD,kBAAe;AACf,gBAAa;;AAIf,MAAI,aAAa;AACf,UAAO,MAAM,aAAa,CAAC;AAC3B,kBAAe;;;AAQnB,KAAI,WAEF,WAAQ,UAAU,CAAC,eAAe,0BAA0B,qCAAqC;AAEnG,SAAQ,OAAO,cAAc;AAC7B,KAAI,SAEF,WAAQ,UAAU,CAAC,eACjB,yBACA,yCAAyC,aAAa,gBAAgB,CAAC,eAAe,KACvF;AAOH,KAAI,kBACF,eAAc,mBAAmB;AAKnC,KAAI,CAAC,UAAU;AACb,sBAAoB,cAAc;AAChC,kBAAe;AAGf,OAAI,aAAa;AACf,gBAAY,SAAS;AACrB,kBAAc;;AAEhB,OAAI,gBAAiB,QAAO,MAAM,sBAAsB,CAAC;;AAE3D,sBAAoB,eAAe;AACjC,OAAI,gBAAiB,QAAO,MAAM,sBAAsB,CAAC;AACzD,kBAAe;AAEf,OAAI,qBAAqB,CAAC,YACxB,eAAc,mBAAmB;AAKnC,WAAQ,YAAY;AACpB,QAAK,aAAa;AAIlB,OAAI,CAAC,aAAa;AAChB,oBAAgB,UAAU;AAC1B,YAAQ,OAAO,cAAc;;;;CAQnC,IAAI;CACJ,IAAI,eAAe;CACnB,MAAM,cAAc,IAAI,SAAe,YAAY;AACjD,sBAAoB;AAClB,OAAI,CAAC,cAAc;AACjB,mBAAe;AACf,aAAS;;;GAGb;AAYF,cAAa;AACX,MAAI,WAAY;AAChB,eAAa;AAKb,MAAI,CAAC,YAAY,OAAO,OAAO;GAC7B,MAAM,eAAe;IACnB,sBAAsB;IACtB,cAAc;IACd;IACD,CAAC,KAAK,GAAG;AACV,OAAI;AACF,cAAW,OAAqC,IAAI,aAAa;WAC3D;AACN,QAAI;AACF,YAAO,MAAM,aAAa;YACpB;;;AAMZ,aAAW,OAAO;AAKlB,MAAI,CAAC,gBAAgB;AACnB,YAAS;AACT,gBAAa;;;AAIjB,qBAAoB,OAAO;CAG3B,IAAI,eAAkD;CACtD,IAAI,aAAa;CAGjB,SAAS,UAAU,KAAa;AAC9B,MAAI,cAAc;GAChB,MAAM,UAAU;AAChB,kBAAe;AACf,WAAQ,IAAI;;;AAahB,sBAAqB,MAAM,gBAAgB;AACzC,MAAI,WAAY;AAChB,MAAI,YAAY;GACd,MAAM,QAAQ,iBAAiB,YAAY,cAAc,gBAAgB;GACzE,MAAM,yBAAQ,IAAI,OAAO,EAAC,OAAO,MAAM,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,KAAK,KAAK,IAAI;AAEvE,aAAQ,UAAU,CAAC,eACjB,0BACA,0BAA0B,MAAM,YAAY,eAAe,EAAE,SAAS,MAAM,IAC7E;;AAEH,MAAI,gBAAgB;AAElB,qBAAkB;AAClB;;AAEF,MAAI,aAAa;AAEf,OAAI,CAAC,iBAAiB;AACpB,sBAAkB;AAClB,yBAAqB;AACnB,SAAI,CAAC,gBAAiB;AACtB,uBAAkB;AAClB,SAAI,CAAC,cAAc,CAAC,aAAa;AAC/B,UAAI,SAEF,WAAQ,UAAU,CAAC,eACjB,yBACA,4DAA4D,eAAe,EAAE,KAC9E;AAEH,oBAAc;AACd,UAAI;AACF,uBAAgB,UAAU;AAC1B,eAAQ,OAAO,cAAc;gBACrB;AACR,qBAAc;;;MAGlB;;AAEJ;;AAEF,MAAI,SAEF,WAAQ,UAAU,CAAC,eACjB,yBACA,mDAAmD,eAAe,EAAE,KACrE;AAEH,gBAAc;AACd,MAAI;AACF,mBAAgB,UAAU;AAC1B,WAAQ,OAAO,cAAc;YACrB;AACR,iBAAc;;GAEhB;CAGF,SAAS,0BACP,MACA,UACgC;AAChC,SAAO,IAAI,SAAS,QAAQ,GAAG,WAAW;GACxC,MAAM,GAAG,KAAK,GAAG,OAAO,MAAM,KAAK;GACnC,UAAU;GACV,OAAO,OAAO,MAAM,KAAK;GACzB,MAAM,MAAM;GACb,EAAE;;;;;;CAOL,SAAS,wBAA8B;AACrC,MAAI,CAAC,oBAAoB,CAAC,eAAe,SAAS,CAAC,cAAe;EAClE,MAAM,OAAO,kBAAkB,eAAe;EAC9C,MAAM,UAAU,uBAAuB,eAAe,OAAO,cAAc,SAAS,MAAM,eAAe,MAAM;AAC/G,MAAI,QAAS,QAAO,MAAM,QAAQ;;;;;CAMpC,SAAS,mBAAyB;AAChC,MAAI,CAAC,cAAc,CAAC,cAAe;EACnC,MAAM,QAAQ,cAAc,KAAK,MAAM,KAAK;AAC5C,aAAW,KAAK,MAAM;;;;;;CAOxB,SAAS,8BAAoC;AAC3C,MAAI,CAAC,cAAc,uBAAuB,EAAG;EAC7C,MAAM,OAAO,OAAO,SAAS;EAC7B,MAAM,OAAO,WAAW,eAAe,qBAAqB,KAAK,KAAK;EAGtE,IAAI,MAAM;AACV,OAAK,IAAI,MAAM,GAAG,MAAM,KAAK,QAAQ,MACnC,QAAO,QAAQ,MAAM,EAAE,YAAY,KAAK,QAAQ;EAIlD,MAAM,YAAY,MAAM,oBAAoB;EAC5C,MAAM,eAAe,KAAK,IAAI,GAAG,KAAK,OAAO,UAAU,SAAS,EAAE;AAClE,SAAO,UAAU,aAAa,UAAU,UAAU;AAElD,SAAO,MAAM,IAAI;;;;;CAMnB,SAAS,yBAA+B;AACtC,MAAI,CAAC,YAAY,UAAU,YAAY,eAAe,EAAG;EACzD,MAAM,QAAQ,YAAY,QAAQ,YAAY;AAC9C,MAAI,CAAC,MAAO;EAEZ,MAAM,OAAO,OAAO,SAAS;EAE7B,IAAI;AACJ,MAAI,cAAc,sBAAsB,GAAG;GAGzC,MAAM,mBADa,WAAW,aACQ,sBAAsB,KAAK;AACjE,eAAY,MAAM,MAAM;QAExB,aAAY,MAAM;AAGpB,MAAI,YAAY,KAAK,aAAa,KAAK,KAAM;EAG7C,IAAI,MAAM,QAAQ,YAAY,EAAE,GAAG,MAAM,WAAW,EAAE;AAEtD,OAAK,IAAI,MAAM,MAAM,UAAU,OAAO,MAAM,QAAQ,MAClD,KAAI,iBAAiB,uBAAuB,EAC1C,QAAO,cAAc,QAAQ,QAAQ,KAAK,UAAU,CAAC;MAErD,QAAO,YAAY,MAAM,MAAM,MAAM,aAAa;AAGtD,SAAO;AACP,SAAO,MAAM,IAAI;;;;;CAMnB,SAAS,yBAA+B;AACtC,MAAI,CAAC,YAAY,OAAQ;EACzB,MAAM,OAAO,OAAO,SAAS;EAC7B,MAAM,MAAM,gBAAgB,aAAa,KAAK,KAAK;AAEnD,SAAO,MAAM,QAAQ,KAAK,KAAK,KAAK,MAAM;;;;;CAM5C,SAAS,iBAAiB,OAA8B;AACtD,MAAI,CAAC,cAAc,CAAC,MAAO,QAAO,EAAE;EACpC,MAAM,gBAAgB,WAAW,OAAO,MAAM;EAC9C,MAAM,aAAa,MAAM,aAAa;EACtC,MAAM,UAAyB,EAAE;AACjC,OAAK,MAAM,WAAW,eAAe;GAKnC,MAAM,SAHO,WAAW,eAAe,WAAW,aAAa,UAAU,GAAG,EAAE,CAC5D,MAAM,IAEL,QAAQ,0BAA0B,GAAG;GACxD,IAAI,MAAM,MAAM,aAAa,CAAC,QAAQ,WAAW;AACjD,UAAO,QAAQ,IAAI;AACjB,YAAQ,KAAK;KAAE,KAAK;KAAS,UAAU;KAAK,QAAQ,MAAM,MAAM,SAAS;KAAG,CAAC;AAC7E,UAAM,MAAM,aAAa,CAAC,QAAQ,YAAY,MAAM,EAAE;;;AAG1D,SAAO;;;;;;;;CAST,SAAS,gBAAgB,OAA2C;AAElE,MAAI,cAAc,YAAY,UAAU,MAAM,SAAS,YAAY;GACjE,MAAM,OAAO,MAAM;AACnB,OAAI,KAAK,IAAI,QAAQ;IACnB,MAAM,CAAC,QAAQ,aAAa,EAAE,MAAM,SAAS,EAAE,YAAY;AAC3D,kBAAc;AACd,0BAAsB;AACtB,WAAO;;AAET,OAAI,KAAK,IAAI,UAAU,CAAC,KAAK,IAAI,OAAO;IACtC,MAAM,CAAC,MAAM,WAAW,aAAa,EAAE,MAAM,aAAa,EAAE,aAAa,iBAAiB;AAC1F,kBAAc;AACd,SAAK,MAAM,OAAO,QAChB,KAAI,IAAI,SAAS,WACf,uBAAsB,KAAK,IAAI,GAAG,WAAW,aAAa,IAAI,MAAM,OAAO,SAAS,CAAC,KAAK;AAG9F,WAAO;;AAET,OAAI,KAAK,IAAI,UAAU,KAAK,IAAI,OAAO;IACrC,MAAM,CAAC,MAAM,WAAW,aAAa,EAAE,MAAM,aAAa,EAAE,aAAa,iBAAiB;AAC1F,kBAAc;AACd,SAAK,MAAM,OAAO,QAChB,KAAI,IAAI,SAAS,WACf,uBAAsB,KAAK,IAAI,GAAG,WAAW,aAAa,IAAI,MAAM,OAAO,SAAS,CAAC,KAAK;AAG9F,WAAO;;AAET,OAAI,KAAK,IAAI,WAAW;IACtB,MAAM,CAAC,MAAM,WAAW,aAAa,EAAE,MAAM,aAAa,EAAE,aAAa,iBAAiB;AAC1F,kBAAc;AACd,SAAK,MAAM,OAAO,QAChB,KAAI,IAAI,SAAS,WACf,uBAAsB,KAAK,IAAI,GAAG,WAAW,aAAa,IAAI,MAAM,OAAO,SAAS,CAAC,KAAK;AAG9F,WAAO;;AAET,OAAI,KAAK,IAAI,WAAW;IACtB,MAAM,CAAC,QAAQ,aAAa,EAAE,MAAM,cAAc,EAAE,YAAY;AAChE,kBAAc;AACd,WAAO;;AAET,OAAI,KAAK,IAAI,YAAY;IACvB,MAAM,CAAC,QAAQ,aAAa,EAAE,MAAM,eAAe,EAAE,YAAY;AACjE,kBAAc;AACd,WAAO;;AAET,OAAI,KAAK,SAAS,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,IAAI,MAAM;IAClD,MAAM,CAAC,MAAM,WAAW,aAAa;KAAE,MAAM;KAAS,MAAM,KAAK;KAAO,EAAE,aAAa,iBAAiB;AACxG,kBAAc;AACd,SAAK,MAAM,OAAO,QAChB,KAAI,IAAI,SAAS,WACf,uBAAsB,KAAK,IAAI,GAAG,WAAW,aAAa,IAAI,MAAM,OAAO,SAAS,CAAC,KAAK;AAG9F,WAAO;;;AAKX,MAAI,cAAc,MAAM,SAAS,YAAY;GAC3C,MAAM,OAAO,MAAM;AACnB,OAAI,KAAK,UAAU,OAAO,KAAK,IAAI,MAAM;IACvC,MAAM,CAAC,QAAQ,aAAa,EAAE,MAAM,QAAQ,EAAE,YAAY;AAC1D,kBAAc;AACd,WAAO;;;AAKX,MAAI,cAAc,MAAM,UAAU,WAAW,MAAM,MAAM;GACvD,MAAM,YAAY,MAAM;AAOxB,OAAI,UAAU,WAAW,SAAS;IAChC,MAAM,cAAc;AACpB,QAAI,UAAU,SAAS,UAAU,QAAQ,EAEvC,uBAAsB,KAAK,IACzB,sBAAsB,aACtB,KAAK,IAAI,GAAG,WAAW,aAAa,OAAO,SAAS,CAAC,KAAK,CAC3D;QAGD,uBAAsB,KAAK,IAAI,GAAG,sBAAsB,YAAY;AAEtE,WAAO;;;AAKX,MAAI,oBAAoB,MAAM,UAAU,WAAW,MAAM,MAAM;GAC7D,MAAM,YAAY,MAAM;AAQxB,OAAI,UAAU,WAAW;QACnB,UAAU,WAAW,QAAQ;AAE/B,SAAI,eAAe,OAAO;MACxB,MAAM,CAAC,WAAW,wBAAwB,EAAE,MAAM,SAAS,EAAE,eAAe;AAC5E,uBAAiB;;KAOnB,MAAM,SAAS,iBAAiB,UAAU;KAC1C,MAAM,MAAM,SAAS,iBAAiB,QAAQ,UAAU,GAAG,UAAU,EAAE,GAAG;KAC1E,MAAM,QAAQ,MAAM,oBAAoB,IAAI,GAAG;KAC/C,MAAM,CAAC,QAAQ,wBACb;MAAE,MAAM;MAAS,KAAK,UAAU;MAAG,KAAK,UAAU;MAAG;MAAO,EAC5D,eACD;AACD,sBAAiB;AACjB,+BAA0B;AAG1B,SAAI,eAAe;AACjB,cAAQ,YAAY;AACpB,sBAAgB,UAAU;AAC1B,cAAQ,OAAO,cAAc;AAC7B,6BAAuB;;eAGhB,UAAU,WAAW,UAAU,eAAe,WAAW;KAClE,MAAM,CAAC,QAAQ,wBAAwB;MAAE,MAAM;MAAU,KAAK,UAAU;MAAG,KAAK,UAAU;MAAG,EAAE,eAAe;AAC9G,sBAAiB;AACjB,+BAA0B;AAE1B,SAAI,eAAe;AACjB,cAAQ,OAAO,cAAc;AAC7B,6BAAuB;;AAGzB,YAAO;eACE,UAAU,WAAW,QAAQ,eAAe,WAAW;KAChE,MAAM,CAAC,QAAQ,wBAAwB,EAAE,MAAM,UAAU,EAAE,eAAe;AAC1E,sBAAiB;AACjB,+BAA0B;AAG1B,SAAI,KAAK,SAAS,eAAe;MAC/B,MAAM,OAAO,YAAY,cAAc,SAAS,KAAK,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;AAClF,UAAI,KAAK,SAAS,GAAG;OACnB,MAAM,SAAS,WAAW,OAAO,KAAK,KAAK,CAAC,SAAS,SAAS;AAC9D,cAAO,MAAM,aAAa,OAAO,MAAM;;;AAI3C,SAAI,eAAe;AACjB,cAAQ,OAAO,cAAc;AAC7B,6BAAuB;;;;;AAQ/B,MAAI,oBAAoB,MAAM,SAAS,cAAc,eAAe,OAAO;GACzE,MAAM,CAAC,QAAQ,wBAAwB,EAAE,MAAM,SAAS,EAAE,eAAe;AACzE,oBAAiB;AACjB,6BAA0B;AAE1B,OAAI,eAAe;AACjB,YAAQ,YAAY;AACpB,oBAAgB,UAAU;AAC1B,YAAQ,OAAO,cAAc;;;AAMjC,MAAI,cAAc,sBAAsB,KAAK,MAAM,SAAS,YAAY;AAEtE,yBAAsB;AACtB,UAAO;;AAIT,SAAO,mBAAmB,OAAO,UADrB,qBAAqB,OAAO,cAAc,UAAU,EAChB,iBAAiB,UAAU;;;;;;;;;;;;CAa7E,eAAe,kBAAkB,QAAmD;;;AAClF,OAAI,cAAc,OAAO,WAAW,EAAG,QAAO;AAC9C,kBAAe;AACf,iBAAc,YAAY,KAAK;GAK/B,MAAM,YAAA,YAAA,EAAY,QAAQ,OACxB,mBACO;AACL,mBAAe;IACf,MAAM,YAAY,OAAO,QAAQ,MAAM,EAAE,SAAS,WAAW;AAC7D,WAAO,EACL,KACE,UAAU,SAAS,IACf,UAAU,KAAK,MAAO,EAAE,KAA2B,MAAM,CAAC,KAAK,IAAI,GAClE,OAAO,IAAI,QAAQ,WAC3B;OACC,CACL,CAAA;AAID,OAAI,CAAC,UAAU;AACb,SAAK,IAAI,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;KAC3C,MAAM,QAAQ,OAAO;AACrB,SAAI,MAAM,SAAS,WAAY;KAC/B,MAAM,OAAO,MAAM;AAGnB,SAAI,KAAK,UAAU,OAAO,KAAK,IAAI,QAAQ,cAEzC,KAAI,EADc,iBAAiB,KAAK,QACxB;AAEd,aAAO,OAAO,GAAG,EAAE;AAWnB,qBAVc,qBAAqB;OACjC;OACA,cAAc;OACd,OAAO;OACP,OAAO;OACP;OACA,gBAAgB;OAChB,SAAS;OACT,gBAAgB;OACjB,CAAC,EACoB,QAAQ,aAAa;AAEzC,eAAQ,YAAY;AACpB,uBAAgB;QAChB;WAEF,QAAO,OAAO,GAAG,EAAE;AAKvB,SAAI,KAAK,UAAU,OAAO,KAAK,IAAI,QAAQ,mBAAmB;AAE5D,UAAI,EADc,mBAAmB,KAAK,QAC1B;AACd,aAAM;AACN,cAAO;;AAET,aAAO,OAAO,GAAG,EAAE;;;AAGvB,QAAI,OAAO,WAAW,EAAG,QAAO;;AAIlC,oBAAiB;AACjB,iBAAc;AAYd,QAAK,MAAM,SAAS,QAAQ;AAC1B,QAAI,MAAM,SAAS,YAAY;KAC7B,MAAM,EAAE,OAAO,KAAK,cAAc,MAAM;AAIxC,6BAAwB,iBAAiB,UAAU;AAInD,SAAI,UAAU,cAAc,aAAa,oBAAoB,OAAO,UAAU,EAAE;AAC9E,WAAK,MAAM,YAAY,sBACrB,UAAS,OAAO,UAAU;AAE5B,UAAI,YAAY;AACd,wBAAiB;AACjB,cAAO;;AAET;;KAMF,IAAI,gBAAgB;AACpB,SAAI,aAAa,cAEf,iBADoB,sBAAsB,OAAO,WAAW,cAAc,UAAU,KACpD;AAKlC,SAAI,CAAC,cACH,MAAK,MAAM,YAAY,sBACrB,UAAS,OAAO,UAAU;eAGrB,MAAM,SAAS,cAAc;KACtC,MAAM,EAAE,SAAS,MAAM;AACvB,UAAK,MAAM,YAAY,sBACrB,UAAS,KAAK;eAEP,MAAM,SAAS,cAAc;KACtC,MAAM,EAAE,YAAY,MAAM;AAC1B,UAAK,MAAM,YAAY,sBACrB,UAAS,QAAQ;;AAMrB,QAAI,YAAY;AACd,sBAAiB;AACjB,YAAO;;AAKT,QAAI,MAAM,SAAS,YAAY;KAC7B,MAAM,EAAE,OAAO,KAAK,MAAM,MAAM;AAChC,SAAI,EAAE,cAAc,UAAW;AAC/B,SAAI,oBAAoB,OAAO,EAAE,CAAE;;IAGrC,MAAM,SAAS,gBAAgB,MAAM;AACrC,QAAI,WAAW,OAAO;AACpB,mBAAc;AACd,sBAAiB;AACjB,WAAM;AACN,YAAO;;AAYT,QAAI,WAAW,SAAS;AACtB,uBAAkB;AAClB,qBAAgB,UAAU;AAC1B,aAAQ,OAAO,cAAc;AAE7B,WAAM,QAAQ,SAAS;AACvB,SAAI,iBAAiB;AACnB,wBAAkB;AAClB,sBAAgB,UAAU;AAC1B,cAAQ,OAAO,cAAc;;;;AAOnC,qBAAkB;AAGlB,OAAI;AACF,oBAAgB,UAAU;aAClB;AACR,kBAAc;;GAShB,IAAI,aAAa;GACjB,MAAM,aAAa;AACnB,UAAO,aAAa,YAAY;AAC9B,UAAM,QAAQ,SAAS;AACvB,QAAI,CAAC,gBAAiB;AACtB,sBAAkB;AAClB,kBAAc;AACd,QAAI;AACF,qBAAgB,UAAU;cAClB;AACR,mBAAc;;AAEhB;;AAWF,iBAAc,QAAQ,kBAAkB;AAExC,oBAAiB;GACjB,MAAM,eAAe,YAAY,KAAK;AACtC,WAAQ,OAAO,cAAc;AAE7B,qBAAkB;AAClB,OAAI,sBAAsB,EACxB,8BAA6B;AAE/B,0BAAuB;AACvB,2BAAwB;AACxB,2BAAwB;GACxB,MAAM,YAAY,YAAY,KAAK,GAAG;AACtC,OAAI,UAAU;IACZ,MAAM,UAAU,YAAY,KAAK,GAAG;AAEpC,cAAQ,UAAU,CAAC,eACjB,yBACA,eAAe,OAAO,OAAO,GAAG,OAAO,IAAI,KAAK,KAAK,QAAQ,QAAQ,EAAE,CAAC,YAAY,aAAa,oCAAoC,UAAU,QAAQ,EAAE,CAAC,WAC3J;;AAGH,OAAI,UACF,aAAY,OAAO,IAAI,QAAQ,SAAS,YAAY,KAAK,GAAG,YAAY;AAE1E,UAAO;;;;;;;CAaT,MAAM,aAAgC,EAAE;CACxC,IAAI,oBAAyC;CAE7C,MAAM,YAAY,YAAY;EAM5B,MAAM,YAAY,MAAM,GAJK,OAAO,QAAQ,UAAU,CAAC,KAAK,CAAC,MAAM,cACjE,0BAA0B,MAAM,SAAS,CAC1C,CAE+C;EAGhD,MAAM,aAAa,YAAY;AAC7B,OAAI;AACF,eAAW,MAAM,SAAS,UAAU,WAAW,OAAO,EAAE;AACtD,gBAAW,KAAK,MAAM;AACtB,SAAI,mBAAmB;MACrB,MAAM,UAAU;AAChB,0BAAoB;AACpB,eAAS;;AAEX,SAAI,WAAY;;aAEV;AAER,QAAI,mBAAmB;KACrB,MAAM,UAAU;AAChB,yBAAoB;AACpB,cAAS;;;;AASf,MAAI,WACF,KAAI;GAEF,MAAM,SAAS,MAAM;AACrB,OAAI,MAAM,SAAS,CAAC,QAAQ;AAC1B,UAAM,WAAW,KAAK;AACtB,UAAM,QAAQ;AACd,UAAM,YAAY,OAAO;;GAG3B,MAAM,kBACJ,IAAI,SAAiB,YAAY;IAC/B,MAAM,UAAU,SAAiB;AAC/B,WAAM,IAAI,QAAQ,OAAO;AACzB,aAAQ,KAAe;;AAEzB,UAAM,GAAG,QAAQ,OAAO;KACxB;GAEJ,MAAM,cAAc,MAAM,yBACvB,SAAU,cAAc,YAAY,YAAY,KAAK,GAAG,OAAO,MAAM,KAAK,EAC3E,WACA,IACD;AAGD,OAAI,YAAY,cAAc,mBAAmB;AAC/C,wBAAoB,YAAY;AAChC,QAAI,eAAe;AACjB,qBAAgB;MAAE,GAAG;MAAe,qBAAqB;MAAmB;AAC5E,sBAAiB,eAAe,EAAE,MAAM,eAAe,CAAC;AAExD,aAAQ,iBAAiB,eAAe,cAAc;;AAIxD,UAAM;AACN,YAAQ,YAAY;AAEpB,QAAI,CAAC,aAAa;AAChB,mBAAc;AACd,SAAI;AACF,sBAAgB,UAAU;AAC1B,cAAQ,OAAO,cAAc;eACrB;AACR,oBAAc;;;;AAMpB,OAAI,MAAM,SAAS,CAAC,QAAQ;AAC1B,UAAM,WAAW,MAAM;AACvB,UAAM,OAAO;;UAET;AAQV,MAAI,oBACF,KAAI;GACF,MAAM,SAAS,MAAM;AACrB,OAAI,MAAM,SAAS,CAAC,QAAQ;AAC1B,UAAM,WAAW,KAAK;AACtB,UAAM,QAAQ;AACd,UAAM,YAAY,OAAO;;GAG3B,MAAM,gBAA+C,EAAE;GACvD,MAAM,iBAAiB,SAAiB;AACtC,SAAK,MAAM,WAAW,cAAe,SAAQ,KAAK;;AAEpD,SAAM,GAAG,QAAQ,cAAc;GAE/B,MAAM,WAAW,oBAAoB;IACnC,QAAQ,SAAU,cAAc,YAAY,YAAY,KAAK,GAAG,OAAO,MAAM,KAAK;IAClF,SAAS,YAAY;AACnB,mBAAc,KAAK,QAAQ;AAC3B,kBAAa;MACX,MAAM,MAAM,cAAc,QAAQ,QAAQ;AAC1C,UAAI,OAAO,EAAG,eAAc,OAAO,KAAK,EAAE;;;IAG9C,WAAW;IACZ,CAAC;GAEF,MAAM,cAAc,MAAM,SAAS,QAAQ;AAC3C,YAAS,SAAS;AAClB,SAAM,IAAI,QAAQ,cAAc;AAGhC,OAAI,eAAe;IACjB,MAAM,cAAc,iBAAiB,eAAe,YAAY;AAIhE,QAFE,YAAY,kBAAkB,cAAc,iBAC5C,YAAY,wBAAwB,cAAc,qBACnC;AACf,qBAAgB;AAChB,sBAAiB,eAAe,EAAE,MAAM,eAAe,CAAC;AACxD,aAAQ,iBAAiB,eAAe,cAAc;AAEtD,WAAM;AACN,aAAQ,YAAY;AACpB,SAAI,CAAC,aAAa;AAChB,oBAAc;AACd,UAAI;AACF,uBAAgB,UAAU;AAC1B,eAAQ,OAAO,cAAc;gBACrB;AACR,qBAAc;;;;;AAMtB,OAAI,MAAM,SAAS,CAAC,QAAQ;AAC1B,UAAM,WAAW,MAAM;AACvB,UAAM,OAAO;;UAET;AAQV,cAAY,CAAC,OAAO,QAAiB,IAAI,QAAQ,sBAAsB,MAAM,CAAC;AAK9E,MAAI,wBAAwB,CAAC,uBAAuB;AAClD,yBAAsB,MAAO,cAAc,YAAY,YAAY,EAAE,GAAG,OAAO,MAAM,EAAE,CAAE;AACzF,2BAAwB;;AAG1B,MAAI;AACF,UAAO,CAAC,cAAc,CAAC,OAAO,SAAS;AAErC,QAAI,WAAW,WAAW,EACxB,OAAM,IAAI,SAAe,YAAY;AACnC,yBAAoB;AACpB,YAAO,iBAAiB,eAAe,SAAS,EAAE,EAAE,MAAM,MAAM,CAAC;MACjE;AAGJ,QAAI,cAAc,OAAO,QAAS;AAClC,QAAI,WAAW,WAAW,EAAG;IAsB7B,MAAM,gBAAgB;IACtB,IAAI,aAAa;IACjB,MAAM,yBAAyB,IAAI,SAAe,YAAY,aAAa,QAAQ,CAAC;AAEpF,UAAM,kBAAkB;IACxB,IAAI,UAAU,WAAW;AACzB,WAAO,aAAa,eAAe;AAEjC,WAAM,kBAAkB;KACxB,MAAM,SAAS,WAAW;AAC1B,SAAI,WAAW,QAAS;AACxB,eAAU;AACV;;AAEF,QAAI,SAEF,WAAQ,UAAU,CAAC,eACjB,yBACA,gBAAgB,WAAW,UAAU,WAAW,OAAO,IACxD;IAIH,MAAM,KAAK;AACX,OAAG,6BAA6B;AAChC,OAAG,4BAA4B,WAAW;AAC1C,OAAG,yBAAyB,GAAG,yBAAyB,KAAK;IAG7D,MAAM,MAAM,MAAM,kBAAkB,WAAW,OAAO,EAAE,CAAC;AACzD,QAAI,IAAK,WAAU,IAAI;;YAEjB;AAER,gBAAa;AACb,OAAI,cAAc;IAChB,MAAM,UAAU;AAChB,mBAAe;AAEf,YAAQ,KAA0B;;AAOpC,OAAI,cAAc,CAAC,aAAa,CAAC,YAAY,MAAM,MACjD,KAAI;AAEF,UAAM,mBAAmB,OAAO;AAChC,UAAM,QAAQ;AAEd,UAAM,IAAI,SAAS,YAAY,WAAW,SAAS,GAAG,CAAC;AAEvD,WAAO,MAAM,MAAM,KAAK;AAGxB,UAAM,OAAO;WACP;AAMV,YAAS;AACT,gBAAa;;;AAKjB,YAAW,CAAC,OAAO,QAAiB,IAAI,QAAQ,qBAAqB,MAAM,CAAC;AA0J5E,QAvJiC;EAC/B,IAAI,OAAO;AACT,UAAO,cAAc;;EAEvB,IAAI,OAAO;AACT,UAAO,iBAAiB,UAAU;;EAEpC,IAAI,SAAS;AACX,UAAO,eAAe,WAAW;;EAEnC,IAAI,QAAQ;AACV,UAAO;;EAET,gBAAgB;AACd,UAAO;;EAET,UAAU;AACR,SAAM;;EAER,CAAC,OAAO,WAAW;AACjB,SAAM;;EAER,MAAM,MAAM,QAAgB;;;IAE1B,MAAM,aAAa,YAAY,KAAK;IAGpC,MAAM,CAAC,OAAO,aAAa,SADX,eAAe,eAAe,OAAO,GAAG,UAAU,OAAO,CAC7B;IAE5C,MAAM,YAAA,WAAA,EAAY,QAAQ,OACxB,mBACO;AACL,oBAAe;AACf,YAAO,EAAE,KAAK,SAAS,QAAQ;QAC7B,CACL,CAAA;AAKD,QAAI,UAAU,OAAO,UAAU,QAAQ;SAEjC,EADc,mBAAmB,KAAK,QAC1B;AACd,YAAM;AACN;;;AAKJ,SAAK,MAAM,YAAY,sBACrB,UAAS,OAAO,UAAU;AAI5B,qBAAiB;AACjB,kBAAc;AAId,QADoB,sBAAsB,OAAO,WAAW,cAAc,UAAU,KAChE,YAAY;AAC9B,uBAAkB;AAClB,mBAAc;AACd,sBAAiB;AACjB,eAAU;AACV,WAAM,QAAQ,SAAS;AACvB,SAAI,UAAW,aAAY,SAAS,QAAQ,YAAY,KAAK,GAAG,WAAW;AAC3E;;AAKF,QAAI,sBAAsB,OAAO,WAAW,UADzB,qBAAqB,OAAO,cAAc,UAAU,CACN,KAAK,QAAQ;AAC5E,mBAAc;AACd,sBAAiB;AACjB,WAAM;AACN;;AAIF,sBAAkB;AAGlB,QAAI;AACF,qBAAgB,UAAU;cAClB;AACR,mBAAc;;IAMhB,IAAI,aAAa;IACjB,MAAM,aAAa;AACnB,WAAO,aAAa,YAAY;AAC9B,WAAM,QAAQ,SAAS;AACvB,SAAI,CAAC,gBAAiB;AACtB,uBAAkB;AAClB,mBAAc;AACd,SAAI;AACF,sBAAgB,UAAU;eAClB;AACR,oBAAc;;AAEhB;;AAQF,QAAI,aAAa,EACf,eAAc,QAAQ,kBAAkB;AAE1C,qBAAiB;AACjB,YAAQ,OAAO,cAAc;AAC7B,QAAI,UAAW,aAAY,SAAS,QAAQ,YAAY,KAAK,GAAG,WAAW;;;;;;;EAG7E,CAAC,OAAO,iBAAwC;AAC9C,UAAO;IACL,MAAM,OAAwC;AAC5C,SAAI,cAAc,WAChB,QAAO;MAAE,MAAM;MAAM,OAAO,KAAA;MAAgC;KAI9D,MAAM,MAAM,MAAM,IAAI,SAAiB,YAAY;AAEjD,UAAI,cAAc,YAAY;AAC5B,eAAQ,KAA0B;AAClC;;AAEF,qBAAe;OACf;AAGF,SAAI,CAAC,IACH,QAAO;MAAE,MAAM;MAAM,OAAO,KAAA;MAAgC;AAG9D,YAAO;MAAE,MAAM;MAAO,OAAO;MAAK;;IAEpC,MAAM,SAA0C;AAC9C,WAAM;AACN,YAAO;MAAE,MAAM;MAAM,OAAO,KAAA;MAAgC;;IAE/D;;EAEJ;;;;;;;;;;;;;;;;;ACr1FH,SAAgB,iBAAiB,SAA8B;CAC7D,MAAM,KAAK,WAAW,eAAe;CAErC,MAAM,aAAa,OAAO,QAAQ;AAClC,YAAW,UAAU;AAErB,iBAAgB;AACd,MAAI,CAAC,GAAI;AACT,SAAO,GAAG,GAAG,UAAU,SAAiB;AACtC,cAAW,QAAQ,KAAK;IACxB;IACD,CAAC,GAAG,CAAC;;;;aCIyC;mBACQ;AA2J3D,eAAsB,IACpB,SACA,gBAAmC,EAAE,EACrC,aACoB;AAEpB,KAAI,OAAO,cAAc,EAAE;EACzB,MAAM,OAAO;AAMb,MALkB,KAA4C,WAKhD;GACZ,MAAM,EAAE,iBAAiB,MAAM,OAAO;GACtC,MAAM,eAAe,IAAI,cAAc;GACvC,MAAM,YAAY,OAAO,OAAO,cAAc;IAC5C,OAAO;IACP,OAAO;IACP,IAAI;IACJ,WAAW,OAAgB;AACzB,eAAU,QAAQ;AAClB,YAAO;;IAET,OAAO;AACL,YAAO;;IAET,SAAS;AACP,YAAO;;IAET,QAAQ;AACN,YAAO;;IAET,MAAM;AACJ,YAAO;;IAET,QAAQ;AACN,YAAO;;IAET,cAAc;AACZ,YAAO;;IAEV,CAAC;AAOF,OAAK,KAAa,UACd,MAAa,aAAa,SAAiB;AAC3C,iBAAa,KAAK,QAAQ,KAAK;;GAQnC,MAAM,YADW,aAAa,SACC,WAAW,QAAQ;AAYlD,UAAO,WATQ,MADH,uBAAuB,EAAE,EAAE,CACd,IAAI,SAAS;IACpC,iBAAiB;IACjB,GAAG;IACH,OAAO;IACP,QAAQ,KAAK;IACb,aAAa;IACb,MAAM,KAAK,QAAQ;IACnB,MAAM,KAAK,QAAQ;IACpB,CAAC,CACuB;;EAI3B,MAAM,OAAO,KAAK,QAAQ,oBAAoB;EAG9C,MAAM,SAAS,oBAAC,eAAD;GAAe,OADhB,MAAM,aAAa;aACY;GAAwB,CAAA;AAgBrE,SAAO,WAdQ,MADH,uBAAuB,EAAE,EAAE,CACd,IAAI,QAAQ;GACnC;GACA,QAAQ,KAAK;GACb,OAAO,KAAK;GACZ,MAAM,KAAK,QAAQ,KAAA;GACnB,MAAM,KAAK,QAAQ,KAAA;GACnB;GACA,iBAAiB;GACjB,OAAO,KAAK;GACZ,OAAO;GACP,gBAAgB;GAChB,YAAY;GACZ,gBAAgB;GACjB,CAAC,CACuB;;CAI3B,MAAM,EAAE,MAAM,GAAG,SAAS;CAC1B,MAAM,OAAO,KAAK,QAAQ,oBAAoB;CAG9C,MAAM,SAFW,KAAK,YAAY,QAAS,KAAK,QAAQ,QAAQ,KAAK,QAAQ,QAAQ,CAAC,KAAK,SAGvF,UACA,MAAM,aAAa,CAAC,MAAM,UAAU,oBAAC,eAAD;EAAsB;YAAQ;EAAwB,CAAA,CAAC;AAa/F,QAAO,WAXQ,MADH,uBAAuB,EAAE,EAAE,CACd,IAAI,QAAQ;EACnC,GAAG;EACH;EACA,iBAAiB,SAAS;EAC1B,eAAe,SAAS;EACxB,OAAO,KAAK,SAAS,KAAK;EAC1B,OAAO,KAAK,SAAS,SAAS;EAC9B,gBAAgB,KAAK,kBAAkB,SAAS;EAChD,YAAY,KAAK,cAAc;EAC/B,gBAAgB,KAAK,kBAAkB;EACxC,CAAC,CACuB;;;;AAK3B,SAAS,OAAO,KAA2B;AACzC,KAAI,OAAO,KAAM,QAAO;AACxB,KAAI,OAAO,QAAQ,YAAY,OAAO,QAAQ,WAAY,QAAO;CACjE,MAAM,IAAI;AACV,QAAO,OAAO,EAAE,aAAa,cAAc,OAAO,EAAE,WAAW;;;AAIjE,SAAS,WAAW,QAQN;AACZ,QAAO;EACL,IAAI,OAAO;AACT,UAAO,OAAO;;EAEhB,IAAI,OAAO;AACT,UAAO,OAAO;;EAEhB,IAAI,SAAS;AACX,UAAO,OAAO;;EAEhB,qBAAqB,OAAO,eAAe;EAC3C,eAAe,OAAO,SAAS;GAC9B,OAAO,gBAAgB,OAAO,OAAO,UAAU;EAChD,QAAQ,QAAgB,OAAO,MAAM,IAAI;EAC1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3TH,SAAgB,WAAW,YAAoB,QAA6C;AAC1F,QAAO,GACJ,OAAO,sBAAsB,mBAAmB,YAAY,OAAO,EACrE;;;;;AAMH,SAAS,mBAAmB,YAAoB,QAA6C;CAC3F,IAAI,QAAQ;CACZ,IAAI;CACJ,IAAI;CACJ,IAAI,OAAO;CAGX,MAAM,gBAAgB;AACpB,SAAO;AACP,MAAI,OAAO;AACT,gBAAa,MAAM;AACnB,WAAQ,KAAA;;AAEV,MAAI,gBAAgB;AAClB,kBAAe;IAAE,MAAM;IAAM,OAAO,KAAA;IAAW,CAAC;AAChD,oBAAiB,KAAA;;;AAIrB,KAAI,OACF,KAAI,OAAO,QACT,QAAO;KAEP,QAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;AAI7D,QAAO;EACL,MAAM,OAAwC;AAC5C,OAAI,KACF,QAAO;IAAE,MAAM;IAAM,OAAO,KAAA;IAAW;AAGzC,UAAO,IAAI,SAAiC,SAAS,YAAY;AAC/D,qBAAiB;AAEjB,YAAQ,iBAAiB;AACvB,SAAI,CAAC,MAAM;MACT,MAAM,QAAQ;AACd,uBAAiB,KAAA;AACjB,cAAQ;OAAE,MAAM;OAAO;OAAO,CAAC;;OAEhC,WAAW;KACd;;EAGJ,MAAM,SAA0C;AAC9C,UAAO;AACP,OAAI,OAAO;AACT,iBAAa,MAAM;AACnB,YAAQ,KAAA;;AAEV,OAAI,OACF,QAAO,oBAAoB,SAAS,QAAQ;AAE9C,OAAI,gBAAgB;AAClB,mBAAe;KAAE,MAAM;KAAM,OAAO,KAAA;KAAW,CAAC;AAChD,qBAAiB,KAAA;;AAEnB,UAAO;IAAE,MAAM;IAAM,OAAO,KAAA;IAAW;;EAE1C;;;;;;;;AASH,SAAgB,gBAAgB,QAA6C;AAC3E,QAAO,WAAW,IAAI,OAAO;;;;;;;;AAS/B,SAAgB,iBAAiB,QAA6C;AAC5E,QAAO,WAAW,KAAM,OAAO;;;;;;;;;;;;AAajC,SAAgB,mBACd,YAAY,IACZ,QACiE;CACjE,MAAM,WAAW,MAAO;CACxB,IAAI,WAAW,KAAK,KAAK;CACzB,IAAI,OAAO;AAEX,QAAO,GACJ,OAAO,sBAAsB;EAC5B,IAAI,OAAO;EACX,IAAI;EACJ,IAAI;EAUJ,MAAM,gBAAgB;AACpB,UAAO;AACP,OAAI,OAAO;AACT,iBAAa,MAAM;AACnB,YAAQ,KAAA;;AAEV,OAAI,gBAAgB;AAClB,mBAAe;KAAE,MAAM;KAAM,OAAO,KAAA;KAAW,CAAC;AAChD,qBAAiB,KAAA;;;AAIrB,MAAI,OACF,KAAI,OAAO,QACT,QAAO;MAEP,QAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;AAI7D,SAAO;GACL,MAAM,OAAkF;AACtF,QAAI,KACF,QAAO;KAAE,MAAM;KAAM,OAAO,KAAA;KAAW;AAGzC,WAAO,IAAI,SAAS,YAAY;AAC9B,sBAAiB;KAEjB,MAAM,UADM,KAAK,KAAK,GACA;KACtB,MAAM,QAAQ,KAAK,IAAI,GAAG,WAAW,QAAQ;AAE7C,aAAQ,iBAAiB;AACvB,UAAI,CAAC,MAAM;OACT,MAAM,cAAc,KAAK,KAAK;OAC9B,MAAM,QAAQ,cAAc;AAC5B,kBAAW;AACX,wBAAiB,KAAA;AACjB,eAAQ;QACN,MAAM;QACN,OAAO;SAAE,MAAM;SAAQ,SAAS;SAAa;SAAO;QACrD,CAAC;;QAEH,MAAM;MACT;;GAGJ,MAAM,SAAoF;AACxF,WAAO;AACP,QAAI,OAAO;AACT,kBAAa,MAAM;AACnB,aAAQ,KAAA;;AAEV,QAAI,OACF,QAAO,oBAAoB,SAAS,QAAQ;AAE9C,QAAI,gBAAgB;AAClB,oBAAe;MAAE,MAAM;MAAM,OAAO,KAAA;MAAW,CAAC;AAChD,sBAAiB,KAAA;;AAEnB,WAAO;KAAE,MAAM;KAAM,OAAO,KAAA;KAAW;;GAE1C;IAEJ"}
|
|
1
|
+
{"version":3,"file":"runtime-PH2xY1DM.mjs","names":["getTabOrder","ESC","CSI","RESET","moveCursor","_enableMouse","_disableMouse","_enableKittyKeyboard","_disableKittyKeyboard","BEL","ESC","log","globalGetCursorState","isTerm","DEFAULT_CONFIG","BORDER_CHARS","ANSI_COLORS","resolveColor","createStore","process"],"sources":["../packages/ag-term/src/scroll-utils.ts","../packages/ag-react/src/hooks/useInput.ts","../packages/ag-term/src/history-buffer.ts","../packages/ag-term/src/list-document.ts","../packages/ag-term/src/text-surface.ts","../packages/ag-term/src/viewport-compositor.ts","../packages/ag-term/src/search-overlay.ts","../packages/ag-term/src/pane-manager.ts","../packages/ag-term/src/output.ts","../packages/ag-react/src/hooks/useExit.ts","../packages/ag-term/src/scroll-region.ts","../packages/ag-react/src/hooks/useCursor.ts","../packages/ag-react/src/error-boundary.tsx","../packages/ag/src/focus-queries.ts","../packages/ag/src/interactive-signals.ts","../packages/ag/src/focus-manager.ts","../packages/ag/src/tree-utils.ts","../packages/ag/src/focus-events.ts","../packages/ag-term/src/hit-registry-core.ts","../packages/test/src/debug-mismatch.ts","../packages/ag-term/src/non-tty.ts","../packages/ag-term/src/clipboard.ts","../packages/ag-term/src/scheduler.ts","../packages/ag-term/src/term-def.ts","../packages/ag-term/src/osc-palette.ts","../packages/ag-term/src/osc-markers.ts","../packages/ag-term/src/kitty-detect.ts","../packages/ag-term/src/termtest.ts","../packages/ag-term/src/measurer.ts","../packages/ag-term/src/cursor-query.ts","../packages/ag-term/src/terminal-colors.ts","../packages/ag-term/src/device-attrs.ts","../packages/ag-term/src/mode-query.ts","../packages/ag-term/src/pixel-size.ts","../packages/ag-term/src/adapters/canvas-adapter.ts","../packages/ag-term/src/adapters/dom-adapter.ts","../packages/ag-term/src/mouse-events.ts","../packages/ag-term/src/inspector.ts","../packages/create/src/core/index.ts","../packages/create/src/store/index.ts","../packages/create/src/streams/index.ts","../packages/create/src/internal/capability-registry.ts","../packages/create/src/internal/capabilities.ts","../packages/headless/src/selection.ts","../packages/ag-term/src/features/selection.ts","../packages/ag-term/src/ansi/width-detection.ts","../packages/ag-term/src/selection-renderer.ts","../packages/ag-term/src/virtual-scrollback.ts","../packages/ag-term/src/index.ts","../packages/ag-term/src/runtime/layout.ts","../packages/ag-term/src/runtime/diff.ts","../packages/ag-term/src/runtime/create-buffer.ts","../packages/ag-term/src/runtime/create-runtime.ts","../packages/create/src/signal-store.ts","../packages/ag-term/src/runtime/event-handlers.ts","../packages/ag-term/src/runtime/terminal-lifecycle.ts","../packages/ag-term/src/runtime/perf.ts","../packages/ag-term/src/runtime/create-app.tsx","../packages/ag-react/src/hooks/usePasteCallback.ts","../packages/ag-term/src/runtime/run.tsx","../packages/ag-term/src/runtime/tick.ts"],"sourcesContent":["/**\n * Scroll Utilities\n *\n * Shared functions for edge-based scrolling behavior across VirtualList,\n * HorizontalVirtualList, and other scroll-aware components.\n */\n\n/**\n * Calculate edge-based scroll offset.\n *\n * Only scrolls when cursor approaches the edge of the visible area.\n * This provides smoother scrolling by starting to scroll before hitting\n * the absolute edge, maintaining context around the selected item.\n *\n * ## Algorithm\n *\n * The viewport is divided into zones:\n * ```\n * |<padding>|<------ safe zone ------>|<padding>|\n * | scroll | no scroll needed | scroll |\n * | if < | | if > |\n * ```\n *\n * When the selected item enters a padding zone, the viewport scrolls\n * to keep the item visible with margin.\n *\n * ## Asymmetry Note\n *\n * The +1 in the \"scroll down/right\" case is intentional:\n * - Offset points to the TOP/LEFT of the viewport\n * - We want the selected item to be `padding` items from the BOTTOM/RIGHT\n * - Formula: `selectedIndex - visibleCount + padding + 1`\n *\n * Example: visibleCount=10, padding=2, selectedIndex=15\n * offset = 15 - 10 + 2 + 1 = 8\n * viewport shows items 8-17, selected item 15 is at position 7 (2 from bottom)\n *\n * @param selectedIndex - Currently selected item index\n * @param currentOffset - Current scroll offset (topmost/leftmost visible item)\n * @param visibleCount - Number of items visible in viewport\n * @param totalCount - Total number of items\n * @param padding - Items to keep visible before/after cursor (default: 1)\n * @returns New scroll offset\n */\nexport function calcEdgeBasedScrollOffset(\n selectedIndex: number,\n currentOffset: number,\n visibleCount: number,\n totalCount: number,\n padding = 1,\n): number {\n // If everything fits, no scrolling needed\n if (totalCount <= visibleCount) return 0\n\n // Reduce padding when viewport is too small to have a non-empty safe zone.\n // With padding=1 and visibleCount=2, paddedStart > paddedEnd (inverted zone),\n // causing every re-render to trigger a scroll in one direction.\n const effectivePadding = padding * 2 >= visibleCount ? 0 : padding\n\n // Calculate visible range\n const visibleStart = currentOffset\n const visibleEnd = currentOffset + visibleCount - 1\n\n // Define the \"safe zone\" where cursor doesn't trigger scroll\n const paddedStart = visibleStart + effectivePadding\n const paddedEnd = visibleEnd - effectivePadding\n\n let newOffset = currentOffset\n\n if (selectedIndex < paddedStart) {\n // Scrolling UP/LEFT: place item `effectivePadding` rows from top\n newOffset = Math.max(0, selectedIndex - effectivePadding)\n } else if (\n effectivePadding === 0 &&\n selectedIndex === paddedStart &&\n currentOffset > 0 &&\n // Only scroll back if the viewport is large enough to show both the\n // context item and the selected item. When visibleCount <= padding,\n // scrolling back pushes the selected item out of view, which triggers\n // a forward scroll on the next render → infinite oscillation.\n visibleCount > padding\n ) {\n // Small viewport (effectivePadding forced to 0): cursor at the very first visible\n // position should still scroll back to provide context. Without this, scrolling\n // right works (cursor past last visible triggers scroll) but scrolling left doesn't\n // (cursor at first visible doesn't trigger), creating asymmetric behavior.\n // Use original padding for the offset formula to show context before the cursor.\n newOffset = Math.max(0, selectedIndex - padding)\n } else if (selectedIndex > paddedEnd) {\n // Scrolling DOWN/RIGHT: place item `effectivePadding` rows from bottom\n // The +1 converts from 0-indexed offset to correct position\n newOffset = Math.min(totalCount - visibleCount, selectedIndex - visibleCount + effectivePadding + 1)\n }\n\n // Clamp to valid range\n return Math.max(0, Math.min(newOffset, totalCount - visibleCount))\n}\n","/**\n * Silvery useInput Hook\n *\n * Handles keyboard input via the unified RuntimeContext.\n * Compatible with Ink's useInput API.\n *\n * No-ops when called outside a runtime (e.g., in createRenderer() tests where\n * RuntimeContext is absent). Components render without input handling, which\n * is correct for static rendering.\n * Use useRuntime() for components that need to detect interactive vs static mode.\n */\n\nimport { useContext, useEffect, useRef } from \"react\"\nimport { RuntimeContext } from \"../context\"\nimport { isModifierOnlyEvent, type InputHandler, type Key } from \"@silvery/ag/keys\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n// Re-export Key and InputHandler for consumers that import from useInput\nexport type { Key, InputHandler } from \"@silvery/ag/keys\"\n\n/**\n * Options for useInput hook.\n */\nexport interface UseInputOptions {\n /**\n * Enable or disable input handling.\n * Useful when there are multiple useInput hooks and you want to disable some.\n * @default true\n */\n isActive?: boolean\n\n /**\n * Callback for bracketed paste events.\n * When the terminal has bracketed paste mode enabled,\n * pasted text is delivered as a single string instead of\n * individual keystrokes.\n */\n onPaste?: (text: string) => void\n\n /**\n * Callback for key release events.\n * Requires Kitty protocol with REPORT_EVENTS flag enabled.\n * When provided, release events are dispatched here instead of being silently dropped.\n *\n * @example\n * ```tsx\n * useInput((input, key) => {\n * // Handle press/repeat events\n * }, {\n * onRelease: (input, key) => {\n * // Handle release events (e.g., stop scrolling, end drag)\n * },\n * })\n * ```\n */\n onRelease?: InputHandler\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/**\n * Hook for handling user input.\n *\n * No-ops if RuntimeContext is not provided (i.e., outside a runtime).\n * Components render normally without input handling in static mode.\n * Use useRuntime() for components that need to detect interactive vs static mode.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * useInput((input, key) => {\n * if (input === 'q') {\n * // Quit\n * }\n * if (key.upArrow) {\n * // Move up\n * }\n * }, {\n * onRelease: (input, key) => {\n * // Handle key release (requires Kitty REPORT_EVENTS)\n * },\n * });\n *\n * return <Text>Press q to quit</Text>;\n * }\n * ```\n */\nexport function useInput(inputHandler: InputHandler, options: UseInputOptions = {}): void {\n const rt = useContext(RuntimeContext)\n\n const { isActive = true, onPaste, onRelease } = options\n\n // Stable ref for the handler — avoids tearing down/recreating the\n // subscription on every render. Without this, rapid keystrokes between\n // effect cleanup and setup are lost.\n const handlerRef = useRef(inputHandler)\n handlerRef.current = inputHandler\n\n const onPasteRef = useRef(onPaste)\n onPasteRef.current = onPaste\n\n const onReleaseRef = useRef(onRelease)\n onReleaseRef.current = onRelease\n\n // Subscribe to input events via RuntimeContext\n // In static mode (no runtime), this is a no-op — components render\n // without input handling, which is correct for createRenderer() tests.\n useEffect(() => {\n if (!isActive || !rt) return\n\n return rt.on(\"input\", (input: string, key: Key) => {\n // Skip modifier-only keys (Cmd, Shift, Ctrl, Alt pressed alone).\n // These are handled by useModifierKeys, not useInput consumers.\n if (isModifierOnlyEvent(input, key)) return\n // Release events are dispatched to onRelease if provided,\n // otherwise silently dropped (handlers expect press-only semantics).\n if (key.eventType === \"release\") {\n onReleaseRef.current?.(input, key)\n return\n }\n const result = handlerRef.current(input, key)\n if (result === \"exit\") rt.exit()\n })\n }, [isActive, rt])\n\n // Subscribe to paste events via RuntimeContext\n useEffect(() => {\n if (!isActive || !rt) return\n\n return rt.on(\"paste\", (text: string) => {\n onPasteRef.current?.(text)\n })\n }, [isActive, rt])\n}\n","/**\n * Ring buffer for frozen scrollback items.\n *\n * Each item represents a rendered list entry (card, row, etc.) with its\n * ANSI snapshot, split rows, and plain-text rows for searching.\n * When total rows exceed capacity, the oldest items are evicted.\n */\n\nimport { stripAnsi } from \"./unicode\"\n\nexport interface HistoryItem {\n key: string | number\n ansi: string\n rows: string[]\n plainTextRows: string[]\n width: number\n}\n\nexport interface HistoryBuffer {\n push(item: HistoryItem): void\n readonly totalRows: number\n readonly itemCount: number\n getRows(startRow: number, count: number): string[]\n getPlainTextRows(startRow: number, count: number): string[]\n search(query: string): number[]\n getItemAtRow(row: number): { item: HistoryItem; localRow: number } | null\n clear(): void\n readonly capacity: number\n}\n\nexport function createHistoryItem(key: string | number, ansi: string, width: number): HistoryItem {\n const rows = ansi.split(\"\\n\")\n const plainTextRows = rows.map((r) => stripAnsi(r))\n return { key, ansi, rows, plainTextRows, width }\n}\n\nexport function createHistoryBuffer(capacity = 10_000): HistoryBuffer {\n // Store items in insertion order; evict from front when over budget.\n let items: HistoryItem[] = []\n let _totalRows = 0\n\n function evict(): void {\n while (_totalRows > capacity && items.length > 0) {\n const removed = items.shift()!\n _totalRows -= removed.rows.length\n }\n }\n\n /** Walk items to find which item contains the given document row. */\n function resolveRow(row: number): { itemIndex: number; localRow: number } | null {\n if (row < 0 || row >= _totalRows) return null\n let cumulative = 0\n for (let i = 0; i < items.length; i++) {\n const itemRows = items[i]!.rows.length\n if (row < cumulative + itemRows) {\n return { itemIndex: i, localRow: row - cumulative }\n }\n cumulative += itemRows\n }\n return null\n }\n\n return {\n push(item: HistoryItem): void {\n items.push(item)\n _totalRows += item.rows.length\n evict()\n },\n\n get totalRows(): number {\n return _totalRows\n },\n\n get itemCount(): number {\n return items.length\n },\n\n get capacity(): number {\n return capacity\n },\n\n getRows(startRow: number, count: number): string[] {\n const result: string[] = []\n for (let r = startRow; r < startRow + count; r++) {\n const resolved = resolveRow(r)\n if (resolved) {\n result.push(items[resolved.itemIndex]!.rows[resolved.localRow]!)\n } else {\n result.push(\"\")\n }\n }\n return result\n },\n\n getPlainTextRows(startRow: number, count: number): string[] {\n const result: string[] = []\n for (let r = startRow; r < startRow + count; r++) {\n const resolved = resolveRow(r)\n if (resolved) {\n result.push(items[resolved.itemIndex]!.plainTextRows[resolved.localRow]!)\n } else {\n result.push(\"\")\n }\n }\n return result\n },\n\n search(query: string): number[] {\n if (!query) return []\n const lowerQuery = query.toLowerCase()\n const matches: number[] = []\n let rowOffset = 0\n for (const item of items) {\n for (let r = 0; r < item.plainTextRows.length; r++) {\n if (item.plainTextRows[r]!.toLowerCase().includes(lowerQuery)) {\n matches.push(rowOffset + r)\n }\n }\n rowOffset += item.rows.length\n }\n return matches\n },\n\n getItemAtRow(row: number): { item: HistoryItem; localRow: number } | null {\n const resolved = resolveRow(row)\n if (!resolved) return null\n return { item: items[resolved.itemIndex]!, localRow: resolved.localRow }\n },\n\n clear(): void {\n items = []\n _totalRows = 0\n },\n }\n}\n","/**\n * Canonical row model spanning frozen history + live content.\n *\n * Frozen rows (from HistoryBuffer) occupy rows 0..frozenRows-1.\n * Live rows follow at frozenRows..totalRows-1.\n * All row indices are document-global.\n */\n\nimport type { HistoryBuffer } from \"./history-buffer\"\nimport type { SearchMatch } from \"./search-overlay\"\n\nexport interface LiveItemBlock {\n key: string | number\n itemIndex: number\n rows: string[]\n plainTextRows: string[]\n}\n\nexport interface DocumentSource {\n type: \"frozen\" | \"live\"\n itemKey?: string | number\n itemIndex?: number\n localRow: number\n}\n\nexport interface ListDocument {\n readonly totalRows: number\n readonly frozenRows: number\n readonly liveRows: number\n getRows(startRow: number, count: number): string[]\n getPlainTextRows(startRow: number, count: number): string[]\n getSource(row: number): DocumentSource | null\n search(query: string): SearchMatch[]\n}\n\nexport function createListDocument(history: HistoryBuffer, getLiveItems: () => LiveItemBlock[]): ListDocument {\n function liveRowCount(): number {\n let total = 0\n for (const block of getLiveItems()) {\n total += block.rows.length\n }\n return total\n }\n\n return {\n get totalRows(): number {\n return history.totalRows + liveRowCount()\n },\n\n get frozenRows(): number {\n return history.totalRows\n },\n\n get liveRows(): number {\n return liveRowCount()\n },\n\n getRows(startRow: number, count: number): string[] {\n const frozen = history.totalRows\n const result: string[] = []\n for (let r = startRow; r < startRow + count; r++) {\n if (r < 0 || r >= this.totalRows) {\n result.push(\"\")\n } else if (r < frozen) {\n result.push(...history.getRows(r, 1))\n } else {\n let liveRow = r - frozen\n let found = false\n for (const block of getLiveItems()) {\n if (liveRow < block.rows.length) {\n result.push(block.rows[liveRow]!)\n found = true\n break\n }\n liveRow -= block.rows.length\n }\n if (!found) result.push(\"\")\n }\n }\n return result\n },\n\n getPlainTextRows(startRow: number, count: number): string[] {\n const frozen = history.totalRows\n const result: string[] = []\n for (let r = startRow; r < startRow + count; r++) {\n if (r < 0 || r >= this.totalRows) {\n result.push(\"\")\n } else if (r < frozen) {\n result.push(...history.getPlainTextRows(r, 1))\n } else {\n let liveRow = r - frozen\n let found = false\n for (const block of getLiveItems()) {\n if (liveRow < block.plainTextRows.length) {\n result.push(block.plainTextRows[liveRow]!)\n found = true\n break\n }\n liveRow -= block.plainTextRows.length\n }\n if (!found) result.push(\"\")\n }\n }\n return result\n },\n\n getSource(row: number): DocumentSource | null {\n const frozen = history.totalRows\n if (row < 0 || row >= this.totalRows) return null\n if (row < frozen) {\n const hit = history.getItemAtRow(row)\n if (!hit) return null\n return {\n type: \"frozen\",\n itemKey: hit.item.key,\n localRow: hit.localRow,\n }\n }\n // Live: walk item blocks\n const liveItems = getLiveItems()\n let liveRow = row - frozen\n for (const block of liveItems) {\n if (liveRow < block.rows.length) {\n return { type: \"live\", itemIndex: block.itemIndex, localRow: liveRow }\n }\n liveRow -= block.rows.length\n }\n return null\n },\n\n search(query: string): SearchMatch[] {\n if (!query) return []\n const lowerQuery = query.toLowerCase()\n const matches: SearchMatch[] = []\n const frozen = history.totalRows\n\n // Search frozen rows\n const frozenRowMatches = history.search(query)\n for (const row of frozenRowMatches) {\n const plainRows = history.getPlainTextRows(row, 1)\n const line = plainRows[0]!.toLowerCase()\n let pos = line.indexOf(lowerQuery)\n while (pos !== -1) {\n matches.push({ row, startCol: pos, endCol: pos + query.length })\n pos = line.indexOf(lowerQuery, pos + 1)\n }\n }\n\n // Search live rows (walk item blocks)\n let rowOffset = 0\n for (const block of getLiveItems()) {\n for (let i = 0; i < block.plainTextRows.length; i++) {\n const line = block.plainTextRows[i]!.toLowerCase()\n let pos = line.indexOf(lowerQuery)\n while (pos !== -1) {\n matches.push({ row: frozen + rowOffset + i, startCol: pos, endCol: pos + query.length })\n pos = line.indexOf(lowerQuery, pos + 1)\n }\n }\n rowOffset += block.plainTextRows.length\n }\n\n return matches\n },\n }\n}\n","/**\n * Read/query facade over a ListDocument.\n *\n * Provides text extraction, search, hit-testing, and reveal for\n * pane-based scrollback UIs. Pure data — no React, no rendering.\n */\n\nimport type { ListDocument } from \"./list-document\"\nimport type { SearchMatch } from \"./search-overlay\"\nimport { stripAnsi } from \"./unicode\"\n\nexport interface SurfaceCapabilities {\n paneSafe: boolean\n searchableHistory: boolean\n selectableHistory: boolean\n overlayHistory: boolean\n}\n\nexport interface TextSurface {\n readonly id: string\n readonly document: ListDocument\n getText(startRow: number, startCol: number, endRow: number, endCol: number): string\n search(query: string): SearchMatch[]\n hitTest(viewportRow: number, viewportCol: number): { row: number; col: number } | null\n reveal(row: number): void\n /** Notify subscribers that content has changed (search results may be stale) */\n notifyContentChange(): void\n subscribe(listener: () => void): () => void\n readonly capabilities: SurfaceCapabilities\n}\n\nexport function createTextSurface(config: {\n id: string\n document: ListDocument\n viewportToDocument: (viewportRow: number) => number\n onReveal: (documentRow: number) => void\n capabilities: SurfaceCapabilities\n}): TextSurface {\n const listeners = new Set<() => void>()\n\n function notify(): void {\n for (const fn of listeners) fn()\n }\n\n return {\n get id(): string {\n return config.id\n },\n\n get document(): ListDocument {\n return config.document\n },\n\n getText(startRow: number, startCol: number, endRow: number, endCol: number): string {\n const rows = config.document.getRows(startRow, endRow - startRow + 1)\n const lines: string[] = []\n for (let i = 0; i < rows.length; i++) {\n const plain = stripAnsi(rows[i]!)\n const row = startRow + i\n if (row === startRow && row === endRow) {\n lines.push(plain.slice(startCol, endCol))\n } else if (row === startRow) {\n lines.push(plain.slice(startCol))\n } else if (row === endRow) {\n lines.push(plain.slice(0, endCol))\n } else {\n lines.push(plain)\n }\n }\n return lines.join(\"\\n\")\n },\n\n search(query: string): SearchMatch[] {\n return config.document.search(query)\n },\n\n hitTest(viewportRow: number, viewportCol: number): { row: number; col: number } | null {\n const docRow = config.viewportToDocument(viewportRow)\n if (docRow < 0 || docRow >= config.document.totalRows) return null\n return { row: docRow, col: viewportCol }\n },\n\n reveal(row: number): void {\n config.onReveal(row)\n notify()\n },\n\n notifyContentChange(): void {\n notify()\n },\n\n subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n\n get capabilities(): SurfaceCapabilities {\n return config.capabilities\n },\n }\n}\n","/**\n * Viewport Compositor - Merges frozen history rows with the live viewport.\n *\n * When a user scrolls up into history, the compositor provides the\n * history rows that should be shown. When at the tail (scrollOffset=0),\n * the live React-rendered content is shown instead.\n *\n * scrollOffset uses bottom-origin semantics: scrollOffset=N means\n * \"show rows starting at totalHistory - N from the tail\".\n *\n * This does NOT replace the React rendering pipeline. It provides\n * overlay data that the rendering layer can use when scrolled up.\n */\n\nimport type { HistoryBuffer } from \"./history-buffer\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ViewportCompositorConfig {\n /** The history buffer containing frozen items */\n history: HistoryBuffer\n /** Height of the viewport in rows */\n viewportHeight: number\n /** Current scroll offset into history (0 = at tail/live) */\n scrollOffset: number\n}\n\nexport interface ComposedViewport {\n /** History rows to overlay at top of viewport */\n overlayRows: string[]\n /** Number of top rows occupied by history */\n overlayRowCount: number\n /** Number of bottom rows for live content */\n liveRowsVisible: number\n /** Whether viewing history */\n isScrolledUp: boolean\n /** Total scrollable height */\n totalHeight: number\n}\n\n// ============================================================================\n// Compositor\n// ============================================================================\n\nexport function composeViewport(config: ViewportCompositorConfig): ComposedViewport {\n const { history, viewportHeight, scrollOffset } = config\n\n const totalHistory = history.totalRows\n\n if (scrollOffset <= 0 || totalHistory === 0) {\n return {\n overlayRows: [],\n overlayRowCount: 0,\n liveRowsVisible: viewportHeight,\n isScrolledUp: false,\n totalHeight: totalHistory + viewportHeight,\n }\n }\n\n // scrollOffset from tail: show rows starting at (totalHistory - clampedOffset)\n const clampedOffset = Math.min(scrollOffset, totalHistory)\n const startRow = Math.max(0, totalHistory - clampedOffset)\n const rowsToShow = Math.min(viewportHeight, totalHistory - startRow)\n const overlayRows = history.getRows(startRow, rowsToShow)\n const liveRowsVisible = viewportHeight - rowsToShow\n\n return {\n overlayRows,\n overlayRowCount: rowsToShow,\n liveRowsVisible,\n isScrolledUp: true,\n totalHeight: totalHistory + viewportHeight,\n }\n}\n","/**\n * Search overlay state machine for virtual inline mode.\n *\n * Pure TEA: (action, state) -> [state, effects[]]\n * Provides incremental search-as-you-type with match navigation.\n */\n\nexport interface SearchMatch {\n row: number\n startCol: number\n endCol: number\n}\n\nexport interface SearchState {\n active: boolean\n query: string\n matches: SearchMatch[]\n currentMatch: number // Index into matches, -1 when no matches\n cursorPosition: number // Cursor position within query string\n}\n\nexport type SearchAction =\n | { type: \"open\" }\n | { type: \"close\" }\n | { type: \"input\"; char: string }\n | { type: \"backspace\" }\n | { type: \"nextMatch\" } // Enter\n | { type: \"prevMatch\" } // Shift+Enter\n | { type: \"cursorLeft\" }\n | { type: \"cursorRight\" }\n | { type: \"cursorToStart\" } // Ctrl+A / Home\n | { type: \"cursorToEnd\" } // Ctrl+E / End\n | { type: \"deleteWordBack\" } // Ctrl+W / Alt+Backspace\n | { type: \"deleteToStart\" } // Ctrl+U\n\nexport type SearchEffect = { type: \"scrollTo\"; row: number } | { type: \"render\" }\n\nexport function createSearchState(): SearchState {\n return {\n active: false,\n query: \"\",\n matches: [],\n currentMatch: -1,\n cursorPosition: 0,\n }\n}\n\n/**\n * Update search state. searchFn is called when query changes to find matches.\n */\nexport function searchUpdate(\n action: SearchAction,\n state: SearchState,\n searchFn?: (query: string) => SearchMatch[],\n): [SearchState, SearchEffect[]] {\n switch (action.type) {\n case \"open\":\n return [\n { ...state, active: true, query: \"\", matches: [], currentMatch: -1, cursorPosition: 0 },\n [{ type: \"render\" }],\n ]\n\n case \"close\":\n return [createSearchState(), [{ type: \"render\" }]]\n\n case \"input\": {\n const query = state.query.slice(0, state.cursorPosition) + action.char + state.query.slice(state.cursorPosition)\n const cursorPosition = state.cursorPosition + 1\n const matches = searchFn ? searchFn(query) : []\n const currentMatch = matches.length > 0 ? 0 : -1\n const effects: SearchEffect[] = [{ type: \"render\" }]\n if (currentMatch >= 0) {\n effects.push({ type: \"scrollTo\", row: matches[0]!.row })\n }\n return [{ ...state, query, cursorPosition, matches, currentMatch }, effects]\n }\n\n case \"backspace\": {\n if (state.cursorPosition === 0) return [state, []]\n const query = state.query.slice(0, state.cursorPosition - 1) + state.query.slice(state.cursorPosition)\n const cursorPosition = state.cursorPosition - 1\n const matches = searchFn ? searchFn(query) : []\n const currentMatch = matches.length > 0 ? 0 : -1\n const effects: SearchEffect[] = [{ type: \"render\" }]\n if (currentMatch >= 0) {\n effects.push({ type: \"scrollTo\", row: matches[0]!.row })\n }\n return [{ ...state, query, cursorPosition, matches, currentMatch }, effects]\n }\n\n case \"nextMatch\": {\n if (state.matches.length === 0) return [state, []]\n const currentMatch = (state.currentMatch + 1) % state.matches.length\n return [\n { ...state, currentMatch },\n [{ type: \"scrollTo\", row: state.matches[currentMatch]!.row }, { type: \"render\" }],\n ]\n }\n\n case \"prevMatch\": {\n if (state.matches.length === 0) return [state, []]\n const currentMatch = (state.currentMatch - 1 + state.matches.length) % state.matches.length\n return [\n { ...state, currentMatch },\n [{ type: \"scrollTo\", row: state.matches[currentMatch]!.row }, { type: \"render\" }],\n ]\n }\n\n case \"cursorLeft\":\n if (state.cursorPosition === 0) return [state, []]\n return [{ ...state, cursorPosition: state.cursorPosition - 1 }, []]\n\n case \"cursorRight\":\n if (state.cursorPosition >= state.query.length) return [state, []]\n return [{ ...state, cursorPosition: state.cursorPosition + 1 }, []]\n\n case \"cursorToStart\":\n if (state.cursorPosition === 0) return [state, []]\n return [{ ...state, cursorPosition: 0 }, []]\n\n case \"cursorToEnd\":\n if (state.cursorPosition >= state.query.length) return [state, []]\n return [{ ...state, cursorPosition: state.query.length }, []]\n\n case \"deleteWordBack\": {\n if (state.cursorPosition === 0) return [state, []]\n // Find the start of the previous word (skip trailing whitespace, then non-whitespace)\n let pos = state.cursorPosition\n while (pos > 0 && /\\s/.test(state.query[pos - 1] ?? \"\")) pos--\n while (pos > 0 && !/\\s/.test(state.query[pos - 1] ?? \"\")) pos--\n const query = state.query.slice(0, pos) + state.query.slice(state.cursorPosition)\n const cursorPosition = pos\n const matches = searchFn ? searchFn(query) : []\n const currentMatch = matches.length > 0 ? 0 : -1\n const effects: SearchEffect[] = [{ type: \"render\" }]\n if (currentMatch >= 0) {\n effects.push({ type: \"scrollTo\", row: matches[0]!.row })\n }\n return [{ ...state, query, cursorPosition, matches, currentMatch }, effects]\n }\n\n case \"deleteToStart\": {\n if (state.cursorPosition === 0) return [state, []]\n const query = state.query.slice(state.cursorPosition)\n const cursorPosition = 0\n const matches = searchFn ? searchFn(query) : []\n const currentMatch = matches.length > 0 ? 0 : -1\n const effects: SearchEffect[] = [{ type: \"render\" }]\n if (currentMatch >= 0) {\n effects.push({ type: \"scrollTo\", row: matches[0]!.row })\n }\n return [{ ...state, query, cursorPosition, matches, currentMatch }, effects]\n }\n }\n}\n\n/**\n * Render the search bar as an ANSI string.\n * Format: \" / query [2/15]\" or \" / query [no matches]\"\n */\nexport function renderSearchBar(state: SearchState, cols: number): string {\n const prefix = \" / \"\n const matchInfo =\n state.matches.length > 0\n ? ` [${state.currentMatch + 1}/${state.matches.length}]`\n : state.query\n ? \" [no matches]\"\n : \"\"\n\n const content = prefix + state.query + matchInfo\n const padded = content.padEnd(cols)\n\n // Inverse video: ESC[7m ... ESC[27m\n return `\\x1b[7m${padded}\\x1b[27m`\n}\n","/**\n * Pane Manager - Pure functions for manipulating split layout trees.\n *\n * All functions are pure: they return new layout trees, never mutate.\n * The layout tree is a binary tree where leaves are panes and internal\n * nodes are splits (horizontal or vertical) with a ratio.\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type LayoutNode =\n | { type: \"leaf\"; id: string }\n | {\n type: \"split\"\n direction: \"horizontal\" | \"vertical\"\n ratio: number\n first: LayoutNode\n second: LayoutNode\n }\n\n// ============================================================================\n// Construction\n// ============================================================================\n\n/** Create a single-pane layout */\nexport function createLeaf(id: string): LayoutNode {\n return { type: \"leaf\", id }\n}\n\n// ============================================================================\n// Tree Queries\n// ============================================================================\n\n/** Get all leaf pane IDs in depth-first left-to-right order */\nexport function getPaneIds(layout: LayoutNode): string[] {\n if (layout.type === \"leaf\") return [layout.id]\n return [...getPaneIds(layout.first), ...getPaneIds(layout.second)]\n}\n\n/** Find the next/previous pane in tab order (depth-first left-to-right) */\nexport function getTabOrder(layout: LayoutNode): string[] {\n return getPaneIds(layout)\n}\n\n// ============================================================================\n// Tree Mutations (Pure)\n// ============================================================================\n\n/**\n * Split a pane into two. Returns new layout tree with the target pane split.\n * The original pane becomes the first child; the new pane becomes the second.\n */\nexport function splitPane(\n layout: LayoutNode,\n targetPaneId: string,\n direction: \"horizontal\" | \"vertical\",\n newPaneId: string,\n ratio = 0.5,\n): LayoutNode {\n const clampedRatio = clampRatio(ratio)\n\n if (layout.type === \"leaf\") {\n if (layout.id === targetPaneId) {\n return {\n type: \"split\",\n direction,\n ratio: clampedRatio,\n first: { type: \"leaf\", id: targetPaneId },\n second: { type: \"leaf\", id: newPaneId },\n }\n }\n return layout\n }\n\n // Recurse into children\n const newFirst = splitPane(layout.first, targetPaneId, direction, newPaneId, ratio)\n const newSecond = splitPane(layout.second, targetPaneId, direction, newPaneId, ratio)\n\n if (newFirst === layout.first && newSecond === layout.second) return layout\n\n return { ...layout, first: newFirst, second: newSecond }\n}\n\n/**\n * Remove a pane from the layout. The sibling takes the full space.\n * Returns null if the removed pane was the last one.\n */\nexport function removePane(layout: LayoutNode, paneId: string): LayoutNode | null {\n if (layout.type === \"leaf\") {\n return layout.id === paneId ? null : layout\n }\n\n // Check if either direct child is the target leaf\n if (layout.first.type === \"leaf\" && layout.first.id === paneId) {\n return layout.second\n }\n if (layout.second.type === \"leaf\" && layout.second.id === paneId) {\n return layout.first\n }\n\n // Recurse\n const newFirst = removePane(layout.first, paneId)\n const newSecond = removePane(layout.second, paneId)\n\n // If a subtree collapsed, promote the survivor\n if (newFirst === null) return newSecond\n if (newSecond === null) return newFirst\n\n if (newFirst === layout.first && newSecond === layout.second) return layout\n\n return { ...layout, first: newFirst, second: newSecond }\n}\n\n/** Swap two panes' positions in the layout */\nexport function swapPanes(layout: LayoutNode, paneId1: string, paneId2: string): LayoutNode {\n if (layout.type === \"leaf\") {\n if (layout.id === paneId1) return { type: \"leaf\", id: paneId2 }\n if (layout.id === paneId2) return { type: \"leaf\", id: paneId1 }\n return layout\n }\n\n const newFirst = swapPanes(layout.first, paneId1, paneId2)\n const newSecond = swapPanes(layout.second, paneId1, paneId2)\n\n if (newFirst === layout.first && newSecond === layout.second) return layout\n\n return { ...layout, first: newFirst, second: newSecond }\n}\n\n/**\n * Resize a split: adjust the ratio of the nearest ancestor split\n * that contains the target pane as its first child.\n * Positive delta = grow (first child gets more), negative = shrink.\n */\nexport function resizeSplit(layout: LayoutNode, paneId: string, delta: number): LayoutNode {\n if (layout.type === \"leaf\") return layout\n\n const firstIds = getPaneIds(layout.first)\n\n if (firstIds.includes(paneId)) {\n // Pane is in the first child — adjust this split's ratio\n const newRatio = clampRatio(layout.ratio + delta)\n if (newRatio === layout.ratio) return layout\n\n return { ...layout, ratio: newRatio }\n }\n\n const secondIds = getPaneIds(layout.second)\n\n if (secondIds.includes(paneId)) {\n // Pane is in the second child — shrink ratio (give less to first)\n const newRatio = clampRatio(layout.ratio - delta)\n if (newRatio === layout.ratio) return layout\n\n return { ...layout, ratio: newRatio }\n }\n\n return layout\n}\n\n// ============================================================================\n// Navigation\n// ============================================================================\n\n/**\n * Find the pane adjacent to the given pane in a direction.\n *\n * For left/right: looks for siblings in horizontal splits.\n * For up/down: looks for siblings in vertical splits.\n *\n * Returns null if no adjacent pane exists in that direction.\n */\nexport function findAdjacentPane(\n layout: LayoutNode,\n paneId: string,\n direction: \"left\" | \"right\" | \"up\" | \"down\",\n): string | null {\n const path = findPath(layout, paneId)\n if (!path) return null\n\n const splitDirection = direction === \"left\" || direction === \"right\" ? \"horizontal\" : \"vertical\"\n const goToSecond = direction === \"right\" || direction === \"down\"\n\n // Walk up the path looking for a relevant split\n for (let i = path.length - 1; i >= 0; i--) {\n const step = path[i]!\n if (step.node.type !== \"split\") continue\n if (step.node.direction !== splitDirection) continue\n\n // We came from 'first' and want to go to second (right/down)\n if (step.side === \"first\" && goToSecond) {\n return firstLeaf(step.node.second)\n }\n\n // We came from 'second' and want to go to first (left/up)\n if (step.side === \"second\" && !goToSecond) {\n return lastLeaf(step.node.first)\n }\n }\n\n return null\n}\n\n// ============================================================================\n// Internal Helpers\n// ============================================================================\n\nfunction clampRatio(ratio: number): number {\n return Math.max(0.1, Math.min(0.9, ratio))\n}\n\n/** Get the first (leftmost/topmost) leaf ID */\nfunction firstLeaf(node: LayoutNode): string {\n if (node.type === \"leaf\") return node.id\n return firstLeaf(node.first)\n}\n\n/** Get the last (rightmost/bottommost) leaf ID */\nfunction lastLeaf(node: LayoutNode): string {\n if (node.type === \"leaf\") return node.id\n return lastLeaf(node.second)\n}\n\ninterface PathStep {\n node: LayoutNode\n side: \"first\" | \"second\"\n}\n\n/** Find the path from root to a leaf, recording which side we took at each split */\nfunction findPath(layout: LayoutNode, paneId: string): PathStep[] | null {\n if (layout.type === \"leaf\") {\n return layout.id === paneId ? [] : null\n }\n\n const firstPath = findPath(layout.first, paneId)\n if (firstPath !== null) {\n return [{ node: layout, side: \"first\" }, ...firstPath]\n }\n\n const secondPath = findPath(layout.second, paneId)\n if (secondPath !== null) {\n return [{ node: layout, side: \"second\" }, ...secondPath]\n }\n\n return null\n}\n","/**\n * ANSI output constants and terminal control utilities.\n *\n * NOTE: Buffer rendering is handled by pipeline/output-phase.ts.\n * This file contains only terminal control sequences and constants.\n */\n\nimport { hostname } from \"node:os\"\nimport {\n enableMouse as _enableMouse,\n disableMouse as _disableMouse,\n enableKittyKeyboard as _enableKittyKeyboard,\n disableKittyKeyboard as _disableKittyKeyboard,\n} from \"@silvery/ansi\"\n\n// ============================================================================\n// ANSI Escape Codes\n// ============================================================================\n\nconst ESC = \"\\x1b\"\nconst CSI = `${ESC}[`\n\n// Cursor control\nconst CURSOR_HIDE = `${CSI}?25l`\nconst CURSOR_SHOW = `${CSI}?25h`\nconst CURSOR_HOME = `${CSI}H`\n\n// Synchronized Update Mode (DEC private mode 2026)\n// Tells the terminal to batch output and paint atomically, preventing tearing.\n// Supported by: Ghostty, Kitty, WezTerm, iTerm2, Foot, Alacritty 0.14+, tmux 3.2+\n// Terminals that don't support it safely ignore these sequences.\nconst SYNC_BEGIN = `${CSI}?2026h`\nconst SYNC_END = `${CSI}?2026l`\n\n// Style reset\nconst RESET = `${CSI}0m`\n\n// SGR (Select Graphic Rendition) codes\nconst SGR = {\n // Attributes\n bold: 1,\n dim: 2,\n italic: 3,\n underline: 4,\n blink: 5,\n inverse: 7,\n hidden: 8,\n strikethrough: 9,\n\n // Attribute resets\n boldOff: 22, // Also resets dim\n italicOff: 23,\n underlineOff: 24,\n blinkOff: 25,\n inverseOff: 27,\n hiddenOff: 28,\n strikethroughOff: 29,\n\n // Colors (foreground)\n fgDefault: 39,\n fgBlack: 30,\n fgRed: 31,\n fgGreen: 32,\n fgYellow: 33,\n fgBlue: 34,\n fgMagenta: 35,\n fgCyan: 36,\n fgWhite: 37,\n fgBrightBlack: 90,\n fgBrightRed: 91,\n fgBrightGreen: 92,\n fgBrightYellow: 93,\n fgBrightBlue: 94,\n fgBrightMagenta: 95,\n fgBrightCyan: 96,\n fgBrightWhite: 97,\n\n // Colors (background)\n bgDefault: 49,\n bgBlack: 40,\n bgRed: 41,\n bgGreen: 42,\n bgYellow: 43,\n bgBlue: 44,\n bgMagenta: 45,\n bgCyan: 46,\n bgWhite: 47,\n bgBrightBlack: 100,\n bgBrightRed: 101,\n bgBrightGreen: 102,\n bgBrightYellow: 103,\n bgBrightBlue: 104,\n bgBrightMagenta: 105,\n bgBrightCyan: 106,\n bgBrightWhite: 107,\n} as const\n\n// ============================================================================\n// Cursor Movement\n// ============================================================================\n\n/**\n * Generate ANSI sequence to move cursor to position.\n * Terminal positions are 1-indexed.\n */\nfunction moveCursor(x: number, y: number): string {\n return `${CSI}${y + 1};${x + 1}H`\n}\n\n/**\n * Generate ANSI sequence to move cursor up N lines.\n */\nfunction cursorUp(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}A`\n return `${CSI}${n}A`\n}\n\n/**\n * Generate ANSI sequence to move cursor down N lines.\n */\nfunction cursorDown(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}B`\n return `${CSI}${n}B`\n}\n\n/**\n * Generate ANSI sequence to move cursor right N columns.\n */\nfunction cursorRight(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}C`\n return `${CSI}${n}C`\n}\n\n/**\n * Generate ANSI sequence to move cursor left N columns.\n */\nfunction cursorLeft(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}D`\n return `${CSI}${n}D`\n}\n\n/**\n * Generate ANSI sequence to move cursor to column.\n */\nfunction cursorToColumn(x: number): string {\n return `${CSI}${x + 1}G`\n}\n\n// ============================================================================\n// Cursor Shape (DECSCUSR)\n// ============================================================================\n\n/**\n * Terminal cursor shape. Combined with blink parameter in setCursorStyle().\n */\nexport type CursorShape = \"block\" | \"underline\" | \"bar\"\n\nconst CURSOR_SHAPE_CODES: Record<CursorShape, { blink: number; steady: number }> = {\n block: { blink: 1, steady: 2 },\n underline: { blink: 3, steady: 4 },\n bar: { blink: 5, steady: 6 },\n}\n\n/**\n * Set the terminal cursor shape via DECSCUSR (CSI Ps SP q).\n *\n * Supported by: xterm, Ghostty, Kitty, WezTerm, iTerm2, Alacritty, foot.\n * Terminals that don't support it safely ignore the sequence.\n *\n * @param shape - \"block\", \"underline\", or \"bar\"\n * @param blink - Whether the cursor should blink (default: false)\n */\nexport function setCursorStyle(shape: CursorShape, blink = false): string {\n const code = blink ? CURSOR_SHAPE_CODES[shape].blink : CURSOR_SHAPE_CODES[shape].steady\n return `${CSI}${code} q`\n}\n\n/**\n * Reset the terminal cursor style to the terminal's default (DECSCUSR 0).\n */\nexport function resetCursorStyle(): string {\n return `${CSI}0 q`\n}\n\n// ============================================================================\n// Terminal Control\n// ============================================================================\n\n/**\n * Enter alternate screen buffer, clear screen, and hide cursor.\n * Cursor is hidden by default - applications must explicitly show it for text input.\n *\n * The clear screen (\\x1b[2J) and cursor home (\\x1b[H) are essential after entering\n * the alternate buffer to ensure a clean slate. Without this, the terminal may have\n * leftover content from previous sessions that causes rendering artifacts like\n * content appearing at wrong Y positions (bug km-x7ih).\n */\nexport function enterAlternateScreen(): string {\n return `${CSI}?1049h${CSI}2J${CURSOR_HOME}${CURSOR_HIDE}`\n}\n\n/**\n * Leave alternate screen buffer and restore cursor.\n * Includes SYNC_END as a safety belt — ensures synchronized update mode is\n * reset even if the process was interrupted mid-render. Sending SYNC_END\n * when not in sync mode is a harmless no-op.\n */\nexport function leaveAlternateScreen(): string {\n return `${SYNC_END}${CURSOR_SHOW}${CSI}?1049l`\n}\n\n// Re-export from @silvery/ansi — canonical implementations\nexport const enableMouse = _enableMouse\nexport const disableMouse = _disableMouse\n\n/**\n * Kitty keyboard protocol flags (bitfield).\n *\n * | Flag | Bit | Description |\n * | ---- | --- | ---------------------------------------------- |\n * | 1 | 0 | Disambiguate escape codes |\n * | 2 | 1 | Report event types (press/repeat/release) |\n * | 4 | 2 | Report alternate keys |\n * | 8 | 3 | Report all keys as escape codes |\n * | 16 | 4 | Report associated text |\n */\nexport const KittyFlags = {\n DISAMBIGUATE: 1,\n REPORT_EVENTS: 2,\n REPORT_ALTERNATE: 4,\n REPORT_ALL_KEYS: 8,\n REPORT_TEXT: 16,\n} as const\n\n/**\n * Enable Kitty keyboard protocol (push mode).\n * Sends CSI > flags u to opt into the specified modes.\n * Default flags=11 (DISAMBIGUATE | REPORT_EVENTS | REPORT_ALL_KEYS) —\n * enables modifier-only key reporting needed for useModifierKeys() Cmd tracking.\n * Supported: Ghostty, Kitty, WezTerm, foot. Ignored by unsupported terminals.\n *\n * @param flags Bitfield of KittyFlags (default: DISAMBIGUATE)\n */\nexport const enableKittyKeyboard = _enableKittyKeyboard\nexport function queryKittyKeyboard(): string {\n return `${CSI}?u`\n}\nexport const disableKittyKeyboard = _disableKittyKeyboard\n\n// ============================================================================\n// Terminal Notifications\n// ============================================================================\n\n/** BEL character — basic terminal bell/notification */\nexport const BEL = \"\\x07\"\n\n/** iTerm2 notification (OSC 9) */\nexport function notifyITerm2(message: string): string {\n return `${ESC}]9;${message}${BEL}`\n}\n\n/** Kitty notification (OSC 99) with optional title */\nexport function notifyKitty(message: string, opts?: { title?: string }): string {\n const params = opts?.title ? `;t=t;${opts.title}` : \"\"\n return `${ESC}]99;i=1:d=0${params};${message}${ESC}\\\\`\n}\n\n/**\n * Send a terminal notification using the best available method.\n *\n * Auto-detects terminal type via TERM_PROGRAM / TERM env vars:\n * - iTerm2 → OSC 9\n * - Kitty → OSC 99\n * - Others → BEL (audible/visual bell)\n */\nexport function notify(stdout: NodeJS.WriteStream, message: string, opts?: { title?: string }): void {\n const termProgram = process.env.TERM_PROGRAM ?? \"\"\n const term = process.env.TERM ?? \"\"\n\n if (termProgram === \"iTerm.app\") {\n stdout.write(notifyITerm2(message))\n } else if (term === \"xterm-kitty\") {\n stdout.write(notifyKitty(message, opts))\n } else {\n stdout.write(BEL)\n }\n}\n\n// ============================================================================\n// Window Title (OSC 0/2)\n// ============================================================================\n\n/**\n * Set the terminal window title using OSC 2 (window title only).\n * Does not affect icon title (tab name in some terminals).\n * Widely supported: xterm, Ghostty, iTerm2, Kitty, WezTerm, Alacritty, foot.\n */\nexport function setWindowTitle(stdout: NodeJS.WriteStream, title: string): void {\n stdout.write(`${ESC}]2;${title}${BEL}`)\n}\n\n/**\n * Set both the window title and icon title using OSC 0.\n * Some terminals treat OSC 0 as equivalent to OSC 2; others also change the\n * dock/taskbar icon name.\n */\nexport function setWindowAndIconTitle(stdout: NodeJS.WriteStream, title: string): void {\n stdout.write(`${ESC}]0;${title}${BEL}`)\n}\n\n/**\n * Reset the terminal window title by sending an empty OSC 2 sequence.\n * The terminal typically reverts to its default title (shell command, etc.).\n */\nexport function resetWindowTitle(stdout: NodeJS.WriteStream): void {\n stdout.write(`${ESC}]2;${BEL}`)\n}\n\n// ============================================================================\n// Directory Reporting\n// ============================================================================\n\n/** Report current working directory to the terminal via OSC 7.\n * Used by terminals (iTerm2, Ghostty, WezTerm) for tab/split directory inheritance.\n */\nexport function reportDirectory(stdout: NodeJS.WriteStream, path: string): void {\n // OSC 7 format: ESC ] 7 ; file://hostname/path BEL\n const host = hostname()\n const encoded = encodeURI(path).replace(/#/g, \"%23\")\n stdout.write(`${ESC}]7;file://${host}${encoded}${BEL}`)\n}\n\n// ============================================================================\n// Mouse Cursor Shape (OSC 22)\n// ============================================================================\n\n/**\n * Mouse cursor shape names for OSC 22.\n *\n * Uses X11/CSS cursor names. Supported by: Ghostty, Kitty (>=0.33), foot,\n * WezTerm (partial). Terminals that don't support OSC 22 safely ignore it.\n */\nexport type MouseCursorShape = \"default\" | \"text\" | \"pointer\" | \"crosshair\" | \"move\" | \"not-allowed\" | \"wait\" | \"help\"\n\n/**\n * Generate OSC 22 sequence to set the mouse cursor shape.\n *\n * @param shape - X11/CSS cursor name\n * @returns ANSI escape sequence string\n */\nexport function setMouseCursorShape(shape: MouseCursorShape): string {\n return `${ESC}]22;${shape}${BEL}`\n}\n\n/**\n * Generate OSC 22 sequence to reset mouse cursor to default.\n *\n * @returns ANSI escape sequence string\n */\nexport function resetMouseCursorShape(): string {\n return `${ESC}]22;default${BEL}`\n}\n\n// ============================================================================\n// Export Constants\n// ============================================================================\n\nexport const ANSI = {\n ESC,\n CSI,\n CURSOR_HIDE,\n CURSOR_SHOW,\n CURSOR_HOME,\n SYNC_BEGIN,\n SYNC_END,\n RESET,\n SGR,\n moveCursor,\n cursorUp,\n cursorDown,\n cursorLeft,\n cursorRight,\n cursorToColumn,\n} as const\n","/**\n * useExit — programmatic exit hook.\n *\n * Thin wrapper over useApp().exit that throws outside a runtime\n * (unlike useApp which returns no-ops in static mode).\n *\n * Prefer `return \"exit\"` from useInput handlers when possible.\n * Use useExit() for imperative exit from event handlers, timers, etc.\n */\n\nimport { useContext } from \"react\"\nimport { RuntimeContext } from \"../context\"\n\n/**\n * Returns a function that exits the app.\n * Throws if called outside a runtime (run(), createApp(), test renderer).\n */\nexport function useExit(): () => void {\n const rt = useContext(RuntimeContext)\n if (!rt) throw new Error(\"useExit must be used within run() or createApp()\")\n return rt.exit\n}\n","/**\n * Terminal scroll region (DECSTBM) utilities.\n *\n * Scroll regions tell the terminal to natively scroll content within\n * a defined row range, which is faster than re-rendering all cells.\n *\n * DECSTBM (DEC Set Top and Bottom Margins) is supported by most modern\n * terminal emulators: xterm, iTerm2, Kitty, Ghostty, WezTerm, etc.\n */\n\nconst ESC = \"\\x1b\"\n\n/** Set terminal scroll region (1-indexed top and bottom rows). */\nexport function setScrollRegion(stdout: NodeJS.WriteStream, top: number, bottom: number): void {\n stdout.write(`${ESC}[${top};${bottom}r`)\n}\n\n/** Reset scroll region to full terminal. */\nexport function resetScrollRegion(stdout: NodeJS.WriteStream): void {\n stdout.write(`${ESC}[r`)\n}\n\n/** Scroll content up by N lines within the current scroll region. */\nexport function scrollUp(stdout: NodeJS.WriteStream, lines: number = 1): void {\n stdout.write(`${ESC}[${lines}S`)\n}\n\n/** Scroll content down by N lines within the current scroll region. */\nexport function scrollDown(stdout: NodeJS.WriteStream, lines: number = 1): void {\n stdout.write(`${ESC}[${lines}T`)\n}\n\n/** Move cursor to a specific position (1-indexed row and column). */\nexport function moveCursor(stdout: NodeJS.WriteStream, row: number, col: number): void {\n stdout.write(`${ESC}[${row};${col}H`)\n}\n\nexport interface ScrollRegionConfig {\n /** Top row of the scrollable area (0-indexed). */\n top: number\n /** Bottom row of the scrollable area (0-indexed). */\n bottom: number\n /** Whether scroll region optimization is enabled. */\n enabled: boolean\n}\n\n/**\n * Detect if the terminal likely supports DECSTBM scroll regions.\n *\n * Most modern terminals do (xterm, iTerm2, Kitty, Ghostty, WezTerm, etc.)\n * but some (e.g., Linux console) may not handle them correctly.\n */\nexport function supportsScrollRegions(): boolean {\n const term = process.env.TERM ?? \"\"\n const termProgram = process.env.TERM_PROGRAM ?? \"\"\n\n // Known-good terminal programs\n if (termProgram === \"iTerm.app\" || termProgram === \"WezTerm\" || termProgram === \"ghostty\" || termProgram === \"vscode\")\n return true\n\n // Known-good TERM values\n if (term.includes(\"xterm\") || term.includes(\"screen\") || term.includes(\"tmux\") || term.includes(\"kitty\")) return true\n\n // Linux console doesn't support DECSTBM\n if (term === \"linux\") return false\n\n // Default: assume support for any term that's not empty\n return term !== \"\"\n}\n","/**\n * useCursor - Show and position the terminal's blinking cursor.\n *\n * Maps component-relative (col, row) to absolute terminal coordinates\n * using useScrollRect. Per-instance last-writer-wins: only one cursor\n * can be active at a time per silvery instance (the terminal has one hardware cursor).\n *\n * Cursor state is isolated per silvery instance via CursorContext. Each runtime\n * (run(), createApp(), test renderer) provides its own CursorProvider so\n * multiple silvery instances don't fight over cursor position.\n *\n * Falls back to module-level globals when no CursorProvider is present\n * (backward compatibility / deprecation path).\n */\n\nimport React, {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n type ReactNode,\n} from \"react\"\nimport type { CursorShape } from \"@silvery/ag-term/output\"\nimport type { BoxProps } from \"@silvery/ag/types\"\nimport { NodeContext } from \"@silvery/ag-react/context\"\nimport { useScrollRect, type Rect } from \"./useLayout\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CursorPosition {\n /** Column offset within the component (0-indexed) */\n col: number\n /** Row offset within the component (0-indexed) */\n row: number\n /** Whether the cursor should be visible. Default: true */\n visible?: boolean\n /** Terminal cursor shape (DECSCUSR). Default: terminal default */\n shape?: CursorShape\n}\n\nexport interface CursorState {\n /** Absolute terminal X position (0-indexed) */\n x: number\n /** Absolute terminal Y position (0-indexed) */\n y: number\n /** Whether cursor is visible */\n visible: boolean\n /** Terminal cursor shape (DECSCUSR) */\n shape?: CursorShape\n}\n\n// ============================================================================\n// Cursor Accessors — imperative interface for non-React consumers\n// ============================================================================\n\n/**\n * Imperative cursor accessors for non-React code (scheduler, output phase).\n * Created by createCursorStore() and threaded to consumers that can't use hooks.\n */\nexport interface CursorAccessors {\n getCursorState(): CursorState | null\n subscribeCursor(listener: () => void): () => void\n}\n\n// ============================================================================\n// Cursor Store — per-instance state + accessors\n// ============================================================================\n\nexport interface CursorStore {\n state: CursorState | null\n listeners: Set<() => void>\n accessors: CursorAccessors\n setCursorState(state: CursorState | null): void\n}\n\n/**\n * Create an isolated cursor store. Each silvery instance gets one.\n * Returns the store (for CursorProvider) and accessors (for scheduler/output).\n */\nexport function createCursorStore(): CursorStore {\n const store: CursorStore = {\n state: null,\n listeners: new Set(),\n accessors: null!,\n setCursorState(s: CursorState | null) {\n store.state = s\n for (const listener of store.listeners) listener()\n },\n }\n store.accessors = {\n getCursorState: () => store.state,\n subscribeCursor: (listener: () => void) => {\n store.listeners.add(listener)\n return () => {\n store.listeners.delete(listener)\n }\n },\n }\n return store\n}\n\n// ============================================================================\n// React Context\n// ============================================================================\n\nconst CursorCtx = createContext<CursorStore | null>(null)\n\n/**\n * Provider that gives its subtree an isolated cursor store.\n * Wrap your silvery app root in this to isolate cursor state per instance.\n */\nexport function CursorProvider({ store, children }: { store: CursorStore; children?: ReactNode }) {\n return React.createElement(CursorCtx.Provider, { value: store }, children)\n}\n\n// ============================================================================\n// Module-level Fallback (deprecated — for bare render() without provider)\n// ============================================================================\n\nlet _globalCursorState: CursorState | null = null\nlet _globalCursorListeners = new Set<() => void>()\n\nfunction globalSetCursorState(state: CursorState | null): void {\n _globalCursorState = state\n for (const listener of _globalCursorListeners) listener()\n}\n\nfunction globalGetCursorState(): CursorState | null {\n return _globalCursorState\n}\n\nfunction globalSubscribeCursor(listener: () => void): () => void {\n _globalCursorListeners.add(listener)\n return () => {\n _globalCursorListeners.delete(listener)\n }\n}\n\n/** For testing -- reset global fallback state between tests. */\nexport function resetCursorState(): void {\n _globalCursorState = null\n _globalCursorListeners = new Set()\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\n/**\n * Show and position the terminal's blinking cursor within this component.\n *\n * The cursor position is relative to the component's screen position.\n * Only one cursor can be active per silvery instance -- last caller with visible=true wins.\n *\n * Uses CursorContext if a CursorProvider is present (per-instance isolation).\n * Falls back to module-level globals otherwise (backward compat).\n */\nexport function useCursor(position: CursorPosition): void {\n const { col, row, visible = true, shape } = position\n const store = useContext(CursorCtx)\n const node = useContext(NodeContext)\n\n // Resolve set/get functions from context or global fallback\n const set = store ? store.setCursorState.bind(store) : globalSetCursorState\n const get = store ? store.accessors.getCursorState : globalGetCursorState\n\n // Compute content area offset from the parent node's border + padding.\n // useScrollRect provides the node's border-box rect, but cursor\n // col/row are relative to the content area (inside border + padding).\n const props = node?.props as BoxProps | undefined\n const padLeft = props?.paddingLeft ?? props?.paddingX ?? props?.padding ?? 0\n const padTop = props?.paddingTop ?? props?.paddingY ?? props?.padding ?? 0\n const borderLeft = props?.borderStyle ? 1 : 0\n const borderTop = props?.borderStyle ? 1 : 0\n const contentOffsetX = borderLeft + padLeft\n const contentOffsetY = borderTop + padTop\n\n // Keep current args in refs so the callback always reads fresh values\n const colRef = useRef(col)\n const rowRef = useRef(row)\n const visibleRef = useRef(visible)\n const shapeRef = useRef(shape)\n const setRef = useRef(set)\n const getRef = useRef(get)\n const lastRectRef = useRef<Rect | null>(null)\n const contentOffsetXRef = useRef(contentOffsetX)\n const contentOffsetYRef = useRef(contentOffsetY)\n colRef.current = col\n rowRef.current = row\n visibleRef.current = visible\n shapeRef.current = shape\n setRef.current = set\n getRef.current = get\n contentOffsetXRef.current = contentOffsetX\n contentOffsetYRef.current = contentOffsetY\n\n // Called synchronously during layout (useLayoutEffect) whenever\n // the component's screen position changes.\n useScrollRect(\n useCallback((rect) => {\n lastRectRef.current = rect\n if (!visibleRef.current) {\n return\n }\n setRef.current({\n x: rect.x + contentOffsetXRef.current + colRef.current,\n y: rect.y + contentOffsetYRef.current + rowRef.current,\n visible: true,\n shape: shapeRef.current,\n })\n }, []),\n )\n\n // When col/row/shape change without a layout change, update cursor\n // position from the last known screen rect. This handles the common case\n // where typing moves the cursor within a component but the component's\n // layout position stays the same (e.g., TextInput cursor moves on keystroke).\n useLayoutEffect(() => {\n const rect = lastRectRef.current\n if (!rect || !visible) return\n set({\n x: rect.x + contentOffsetX + col,\n y: rect.y + contentOffsetY + row,\n visible: true,\n shape,\n })\n }, [col, row, contentOffsetX, contentOffsetY, shape, visible, set])\n\n // On unmount or when visible becomes false, clear cursor state\n useEffect(() => {\n if (!visible) {\n // If we are hiding, clear state now\n const current = getRef.current()\n if (current) {\n setRef.current(null)\n }\n }\n\n return () => {\n // On unmount, clear cursor state\n setRef.current(null)\n }\n }, [visible])\n}\n\n// ============================================================================\n// Exports for scheduler integration\n// ============================================================================\n\n/**\n * @deprecated Use CursorAccessors from createCursorStore() instead.\n * These module-level functions are the global fallback for backward compat.\n */\nfunction getCursorState(): CursorState | null {\n return globalGetCursorState()\n}\n\nfunction subscribeCursor(listener: () => void): () => void {\n return globalSubscribeCursor(listener)\n}\n\nexport { getCursorState, subscribeCursor }\n","/**\n * ErrorBoundary — Built-in error boundary for silvery apps.\n *\n * Catches render errors in the component tree and displays a rich\n * error message with source location, code excerpt, and stack trace\n * using low-level silvery host elements (no dependency on Box/Text\n * from @silvery/ag-react/ui). This is the default root wrapper for all\n * silvery apps — createApp() and run() wrap the element tree with it\n * automatically.\n *\n * Uses `silvery-box` and `silvery-text` host elements directly to\n * avoid circular deps with higher-level component libraries.\n *\n * @packageDocumentation\n */\n\nimport * as fs from \"node:fs\"\nimport React, { Component } from \"react\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface SilveryErrorBoundaryProps {\n children?: React.ReactNode\n /** Called when an error is caught. Use for logging or cleanup. */\n onError?: (error: Error) => void\n}\n\ninterface SilveryErrorBoundaryState {\n error: Error | null\n}\n\n// ============================================================================\n// Stack parsing utilities\n// ============================================================================\n\n/**\n * Parse a stack line to extract function name, file, line, column.\n * Handles both `at Foo (file:line:col)` and `at file:line:col` formats.\n */\nfunction parseStackLine(line: string): { function?: string; file?: string; line?: number; column?: number } | null {\n const trimmed = line.trim()\n if (!trimmed.startsWith(\"at \")) return null\n\n const rest = trimmed.slice(3)\n // Match: functionName (file:line:col)\n const match1 = rest.match(/^(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)$/)\n if (match1) {\n return {\n function: match1[1],\n file: match1[2],\n line: Number(match1[3]),\n column: Number(match1[4]),\n }\n }\n // Match: file:line:col (no function name)\n const match2 = rest.match(/^(.+?):(\\d+):(\\d+)$/)\n if (match2) {\n return { file: match2[1], line: Number(match2[2]), column: Number(match2[3]) }\n }\n return null\n}\n\n/**\n * Clean up file path by removing cwd prefix and file:// protocol.\n */\nfunction cleanupPath(filePath: string | undefined): string | undefined {\n if (!filePath) return filePath\n let p = filePath\n const cwdPath = process.cwd()\n // Remove file:// protocol\n p = p.replace(/^file:\\/\\//, \"\")\n // Remove cwd prefix\n for (const prefix of [cwdPath, `/private${cwdPath}`]) {\n if (p.startsWith(`${prefix}/`)) {\n p = p.slice(prefix.length + 1)\n break\n }\n }\n return p\n}\n\n/**\n * Get source code excerpt around a line number (±3 lines).\n */\nfunction getCodeExcerpt(filePath: string, line: number): Array<{ line: number; value: string }> | null {\n try {\n if (!fs.existsSync(filePath)) return null\n const source = fs.readFileSync(filePath, \"utf8\")\n const lines = source.split(\"\\n\")\n const start = Math.max(0, line - 4)\n const end = Math.min(lines.length, line + 3)\n const result: Array<{ line: number; value: string }> = []\n for (let i = start; i < end; i++) {\n result.push({ line: i + 1, value: (lines[i] ?? \"\").replace(/\\t/g, \" \") })\n }\n return result\n } catch {\n return null\n }\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * Rich error boundary for silvery's runtime layer.\n *\n * Must be a class component (React limitation for error boundaries).\n * Renders error info using silvery-box/silvery-text host elements — no Box/Text dependency.\n * Shows: ERROR label, error message, file location, source code excerpt, and stack trace.\n */\nexport class SilveryErrorBoundary extends Component<SilveryErrorBoundaryProps, SilveryErrorBoundaryState> {\n override state: SilveryErrorBoundaryState = { error: null }\n\n static getDerivedStateFromError(error: Error): SilveryErrorBoundaryState {\n return { error }\n }\n\n override componentDidCatch(error: Error) {\n this.props.onError?.(error)\n }\n\n override render() {\n if (this.state.error) {\n const err = this.state.error\n const stack = err.stack ? err.stack.split(\"\\n\").slice(1) : []\n const origin = stack.length > 0 ? parseStackLine(stack[0]!) : null\n const filePath = cleanupPath(origin?.file)\n\n // Get source code excerpt\n let excerpt: Array<{ line: number; value: string }> | null = null\n let lineWidth = 0\n if (filePath && origin?.line) {\n excerpt = getCodeExcerpt(filePath, origin.line)\n if (excerpt) {\n for (const { line } of excerpt) {\n lineWidth = Math.max(lineWidth, String(line).length)\n }\n }\n }\n\n // Build the error display using silvery host elements\n // Format: padding=1 column layout with ERROR label, location, code, and stack\n const children: React.ReactNode[] = []\n\n // ERROR label + message\n children.push(\n React.createElement(\n \"silvery-box\",\n { key: \"header\" },\n React.createElement(\"silvery-text\", { backgroundColor: \"red\", color: \"white\" }, \" ERROR \"),\n React.createElement(\"silvery-text\", {}, ` ${err.message}`),\n ),\n )\n\n // File location\n if (filePath && origin) {\n children.push(\n React.createElement(\n \"silvery-box\",\n { key: \"location\", marginTop: 1 },\n React.createElement(\"silvery-text\", { dimColor: true }, `${filePath}:${origin.line}:${origin.column}`),\n ),\n )\n }\n\n // Source code excerpt\n if (excerpt && origin) {\n const codeLines = excerpt.map(({ line, value }) => {\n const lineNum = String(line).padStart(lineWidth, \" \")\n return React.createElement(\n \"silvery-box\",\n { key: `code-${line}` },\n React.createElement(\n \"silvery-text\",\n {\n dimColor: line !== origin.line,\n backgroundColor: line === origin.line ? \"red\" : undefined,\n color: line === origin.line ? \"white\" : undefined,\n },\n `${lineNum}:`,\n ),\n React.createElement(\n \"silvery-text\",\n {\n backgroundColor: line === origin.line ? \"red\" : undefined,\n color: line === origin.line ? \"white\" : undefined,\n },\n ` ${value}`,\n ),\n )\n })\n children.push(\n React.createElement(\"silvery-box\", { key: \"code\", marginTop: 1, flexDirection: \"column\" }, ...codeLines),\n )\n }\n\n // Stack trace\n if (stack.length > 0) {\n const stackLines = stack.map((line, i) => {\n const parsed = parseStackLine(line)\n if (!parsed) {\n return React.createElement(\n \"silvery-box\",\n { key: `stack-${i}` },\n React.createElement(\"silvery-text\", { dimColor: true }, `- ${line.trim()}`),\n )\n }\n const cleanFile = cleanupPath(parsed.file)\n return React.createElement(\n \"silvery-box\",\n { key: `stack-${i}` },\n React.createElement(\"silvery-text\", { dimColor: true }, \"- \"),\n React.createElement(\"silvery-text\", { dimColor: true, bold: true }, parsed.function ?? \"\"),\n React.createElement(\n \"silvery-text\",\n { dimColor: true, color: \"gray\" },\n ` (${cleanFile ?? \"\"}:${parsed.line}:${parsed.column})`,\n ),\n )\n })\n children.push(\n React.createElement(\"silvery-box\", { key: \"stack\", marginTop: 1, flexDirection: \"column\" }, ...stackLines),\n )\n }\n\n return React.createElement(\"silvery-box\", { flexDirection: \"column\", padding: 1 }, ...children)\n }\n return this.props.children\n }\n}\n","/**\n * Focus Queries — pure tree query functions for the silvery focus system.\n *\n * All functions are pure: no state, no React, no side effects.\n * They operate on the SilveryNode tree to resolve focusable elements,\n * tab order, spatial navigation targets, and explicit focus links.\n */\n\nimport type { AgNode, Rect } from \"./types\"\n\n// ============================================================================\n// Focusable Detection\n// ============================================================================\n\n/** Check if a node has the focusable prop set to true (or truthy). */\nfunction isFocusable(node: AgNode): boolean {\n if (node.hidden) return false\n const props = node.props as Record<string, unknown>\n return Boolean(props.focusable) && props.display !== \"none\"\n}\n\n/** Check if a node creates a focus scope (isolated Tab cycle). */\nfunction isFocusScope(node: AgNode): boolean {\n const props = node.props as Record<string, unknown>\n return Boolean(props.focusScope)\n}\n\n// ============================================================================\n// Tree Queries\n// ============================================================================\n\n/**\n * Walk up from node to nearest ancestor (or self) with focusable prop.\n * Useful for mouse clicks — find the focusable target from a deep text node.\n */\nexport function findFocusableAncestor(node: AgNode): AgNode | null {\n let current: AgNode | null = node\n while (current) {\n if (isFocusable(current)) return current\n current = current.parent\n }\n return null\n}\n\n/**\n * DFS traversal of focusable nodes in tab order, optionally scoped.\n *\n * When scope is provided, only nodes within that scope subtree are included.\n * If a focusScope node is encountered during traversal, its children are\n * skipped (they belong to a different scope), unless that scope IS the\n * provided scope node.\n */\nexport function getTabOrder(root: AgNode, scope?: AgNode): AgNode[] {\n const result: AgNode[] = []\n const walkRoot = scope ?? root\n\n function walk(node: AgNode): void {\n // Skip hidden nodes (Suspense) and display: none — entire subtree is excluded\n if (node.hidden) return\n const props = node.props as Record<string, unknown>\n if (props.display === \"none\") return\n\n // If this node is a focusScope boundary and it's NOT the walk root,\n // skip its children — they belong to a different Tab cycle.\n // The focusScope node itself may still be focusable (included below).\n if (node !== walkRoot && isFocusScope(node)) {\n // Include the scope node itself if it's focusable, but don't descend\n if (isFocusable(node)) {\n result.push(node)\n }\n return\n }\n\n if (isFocusable(node)) {\n result.push(node)\n }\n\n for (const child of node.children) {\n walk(child)\n }\n }\n\n walk(walkRoot)\n return result\n}\n\n/**\n * Walk up from a node to find the nearest ancestor (or self) with focusScope prop.\n * Returns the testID of the enclosing scope, or null if none found.\n */\nexport function findEnclosingScope(node: AgNode): string | null {\n let current: AgNode | null = node\n while (current) {\n if (isFocusScope(current)) {\n const props = current.props as Record<string, unknown>\n return typeof props.testID === \"string\" ? props.testID : null\n }\n current = current.parent\n }\n return null\n}\n\n/**\n * Find a node by testID in the subtree rooted at root.\n * DFS, returns the first match.\n */\nexport function findByTestID(root: AgNode, testID: string): AgNode | null {\n const props = root.props as Record<string, unknown>\n if (props.testID === testID) return root\n\n for (const child of root.children) {\n const found = findByTestID(child, testID)\n if (found) return found\n }\n return null\n}\n\n// ============================================================================\n// Spatial Navigation\n// ============================================================================\n\n/**\n * Compute center point of a Rect.\n */\nfunction rectCenter(rect: Rect): { cx: number; cy: number } {\n return {\n cx: rect.x + rect.width / 2,\n cy: rect.y + rect.height / 2,\n }\n}\n\n/**\n * Check if a candidate point falls within a 45-degree cone from source\n * in the given direction (tvOS-style spatial navigation).\n *\n * The cone extends from the center of the source rect in the specified\n * direction with a 45-degree half-angle (90-degree total aperture).\n */\nfunction isInCone(\n source: { cx: number; cy: number },\n candidate: { cx: number; cy: number },\n direction: \"up\" | \"down\" | \"left\" | \"right\",\n): boolean {\n const dx = candidate.cx - source.cx\n const dy = candidate.cy - source.cy\n\n // Must be in the correct general direction\n switch (direction) {\n case \"up\":\n if (dy >= 0) return false\n // Within 45-degree cone: |dx| <= |dy|\n return Math.abs(dx) <= Math.abs(dy)\n case \"down\":\n if (dy <= 0) return false\n return Math.abs(dx) <= Math.abs(dy)\n case \"left\":\n if (dx >= 0) return false\n return Math.abs(dy) <= Math.abs(dx)\n case \"right\":\n if (dx <= 0) return false\n return Math.abs(dy) <= Math.abs(dx)\n }\n}\n\n/**\n * Euclidean distance between two points.\n */\nfunction distance(a: { cx: number; cy: number }, b: { cx: number; cy: number }): number {\n const dx = a.cx - b.cx\n const dy = a.cy - b.cy\n return Math.sqrt(dx * dx + dy * dy)\n}\n\n/**\n * Find the nearest focusable candidate in a given direction using\n * 45-degree cone heuristic (tvOS-style spatial navigation).\n *\n * From the center of the source rect, draw a cone in the target direction.\n * Filter candidates whose center falls within the cone. Pick the closest\n * by Euclidean distance.\n *\n * @param from - The currently focused node\n * @param direction - Direction to search\n * @param candidates - All focusable nodes to consider\n * @param layoutFn - Function to get screen rect for a node (null if not laid out)\n */\nexport function findSpatialTarget(\n from: AgNode,\n direction: \"up\" | \"down\" | \"left\" | \"right\",\n candidates: AgNode[],\n layoutFn: (node: AgNode) => Rect | null,\n): AgNode | null {\n const sourceRect = layoutFn(from)\n if (!sourceRect) return null\n\n const source = rectCenter(sourceRect)\n\n let best: AgNode | null = null\n let bestDist = Infinity\n\n for (const candidate of candidates) {\n if (candidate === from) continue\n\n const candidateRect = layoutFn(candidate)\n if (!candidateRect) continue\n\n const target = rectCenter(candidateRect)\n\n if (!isInCone(source, target, direction)) continue\n\n const dist = distance(source, target)\n if (dist < bestDist) {\n bestDist = dist\n best = candidate\n }\n }\n\n return best\n}\n\n// ============================================================================\n// Explicit Focus Links\n// ============================================================================\n\n/**\n * Check if a node has an explicit nextFocus{Direction} override prop.\n *\n * These props allow components to declare explicit focus targets for\n * spatial navigation, overriding the cone heuristic.\n *\n * @param node - The node to check\n * @param direction - Direction string: \"up\", \"down\", \"left\", \"right\"\n * @returns The testID of the explicit target, or null\n */\nexport function getExplicitFocusLink(node: AgNode, direction: string): string | null {\n const props = node.props as Record<string, unknown>\n // Props follow the pattern: nextFocusUp, nextFocusDown, nextFocusLeft, nextFocusRight\n const propName = `nextFocus${direction.charAt(0).toUpperCase()}${direction.slice(1)}`\n const value = props[propName]\n return typeof value === \"string\" ? value : null\n}\n","/**\n * Interactive Signal Utilities\n *\n * Writer functions for per-node interactive state. State machines call these\n * during event processing to update hovered/armed/selected/focused/dropTarget\n * on AgNode instances.\n *\n * Each setter returns true if the value actually changed — callers can use\n * this for efficient dirty tracking (skip re-render if nothing changed).\n *\n * The InteractiveState object is lazily created on first write to avoid\n * allocating on nodes that never receive interactive events.\n */\n\nimport type { AgNode, InteractiveState } from \"./types\"\n\n// ============================================================================\n// Lazy Initialization\n// ============================================================================\n\n/**\n * Ensure a node has an InteractiveState object, creating one if needed.\n * Returns the (possibly newly created) state.\n */\nexport function ensureInteractiveState(node: AgNode): InteractiveState {\n if (!node.interactiveState) {\n node.interactiveState = {\n hovered: false,\n armed: false,\n selected: false,\n focused: false,\n dropTarget: false,\n }\n }\n return node.interactiveState\n}\n\n// ============================================================================\n// Individual Setters (return true if value changed)\n// ============================================================================\n\n/**\n * Set the hovered state. Returns true if the value changed.\n */\nexport function setHovered(node: AgNode, value: boolean): boolean {\n const state = ensureInteractiveState(node)\n if (state.hovered === value) return false\n state.hovered = value\n return true\n}\n\n/**\n * Set the armed state (pointer-down, awaiting click). Returns true if the value changed.\n */\nexport function setArmed(node: AgNode, value: boolean): boolean {\n const state = ensureInteractiveState(node)\n if (state.armed === value) return false\n state.armed = value\n return true\n}\n\n/**\n * Set the selected state. Returns true if the value changed.\n */\nexport function setSelected(node: AgNode, value: boolean): boolean {\n const state = ensureInteractiveState(node)\n if (state.selected === value) return false\n state.selected = value\n return true\n}\n\n/**\n * Set the focused state. Returns true if the value changed.\n */\nexport function setFocused(node: AgNode, value: boolean): boolean {\n const state = ensureInteractiveState(node)\n if (state.focused === value) return false\n state.focused = value\n return true\n}\n\n/**\n * Set the dropTarget state. Returns true if the value changed.\n */\nexport function setDropTarget(node: AgNode, value: boolean): boolean {\n const state = ensureInteractiveState(node)\n if (state.dropTarget === value) return false\n state.dropTarget = value\n return true\n}\n\n// ============================================================================\n// Batch Operations\n// ============================================================================\n\n/**\n * Clear all interactive state on a node.\n * Useful on pointer-up, focus-change, or when a node is removed from the tree.\n *\n * Sets the interactiveState reference to null to free the object.\n */\nexport function clearInteractiveState(node: AgNode): void {\n node.interactiveState = null\n}\n","/**\n * Focus Manager — standalone state container for the silvery focus system.\n *\n * Pure TypeScript, no React dependency. The subscribe/getSnapshot pattern\n * enables useSyncExternalStore in hooks.\n *\n * Replaces the flat focus list in context.ts (FocusContext with focusables Map).\n */\n\nimport type { AgNode, Rect } from \"./types\"\nimport {\n findByTestID,\n findFocusableAncestor,\n getTabOrder,\n findSpatialTarget,\n getExplicitFocusLink,\n} from \"./focus-queries\"\nimport { setFocused } from \"./interactive-signals\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type FocusOrigin = \"keyboard\" | \"mouse\" | \"programmatic\"\n\n/**\n * Callback fired when focus changes. Used by the runtime to dispatch\n * DOM-level focus/blur events without coupling FocusManager to the event system.\n *\n * @param oldNode - The node losing focus (null if nothing was focused)\n * @param newNode - The node gaining focus (null on blur)\n * @param origin - How focus was acquired\n */\nexport type FocusChangeCallback = (oldNode: AgNode | null, newNode: AgNode | null, origin: FocusOrigin | null) => void\n\nexport interface FocusSnapshot {\n activeId: string | null\n previousId: string | null\n focusOrigin: FocusOrigin | null\n scopeStack: readonly string[]\n /** The currently active peer scope (WPF FocusScope model) */\n activeScopeId: string | null\n}\n\nexport interface FocusManagerOptions {\n /** Called when focus changes — wire up event dispatch here */\n onFocusChange?: FocusChangeCallback\n}\n\n/**\n * Options for registering a hook-based (virtual) focusable.\n *\n * Hook focusables are registered via React hooks (e.g. `useFocus()` in the\n * Ink compat layer) rather than by the `focusable` prop on a tree node. They\n * participate in Tab cycling but don't have a backing `AgNode` — activeId\n * tracking is by id only, and `activeElement` is null when a hook focusable\n * is the active target.\n */\nexport interface HookFocusableOptions {\n /** Registration is inert when false — skipped in tab order, never reports focused */\n isActive?: boolean\n /** Focus this id when registered (only when isActive !== false) */\n autoFocus?: boolean\n}\n\nexport interface FocusManager {\n /** Currently focused node */\n readonly activeElement: AgNode | null\n /** testID of the currently focused node */\n readonly activeId: string | null\n /** Previously focused node */\n readonly previousElement: AgNode | null\n /** testID of the previously focused node */\n readonly previousId: string | null\n /** How focus was most recently acquired */\n readonly focusOrigin: FocusOrigin | null\n /** Stack of active focus scope IDs */\n readonly scopeStack: readonly string[]\n /** Map of scope ID -> last focused testID within that scope */\n readonly scopeMemory: Readonly<Record<string, string>>\n\n /** Focus a specific node */\n focus(node: AgNode, origin?: FocusOrigin): void\n /** Focus a node by testID (requires root for tree search) */\n focusById(id: string, root: AgNode, origin?: FocusOrigin): void\n /**\n * Focus a hook-registered (virtual) id directly without tree traversal.\n * Unlike `focusById`, this never needs a root — used by `useFocus()` hooks\n * that track focus by id only.\n */\n focusVirtualId(id: string, origin?: FocusOrigin): void\n /** Clear focus */\n blur(): void\n\n // ---- Hook-based (virtual) focusables ----\n\n /**\n * Register a hook-based focusable id (e.g. from `useFocus()` in Ink compat).\n *\n * Hook focusables form a flat list alongside the tree-based focusables.\n * `focusNext`/`focusPrev` interleave: tree focusables come first (document\n * order), then hook focusables (registration order). A single unified tab\n * cycle walks both.\n *\n * Returns an unregister callback (safe to call on effect cleanup).\n */\n registerHookFocusable(id: string, options?: HookFocusableOptions): () => void\n /** Update an existing hook-focusable's active state. */\n setHookFocusableActive(id: string, isActive: boolean): void\n /** Whether any hook focusables are currently registered. */\n readonly hasHookFocusables: boolean\n /**\n * Global focus enable (Ink compat). When false, `focusNext`/`focusPrev`\n * become no-ops for hook-registered focusables. Tree-based focusables\n * ignore this flag — apps using `useFocusable` are not affected.\n */\n readonly hookFocusEnabled: boolean\n setHookFocusEnabled(enabled: boolean): void\n\n /**\n * Handle a subtree being removed from the tree.\n * If the focused node (or previous node) is within the removed subtree,\n * clear the reference to prevent dead node retention and broken navigation.\n */\n handleSubtreeRemoved(removedRoot: AgNode): void\n\n /** Push a focus scope onto the stack */\n enterScope(scopeId: string): void\n /** Pop the current focus scope */\n exitScope(): void\n\n /** The currently active peer scope ID (WPF FocusScope model) */\n readonly activeScopeId: string | null\n /**\n * Activate a peer focus scope. Saves current focus in the old scope's memory,\n * switches to the new scope, and restores the remembered focus (or focuses\n * the first focusable element in the scope subtree).\n */\n activateScope(scopeId: string, root: AgNode): void\n\n /** Get the testID path from focused node to root */\n getFocusPath(root: AgNode): string[]\n /** Check if a subtree rooted at testID contains the focused node */\n hasFocusWithin(root: AgNode, testID: string): boolean\n\n /** Focus the next focusable node in tab order */\n focusNext(root: AgNode, scope?: AgNode): void\n /** Focus the previous focusable node in tab order */\n focusPrev(root: AgNode, scope?: AgNode): void\n /** Focus in a spatial direction (up/down/left/right) */\n focusDirection(\n root: AgNode,\n direction: \"up\" | \"down\" | \"left\" | \"right\",\n layoutFn?: (node: AgNode) => Rect | null,\n ): void\n\n /** Subscribe for React integration (useSyncExternalStore) */\n subscribe(listener: () => void): () => void\n /** Get immutable snapshot for useSyncExternalStore */\n getSnapshot(): FocusSnapshot\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Internal shape for a hook-registered focusable.\n * Order in the list reflects registration order (Ink-compatible tab order).\n */\ninterface HookFocusable {\n id: string\n isActive: boolean\n}\n\nexport function createFocusManager(options?: FocusManagerOptions): FocusManager {\n const onFocusChange = options?.onFocusChange\n\n // Internal state\n let activeElement: AgNode | null = null\n let activeId: string | null = null\n let previousElement: AgNode | null = null\n let previousId: string | null = null\n let focusOrigin: FocusOrigin | null = null\n const scopeStack: string[] = []\n const scopeMemory: Record<string, string> = {}\n let activeScopeId: string | null = null\n\n // Hook-registered focusables (flat list, Ink-style).\n const hookFocusables: HookFocusable[] = []\n // Global focus enable (Ink compat — `enableFocus()` / `disableFocus()` knob).\n let hookFocusEnabled = true\n\n // Subscriber management\n const listeners = new Set<() => void>()\n let snapshot: FocusSnapshot | null = null\n /** Counter incremented on every notify(); used by activateScope to detect inner notifications. */\n let notifyCount = 0\n\n function notify(): void {\n snapshot = null // Invalidate cached snapshot\n notifyCount++\n for (const listener of listeners) {\n listener()\n }\n }\n\n function getTestID(node: AgNode): string | null {\n const props = node.props as Record<string, unknown>\n return typeof props.testID === \"string\" ? props.testID : null\n }\n\n // ---- Focus operations ----\n\n function focus(node: AgNode, origin: FocusOrigin = \"programmatic\"): void {\n // Skip if already focused on this node\n if (activeElement === node) {\n // Still update origin if different\n if (focusOrigin !== origin) {\n focusOrigin = origin\n notify()\n }\n return\n }\n\n const oldElement = activeElement\n previousElement = activeElement\n previousId = activeId\n activeElement = node\n activeId = getTestID(node)\n focusOrigin = origin\n\n // Update interactive state on affected nodes\n if (oldElement) setFocused(oldElement, false)\n setFocused(node, true)\n\n // Remember this focus in the current scope\n if (activeId && scopeStack.length > 0) {\n scopeMemory[scopeStack[scopeStack.length - 1]!] = activeId\n }\n\n notify()\n\n // Fire focus change callback (after state is updated)\n onFocusChange?.(oldElement, node, origin)\n }\n\n function focusById(id: string, root: AgNode, origin: FocusOrigin = \"programmatic\"): void {\n const node = findByTestID(root, id)\n if (node) {\n // Walk up to the nearest focusable ancestor if the found node isn't focusable\n const focusable = findFocusableAncestor(node)\n if (focusable) {\n focus(focusable, origin)\n return\n }\n }\n // Virtual focus: set the ID without a DOM node. This enables named focus\n // targets (e.g. \"board-area\", \"detail-pane\") without requiring wrapper Boxes\n // that would disrupt layout.\n\n // Idempotent: skip if already virtually focused on this ID (prevents infinite\n // re-render loops when focus() is called during render).\n if (activeId === id && !activeElement) return\n\n const oldElement = activeElement\n previousElement = activeElement\n previousId = activeId\n activeElement = null\n activeId = id\n focusOrigin = origin\n\n // Update interactive state — old element loses focus, no new node to set\n if (oldElement) setFocused(oldElement, false)\n\n notify()\n\n // Fire focus change callback (old element blurs, no new node for virtual focus)\n onFocusChange?.(oldElement, null, origin)\n }\n\n function blur(): void {\n if (!activeElement && !activeId) return\n\n const oldElement = activeElement\n previousElement = activeElement\n previousId = activeId\n activeElement = null\n activeId = null\n focusOrigin = null\n\n // Update interactive state — old element loses focus\n if (oldElement) setFocused(oldElement, false)\n\n notify()\n\n // Fire focus change callback (after state is updated)\n onFocusChange?.(oldElement, null, null)\n }\n\n // ---- Hook-based (virtual) focusables ----\n\n /**\n * Set the focused target to a hook-registered id directly (no tree lookup).\n * activeElement becomes null (no backing node), activeId is the hook id.\n */\n function focusVirtualId(id: string, origin: FocusOrigin = \"programmatic\"): void {\n if (activeId === id && !activeElement) {\n if (focusOrigin !== origin) {\n focusOrigin = origin\n notify()\n }\n return\n }\n const oldElement = activeElement\n previousElement = activeElement\n previousId = activeId\n activeElement = null\n activeId = id\n focusOrigin = origin\n\n if (oldElement) setFocused(oldElement, false)\n\n notify()\n\n onFocusChange?.(oldElement, null, origin)\n }\n\n function unregisterHookFocusable(id: string): void {\n const idx = hookFocusables.findIndex((f) => f.id === id)\n if (idx === -1) return\n hookFocusables.splice(idx, 1)\n // Clear active focus if the removed id was focused.\n if (activeId === id && !activeElement) {\n previousId = activeId\n activeId = null\n focusOrigin = null\n notify()\n onFocusChange?.(null, null, null)\n return\n }\n notify()\n }\n\n function registerHookFocusable(id: string, opts: HookFocusableOptions = {}): () => void {\n const { isActive = true, autoFocus = false } = opts\n\n // Dedup by id — last registration wins (matches Ink behavior on re-mount).\n const existing = hookFocusables.findIndex((f) => f.id === id)\n if (existing !== -1) {\n // Skip notification if nothing changed (prevents render loops)\n if (hookFocusables[existing]!.isActive === isActive && !autoFocus) {\n return () => unregisterHookFocusable(id)\n }\n hookFocusables[existing] = { id, isActive }\n } else {\n hookFocusables.push({ id, isActive })\n }\n\n if (autoFocus && isActive && activeId === null) {\n focusVirtualId(id, \"programmatic\")\n } else {\n notify()\n }\n\n return () => unregisterHookFocusable(id)\n }\n\n function setHookFocusableActive(id: string, isActive: boolean): void {\n const entry = hookFocusables.find((f) => f.id === id)\n if (!entry) return\n if (entry.isActive === isActive) return\n entry.isActive = isActive\n // If the active virtual id was deactivated, clear it.\n if (!isActive && activeId === id && !activeElement) {\n previousId = activeId\n activeId = null\n focusOrigin = null\n notify()\n onFocusChange?.(null, null, null)\n return\n }\n notify()\n }\n\n function setHookFocusEnabled(enabled: boolean): void {\n if (hookFocusEnabled === enabled) return\n hookFocusEnabled = enabled\n notify()\n }\n\n // ---- Subtree removal ----\n\n /**\n * Check if a node is the given target or contains it as a descendant.\n */\n function subtreeContains(subtreeRoot: AgNode, target: AgNode): boolean {\n if (subtreeRoot === target) return true\n for (const child of subtreeRoot.children) {\n if (subtreeContains(child, target)) return true\n }\n return false\n }\n\n /**\n * Handle a subtree being removed from the tree. If the active or previous\n * element lives within the removed subtree, clear the dangling reference.\n * This prevents dead node retention and broken navigation (indexOf → -1).\n */\n function handleSubtreeRemoved(removedRoot: AgNode): void {\n let changed = false\n\n if (activeElement && subtreeContains(removedRoot, activeElement)) {\n const oldElement = activeElement\n // Clear interactive focus state before removing reference\n setFocused(oldElement, false)\n previousElement = activeElement\n previousId = activeId\n activeElement = null\n activeId = null\n focusOrigin = null\n changed = true\n onFocusChange?.(oldElement, null, null)\n }\n\n if (previousElement && subtreeContains(removedRoot, previousElement)) {\n previousElement = null\n previousId = null\n changed = true\n }\n\n if (changed) {\n notify()\n }\n }\n\n // ---- Scope management ----\n\n function enterScope(scopeId: string): void {\n scopeStack.push(scopeId)\n notify()\n }\n\n function exitScope(): void {\n const exited = scopeStack.pop()\n if (exited === undefined) return\n\n // Restore focus to the remembered element in the parent scope\n // (Caller is responsible for providing root to restore if needed)\n notify()\n }\n\n // ---- Peer scope activation (WPF FocusScope model) ----\n\n function activateScope(scopeId: string, root: AgNode): void {\n // Save current focus in the outgoing scope's memory\n if (activeScopeId && activeId) {\n scopeMemory[activeScopeId] = activeId\n }\n\n // Switch scope\n activeScopeId = scopeId\n\n // Restore focus: remembered element, or first focusable in scope.\n // Track whether notify() fired during focus/focusById to avoid double-notify.\n const countBefore = notifyCount\n const remembered = scopeMemory[scopeId]\n if (remembered) {\n focusById(remembered, root, \"programmatic\")\n } else {\n const scopeNode = findByTestID(root, scopeId)\n if (scopeNode) {\n const order = getTabOrder(root, scopeNode)\n if (order.length > 0) {\n focus(order[0]!, \"programmatic\")\n }\n }\n }\n\n // Only notify if focus/focusById didn't already notify.\n if (notifyCount === countBefore) {\n notify()\n }\n }\n\n // ---- Tree queries ----\n\n function getFocusPath(root: AgNode): string[] {\n if (!activeElement) return []\n\n const path: string[] = []\n let current: AgNode | null = activeElement\n while (current && current !== root.parent) {\n const id = getTestID(current)\n if (id) path.push(id)\n current = current.parent\n }\n return path\n }\n\n function hasFocusWithin(root: AgNode, testID: string): boolean {\n if (!activeElement) return false\n\n // Find the node with the given testID\n const target = findByTestID(root, testID)\n if (!target) return false\n\n // Walk up from activeElement to see if we pass through target\n let current: AgNode | null = activeElement\n while (current) {\n if (current === target) return true\n current = current.parent\n }\n return false\n }\n\n // ---- Navigation ----\n\n /**\n * Resolve the effective scope node for tab navigation.\n * If an explicit scope is provided, use it. Otherwise, if the scopeStack\n * is non-empty, find the topmost scope node in the tree by testID.\n */\n function resolveScope(root: AgNode, explicitScope?: AgNode): AgNode | undefined {\n if (explicitScope) return explicitScope\n\n if (scopeStack.length > 0) {\n const scopeId = scopeStack[scopeStack.length - 1]!\n const scopeNode = findByTestID(root, scopeId)\n if (scopeNode) return scopeNode\n }\n\n return undefined\n }\n\n /**\n * Unified tab entry — either a tree-based AgNode or a hook-registered id.\n * Tree focusables come first (document order), then hook focusables\n * (registration order). Inactive hook focusables are excluded.\n */\n type TabEntry = { kind: \"node\"; node: AgNode } | { kind: \"hook\"; id: string }\n\n function buildTabEntries(root: AgNode, scope?: AgNode): TabEntry[] {\n const effectiveScope = resolveScope(root, scope)\n const nodes = getTabOrder(root, effectiveScope)\n const entries: TabEntry[] = nodes.map((node) => ({ kind: \"node\", node }))\n // Hook focusables are only included when no explicit tree scope is active —\n // they're inherently scope-less. Skip inactive or when globally disabled.\n // Dedup: skip hook focusables whose id matches a tree node's testID —\n // the component is already in the tab order via the tree scan.\n if (!effectiveScope && hookFocusEnabled) {\n const treeIds = new Set(\n nodes.map((n) => (n.props as Record<string, unknown>).testID as string | undefined).filter(Boolean),\n )\n for (const entry of hookFocusables) {\n if (entry.isActive && !treeIds.has(entry.id)) entries.push({ kind: \"hook\", id: entry.id })\n }\n }\n return entries\n }\n\n function currentTabIndex(entries: TabEntry[]): number {\n if (activeElement) {\n for (let i = 0; i < entries.length; i++) {\n const e = entries[i]!\n if (e.kind === \"node\" && e.node === activeElement) return i\n }\n return -1\n }\n if (activeId) {\n for (let i = 0; i < entries.length; i++) {\n const e = entries[i]!\n if (e.kind === \"hook\" && e.id === activeId) return i\n }\n }\n return -1\n }\n\n function focusTabEntry(entry: TabEntry, origin: FocusOrigin): void {\n if (entry.kind === \"node\") {\n focus(entry.node, origin)\n } else {\n focusVirtualId(entry.id, origin)\n }\n }\n\n function focusNext(root: AgNode, scope?: AgNode): void {\n const entries = buildTabEntries(root, scope)\n if (entries.length === 0) return\n\n const currentIndex = currentTabIndex(entries)\n if (currentIndex === -1) {\n // Nothing focused — focus the first entry\n focusTabEntry(entries[0]!, \"keyboard\")\n return\n }\n\n const nextIndex = (currentIndex + 1) % entries.length\n focusTabEntry(entries[nextIndex]!, \"keyboard\")\n }\n\n function focusPrev(root: AgNode, scope?: AgNode): void {\n const entries = buildTabEntries(root, scope)\n if (entries.length === 0) return\n\n const currentIndex = currentTabIndex(entries)\n if (currentIndex === -1) {\n // Nothing focused — focus the last entry\n focusTabEntry(entries[entries.length - 1]!, \"keyboard\")\n return\n }\n\n const prevIndex = currentIndex <= 0 ? entries.length - 1 : currentIndex - 1\n focusTabEntry(entries[prevIndex]!, \"keyboard\")\n }\n\n function focusDirection(\n root: AgNode,\n direction: \"up\" | \"down\" | \"left\" | \"right\",\n layoutFn?: (node: AgNode) => Rect | null,\n ): void {\n if (!activeElement) return\n\n // Check for explicit focus link first\n const explicitTarget = getExplicitFocusLink(activeElement, direction)\n if (explicitTarget) {\n focusById(explicitTarget, root, \"keyboard\")\n return\n }\n\n // Fall back to spatial navigation\n const candidates = getTabOrder(root)\n const resolvedLayoutFn = layoutFn ?? ((node: AgNode) => node.scrollRect)\n const target = findSpatialTarget(activeElement, direction, candidates, resolvedLayoutFn)\n if (target) {\n focus(target, \"keyboard\")\n }\n }\n\n // ---- Subscribe/snapshot for useSyncExternalStore ----\n\n function subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n }\n\n function getSnapshot(): FocusSnapshot {\n if (!snapshot) {\n snapshot = {\n activeId,\n previousId,\n focusOrigin,\n scopeStack: [...scopeStack],\n activeScopeId,\n }\n }\n return snapshot\n }\n\n // ---- Public interface ----\n\n return {\n get activeElement() {\n return activeElement\n },\n get activeId() {\n return activeId\n },\n get previousElement() {\n return previousElement\n },\n get previousId() {\n return previousId\n },\n get focusOrigin() {\n return focusOrigin\n },\n get scopeStack() {\n return [...scopeStack] as readonly string[]\n },\n get scopeMemory() {\n return scopeMemory as Readonly<Record<string, string>>\n },\n get activeScopeId() {\n return activeScopeId\n },\n\n focus,\n focusById,\n focusVirtualId,\n blur,\n handleSubtreeRemoved,\n\n enterScope,\n exitScope,\n activateScope,\n\n getFocusPath,\n hasFocusWithin,\n\n focusNext,\n focusPrev,\n focusDirection,\n\n subscribe,\n getSnapshot,\n\n // Hook-based focusables (Ink compat)\n registerHookFocusable,\n setHookFocusableActive,\n get hasHookFocusables() {\n return hookFocusables.length > 0\n },\n get hookFocusEnabled() {\n return hookFocusEnabled\n },\n setHookFocusEnabled,\n }\n}\n","/**\n * Shared tree utilities for silvery event systems.\n *\n * Functions used by both focus-events.ts and mouse-events.ts.\n */\n\nimport type { AgNode, Rect } from \"./types.js\"\n\n/**\n * Collect the ancestor path from target to root (inclusive).\n */\nexport function getAncestorPath(node: AgNode): AgNode[] {\n const path: AgNode[] = []\n let current: AgNode | null = node\n while (current) {\n path.push(current)\n current = current.parent\n }\n return path\n}\n\n/**\n * Check if a point is inside a rect.\n */\nexport function pointInRect(x: number, y: number, rect: Rect): boolean {\n return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height\n}\n","/**\n * DOM-level Focus and Keyboard Events for silvery\n *\n * Provides React DOM-compatible focus/keyboard event infrastructure:\n * - SilveryKeyEvent / SilveryFocusEvent synthetic event objects\n * - Event dispatch with capture/target/bubble phases (key events)\n * - Event dispatch with target + bubble (focus events)\n *\n * Follows the same patterns as mouse-events.ts for consistency.\n */\n\nimport type { Key } from \"./keys\"\nimport { getAncestorPath } from \"./tree-utils.js\"\nimport type { AgNode } from \"./types\"\n\n// ============================================================================\n// Event Types\n// ============================================================================\n\n/**\n * Synthetic keyboard event, mirroring React.KeyboardEvent / DOM KeyboardEvent.\n */\nexport interface SilveryKeyEvent {\n /** The printable character, or \"\" for non-printable keys */\n key: string\n /** Raw terminal input string */\n input: string\n /** Modifier keys */\n ctrl: boolean\n meta: boolean\n shift: boolean\n super: boolean\n hyper: boolean\n /** Kitty event type */\n eventType?: \"press\" | \"repeat\" | \"release\"\n /** Deepest focusable node that received this event */\n target: AgNode\n /** Node whose handler is currently firing (changes during capture/bubble) */\n currentTarget: AgNode\n /** Stop event from propagating further */\n stopPropagation(): void\n /** Prevent default behavior */\n preventDefault(): void\n /** Whether stopPropagation() was called */\n readonly propagationStopped: boolean\n /** Whether preventDefault() was called */\n readonly defaultPrevented: boolean\n /** Raw parsed key data */\n nativeEvent: { input: string; key: Key }\n}\n\n/**\n * Synthetic focus event, mirroring React.FocusEvent / DOM FocusEvent.\n */\nexport interface SilveryFocusEvent {\n /** The node gaining or losing focus */\n target: AgNode\n /** The other node involved (losing focus on 'focus', gaining on 'blur') */\n relatedTarget: AgNode | null\n /** Event type */\n type: \"focus\" | \"blur\"\n /** Node whose handler is currently firing (changes during bubble) */\n currentTarget: AgNode\n /** Stop event from bubbling to parent nodes */\n stopPropagation(): void\n /** Whether stopPropagation() was called */\n readonly propagationStopped: boolean\n}\n\n// ============================================================================\n// Focus Event Handler Props (added to BoxProps)\n// ============================================================================\n\nexport interface FocusEventProps {\n /** Whether this node can receive focus */\n focusable?: boolean\n /** Whether this node should receive focus on mount */\n autoFocus?: boolean\n /** Whether this node creates a focus scope (focus trapping boundary) */\n focusScope?: boolean\n /** ID of the node to focus when pressing Up from this node */\n nextFocusUp?: string\n /** ID of the node to focus when pressing Down from this node */\n nextFocusDown?: string\n /** ID of the node to focus when pressing Left from this node */\n nextFocusLeft?: string\n /** ID of the node to focus when pressing Right from this node */\n nextFocusRight?: string\n /** Called when this node gains focus */\n onFocus?: (event: SilveryFocusEvent) => void\n /** Called when this node loses focus */\n onBlur?: (event: SilveryFocusEvent) => void\n /** Called on key down (bubble phase) */\n onKeyDown?: (event: SilveryKeyEvent, dispatch?: (msg: unknown) => void) => void\n /** Called on key up (bubble phase) */\n onKeyUp?: (event: SilveryKeyEvent, dispatch?: (msg: unknown) => void) => void\n /** Called on key down (capture phase — fires before target) */\n onKeyDownCapture?: (event: SilveryKeyEvent) => void\n}\n\n// ============================================================================\n// Event Factories\n// ============================================================================\n\n/**\n * Create a synthetic keyboard event.\n */\nexport function createKeyEvent(input: string, key: Key, target: AgNode): SilveryKeyEvent {\n let propagationStopped = false\n let defaultPrevented = false\n\n return {\n key: input,\n input,\n ctrl: key.ctrl,\n meta: key.meta,\n shift: key.shift,\n super: key.super,\n hyper: key.hyper,\n eventType: key.eventType,\n target,\n currentTarget: target,\n nativeEvent: { input, key },\n get propagationStopped() {\n return propagationStopped\n },\n get defaultPrevented() {\n return defaultPrevented\n },\n stopPropagation() {\n propagationStopped = true\n },\n preventDefault() {\n defaultPrevented = true\n },\n }\n}\n\n/**\n * Create a synthetic focus event.\n */\nexport function createFocusEvent(\n type: \"focus\" | \"blur\",\n target: AgNode,\n relatedTarget: AgNode | null,\n): SilveryFocusEvent {\n let propagationStopped = false\n\n return {\n type,\n target,\n relatedTarget,\n currentTarget: target,\n get propagationStopped() {\n return propagationStopped\n },\n stopPropagation() {\n propagationStopped = true\n },\n }\n}\n\n// ============================================================================\n// Tree Walking\n// ============================================================================\n\n// ============================================================================\n// Event Dispatch\n// ============================================================================\n\n/**\n * Dispatch a keyboard event through the render tree with DOM-style\n * capture/target/bubble phases.\n *\n * For press/repeat events:\n * 1. Capture phase: root → target (onKeyDownCapture props)\n * 2. Target phase: target's onKeyDown\n * 3. Bubble phase: target parent → root (onKeyDown props)\n *\n * For release events:\n * 1. Target phase: target's onKeyUp\n * 2. Bubble phase: target parent → root (onKeyUp props)\n * (No capture phase for keyUp — deliberate simplification; React DOM has onKeyUpCapture)\n *\n * stopPropagation() halts traversal at any phase.\n */\nexport function dispatchKeyEvent(event: SilveryKeyEvent, dispatch?: (msg: unknown) => void): void {\n const path = getAncestorPath(event.target)\n const mutableEvent = event as { currentTarget: AgNode }\n\n // Release events → onKeyUp (no capture phase — deliberate simplification; React DOM has onKeyUpCapture)\n const isRelease = event.eventType === \"release\"\n const handlerProp = isRelease ? \"onKeyUp\" : \"onKeyDown\"\n\n // Capture phase: root → target (onKeyDownCapture — press/repeat only)\n if (!isRelease) {\n for (let i = path.length - 1; i > 0; i--) {\n if (event.propagationStopped) return\n const node = path[i]!\n const handler = (node.props as Record<string, unknown>).onKeyDownCapture as\n | ((e: SilveryKeyEvent) => void)\n | undefined\n if (handler) {\n mutableEvent.currentTarget = node\n handler(event)\n }\n }\n }\n\n // Target phase\n if (!event.propagationStopped) {\n const target = path[0]!\n mutableEvent.currentTarget = target\n const handler = (target.props as Record<string, unknown>)[handlerProp] as\n | ((e: SilveryKeyEvent, d?: (msg: unknown) => void) => void)\n | undefined\n if (handler) {\n handler(event, dispatch)\n }\n }\n\n // Bubble phase: target parent → root\n for (let i = 1; i < path.length; i++) {\n if (event.propagationStopped) return\n const node = path[i]!\n const handler = (node.props as Record<string, unknown>)[handlerProp] as\n | ((e: SilveryKeyEvent, d?: (msg: unknown) => void) => void)\n | undefined\n if (handler) {\n mutableEvent.currentTarget = node\n handler(event, dispatch)\n }\n }\n}\n\n/**\n * Dispatch a focus event through the render tree.\n *\n * Fires onFocus/onBlur on the target, then bubbles to ancestors.\n */\nexport function dispatchFocusEvent(event: SilveryFocusEvent): void {\n const handlerProp = event.type === \"focus\" ? \"onFocus\" : \"onBlur\"\n const path = getAncestorPath(event.target)\n const mutableEvent = event as { currentTarget: AgNode }\n\n for (const node of path) {\n if (event.propagationStopped) break\n\n const handler = (node.props as Record<string, unknown>)[handlerProp] as ((e: SilveryFocusEvent) => void) | undefined\n if (handler) {\n mutableEvent.currentTarget = node\n handler(event)\n }\n }\n}\n","/**\n * Hit Registry Core — Pure logic for mouse hit testing.\n *\n * This module contains the React-free core of the hit registry:\n * types, registry class, z-index constants, and ID counter.\n *\n * React hooks and context live in ./hit-registry (which re-exports everything\n * from here plus adds useHitRegion, useHitRegionCallback, HitRegistryContext).\n *\n * The @silvery/ag-term barrel imports from this file to stay React-free.\n * Consumers who need React hooks should import from @silvery/ag-term/hit-registry.\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Target type for hit testing.\n * Each type represents a different clickable element in the UI.\n */\nexport interface HitTarget {\n /** The type of element that was clicked */\n type: \"node\" | \"fold-toggle\" | \"link\" | \"column-header\" | \"scroll-area\" | \"button\"\n /** Column index (for column-header, or items within a column) */\n colIndex?: number\n /** Card index within a column */\n cardIndex?: number\n /** Sub-item index within a card (e.g., checklist items) */\n subIndex?: number\n /** Node ID for node-specific targets */\n nodeId?: string\n /** URL for link targets */\n linkUrl?: string\n /** Custom action identifier */\n action?: string\n}\n\n/**\n * A registered hit region with position, size, target, and z-index.\n */\nexport interface HitRegion {\n /** X position on screen (0-indexed column) */\n x: number\n /** Y position on screen (0-indexed row) */\n y: number\n /** Width in columns */\n width: number\n /** Height in rows */\n height: number\n /** The target to return when this region is clicked */\n target: HitTarget\n /** Z-index for layering (higher values are on top) */\n zIndex: number\n}\n\n// ============================================================================\n// HitRegistry Class\n// ============================================================================\n\n/**\n * Registry for managing hit regions.\n *\n * Components register their screen regions with targets, and the registry\n * resolves mouse clicks to the appropriate target based on position and z-index.\n *\n * @example\n * ```typescript\n * const registry = new HitRegistry();\n *\n * // Register a card region\n * registry.register('card-1', {\n * x: 10, y: 5, width: 30, height: 8,\n * target: { type: 'node', nodeId: 'abc123' },\n * zIndex: 10\n * });\n *\n * // Hit test a click\n * const target = registry.hitTest(15, 7);\n * // Returns { type: 'node', nodeId: 'abc123' }\n * ```\n */\nexport class HitRegistry {\n private regions = new Map<string, HitRegion>()\n\n /**\n * Register a hit region with a unique ID.\n *\n * @param id - Unique identifier for the region (used for unregistration)\n * @param region - The region definition including position, size, target, and z-index\n */\n register(id: string, region: HitRegion): void {\n this.regions.set(id, region)\n }\n\n /**\n * Unregister a hit region by ID.\n *\n * @param id - The ID used when registering the region\n */\n unregister(id: string): void {\n this.regions.delete(id)\n }\n\n /**\n * Clear all registered regions.\n * Useful when the UI is completely redrawn.\n */\n clear(): void {\n this.regions.clear()\n }\n\n /**\n * Get the number of registered regions.\n * Useful for debugging.\n */\n get size(): number {\n return this.regions.size\n }\n\n /**\n * Test a screen position and return the highest z-index matching target.\n *\n * @param screenX - X position on screen (0-indexed column)\n * @param screenY - Y position on screen (0-indexed row)\n * @returns The target of the highest z-index region containing the point, or null if none\n */\n hitTest(screenX: number, screenY: number): HitTarget | null {\n let bestMatch: HitRegion | null = null\n\n for (const region of this.regions.values()) {\n // Check if point is within region bounds\n if (\n screenX >= region.x &&\n screenX < region.x + region.width &&\n screenY >= region.y &&\n screenY < region.y + region.height\n ) {\n // Keep the highest z-index match\n if (!bestMatch || region.zIndex > bestMatch.zIndex) {\n bestMatch = region\n }\n }\n }\n\n return bestMatch?.target ?? null\n }\n\n /**\n * Get all regions that contain a point, sorted by z-index (highest first).\n * Useful for debugging or when you need to know all overlapping elements.\n *\n * @param screenX - X position on screen (0-indexed column)\n * @param screenY - Y position on screen (0-indexed row)\n * @returns Array of matching regions, sorted by z-index descending\n */\n hitTestAll(screenX: number, screenY: number): HitRegion[] {\n const matches: HitRegion[] = []\n\n for (const region of this.regions.values()) {\n if (\n screenX >= region.x &&\n screenX < region.x + region.width &&\n screenY >= region.y &&\n screenY < region.y + region.height\n ) {\n matches.push(region)\n }\n }\n\n // Sort by z-index descending (highest first)\n return matches.sort((a, b) => b.zIndex - a.zIndex)\n }\n\n /**\n * Debug helper: get all registered regions.\n */\n getAllRegions(): Map<string, HitRegion> {\n return new Map(this.regions)\n }\n}\n\n// ============================================================================\n// ID Counter\n// ============================================================================\n\n/**\n * Generate a unique ID for hit region registration.\n */\nlet hitRegionIdCounter = 0\nexport function generateHitRegionId(): string {\n return `hit-${++hitRegionIdCounter}`\n}\n\n/**\n * Reset the ID counter (useful for testing).\n */\nexport function resetHitRegionIdCounter(): void {\n hitRegionIdCounter = 0\n}\n\n// ============================================================================\n// Z-Index Constants\n// ============================================================================\n\n/**\n * Recommended z-index values for different UI layers.\n */\nexport const Z_INDEX = {\n /** Background elements */\n BACKGROUND: 0,\n /** Column headers */\n COLUMN_HEADER: 5,\n /** Cards in the main view */\n CARD: 10,\n /** Fold toggles (above cards for easier clicking) */\n FOLD_TOGGLE: 15,\n /** Links within cards */\n LINK: 20,\n /** Floating elements */\n FLOATING: 50,\n /** Modal dialogs */\n DIALOG: 100,\n /** Dropdown menus */\n DROPDOWN: 150,\n /** Tooltips */\n TOOLTIP: 200,\n} as const\n","/**\n * Debug utilities for incremental render mismatch diagnostics.\n *\n * When SILVERY_STRICT detects a mismatch between incremental and fresh renders,\n * these utilities help identify the root cause by providing:\n * - Node attribution (which node owns the mismatched cell)\n * - Dirty flag state (what flags were set before render)\n * - Layout changes (prevLayout vs boxRect)\n * - Scroll context (offset changes, hidden items)\n */\n\nimport type { Cell } from \"@silvery/ag-term/buffer\"\nimport type { BoxProps, AgNode, Rect, TextProps } from \"@silvery/ag/types\"\nimport { isDirty, isAnyDirty, CONTENT_BIT, STYLE_PROPS_BIT, SUBTREE_BIT, CHILDREN_BIT } from \"@silvery/ag/epoch\"\nimport type { RenderPhaseStats } from \"@silvery/ag-term/pipeline/types\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Debug info about a node at a screen position */\nexport interface NodeDebugInfo {\n /** Node ID (if set via props.id) */\n id: string | undefined\n /** Node type (silvery-box, silvery-text, silvery-root) */\n type: string\n /** Path from root to this node (IDs or indices) */\n path: string\n /** Index within parent's children array */\n childIndex: number | null\n /** Dirty flags at time of mismatch */\n dirtyFlags: {\n contentDirty: boolean\n stylePropsDirty: boolean\n subtreeDirty: boolean\n childrenDirty: boolean\n layoutDirty: boolean\n }\n /** Layout info */\n layout: {\n prevLayout: Rect | null\n boxRect: Rect | null\n scrollRect: Rect | null\n layoutChanged: boolean\n }\n /** Scroll context (if this is a scroll container or inside one) */\n scroll?: {\n offset: number\n prevOffset: number\n offsetChanged: boolean\n contentHeight: number\n viewportHeight: number\n hiddenAbove: number\n hiddenBelow: number\n firstVisibleChild: number\n lastVisibleChild: number\n }\n /** Background color from props */\n backgroundColor: string | undefined\n /** Number of children */\n childCount: number\n /** Whether node is hidden (Suspense) */\n hidden: boolean\n}\n\n/** Full mismatch debug context */\nexport interface MismatchDebugContext {\n /** Screen position of the mismatch */\n position: { x: number; y: number }\n /** Cell values */\n cells: {\n incremental: Cell\n fresh: Cell\n }\n /** Render number */\n renderNum: number\n /** Node that owns this screen position (innermost) */\n node: NodeDebugInfo | null\n /** Scroll container ancestry (if any) */\n scrollAncestors: NodeDebugInfo[]\n /** All nodes whose scrollRect contains this position */\n containingNodes: NodeDebugInfo[]\n /** Fast-path analysis - why the node was likely skipped */\n fastPathAnalysis: string[]\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Find the innermost node at a screen position.\n */\nexport function findNodeAtPosition(root: AgNode, x: number, y: number): AgNode | null {\n let result: AgNode | null = null\n\n function visit(node: AgNode): void {\n const rect = node.scrollRect\n if (!rect) return\n\n // Check if position is within this node's scrollRect\n if (x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height) {\n result = node // This node contains the position\n\n // Check children (later children render on top of earlier ones)\n for (const child of node.children) {\n visit(child)\n }\n }\n }\n\n visit(root)\n return result\n}\n\n/**\n * Find all nodes whose scrollRect contains the given position.\n * Returns nodes from root to innermost (outermost first).\n */\nexport function findAllContainingNodes(root: AgNode, x: number, y: number): AgNode[] {\n const result: AgNode[] = []\n\n function visit(node: AgNode): void {\n const rect = node.scrollRect\n if (!rect) return\n\n if (x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height) {\n result.push(node)\n for (const child of node.children) {\n visit(child)\n }\n }\n }\n\n visit(root)\n return result\n}\n\n/**\n * Get the path from root to a node (for identification).\n */\nfunction getNodePath(node: AgNode): string {\n const parts: string[] = []\n let current: AgNode | null = node\n\n while (current) {\n const props = current.props as BoxProps & TextProps\n if (props.id) {\n parts.unshift(`#${props.id}`)\n } else if (current.parent) {\n const idx = current.parent.children.indexOf(current)\n parts.unshift(`[${idx}]`)\n } else {\n parts.unshift(\"root\")\n }\n current = current.parent\n }\n\n return parts.join(\" > \")\n}\n\n/**\n * Check if a rect changed (position or size).\n */\nfunction rectChanged(a: Rect | null, b: Rect | null): boolean {\n if (a === b) return false\n if (!a || !b) return true\n return a.x !== b.x || a.y !== b.y || a.width !== b.width || a.height !== b.height\n}\n\n/**\n * Extract debug info from a node.\n */\nexport function getNodeDebugInfo(node: AgNode): NodeDebugInfo {\n const props = node.props as BoxProps & TextProps\n\n // Get child index within parent\n let childIndex: number | null = null\n if (node.parent) {\n childIndex = node.parent.children.indexOf(node)\n }\n\n return {\n id: props.id,\n type: node.type,\n path: getNodePath(node),\n childIndex,\n dirtyFlags: {\n contentDirty: isDirty(node.dirtyBits, node.dirtyEpoch, CONTENT_BIT),\n stylePropsDirty: isDirty(node.dirtyBits, node.dirtyEpoch, STYLE_PROPS_BIT),\n subtreeDirty: isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT),\n childrenDirty: isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT),\n layoutDirty: node.layoutDirty,\n },\n layout: {\n prevLayout: node.prevLayout,\n boxRect: node.boxRect,\n scrollRect: node.scrollRect,\n layoutChanged: rectChanged(node.prevLayout, node.boxRect),\n },\n scroll: node.scrollState\n ? {\n offset: node.scrollState.offset,\n prevOffset: node.scrollState.prevOffset,\n offsetChanged: node.scrollState.offset !== node.scrollState.prevOffset,\n contentHeight: node.scrollState.contentHeight,\n viewportHeight: node.scrollState.viewportHeight,\n hiddenAbove: node.scrollState.hiddenAbove,\n hiddenBelow: node.scrollState.hiddenBelow,\n firstVisibleChild: node.scrollState.firstVisibleChild,\n lastVisibleChild: node.scrollState.lastVisibleChild,\n }\n : undefined,\n backgroundColor: props.backgroundColor,\n childCount: node.children.length,\n hidden: node.hidden ?? false,\n }\n}\n\n/**\n * Find scroll container ancestors for a node.\n */\nfunction findScrollAncestors(node: AgNode): AgNode[] {\n const result: AgNode[] = []\n let current = node.parent\n\n while (current) {\n if (current.scrollState) {\n result.push(current)\n }\n current = current.parent\n }\n\n return result\n}\n\n/**\n * Analyze why a node might have been incorrectly skipped by fast-path.\n */\nfunction analyzeFastPath(node: AgNode | null, scrollAncestors: AgNode[]): string[] {\n const analysis: string[] = []\n\n if (!node) {\n analysis.push(\"⚠ No node found at mismatch position - possible virtualization issue\")\n return analysis\n }\n\n const flags = node\n const allClean = !isAnyDirty(flags.dirtyBits, flags.dirtyEpoch) && !flags.layoutDirty\n\n if (allClean) {\n analysis.push(\"⚠ ALL DIRTY FLAGS FALSE - fast-path likely skipped this node\")\n }\n\n // Check if node is in a scroll container\n const scrollParent = scrollAncestors[0]\n if (scrollParent?.scrollState) {\n const ss = scrollParent.scrollState\n const childIndex = node.parent ? node.parent.children.indexOf(node) : -1\n\n // Check if this node SHOULD be in visible range\n const inVisibleRange = childIndex >= ss.firstVisibleChild && childIndex <= ss.lastVisibleChild\n if (!inVisibleRange && childIndex >= 0) {\n analysis.push(\n `⚠ Node index ${childIndex} is OUTSIDE visible range [${ss.firstVisibleChild}..${ss.lastVisibleChild}]`,\n )\n analysis.push(\" → Node should have been skipped, but mismatch suggests it should render\")\n } else if (inVisibleRange) {\n analysis.push(`✓ Node index ${childIndex} is in visible range [${ss.firstVisibleChild}..${ss.lastVisibleChild}]`)\n }\n\n // Check scroll offset\n if (ss.offset === ss.prevOffset) {\n analysis.push(\"✓ Scroll offset unchanged (fast-path enabled for children)\")\n } else {\n analysis.push(`⚠ Scroll offset CHANGED: ${ss.prevOffset} → ${ss.offset}`)\n }\n\n // Check if visible range might have changed\n if (ss.firstVisibleChild !== 0 || ss.lastVisibleChild !== scrollParent.children.length - 1) {\n analysis.push(\n ` Visible range is partial: [${ss.firstVisibleChild}..${ss.lastVisibleChild}] of ${scrollParent.children.length} children`,\n )\n analysis.push(\" → If visible range changed, newly visible children need rendering\")\n }\n }\n\n // Check prevLayout\n const layoutChanged = rectChanged(node.prevLayout, node.boxRect)\n if (!layoutChanged && node.prevLayout) {\n analysis.push(\"✓ Layout unchanged (prevLayout matches boxRect)\")\n } else if (!node.prevLayout) {\n analysis.push(\"⚠ prevLayout is NULL - node may never have been rendered before\")\n } else {\n analysis.push(\"⚠ Layout CHANGED but node still skipped - dirty flag not set?\")\n }\n\n // Check for sibling child position changes\n if (node.parent && node.parent.children.length > 1) {\n let siblingMoved = false\n for (const sibling of node.parent.children) {\n if (sibling !== node && sibling.boxRect && sibling.prevLayout) {\n if (sibling.boxRect.x !== sibling.prevLayout.x || sibling.boxRect.y !== sibling.prevLayout.y) {\n siblingMoved = true\n break\n }\n }\n }\n if (siblingMoved) {\n analysis.push(\"⚠ SIBLING POSITION CHANGED - parent should have detected this\")\n }\n }\n\n // Check hidden state\n if (node.hidden) {\n analysis.push(\"⚠ Node is HIDDEN (Suspense) - should not be rendered\")\n }\n\n return analysis\n}\n\n/**\n * Build full mismatch debug context.\n */\nexport function buildMismatchContext(\n root: AgNode,\n x: number,\n y: number,\n incrementalCell: Cell,\n freshCell: Cell,\n renderNum: number,\n): MismatchDebugContext {\n const innermost = findNodeAtPosition(root, x, y)\n const containing = findAllContainingNodes(root, x, y)\n const scrollAncestorNodes = innermost ? findScrollAncestors(innermost) : []\n\n return {\n position: { x, y },\n cells: {\n incremental: incrementalCell,\n fresh: freshCell,\n },\n renderNum,\n node: innermost ? getNodeDebugInfo(innermost) : null,\n scrollAncestors: scrollAncestorNodes.map(getNodeDebugInfo),\n containingNodes: containing.map(getNodeDebugInfo),\n fastPathAnalysis: analyzeFastPath(innermost, scrollAncestorNodes),\n }\n}\n\n/**\n * Format mismatch context as a human-readable string.\n *\n * @param ctx - The mismatch debug context (node attribution, dirty flags, scroll, fast-path)\n * @param renderPhaseStats - Optional render-phase instrumentation snapshot (auto-included by SILVERY_STRICT)\n */\nexport function formatMismatchContext(ctx: MismatchDebugContext, renderPhaseStats?: RenderPhaseStats): string {\n const lines: string[] = []\n\n // Header\n lines.push(`SILVERY_STRICT: MISMATCH at (${ctx.position.x}, ${ctx.position.y}) on render #${ctx.renderNum}`)\n lines.push(\"\")\n\n // Cell values\n const { incremental, fresh } = ctx.cells\n lines.push(\"CELL VALUES:\")\n lines.push(\n ` incremental: char=${JSON.stringify(incremental.char)} fg=${JSON.stringify(incremental.fg)} bg=${JSON.stringify(incremental.bg)} attrs=${JSON.stringify(incremental.attrs)}`,\n )\n lines.push(\n ` fresh: char=${JSON.stringify(fresh.char)} fg=${JSON.stringify(fresh.fg)} bg=${JSON.stringify(fresh.bg)} attrs=${JSON.stringify(fresh.attrs)}`,\n )\n lines.push(\"\")\n\n // Node attribution\n if (ctx.node) {\n lines.push(\"INNERMOST NODE:\")\n lines.push(` path: ${ctx.node.path}`)\n lines.push(` type: ${ctx.node.type}`)\n if (ctx.node.backgroundColor) {\n lines.push(` backgroundColor: ${ctx.node.backgroundColor}`)\n }\n lines.push(\"\")\n\n // Dirty flags\n const flags = ctx.node.dirtyFlags\n const activeFlags = Object.entries(flags)\n .filter(([, v]) => v)\n .map(([k]) => k)\n lines.push(\"DIRTY FLAGS:\")\n if (activeFlags.length > 0) {\n lines.push(` active: ${activeFlags.join(\", \")}`)\n } else {\n lines.push(\" active: (none - node was clean)\")\n }\n lines.push(\n ` all: contentDirty=${flags.contentDirty} stylePropsDirty=${flags.stylePropsDirty} subtreeDirty=${flags.subtreeDirty} childrenDirty=${flags.childrenDirty} layoutDirty=${flags.layoutDirty}`,\n )\n lines.push(\"\")\n\n // Layout info\n const { layout } = ctx.node\n lines.push(\"LAYOUT:\")\n if (layout.layoutChanged) {\n lines.push(\" ⚠ LAYOUT CHANGED:\")\n lines.push(` prevLayout: ${formatRect(layout.prevLayout)}`)\n lines.push(` boxRect: ${formatRect(layout.boxRect)}`)\n } else {\n lines.push(` boxRect: ${formatRect(layout.boxRect)}`)\n }\n lines.push(` scrollRect: ${formatRect(layout.scrollRect)}`)\n lines.push(\"\")\n\n // Scroll context\n if (ctx.node.scroll) {\n lines.push(\"SCROLL STATE (this node):\")\n formatScrollState(lines, ctx.node.scroll)\n lines.push(\"\")\n }\n } else {\n lines.push(\"INNERMOST NODE: (none found at this position)\")\n lines.push(\"\")\n }\n\n // Scroll ancestors\n if (ctx.scrollAncestors.length > 0) {\n lines.push(\"SCROLL ANCESTORS:\")\n for (const ancestor of ctx.scrollAncestors) {\n lines.push(` ${ancestor.path}:`)\n if (ancestor.scroll) {\n formatScrollState(lines, ancestor.scroll, \" \")\n }\n }\n lines.push(\"\")\n }\n\n // Containing nodes (for debugging layering issues)\n if (ctx.containingNodes.length > 1) {\n lines.push(\"ALL CONTAINING NODES (outermost to innermost):\")\n for (const node of ctx.containingNodes) {\n const flags = Object.entries(node.dirtyFlags)\n .filter(([, v]) => v)\n .map(([k]) => k.replace(\"Dirty\", \"\"))\n .join(\",\")\n const flagStr = flags ? ` [${flags}]` : \" [clean]\"\n const bgStr = node.backgroundColor ? ` bg=${node.backgroundColor}` : \"\"\n const childStr = node.childIndex !== null ? ` child[${node.childIndex}]` : \"\"\n lines.push(` ${node.path}${flagStr}${bgStr}${childStr}`)\n }\n lines.push(\"\")\n }\n\n // Fast-path analysis\n if (ctx.fastPathAnalysis.length > 0) {\n lines.push(\"FAST-PATH ANALYSIS:\")\n for (const line of ctx.fastPathAnalysis) {\n lines.push(` ${line}`)\n }\n lines.push(\"\")\n }\n\n // Render-phase instrumentation stats\n if (renderPhaseStats) {\n const s = renderPhaseStats\n lines.push(\"RENDER PHASE STATS:\")\n lines.push(` nodesVisited: ${s.nodesVisited} nodesRendered: ${s.nodesRendered} nodesSkipped: ${s.nodesSkipped}`)\n lines.push(` textNodes: ${s.textNodes} boxNodes: ${s.boxNodes} clearOps: ${s.clearOps}`)\n // Per-flag breakdown (why nodes weren't skipped)\n const flagLines: string[] = []\n if (s.noPrevBuffer) flagLines.push(`noPrevBuffer=${s.noPrevBuffer}`)\n if (s.flagContentDirty) flagLines.push(`contentDirty=${s.flagContentDirty}`)\n if (s.flagStylePropsDirty) flagLines.push(`stylePropsDirty=${s.flagStylePropsDirty}`)\n if (s.flagLayoutChanged) flagLines.push(`layoutChanged=${s.flagLayoutChanged}`)\n if (s.flagSubtreeDirty) flagLines.push(`subtreeDirty=${s.flagSubtreeDirty}`)\n if (s.flagChildrenDirty) flagLines.push(`childrenDirty=${s.flagChildrenDirty}`)\n if (s.flagChildPositionChanged) flagLines.push(`childPositionChanged=${s.flagChildPositionChanged}`)\n if (flagLines.length > 0) {\n lines.push(` render reasons: ${flagLines.join(\", \")}`)\n }\n // Scroll container diagnostics\n if (s.scrollContainerCount > 0) {\n lines.push(` scrollContainers: ${s.scrollContainerCount} viewportCleared: ${s.scrollViewportCleared}`)\n if (s.scrollClearReason) lines.push(` scrollClearReason: ${s.scrollClearReason}`)\n }\n // Normal container diagnostics\n if (s.normalChildrenRepaint > 0) {\n lines.push(` normalChildrenRepaint: ${s.normalChildrenRepaint}`)\n if (s.normalRepaintReason) lines.push(` normalRepaintReason: ${s.normalRepaintReason}`)\n }\n // Cascade diagnostics\n if (s.cascadeMinDepth < 999) {\n lines.push(` cascadeMinDepth: ${s.cascadeMinDepth}`)\n if (s.cascadeNodes) lines.push(` cascadeNodes: ${s.cascadeNodes}`)\n }\n lines.push(\"\")\n }\n\n return lines.join(\"\\n\")\n}\n\nfunction formatRect(rect: Rect | null): string {\n if (!rect) return \"(null)\"\n return `{x:${rect.x}, y:${rect.y}, w:${rect.width}, h:${rect.height}}`\n}\n\nfunction formatScrollState(lines: string[], scroll: NonNullable<NodeDebugInfo[\"scroll\"]>, indent = \" \"): void {\n if (scroll.offsetChanged) {\n lines.push(`${indent}⚠ SCROLL CHANGED: offset ${scroll.prevOffset} → ${scroll.offset}`)\n } else {\n lines.push(`${indent}offset: ${scroll.offset}`)\n }\n lines.push(\n `${indent}viewport: ${scroll.viewportHeight}/${scroll.contentHeight} (hidden: ▲${scroll.hiddenAbove} ▼${scroll.hiddenBelow})`,\n )\n lines.push(`${indent}visibleRange: [${scroll.firstVisibleChild}..${scroll.lastVisibleChild}]`)\n}\n","/**\n * Non-TTY Mode Support for Silvery\n *\n * Provides detection and rendering modes for non-interactive environments:\n * - Piped output (process.stdout.isTTY === false)\n * - CI environments\n * - TERM=dumb\n *\n * When in non-TTY mode, silvery avoids cursor positioning codes that garble\n * output in non-interactive environments.\n *\n * Modes:\n * - 'auto': Detect based on environment (default)\n * - 'tty': Force TTY mode (normal cursor positioning)\n * - 'line-by-line': Simple newline-separated output, no cursor movement\n * - 'static': Single output at end (no updates)\n * - 'plain': Strip all ANSI codes\n */\n\nimport { stripAnsi } from \"./unicode\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Non-TTY rendering mode.\n *\n * - 'auto': Auto-detect based on environment\n * - 'tty': Force TTY mode with cursor positioning\n * - 'line-by-line': Output lines without cursor repositioning\n * - 'static': Single final output only\n * - 'plain': Strip all ANSI escape codes\n */\nexport type NonTTYMode = \"auto\" | \"tty\" | \"line-by-line\" | \"static\" | \"plain\"\n\n/**\n * Options for non-TTY output.\n */\nexport interface NonTTYOptions {\n /** The rendering mode. Default: 'auto' */\n mode?: NonTTYMode\n /** Output stream to check for TTY status. Default: process.stdout */\n stdout?: NodeJS.WriteStream\n}\n\n/**\n * Resolved non-TTY mode after auto-detection.\n */\nexport type ResolvedNonTTYMode = Exclude<NonTTYMode, \"auto\">\n\n// ============================================================================\n// Detection\n// ============================================================================\n\n/**\n * Check if the environment is a TTY.\n *\n * Returns false if:\n * - stdout.isTTY is false or undefined\n * - TERM=dumb\n * - CI environment variables are set\n */\nexport function isTTY(stdout: NodeJS.WriteStream = process.stdout): boolean {\n // Check stdout.isTTY\n if (!stdout.isTTY) {\n return false\n }\n\n // Check TERM=dumb\n if (process.env.TERM === \"dumb\") {\n return false\n }\n\n // Check common CI environment variables\n if (\n process.env.CI ||\n process.env.GITHUB_ACTIONS ||\n process.env.GITLAB_CI ||\n process.env.JENKINS_URL ||\n process.env.BUILDKITE ||\n process.env.CIRCLECI ||\n process.env.TRAVIS\n ) {\n return false\n }\n\n return true\n}\n\n/**\n * Resolve the non-TTY mode based on options and environment.\n *\n * When mode is 'auto':\n * - If TTY detected: returns 'tty'\n * - If non-TTY detected: returns 'line-by-line'\n */\nexport function resolveNonTTYMode(options: NonTTYOptions = {}): ResolvedNonTTYMode {\n const { mode = \"auto\", stdout = process.stdout } = options\n\n if (mode !== \"auto\") {\n return mode\n }\n\n // Auto-detect based on environment\n return isTTY(stdout) ? \"tty\" : \"line-by-line\"\n}\n\n// Re-export stripAnsi from unicode.ts (canonical implementation)\nexport { stripAnsi } from \"./unicode\"\n\n// ============================================================================\n// Line-by-Line Output\n// ============================================================================\n\n/**\n * Convert buffer output to line-by-line format.\n *\n * Instead of using cursor positioning, outputs each line with a simple\n * carriage return and clear-to-end-of-line sequence.\n *\n * @param content The rendered content (may contain ANSI codes but no cursor positioning)\n * @param prevLineCount Number of lines in the previous frame (for clearing)\n * @returns Output string suitable for non-TTY rendering\n */\nexport function toLineByLineOutput(content: string, prevLineCount: number): string {\n const lines = content.split(\"\\n\")\n let output = \"\"\n\n // Move cursor up to overwrite previous content (if any)\n if (prevLineCount > 0) {\n // Move to start of first line\n output += \"\\r\"\n // Move up\n if (prevLineCount > 1) {\n output += `\\x1b[${prevLineCount - 1}A`\n }\n }\n\n // Output each line\n for (let i = 0; i < lines.length; i++) {\n if (i > 0) {\n output += \"\\n\"\n }\n output += lines[i]\n // Clear to end of line (removes leftover content from longer previous lines)\n output += \"\\x1b[K\"\n }\n\n // Clear any remaining lines from previous frame\n const extraLines = prevLineCount - lines.length\n if (extraLines > 0) {\n for (let i = 0; i < extraLines; i++) {\n output += \"\\n\\x1b[K\"\n }\n // Move cursor back up to end of content\n output += `\\x1b[${extraLines}A`\n }\n\n return output\n}\n\n/**\n * Convert buffer output to plain text format.\n *\n * Strips all ANSI codes and outputs simple newline-separated text.\n * No cursor movement or clearing.\n *\n * @param content The rendered content\n * @param prevLineCount Number of lines in the previous frame (unused in plain mode)\n * @returns Plain text output\n */\nexport function toPlainOutput(content: string, _prevLineCount: number): string {\n // Strip ANSI codes\n const plain = stripAnsi(content)\n\n // Trim trailing whitespace from each line but preserve structure\n const lines = plain.split(\"\\n\").map((line) => line.trimEnd())\n\n // Remove trailing empty lines\n while (lines.length > 0 && lines[lines.length - 1] === \"\") {\n lines.pop()\n }\n\n return lines.join(\"\\n\")\n}\n\n// ============================================================================\n// Output Helpers\n// ============================================================================\n\n/**\n * Create an output transformer based on the non-TTY mode.\n *\n * @param mode The resolved non-TTY mode\n * @returns A function that transforms output based on the mode\n */\nexport function createOutputTransformer(mode: ResolvedNonTTYMode): (content: string, prevLineCount: number) => string {\n switch (mode) {\n case \"tty\":\n // Pass through unchanged\n return (content) => content\n\n case \"line-by-line\":\n return toLineByLineOutput\n\n case \"static\":\n // For static mode, we return empty string for intermediate renders\n // The final render is handled by the caller\n return () => \"\"\n\n case \"plain\":\n return toPlainOutput\n }\n}\n\n/**\n * Count the number of lines in a string.\n */\nexport function countLines(str: string): number {\n if (!str) return 0\n return str.split(\"\\n\").length\n}\n","/**\n * Clipboard Backend Abstraction\n *\n * Pluggable clipboard system with support for multiple backends.\n * The default backend uses OSC 52 for terminal clipboard access.\n *\n * Architecture:\n * - ClipboardBackend: interface for clipboard read/write\n * - ClipboardData: multi-format clipboard content (text, markdown, html, internal)\n * - createOsc52Backend: OSC 52 terminal clipboard (default)\n * - createInternalClipboardBackend: in-memory store for rich app-internal paste\n * - createCompositeClipboard: fan-out writes to multiple backends\n *\n * OSC 52 Protocol:\n * - Copy: ESC ] 52 ; c ; <base64> BEL\n * - Query: ESC ] 52 ; c ; ? BEL\n * - Response: ESC ] 52 ; c ; <base64> BEL (or ST terminator)\n *\n * Supported by: Ghostty, Kitty, WezTerm, iTerm2, xterm, foot, tmux\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Multi-format clipboard content.\n *\n * Plain text is always present. Optional rich formats allow applications\n * to provide structured data for within-app paste without losing it\n * through the plain-text-only system clipboard.\n */\nexport interface ClipboardData {\n /** Plain text content (always present) */\n text: string\n /** Markdown representation */\n markdown?: string\n /** HTML representation */\n html?: string\n /** App-specific structured data (e.g., node tree for structured paste) */\n internal?: unknown\n}\n\n/**\n * Clipboard backend capabilities.\n *\n * `text` is always true — every backend supports plain text.\n * Rich format support is backend-dependent.\n */\nexport interface ClipboardCapabilities {\n readonly text: true\n readonly html?: boolean\n readonly markdown?: boolean\n readonly internal?: boolean\n}\n\n/**\n * Pluggable clipboard backend.\n *\n * Backends handle the transport of clipboard data to/from the system\n * or an in-memory store. The framework writes ClipboardData; the backend\n * decides what formats it can actually carry.\n */\nexport interface ClipboardBackend {\n /** Write clipboard data. Backends may ignore formats they don't support. */\n write(data: ClipboardData): void | Promise<void>\n /** Read clipboard contents as plain text. Not all backends support read. */\n read?(): Promise<string>\n /** What formats this backend supports */\n readonly capabilities: ClipboardCapabilities\n}\n\n// ============================================================================\n// Writable interface (avoid coupling to Node.js WriteStream)\n// ============================================================================\n\n/** Minimal writable interface for clipboard output */\ninterface Writable {\n write(data: string): boolean | void\n}\n\n// ============================================================================\n// OSC 52 Constants\n// ============================================================================\n\nconst ESC = \"\\x1b\"\nconst BEL = \"\\x07\"\n\n/** OSC 52 response prefix */\nconst OSC52_PREFIX = `${ESC}]52;c;`\n\n// ============================================================================\n// OSC 52 Backend\n// ============================================================================\n\n/**\n * Create an OSC 52 clipboard backend.\n *\n * Writes plain text to the system clipboard via the terminal's OSC 52 support.\n * Works across SSH sessions. Rich formats (markdown, html, internal) are\n * silently ignored — OSC 52 only carries plain text.\n *\n * Quirks:\n * - Some terminals limit payload size (~100KB)\n * - tmux requires `set -g set-clipboard on`\n * - Some terminals only support BEL terminator (not ST)\n */\nexport function createOsc52Backend(stdout: Writable): ClipboardBackend {\n return {\n write(data: ClipboardData): void {\n const base64 = Buffer.from(data.text, \"utf-8\").toString(\"base64\")\n stdout.write(`${ESC}]52;c;${base64}${BEL}`)\n },\n\n async read(): Promise<string> {\n // OSC 52 read requires async response parsing from stdin.\n // The query is sent here; the caller must parse the response\n // from the terminal input stream using parseClipboardResponse().\n stdout.write(`${ESC}]52;c;?${BEL}`)\n // Note: actual response arrives asynchronously via stdin.\n // This is a limitation of the terminal protocol — true async\n // read requires coordination with the input parser.\n return \"\"\n },\n\n capabilities: { text: true },\n }\n}\n\n// ============================================================================\n// Internal Clipboard Backend\n// ============================================================================\n\n/**\n * In-memory clipboard store for within-app paste.\n *\n * Stores the full ClipboardData including rich formats that OSC 52 can't carry.\n * Used alongside OSC 52 so plain text goes to the system clipboard while\n * rich data is available for internal paste operations.\n */\nexport function createInternalClipboardBackend(): ClipboardBackend & {\n /** Get the stored clipboard data, or null if empty */\n getData(): ClipboardData | null\n /** Get the timestamp of the last write */\n getTimestamp(): number\n} {\n let stored: ClipboardData | null = null\n let timestamp = 0\n\n return {\n write(data: ClipboardData): void {\n stored = { ...data }\n timestamp = Date.now()\n },\n\n async read(): Promise<string> {\n return stored?.text ?? \"\"\n },\n\n getData(): ClipboardData | null {\n return stored ? { ...stored } : null\n },\n\n getTimestamp(): number {\n return timestamp\n },\n\n capabilities: { text: true, html: true, markdown: true, internal: true },\n }\n}\n\n// ============================================================================\n// Composite Clipboard\n// ============================================================================\n\n/**\n * Create a composite clipboard that writes to multiple backends.\n *\n * Writes fan out to all backends. Reads come from the first backend\n * that supports read (in order). This lets you do OSC 52 + internal\n * store simultaneously: plain text goes to system clipboard, rich\n * data stays in memory for structured paste.\n */\nexport function createCompositeClipboard(...backends: ClipboardBackend[]): ClipboardBackend {\n return {\n write(data: ClipboardData): void | Promise<void> {\n const promises: Promise<void>[] = []\n for (const backend of backends) {\n const result = backend.write(data)\n if (result instanceof Promise) {\n promises.push(result)\n }\n }\n if (promises.length > 0) {\n return Promise.all(promises).then(() => undefined)\n }\n },\n\n async read(): Promise<string> {\n for (const backend of backends) {\n if (backend.read) {\n const text = await backend.read()\n if (text) return text\n }\n }\n return \"\"\n },\n\n capabilities: {\n text: true,\n html: backends.some((b) => b.capabilities.html) || undefined,\n markdown: backends.some((b) => b.capabilities.markdown) || undefined,\n internal: backends.some((b) => b.capabilities.internal) || undefined,\n },\n }\n}\n\n// ============================================================================\n// Backwards-compatible API (delegates to OSC 52)\n// ============================================================================\n\n/**\n * Copy text to the system clipboard via OSC 52.\n * Encodes the text as base64 and writes the OSC 52 sequence to stdout.\n *\n * @deprecated Use createOsc52Backend() for new code.\n */\nexport function copyToClipboard(stdout: NodeJS.WriteStream, text: string): void {\n const base64 = Buffer.from(text).toString(\"base64\")\n stdout.write(`${ESC}]52;c;${base64}${BEL}`)\n}\n\n/**\n * Request clipboard contents via OSC 52.\n * Writes the OSC 52 query sequence. The terminal will respond with\n * an OSC 52 response containing the clipboard contents as base64.\n * Use parseClipboardResponse() to decode the response.\n *\n * @deprecated Use createOsc52Backend() for new code.\n */\nexport function requestClipboard(stdout: NodeJS.WriteStream): void {\n stdout.write(`${ESC}]52;c;?${BEL}`)\n}\n\n// ============================================================================\n// Response Parsing\n// ============================================================================\n\n/**\n * Parse an OSC 52 clipboard response and decode the base64 content.\n *\n * Returns the decoded clipboard text, or null if the input is not\n * an OSC 52 clipboard response.\n *\n * Handles both BEL (\\x07) and ST (ESC \\) terminators.\n */\nexport function parseClipboardResponse(input: string): string | null {\n const prefixIdx = input.indexOf(OSC52_PREFIX)\n if (prefixIdx === -1) return null\n\n const contentStart = prefixIdx + OSC52_PREFIX.length\n\n // Reject the query marker — it's not a response\n if (input[contentStart] === \"?\") return null\n\n // Find terminator: BEL (\\x07) or ST (ESC \\)\n let contentEnd = input.indexOf(BEL, contentStart)\n if (contentEnd === -1) {\n contentEnd = input.indexOf(`${ESC}\\\\`, contentStart)\n }\n if (contentEnd === -1) return null\n\n const base64 = input.slice(contentStart, contentEnd)\n return Buffer.from(base64, \"base64\").toString(\"utf-8\")\n}\n","/**\n * Silvery Render Scheduler\n *\n * Batches rapid state updates to prevent flicker and improve performance.\n * Uses queueMicrotask for coalescing multiple synchronous state changes\n * into a single render pass.\n *\n * Features:\n * - Microtask-based batching (coalesces synchronous updates)\n * - Frame batching to prevent flicker\n * - Resize handling with debounce\n * - Clean shutdown\n */\n\nimport { appendFileSync } from \"node:fs\"\nimport { type Logger, createLogger } from \"loggily\"\nimport { type TerminalBuffer, bufferToText, cellEquals } from \"./buffer\"\nimport { buildMismatchContext, formatMismatchContext } from \"@silvery/test/debug-mismatch\"\nimport {\n type ResolvedNonTTYMode as ResolvedMode,\n countLines,\n createOutputTransformer,\n resolveNonTTYMode,\n stripAnsi,\n} from \"./non-tty\"\nimport { getCursorState as globalGetCursorState, type CursorAccessors } from \"@silvery/ag-react/hooks/useCursor\"\nimport { copyToClipboard as copyToClipboardImpl } from \"./clipboard\"\nimport { ANSI, notify as notifyTerminal, setCursorStyle, resetCursorStyle } from \"./output\"\nimport { executeRender, type PipelineConfig } from \"./pipeline\"\nimport type { RenderPhaseStats } from \"./pipeline/types\"\nimport type { AgNode } from \"@silvery/ag/types\"\n\nconst log = createLogger(\"silvery:scheduler\")\n\n/**\n * Whether synchronized update mode (DEC 2026) is enabled.\n *\n * Disabled by default due to a Ghostty rendering bug where incremental\n * cursor-positioned updates inside a sync region cause progressive visual\n * corruption. Works correctly in Kitty. Full renders (bufferToAnsi) work\n * fine with sync — only incremental diff output (changesToAnsi) triggers it.\n *\n * Set SILVERY_SYNC_UPDATE=1 to force-enable (e.g., for testing in Kitty).\n * TODO: Re-enable by default once the Ghostty bug is fixed.\n * See: https://github.com/ghostty-org/ghostty/discussions/11002\n */\nconst SYNC_UPDATE_ENABLED = process.env.SILVERY_SYNC_UPDATE === \"1\" || process.env.SILVERY_SYNC_UPDATE === \"true\"\n\n// ============================================================================\n// Errors\n// ============================================================================\n\n// Re-export from errors.ts (kept separate for React-free barrel imports)\nexport { IncrementalRenderMismatchError } from \"./errors\"\nimport { IncrementalRenderMismatchError } from \"./errors\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Non-TTY mode for rendering in non-interactive environments.\n */\nexport type NonTTYMode = \"auto\" | \"tty\" | \"line-by-line\" | \"static\" | \"plain\"\n\n/**\n * Resolved non-TTY mode after auto-detection.\n */\nexport type ResolvedNonTTYMode = Exclude<NonTTYMode, \"auto\">\n\nexport interface SchedulerOptions {\n /** stdout stream for writing output */\n stdout: NodeJS.WriteStream\n /** Root Silvery node */\n root: AgNode\n /** Debug mode - logs render timing */\n debug?: boolean\n /** Minimum time between frames in ms (default: 16 for ~60fps) */\n minFrameTime?: number\n /** Render mode: fullscreen (absolute positioning) or inline (relative positioning) */\n mode?: \"fullscreen\" | \"inline\"\n /**\n * Non-TTY mode for non-interactive environments (default: 'auto')\n *\n * - 'auto': Detect based on environment\n * - 'tty': Force TTY mode\n * - 'line-by-line': Simple line output\n * - 'static': Only output final frame\n * - 'plain': Strip all ANSI codes\n */\n nonTTYMode?: NonTTYMode\n /** Slow frame warning threshold in ms (default: 50). Set to 0 to disable. */\n slowFrameThreshold?: number\n /** Pipeline configuration (caps-scoped measurer + output phase) */\n pipelineConfig?: PipelineConfig\n /** Per-instance cursor accessors. Falls back to module-level globals if not provided. */\n cursorAccessors?: CursorAccessors\n /**\n * Custom output writer. When provided, all render output is routed through\n * this function instead of stdout.write(). Used by the output guard to\n * ensure only silvery's render pipeline writes to stdout in alt screen mode.\n */\n writeOutput?: (data: string) => boolean\n}\n\nexport interface RenderStats {\n /** Number of renders executed */\n renderCount: number\n /** Number of renders skipped (batched) */\n skippedCount: number\n /** Last render duration in ms */\n lastRenderTime: number\n /** Average render time in ms */\n avgRenderTime: number\n}\n\n// ============================================================================\n// RenderScheduler Class\n// ============================================================================\n\n/**\n * Schedules and batches render operations.\n *\n * Usage:\n * ```ts\n * const scheduler = new RenderScheduler({\n * stdout: process.stdout,\n * root: rootNode,\n * });\n *\n * // Schedule renders (automatically batched)\n * scheduler.scheduleRender();\n * scheduler.scheduleRender(); // This won't cause duplicate render\n *\n * // Force immediate render\n * scheduler.forceRender();\n *\n * // Clean shutdown\n * scheduler.dispose();\n * ```\n */\nexport class RenderScheduler {\n private stdout: NodeJS.WriteStream\n private root: AgNode\n private debugMode: boolean\n private minFrameTime: number\n private slowFrameThreshold: number\n private mode: \"fullscreen\" | \"inline\"\n private pipelineConfig?: PipelineConfig\n private getCursorState: () => import(\"@silvery/ag-react/hooks/useCursor\").CursorState | null\n private nonTTYMode: ResolvedMode\n private outputTransformer: (content: string, prevLineCount: number) => string\n private writeOutput: (data: string) => boolean\n private log: Logger\n\n /** Previous buffer for diffing */\n private prevBuffer: TerminalBuffer | null = null\n\n /** Line count of previous render (for non-TTY modes) */\n private prevLineCount = 0\n\n /** Accumulated output for static mode */\n private staticOutput = \"\"\n\n /** Is a render currently scheduled? */\n private renderScheduled = false\n\n /** Last render timestamp */\n private lastRenderTime = 0\n\n /** Pending frame timeout (for frame rate limiting) */\n private frameTimeout: ReturnType<typeof setTimeout> | null = null\n\n /** Resize listener cleanup */\n private resizeCleanup: (() => void) | null = null\n\n /** Render statistics */\n private stats: RenderStats = {\n renderCount: 0,\n skippedCount: 0,\n lastRenderTime: 0,\n avgRenderTime: 0,\n }\n\n /** Is the scheduler disposed? */\n private disposed = false\n\n /** Is the scheduler paused? When paused, renders are deferred until resume. */\n private paused = false\n\n /** Was a render requested while paused? */\n private pendingWhilePaused = false\n\n /**\n * Lines written to stdout between renders (inline mode only).\n * When useScrollback or other code writes to stdout, those lines\n * displace the terminal cursor. This offset is consumed on the next render.\n */\n private scrollbackOffset = 0\n\n constructor(options: SchedulerOptions) {\n this.stdout = options.stdout\n this.root = options.root\n this.debugMode = options.debug ?? false\n this.minFrameTime = options.minFrameTime ?? 16\n this.slowFrameThreshold = options.slowFrameThreshold ?? 50\n this.mode = options.mode ?? \"fullscreen\"\n this.pipelineConfig = options.pipelineConfig\n this.getCursorState = options.cursorAccessors?.getCursorState ?? globalGetCursorState\n this.writeOutput = options.writeOutput ?? ((data: string) => options.stdout.write(data))\n this.log = createLogger(\"silvery:scheduler\") as unknown as Logger\n\n // Resolve non-TTY mode based on environment\n this.nonTTYMode = resolveNonTTYMode({\n mode: options.nonTTYMode,\n stdout: this.stdout,\n })\n this.outputTransformer = createOutputTransformer(this.nonTTYMode)\n\n log.debug?.(`non-TTY mode resolved to: ${this.nonTTYMode}`)\n\n // Listen for terminal resize (only in TTY mode)\n if (this.nonTTYMode === \"tty\") {\n this.setupResizeListener()\n }\n }\n\n /**\n * Get the resolved non-TTY mode.\n */\n getNonTTYMode(): ResolvedMode {\n return this.nonTTYMode\n }\n\n // ==========================================================================\n // Public API\n // ==========================================================================\n\n /**\n * Schedule a render on the next microtask.\n *\n * Multiple calls within the same synchronous execution will be\n * coalesced into a single render.\n */\n scheduleRender(): void {\n if (this.disposed) return\n\n if (this.paused) {\n this.pendingWhilePaused = true\n return\n }\n\n if (this.renderScheduled) {\n this.stats.skippedCount++\n log.debug?.(`render skipped (batched), total: ${this.stats.skippedCount}`)\n return\n }\n\n this.renderScheduled = true\n log.debug?.(\"render scheduled\")\n\n // Use queueMicrotask for batching synchronous updates\n queueMicrotask(() => {\n this.renderScheduled = false\n\n if (this.disposed) return\n\n // Check frame rate limiting\n const now = Date.now()\n const timeSinceLastRender = now - this.lastRenderTime\n\n if (timeSinceLastRender < this.minFrameTime) {\n // Schedule for next frame\n log.debug?.(`frame limited, delay: ${this.minFrameTime - timeSinceLastRender}ms`)\n this.scheduleNextFrame(this.minFrameTime - timeSinceLastRender)\n } else {\n this.executeRender()\n }\n })\n }\n\n /**\n * Force an immediate render, bypassing batching.\n */\n forceRender(): void {\n if (this.disposed) return\n\n if (this.paused) {\n this.pendingWhilePaused = true\n return\n }\n\n // Cancel any pending scheduled render\n this.renderScheduled = false\n if (this.frameTimeout) {\n clearTimeout(this.frameTimeout)\n this.frameTimeout = null\n }\n\n this.executeRender()\n }\n\n /**\n * Get render statistics.\n */\n getStats(): RenderStats {\n return { ...this.stats }\n }\n\n /**\n * Report lines written to stdout between renders (inline mode only).\n * This adjusts cursor position tracking so the next render accounts\n * for the extra lines. Used by useScrollback to notify the scheduler\n * when it writes frozen items to stdout.\n */\n addScrollbackLines(lines: number): void {\n if (this.mode !== \"inline\" || lines <= 0) return\n this.scrollbackOffset += lines\n }\n\n /**\n * Send a terminal notification.\n *\n * Auto-detects terminal type and uses the best available method:\n * - iTerm2 → OSC 9\n * - Kitty → OSC 99\n * - Others → BEL\n */\n notify(message: string, opts?: { title?: string }): void {\n if (this.disposed) return\n // Route through writeOutput so the output guard allows it through\n const writable = { write: (data: string) => this.writeOutput(data) } as NodeJS.WriteStream\n notifyTerminal(writable, message, opts)\n }\n\n /**\n * Copy text to the system clipboard via OSC 52.\n * Works across SSH sessions in terminals that support it.\n */\n copyToClipboard(text: string): void {\n if (this.disposed) return\n // Route through writeOutput so the output guard allows it through\n const writable = { write: (data: string) => this.writeOutput(data) } as NodeJS.WriteStream\n copyToClipboardImpl(writable, text)\n }\n\n /**\n * Pause rendering. While paused, scheduled and forced renders are deferred.\n * Input handling continues normally. Call resume() to unpause and force a\n * full redraw. Used for screen-switching (alt screen ↔ normal screen).\n */\n pause(): void {\n if (this.disposed || this.paused) return\n this.paused = true\n this.pendingWhilePaused = false\n log.debug?.(\"scheduler paused\")\n }\n\n /**\n * Resume rendering after pause. Resets the previous buffer so the next\n * render outputs everything (full redraw), then forces an immediate render.\n */\n resume(): void {\n if (this.disposed || !this.paused) return\n this.paused = false\n log.debug?.(\"scheduler resumed\")\n\n // Reset buffer for full redraw (alt screen was switched)\n this.prevBuffer = null\n\n // If anything was deferred, render now\n if (this.pendingWhilePaused) {\n this.pendingWhilePaused = false\n this.executeRender()\n }\n }\n\n /**\n * Whether the scheduler is currently paused.\n */\n isPaused(): boolean {\n return this.paused\n }\n\n /**\n * Clear the terminal and reset buffer.\n */\n clear(): void {\n if (this.disposed) return\n\n // Clear screen and keep cursor hidden\n this.writeOutput(\"\\x1b[2J\\x1b[H\\x1b[?25l\")\n\n // Reset buffer so next render outputs everything\n this.prevBuffer = null\n }\n\n /**\n * Dispose the scheduler and clean up resources.\n */\n [Symbol.dispose](): void {\n this.dispose()\n }\n\n dispose(): void {\n if (this.disposed) return\n\n log.info?.(\n `dispose: renders=${this.stats.renderCount}, skipped=${this.stats.skippedCount}, avg=${Math.round(this.stats.avgRenderTime)}ms`,\n )\n this.disposed = true\n\n // Cancel pending renders\n this.renderScheduled = false\n if (this.frameTimeout) {\n clearTimeout(this.frameTimeout)\n this.frameTimeout = null\n }\n\n // Remove resize listener\n if (this.resizeCleanup) {\n this.resizeCleanup()\n this.resizeCleanup = null\n }\n\n // In static mode, output the final frame on dispose\n if (this.nonTTYMode === \"static\" && this.staticOutput) {\n this.writeOutput(this.staticOutput)\n this.writeOutput(\"\\n\")\n }\n }\n\n /**\n * Get the last rendered output (for static mode).\n * Returns the plain text output that would be written on dispose.\n */\n getStaticOutput(): string {\n return this.staticOutput\n }\n\n // ==========================================================================\n // Private Methods\n // ==========================================================================\n\n /**\n * Schedule render for next frame (frame rate limiting).\n */\n private scheduleNextFrame(delay: number): void {\n if (this.frameTimeout) return\n\n this.frameTimeout = setTimeout(() => {\n this.frameTimeout = null\n if (!this.disposed) {\n this.executeRender()\n }\n }, delay)\n }\n\n /**\n * Execute the actual render.\n */\n private executeRender(): void {\n using render = this.log.span(\"render\")\n const startTime = Date.now()\n\n try {\n // Get terminal dimensions\n const width = this.stdout.columns ?? 80\n // Inline mode: use NaN height so layout engine auto-sizes to content.\n // Fullscreen mode: use terminal rows as the constraint.\n const height = this.mode === \"inline\" ? NaN : (this.stdout.rows ?? 24)\n\n log.debug?.(`render #${this.stats.renderCount + 1}: ${width}x${height}, nonTTYMode=${this.nonTTYMode}`)\n\n // Run render pipeline\n const scrollbackOffset = this.scrollbackOffset\n this.scrollbackOffset = 0 // Consume the offset\n // For inline mode, pass cursor state into the pipeline so the output\n // phase can position the real terminal cursor at the useCursor() location.\n const inlineCursor = this.mode === \"inline\" ? this.getCursorState() : undefined\n const { output, buffer } = executeRender(\n this.root,\n width,\n height,\n this.prevBuffer,\n {\n mode: this.mode,\n scrollbackOffset,\n termRows: this.mode === \"inline\" ? (this.stdout.rows ?? 24) : undefined,\n cursorPos: inlineCursor,\n },\n this.pipelineConfig,\n )\n\n // Transform output based on non-TTY mode\n let transformedOutput: string\n if (this.nonTTYMode === \"tty\") {\n // Pass through unchanged\n transformedOutput = output\n } else if (this.nonTTYMode === \"static\") {\n // Store for final output, don't write yet\n this.staticOutput = stripAnsi(output)\n transformedOutput = \"\"\n } else {\n // Apply line-by-line or plain transformation\n transformedOutput = this.outputTransformer(output, this.prevLineCount)\n this.prevLineCount = countLines(output)\n }\n\n // Build cursor control suffix (position + show/hide).\n // This goes after rendered content so the terminal cursor lands\n // at the right spot after painting.\n let cursorSuffix = \"\"\n if (this.nonTTYMode === \"tty\") {\n const cursor = this.getCursorState()\n if (cursor?.visible) {\n const shapeSeq = cursor.shape ? setCursorStyle(cursor.shape) : resetCursorStyle()\n cursorSuffix = ANSI.moveCursor(cursor.x, cursor.y) + shapeSeq + ANSI.CURSOR_SHOW\n } else {\n cursorSuffix = ANSI.CURSOR_HIDE\n }\n }\n\n // Write output wrapped with synchronized update (DEC 2026) for TTY mode.\n // This tells the terminal to batch the output and paint atomically,\n // preventing tearing during rapid screen updates.\n if (transformedOutput.length > 0 || cursorSuffix.length > 0) {\n const fullOutput =\n this.nonTTYMode === \"tty\" && SYNC_UPDATE_ENABLED\n ? `${ANSI.SYNC_BEGIN}${transformedOutput}${cursorSuffix}${ANSI.SYNC_END}`\n : transformedOutput + cursorSuffix\n\n // Debug: log output sizes to detect potential pipe buffer splits\n if (log.debug) {\n const bytes = Buffer.byteLength(fullOutput)\n log.debug?.(\n `stdout.write: ${bytes} bytes (${transformedOutput.length} chars output + ${cursorSuffix.length} chars cursor)`,\n )\n if (bytes > 16384) {\n log.debug?.(\n `large output: ${bytes} bytes may exceed pipe buffer (16KB on macOS), risk of mid-sequence split`,\n )\n }\n }\n\n // Capture raw ANSI output to file for debugging garbled rendering\n const captureFile = process.env.SILVERY_CAPTURE_OUTPUT\n if (captureFile) {\n const fs = require(\"fs\")\n fs.appendFileSync(\n captureFile,\n `--- FRAME ${this.stats.renderCount + 1} (${Buffer.byteLength(fullOutput)} bytes) ---\\n`,\n )\n fs.appendFileSync(captureFile, fullOutput)\n fs.appendFileSync(captureFile, \"\\n\")\n }\n\n this.writeOutput(fullOutput)\n }\n\n // Save buffer for next diff\n this.prevBuffer = buffer\n\n // SILVERY_STRICT: compare incremental render against fresh render\n const strictEnv = process.env.SILVERY_STRICT\n const strictMode = strictEnv && strictEnv !== \"0\" && strictEnv !== \"false\"\n if (strictMode && this.stats.renderCount > 0) {\n const renderNum = this.stats.renderCount + 1\n const { buffer: freshBuffer } = executeRender(\n this.root,\n width,\n height,\n null,\n {\n mode: this.mode === \"fullscreen\" ? \"fullscreen\" : \"inline\",\n skipLayoutNotifications: true,\n },\n this.pipelineConfig,\n )\n let found = false\n for (let y = 0; y < buffer.height && !found; y++) {\n for (let x = 0; x < buffer.width && !found; x++) {\n const a = buffer.getCell(x, y)\n const b = freshBuffer.getCell(x, y)\n if (!cellEquals(a, b)) {\n found = true\n\n // Build rich debug context\n const ctx = buildMismatchContext(this.root, x, y, a, b, renderNum)\n\n // Capture render-phase instrumentation snapshot\n const renderPhaseStats: RenderPhaseStats | undefined = (globalThis as any).__silvery_content_detail\n ? structuredClone((globalThis as any).__silvery_content_detail)\n : undefined\n\n const debugInfo = formatMismatchContext(ctx, renderPhaseStats)\n\n // Include text output for full picture\n const incText = bufferToText(buffer)\n const freshText = bufferToText(freshBuffer)\n const msg = debugInfo + `--- incremental ---\\n${incText}\\n--- fresh ---\\n${freshText}`\n\n if (process.env.DEBUG_LOG) {\n appendFileSync(process.env.DEBUG_LOG, msg + \"\\n\")\n }\n log.error?.(msg)\n // Throw special error that won't be caught by general error handler\n throw new IncrementalRenderMismatchError(msg, {\n renderPhaseStats,\n mismatchContext: ctx,\n })\n }\n }\n }\n if (!found && process.env.DEBUG_LOG) {\n appendFileSync(process.env.DEBUG_LOG, `SILVERY_STRICT: render #${renderNum} OK\\n`)\n }\n }\n\n // Update stats\n const renderTime = Date.now() - startTime\n this.stats.renderCount++\n this.stats.lastRenderTime = renderTime\n this.stats.avgRenderTime =\n (this.stats.avgRenderTime * (this.stats.renderCount - 1) + renderTime) / this.stats.renderCount\n this.lastRenderTime = Date.now()\n\n // Record span data\n render.spanData.renderCount = this.stats.renderCount\n render.spanData.renderTime = renderTime\n render.spanData.bytes = transformedOutput.length\n\n log.debug?.(\n `render #${this.stats.renderCount} complete: ${renderTime}ms, output: ${transformedOutput.length} bytes`,\n )\n\n // First render is always slow (initialization); use 5x threshold for it\n const threshold = this.stats.renderCount <= 1 ? this.slowFrameThreshold * 5 : this.slowFrameThreshold\n if (threshold > 0 && renderTime > threshold) {\n log.debug?.(\n `slow frame: render #${this.stats.renderCount} took ${renderTime}ms (threshold: ${this.slowFrameThreshold}ms, bytes: ${transformedOutput.length})`,\n )\n }\n\n if (this.debugMode) {\n this.logDebug(`Render #${this.stats.renderCount} took ${renderTime}ms`)\n }\n } catch (error) {\n // Log and re-throw all render errors - the app should handle cleanup\n log.error?.(`render error: ${error}`)\n this.logError(\"Render error:\", error)\n throw error\n }\n }\n\n /**\n * Set up terminal resize listener.\n */\n private setupResizeListener(): void {\n let resizeTimeout: ReturnType<typeof setTimeout> | null = null\n\n const handleResize = () => {\n // Debounce resize events\n if (resizeTimeout) {\n clearTimeout(resizeTimeout)\n }\n\n resizeTimeout = setTimeout(() => {\n resizeTimeout = null\n\n // Reset buffer to force full redraw\n this.prevBuffer = null\n\n // Schedule render\n this.scheduleRender()\n }, 50) // 50ms debounce\n }\n\n this.stdout.on(\"resize\", handleResize)\n\n this.resizeCleanup = () => {\n this.stdout.off(\"resize\", handleResize)\n if (resizeTimeout) {\n clearTimeout(resizeTimeout)\n }\n }\n }\n\n /**\n * Log debug message (via loggily, not stderr — stderr corrupts alt screen).\n */\n private logDebug(message: string): void {\n log.debug?.(message)\n }\n\n /**\n * Log error message (via loggily, not stderr — stderr corrupts alt screen).\n */\n private logError(message: string, error: unknown): void {\n if (error instanceof Error) {\n log.error?.(`${message} ${error.stack ?? error.message}`)\n } else {\n log.error?.(`${message} ${String(error)}`)\n }\n }\n}\n\n// ============================================================================\n// Factory Function\n// ============================================================================\n\n/**\n * Create a render scheduler.\n *\n * @param options Scheduler options\n * @returns A new RenderScheduler instance\n */\nexport function createScheduler(options: SchedulerOptions): RenderScheduler {\n return new RenderScheduler(options)\n}\n\n// ============================================================================\n// Utility: Simple Render (for testing/debugging)\n// ============================================================================\n\n/**\n * Render once to a string (for testing).\n *\n * Does not batch or diff - just runs the pipeline and returns ANSI output.\n */\nexport function renderToString(root: AgNode, width: number, height: number): string {\n const { output } = executeRender(root, width, height, null)\n return output\n}\n","/**\n * TermDef Resolution\n *\n * Converts TermDef (minimal render config) into resolved values for rendering.\n * Handles auto-detection of events from stdin, dimension defaults, etc.\n */\n\nimport type { ColorLevel, Term } from \"./ansi/index\"\nimport type { Event, EventSource } from \"@silvery/ag/types\"\n\n// ============================================================================\n// Terminal-Specific Types (moved from @silvery/ag/types)\n// ============================================================================\n\n/**\n * Minimal surface for configuring render().\n *\n * TermDef provides a simple way to configure rendering without requiring\n * a full Term instance. It's useful for:\n * - Static rendering (just width/height, no events)\n * - Testing (mock dimensions and events)\n * - Quick scripts (auto-detect everything from stdin/stdout)\n *\n * The presence of `events` (or `stdin` which auto-creates events)\n * determines the render mode:\n * - No events → static mode (render until stable)\n * - Has events → interactive mode (render until exit() called)\n *\n * @example\n * ```tsx\n * // Static render with custom width\n * const output = await render(<App />, { width: 100 })\n *\n * // Interactive with stdin/stdout\n * await render(<App />, { stdin: process.stdin, stdout: process.stdout })\n *\n * // Custom events\n * await render(<App />, { events: myEventSource })\n * ```\n */\nexport interface TermDef {\n // -------------------------------------------------------------------------\n // Output Configuration\n // -------------------------------------------------------------------------\n\n /** Output stream (used for dimensions if not specified) */\n stdout?: NodeJS.WriteStream\n\n /** Width in columns (default: stdout?.columns ?? 80) */\n width?: number\n\n /** Height in rows (default: stdout?.rows ?? 24) */\n height?: number\n\n /** Color support (true=detect, false=none, or specific level) */\n colors?: boolean | ColorLevel | null\n\n // -------------------------------------------------------------------------\n // Input Configuration\n // -------------------------------------------------------------------------\n\n /**\n * Event source for interactive mode.\n *\n * When present, render runs until exit() is called.\n * When absent, render completes when UI is stable.\n */\n events?: AsyncIterable<Event> | EventSource\n\n /**\n * Standard input stream.\n *\n * When provided (and events is not), automatically creates input events\n * from stdin, enabling interactive mode.\n */\n stdin?: NodeJS.ReadStream\n}\n\n/**\n * Options passed to the render function.\n */\nexport interface RenderOptions {\n stdout?: NodeJS.WriteStream\n stdin?: NodeJS.ReadStream\n exitOnCtrlC?: boolean\n debug?: boolean\n}\n\n/**\n * The render instance returned by render().\n */\nexport interface RenderInstance {\n /** Re-render with new element */\n rerender: (element: unknown) => void\n /** Unmount and clean up */\n unmount: () => void\n /** Wait for render to complete */\n waitUntilExit: () => Promise<void>\n /** Clear terminal output */\n clear: () => void\n}\n\n// ============================================================================\n// Resolved TermDef\n// ============================================================================\n\n/**\n * Resolved values from a TermDef, ready for use by the render system.\n */\nexport interface ResolvedTermDef {\n /** Output stream (may be mock for static rendering) */\n stdout: NodeJS.WriteStream | null\n\n /** Width in columns */\n width: number\n\n /** Height in rows */\n height: number\n\n /** Color level (null = no colors) */\n colors: ColorLevel | null\n\n /** Event source (null = static mode) */\n events: AsyncIterable<Event> | null\n\n /** Whether this is static mode (no events = render until stable) */\n isStatic: boolean\n}\n\n// ============================================================================\n// Resolution Logic\n// ============================================================================\n\n/**\n * Default dimensions when not detectable.\n */\nconst DEFAULT_WIDTH = 80\nconst DEFAULT_HEIGHT = 24\n\n/**\n * Check if a value is a Term instance (duck typing).\n */\nexport function isTerm(value: unknown): value is Term {\n // Term can be a callable Proxy (typeof === 'function') or object\n if (!value || (typeof value !== \"object\" && typeof value !== \"function\")) {\n return false\n }\n const obj = value as Record<string, unknown>\n return (\n typeof obj.hasCursor === \"function\" &&\n typeof obj.hasInput === \"function\" &&\n typeof obj.hasColor === \"function\" &&\n typeof obj.write === \"function\"\n )\n}\n\n/**\n * Check if a value is a TermDef (not a Term).\n */\nexport function isTermDef(value: unknown): value is TermDef {\n if (!value || typeof value !== \"object\") return false\n // TermDef doesn't have hasCursor method\n const obj = value as Record<string, unknown>\n return typeof obj.hasCursor !== \"function\"\n}\n\n/**\n * Resolve a TermDef into concrete values.\n *\n * @param def - TermDef to resolve\n * @returns Resolved values ready for rendering\n */\nexport function resolveTermDef(def: TermDef): ResolvedTermDef {\n // Resolve dimensions\n const width = def.width ?? def.stdout?.columns ?? DEFAULT_WIDTH\n const height = def.height ?? def.stdout?.rows ?? DEFAULT_HEIGHT\n\n // Resolve colors\n let colors: ColorLevel | null = null\n if (def.colors === true) {\n // Auto-detect from stdout\n colors = detectColorLevel(def.stdout)\n } else if (def.colors === false || def.colors === null) {\n colors = null\n } else if (def.colors) {\n colors = def.colors\n } else {\n // Default: auto-detect\n colors = detectColorLevel(def.stdout)\n }\n\n // Resolve events\n let events: AsyncIterable<Event> | null = null\n if (def.events) {\n // Explicit events provided\n events = def.events\n } else if (def.stdin) {\n // Auto-create events from stdin\n events = createInputEvents(def.stdin)\n }\n\n return {\n stdout: def.stdout ?? null,\n width,\n height,\n colors,\n events,\n isStatic: events === null,\n }\n}\n\n/**\n * Resolve a Term instance into ResolvedTermDef.\n *\n * @param term - Term instance\n * @returns Resolved values\n */\nexport function resolveFromTerm(term: Term): ResolvedTermDef {\n return {\n stdout: term.stdout,\n width: term.cols ?? DEFAULT_WIDTH,\n height: term.rows ?? DEFAULT_HEIGHT,\n colors: term.hasColor(),\n // Term instances always have interactive capabilities\n events: createInputEvents(term.stdin),\n isStatic: false,\n }\n}\n\n// ============================================================================\n// Color Detection\n// ============================================================================\n\n/**\n * Detect color level from stdout stream.\n */\nfunction detectColorLevel(stdout?: NodeJS.WriteStream): ColorLevel | null {\n // Check environment variables\n if (process.env.NO_COLOR !== undefined) {\n return null\n }\n\n if (process.env.FORCE_COLOR !== undefined) {\n const level = Number.parseInt(process.env.FORCE_COLOR, 10)\n if (level === 0) return null\n if (level === 1) return \"basic\"\n if (level === 2) return \"256\"\n if (level >= 3) return \"truecolor\"\n return \"basic\"\n }\n\n // Check COLORTERM for truecolor\n if (process.env.COLORTERM === \"truecolor\" || process.env.COLORTERM === \"24bit\") {\n return \"truecolor\"\n }\n\n // Check if TTY\n if (!stdout?.isTTY) {\n return null\n }\n\n // Check TERM for 256 color support\n const term = process.env.TERM ?? \"\"\n if (term.includes(\"256color\") || term.includes(\"256\")) {\n return \"256\"\n }\n\n // Default to basic if TTY\n return \"basic\"\n}\n\n// ============================================================================\n// Input Events\n// ============================================================================\n\n/**\n * Create an async iterable of input events from a stdin stream.\n *\n * This enables interactive mode by providing a source of keyboard events.\n */\nexport function createInputEvents(stdin: NodeJS.ReadStream): AsyncIterable<Event> {\n return {\n [Symbol.asyncIterator](): AsyncIterator<Event> {\n const buffer: Event[] = []\n let resolveNext: ((value: IteratorResult<Event>) => void) | null = null\n let done = false\n\n // Set up stdin reading\n const handleData = (chunk: Buffer | string) => {\n const data = typeof chunk === \"string\" ? chunk : chunk.toString(\"utf8\")\n\n // Convert raw input to key events\n // This is simplified - real implementation would parse ANSI sequences\n for (const char of data) {\n const event: Event = {\n type: \"key\",\n key: char,\n ctrl: char.charCodeAt(0) < 32 && char !== \"\\r\" && char !== \"\\n\" && char !== \"\\t\",\n }\n\n if (resolveNext) {\n resolveNext({ value: event, done: false })\n resolveNext = null\n } else {\n buffer.push(event)\n }\n }\n }\n\n const handleEnd = () => {\n done = true\n if (resolveNext) {\n resolveNext({ value: undefined as unknown as Event, done: true })\n resolveNext = null\n }\n }\n\n // Only set up if stdin supports raw mode\n if (stdin.isTTY && typeof stdin.setRawMode === \"function\") {\n stdin.setEncoding(\"utf8\")\n stdin.on(\"data\", handleData)\n stdin.on(\"end\", handleEnd)\n }\n\n return {\n next(): Promise<IteratorResult<Event>> {\n // Return buffered event if available\n const buffered = buffer.shift()\n if (buffered) {\n return Promise.resolve({ value: buffered, done: false })\n }\n\n // If done, return done\n if (done) {\n return Promise.resolve({\n value: undefined as unknown as Event,\n done: true,\n })\n }\n\n // Wait for next event\n return new Promise((resolve) => {\n resolveNext = resolve\n })\n },\n\n return(): Promise<IteratorResult<Event>> {\n done = true\n stdin.off(\"data\", handleData)\n stdin.off(\"end\", handleEnd)\n return Promise.resolve({\n value: undefined as unknown as Event,\n done: true,\n })\n },\n }\n },\n }\n}\n","/**\n * OSC 4 Terminal Color Palette — re-exported from @silvery/ansi (canonical location).\n */\n\nexport { queryPaletteColor, queryMultiplePaletteColors, setPaletteColor, parsePaletteResponse } from \"@silvery/ansi\"\n","/**\n * OSC 133 Semantic Prompt Markers\n *\n * Shell integration protocol that marks prompts and commands in terminal output.\n * Terminals like iTerm2, Kitty, and WezTerm use these markers to provide\n * \"jump to previous/next prompt\" navigation (Cmd+Up/Cmd+Down in iTerm2).\n *\n * Protocol: OSC 133\n * - Prompt start: ESC ] 133 ; A BEL (before user input)\n * - Prompt end: ESC ] 133 ; B BEL (after user input, before command output)\n * - Command output start: ESC ] 133 ; C BEL (before command output)\n * - Command output end: ESC ] 133 ; D ; N BEL (after command output, N = exit code)\n *\n * For a chat-style app, each \"exchange\" (user prompt + assistant response) maps to:\n * - 133;A before the user's message\n * - 133;B after the user's message\n * - 133;C before the assistant's response\n * - 133;D;0 after the assistant's response\n *\n * Supported by: iTerm2, Kitty, WezTerm, foot, Ghostty\n */\n\nexport const OSC133 = {\n /** Mark prompt start (before user input) */\n promptStart: \"\\x1b]133;A\\x07\",\n /** Mark prompt end (after user input, before command output) */\n promptEnd: \"\\x1b]133;B\\x07\",\n /** Mark command output start */\n commandStart: \"\\x1b]133;C\\x07\",\n /** Mark command output end (exit code defaults to 0 = success) */\n commandEnd: (exitCode?: number) => `\\x1b]133;D;${exitCode ?? 0}\\x07`,\n} as const\n","/**\n * Kitty keyboard protocol detection.\n *\n * Sends CSI ? u and parses the response to determine whether the terminal\n * supports the Kitty keyboard protocol and which flags it reports.\n */\n\nimport { queryKittyKeyboard } from \"./output\"\n\nexport interface KittyDetectResult {\n /** Whether the terminal responded to the Kitty protocol query */\n supported: boolean\n /** Bitfield of KittyFlags the terminal reported supporting (0 if unsupported) */\n flags: number\n /** Any non-response data that was read during detection (regular input that arrived) */\n buffered?: string\n}\n\n/** Regex to match a Kitty keyboard query response: CSI ? <flags> u */\nconst KITTY_RESPONSE_RE = /\\x1b\\[\\?(\\d+)u/\n\n/**\n * Detect Kitty keyboard protocol support.\n *\n * Sends CSI ? u to the terminal and waits for a response.\n * Supported terminals respond with CSI ? flags u.\n * Unsupported terminals either ignore the query or echo it.\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin (should resolve with data or null on timeout)\n * @param timeoutMs How long to wait for response (default: 200ms)\n */\nexport async function detectKittySupport(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<KittyDetectResult> {\n write(queryKittyKeyboard())\n\n const data = await read(timeoutMs)\n if (data == null) {\n return { supported: false, flags: 0 }\n }\n\n const match = KITTY_RESPONSE_RE.exec(data)\n if (!match) {\n return { supported: false, flags: 0, buffered: data }\n }\n\n const flags = parseInt(match[1]!, 10)\n // Anything outside the matched response is buffered input\n const before = data.slice(0, match.index)\n const after = data.slice(match.index + match[0].length)\n const buffered = before + after\n return { supported: true, flags, buffered: buffered || undefined }\n}\n\n/**\n * Detect Kitty support using real stdin/stdout.\n * Convenience wrapper around detectKittySupport.\n */\nexport async function detectKittyFromStdio(\n stdout: { write: (s: string) => boolean | void },\n stdin: NodeJS.ReadStream,\n timeoutMs = 200,\n): Promise<KittyDetectResult> {\n const wasRaw = stdin.isRaw\n if (!wasRaw) stdin.setRawMode(true)\n\n try {\n const write = (s: string) => {\n stdout.write(s)\n }\n\n const read = (ms: number): Promise<string | null> =>\n new Promise((resolve) => {\n const timer = setTimeout(() => {\n stdin.removeListener(\"data\", onData)\n resolve(null)\n }, ms)\n\n function onData(chunk: Buffer) {\n clearTimeout(timer)\n stdin.removeListener(\"data\", onData)\n resolve(chunk.toString())\n }\n\n stdin.on(\"data\", onData)\n })\n\n return await detectKittySupport(write, read, timeoutMs)\n } finally {\n if (!wasRaw) stdin.setRawMode(false)\n }\n}\n","/**\n * Terminal Capability Test\n *\n * Renders labeled test patterns for each terminal feature.\n * Run in any terminal to visually verify what it supports.\n *\n * Usage:\n * import { runTermtest } from \"@silvery/ag-react\"\n * runTermtest() // all sections\n * runTermtest({ sections: [\"emoji\", \"colors\"] }) // specific sections\n *\n * Compare output across terminals to build/verify profiles.\n */\n\nimport { detectTerminalCaps } from \"./terminal-caps\"\n\nconst ESC = \"\\x1b\"\nconst CSI = `${ESC}[`\nconst RESET = `${CSI}0m`\n\nfunction sgr(...codes: (string | number)[]): string {\n return `${CSI}${codes.join(\";\")}m`\n}\n\nfunction sectionHeader(title: string): string {\n return `\\n${sgr(1)}═══ ${title} ═══${RESET}\\n`\n}\n\nfunction row(label: string, content: string): string {\n return ` ${label.padEnd(24)} ${content}${RESET}`\n}\n\n/** Available test sections */\nexport const TERMTEST_SECTIONS = [\n \"sgr\",\n \"underline\",\n \"colors\",\n \"256\",\n \"truecolor\",\n \"unicode\",\n \"emoji\",\n \"borders\",\n \"inverse\",\n \"profile\",\n] as const\n\nexport type TermtestSection = (typeof TERMTEST_SECTIONS)[number]\n\nexport interface TermtestOptions {\n /** Writable stream (defaults to process.stdout) */\n output?: { write(s: string): boolean }\n /** Show only these sections. Omit or empty = all sections. */\n sections?: TermtestSection[]\n}\n\n/**\n * Run the terminal capability test.\n * Pass section names to filter: `runTermtest({ sections: [\"emoji\"] })`\n */\nexport function runTermtest(options?: TermtestOptions): void {\n const w = options?.output ?? process.stdout\n const filter = options?.sections\n const show = (s: TermtestSection) => !filter || filter.length === 0 || filter.includes(s)\n\n const caps = detectTerminalCaps()\n\n w.write(`\\n${sgr(1)}Terminal Capability Test${RESET}\\n`)\n w.write(` Program: ${caps.program || \"(unknown)\"}\\n`)\n w.write(` TERM: ${caps.term || \"(unknown)\"}\\n`)\n w.write(` COLORTERM: ${process.env.COLORTERM || \"(unset)\"}\\n`)\n w.write(` Detected: color=${caps.colorLevel} dark=${caps.darkBackground} nerdfont=${caps.nerdfont}\\n`)\n w.write(` Underline: styles=${caps.underlineStyles} color=${caps.underlineColor}\\n`)\n w.write(` Emoji wide: ${caps.textEmojiWide}\\n`)\n\n if (show(\"sgr\")) {\n w.write(sectionHeader(\"SGR Text Attributes\"))\n w.write(row(\"Bold\", `${sgr(1)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Dim\", `${sgr(2)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Italic\", `${sgr(3)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Underline\", `${sgr(4)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Strikethrough\", `${sgr(9)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Inverse\", `${sgr(7)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Blink\", `${sgr(5)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Bold+Italic\", `${sgr(1, 3)}The quick brown fox${RESET}`) + \"\\n\")\n }\n\n if (show(\"underline\")) {\n w.write(sectionHeader(\"SGR 4:x Underline Styles (Terminal.app BREAKS here)\"))\n w.write(row(\"4:1 Single\", `${CSI}4:1mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"4:2 Double\", `${CSI}4:2mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"4:3 Curly\", `${CSI}4:3mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"4:4 Dotted\", `${CSI}4:4mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"4:5 Dashed\", `${CSI}4:5mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(\n row(\"After 4:x (clean?)\", `${CSI}4:3m${RESET}This text should be normal — if garbled, 4:x corrupted SGR state`) +\n \"\\n\",\n )\n\n w.write(sectionHeader(\"SGR 58 Underline Color (Terminal.app BREAKS here)\"))\n w.write(row(\"58;5;1 Red UL\", `${sgr(4)}${CSI}58;5;1mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"58;5;2 Green UL\", `${sgr(4)}${CSI}58;5;2mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"58;5;4 Blue UL\", `${sgr(4)}${CSI}58;5;4mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"58;2;R;G;B TC UL\", `${sgr(4)}${CSI}58;2;255;128;0mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(\n row(\n \"After SGR 58 (clean?)\",\n `${sgr(4)}${CSI}58;5;1m${RESET}This text should be normal — if garbled, 58 corrupted SGR state`,\n ) + \"\\n\",\n )\n }\n\n if (show(\"colors\")) {\n w.write(sectionHeader(\"ANSI 16 Colors\"))\n const colorNames = [\"Black\", \"Red\", \"Green\", \"Yellow\", \"Blue\", \"Magenta\", \"Cyan\", \"White\"]\n let fgLine = \" FG: \"\n for (let i = 0; i < 8; i++) fgLine += `${sgr(30 + i)} ${colorNames[i]}${RESET}`\n w.write(fgLine + \"\\n\")\n let fgBrLine = \" Br: \"\n for (let i = 0; i < 8; i++) fgBrLine += `${sgr(90 + i)} ${colorNames[i]}${RESET}`\n w.write(fgBrLine + \"\\n\")\n let bgLine = \" BG: \"\n for (let i = 0; i < 8; i++) bgLine += `${sgr(40 + i)} ${colorNames[i]} ${RESET}`\n w.write(bgLine + \"\\n\")\n let bgBrLine = \" BrBG:\"\n for (let i = 0; i < 8; i++) bgBrLine += `${sgr(100 + i)} ${colorNames[i]} ${RESET}`\n w.write(bgBrLine + \"\\n\")\n }\n\n if (show(\"256\")) {\n w.write(sectionHeader(\"256 Colors (indices 0-15, 16-231, 232-255)\"))\n let stdLine = \" 0-15: \"\n for (let i = 0; i < 16; i++) stdLine += `${CSI}48;5;${i}m ${RESET}`\n w.write(stdLine + \"\\n\")\n let cubeLine = \" Cube: \"\n for (let i = 16; i < 52; i++) cubeLine += `${CSI}48;5;${i}m ${RESET}`\n w.write(cubeLine + \"\\n\")\n let grayLine = \" Gray: \"\n for (let i = 232; i < 256; i++) grayLine += `${CSI}48;5;${i}m ${RESET}`\n w.write(grayLine + \"\\n\")\n }\n\n if (show(\"truecolor\")) {\n w.write(sectionHeader(\"Truecolor (38;2;R;G;B / 48;2;R;G;B)\"))\n let tcLine = \" Gradient: \"\n for (let i = 0; i < 40; i++) {\n const r = Math.round((i / 39) * 255)\n const g = Math.round(((39 - i) / 39) * 128)\n tcLine += `${CSI}48;2;${r};${g};80m ${RESET}`\n }\n w.write(tcLine + \"\\n\")\n w.write(row(\"If solid blocks →\", \"Truecolor NOT supported (256-color fallback)\") + \"\\n\")\n }\n\n if (show(\"unicode\")) {\n w.write(sectionHeader(\"Unicode, Emoji, PUA (Nerd Fonts)\"))\n w.write(row(\"ASCII\", \"Hello World! 0123456789\") + \"\\n\")\n w.write(row(\"Latin Extended\", \"àéîõü ñ ß ø å\") + \"\\n\")\n w.write(row(\"CJK\", \"你好世界 日本語 한국어\") + \"\\n\")\n w.write(row(\"Box Drawing\", \"┌─┬─┐ ╔═╦═╗ ╭─╮\") + \"\\n\")\n w.write(row(\"Block Elements\", \"▀▄█▌▐░▒▓\") + \"\\n\")\n w.write(row(\"Symbols\", \"● ○ ◉ ▶ ◀ ⚠ ✓ ✗ ⋮ §\") + \"\\n\")\n w.write(row(\"Emoji\", \"🎉 🚀 📁 📄 ⭐ 🔥 👍\") + \"\\n\")\n w.write(row(\"Nerd Font PUA\", \"\\uF114 folder \\uF0F6 file \\uE0B0 arrow \\uF013 gear\") + \"\\n\")\n w.write(row(\"If PUA = boxes →\", \"Nerd Fonts not installed\") + \"\\n\")\n }\n\n if (show(\"emoji\")) {\n // Each test line places a character then fills to exactly 10 visible columns.\n // If the _'s don't align with the ruler, the terminal's character width\n // disagrees with the expected width (shown in parentheses).\n w.write(sectionHeader(\"Emoji Width Alignment (_'s should align with ruler)\"))\n w.write(\" Ruler: |1234567890|\\n\")\n w.write(\" ASCII 'A': |A_________| (w=1)\\n\")\n w.write(\" CJK '你': |你________| (w=2)\\n\")\n w.write(\" Flag 🇨🇦: |🇨🇦________| (w=2)\\n\")\n w.write(\" Flag 🇺🇸: |🇺🇸________| (w=2)\\n\")\n w.write(\" Emoji 📁: |📁________| (w=2)\\n\")\n w.write(\" Emoji 📄: |📄________| (w=2)\\n\")\n w.write(\" Emoji 📋: |📋________| (w=2)\\n\")\n w.write(\" Emoji ⚠️: |⚠️________| (w=2)\\n\")\n w.write(\" Text ⚠: |⚠_________| (w=1 text, 2 emoji)\\n\")\n w.write(\" Text ⭐: |⭐________| (w=1 text, 2 emoji)\\n\")\n w.write(\" Text ☑: |☑_________| (w=1 text, 2 emoji)\\n\")\n w.write(\" Emoji 🏠: |🏠________| (w=2)\\n\")\n w.write(\" Emoji 👓: |👓________| (w=2)\\n\")\n w.write(\" ZWJ 👨🏻💻: |👨🏻💻________| (w=2)\\n\")\n w.write(\" Arrow →: |→_________| (w=1)\\n\")\n w.write(\" Arrow ▸: |▸_________| (w=1)\\n\")\n w.write(\" Circle ○: |○_________| (w=1)\\n\")\n w.write(\" Square □: |□_________| (w=1)\\n\")\n w.write(\" Check ✓: |✓_________| (w=1)\\n\")\n }\n\n if (show(\"borders\")) {\n w.write(sectionHeader(\"Box Drawing Borders\"))\n w.write(\" ┌──────────┐ ╔══════════╗ ╭──────────╮\\n\")\n w.write(\" │ single │ ║ double ║ │ round │\\n\")\n w.write(\" └──────────┘ ╚══════════╝ ╰──────────╯\\n\")\n }\n\n if (show(\"inverse\")) {\n w.write(sectionHeader(\"Inverse + Background (potential artifact source)\"))\n w.write(row(\"Red FG + Inverse\", `${sgr(31, 7)}This should have red background${RESET}`) + \"\\n\")\n w.write(row(\"Cyan BG + White FG\", `${sgr(46, 37)}Cyan background, white text${RESET}`) + \"\\n\")\n w.write(row(\"Black BG + White FG\", `${sgr(40, 97)}Black bg, bright white text${RESET}`) + \"\\n\")\n w.write(row(\"White BG + Black FG\", `${sgr(107, 30)}White bg, black text${RESET}`) + \"\\n\")\n\n w.write(sectionHeader(\"Reset Sanity Check\"))\n w.write(\" This line should be completely normal with no formatting artifacts.\\n\")\n w.write(\" If you see colors, underlines, or other styling above, the terminal\\n\")\n w.write(\" failed to process an SGR reset (\\\\x1b[0m) correctly.\\n\")\n }\n\n if (show(\"profile\")) {\n w.write(sectionHeader(\"Detected Terminal Profile\"))\n const entries = Object.entries(caps) as [string, unknown][]\n for (const [key, value] of entries) {\n const indicator = value === true ? \"✓\" : value === false ? \"✗\" : String(value)\n w.write(` ${key.padEnd(20)} ${indicator}\\n`)\n }\n }\n\n w.write(\"\\n\")\n}\n","/**\n * Measurer composition layer.\n *\n * Creates term-scoped measurers and pipeline configs.\n * Bridges ansi Term with silvery measurement capabilities.\n */\n\nimport type { Term, TerminalCaps } from \"./ansi/index\"\nimport { createWidthMeasurer, type Measurer } from \"./unicode\"\nimport { createOutputPhase } from \"./pipeline/output-phase\"\nimport type { PipelineConfig } from \"./pipeline\"\n\nexport type { Measurer } from \"./unicode\"\n\n/**\n * Term extended with measurement capabilities.\n */\nexport interface MeasuredTerm extends Term, Measurer {}\n\n/**\n * Extend a Term with measurement capabilities.\n *\n * Creates a width measurer from the term's caps and adds measurement\n * methods (displayWidth, graphemeWidth, wrapText, etc.) to the term.\n */\nexport function withMeasurer(term: Term): MeasuredTerm {\n const caps = term.caps\n const measurer = createWidthMeasurer(\n caps ? { textEmojiWide: caps.textEmojiWide, textSizingEnabled: caps.textSizingSupported } : {},\n )\n\n return Object.create(term, {\n textEmojiWide: { get: () => measurer.textEmojiWide, enumerable: true },\n textSizingEnabled: { get: () => measurer.textSizingEnabled, enumerable: true },\n displayWidth: { value: measurer.displayWidth.bind(measurer), enumerable: true },\n displayWidthAnsi: { value: measurer.displayWidthAnsi.bind(measurer), enumerable: true },\n graphemeWidth: { value: measurer.graphemeWidth.bind(measurer), enumerable: true },\n wrapText: { value: measurer.wrapText.bind(measurer), enumerable: true },\n sliceByWidth: { value: measurer.sliceByWidth.bind(measurer), enumerable: true },\n sliceByWidthFromEnd: { value: measurer.sliceByWidthFromEnd.bind(measurer), enumerable: true },\n }) as MeasuredTerm\n}\n\n/**\n * Create a pipeline configuration from caps and/or measurer.\n *\n * This is the single factory for PipelineConfig -- use it instead of\n * manually constructing { measurer, outputPhaseFn }.\n *\n * @param options.caps - Terminal capabilities (for output phase SGR generation)\n * @param options.measurer - Explicit measurer (if omitted, created from caps)\n */\nexport function createPipeline(\n options: {\n caps?: TerminalCaps\n measurer?: Measurer\n } = {},\n): PipelineConfig {\n const { caps, measurer: explicitMeasurer } = options\n const measurer =\n explicitMeasurer ??\n createWidthMeasurer(caps ? { textEmojiWide: caps.textEmojiWide, textSizingEnabled: caps.textSizingSupported } : {})\n const outputPhaseFn = createOutputPhase(\n caps\n ? {\n underlineStyles: caps.underlineStyles,\n underlineColor: caps.underlineColor,\n colorLevel: caps.colorLevel,\n }\n : {},\n measurer,\n )\n return { measurer, outputPhaseFn }\n}\n","/**\n * CSI 6n Cursor Position Query\n *\n * Queries the terminal for the current cursor position using the standard\n * Device Status Report (DSR) mechanism.\n *\n * Protocol:\n * - Query: CSI 6 n (\\x1b[6n)\n * - Response: CSI {row} ; {col} R (\\x1b[{row};{col}R)\n *\n * Row and column are 1-indexed in the protocol response.\n *\n * Supported by: virtually all terminals (VT100+)\n */\n\n/** Regex to match a CPR response: CSI row ; col R */\nconst CPR_RESPONSE_RE = /\\x1b\\[(\\d+);(\\d+)R/\n\n/**\n * Query the terminal cursor position.\n *\n * Sends CSI 6n and parses the CPR response.\n * Returns 1-indexed row and column.\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin (resolves with data or null on timeout)\n * @param timeoutMs How long to wait for response (default: 200ms)\n */\nexport async function queryCursorPosition(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<{ row: number; col: number } | null> {\n write(\"\\x1b[6n\")\n\n const data = await read(timeoutMs)\n if (data == null) return null\n\n const match = CPR_RESPONSE_RE.exec(data)\n if (!match) return null\n\n return {\n row: parseInt(match[1]!, 10),\n col: parseInt(match[2]!, 10),\n }\n}\n\n/**\n * Query cursor position using real stdin/stdout.\n * Convenience wrapper around queryCursorPosition.\n */\nexport async function queryCursorFromStdio(\n stdout: { write: (s: string) => boolean | void },\n stdin: NodeJS.ReadStream,\n timeoutMs = 200,\n): Promise<{ row: number; col: number } | null> {\n const wasRaw = stdin.isRaw\n if (!wasRaw) stdin.setRawMode(true)\n\n try {\n const write = (s: string) => {\n stdout.write(s)\n }\n\n const read = (ms: number): Promise<string | null> =>\n new Promise((resolve) => {\n const timer = setTimeout(() => {\n stdin.removeListener(\"data\", onData)\n resolve(null)\n }, ms)\n\n function onData(chunk: Buffer) {\n clearTimeout(timer)\n stdin.removeListener(\"data\", onData)\n resolve(chunk.toString())\n }\n\n stdin.on(\"data\", onData)\n })\n\n return await queryCursorPosition(write, read, timeoutMs)\n } finally {\n if (!wasRaw) stdin.setRawMode(false)\n }\n}\n","/**\n * OSC 10/11/12 Terminal Color Queries — re-exported from @silvery/ansi (canonical location).\n */\n\nexport {\n queryForegroundColor,\n queryBackgroundColor,\n queryCursorColor,\n setForegroundColor,\n setBackgroundColor,\n setCursorColor,\n resetForegroundColor,\n resetBackgroundColor,\n resetCursorColor,\n detectColorScheme,\n} from \"@silvery/ansi\"\n","/**\n * Device Attributes (DA1/DA2/DA3) + XTVERSION Queries\n *\n * Provides functions to query terminal identity and capabilities using\n * the standard VT device attribute escape sequences.\n *\n * Protocols:\n *\n * DA1 (Primary Device Attributes):\n * Query: CSI c (\\x1b[c)\n * Response: CSI ? Ps ; Ps ; ... c\n *\n * DA2 (Secondary Device Attributes):\n * Query: CSI > c (\\x1b[>c)\n * Response: CSI > Pt ; Pv ; Pc c\n * Where Pt=terminal type, Pv=firmware version, Pc=ROM cartridge id\n *\n * DA3 (Tertiary Device Attributes):\n * Query: CSI = c (\\x1b[=c)\n * Response: DCS ! | hex-encoded-id ST (\\x1bP!|{hex}\\x1b\\\\)\n *\n * XTVERSION (Terminal Name + Version):\n * Query: CSI > 0 q (\\x1b[>0q)\n * Response: DCS > | name(version) ST (\\x1bP>|{text}\\x1b\\\\)\n *\n * Supported by: xterm, Ghostty, Kitty, WezTerm, foot, VTE-based terminals\n */\n\n/** Regex for DA1 response: CSI ? params c */\nconst DA1_RESPONSE_RE = /\\x1b\\[\\?([\\d;]+)c/\n\n/** Regex for DA2 response: CSI > params c */\nconst DA2_RESPONSE_RE = /\\x1b\\[>([\\d;]+)c/\n\n/** Regex for DA3 response: DCS ! | hex ST */\nconst DA3_RESPONSE_RE = /\\x1bP!\\|([0-9a-fA-F]*)\\x1b\\\\/\n\n/** Regex for XTVERSION response: DCS > | text ST */\nconst XTVERSION_RESPONSE_RE = /\\x1bP>\\|([^\\x1b]*)\\x1b\\\\/\n\n// ============================================================================\n// DA1 — Primary Device Attributes\n// ============================================================================\n\n/**\n * Query primary device attributes (DA1).\n *\n * Returns the list of attribute parameters the terminal reports.\n * Common params: 1=132-cols, 4=sixel, 6=selective-erase, 22=ANSI-color\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param timeoutMs How long to wait for response (default: 200ms)\n */\nexport async function queryPrimaryDA(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<{ params: number[] } | null> {\n write(\"\\x1b[c\")\n\n const data = await read(timeoutMs)\n if (data == null) return null\n\n const match = DA1_RESPONSE_RE.exec(data)\n if (!match) return null\n\n const params = match[1]!.split(\";\").map((s) => parseInt(s, 10))\n return { params }\n}\n\n// ============================================================================\n// DA2 — Secondary Device Attributes\n// ============================================================================\n\n/**\n * Query secondary device attributes (DA2).\n *\n * Returns terminal type, firmware version, and ROM cartridge id.\n * Common type values: 0=VT100, 1=VT220, 41=xterm, 65=VT500\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param timeoutMs How long to wait for response (default: 200ms)\n */\nexport async function querySecondaryDA(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<{ type: number; version: number; id: number } | null> {\n write(\"\\x1b[>c\")\n\n const data = await read(timeoutMs)\n if (data == null) return null\n\n const match = DA2_RESPONSE_RE.exec(data)\n if (!match) return null\n\n const parts = match[1]!.split(\";\")\n if (parts.length < 3) return null\n\n return {\n type: parseInt(parts[0]!, 10),\n version: parseInt(parts[1]!, 10),\n id: parseInt(parts[2]!, 10),\n }\n}\n\n// ============================================================================\n// DA3 — Tertiary Device Attributes\n// ============================================================================\n\n/**\n * Query tertiary device attributes (DA3).\n *\n * Returns a hex-encoded unit ID string. Decode with Buffer.from(hex, 'hex').\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param timeoutMs How long to wait for response (default: 200ms)\n */\nexport async function queryTertiaryDA(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<string | null> {\n write(\"\\x1b[=c\")\n\n const data = await read(timeoutMs)\n if (data == null) return null\n\n const match = DA3_RESPONSE_RE.exec(data)\n if (!match) return null\n\n return match[1]!\n}\n\n// ============================================================================\n// XTVERSION — Terminal Name + Version\n// ============================================================================\n\n/**\n * Query the terminal name and version via XTVERSION.\n *\n * Returns the version string as reported by the terminal, e.g.:\n * - \"xterm(388)\"\n * - \"tmux 3.4\"\n * - \"WezTerm 20230712-072601-f4abf8fd\"\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param timeoutMs How long to wait for response (default: 200ms)\n */\nexport async function queryTerminalVersion(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<string | null> {\n write(\"\\x1b[>0q\")\n\n const data = await read(timeoutMs)\n if (data == null) return null\n\n const match = XTVERSION_RESPONSE_RE.exec(data)\n if (!match) return null\n\n return match[1]!\n}\n\n// ============================================================================\n// Combined Query\n// ============================================================================\n\n/** Combined device attributes result. */\nexport interface DeviceAttributes {\n da1: { params: number[] } | null\n da2: { type: number; version: number; id: number } | null\n version: string | null\n}\n\n/**\n * Query all device attributes: DA1, DA2, and XTVERSION.\n *\n * Convenience wrapper that queries all three sequentially.\n * DA3 is omitted from the combined query as it's rarely needed.\n *\n * @param stdout Writable stream (e.g., process.stdout)\n * @param stdin Readable stream (e.g., process.stdin)\n * @param timeoutMs Per-query timeout (default: 200ms)\n */\nexport async function queryDeviceAttributes(\n stdout: { write: (s: string) => boolean | void },\n stdin: NodeJS.ReadStream,\n timeoutMs = 200,\n): Promise<DeviceAttributes> {\n const wasRaw = stdin.isRaw\n if (!wasRaw) stdin.setRawMode(true)\n\n try {\n const write = (s: string) => {\n stdout.write(s)\n }\n\n const read = (ms: number): Promise<string | null> =>\n new Promise((resolve) => {\n const timer = setTimeout(() => {\n stdin.removeListener(\"data\", onData)\n resolve(null)\n }, ms)\n\n function onData(chunk: Buffer) {\n clearTimeout(timer)\n stdin.removeListener(\"data\", onData)\n resolve(chunk.toString())\n }\n\n stdin.on(\"data\", onData)\n })\n\n const da1 = await queryPrimaryDA(write, read, timeoutMs)\n const da2 = await querySecondaryDA(write, read, timeoutMs)\n const version = await queryTerminalVersion(write, read, timeoutMs)\n\n return { da1, da2, version }\n } finally {\n if (!wasRaw) stdin.setRawMode(false)\n }\n}\n","/**\n * DECRQM — DEC Private Mode Query\n *\n * Queries the terminal for the state of DEC private modes.\n *\n * Protocol:\n * - Query: CSI ? {mode} $ p\n * - Response: CSI ? {mode} ; {Ps} $ y\n *\n * Where Ps is:\n * 1 = set (mode is enabled)\n * 2 = reset (mode is disabled)\n * 0 = not recognized (unknown mode)\n * 3 = permanently set\n * 4 = permanently reset\n *\n * We normalize 3→\"set\" and 4→\"reset\" for simplicity.\n *\n * Supported by: xterm, Ghostty, Kitty, WezTerm, foot, VTE-based terminals\n */\n\n/** Regex for DECRPM response: CSI ? mode ; Ps $ y */\nconst DECRPM_RESPONSE_RE = /\\x1b\\[\\?(\\d+);(\\d+)\\$y/\n\n/** Well-known DEC private mode constants. */\nexport const DecMode = {\n /** DEC cursor visible (DECTCEM) */\n CURSOR_VISIBLE: 25,\n /** Alternate screen buffer (DECSET 1049) */\n ALT_SCREEN: 1049,\n /** Normal mouse tracking (X10) */\n MOUSE_TRACKING: 1000,\n /** Bracketed paste mode */\n BRACKETED_PASTE: 2004,\n /** Synchronized output */\n SYNC_OUTPUT: 2026,\n /** Focus reporting */\n FOCUS_REPORTING: 1004,\n} as const\n\ntype ModeState = \"set\" | \"reset\" | \"unknown\"\n\n/**\n * Query the state of a single DEC private mode.\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param mode DEC private mode number (e.g., DecMode.ALT_SCREEN)\n * @param timeoutMs How long to wait for response (default: 200ms)\n * @returns \"set\", \"reset\", or \"unknown\"\n */\nexport async function queryMode(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n mode: number,\n timeoutMs = 200,\n): Promise<ModeState> {\n write(`\\x1b[?${mode}$p`)\n\n const data = await read(timeoutMs)\n if (data == null) return \"unknown\"\n\n const match = DECRPM_RESPONSE_RE.exec(data)\n if (!match) return \"unknown\"\n\n const reportedMode = parseInt(match[1]!, 10)\n if (reportedMode !== mode) return \"unknown\"\n\n const ps = parseInt(match[2]!, 10)\n switch (ps) {\n case 1:\n case 3:\n return \"set\"\n case 2:\n case 4:\n return \"reset\"\n default:\n return \"unknown\"\n }\n}\n\n/**\n * Query the state of multiple DEC private modes.\n *\n * Queries each mode sequentially and returns a Map of results.\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param modes Array of DEC private mode numbers\n * @param timeoutMs Per-query timeout (default: 200ms)\n */\nexport async function queryModes(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n modes: number[],\n timeoutMs = 200,\n): Promise<Map<number, ModeState>> {\n const results = new Map<number, ModeState>()\n\n for (const mode of modes) {\n const state = await queryMode(write, read, mode, timeoutMs)\n results.set(mode, state)\n }\n\n return results\n}\n","/**\n * CSI 14t/18t — Terminal Pixel and Text Area Size Queries\n *\n * Queries the terminal for window dimensions in pixels and characters.\n *\n * Protocols:\n *\n * Text Area Pixels (CSI 14t):\n * Query: CSI 14 t\n * Response: CSI 4 ; height ; width t\n *\n * Text Area Size in Characters (CSI 18t):\n * Query: CSI 18 t\n * Response: CSI 8 ; rows ; cols t\n *\n * Cell size can be derived by dividing pixel dimensions by character dimensions.\n *\n * Supported by: xterm, Ghostty, Kitty, WezTerm, foot, iTerm2\n */\n\n/** Regex for CSI 4 ; height ; width t (pixel size response) */\nconst PIXEL_RESPONSE_RE = /\\x1b\\[4;(\\d+);(\\d+)t/\n\n/** Regex for CSI 8 ; rows ; cols t (text area size response) */\nconst TEXT_AREA_RESPONSE_RE = /\\x1b\\[8;(\\d+);(\\d+)t/\n\n// ============================================================================\n// Pixel Size Query\n// ============================================================================\n\n/**\n * Query the terminal text area size in pixels.\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param timeoutMs How long to wait for response (default: 200ms)\n * @returns Width and height in pixels, or null on timeout/unsupported\n */\nexport async function queryTextAreaPixels(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<{ width: number; height: number } | null> {\n write(\"\\x1b[14t\")\n\n const data = await read(timeoutMs)\n if (data == null) return null\n\n const match = PIXEL_RESPONSE_RE.exec(data)\n if (!match) return null\n\n return {\n height: parseInt(match[1]!, 10),\n width: parseInt(match[2]!, 10),\n }\n}\n\n// ============================================================================\n// Text Area Size Query (characters)\n// ============================================================================\n\n/**\n * Query the terminal text area size in characters (rows x columns).\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param timeoutMs How long to wait for response (default: 200ms)\n * @returns Rows and columns, or null on timeout/unsupported\n */\nexport async function queryTextAreaSize(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<{ cols: number; rows: number } | null> {\n write(\"\\x1b[18t\")\n\n const data = await read(timeoutMs)\n if (data == null) return null\n\n const match = TEXT_AREA_RESPONSE_RE.exec(data)\n if (!match) return null\n\n return {\n rows: parseInt(match[1]!, 10),\n cols: parseInt(match[2]!, 10),\n }\n}\n\n// ============================================================================\n// Cell Size (derived)\n// ============================================================================\n\n/**\n * Query the terminal cell size in pixels by querying both pixel\n * dimensions and character dimensions, then dividing.\n *\n * @param write Function to write to stdout\n * @param read Function to read a chunk from stdin\n * @param timeoutMs Per-query timeout (default: 200ms)\n * @returns Cell width and height in pixels, or null if either query fails\n */\nexport async function queryCellSize(\n write: (data: string) => void,\n read: (timeoutMs: number) => Promise<string | null>,\n timeoutMs = 200,\n): Promise<{ width: number; height: number } | null> {\n const pixels = await queryTextAreaPixels(write, read, timeoutMs)\n if (pixels == null) return null\n\n const size = await queryTextAreaSize(write, read, timeoutMs)\n if (size == null) return null\n\n if (size.cols === 0 || size.rows === 0) return null\n\n return {\n width: pixels.width / size.cols,\n height: pixels.height / size.rows,\n }\n}\n","/**\n * Canvas Render Adapter\n *\n * Two modes:\n * - **monospace** (default): Layout in cell units (cols x rows), convert to pixels at draw time.\n * - **proportional** (monospace: false): Layout in pixel units, draw at pixel coordinates directly.\n * Uses canvas measureText() for real font metrics. Enables proportional fonts (Inter, SF Pro, etc.)\n */\n\nimport type {\n BorderChars,\n RenderAdapter,\n RenderBuffer,\n RenderStyle,\n TextMeasureResult,\n TextMeasureStyle,\n TextMeasurer,\n} from \"../render-adapter\"\nimport type { Measurer } from \"../unicode\"\nimport { wrapTextWithMeasurer, stripAnsi } from \"../unicode\"\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nexport interface CanvasAdapterConfig {\n /** Font size in pixels (default: 14) */\n fontSize?: number\n /** Font family (default: 'monospace') */\n fontFamily?: string\n /** Line height multiplier (default: 1.2) */\n lineHeight?: number\n /** Background color (default: '#1e1e1e') */\n backgroundColor?: string\n /** Default foreground color (default: '#d4d4d4') */\n foregroundColor?: string\n /** Monospace mode (default: true). When false, uses proportional font measurement. */\n monospace?: boolean\n /** Device pixel ratio for sharp rendering on HiDPI displays (default: 1) */\n dpr?: number\n}\n\nconst DEFAULT_CONFIG: Required<CanvasAdapterConfig> = {\n fontSize: 14,\n fontFamily: \"monospace\",\n lineHeight: 1.2,\n backgroundColor: \"#1e1e1e\",\n foregroundColor: \"#d4d4d4\",\n monospace: true,\n dpr: 1,\n}\n\n// ============================================================================\n// Border Characters (same as terminal for consistency)\n// ============================================================================\n\nconst BORDER_CHARS: Record<string, BorderChars> = {\n single: {\n topLeft: \"┌\",\n topRight: \"┐\",\n bottomLeft: \"└\",\n bottomRight: \"┘\",\n horizontal: \"─\",\n vertical: \"│\",\n },\n double: {\n topLeft: \"╔\",\n topRight: \"╗\",\n bottomLeft: \"╚\",\n bottomRight: \"╝\",\n horizontal: \"═\",\n vertical: \"║\",\n },\n round: {\n topLeft: \"╭\",\n topRight: \"╮\",\n bottomLeft: \"╰\",\n bottomRight: \"╯\",\n horizontal: \"─\",\n vertical: \"│\",\n },\n bold: {\n topLeft: \"┏\",\n topRight: \"┓\",\n bottomLeft: \"┗\",\n bottomRight: \"┛\",\n horizontal: \"━\",\n vertical: \"┃\",\n },\n}\n\n// ============================================================================\n// Canvas Measurer\n// ============================================================================\n\n/** Create a scratch canvas 2D context for text measurement. */\nfunction createMeasureContext(\n fontSize: number,\n fontFamily: string,\n): CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D {\n // Prefer document.createElement(\"canvas\") over OffscreenCanvas for measurement —\n // OffscreenCanvas may not have access to web fonts loaded via <link> or @font-face.\n // A regular canvas element shares the document's font loading state.\n const canvas =\n typeof document !== \"undefined\"\n ? document.createElement(\"canvas\")\n : typeof OffscreenCanvas !== \"undefined\"\n ? new OffscreenCanvas(1, 1)\n : null\n if (!canvas) throw new Error(\"Canvas not available for text measurement\")\n const ctx = canvas.getContext(\"2d\")\n if (!ctx) throw new Error(\"Could not get 2d context for measurement\")\n ;(ctx as CanvasRenderingContext2D).font = `${fontSize}px ${fontFamily}`\n return ctx as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D\n}\n\nfunction createCanvasMeasurer(config: Required<CanvasAdapterConfig>): TextMeasurer {\n if (config.monospace) {\n return {\n measureText(text: string, _style?: TextMeasureStyle): TextMeasureResult {\n return { width: text.length, height: 1 }\n },\n getLineHeight(_style?: TextMeasureStyle): number {\n return 1\n },\n }\n }\n\n const lineHeightPx = config.fontSize * config.lineHeight\n const ctx = createMeasureContext(config.fontSize, config.fontFamily)\n\n return {\n measureText(text: string, _style?: TextMeasureStyle): TextMeasureResult {\n return { width: ctx.measureText(text).width, height: lineHeightPx }\n },\n getLineHeight(_style?: TextMeasureStyle): number {\n return lineHeightPx\n },\n }\n}\n\n// ============================================================================\n// Proportional (Pixel) Measurer\n// ============================================================================\n\n/** Font config for pixel measurement — only what's needed. */\nexport interface CanvasPixelMeasurerConfig {\n fontSize: number\n fontFamily: string\n lineHeight: number\n}\n\n/**\n * Create a pipeline Measurer that uses canvas font metrics for pixel-accurate widths.\n * All measurements return pixel values. Wrapping reuses silvery's wrapTextWithMeasurer.\n */\nexport function createCanvasPixelMeasurer(config: CanvasPixelMeasurerConfig): Measurer {\n const ctx = createMeasureContext(config.fontSize, config.fontFamily)\n const lineHeightPx = config.fontSize * config.lineHeight\n\n // Simple cache with full eviction at capacity (5000 entries, mostly single graphemes)\n const cache = new Map<string, number>()\n\n function pixelWidth(text: string): number {\n if (text.length === 0) return 0\n const cached = cache.get(text)\n if (cached !== undefined) return cached\n if (cache.size >= 5000) cache.clear()\n const w = ctx.measureText(text).width\n cache.set(text, w)\n return w\n }\n\n // Use Intl.Segmenter for proper grapheme iteration (emoji, CJK, combining marks)\n const segmenter = new Intl.Segmenter(undefined, { granularity: \"grapheme\" })\n\n function sliceGraphemes(text: string, maxWidth: number, fromEnd: boolean): string {\n const stripped = stripAnsi(text)\n if (pixelWidth(stripped) <= maxWidth) return text\n const graphemes = [...segmenter.segment(stripped)].map((s) => s.segment)\n let width = 0\n if (fromEnd) {\n for (let i = graphemes.length - 1; i >= 0; i--) {\n const gw = pixelWidth(graphemes[i]!)\n if (width + gw > maxWidth) return graphemes.slice(i + 1).join(\"\")\n width += gw\n }\n return stripped\n }\n for (let i = 0; i < graphemes.length; i++) {\n const gw = pixelWidth(graphemes[i]!)\n if (width + gw > maxWidth) return graphemes.slice(0, i).join(\"\")\n width += gw\n }\n return stripped\n }\n\n const measurer: Measurer = {\n textEmojiWide: false,\n textSizingEnabled: false,\n lineHeight: lineHeightPx,\n displayWidth(text: string): number {\n return pixelWidth(stripAnsi(text))\n },\n displayWidthAnsi(text: string): number {\n return pixelWidth(stripAnsi(text))\n },\n graphemeWidth(grapheme: string): number {\n return pixelWidth(grapheme)\n },\n wrapText(text: string, width: number, trim?: boolean, hard?: boolean): string[] {\n return wrapTextWithMeasurer(text, width, measurer, trim ?? false, hard ?? false)\n },\n sliceByWidth(text: string, maxWidth: number): string {\n return sliceGraphemes(text, maxWidth, false)\n },\n sliceByWidthFromEnd(text: string, maxWidth: number): string {\n return sliceGraphemes(text, maxWidth, true)\n },\n }\n\n return measurer\n}\n\n// ============================================================================\n// Color Conversion\n// ============================================================================\n\n// ANSI 256-color palette (standard 16 colors)\nconst ANSI_COLORS: Record<string, string> = {\n black: \"#000000\",\n red: \"#cd0000\",\n green: \"#00cd00\",\n yellow: \"#cdcd00\",\n blue: \"#0000ee\",\n magenta: \"#cd00cd\",\n cyan: \"#00cdcd\",\n white: \"#e5e5e5\",\n gray: \"#7f7f7f\",\n grey: \"#7f7f7f\",\n brightBlack: \"#7f7f7f\",\n brightRed: \"#ff0000\",\n brightGreen: \"#00ff00\",\n brightYellow: \"#ffff00\",\n brightBlue: \"#5c5cff\",\n brightMagenta: \"#ff00ff\",\n brightCyan: \"#00ffff\",\n brightWhite: \"#ffffff\",\n}\n\nfunction resolveColor(color: string | undefined, fallback: string): string {\n if (!color) return fallback\n\n // Already a CSS color (hex, rgb, etc.)\n if (color.startsWith(\"#\") || color.startsWith(\"rgb\")) {\n return color\n }\n\n // Named ANSI color\n const named = ANSI_COLORS[color.toLowerCase()]\n if (named) return named\n\n // Pass through (might be a CSS color name like 'cyan')\n return color\n}\n\n// ============================================================================\n// Canvas Render Buffer\n// ============================================================================\n\nexport class CanvasRenderBuffer implements RenderBuffer {\n readonly width: number\n readonly height: number\n readonly canvas: OffscreenCanvas | HTMLCanvasElement\n private ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D\n private config: Required<CanvasAdapterConfig>\n\n // Cell-to-pixel conversion factors\n private readonly charWidth: number\n private readonly cellHeight: number\n\n constructor(width: number, height: number, config: Required<CanvasAdapterConfig>) {\n this.width = width\n this.height = height\n this.config = config\n\n const dpr = config.dpr\n\n let cssWidth: number\n let cssHeight: number\n\n if (config.monospace) {\n // Monospace: layout in cell units, convert to pixels at draw time\n this.charWidth = config.fontSize * 0.6\n this.cellHeight = config.fontSize * config.lineHeight\n cssWidth = width * this.charWidth\n cssHeight = height * this.cellHeight\n } else {\n // Proportional: layout already in pixels, no conversion needed\n this.charWidth = 1\n this.cellHeight = 1\n cssWidth = width\n cssHeight = height\n }\n\n // Create canvas at native resolution (CSS pixels * DPR) for sharp HiDPI rendering.\n // The context transform scales all drawing by DPR, so coordinates stay in CSS pixels.\n const nativeWidth = Math.ceil(cssWidth * dpr)\n const nativeHeight = Math.ceil(cssHeight * dpr)\n\n if (typeof OffscreenCanvas !== \"undefined\") {\n this.canvas = new OffscreenCanvas(nativeWidth, nativeHeight)\n } else if (typeof document !== \"undefined\") {\n this.canvas = document.createElement(\"canvas\")\n this.canvas.width = nativeWidth\n this.canvas.height = nativeHeight\n } else {\n throw new Error(\"Canvas not available\")\n }\n\n const ctx = this.canvas.getContext(\"2d\")\n if (!ctx) throw new Error(\"Could not get 2d context\")\n this.ctx = ctx as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D\n\n // Scale context so all drawing uses CSS pixel coordinates\n if (dpr !== 1) {\n ;(this.ctx as CanvasRenderingContext2D).setTransform(dpr, 0, 0, dpr, 0, 0)\n }\n\n // Initialize with background\n this.ctx.fillStyle = config.backgroundColor\n this.ctx.fillRect(0, 0, cssWidth, cssHeight)\n }\n\n fillRect(x: number, y: number, width: number, height: number, style: RenderStyle): void {\n if (style.bg) {\n // Convert cell coordinates to pixel coordinates\n const px = x * this.charWidth\n const py = y * this.cellHeight\n const pw = width * this.charWidth\n const ph = height * this.cellHeight\n this.ctx.fillStyle = resolveColor(style.bg, this.config.backgroundColor)\n this.ctx.fillRect(px, py, pw, ph)\n }\n }\n\n drawText(x: number, y: number, text: string, style: RenderStyle): void {\n // Convert cell coordinates to pixel coordinates\n const px = x * this.charWidth\n\n const attrs = style.attrs ?? {}\n\n // Build font string\n const weight = attrs.bold ? \"bold\" : \"normal\"\n const fontStyle = attrs.italic ? \"italic\" : \"normal\"\n this.ctx.font = `${fontStyle} ${weight} ${this.config.fontSize}px ${this.config.fontFamily}`\n\n let py = y * this.cellHeight\n\n // Set colors\n this.ctx.fillStyle = resolveColor(style.fg, this.config.foregroundColor)\n\n if (!this.config.monospace) {\n // Match CSS line-height centering exactly.\n // CSS positions text by placing the alphabetic baseline at:\n // y + halfLeading + fontAscent\n // where halfLeading = (lineHeight - contentArea) / 2\n // and contentArea = fontBoundingBoxAscent + fontBoundingBoxDescent.\n // fontBoundingBox metrics are font-wide (not glyph-specific), matching CSS.\n const metrics = this.ctx.measureText(\"Hg\")\n const fontAscent = metrics.fontBoundingBoxAscent\n const fontDescent = metrics.fontBoundingBoxDescent\n const contentArea = fontAscent + fontDescent\n const lineHeightPx = this.config.fontSize * this.config.lineHeight\n const halfLeading = (lineHeightPx - contentArea) / 2\n this.ctx.textBaseline = \"alphabetic\"\n py += halfLeading + fontAscent\n } else {\n this.ctx.textBaseline = \"top\"\n }\n\n // Draw text\n this.ctx.fillText(text, px, py)\n\n // Handle underline\n if (attrs.underline) {\n this.drawUnderline(px, py, text, style)\n }\n\n // Handle strikethrough\n if (attrs.strikethrough) {\n const metrics = this.ctx.measureText(text)\n const textWidth = metrics.width\n const strikeY = py + this.config.fontSize * 0.5\n\n this.ctx.strokeStyle = resolveColor(style.fg, this.config.foregroundColor)\n this.ctx.lineWidth = 1\n this.ctx.beginPath()\n this.ctx.moveTo(px, strikeY)\n this.ctx.lineTo(px + textWidth, strikeY)\n this.ctx.stroke()\n }\n }\n\n /**\n * Draw underline decorations at pixel coordinates.\n * Note: px, py are already in pixel coordinates.\n */\n private drawUnderline(px: number, py: number, text: string, style: RenderStyle): void {\n const attrs = style.attrs ?? {}\n const metrics = this.ctx.measureText(text)\n const textWidth = metrics.width\n const underlineY = py + this.config.fontSize * 0.9\n\n const underlineColor = resolveColor(attrs.underlineColor ?? style.fg, this.config.foregroundColor)\n\n this.ctx.strokeStyle = underlineColor\n this.ctx.lineWidth = 1\n\n const underlineStyle = attrs.underlineStyle ?? \"single\"\n\n switch (underlineStyle) {\n case \"double\":\n // Two parallel lines\n this.ctx.beginPath()\n this.ctx.moveTo(px, underlineY - 1)\n this.ctx.lineTo(px + textWidth, underlineY - 1)\n this.ctx.moveTo(px, underlineY + 1)\n this.ctx.lineTo(px + textWidth, underlineY + 1)\n this.ctx.stroke()\n break\n\n case \"curly\":\n // Wavy line using bezier curves\n this.ctx.beginPath()\n this.ctx.moveTo(px, underlineY)\n const waveLength = 4\n const amplitude = 2\n for (let wx = 0; wx < textWidth; wx += waveLength * 2) {\n this.ctx.quadraticCurveTo(px + wx + waveLength / 2, underlineY - amplitude, px + wx + waveLength, underlineY)\n this.ctx.quadraticCurveTo(\n px + wx + (waveLength * 3) / 2,\n underlineY + amplitude,\n px + wx + waveLength * 2,\n underlineY,\n )\n }\n this.ctx.stroke()\n break\n\n case \"dotted\":\n this.ctx.setLineDash([2, 2])\n this.ctx.beginPath()\n this.ctx.moveTo(px, underlineY)\n this.ctx.lineTo(px + textWidth, underlineY)\n this.ctx.stroke()\n this.ctx.setLineDash([])\n break\n\n case \"dashed\":\n this.ctx.setLineDash([4, 2])\n this.ctx.beginPath()\n this.ctx.moveTo(px, underlineY)\n this.ctx.lineTo(px + textWidth, underlineY)\n this.ctx.stroke()\n this.ctx.setLineDash([])\n break\n\n default: // 'single'\n this.ctx.beginPath()\n this.ctx.moveTo(px, underlineY)\n this.ctx.lineTo(px + textWidth, underlineY)\n this.ctx.stroke()\n }\n }\n\n drawChar(x: number, y: number, char: string, style: RenderStyle): void {\n // For canvas, drawChar is essentially drawText for single chars\n this.drawText(x, y, char, style)\n }\n\n inBounds(x: number, y: number): boolean {\n return x >= 0 && x < this.width && y >= 0 && y < this.height\n }\n\n fillRoundedRect(\n x: number,\n y: number,\n width: number,\n height: number,\n radius: number,\n fill: string | undefined,\n stroke: string | undefined,\n lineWidth = 1,\n ): void {\n const px = x * this.charWidth\n const py = y * this.cellHeight\n const pw = width * this.charWidth\n const ph = height * this.cellHeight\n const r = Math.min(radius, pw / 2, ph / 2)\n\n this.ctx.beginPath()\n this.ctx.moveTo(px + r, py)\n this.ctx.lineTo(px + pw - r, py)\n this.ctx.quadraticCurveTo(px + pw, py, px + pw, py + r)\n this.ctx.lineTo(px + pw, py + ph - r)\n this.ctx.quadraticCurveTo(px + pw, py + ph, px + pw - r, py + ph)\n this.ctx.lineTo(px + r, py + ph)\n this.ctx.quadraticCurveTo(px, py + ph, px, py + ph - r)\n this.ctx.lineTo(px, py + r)\n this.ctx.quadraticCurveTo(px, py, px + r, py)\n this.ctx.closePath()\n\n if (fill) {\n this.ctx.fillStyle = fill\n this.ctx.fill()\n }\n if (stroke) {\n this.ctx.strokeStyle = stroke\n this.ctx.lineWidth = lineWidth\n this.ctx.stroke()\n }\n }\n}\n\n// ============================================================================\n// Canvas Adapter Factory\n// ============================================================================\n\nexport function createCanvasAdapter(config: CanvasAdapterConfig = {}): RenderAdapter {\n const cfg = { ...DEFAULT_CONFIG, ...config }\n const measurer = createCanvasMeasurer(cfg)\n\n return {\n name: \"canvas\",\n measurer,\n\n createBuffer(width: number, height: number): RenderBuffer {\n return new CanvasRenderBuffer(width, height, cfg)\n },\n\n flush(_buffer: RenderBuffer, _prevBuffer: RenderBuffer | null): void {\n // Canvas draws directly to the buffer during render.\n // The caller (renderToCanvas) copies the buffer to the visible canvas.\n },\n\n getBorderChars(style: string): BorderChars {\n return BORDER_CHARS[style] ?? BORDER_CHARS.single!\n },\n }\n}\n","/**\n * DOM Render Adapter\n *\n * Implements the RenderAdapter interface for browser DOM output.\n * Uses a line-based approach: one <div> per row, <span> elements for styled text runs.\n * The layout engine operates in cell units (columns x rows). This adapter\n * converts cell coordinates to pixel coordinates when rendering to the DOM,\n * using charWidth (fontSize * 0.6) and cellHeight (fontSize * lineHeight).\n *\n * Advantages over Canvas:\n * - Native text selection and copying\n * - Screen reader accessibility\n * - Browser font rendering (subpixel antialiasing, ligatures)\n * - CSS integration (theming, hover states)\n * - DevTools inspection\n *\n * Architecture follows xterm.js DOM renderer approach.\n * @see https://github.com/xtermjs/xterm.js/issues/3271\n */\n\nimport type {\n BorderChars,\n RenderAdapter,\n RenderBuffer,\n RenderStyle,\n TextMeasureResult,\n TextMeasureStyle,\n TextMeasurer,\n} from \"../render-adapter\"\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nexport interface DOMAdapterConfig {\n /** Font size in pixels (default: 14) */\n fontSize?: number\n /** Font family (default: 'monospace') */\n fontFamily?: string\n /** Line height multiplier (default: 1.2) */\n lineHeight?: number\n /** Background color (default: '#1e1e1e') */\n backgroundColor?: string\n /** Default foreground color (default: '#d4d4d4') */\n foregroundColor?: string\n /** CSS class prefix (default: 'silvery') */\n classPrefix?: string\n}\n\nconst DEFAULT_CONFIG: Required<DOMAdapterConfig> = {\n fontSize: 14,\n fontFamily: \"monospace\",\n lineHeight: 1.2,\n backgroundColor: \"#1e1e1e\",\n foregroundColor: \"#d4d4d4\",\n classPrefix: \"silvery\",\n}\n\n// ============================================================================\n// Border Characters (same as terminal/canvas for consistency)\n// ============================================================================\n\nconst BORDER_CHARS: Record<string, BorderChars> = {\n single: {\n topLeft: \"┌\",\n topRight: \"┐\",\n bottomLeft: \"└\",\n bottomRight: \"┘\",\n horizontal: \"─\",\n vertical: \"│\",\n },\n double: {\n topLeft: \"╔\",\n topRight: \"╗\",\n bottomLeft: \"╚\",\n bottomRight: \"╝\",\n horizontal: \"═\",\n vertical: \"║\",\n },\n round: {\n topLeft: \"╭\",\n topRight: \"╮\",\n bottomLeft: \"╰\",\n bottomRight: \"╯\",\n horizontal: \"─\",\n vertical: \"│\",\n },\n bold: {\n topLeft: \"┏\",\n topRight: \"┓\",\n bottomLeft: \"┗\",\n bottomRight: \"┛\",\n horizontal: \"━\",\n vertical: \"┃\",\n },\n}\n\n// ============================================================================\n// Color Conversion\n// ============================================================================\n\nconst ANSI_COLORS: Record<string, string> = {\n black: \"#000000\",\n red: \"#cd0000\",\n green: \"#00cd00\",\n yellow: \"#cdcd00\",\n blue: \"#0000ee\",\n magenta: \"#cd00cd\",\n cyan: \"#00cdcd\",\n white: \"#e5e5e5\",\n gray: \"#7f7f7f\",\n grey: \"#7f7f7f\",\n brightblack: \"#7f7f7f\",\n brightred: \"#ff0000\",\n brightgreen: \"#00ff00\",\n brightyellow: \"#ffff00\",\n brightblue: \"#5c5cff\",\n brightmagenta: \"#ff00ff\",\n brightcyan: \"#00ffff\",\n brightwhite: \"#ffffff\",\n}\n\nfunction resolveColor(color: string | undefined, fallback: string): string {\n if (!color) return fallback\n if (color.startsWith(\"#\") || color.startsWith(\"rgb\")) return color\n const named = ANSI_COLORS[color.toLowerCase()]\n return named ?? color\n}\n\n// ============================================================================\n// DOM Measurer\n// ============================================================================\n\nfunction createDOMMeasurer(_config: Required<DOMAdapterConfig>): TextMeasurer {\n // The layout engine operates in cell units (columns x rows), matching the\n // terminal convention. For monospace fonts, text width = character count\n // and line height = 1 row.\n return {\n measureText(text: string, _style?: TextMeasureStyle): TextMeasureResult {\n // For monospace fonts, width is simply the character count (one cell per char)\n return {\n width: text.length,\n height: 1,\n }\n },\n\n getLineHeight(_style?: TextMeasureStyle): number {\n return 1\n },\n }\n}\n\n// ============================================================================\n// Styled Text Run\n// ============================================================================\n\ninterface TextRun {\n text: string\n style: RenderStyle\n x: number\n}\n\n// ============================================================================\n// DOM Render Buffer\n// ============================================================================\n\nexport class DOMRenderBuffer implements RenderBuffer {\n readonly width: number\n readonly height: number\n\n private config: Required<DOMAdapterConfig>\n private lines: Map<number, TextRun[]>\n private backgrounds: Map<string, { x: number; y: number; w: number; h: number; color: string }>\n\n // Cell-to-pixel conversion factors\n private readonly charWidth: number\n private readonly cellHeight: number\n\n // Container element (set when flushing)\n private container: HTMLElement | null = null\n\n constructor(width: number, height: number, config: Required<DOMAdapterConfig>) {\n this.width = width\n this.height = height\n this.config = config\n this.lines = new Map()\n this.backgrounds = new Map()\n\n // Compute cell dimensions for coordinate conversion.\n // Width/height are in cell units (cols/rows); rendering converts to pixels.\n this.charWidth = config.fontSize * 0.6\n this.cellHeight = config.fontSize * config.lineHeight\n }\n\n /**\n * Set the container element for rendering.\n */\n setContainer(container: HTMLElement): void {\n this.container = container\n }\n\n /**\n * Get the container element.\n */\n getContainer(): HTMLElement | null {\n return this.container\n }\n\n fillRect(x: number, y: number, width: number, height: number, style: RenderStyle): void {\n if (style.bg) {\n const key = `${x},${y},${width},${height}`\n this.backgrounds.set(key, {\n x,\n y,\n w: width,\n h: height,\n color: resolveColor(style.bg, this.config.backgroundColor),\n })\n }\n }\n\n drawText(x: number, y: number, text: string, style: RenderStyle): void {\n if (!this.lines.has(y)) {\n this.lines.set(y, [])\n }\n this.lines.get(y)!.push({ text, style, x })\n }\n\n drawChar(x: number, y: number, char: string, style: RenderStyle): void {\n this.drawText(x, y, char, style)\n }\n\n inBounds(x: number, y: number): boolean {\n return x >= 0 && x < this.width && y >= 0 && y < this.height\n }\n\n /**\n * Render the buffer to the container element.\n * Coordinates in the buffer are in cell units (cols/rows).\n * This method converts them to pixel coordinates for DOM positioning.\n */\n render(): void {\n if (!this.container) {\n throw new Error(\"DOMRenderBuffer: No container set. Call setContainer() first.\")\n }\n\n const container = this.container\n const cw = this.charWidth\n const ch = this.cellHeight\n\n // Container dimensions in pixels (convert cell units back to pixels)\n const containerWidthPx = this.width * cw\n const containerHeightPx = this.height * ch\n\n // Clear previous content\n container.innerHTML = \"\"\n\n // Set container styles\n container.style.cssText = `\n\t\t\tposition: relative;\n\t\t\tfont-family: ${this.config.fontFamily};\n\t\t\tfont-size: ${this.config.fontSize}px;\n\t\t\tline-height: ${this.config.lineHeight};\n\t\t\tbackground-color: ${this.config.backgroundColor};\n\t\t\tcolor: ${this.config.foregroundColor};\n\t\t\twhite-space: pre;\n\t\t\toverflow: hidden;\n\t\t\twidth: ${containerWidthPx}px;\n\t\t\theight: ${containerHeightPx}px;\n\t\t`\n\n // Render background rectangles (convert cell coords to pixels)\n for (const bg of this.backgrounds.values()) {\n const bgDiv = document.createElement(\"div\")\n bgDiv.className = `${this.config.classPrefix}-bg`\n bgDiv.style.cssText = `\n\t\t\t\tposition: absolute;\n\t\t\t\tleft: ${bg.x * cw}px;\n\t\t\t\ttop: ${bg.y * ch}px;\n\t\t\t\twidth: ${bg.w * cw}px;\n\t\t\t\theight: ${bg.h * ch}px;\n\t\t\t\tbackground-color: ${bg.color};\n\t\t\t`\n container.appendChild(bgDiv)\n }\n\n // Render text lines (convert cell coords to pixels)\n const sortedLines = Array.from(this.lines.entries()).sort((a, b) => a[0] - b[0])\n\n for (const [y, runs] of sortedLines) {\n const lineDiv = document.createElement(\"div\")\n lineDiv.className = `${this.config.classPrefix}-line`\n lineDiv.style.cssText = `\n\t\t\t\tposition: absolute;\n\t\t\t\tleft: 0;\n\t\t\t\ttop: ${y * ch}px;\n\t\t\t\theight: ${ch}px;\n\t\t\t\twhite-space: pre;\n\t\t\t`\n\n // Sort runs by x position\n const sortedRuns = runs.sort((a, b) => a.x - b.x)\n\n for (const run of sortedRuns) {\n const span = document.createElement(\"span\")\n span.className = `${this.config.classPrefix}-text`\n span.textContent = run.text\n\n // Apply styles (convert cell x to pixel x)\n const styles: string[] = [`position: absolute`, `left: ${run.x * cw}px`]\n\n if (run.style.fg) {\n styles.push(`color: ${resolveColor(run.style.fg, this.config.foregroundColor)}`)\n }\n if (run.style.bg) {\n styles.push(`background-color: ${resolveColor(run.style.bg, this.config.backgroundColor)}`)\n }\n\n const attrs = run.style.attrs\n if (attrs) {\n if (attrs.bold) styles.push(\"font-weight: bold\")\n if (attrs.dim) styles.push(\"opacity: 0.5\")\n if (attrs.italic) styles.push(\"font-style: italic\")\n\n // Underline handling\n if (attrs.underline || attrs.underlineStyle) {\n const underlineStyle = attrs.underlineStyle ?? \"single\"\n const underlineColor = attrs.underlineColor\n ? resolveColor(attrs.underlineColor, this.config.foregroundColor)\n : \"currentColor\"\n\n switch (underlineStyle) {\n case \"double\":\n styles.push(`text-decoration: underline double ${underlineColor}`)\n break\n case \"curly\":\n styles.push(`text-decoration: underline wavy ${underlineColor}`)\n break\n case \"dotted\":\n styles.push(`text-decoration: underline dotted ${underlineColor}`)\n break\n case \"dashed\":\n styles.push(`text-decoration: underline dashed ${underlineColor}`)\n break\n default:\n styles.push(`text-decoration: underline solid ${underlineColor}`)\n }\n }\n\n if (attrs.strikethrough) {\n const existing = styles.find((s) => s.startsWith(\"text-decoration:\"))\n if (existing) {\n const idx = styles.indexOf(existing)\n styles[idx] = existing.replace(\"underline\", \"underline line-through\")\n } else {\n styles.push(\"text-decoration: line-through\")\n }\n }\n\n if (attrs.inverse) {\n // Swap foreground/background\n const fg = run.style.fg\n ? resolveColor(run.style.fg, this.config.foregroundColor)\n : this.config.foregroundColor\n const bg = run.style.bg\n ? resolveColor(run.style.bg, this.config.backgroundColor)\n : this.config.backgroundColor\n styles.push(`color: ${bg}`, `background-color: ${fg}`)\n }\n }\n\n span.style.cssText = styles.join(\"; \")\n lineDiv.appendChild(span)\n }\n\n container.appendChild(lineDiv)\n }\n }\n\n /**\n * Clear the buffer.\n */\n clear(): void {\n this.lines.clear()\n this.backgrounds.clear()\n }\n}\n\n// ============================================================================\n// DOM Adapter Factory\n// ============================================================================\n\nexport function createDOMAdapter(config: DOMAdapterConfig = {}): RenderAdapter {\n const cfg = { ...DEFAULT_CONFIG, ...config }\n const measurer = createDOMMeasurer(cfg)\n\n return {\n name: \"dom\",\n measurer,\n\n createBuffer(width: number, height: number): RenderBuffer {\n return new DOMRenderBuffer(width, height, cfg)\n },\n\n flush(buffer: RenderBuffer, _prevBuffer: RenderBuffer | null): void {\n // DOM buffer renders directly when render() is called\n const domBuffer = buffer as DOMRenderBuffer\n if (domBuffer.getContainer()) {\n domBuffer.render()\n }\n },\n\n getBorderChars(style: string): BorderChars {\n return BORDER_CHARS[style] ?? BORDER_CHARS.single!\n },\n }\n}\n\n// ============================================================================\n// Inject Global Styles (Optional)\n// ============================================================================\n\nlet stylesInjected = false\n\n/**\n * Inject global CSS styles for silvery DOM rendering.\n * Call once at application startup if you want default styling.\n */\nexport function injectDOMStyles(classPrefix = \"silvery\"): void {\n if (stylesInjected || typeof document === \"undefined\") return\n\n const style = document.createElement(\"style\")\n style.textContent = `\n\t\t.${classPrefix}-container {\n\t\t\tfont-family: monospace;\n\t\t\twhite-space: pre;\n\t\t\toverflow: hidden;\n\t\t}\n\t\t.${classPrefix}-line {\n\t\t\twhite-space: pre;\n\t\t}\n\t\t.${classPrefix}-text {\n\t\t\twhite-space: pre;\n\t\t}\n\t\t/* Selection styling */\n\t\t.${classPrefix}-text::selection {\n\t\t\tbackground-color: rgba(100, 150, 255, 0.3);\n\t\t}\n\t`\n document.head.appendChild(style)\n stylesInjected = true\n}\n","/**\n * DOM-level Mouse Events for silvery\n *\n * Provides React DOM-compatible mouse event infrastructure:\n * - SilveryMouseEvent / SilveryWheelEvent synthetic event objects\n * - Tree-based hit testing using scrollRect (replaces manual HitRegistry)\n * - Event dispatch with bubbling (target → root, stopPropagation support)\n * - Double-click detection (300ms / 2-cell threshold)\n * - mouseenter/mouseleave tracking (no bubble, like DOM spec)\n */\n\nimport { createLogger } from \"loggily\"\nimport type { FocusManager } from \"@silvery/ag/focus-manager\"\nimport { findFocusableAncestor } from \"@silvery/ag/focus-queries\"\nimport type { ParsedMouse } from \"./mouse\"\nimport { getAncestorPath, pointInRect } from \"@silvery/ag/tree-utils\"\nimport type { AgNode, Rect, UserSelect } from \"@silvery/ag/types\"\nimport type { SelectionScope } from \"@silvery/headless/selection\"\nimport { setHovered, setArmed } from \"@silvery/ag/interactive-signals\"\n\n// Re-export canonical types from ag (avoid duplicate type definitions)\nexport type { SilveryMouseEvent, SilveryWheelEvent } from \"@silvery/ag/mouse-event-types\"\nimport type { SilveryMouseEvent, SilveryWheelEvent } from \"@silvery/ag/mouse-event-types\"\n\nconst mouseLog = createLogger(\"silvery:mouse\")\n\n// ============================================================================\n// Mouse Event Handler Props — canonical location is @silvery/ag\n// ============================================================================\n\nimport type { MouseEventProps } from \"@silvery/ag/mouse-event-types\"\n\n// ============================================================================\n// Event Factory\n// ============================================================================\n\n/**\n * Create a synthetic mouse event.\n *\n * Modifier keys are merged from two sources:\n * - SGR mouse protocol: reports Ctrl, Alt/Meta, Shift (reliable)\n * - Keyboard tracking: reports Super/Cmd, Hyper, CapsLock, NumLock (via Kitty protocol)\n *\n * `metaKey` = keyboard-tracked Super (Cmd on macOS). SGR \"meta\" maps to `altKey`.\n */\nexport function createMouseEvent(\n type: SilveryMouseEvent[\"type\"],\n x: number,\n y: number,\n target: AgNode,\n parsed: ParsedMouse,\n keyboardMods?: KeyboardModifierState,\n): SilveryMouseEvent {\n let propagationStopped = false\n let defaultPrevented = false\n const metaKey = keyboardMods?.super ?? false\n if (type === \"click\" || type === \"mousedown\") {\n mouseLog.debug?.(`createMouseEvent(${type}) metaKey=${metaKey} keyboardMods.super=${keyboardMods?.super}`)\n }\n\n return {\n type,\n clientX: x,\n clientY: y,\n button: parsed.button,\n altKey: parsed.meta,\n ctrlKey: parsed.ctrl,\n metaKey,\n shiftKey: parsed.shift,\n target,\n currentTarget: target,\n nativeEvent: parsed,\n get propagationStopped() {\n return propagationStopped\n },\n get defaultPrevented() {\n return defaultPrevented\n },\n stopPropagation() {\n propagationStopped = true\n },\n preventDefault() {\n defaultPrevented = true\n },\n }\n}\n\n/**\n * Create a synthetic wheel event.\n */\nexport function createWheelEvent(\n x: number,\n y: number,\n target: AgNode,\n parsed: ParsedMouse,\n keyboardMods?: KeyboardModifierState,\n): SilveryWheelEvent {\n const base = createMouseEvent(\"wheel\", x, y, target, parsed, keyboardMods) as SilveryWheelEvent\n base.deltaY = parsed.delta ?? 0\n base.deltaX = 0\n return base\n}\n\n// ============================================================================\n// Hit Testing\n// ============================================================================\n\n/**\n * Tree-based hit test: find the deepest node whose scrollRect contains (x, y).\n * Uses reverse child order (last sibling wins = highest z-order, like DOM).\n * Respects overflow:hidden clipping.\n */\nexport function hitTest(node: AgNode, x: number, y: number): AgNode | null {\n const rect = node.scrollRect\n if (!rect) return null\n\n // Check if point is within this node's bounds\n if (!pointInRect(x, y, rect)) return null\n\n // pointerEvents=\"none\" makes this node and its subtree invisible to hit testing\n const props = node.props as { overflow?: string; pointerEvents?: string }\n if (props.pointerEvents === \"none\") return null\n\n // Check overflow clipping — if overflow is \"hidden\" or \"scroll\",\n // children outside this node's rect are not hittable\n const clips = props.overflow === \"hidden\" || props.overflow === \"scroll\"\n\n // DFS: check children in reverse order (last child = top z-order, like DOM)\n for (let i = node.children.length - 1; i >= 0; i--) {\n const child = node.children[i]!\n // If parent clips, skip children whose scrollRect doesn't overlap parent\n if (clips) {\n const childRect = child.scrollRect\n if (childRect && !pointInRect(x, y, rect)) {\n continue\n }\n }\n const hit = hitTest(child, x, y)\n if (hit) return hit\n }\n\n // Check virtual text children with inlineRects (nested Text inside Text).\n // These don't have scrollRect/layoutNode, so standard DFS misses them.\n if (node.type === \"silvery-text\") {\n for (let i = node.children.length - 1; i >= 0; i--) {\n const child = node.children[i]!\n if (child.inlineRects) {\n for (const inlineRect of child.inlineRects) {\n if (pointInRect(x, y, inlineRect)) return child\n }\n }\n }\n }\n\n // No child matched — this node is the target (if it has a scrollRect)\n return node\n}\n\n// ============================================================================\n// Selection Hit Testing\n// ============================================================================\n\n/**\n * Resolve the effective userSelect value for a node.\n * \"auto\" inherits from parent; root defaults to \"text\".\n */\nexport function resolveUserSelect(node: AgNode): \"none\" | \"text\" | \"contain\" {\n let current: AgNode | null = node\n while (current) {\n const props = current.props as { userSelect?: UserSelect }\n const value = props.userSelect\n if (value === \"none\" || value === \"text\" || value === \"contain\") return value\n // \"auto\" or undefined — walk up\n current = current.parent\n }\n // Root default is \"text\"\n return \"text\"\n}\n\n/**\n * Selection hit test: find the deepest node whose text is selectable at (x, y).\n *\n * Unlike pointer hitTest, this:\n * - Ignores pointerEvents (a node with pointerEvents=\"none\" can still be selectable)\n * - Respects userSelect (a node with userSelect=\"none\" is not a selection target)\n */\nexport function selectionHitTest(node: AgNode, x: number, y: number): AgNode | null {\n const rect = node.scrollRect\n if (!rect) return null\n\n if (!pointInRect(x, y, rect)) return null\n\n // userSelect=\"none\" blocks this subtree from selection hit testing\n // But only if explicitly \"none\" — \"auto\" inherits and root defaults to \"text\"\n const props = node.props as { overflow?: string; userSelect?: UserSelect }\n const resolved = resolveUserSelect(node)\n if (resolved === \"none\") return null\n\n // Check overflow clipping (same as pointer hitTest)\n const clips = props.overflow === \"hidden\" || props.overflow === \"scroll\"\n\n // DFS: check children in reverse order (last child = top z-order)\n for (let i = node.children.length - 1; i >= 0; i--) {\n const child = node.children[i]!\n if (clips) {\n const childRect = child.scrollRect\n if (childRect && !pointInRect(x, y, rect)) {\n continue\n }\n }\n const hit = selectionHitTest(child, x, y)\n if (hit) return hit\n }\n\n // Check virtual text children with inlineRects\n if (node.type === \"silvery-text\") {\n for (let i = node.children.length - 1; i >= 0; i--) {\n const child = node.children[i]!\n if (child.inlineRects) {\n for (const inlineRect of child.inlineRects) {\n if (pointInRect(x, y, inlineRect)) return child\n }\n }\n }\n }\n\n return node\n}\n\n/**\n * Find the contain boundary for a node.\n * Walks up to the nearest `userSelect=\"contain\"` ancestor and returns its scrollRect\n * as a SelectionScope. Returns null if no contain boundary exists.\n */\nexport function findContainBoundary(node: AgNode): SelectionScope | null {\n let current: AgNode | null = node\n while (current) {\n const props = current.props as { userSelect?: UserSelect }\n if (props.userSelect === \"contain\") {\n const rect = current.scrollRect\n if (rect) {\n return {\n top: rect.y,\n bottom: rect.y + rect.height - 1,\n left: rect.x,\n right: rect.x + rect.width - 1,\n }\n }\n }\n current = current.parent\n }\n return null\n}\n\n// ============================================================================\n// Draggable Resolution\n// ============================================================================\n\n/**\n * Check if a node has draggable=true.\n * Unlike userSelect, draggable is NOT inherited — only the exact node is checked.\n * Ancestors' draggable prop has no effect on children.\n */\nexport function resolveNodeDraggable(node: AgNode | null): boolean {\n if (!node) return false\n const props = node.props as { draggable?: boolean }\n return props.draggable === true\n}\n\n// ============================================================================\n// Event Dispatch\n// ============================================================================\n\n/** Map event type to the handler prop name */\nconst EVENT_HANDLER_MAP: Record<string, string & keyof MouseEventProps> = {\n click: \"onClick\",\n dblclick: \"onDoubleClick\",\n mousedown: \"onMouseDown\",\n mouseup: \"onMouseUp\",\n mousemove: \"onMouseMove\",\n mouseenter: \"onMouseEnter\",\n mouseleave: \"onMouseLeave\",\n wheel: \"onWheel\",\n}\n\n/**\n * Dispatch a mouse event through the render tree with DOM-style bubbling.\n *\n * Bubbles from target → root, calling the appropriate handler on each node.\n * stopPropagation() halts bubbling. mouseenter/mouseleave do NOT bubble (DOM spec).\n */\nexport function dispatchMouseEvent(event: SilveryMouseEvent): void {\n const handlerProp = EVENT_HANDLER_MAP[event.type]\n if (!handlerProp) return\n\n // mouseenter/mouseleave don't bubble (DOM spec)\n const noBubble = event.type === \"mouseenter\" || event.type === \"mouseleave\"\n\n if (noBubble) {\n // Only fire on the target itself\n const handler = (event.target.props as Record<string, unknown>)[handlerProp] as\n | ((e: SilveryMouseEvent) => void)\n | undefined\n if (handler) {\n const mutableEvent = event as { currentTarget: AgNode }\n mutableEvent.currentTarget = event.target\n handler(event)\n }\n return\n }\n\n // Bubble phase: fire from target up to root\n const path = getAncestorPath(event.target)\n for (const node of path) {\n if (event.propagationStopped) break\n\n const handler = (node.props as Record<string, unknown>)[handlerProp] as ((e: SilveryMouseEvent) => void) | undefined\n if (handler) {\n const mutableEvent = event as { currentTarget: AgNode }\n mutableEvent.currentTarget = node\n handler(event)\n }\n }\n}\n\n// ============================================================================\n// Double-Click Detection\n// ============================================================================\n\nexport interface DoubleClickState {\n lastClickTime: number\n lastClickX: number\n lastClickY: number\n lastClickButton: number\n}\n\nexport function createDoubleClickState(): DoubleClickState {\n return {\n lastClickTime: 0,\n lastClickX: -999,\n lastClickY: -999,\n lastClickButton: -1,\n }\n}\n\nconst DOUBLE_CLICK_TIME_MS = 300\nconst DOUBLE_CLICK_DISTANCE = 2\n\n/**\n * Check if a click qualifies as a double-click, given the previous click state.\n * Updates the state for the next check.\n * Returns true if this is a double-click.\n */\nexport function checkDoubleClick(\n state: DoubleClickState,\n x: number,\n y: number,\n button: number,\n now: number = Date.now(),\n): boolean {\n const timeDelta = now - state.lastClickTime\n const dx = Math.abs(x - state.lastClickX)\n const dy = Math.abs(y - state.lastClickY)\n const sameButton = button === state.lastClickButton\n\n const isDouble =\n sameButton && timeDelta <= DOUBLE_CLICK_TIME_MS && dx <= DOUBLE_CLICK_DISTANCE && dy <= DOUBLE_CLICK_DISTANCE\n\n // Update state\n state.lastClickTime = now\n state.lastClickX = x\n state.lastClickY = y\n state.lastClickButton = button\n\n // If double-click, reset so triple-click doesn't register as another double\n if (isDouble) {\n state.lastClickTime = 0\n }\n\n return isDouble\n}\n\n// ============================================================================\n// Mouse Enter/Leave Tracking\n// ============================================================================\n\n/**\n * Compute mouseenter/mouseleave transitions between two ancestor paths.\n *\n * Returns { entered, left } — arrays of nodes that were entered or left.\n * Mirrors the DOM spec: fire mouseleave on nodes in prevPath not in nextPath,\n * and mouseenter on nodes in nextPath not in prevPath.\n */\nexport function computeEnterLeave(prevPath: AgNode[], nextPath: AgNode[]): { entered: AgNode[]; left: AgNode[] } {\n const prevSet = new Set(prevPath)\n const nextSet = new Set(nextPath)\n\n const entered = nextPath.filter((n) => !prevSet.has(n))\n const left = prevPath.filter((n) => !nextSet.has(n))\n\n return { entered, left }\n}\n\n// ============================================================================\n// High-Level Mouse Event Processor\n// ============================================================================\n\n/**\n * Options for creating a mouse event processor.\n */\nexport interface MouseEventProcessorOptions {\n /** Optional focus manager — enables click-to-focus behavior.\n * On mousedown, the deepest focusable ancestor of the hit target is focused. */\n focusManager?: FocusManager\n}\n\n/**\n * State for the mouse event processor.\n */\n/**\n * Keyboard modifier state tracked from Kitty protocol key events.\n * Merged into mouse events to provide accurate modifier detection\n * (SGR mouse protocol reports Ctrl/Alt/Shift but NOT Cmd/Super).\n */\nexport interface KeyboardModifierState {\n super: boolean\n hyper: boolean\n capsLock: boolean\n numLock: boolean\n}\n\nexport interface MouseEventProcessorState {\n doubleClick: DoubleClickState\n /** Previous hover path (for enter/leave tracking) */\n hoverPath: AgNode[]\n /** Whether the left button is currently down (for click detection) */\n mouseDownTarget: AgNode | null\n /** Optional focus manager for click-to-focus */\n focusManager?: FocusManager\n /** Modifier state from Kitty keyboard events, merged into mouse events */\n keyboardModifiers: KeyboardModifierState\n}\n\nexport function createMouseEventProcessor(options?: MouseEventProcessorOptions): MouseEventProcessorState {\n return {\n doubleClick: createDoubleClickState(),\n hoverPath: [],\n mouseDownTarget: null,\n focusManager: options?.focusManager,\n keyboardModifiers: { super: false, hyper: false, capsLock: false, numLock: false },\n }\n}\n\n/**\n * Update keyboard modifier state from a parsed key event.\n * Call this for every keyboard event so mouse events can include accurate modifiers.\n */\nexport function updateKeyboardModifiers(\n state: MouseEventProcessorState,\n key: {\n super?: boolean\n hyper?: boolean\n capsLock?: boolean\n numLock?: boolean\n eventType?: string\n },\n): void {\n // On key release events, clear the modifier. On press/repeat, set it.\n const isRelease = key.eventType === \"release\"\n const prevSuper = state.keyboardModifiers.super\n if (key.super !== undefined) state.keyboardModifiers.super = isRelease ? false : key.super\n if (key.hyper !== undefined) state.keyboardModifiers.hyper = isRelease ? false : key.hyper\n if (key.capsLock !== undefined) state.keyboardModifiers.capsLock = key.capsLock\n if (key.numLock !== undefined) state.keyboardModifiers.numLock = key.numLock\n if (state.keyboardModifiers.super !== prevSuper) {\n mouseLog.debug?.(\n `keyboardModifiers.super: ${prevSuper} → ${state.keyboardModifiers.super} (key.super=${key.super}, eventType=${key.eventType})`,\n )\n }\n}\n\n/**\n * Process a raw ParsedMouse event and dispatch DOM-level events on the render tree.\n *\n * Call this for every SGR mouse event received. It handles:\n * - mousedown / mouseup\n * - click (on mouseup if same target as mousedown)\n * - dblclick (based on timing)\n * - mousemove + mouseenter/mouseleave\n * - wheel\n */\nexport function processMouseEvent(state: MouseEventProcessorState, parsed: ParsedMouse, root: AgNode): boolean {\n const { x, y, action } = parsed\n const target = hitTest(root, x, y)\n if (action === \"move\") {\n const nodeType = target?.type ?? \"null\"\n const nodeId = target ? ((target.props as Record<string, unknown>).id ?? \"\") : \"\"\n // Check entire ancestor path for onMouseEnter\n let enterAncestor = \"\"\n if (target) {\n let n: AgNode | null = target\n while (n) {\n if (\"onMouseEnter\" in (n.props as Record<string, unknown>)) {\n enterAncestor = `${n.type}#${(n.props as Record<string, unknown>).id ?? \"\"}`\n break\n }\n n = n.parent\n }\n }\n const newPath = target ? getAncestorPath(target) : []\n const { entered } = computeEnterLeave(state.hoverPath, newPath)\n mouseLog.debug?.(\n `move x=${x} y=${y} target=${nodeType}#${nodeId} enterAncestor=${enterAncestor || \"none\"} entered=${entered.length} prevPath=${state.hoverPath.length}`,\n )\n }\n if (!target) return false\n let defaultPrevented = false\n\n if (action === \"down\") {\n state.mouseDownTarget = target\n\n // Set armed state on the target node\n setArmed(target, true)\n\n // Click-to-focus: find nearest focusable ancestor and focus it\n if (state.focusManager) {\n const focusable = findFocusableAncestor(target)\n if (focusable) {\n state.focusManager.focus(focusable, \"mouse\")\n }\n }\n\n const event = createMouseEvent(\"mousedown\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(event)\n if (event.defaultPrevented) defaultPrevented = true\n } else if (action === \"up\") {\n // Clear armed state on the mousedown target\n if (state.mouseDownTarget) {\n setArmed(state.mouseDownTarget, false)\n }\n\n const event = createMouseEvent(\"mouseup\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(event)\n\n // Click = mouseup on the same node (or ancestor) where mousedown happened\n // DOM actually fires click even if up is on a different element, but the target\n // is the nearest common ancestor. For simplicity, we fire click on the up target\n // if mousedown was on the same target or a descendant.\n if (state.mouseDownTarget) {\n const clickEvent = createMouseEvent(\"click\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(clickEvent)\n if (clickEvent.defaultPrevented) defaultPrevented = true\n\n // Check for double-click\n const isDouble = checkDoubleClick(state.doubleClick, x, y, parsed.button)\n if (isDouble) {\n const dblEvent = createMouseEvent(\"dblclick\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(dblEvent)\n if (dblEvent.defaultPrevented) defaultPrevented = true\n }\n }\n\n state.mouseDownTarget = null\n } else if (action === \"move\") {\n const event = createMouseEvent(\"mousemove\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(event)\n\n // Compute enter/leave transitions\n const newPath = getAncestorPath(target)\n const { entered, left } = computeEnterLeave(state.hoverPath, newPath)\n\n // Fire mouseleave on nodes that were left (reverse order = deepest first)\n for (const node of left) {\n setHovered(node, false)\n const leaveEvent = createMouseEvent(\"mouseleave\", x, y, node, parsed, state.keyboardModifiers)\n dispatchMouseEvent(leaveEvent)\n }\n\n // Fire mouseenter on newly entered nodes (forward order = shallowest first)\n for (const node of entered.reverse()) {\n setHovered(node, true)\n const enterEvent = createMouseEvent(\"mouseenter\", x, y, node, parsed, state.keyboardModifiers)\n dispatchMouseEvent(enterEvent)\n }\n\n state.hoverPath = newPath\n } else if (action === \"wheel\") {\n const event = createWheelEvent(x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(event)\n if (event.defaultPrevented) defaultPrevented = true\n }\n return defaultPrevented\n}\n","/**\n * silvery Inspector — Debug introspection for rendering pipeline.\n *\n * Activate with SILVERY_DEV=1 env var or by calling enableInspector().\n * Outputs debug info to stderr or a log file (never to the TUI stdout).\n *\n * Features:\n * - Component tree dump (with layout rects)\n * - Focus path display\n * - Render stats (frame time, dirty rows, cell changes)\n * - Dirty region visualization\n *\n * This is DISTINCT from React DevTools (devtools.ts). This inspector provides\n * silvery-specific introspection: render pipeline stats, focus tree, dirty regions,\n * layout info.\n */\n\nimport type { createWriteStream as createWriteStreamType } from \"node:fs\"\nimport type { RenderStats } from \"./scheduler\"\nimport type { AgNode } from \"@silvery/ag/types\"\nimport { isDirty, CONTENT_BIT, STYLE_PROPS_BIT, BG_BIT, SUBTREE_BIT, CHILDREN_BIT } from \"@silvery/ag/epoch\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface InspectorOptions {\n /** Output stream (default: process.stderr) */\n output?: NodeJS.WritableStream\n /** Log file path (overrides output stream) */\n logFile?: string\n /** Include layout rects in tree dump */\n showLayout?: boolean\n /** Include style info in tree dump */\n showStyles?: boolean\n}\n\n// =============================================================================\n// State\n// =============================================================================\n\nlet inspectorEnabled = false\nlet inspectorOutput: NodeJS.WritableStream = process.stderr\n\n// =============================================================================\n// Public API\n// =============================================================================\n\n/** Enable the silvery inspector. */\nexport function enableInspector(options?: InspectorOptions): void {\n inspectorEnabled = true\n if (options?.logFile) {\n // Dynamic require to avoid pulling in fs for non-inspector users\n const fs: { createWriteStream: typeof createWriteStreamType } = require(\"node:fs\")\n inspectorOutput = fs.createWriteStream(options.logFile, { flags: \"a\" })\n } else if (options?.output) {\n inspectorOutput = options.output\n } else {\n inspectorOutput = process.stderr\n }\n}\n\n/** Disable the inspector. */\nexport function disableInspector(): void {\n inspectorEnabled = false\n}\n\n/** Check if inspector is active. */\nexport function isInspectorEnabled(): boolean {\n return inspectorEnabled\n}\n\n/**\n * Log render stats after each frame.\n *\n * Called by the scheduler after executeRender completes. When the inspector\n * is disabled this is a no-op (zero overhead).\n */\nexport function inspectFrame(stats: RenderStats): void {\n if (!inspectorEnabled) return\n const line =\n `[silvery] frame #${stats.renderCount} ` +\n `${stats.lastRenderTime.toFixed(1)}ms ` +\n `avg=${stats.avgRenderTime.toFixed(1)}ms ` +\n `skipped=${stats.skippedCount}\\n`\n inspectorOutput.write(line)\n}\n\n/**\n * Dump the component tree structure as indented text.\n *\n * Walks the SilveryNode tree and formats each node with its type, testID,\n * layout rect, and dirty flags.\n */\nexport function inspectTree(rootNode: AgNode, options?: { depth?: number; showLayout?: boolean }): string {\n const maxDepth = options?.depth ?? 10\n const showLayout = options?.showLayout ?? true\n const lines: string[] = []\n\n function walk(node: AgNode, indent: number): void {\n if (indent > maxDepth) return\n\n const prefix = \" \".repeat(indent)\n const type = node.type\n const testID = (node.props as Record<string, unknown>)?.testID\n const idStr = testID ? ` #${testID}` : \"\"\n\n // Layout rect from computed layout node or boxRect\n let rectStr = \"\"\n if (showLayout) {\n if (node.boxRect) {\n const r = node.boxRect\n rectStr = ` [${r.x},${r.y} ${r.width}x${r.height}]`\n } else if (node.layoutNode) {\n const ln = node.layoutNode\n rectStr = ` [${ln.getComputedLeft()},${ln.getComputedTop()} ${ln.getComputedWidth()}x${ln.getComputedHeight()}]`\n }\n }\n\n // Dirty flags\n const dirtyFlags: string[] = []\n if (node.layoutDirty) dirtyFlags.push(\"layout\")\n if (isDirty(node.dirtyBits, node.dirtyEpoch, CONTENT_BIT)) dirtyFlags.push(\"content\")\n if (isDirty(node.dirtyBits, node.dirtyEpoch, STYLE_PROPS_BIT)) dirtyFlags.push(\"paint\")\n if (isDirty(node.dirtyBits, node.dirtyEpoch, BG_BIT)) dirtyFlags.push(\"bg\")\n if (isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT)) dirtyFlags.push(\"subtree\")\n if (isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT)) dirtyFlags.push(\"children\")\n const dirtyStr = dirtyFlags.length > 0 ? ` dirty=[${dirtyFlags.join(\",\")}]` : \"\"\n\n // Text content (for text nodes)\n const textStr = node.textContent\n ? ` \"${node.textContent.slice(0, 30)}${node.textContent.length > 30 ? \"...\" : \"\"}\"`\n : \"\"\n\n lines.push(`${prefix}${type}${idStr}${rectStr}${dirtyStr}${textStr}`)\n\n for (const child of node.children) {\n walk(child, indent + 1)\n }\n }\n\n walk(rootNode, 0)\n return lines.join(\"\\n\")\n}\n\n/**\n * Auto-enable if SILVERY_DEV=1 is set.\n *\n * Call this at startup to respect the environment variable convention.\n */\nexport function autoEnableInspector(): void {\n if (process.env.SILVERY_DEV === \"1\" || process.env.SILVERY_DEV === \"true\") {\n const logFile = process.env.SILVERY_DEV_LOG\n enableInspector(logFile ? { logFile } : undefined)\n }\n}\n","/**\n * @silvery/core — TEA runtime, React reconciler, store, and effect system.\n *\n * Portable core that doesn't depend on terminal specifics. Can target\n * terminal, canvas, DOM, or any custom render backend.\n *\n * @packageDocumentation\n */\n\n/**\n * silvery/core — Pure functions and types, NO React dependency.\n *\n * This sub-path export provides:\n * - TEA (The Elm Architecture) types: SilveryModel, SilveryMsg, Effect, Sub, Plugin\n * - Focus manager: createFocusManager + types\n * - Focus events: event factories + dispatch functions\n * - Focus queries: tree query functions\n * - Plugin composition: compose() for middleware-style plugin chaining\n *\n * Everything here is pure TypeScript — no React import anywhere in the\n * dependency graph. Safe for use in non-React contexts (CLI tools, tests,\n * server-side logic).\n *\n * @packageDocumentation\n */\n\n// =============================================================================\n// TEA Types (The Elm Architecture)\n// =============================================================================\n\nimport type { FocusOrigin } from \"../focus-manager.js\"\nexport type { FocusOrigin } from \"../focus-manager.js\"\n\n/**\n * The model type that silvery manages for focus state.\n *\n * Applications extend this with their own model fields.\n * The store's update function receives the full model and returns\n * a new model + effects tuple.\n */\nexport interface SilveryModel {\n focus: {\n activeId: string | null\n previousId: string | null\n origin: FocusOrigin | null\n scopeStack: string[]\n scopeMemory: Record<string, string>\n }\n}\n\n/**\n * Direction type used in spatial navigation messages.\n */\nexport type Direction = \"up\" | \"down\" | \"left\" | \"right\"\n\n/**\n * Message types that silvery understands.\n *\n * Applications can extend this union with their own message types.\n * The store's update function pattern-matches on `type` to decide\n * how to update the model.\n */\nexport type SilveryMsg =\n | { type: \"focus\"; nodeId: string; origin?: FocusOrigin }\n | { type: \"blur\" }\n | { type: \"focus-next\" }\n | { type: \"focus-prev\" }\n | { type: \"focus-direction\"; direction: Direction }\n | { type: \"scope-enter\"; scopeId: string }\n | { type: \"scope-exit\" }\n | {\n type: \"term:key\"\n key: string\n input: string\n ctrl: boolean\n meta: boolean\n shift: boolean\n }\n | {\n type: \"term:mouse\"\n action: \"down\" | \"up\" | \"move\" | \"scroll\"\n x: number\n y: number\n button: number\n }\n | { type: \"term:resize\"; cols: number; rows: number }\n\n/**\n * Effect commands returned by update functions.\n *\n * Effects are declarative descriptions of side effects. The store\n * executes them after the model update, keeping the update function pure.\n *\n * - `none`: No effect (useful as a default)\n * - `batch`: Multiple effects to execute\n * - `dispatch`: Queue another message (no re-entrant dispatch)\n */\nexport type Effect = { type: \"none\" } | { type: \"batch\"; effects: Effect[] } | { type: \"dispatch\"; msg: SilveryMsg }\n\n/**\n * Subscription descriptor (for future use).\n *\n * Subscriptions represent long-running side effects (timers, event listeners)\n * that produce messages over time. The store manages their lifecycle.\n */\nexport type Sub = {\n type: \"none\"\n}\n\n// =============================================================================\n// Effect Constructors\n// =============================================================================\n\n/** No-op effect. */\nexport const none: Effect = { type: \"none\" }\n\n/** Batch multiple effects. */\nexport function batch(...effects: Effect[]): Effect {\n // Flatten nested batches and filter out none effects\n const flat: Effect[] = []\n for (const e of effects) {\n if (e.type === \"none\") continue\n if (e.type === \"batch\") {\n flat.push(...e.effects)\n } else {\n flat.push(e)\n }\n }\n if (flat.length === 0) return none\n if (flat.length === 1) return flat[0]!\n return { type: \"batch\", effects: flat }\n}\n\n/** Queue a message dispatch as an effect. */\nexport function dispatch(msg: SilveryMsg): Effect {\n return { type: \"dispatch\", msg }\n}\n\n// =============================================================================\n// Plugin Type (Middleware Composition)\n// =============================================================================\n\n/**\n * A plugin wraps an update function, adding behavior before/after/around it.\n *\n * Plugins compose via `compose()` — the outermost plugin runs first on\n * message receive, but the innermost (original) update runs first for\n * model updates. This is the standard middleware pattern.\n *\n * @example\n * ```ts\n * const logging: Plugin<MyModel, MyMsg> = (inner) => (msg, model) => {\n * console.log('msg:', msg.type)\n * const result = inner(msg, model)\n * console.log('new model:', result[0])\n * return result\n * }\n * ```\n */\nexport type Plugin<Model, Msg> = (\n innerUpdate: (msg: Msg, model: Model) => [Model, Effect[]],\n) => (msg: Msg, model: Model) => [Model, Effect[]]\n\n/**\n * Compose multiple plugins into a single update function wrapper.\n *\n * Plugins are applied right-to-left (innermost first), so the first\n * plugin in the array is the outermost wrapper — it sees messages first\n * and model changes last.\n *\n * @example\n * ```ts\n * const update = compose(logging, focusNav, spatialNav)(baseUpdate)\n * // Equivalent to: logging(focusNav(spatialNav(baseUpdate)))\n * ```\n */\nexport function compose<Model, Msg>(...plugins: Plugin<Model, Msg>[]): Plugin<Model, Msg> {\n return (innerUpdate) => {\n let update = innerUpdate\n // Apply right-to-left so first plugin is outermost\n for (let i = plugins.length - 1; i >= 0; i--) {\n update = plugins[i]!(update)\n }\n return update\n }\n}\n\n// =============================================================================\n// Focus Manager (pure, no React)\n// =============================================================================\n\nexport { createFocusManager } from \"../focus-manager.js\"\nexport type { FocusManager, FocusManagerOptions, FocusChangeCallback, FocusSnapshot } from \"../focus-manager.js\"\n\n// =============================================================================\n// Focus Events (pure, no React)\n// =============================================================================\n\nexport { createKeyEvent, createFocusEvent, dispatchKeyEvent, dispatchFocusEvent } from \"../focus-events.js\"\nexport type { SilveryKeyEvent, SilveryFocusEvent, FocusEventProps } from \"../focus-events.js\"\n\n// =============================================================================\n// Focus Queries (pure, no React)\n// =============================================================================\n\nexport {\n findFocusableAncestor,\n getTabOrder,\n findByTestID,\n findSpatialTarget,\n getExplicitFocusLink,\n} from \"@silvery/ag/focus-queries\"\n\n// =============================================================================\n// Slices (ops-as-data helper)\n// =============================================================================\n\nexport { createSlice } from \"./slice.js\"\nexport type { Slice, SliceWithInit, InferOp } from \"./slice.js\"\n\n// =============================================================================\n// Shared Types (pure)\n// =============================================================================\n\nexport type { AgNode, Rect } from \"../types.js\"\n","/**\n * silvery/store — TEA-style state container with effects.\n *\n * Wraps FocusManager into a dispatch/subscribe loop following\n * The Elm Architecture (TEA): Model + Msg -> [Model, Effect[]].\n *\n * The store provides:\n * - `dispatch(msg)` — send a message, run update, execute effects\n * - `getModel()` — current model snapshot\n * - `subscribe(listener)` — for useSyncExternalStore integration\n * - `getSnapshot(selector)` — selector-based access\n *\n * Effects are executed after each update cycle. `dispatch` effects\n * are queued (not re-entrant) to prevent stack overflow.\n *\n * @packageDocumentation\n */\n\nimport type { Effect, SilveryModel, SilveryMsg, Plugin } from \"../core\"\nimport { none } from \"../core\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Configuration for creating a store.\n */\nexport interface StoreConfig<Model extends SilveryModel, Msg extends SilveryMsg> {\n /** Initialize model and effects. Called once on store creation. */\n init: () => [Model, Effect[]]\n /** Pure update function: (msg, model) -> [newModel, effects] */\n update: (msg: Msg, model: Model) => [Model, Effect[]]\n}\n\n/**\n * The store API returned by createStore.\n */\nexport interface StoreApi<Model extends SilveryModel, Msg extends SilveryMsg> {\n /** Send a message through the update function. */\n dispatch(msg: Msg): void\n /** Get the current model. */\n getModel(): Model\n /** Subscribe to model changes. Returns unsubscribe function. */\n subscribe(listener: () => void): () => void\n /** Get a derived value from the model via selector. */\n getSnapshot<T>(selector: (model: Model) => T): T\n}\n\n// =============================================================================\n// Built-in Plugins\n// =============================================================================\n\n/**\n * Plugin that handles focus-related messages by updating the focus slice.\n *\n * Handles: focus, blur, scope-enter, scope-exit.\n * Passes focus-next, focus-prev, focus-direction through (they need\n * the node tree, which the store doesn't have — those are handled\n * at the React integration layer).\n */\nexport function withFocusManagement<Model extends SilveryModel, Msg extends SilveryMsg>(): Plugin<Model, Msg> {\n return (innerUpdate) => (msg, model) => {\n switch (msg.type) {\n case \"focus\": {\n const focusMsg = msg as Extract<SilveryMsg, { type: \"focus\" }>\n const newModel = {\n ...model,\n focus: {\n ...model.focus,\n previousId: model.focus.activeId,\n activeId: focusMsg.nodeId,\n origin: focusMsg.origin ?? \"programmatic\",\n // Remember in current scope\n scopeMemory:\n model.focus.scopeStack.length > 0\n ? {\n ...model.focus.scopeMemory,\n [model.focus.scopeStack[model.focus.scopeStack.length - 1]!]: focusMsg.nodeId,\n }\n : model.focus.scopeMemory,\n },\n }\n return [newModel as Model, [none]]\n }\n\n case \"blur\": {\n const newModel = {\n ...model,\n focus: {\n ...model.focus,\n previousId: model.focus.activeId,\n activeId: null,\n origin: null,\n },\n }\n return [newModel as Model, [none]]\n }\n\n case \"scope-enter\": {\n const scopeMsg = msg as Extract<SilveryMsg, { type: \"scope-enter\" }>\n const newModel = {\n ...model,\n focus: {\n ...model.focus,\n scopeStack: [...model.focus.scopeStack, scopeMsg.scopeId],\n },\n }\n return [newModel as Model, [none]]\n }\n\n case \"scope-exit\": {\n const newModel = {\n ...model,\n focus: {\n ...model.focus,\n scopeStack: model.focus.scopeStack.slice(0, -1),\n },\n }\n return [newModel as Model, [none]]\n }\n\n default:\n return innerUpdate(msg, model)\n }\n }\n}\n\n// =============================================================================\n// Default Update\n// =============================================================================\n\n/**\n * The default silvery update function.\n *\n * Returns the model unchanged with no effects for any unhandled message.\n * Compose with plugins to add behavior.\n */\nexport function silveryUpdate<Model extends SilveryModel, Msg extends SilveryMsg>(\n _msg: Msg,\n model: Model,\n): [Model, Effect[]] {\n return [model, [none]]\n}\n\n// =============================================================================\n// Default Init\n// =============================================================================\n\n/**\n * Create a default initial SilveryModel.\n */\nexport function defaultInit(): [SilveryModel, Effect[]] {\n return [\n {\n focus: {\n activeId: null,\n previousId: null,\n origin: null,\n scopeStack: [],\n scopeMemory: {},\n },\n },\n [none],\n ]\n}\n\n// =============================================================================\n// Store Factory\n// =============================================================================\n\n/**\n * Create a TEA-style store.\n *\n * The store manages model state and effect execution. Messages are\n * dispatched through the update function, which returns a new model\n * and a list of effects. Effects are executed after each update cycle.\n *\n * Dispatch effects are queued to prevent re-entrant dispatch:\n * if dispatching msg A triggers effect dispatch(B), B is queued and\n * processed after A's full update cycle completes.\n *\n * @example\n * ```ts\n * import { createStore, withFocusManagement, silveryUpdate } from '@silvery/create/store'\n * import { compose } from '@silvery/create/core'\n *\n * const store = createStore({\n * init: () => [{ focus: { activeId: null, ... }, count: 0 }, []],\n * update: compose(withFocusManagement())(silveryUpdate),\n * })\n *\n * store.dispatch({ type: 'focus', nodeId: 'btn1' })\n * console.log(store.getModel().focus.activeId) // 'btn1'\n * ```\n */\nexport function createStore<Model extends SilveryModel, Msg extends SilveryMsg>(\n config: StoreConfig<Model, Msg>,\n): StoreApi<Model, Msg> {\n // Initialize\n const [initialModel, initialEffects] = config.init()\n let model = initialModel\n\n // Subscriber management\n const listeners = new Set<() => void>()\n\n function notify(): void {\n for (const listener of listeners) {\n listener()\n }\n }\n\n // Effect execution with queue for dispatch effects\n let isDispatching = false\n const dispatchQueue: Msg[] = []\n\n function executeEffects(effects: Effect[]): void {\n for (const effect of effects) {\n executeEffect(effect)\n }\n }\n\n function executeEffect(effect: Effect): void {\n switch (effect.type) {\n case \"none\":\n break\n case \"batch\":\n executeEffects(effect.effects)\n break\n case \"dispatch\":\n // Queue dispatch effects to prevent re-entrant dispatch\n dispatchQueue.push(effect.msg as Msg)\n break\n }\n }\n\n function dispatch(msg: Msg): void {\n if (isDispatching) {\n // Queue if we're already in a dispatch cycle\n dispatchQueue.push(msg)\n return\n }\n\n isDispatching = true\n try {\n // Run update\n const [newModel, effects] = config.update(msg, model)\n const changed = newModel !== model\n model = newModel\n\n // Execute effects (may queue more dispatches)\n executeEffects(effects)\n\n // Notify subscribers if model changed\n if (changed) {\n notify()\n }\n\n // Process queued dispatches\n while (dispatchQueue.length > 0) {\n const queued = dispatchQueue.shift()!\n const [nextModel, nextEffects] = config.update(queued, model)\n const nextChanged = nextModel !== model\n model = nextModel\n executeEffects(nextEffects)\n if (nextChanged) {\n notify()\n }\n }\n } finally {\n isDispatching = false\n }\n }\n\n function getModel(): Model {\n return model\n }\n\n function subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n }\n\n function getSnapshot<T>(selector: (model: Model) => T): T {\n return selector(model)\n }\n\n // Execute initial effects and drain any queued dispatches\n executeEffects(initialEffects)\n while (dispatchQueue.length > 0) {\n const queued = dispatchQueue.shift()!\n const [nextModel, nextEffects] = config.update(queued, model)\n model = nextModel\n executeEffects(nextEffects)\n // No notify during init — no subscribers yet\n }\n\n return {\n dispatch,\n getModel,\n subscribe,\n getSnapshot,\n }\n}\n","/**\n * AsyncIterable stream helpers for event-driven TUI architecture.\n *\n * These are pure functions over AsyncIterables - no EventEmitters, no callbacks.\n * All helpers properly handle cleanup via return() on early break.\n *\n * @example\n * ```typescript\n * const keys = term.keys()\n * const resizes = term.resizes()\n *\n * // Merge multiple sources\n * const events = merge(\n * map(keys, k => ({ type: 'key', ...k })),\n * map(resizes, r => ({ type: 'resize', ...r }))\n * )\n *\n * // Consume until ctrl+c\n * for await (const event of events) {\n * if (event.type === 'key' && event.key === 'ctrl+c') break\n * }\n * ```\n */\n\n/**\n * Merge multiple AsyncIterables into one.\n *\n * Values are emitted in arrival order (first-come). When all sources complete,\n * the merged iterable completes. If any source throws, the error propagates\n * and remaining sources are cleaned up.\n *\n * IMPORTANT: Each call to merge() creates a fresh iterable. Don't share\n * the same merged iterable between multiple consumers.\n *\n * @example\n * ```typescript\n * const merged = merge(keys, resizes, ticks)\n * for await (const event of merged) {\n * // Process events from any source\n * }\n * ```\n */\nexport async function* merge<T>(...sources: AsyncIterable<T>[]): AsyncGenerator<T, void, undefined> {\n if (sources.length === 0) return\n\n // Track active iterators and their pending promises\n const iterators = sources.map((source) => source[Symbol.asyncIterator]())\n const pending = new Map<number, Promise<{ index: number; result: IteratorResult<T, unknown> }>>()\n\n async function nextWithIndex(idx: number): Promise<{ index: number; result: IteratorResult<T, unknown> }> {\n const iterator = iterators[idx]\n if (!iterator) throw new Error(`No iterator at index ${idx}`)\n const result = await iterator.next()\n return { index: idx, result }\n }\n\n // Start all iterators\n for (let i = 0; i < iterators.length; i++) {\n pending.set(i, nextWithIndex(i))\n }\n\n try {\n while (pending.size > 0) {\n // Race all pending promises\n const { index, result } = await Promise.race(pending.values())\n\n if (result.done) {\n // This source is exhausted, remove it\n pending.delete(index)\n } else {\n // Yield the value and request next from this source\n yield result.value\n pending.set(index, nextWithIndex(index))\n }\n }\n } finally {\n // Clean up all iterators on early exit or error\n await Promise.all(iterators.map((it) => (it.return ? it.return() : Promise.resolve())))\n }\n}\n\n/**\n * Transform each value from an AsyncIterable.\n *\n * @example\n * ```typescript\n * const keyEvents = map(keys, k => ({ type: 'key' as const, key: k }))\n * ```\n */\nexport async function* map<T, U>(source: AsyncIterable<T>, fn: (value: T) => U): AsyncGenerator<U, void, undefined> {\n const iterator = source[Symbol.asyncIterator]()\n try {\n // Use the iterator directly to avoid double-iteration\n for await (const value of { [Symbol.asyncIterator]: () => iterator }) {\n yield fn(value)\n }\n } finally {\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Filter values from an AsyncIterable.\n *\n * @example\n * ```typescript\n * const letters = filter(keys, k => k.key.length === 1)\n * ```\n */\nexport async function* filter<T>(\n source: AsyncIterable<T>,\n predicate: (value: T) => boolean,\n): AsyncGenerator<T, void, undefined> {\n const iterator = source[Symbol.asyncIterator]()\n try {\n for await (const value of { [Symbol.asyncIterator]: () => iterator }) {\n if (predicate(value)) {\n yield value\n }\n }\n } finally {\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Filter and transform in one pass (type narrowing).\n *\n * @example\n * ```typescript\n * const keyEvents = filterMap(events, e =>\n * e.type === 'key' ? e : undefined\n * )\n * ```\n */\nexport async function* filterMap<T, U>(\n source: AsyncIterable<T>,\n fn: (value: T) => U | undefined,\n): AsyncGenerator<U, void, undefined> {\n const iterator = source[Symbol.asyncIterator]()\n try {\n for await (const value of { [Symbol.asyncIterator]: () => iterator }) {\n const mapped = fn(value)\n if (mapped !== undefined) {\n yield mapped\n }\n }\n } finally {\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Take values until an AbortSignal fires.\n *\n * When the signal aborts, the iterator completes gracefully (no error thrown).\n * The source iterator is properly cleaned up.\n *\n * @example\n * ```typescript\n * const controller = new AbortController()\n * const events = takeUntil(allEvents, controller.signal)\n *\n * // Later: controller.abort() will end the iteration\n * ```\n */\nexport async function* takeUntil<T>(source: AsyncIterable<T>, signal: AbortSignal): AsyncGenerator<T, void, undefined> {\n if (signal.aborted) return\n\n const iterator = source[Symbol.asyncIterator]()\n\n // Create a promise that resolves when signal aborts\n let abortResolve: () => void\n const abortPromise = new Promise<void>((resolve) => {\n abortResolve = resolve\n })\n const onAbort = () => abortResolve()\n signal.addEventListener(\"abort\", onAbort, { once: true })\n\n try {\n while (!signal.aborted) {\n // Race between next value and abort\n const result = await Promise.race([\n iterator.next(),\n abortPromise.then(() => ({ done: true, value: undefined }) as const),\n ])\n\n if (result.done) break\n yield result.value as T\n }\n } finally {\n signal.removeEventListener(\"abort\", onAbort)\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Take the first n values from an AsyncIterable.\n *\n * @example\n * ```typescript\n * const firstThree = take(events, 3)\n * ```\n */\nexport async function* take<T>(source: AsyncIterable<T>, count: number): AsyncGenerator<T, void, undefined> {\n if (count <= 0) return\n\n const iterator = source[Symbol.asyncIterator]()\n let taken = 0\n\n try {\n for await (const value of { [Symbol.asyncIterator]: () => iterator }) {\n yield value\n taken++\n if (taken >= count) break\n }\n } finally {\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Create an AsyncIterable from an array (useful for testing).\n *\n * @example\n * ```typescript\n * const events = fromArray([\n * { type: 'key', key: 'j' },\n * { type: 'key', key: 'k' },\n * ])\n * ```\n */\nexport async function* fromArray<T>(items: T[]): AsyncGenerator<T, void, undefined> {\n for (const item of items) {\n yield item\n }\n}\n\n/**\n * Create an AsyncIterable that yields after a delay (useful for testing).\n *\n * @example\n * ```typescript\n * const delayed = fromArrayWithDelay([1, 2, 3], 100) // 100ms between each\n * ```\n */\nexport async function* fromArrayWithDelay<T>(items: T[], delayMs: number): AsyncGenerator<T, void, undefined> {\n for (const item of items) {\n await new Promise((resolve) => setTimeout(resolve, delayMs))\n yield item\n }\n}\n\n/**\n * Throttle high-frequency sources.\n *\n * Emits the first value immediately, then ignores values for the specified\n * duration. After the duration, the next value is emitted and the cycle repeats.\n *\n * @example\n * ```typescript\n * const throttled = throttle(mouseMoves, 16) // ~60fps\n * ```\n */\nexport async function* throttle<T>(source: AsyncIterable<T>, ms: number): AsyncGenerator<T, void, undefined> {\n const iterator = source[Symbol.asyncIterator]()\n let lastEmit = 0\n\n try {\n for await (const value of { [Symbol.asyncIterator]: () => iterator }) {\n const now = Date.now()\n if (now - lastEmit >= ms) {\n lastEmit = now\n yield value\n }\n }\n } finally {\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Debounce values - only emit after source is quiet for specified duration.\n *\n * NOTE: With pull-based AsyncIterables, true debouncing is complex. This\n * implementation collects all values and only yields the final value after\n * the source completes and quiet period passes. For real-time debouncing,\n * consider using a push-based pattern or EventEmitter.\n *\n * @example\n * ```typescript\n * const debounced = debounce(searchInput, 300) // Yields last value after source ends + delay\n * ```\n */\nexport async function* debounce<T>(source: AsyncIterable<T>, ms: number): AsyncGenerator<T, void, undefined> {\n const iterator = source[Symbol.asyncIterator]()\n let last: { value: T } | undefined\n\n try {\n for await (const value of { [Symbol.asyncIterator]: () => iterator }) {\n last = { value }\n }\n\n if (last) {\n await new Promise((resolve) => setTimeout(resolve, ms))\n yield last.value\n }\n } finally {\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Collect values into batches of specified size.\n *\n * @throws {Error} If size is not positive\n *\n * @example\n * ```typescript\n * const batched = batch(events, 10) // Emit arrays of 10 events\n * ```\n */\nexport function batch<T>(source: AsyncIterable<T>, size: number): AsyncGenerator<T[], void, undefined> {\n if (size <= 0) throw new Error(\"Batch size must be positive\")\n return batchImpl(source, size)\n}\n\nasync function* batchImpl<T>(source: AsyncIterable<T>, size: number): AsyncGenerator<T[], void, undefined> {\n const iterator = source[Symbol.asyncIterator]()\n let buffer: T[] = []\n\n try {\n for await (const value of { [Symbol.asyncIterator]: () => iterator }) {\n buffer.push(value)\n if (buffer.length >= size) {\n yield buffer\n buffer = []\n }\n }\n // Emit remaining items\n if (buffer.length > 0) {\n yield buffer\n }\n } finally {\n if (iterator.return) {\n await iterator.return()\n }\n }\n}\n\n/**\n * Concatenate multiple AsyncIterables in sequence.\n *\n * @example\n * ```typescript\n * const all = concat(header, body, footer)\n * ```\n */\nexport async function* concat<T>(...sources: AsyncIterable<T>[]): AsyncGenerator<T, void, undefined> {\n for (const source of sources) {\n yield* source\n }\n}\n\n/**\n * Zip multiple AsyncIterables together.\n * Completes when the shortest source completes.\n *\n * @example\n * ```typescript\n * const pairs = zip(keys, timestamps) // [key, timestamp][]\n * ```\n */\nexport async function* zip<T extends unknown[]>(\n ...sources: { [K in keyof T]: AsyncIterable<T[K]> }\n): AsyncGenerator<T, void, undefined> {\n const iterators = sources.map((source) => source[Symbol.asyncIterator]())\n\n try {\n while (true) {\n const results = await Promise.all(iterators.map((it) => it.next()))\n\n // If any source is done, we're done\n if (results.some((r) => r.done)) break\n\n yield results.map((r) => r.value) as T\n }\n } finally {\n await Promise.all(iterators.map((it) => (it.return ? it.return() : Promise.resolve())))\n }\n}\n","/**\n * Capability Registry — symbol-keyed service registry for cross-feature communication.\n *\n * Each createApp instance gets its own registry. Interaction features use it to\n * discover each other (e.g., selection looks up clipboard capability).\n *\n * @internal Not exported from the public barrel.\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** Symbol-keyed service registry for cross-feature communication. */\nexport interface CapabilityRegistry {\n /** Register a capability. Overwrites any existing registration for the same key. */\n register<T>(key: symbol, capability: T): void\n\n /** Look up a capability. Returns undefined if not registered. */\n get<T>(key: symbol): T | undefined\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/** Create a new capability registry (one per app instance). */\nexport function createCapabilityRegistry(): CapabilityRegistry {\n const capabilities = new Map<symbol, unknown>()\n\n return {\n register<T>(key: symbol, capability: T): void {\n capabilities.set(key, capability)\n },\n\n get<T>(key: symbol): T | undefined {\n return capabilities.get(key) as T | undefined\n },\n }\n}\n","/**\n * Capability symbols — well-known keys for the CapabilityRegistry.\n *\n * Features register themselves under these symbols so other parts\n * of the composition chain can discover and interact with them.\n *\n * @internal Not exported from the public barrel.\n */\n\n/** Selection feature: text selection state + mouse handling. */\nexport const SELECTION_CAPABILITY = Symbol.for(\"silvery.selection\")\n\n/** Clipboard feature: copy/paste via OSC 52 or other backends. */\nexport const CLIPBOARD_CAPABILITY = Symbol.for(\"silvery.clipboard\")\n\n/** Copy-mode feature: keyboard-driven selection (Esc+v). */\nexport const COPY_MODE_CAPABILITY = Symbol.for(\"silvery.copy-mode\")\n\n/** Find feature: text search (Ctrl+F). */\nexport const FIND_CAPABILITY = Symbol.for(\"silvery.find\")\n\n/** Drag feature: drag-and-drop state + mouse handling. */\nexport const DRAG_CAPABILITY = Symbol.for(\"silvery.drag\")\n\n/** Input router: priority-based event dispatch for interaction features. */\nexport const INPUT_ROUTER = Symbol.for(\"silvery.input-router\")\n\n/** Color scheme detector: Mode 2031 dark/light reactive detection. */\nexport const COLOR_SCHEME_CAPABILITY = Symbol.for(\"silvery.color-scheme\")\n","/**\n * Selection state machine — pure TEA `(action, state) → [state, effects[]]`.\n *\n * Buffer-level text selection (like native terminal selection).\n * Operates on terminal buffer coordinates, not the React tree.\n *\n * Supports three granularity levels:\n * - character: default drag selection\n * - word: double-click then drag extends by words\n * - line: triple-click then drag extends by lines\n */\n\nimport type { TerminalBuffer, RowMetadata } from \"@silvery/ag-term/buffer\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface SelectionPosition {\n col: number\n row: number\n}\n\nexport interface SelectionRange {\n anchor: SelectionPosition\n head: SelectionPosition\n}\n\n/**\n * Rectangular boundary for contain-scoped selection.\n * Derived from the nearest `userSelect=\"contain\"` ancestor's scrollRect.\n */\nexport interface SelectionScope {\n top: number\n bottom: number\n left: number\n right: number\n}\n\nexport type SelectionGranularity = \"character\" | \"word\" | \"line\"\n\nexport interface TerminalSelectionState {\n range: SelectionRange | null\n /** True while mouse button is held */\n selecting: boolean\n /** Who initiated the selection */\n source: \"mouse\" | \"keyboard\" | null\n /** Current selection granularity */\n granularity: SelectionGranularity\n /** Contain boundary — selection range is clamped to this rect */\n scope: SelectionScope | null\n}\n\nexport type SelectionAction =\n | { type: \"start\"; col: number; row: number; scope?: SelectionScope | null; source?: \"mouse\" | \"keyboard\" }\n | {\n type: \"startWord\"\n col: number\n row: number\n buffer: TerminalBuffer\n scope?: SelectionScope | null\n source?: \"mouse\" | \"keyboard\"\n }\n | {\n type: \"startLine\"\n col: number\n row: number\n buffer: TerminalBuffer\n scope?: SelectionScope | null\n source?: \"mouse\" | \"keyboard\"\n }\n | { type: \"extend\"; col: number; row: number; buffer?: TerminalBuffer }\n | { type: \"finish\" }\n | { type: \"clear\" }\n\nexport type SelectionEffect = { type: \"copy\"; text: string } | { type: \"render\" }\n\n// ============================================================================\n// Word Boundary Detection\n// ============================================================================\n\n/**\n * Check if a character is a word character (not whitespace/punctuation).\n * Word chars: letters, digits, underscore (matching \\w).\n */\nfunction isWordChar(ch: string): boolean {\n return /\\w/.test(ch)\n}\n\n/**\n * Find word boundaries at a given column in the buffer row.\n * Returns { startCol, endCol } inclusive of the word.\n * If the position is on whitespace/punctuation, selects that single char.\n */\nexport function findWordBoundary(\n buffer: TerminalBuffer,\n col: number,\n row: number,\n): { startCol: number; endCol: number } {\n const width = buffer.width\n const ch = buffer.getCell(col, row).char\n\n if (isWordChar(ch)) {\n // Walk backwards to find word start\n let startCol = col\n while (startCol > 0 && isWordChar(buffer.getCell(startCol - 1, row).char)) {\n startCol--\n }\n // Walk forwards to find word end\n let endCol = col\n while (endCol < width - 1 && isWordChar(buffer.getCell(endCol + 1, row).char)) {\n endCol++\n }\n return { startCol, endCol }\n }\n\n // Non-word char: select just that character\n return { startCol: col, endCol: col }\n}\n\n// ============================================================================\n// Line Boundary Detection\n// ============================================================================\n\n/**\n * Find line boundaries for a given row.\n * Returns { startCol, endCol } spanning from first content to last content.\n * If the row is empty, returns { startCol: 0, endCol: width - 1 }.\n */\nexport function findLineBoundary(buffer: TerminalBuffer, row: number): { startCol: number; endCol: number } {\n const width = buffer.width\n\n // Find last non-space column\n let endCol = width - 1\n while (endCol > 0 && buffer.getCell(endCol, row).char.trim() === \"\") {\n endCol--\n }\n\n // Find first non-space column\n let startCol = 0\n while (startCol < endCol && buffer.getCell(startCol, row).char.trim() === \"\") {\n startCol++\n }\n\n // If entirely blank (including when startCol == endCol and that cell is blank), select the full row\n if (startCol >= endCol && buffer.getCell(startCol, row).char.trim() === \"\") {\n return { startCol: 0, endCol: width - 1 }\n }\n\n return { startCol, endCol }\n}\n\n// ============================================================================\n// Granularity-Aware Extend\n// ============================================================================\n\n/**\n * Extend the head position according to the current granularity.\n * For word granularity: snaps to word boundaries.\n * For line granularity: snaps to line boundaries.\n */\nfunction extendByGranularity(\n col: number,\n row: number,\n anchor: SelectionPosition,\n granularity: SelectionGranularity,\n buffer?: TerminalBuffer,\n): SelectionPosition {\n if (granularity === \"character\" || !buffer) {\n return { col, row }\n }\n\n if (granularity === \"word\") {\n const { startCol, endCol } = findWordBoundary(buffer, col, row)\n // Extend towards the anchor direction\n const isForward = row > anchor.row || (row === anchor.row && col >= anchor.col)\n return isForward ? { col: endCol, row } : { col: startCol, row }\n }\n\n if (granularity === \"line\") {\n const { startCol, endCol } = findLineBoundary(buffer, row)\n const isForward = row > anchor.row || (row === anchor.row && col >= anchor.col)\n return isForward ? { col: endCol, row } : { col: startCol, row }\n }\n\n return { col, row }\n}\n\n// ============================================================================\n// State\n// ============================================================================\n\nexport function createTerminalSelectionState(): TerminalSelectionState {\n return { range: null, selecting: false, source: null, granularity: \"character\", scope: null }\n}\n\n// ============================================================================\n// Update\n// ============================================================================\n\n/**\n * Clamp a position to a scope boundary.\n */\nfunction clampToScope(col: number, row: number, scope: SelectionScope | null): SelectionPosition {\n if (!scope) return { col, row }\n return {\n col: Math.max(scope.left, Math.min(scope.right, col)),\n row: Math.max(scope.top, Math.min(scope.bottom, row)),\n }\n}\n\nexport function terminalSelectionUpdate(\n action: SelectionAction,\n state: TerminalSelectionState,\n): [TerminalSelectionState, SelectionEffect[]] {\n switch (action.type) {\n case \"start\": {\n const scope = action.scope ?? null\n const pos = clampToScope(action.col, action.row, scope)\n return [\n {\n range: { anchor: pos, head: pos },\n selecting: true,\n source: action.source ?? \"mouse\",\n granularity: \"character\",\n scope,\n },\n [{ type: \"render\" }],\n ]\n }\n\n case \"startWord\": {\n const scope = action.scope ?? null\n const { startCol, endCol } = findWordBoundary(action.buffer, action.col, action.row)\n const anchorPos = clampToScope(startCol, action.row, scope)\n const headPos = clampToScope(endCol, action.row, scope)\n return [\n {\n range: { anchor: anchorPos, head: headPos },\n selecting: true,\n source: action.source ?? \"mouse\",\n granularity: \"word\",\n scope,\n },\n [{ type: \"render\" }],\n ]\n }\n\n case \"startLine\": {\n const scope = action.scope ?? null\n const { startCol, endCol } = findLineBoundary(action.buffer, action.row)\n const anchorPos = clampToScope(startCol, action.row, scope)\n const headPos = clampToScope(endCol, action.row, scope)\n return [\n {\n range: { anchor: anchorPos, head: headPos },\n selecting: true,\n source: action.source ?? \"mouse\",\n granularity: \"line\",\n scope,\n },\n [{ type: \"render\" }],\n ]\n }\n\n case \"extend\": {\n if (!state.selecting) return [state, []]\n const extended = extendByGranularity(\n action.col,\n action.row,\n state.range!.anchor,\n state.granularity,\n action.buffer,\n )\n const head = clampToScope(extended.col, extended.row, state.scope)\n return [\n {\n ...state,\n range: { anchor: state.range!.anchor, head },\n selecting: true,\n },\n [{ type: \"render\" }],\n ]\n }\n\n case \"finish\": {\n if (!state.range) return [{ ...state, selecting: false }, []]\n return [{ ...state, range: state.range, selecting: false }, []]\n }\n\n case \"clear\": {\n const hadRange = state.range !== null\n return [createTerminalSelectionState(), hadRange ? [{ type: \"render\" }] : []]\n }\n }\n}\n\n// ============================================================================\n// Range Normalization\n// ============================================================================\n\nexport function normalizeRange(range: SelectionRange): {\n startRow: number\n startCol: number\n endRow: number\n endCol: number\n} {\n const { anchor, head } = range\n\n if (anchor.row < head.row || (anchor.row === head.row && anchor.col <= head.col)) {\n return { startRow: anchor.row, startCol: anchor.col, endRow: head.row, endCol: head.col }\n }\n\n return { startRow: head.row, startCol: head.col, endRow: anchor.row, endCol: anchor.col }\n}\n\n// ============================================================================\n// Text Extraction\n// ============================================================================\n\nexport interface ExtractTextOptions {\n /** When true, skip cells that don't have SELECTABLE_FLAG set */\n respectSelectableFlag?: boolean\n /** Row metadata for soft-wrap handling and precise trailing space trimming */\n rowMetadata?: readonly RowMetadata[]\n /**\n * Contain scope. When set, every row's col range is clamped to\n * `[scope.left, scope.right]` so the extracted text cannot include cells\n * outside the `userSelect=\"contain\"` ancestor's rect — even across the\n * interior rows of a multi-row selection.\n */\n scope?: SelectionScope | null\n}\n\n/**\n * Extract text from a buffer within a selection range.\n *\n * Handles:\n * - Soft-wrap joining (via RowMetadata.softWrapped)\n * - Trailing space trimming (via RowMetadata.lastContentCol or content scan)\n * - Blank line preservation within selection\n * - Wide-char continuation cell skipping\n * - SELECTABLE_FLAG filtering (when respectSelectableFlag is true)\n * - Contain-scope clipping (when options.scope is set)\n */\nexport function extractText(buffer: TerminalBuffer, range: SelectionRange, options?: ExtractTextOptions): string {\n const { startRow, startCol, endRow, endCol } = normalizeRange(range)\n const respectSelectable = options?.respectSelectableFlag ?? false\n const rowMeta = options?.rowMetadata\n const scope = options?.scope\n\n const parts: string[] = []\n\n for (let row = startRow; row <= endRow; row++) {\n let colStart = row === startRow ? startCol : 0\n let colEnd = row === endRow ? endCol : buffer.width - 1\n // Clip to contain scope on every row, not just the anchor/head rows.\n // Without this, multi-row selections inside a contain ancestor would\n // still grab full-width cells on interior rows.\n if (scope) {\n colStart = Math.max(colStart, scope.left)\n colEnd = Math.min(colEnd, scope.right)\n if (colStart > colEnd) {\n // This row is entirely outside the scope — emit an empty line to\n // preserve row counts, then continue.\n const meta = rowMeta?.[row]\n if (!(meta?.softWrapped && row < endRow)) {\n parts.push(\"\")\n if (row < endRow) parts.push(\"\\n\")\n }\n continue\n }\n }\n\n let line = \"\"\n for (let col = colStart; col <= colEnd; col++) {\n // Skip wide-char continuation cells\n if (buffer.isCellContinuation(col, row)) continue\n\n // Skip non-selectable cells when flag checking is enabled\n if (respectSelectable && !buffer.isCellSelectable(col, row)) continue\n\n line += buffer.getCellChar(col, row)\n }\n\n // Trim trailing spaces using lastContentCol if available, otherwise fallback\n const meta = rowMeta?.[row]\n if (meta && meta.lastContentCol >= 0) {\n // Compute how much of the line is trailing whitespace\n // lastContentCol is the rightmost col with non-space content\n const effectiveEnd = row === endRow ? endCol : buffer.width - 1\n const trailingCols = effectiveEnd - meta.lastContentCol\n if (trailingCols > 0 && line.length > 0) {\n // Trim up to trailingCols chars of trailing spaces\n line = line.replace(/\\s+$/, \"\")\n }\n } else {\n line = line.replace(/\\s+$/, \"\")\n }\n\n // Preserve blank lines within selection (don't drop them)\n // but join soft-wrapped lines without a newline\n if (meta?.softWrapped && row < endRow) {\n parts.push(line)\n } else {\n parts.push(line)\n // Add newline separator unless this is the last row\n if (row < endRow) {\n parts.push(\"\\n\")\n }\n }\n }\n\n return parts.join(\"\")\n}\n","/**\n * SelectionFeature — service wrapping the headless selection machine.\n *\n * Connects the pure `terminalSelectionUpdate` state machine to ag-term's\n * buffer for text extraction, clipboard for copy-on-select, and the\n * input router's invalidate callback for render triggering.\n *\n * Mouse event handling:\n * - mousedown → start selection (character granularity)\n * - mousemove while selecting → extend selection range\n * - mouseup → finish selection, copy to clipboard if available\n *\n * The feature is created by withDomEvents and registered in the\n * CapabilityRegistry under SELECTION_CAPABILITY.\n */\n\nimport {\n terminalSelectionUpdate,\n createTerminalSelectionState,\n extractText,\n type TerminalSelectionState,\n type SelectionRange,\n type SelectionEffect,\n} from \"@silvery/headless/selection\"\nimport type { TerminalBuffer } from \"../buffer\"\nimport type { ClipboardCapability } from \"./clipboard-capability\"\nimport { extractHtml } from \"../extract-html\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Observable selection state + mouse handlers. */\nexport interface SelectionFeature {\n /** Current selection state (getter). */\n readonly state: TerminalSelectionState\n\n /** Subscribe to state changes. Returns an unsubscribe function. */\n subscribe(listener: () => void): () => void\n\n /** Handle mouse down — start selection. */\n handleMouseDown(col: number, row: number, altKey: boolean): void\n\n /** Handle mouse move — extend selection while dragging. */\n handleMouseMove(col: number, row: number): void\n\n /** Handle mouse up — finish selection, trigger copy. */\n handleMouseUp(col: number, row: number): void\n\n /** Programmatically set a selection range (or null to clear). */\n setRange(range: SelectionRange | null): void\n\n /** Clear the current selection. */\n clear(): void\n\n /** Clean up resources. */\n dispose(): void\n}\n\n/**\n * Options for creating a bridge SelectionFeature that delegates to\n * an external state owner (e.g., create-app's inline selection state).\n */\nexport interface SelectionBridgeOptions {\n /** Get the current selection state. */\n getState: () => TerminalSelectionState\n /** Subscribe to state changes. Returns an unsubscribe function. */\n subscribe: (listener: () => void) => () => void\n /** Set a selection range programmatically (used by copy-mode). */\n setRange: (range: SelectionRange | null) => void\n /** Clear the selection. */\n clear: () => void\n}\n\n/** Options for creating a SelectionFeature. */\nexport interface SelectionFeatureOptions {\n /** Terminal buffer to extract text from (required for mouse selection / copy). */\n buffer?: TerminalBuffer\n /** Optional clipboard capability for copy-on-select. */\n clipboard?: ClipboardCapability\n /** Callback to trigger a render pass after state changes. */\n invalidate: () => void\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Create a SelectionFeature that wraps the headless selection machine.\n *\n * The feature manages subscriptions, processes effects from the machine,\n * and coordinates with clipboard and render invalidation.\n */\nexport function createSelectionFeature(options: SelectionFeatureOptions): SelectionFeature {\n const { buffer, clipboard, invalidate } = options\n\n let selectionState = createTerminalSelectionState()\n const listeners = new Set<() => void>()\n\n function notifyListeners(): void {\n for (const listener of listeners) {\n listener()\n }\n }\n\n function processEffects(effects: SelectionEffect[], richRange?: SelectionRange | null): void {\n for (const effect of effects) {\n if (effect.type === \"render\") {\n invalidate()\n } else if (effect.type === \"copy\" && clipboard) {\n if (clipboard.copyRich && richRange && buffer) {\n const html = extractHtml(buffer, richRange)\n clipboard.copyRich(effect.text, html)\n } else {\n clipboard.copy(effect.text)\n }\n }\n }\n }\n\n function updateState(\n newState: TerminalSelectionState,\n effects: SelectionEffect[],\n richRange?: SelectionRange | null,\n ): void {\n selectionState = newState\n notifyListeners()\n processEffects(effects, richRange)\n }\n\n return {\n get state(): TerminalSelectionState {\n return selectionState\n },\n\n subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n\n handleMouseDown(col: number, row: number, _altKey: boolean): void {\n const [newState, effects] = terminalSelectionUpdate({ type: \"start\", col, row, source: \"mouse\" }, selectionState)\n updateState(newState, effects)\n },\n\n handleMouseMove(col: number, row: number): void {\n if (!selectionState.selecting) return\n const [newState, effects] = terminalSelectionUpdate({ type: \"extend\", col, row, buffer: buffer! }, selectionState)\n updateState(newState, effects)\n },\n\n handleMouseUp(_col: number, _row: number): void {\n if (!selectionState.selecting) return\n const [newState, effects] = terminalSelectionUpdate({ type: \"finish\" }, selectionState)\n\n // Extract text and copy to clipboard on mouse up\n const copyEffects = [...effects]\n if (newState.range && clipboard && buffer) {\n const text = extractText(buffer, newState.range)\n if (text.length > 0) {\n copyEffects.push({ type: \"copy\", text })\n }\n }\n\n updateState(newState, copyEffects, newState.range)\n },\n\n setRange(range: SelectionRange | null): void {\n if (range === null) {\n const [newState, effects] = terminalSelectionUpdate({ type: \"clear\" }, selectionState)\n updateState(newState, effects)\n } else {\n // Set range by starting at anchor and extending to head\n const [startState, startEffects] = terminalSelectionUpdate(\n { type: \"start\", col: range.anchor.col, row: range.anchor.row, source: \"keyboard\" },\n selectionState,\n )\n const [extendState, extendEffects] = terminalSelectionUpdate(\n { type: \"extend\", col: range.head.col, row: range.head.row },\n startState,\n )\n const [finishState, finishEffects] = terminalSelectionUpdate({ type: \"finish\" }, extendState)\n updateState(finishState, [...startEffects, ...extendEffects, ...finishEffects])\n }\n },\n\n clear(): void {\n const [newState, effects] = terminalSelectionUpdate({ type: \"clear\" }, selectionState)\n updateState(newState, effects)\n },\n\n dispose(): void {\n selectionState = createTerminalSelectionState()\n listeners.clear()\n },\n }\n}\n\n// ============================================================================\n// Bridge Implementation\n// ============================================================================\n\n/**\n * Create a bridge SelectionFeature that delegates to an external state owner.\n *\n * Used by create-app to expose its inline selection state to React hooks\n * (useSelection) and copy-mode (which calls setRange/clear) without\n * duplicating the state machine. Mouse handlers are no-ops — the external\n * owner (create-app's event loop) handles mouse events directly.\n */\nexport function createSelectionBridge(options: SelectionBridgeOptions): SelectionFeature {\n return {\n get state(): TerminalSelectionState {\n return options.getState()\n },\n\n subscribe(listener: () => void): () => void {\n return options.subscribe(listener)\n },\n\n // Mouse handlers are no-ops — create-app handles mouse events directly.\n handleMouseDown(_col: number, _row: number, _altKey: boolean): void {},\n handleMouseMove(_col: number, _row: number): void {},\n handleMouseUp(_col: number, _row: number): void {},\n\n setRange(range: SelectionRange | null): void {\n options.setRange(range)\n },\n\n clear(): void {\n options.clear()\n },\n\n dispose(): void {},\n }\n}\n","/**\n * DEC Width Mode Detection (xterm patch #407)\n *\n * Queries the terminal for its character width settings using DECRQM\n * (DEC Private Mode Request). This replaces guesswork with definitive\n * answers from the terminal itself.\n *\n * Modes:\n * - 1020: UTF-8 mode\n * - 1021: CJK ambiguous width (1 or 2 cells)\n * - 1022: Emoji width (1 or 2 cells)\n * - 1023: Private-use area width (1 or 2 cells)\n *\n * Protocol:\n * - Query: CSI ? {mode} $ p (DECRQM)\n * - Response: CSI ? {mode} ; {Ps} $ y (DECRPM)\n *\n * Where Ps is:\n * 1 = set (enabled / wide / 2-cell)\n * 2 = reset (disabled / narrow / 1-cell)\n * 0 = not recognized\n * 3 = permanently set\n * 4 = permanently reset\n *\n * @see https://invisible-island.net/xterm/ctlseqs/ctlseqs.html\n */\n\n/** Well-known xterm width DEC mode numbers. */\nexport const WidthMode = {\n /** UTF-8 mode */\n UTF8: 1020,\n /** CJK ambiguous character width */\n CJK_WIDTH: 1021,\n /** Emoji width */\n EMOJI_WIDTH: 1022,\n /** Private-use area width */\n PRIVATE_USE_WIDTH: 1023,\n} as const\n\n/** Terminal-reported character width configuration. */\nexport interface TerminalWidthConfig {\n /** Whether terminal uses UTF-8 mode */\n utf8: boolean\n /** How terminal handles CJK ambiguous width (1 or 2) */\n cjkWidth: 1 | 2\n /** How terminal handles emoji width (1 or 2) */\n emojiWidth: 1 | 2\n /** How terminal handles private-use area width (1 or 2) */\n privateUseWidth: 1 | 2\n}\n\n/** Width detector with async detect() and cleanup. */\nexport interface WidthDetector {\n /** Detected configuration (null until detection completes) */\n readonly config: TerminalWidthConfig | null\n /** Query terminal for width settings */\n detect(): Promise<TerminalWidthConfig>\n /** Clean up resources */\n dispose(): void\n}\n\n/** Options for creating a width detector. */\nexport interface WidthDetectorOptions {\n /** Write data to the terminal */\n write: (data: string) => void\n /** Subscribe to terminal input data; returns unsubscribe function */\n onData: (handler: (data: string) => void) => () => void\n /** Per-mode timeout in milliseconds (default: 200) */\n timeoutMs?: number\n}\n\n/** Default configuration when detection fails or times out. */\nexport const DEFAULT_WIDTH_CONFIG: TerminalWidthConfig = {\n utf8: true,\n cjkWidth: 1,\n emojiWidth: 2,\n privateUseWidth: 1,\n}\n\n/** Regex for DECRPM response: CSI ? mode ; Ps $ y */\nconst DECRPM_RE = /\\x1b\\[\\?(\\d+);(\\d+)\\$y/g\n\n/**\n * Parse a DECRPM response value into a boolean (set/reset).\n * 1 and 3 (permanently set) = true, everything else = false.\n */\nfunction isSet(ps: number): boolean {\n return ps === 1 || ps === 3\n}\n\n/**\n * Query a single DEC width mode via DECRQM.\n * Returns the Ps value from the DECRPM response, or null on timeout.\n */\nfunction queryWidthMode(\n write: (data: string) => void,\n onData: (handler: (data: string) => void) => () => void,\n mode: number,\n timeoutMs: number,\n): Promise<number | null> {\n return new Promise<number | null>((resolve) => {\n let timer: ReturnType<typeof setTimeout> | null = null\n let unsubscribe: (() => void) | null = null\n let buffer = \"\"\n\n function cleanup() {\n if (timer !== null) {\n clearTimeout(timer)\n timer = null\n }\n if (unsubscribe !== null) {\n unsubscribe()\n unsubscribe = null\n }\n }\n\n unsubscribe = onData((data: string) => {\n buffer += data\n // Try to find DECRPM response for our mode\n DECRPM_RE.lastIndex = 0\n let match: RegExpExecArray | null\n while ((match = DECRPM_RE.exec(buffer)) !== null) {\n const reportedMode = parseInt(match[1]!, 10)\n if (reportedMode === mode) {\n const ps = parseInt(match[2]!, 10)\n cleanup()\n resolve(ps)\n return\n }\n }\n })\n\n timer = setTimeout(() => {\n cleanup()\n resolve(null)\n }, timeoutMs)\n\n // Send DECRQM query: CSI ? {mode} $ p\n write(`\\x1b[?${mode}$p`)\n })\n}\n\n/**\n * Apply detected width configuration to terminal capabilities.\n *\n * Maps DEC width modes to the existing TerminalCaps fields:\n * - emojiWidth=2 → textEmojiWide=true\n * - privateUseWidth=2 → textSizingSupported=true (PUA treated as 2-wide)\n *\n * Returns a new caps object with the detected overrides applied.\n * CJK width and UTF-8 mode are informational — they don't yet map to\n * caps fields but are available in the TerminalWidthConfig for consumers.\n */\nexport function applyWidthConfig<T extends { textEmojiWide: boolean; textSizingSupported: boolean }>(\n caps: T,\n config: TerminalWidthConfig,\n): T {\n return {\n ...caps,\n textEmojiWide: config.emojiWidth === 2,\n textSizingSupported: config.privateUseWidth === 2,\n }\n}\n\n/**\n * Create a width detector that queries the terminal for DEC modes 1020-1023.\n *\n * @example\n * ```ts\n * const detector = createWidthDetector({\n * write: (data) => process.stdout.write(data),\n * onData: (handler) => {\n * process.stdin.on('data', (chunk) => handler(chunk.toString()))\n * return () => process.stdin.removeListener('data', handler)\n * },\n * })\n *\n * const config = await detector.detect()\n * console.log(config.emojiWidth) // 1 or 2\n * detector.dispose()\n * ```\n */\nexport function createWidthDetector(options: WidthDetectorOptions): WidthDetector {\n const { write, onData, timeoutMs = 200 } = options\n let config: TerminalWidthConfig | null = null\n let disposed = false\n\n return {\n get config() {\n return config\n },\n\n async detect(): Promise<TerminalWidthConfig> {\n if (disposed) return config ?? { ...DEFAULT_WIDTH_CONFIG }\n if (config !== null) return config\n\n // Query all 4 modes sequentially (each waits for its response)\n const utf8Ps = await queryWidthMode(write, onData, WidthMode.UTF8, timeoutMs)\n const cjkPs = await queryWidthMode(write, onData, WidthMode.CJK_WIDTH, timeoutMs)\n const emojiPs = await queryWidthMode(write, onData, WidthMode.EMOJI_WIDTH, timeoutMs)\n const puaPs = await queryWidthMode(write, onData, WidthMode.PRIVATE_USE_WIDTH, timeoutMs)\n\n config = {\n utf8: utf8Ps !== null ? isSet(utf8Ps) : DEFAULT_WIDTH_CONFIG.utf8,\n cjkWidth: cjkPs !== null ? (isSet(cjkPs) ? 2 : 1) : DEFAULT_WIDTH_CONFIG.cjkWidth,\n emojiWidth: emojiPs !== null ? (isSet(emojiPs) ? 2 : 1) : DEFAULT_WIDTH_CONFIG.emojiWidth,\n privateUseWidth: puaPs !== null ? (isSet(puaPs) ? 2 : 1) : DEFAULT_WIDTH_CONFIG.privateUseWidth,\n }\n\n return config\n },\n\n dispose() {\n disposed = true\n },\n }\n}\n","/**\n * Selection style composition.\n *\n * Applies selection highlight as a cell-style transform in the rendering pipeline,\n * NOT as an ANSI overlay. Selection composes correctly with existing cell styles\n * (fg/bg/attrs), handles already-inverted content, and flows through the normal\n * diff/output renderer.\n *\n * Called after render phase, before output phase.\n */\n\nimport type { Color, TerminalBuffer } from \"./buffer\"\nimport { type SelectionRange, type SelectionScope, normalizeRange } from \"@silvery/headless/selection\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Selection theme tokens. If provided, these override the fallback fg/bg swap.\n */\nexport interface SelectionTheme {\n /** Foreground color for selected text */\n selectionFg?: Color\n /** Background color for selected text */\n selectionBg?: Color\n}\n\n/**\n * A single cell change produced by selection composition.\n * Used as a sparse overlay — only cells within the selection range are affected.\n */\nexport interface SelectionCellChange {\n col: number\n row: number\n fg: Color\n bg: Color\n}\n\n// ============================================================================\n// Style Composition\n// ============================================================================\n\n/**\n * Compute selection style changes for all cells within a selection range.\n *\n * Returns a sparse list of cell changes (fg/bg only). The caller applies these\n * to the buffer before the output phase diffs it. This approach:\n * - Composes correctly with existing cell styles\n * - Handles already-inverted content (swaps back to normal instead of double-inverting)\n * - Respects SELECTABLE_FLAG per cell (skip non-selectable)\n * - Works with the normal diff/output renderer (no separate ANSI pass)\n *\n * @param buffer The rendered buffer (post-render-phase)\n * @param selection Current selection range, or null\n * @param theme Optional selection theme colors\n * @param respectSelectableFlag When true, skip cells without SELECTABLE_FLAG\n */\nexport function composeSelectionCells(\n buffer: TerminalBuffer,\n selection: SelectionRange | null,\n theme?: SelectionTheme,\n respectSelectableFlag = false,\n scope?: SelectionScope | null,\n): SelectionCellChange[] {\n if (!selection) return []\n\n const { startRow, startCol, endRow, endCol } = normalizeRange(selection)\n const changes: SelectionCellChange[] = []\n\n for (let row = startRow; row <= endRow; row++) {\n let colStart = row === startRow ? startCol : 0\n let colEnd = row === endRow ? endCol : buffer.width - 1\n // Clip to contain scope on every row so selection highlight never paints\n // outside a `userSelect=\"contain\"` ancestor, even on interior rows.\n if (scope) {\n colStart = Math.max(colStart, scope.left)\n colEnd = Math.min(colEnd, scope.right)\n if (colStart > colEnd) continue\n }\n\n for (let col = colStart; col <= colEnd; col++) {\n // Skip continuation cells (second half of wide chars)\n if (buffer.isCellContinuation(col, row)) continue\n\n // Skip non-selectable cells when flag checking is enabled\n if (respectSelectableFlag && !buffer.isCellSelectable(col, row)) continue\n\n const cellFg = buffer.getCellFg(col, row)\n const cellBg = buffer.getCellBg(col, row)\n\n let newFg: Color\n let newBg: Color\n\n if (theme?.selectionBg != null) {\n // Use theme tokens\n newFg = theme.selectionFg ?? cellFg\n newBg = theme.selectionBg\n } else {\n // Fallback: swap fg/bg (handles already-inverted content correctly)\n // If fg is null (default), use a visible fallback\n newFg = cellBg\n newBg = cellFg\n }\n\n changes.push({ col, row, fg: newFg, bg: newBg })\n }\n }\n\n return changes\n}\n\n/**\n * Apply selection style changes to a buffer.\n *\n * Modifies the buffer in-place by setting fg/bg on affected cells.\n * Call this after the render phase and before the output phase.\n *\n * @param buffer The rendered buffer to modify\n * @param changes Cell changes from composeSelectionCells\n */\nexport function applySelectionToBuffer(buffer: TerminalBuffer, changes: SelectionCellChange[]): void {\n for (const change of changes) {\n const cell = buffer.getCell(change.col, change.row)\n buffer.setCell(change.col, change.row, {\n ...cell,\n fg: change.fg,\n bg: change.bg,\n })\n }\n}\n\n// ============================================================================\n// Legacy API (deprecated — kept for backwards compatibility)\n// ============================================================================\n\n/**\n * Generate ANSI sequences to render selection overlay (inverse video on selected cells).\n *\n * @deprecated Use composeSelectionCells + applySelectionToBuffer instead.\n * This approach re-emits characters with SGR 7m, which doesn't compose correctly\n * with existing cell styles. The new style composition approach modifies cell data\n * before the output phase, producing correct results.\n */\nexport function renderSelectionOverlay(\n selection: SelectionRange | null,\n buffer: TerminalBuffer,\n mode: \"fullscreen\" | \"inline\" = \"fullscreen\",\n scope?: SelectionScope | null,\n): string {\n if (!selection) return \"\"\n\n const { startRow, startCol, endRow, endCol } = normalizeRange(selection)\n let out = \"\"\n\n for (let row = startRow; row <= endRow; row++) {\n let colStart = row === startRow ? startCol : 0\n let colEnd = row === endRow ? endCol : buffer.width - 1\n // Clip to contain scope on every row (see composeSelectionCells above).\n if (scope) {\n colStart = Math.max(colStart, scope.left)\n colEnd = Math.min(colEnd, scope.right)\n }\n\n if (colStart > colEnd) continue\n\n if (mode === \"fullscreen\") {\n out += `\\x1b[${row + 1};${colStart + 1}H`\n }\n\n out += \"\\x1b[7m\"\n for (let col = colStart; col <= colEnd; col++) {\n out += buffer.getCell(col, row).char\n }\n out += \"\\x1b[27m\"\n }\n\n return out\n}\n","/**\n * Virtual scrollback buffer for storing historical rendered content.\n *\n * Used by virtual inline mode to maintain scrollable history while\n * rendering in altscreen (which normally has no scrollback).\n *\n * Implementation uses a circular buffer for O(1) push and bounded memory.\n */\n\nimport { stripAnsi } from \"./unicode\"\n\nexport interface VirtualScrollbackOptions {\n /** Maximum number of lines to store. Default: 10000 */\n maxLines?: number\n}\n\nexport interface VirtualScrollback {\n /** Push rendered lines into history */\n push(lines: string[]): void\n /** Get visible lines at a scroll offset. offset=0 = most recent (bottom) */\n getVisibleRows(offset: number, count: number): string[]\n /** Total number of lines stored */\n readonly totalLines: number\n /** Search for text across all stored lines. Returns indices of matching lines (0 = oldest). */\n search(query: string): number[]\n /** Clear all stored content */\n clear(): void\n}\n\nexport function createVirtualScrollback(options?: VirtualScrollbackOptions): VirtualScrollback {\n const maxLines = options?.maxLines ?? 10_000\n const ansiLines: string[] = new Array(maxLines)\n const plainLines: string[] = new Array(maxLines)\n let head = 0 // next write position\n let count = 0 // total lines stored (capped at maxLines)\n\n return {\n push(lines: string[]): void {\n for (const line of lines) {\n ansiLines[head] = line\n plainLines[head] = stripAnsi(line)\n head = (head + 1) % maxLines\n if (count < maxLines) count++\n }\n },\n\n getVisibleRows(offset: number, rowCount: number): string[] {\n const result: string[] = []\n for (let i = 0; i < rowCount; i++) {\n const logicalIndex = count - offset - rowCount + i\n if (logicalIndex < 0 || logicalIndex >= count) {\n result.push(\"\")\n } else {\n // Convert logical index (0=oldest) to physical position\n const physical = (head - count + logicalIndex + maxLines) % maxLines\n result.push(ansiLines[physical]!)\n }\n }\n return result\n },\n\n get totalLines(): number {\n return count\n },\n\n search(query: string): number[] {\n if (!query) return []\n const lowerQuery = query.toLowerCase()\n const matches: number[] = []\n for (let i = 0; i < count; i++) {\n // logical index i (0=oldest), convert to physical\n const physical = (head - count + i + maxLines) % maxLines\n if (plainLines[physical]!.toLowerCase().includes(lowerQuery)) {\n matches.push(i)\n }\n }\n return matches\n },\n\n clear(): void {\n head = 0\n count = 0\n },\n }\n}\n","/**\n * @silvery/ag-term — Terminal rendering target for silvery.\n *\n * Provides terminal buffer, pipeline, output, input protocols,\n * layout engine, render adapters, and Unicode text utilities.\n *\n * @packageDocumentation\n */\n\n// =============================================================================\n// Ag (tree + layout + render)\n// =============================================================================\n\nexport { createAg } from \"./ag\"\nexport type { Ag, CreateAgOptions, AgLayoutOptions, AgRenderOptions, AgRenderResult } from \"./ag\"\n\n// =============================================================================\n// Plugin Composition (era2a)\n// =============================================================================\n\nexport { create, pipe, from, withAg, withTerm } from \"./compose\"\nexport type { AppBase, AppWithAg, AppWithTerm, PipeBuilder } from \"./compose\"\nexport { withReact } from \"./compose-react\"\nexport { withTest, type AppWithTest } from \"./compose-test\"\n\n// =============================================================================\n// Buffer\n// =============================================================================\n\nexport type { TerminalBuffer, Color } from \"./buffer\"\nexport { colorEquals, DEFAULT_BG, isDefaultBg, createTextFrame } from \"./buffer\"\n\n// =============================================================================\n// Pipeline\n// =============================================================================\n\nexport {\n executeRender,\n silveryBenchStart,\n silveryBenchStop,\n silveryBenchReset,\n type PipelineConfig,\n type ExecuteRenderOptions,\n type SilveryBenchPhases,\n type SilveryBenchOutputDetail,\n} from \"./pipeline\"\nexport {\n outputPhase,\n setOutputCaps,\n createOutputPhase,\n type OutputPhaseFn,\n type OutputCaps,\n} from \"./pipeline/output-phase\"\nexport type { PipelineContext } from \"./pipeline/types\"\n\n// =============================================================================\n// App Types\n// =============================================================================\n\nexport type { App } from \"./app\"\nexport type { BoundTerm } from \"./bound-term\"\n\n// =============================================================================\n// Layout Engine\n// =============================================================================\n\nexport type { LayoutEngine, LayoutConstants } from \"./layout-engine\"\nexport type { LayoutNode, MeasureFunc, MeasureMode } from \"@silvery/ag/layout-types\"\n\n// =============================================================================\n// Render Adapter Types (RenderAdapter itself is internal — not exported)\n// =============================================================================\n\nexport type {\n RenderBuffer,\n RenderStyle,\n TextMeasurer,\n TextMeasureResult,\n TextMeasureStyle,\n BorderChars,\n} from \"./render-adapter\"\n\n// Canvas adapter\nexport { createCanvasAdapter, CanvasRenderBuffer } from \"./adapters/canvas-adapter\"\nexport type { CanvasAdapterConfig } from \"./adapters/canvas-adapter\"\n\n// DOM adapter\nexport { createDOMAdapter, DOMRenderBuffer, injectDOMStyles } from \"./adapters/dom-adapter\"\nexport type { DOMAdapterConfig } from \"./adapters/dom-adapter\"\n\n// =============================================================================\n// ANSI Sanitizer\n// =============================================================================\n\nexport {\n sanitizeAnsi,\n tokenizeAnsi,\n isCSISGR,\n extractColonSGRReplacements,\n createColonSGRTracker,\n} from \"./ansi-sanitize\"\nexport type { AnsiToken, ColonSGRReplacement } from \"./ansi-sanitize\"\n\n// =============================================================================\n// ANSI Escape Sequences / Output\n// =============================================================================\n\nexport {\n ANSI,\n BEL,\n enableMouse,\n disableMouse,\n KittyFlags,\n enableKittyKeyboard,\n disableKittyKeyboard,\n queryKittyKeyboard,\n notify,\n notifyITerm2,\n notifyKitty,\n reportDirectory,\n setWindowTitle,\n setWindowAndIconTitle,\n resetWindowTitle,\n setCursorStyle,\n resetCursorStyle,\n setMouseCursorShape,\n resetMouseCursorShape,\n} from \"./output\"\nexport type { CursorShape, MouseCursorShape } from \"./output\"\n\n// =============================================================================\n// Bracketed Paste\n// =============================================================================\n\nexport {\n enableBracketedPaste,\n disableBracketedPaste,\n parseBracketedPaste,\n createBracketedPasteEvent,\n createInternalPasteEvent,\n PASTE_START,\n PASTE_END,\n} from \"./bracketed-paste\"\nexport type { BracketedPasteResult, PasteEvent } from \"./bracketed-paste\"\n\n// =============================================================================\n// Clipboard\n// =============================================================================\n\nexport { copyToClipboard, requestClipboard, parseClipboardResponse } from \"./clipboard\"\nexport { createOsc52Backend, createInternalClipboardBackend, createCompositeClipboard } from \"./clipboard\"\nexport type { ClipboardData, ClipboardBackend, ClipboardCapabilities } from \"./clipboard\"\n\n// =============================================================================\n// Advanced Clipboard (OSC 5522)\n// =============================================================================\n\nexport {\n createAdvancedClipboard,\n parseOsc5522Response,\n parsePasteData,\n ENABLE_PASTE_EVENTS,\n DISABLE_PASTE_EVENTS,\n} from \"./ansi/advanced-clipboard\"\nexport type { AdvancedClipboard, AdvancedClipboardOptions, ClipboardEntry } from \"./ansi/advanced-clipboard\"\n\n// =============================================================================\n// OSC 4 Palette Color Query/Set\n// =============================================================================\n\nexport { queryPaletteColor, setPaletteColor, parsePaletteResponse, queryMultiplePaletteColors } from \"./osc-palette\"\n\n// =============================================================================\n// OSC 133 Semantic Prompt Markers\n// =============================================================================\n\nexport { OSC133 } from \"./osc-markers\"\n\n// =============================================================================\n// Kitty Protocol Detection\n// =============================================================================\n\nexport { detectKittySupport, detectKittyFromStdio, type KittyDetectResult } from \"./kitty-detect\"\n\n// =============================================================================\n// Kitty Protocol Manager\n// =============================================================================\n\nexport { createKittyManager, type KittyManager, type KittyManagerOptions } from \"./kitty-manager\"\n\n// =============================================================================\n// Terminal Capability Detection\n// =============================================================================\n\nexport { detectTerminalCaps, defaultCaps, type TerminalCaps } from \"./terminal-caps\"\n\n// =============================================================================\n// Terminal Capability Visual Test\n// =============================================================================\n\nexport { runTermtest, TERMTEST_SECTIONS, type TermtestSection, type TermtestOptions } from \"./termtest\"\n\n// =============================================================================\n// Text Sizing (OSC 66)\n// =============================================================================\n\nexport {\n textSized,\n textScaled,\n resetTextScale,\n isPrivateUseArea,\n isTextSizingLikelySupported,\n detectTextSizingSupport,\n} from \"./text-sizing\"\n\n// =============================================================================\n// CSI 6n Cursor Position Query\n// =============================================================================\n\nexport { queryCursorPosition, queryCursorFromStdio } from \"./cursor-query\"\n\n// =============================================================================\n// OSC 10/11/12 Terminal Color Queries\n// =============================================================================\n\nexport {\n queryForegroundColor,\n queryBackgroundColor,\n queryCursorColor,\n setForegroundColor,\n setBackgroundColor,\n setCursorColor,\n resetForegroundColor,\n resetBackgroundColor,\n resetCursorColor,\n detectColorScheme,\n} from \"./terminal-colors\"\n\n// =============================================================================\n// Color Scheme Detection (Mode 2031) — re-exported from @silvery/ansi\n// =============================================================================\n\nexport {\n createColorSchemeDetector,\n parseColorSchemeResponse,\n ENABLE_COLOR_SCHEME_REPORTING,\n DISABLE_COLOR_SCHEME_REPORTING,\n type ColorSchemeDetector,\n type ColorSchemeDetectorOptions,\n} from \"@silvery/ansi\"\n\n// =============================================================================\n// Width Detection (DEC 1020-1023)\n// =============================================================================\n\nexport {\n createWidthDetector,\n applyWidthConfig,\n DEFAULT_WIDTH_CONFIG,\n type WidthDetector,\n type TerminalWidthConfig,\n} from \"./ansi/width-detection\"\n\n// =============================================================================\n// DA1/DA2/DA3 + XTVERSION Device Attribute Queries\n// =============================================================================\n\nexport {\n queryPrimaryDA,\n querySecondaryDA,\n queryTertiaryDA,\n queryTerminalVersion,\n queryDeviceAttributes,\n type DeviceAttributes,\n} from \"./device-attrs\"\n\n// =============================================================================\n// Focus Reporting\n// =============================================================================\n\nexport { enableFocusReporting, disableFocusReporting, parseFocusEvent } from \"./focus-reporting\"\n\n// =============================================================================\n// DECRQM Mode Query\n// =============================================================================\n\nexport { queryMode, queryModes, DecMode } from \"./mode-query\"\n\n// DEC Width Mode Detection additional exports (WidthMode, WidthDetectorOptions)\nexport { WidthMode } from \"./ansi/width-detection\"\nexport type { WidthDetectorOptions } from \"./ansi/width-detection\"\n\n// =============================================================================\n// CSI 14t/18t Pixel and Text Area Size\n// =============================================================================\n\nexport { queryTextAreaPixels, queryTextAreaSize, queryCellSize } from \"./pixel-size\"\n\n// =============================================================================\n// TermDef Resolution\n// =============================================================================\n\n// TermDef and related terminal-specific types\nexport type {\n TermDef,\n RenderOptions as TermDefRenderOptions,\n RenderInstance as TermDefRenderInstance,\n} from \"./term-def\"\n// TermDef resolution — internal. Use createTerm() instead of TermDef.\n// isTerm and createInputEvents are still public utilities.\nexport { isTerm, createInputEvents } from \"./term-def\"\n\n// =============================================================================\n// Hit Registry (Mouse Support) — React-free core only\n// =============================================================================\n//\n// The barrel exports only the pure core (class, types, constants).\n// React hooks and context are available via @silvery/ag-term/hit-registry.\n//\n\nexport { HitRegistry, resetHitRegionIdCounter, Z_INDEX } from \"./hit-registry-core\"\nexport type { HitTarget, HitRegion } from \"./hit-registry-core\"\n\n// =============================================================================\n// Mouse Parsing (SGR mode 1006)\n// =============================================================================\n\nexport { parseMouseSequence, isMouseSequence, type ParsedMouse } from \"./mouse\"\n\n// =============================================================================\n// Mouse Events (DOM-level)\n// =============================================================================\n\nexport {\n hitTest,\n createMouseEvent,\n createWheelEvent,\n dispatchMouseEvent,\n processMouseEvent,\n createMouseEventProcessor,\n checkDoubleClick,\n createDoubleClickState,\n computeEnterLeave,\n resolveNodeDraggable,\n type SilveryMouseEvent,\n type SilveryWheelEvent,\n type MouseEventProcessorOptions,\n type MouseEventProcessorState,\n type KeyboardModifierState,\n updateKeyboardModifiers,\n} from \"./mouse-events\"\nexport type { MouseEventProps } from \"@silvery/ag/mouse-event-types\"\n\n// =============================================================================\n// Non-TTY Utilities\n// =============================================================================\n\nexport { isTTY, resolveNonTTYMode, stripAnsi } from \"./non-tty\"\nexport type { NonTTYOptions, ResolvedNonTTYMode } from \"./non-tty\"\n\n// =============================================================================\n// DevTools — available via @silvery/ag-term/devtools (not re-exported here to\n// keep this barrel React-free; devtools imports the React reconciler)\n// =============================================================================\n\n// =============================================================================\n// Inspector\n// =============================================================================\n\nexport {\n enableInspector,\n disableInspector,\n isInspectorEnabled,\n inspectTree,\n inspectFrame,\n autoEnableInspector,\n} from \"./inspector\"\nexport type { InspectorOptions } from \"./inspector\"\n\n// =============================================================================\n// Unicode Text Utilities\n// =============================================================================\n\nexport {\n // Measurement\n displayWidth,\n displayWidthAnsi,\n measureText,\n // Manipulation\n wrapText,\n truncateText,\n padText,\n constrainText,\n sliceByWidth,\n sliceByWidthRange,\n sliceByWidthFromEnd,\n // ANSI handling\n hasAnsi,\n parseAnsiText,\n stripAnsi as stripAnsiUnicode,\n truncateAnsi,\n // Grapheme operations\n splitGraphemes,\n graphemeCount,\n graphemeWidth,\n // Character detection\n isWideGrapheme,\n isZeroWidthGrapheme,\n isCJK,\n isLikelyEmoji,\n hasWideCharacters,\n hasZeroWidthCharacters,\n // Emoji presentation\n ensureEmojiPresentation,\n // Text sizing state\n setTextSizingEnabled,\n isTextSizingEnabled,\n // Text-presentation emoji width\n setTextEmojiWide,\n // Buffer writing\n writeTextToBuffer,\n writeTextTruncated,\n writeLinesToBuffer,\n // Utilities\n normalizeText,\n getFirstCodePoint,\n} from \"./unicode\"\nexport type { StyledSegment } from \"./unicode\"\n\n// Width measurer factory\nexport { createWidthMeasurer, createMeasurer, runWithMeasurer, type Measurer, type WidthMeasurer } from \"./unicode\"\n\n// Measurer composition (term + measurement)\nexport { withMeasurer, createPipeline, type MeasuredTerm } from \"./measurer\"\n\n// withRender plugin — available via @silvery/create/with-render (not re-exported\n// here to keep this barrel React-free; withRender's renderStatic() pulls React)\n\n// =============================================================================\n// Scroll Utilities\n// =============================================================================\n\nexport { calcEdgeBasedScrollOffset } from \"./scroll-utils\"\n\n// Scroll Region Optimization (DECSTBM)\nexport {\n setScrollRegion,\n resetScrollRegion,\n scrollUp,\n scrollDown,\n moveCursor,\n supportsScrollRegions,\n} from \"./scroll-region\"\nexport type { ScrollRegionConfig } from \"./scroll-region\"\n\n// =============================================================================\n// Pane Manager\n// =============================================================================\n\nexport type { LayoutNode as SplitLayoutNode } from \"./pane-manager\"\nexport {\n createLeaf,\n splitPane,\n removePane,\n getPaneIds,\n findAdjacentPane,\n resizeSplit,\n swapPanes,\n getTabOrder as getSplitTabOrder,\n} from \"./pane-manager\"\n\n// =============================================================================\n// Scheduler\n// =============================================================================\n\nexport { IncrementalRenderMismatchError } from \"./errors\"\n\n// =============================================================================\n// ANSI Primitives (merged from @silvery/ansi)\n// =============================================================================\n\n// Term factory and lazy instance\nexport { createTerm, term } from \"./ansi/index\"\nexport type { Term, StyleChain, TermEmulatorBackend } from \"./ansi/index\"\n\n// Console patching\nexport { patchConsole } from \"./ansi/index\"\nexport type { PatchedConsole, PatchConsoleOptions, ConsoleStats } from \"./ansi/index\"\n\n// Output guard (alt screen protection)\nexport { createOutputGuard } from \"./ansi/index\"\nexport type { OutputGuard, OutputGuardOptions } from \"./ansi/index\"\n\n// Types\nexport type {\n UnderlineStyle,\n RGB,\n ColorLevel,\n Color as AnsiColor,\n AnsiColorName,\n StyleOptions,\n ConsoleMethod,\n ConsoleEntry,\n CreateTermOptions,\n} from \"./ansi/index\"\n\n// Detection\nexport { detectCursor, detectInput, detectColor, detectUnicode, detectExtendedUnderline } from \"./ansi/index\"\n\n// Utilities\nexport { ANSI_REGEX, displayLength } from \"./ansi/index\"\n\n// Underline functions\nexport {\n underline as ansiUnderline,\n curlyUnderline,\n dottedUnderline,\n dashedUnderline,\n doubleUnderline,\n underlineColor,\n styledUnderline,\n} from \"./ansi/index\"\n\n// Hyperlinks\nexport { hyperlink } from \"./ansi/index\"\n\n// ANSI control helpers (re-exported as ansi* to avoid conflicts with term's own)\nexport {\n enterAltScreen,\n leaveAltScreen,\n clearScreen,\n clearLine,\n cursorTo,\n cursorHome,\n cursorHide,\n cursorShow,\n cursorStyle as ansiCursorStyle,\n setTitle,\n enableSyncUpdate,\n disableSyncUpdate,\n} from \"./ansi/index\"\n\n// Background override\nexport { BG_OVERRIDE_CODE, bgOverride } from \"./ansi/index\"\n\n// =============================================================================\n// Interactive Signals (re-exported from @silvery/ag)\n// =============================================================================\n\nexport {\n ensureInteractiveState,\n setHovered,\n setArmed,\n setSelected,\n setFocused,\n setDropTarget,\n clearInteractiveState,\n} from \"@silvery/ag/interactive-signals\"\n\n// =============================================================================\n// Selection (re-exported from @silvery/headless)\n// =============================================================================\n\nexport {\n terminalSelectionUpdate,\n createTerminalSelectionState,\n normalizeRange,\n extractText,\n findWordBoundary,\n findLineBoundary,\n type TerminalSelectionState,\n type SelectionRange,\n type SelectionPosition,\n type SelectionAction,\n type SelectionEffect,\n type SelectionGranularity,\n} from \"@silvery/headless/selection\"\n\nexport { renderSelectionOverlay } from \"./selection-renderer\"\nexport { extractHtml } from \"./extract-html\"\n\n// =============================================================================\n// Find (re-exported from @silvery/headless)\n// =============================================================================\n\nexport {\n findUpdate,\n createFindState,\n searchBuffer,\n type FindState,\n type FindMatch,\n type FindResult,\n type FindProvider,\n type FindAction,\n type FindEffect,\n} from \"@silvery/headless/find\"\n\n// FindFeature service\nexport { createFindFeature } from \"./find-feature\"\nexport type { FindFeature, FindFeatureOptions } from \"./find-feature\"\n\n// =============================================================================\n// Copy Mode (re-exported from @silvery/headless)\n// =============================================================================\n\nexport {\n copyModeUpdate,\n createCopyModeState,\n type CopyModeState,\n type CopyModePosition,\n type CopyModeBuffer,\n type CopyModeAction,\n type CopyModeEffect,\n} from \"@silvery/headless/copy-mode\"\n\n// =============================================================================\n// Pointer State Machine (re-exported from @silvery/headless)\n// =============================================================================\n\nexport {\n pointerStateUpdate,\n createPointerState,\n createPointerDoubleClickState,\n checkPointerDoubleClick,\n DRAG_THRESHOLD,\n type PointerState,\n type PointerAction,\n type PointerEffect,\n type Position as PointerPosition,\n type PointerDoubleClickState,\n} from \"@silvery/headless/pointer\"\n\n// =============================================================================\n// Drag Events\n// =============================================================================\n\nexport {\n createDragEvent,\n createDragState,\n isDropTarget,\n findDropTarget,\n type DragState,\n type DragEvent,\n type DragEventProps,\n} from \"./drag-events\"\n\n// =============================================================================\n// Virtual Scrollback\n// =============================================================================\n\nexport { createVirtualScrollback, type VirtualScrollback, type VirtualScrollbackOptions } from \"./virtual-scrollback\"\n\n// =============================================================================\n// History Buffer\n// =============================================================================\n\nexport { createHistoryBuffer, createHistoryItem, type HistoryItem, type HistoryBuffer } from \"./history-buffer\"\n\n// =============================================================================\n// Viewport Compositor\n// =============================================================================\n\nexport { composeViewport, type ViewportCompositorConfig, type ComposedViewport } from \"./viewport-compositor\"\n\n// =============================================================================\n// List Document\n// =============================================================================\n\nexport { createListDocument, type ListDocument, type DocumentSource, type LiveItemBlock } from \"./list-document\"\n\n// =============================================================================\n// Text Surface\n// =============================================================================\n\nexport { createTextSurface, type TextSurface, type SurfaceCapabilities } from \"./text-surface\"\n\n// =============================================================================\n// Search Overlay\n// =============================================================================\n\nexport {\n createSearchState,\n searchUpdate,\n renderSearchBar,\n type SearchState,\n type SearchMatch,\n type SearchAction,\n type SearchEffect,\n} from \"./search-overlay\"\n","/**\n * Pure layout function for silvery-loop.\n *\n * Takes a React element and dimensions, returns an immutable Buffer.\n * This is Layer 0 - no runtime, no events, just pure rendering.\n */\n\nimport { createTerm } from \"../ansi/index\"\nimport React, { type ReactElement } from \"react\"\nimport { bufferToStyledText, bufferToText } from \"../buffer\"\nimport { StdoutContext, StderrContext, TermContext } from \"@silvery/ag-react/context\"\nimport { ensureDefaultLayoutEngine, isLayoutEngineInitialized } from \"../layout-engine\"\nimport { executeRender } from \"../pipeline\"\nimport { createContainer, createFiberRoot, getContainerRoot, reconciler } from \"@silvery/ag-react/reconciler\"\nimport type { Buffer, Dims } from \"./types\"\n\n/**\n * Options for the layout function.\n */\nexport interface LayoutOptions {\n /** Skip layout notifications (for static renders). Default: true */\n skipLayoutNotifications?: boolean\n /** Strip ANSI codes for plain text output. Default: false */\n plain?: boolean\n}\n\n/**\n * Ensure layout engine is initialized.\n * Must be called before layout() in async contexts.\n */\nexport async function ensureLayoutEngine(): Promise<void> {\n if (!isLayoutEngineInitialized()) {\n await ensureDefaultLayoutEngine()\n }\n}\n\n/**\n * Pure layout function - renders a React element to a Buffer.\n *\n * IMPORTANT: Call ensureLayoutEngine() first in async contexts.\n * The layout engine must be initialized before calling this.\n *\n * @param element React element to render\n * @param dims Terminal dimensions\n * @param options Layout options\n * @returns Immutable Buffer with text, ansi, and nodes\n *\n * @example\n * ```typescript\n * import { layout, ensureLayoutEngine } from '@silvery/ag-term/runtime'\n *\n * await ensureLayoutEngine()\n * const buffer = layout(<Text>Hello</Text>, { cols: 80, rows: 24 })\n * console.log(buffer.text) // \"Hello\"\n * ```\n */\nexport function layout(element: ReactElement, dims: Dims, options: LayoutOptions = {}): Buffer {\n if (!isLayoutEngineInitialized()) {\n throw new Error(\"Layout engine not initialized. Call ensureLayoutEngine() first.\")\n }\n\n const { skipLayoutNotifications = true, plain = false } = options\n const { cols: width, rows: height } = dims\n\n // Create container for React reconciliation\n const container = createContainer(() => {})\n\n // Create fiber root\n const fiberRoot = createFiberRoot(container)\n\n // Create minimal mock stdout for components that use useStdout\n const mockStdout = {\n columns: width,\n rows: height,\n write: () => true,\n isTTY: false,\n on: () => mockStdout,\n off: () => mockStdout,\n once: () => mockStdout,\n removeListener: () => mockStdout,\n addListener: () => mockStdout,\n } as unknown as NodeJS.WriteStream\n\n // Create mock term for components that use useTerm()\n const mockTerm = createTerm({ color: plain ? null : \"truecolor\" })\n\n // Wrap with minimal contexts (no input handling needed)\n const wrapped = React.createElement(\n TermContext.Provider,\n { value: mockTerm },\n React.createElement(\n StdoutContext.Provider,\n {\n value: {\n stdout: mockStdout,\n write: () => {},\n },\n },\n React.createElement(\n StderrContext.Provider,\n {\n value: {\n stderr: process.stderr,\n write: (data: string) => {\n process.stderr.write(data)\n },\n },\n },\n element,\n ),\n ),\n )\n\n // Mount, render, and unmount - all without act warnings\n withoutActWarnings(() => {\n reconciler.updateContainerSync(wrapped, fiberRoot, null, null)\n reconciler.flushSyncWork()\n })\n\n // Execute render pipeline (skip layout notifications for static renders)\n const root = getContainerRoot(container)\n const { buffer: termBuffer } = executeRender(root, width, height, null, {\n skipLayoutNotifications,\n })\n\n // Get text representations\n const text = bufferToText(termBuffer)\n const ansi = bufferToStyledText(termBuffer)\n\n // Unmount (cleanup)\n withoutActWarnings(() => {\n reconciler.updateContainerSync(null, fiberRoot, null, null)\n reconciler.flushSyncWork()\n })\n\n return {\n text,\n ansi,\n nodes: root,\n _buffer: termBuffer,\n }\n}\n\n/**\n * Synchronous layout - assumes engine is already initialized.\n * Throws if engine not ready.\n */\nexport function layoutSync(element: ReactElement, dims: Dims, options: LayoutOptions = {}): Buffer {\n return layout(element, dims, options)\n}\n\n/**\n * Run a function with React act warnings disabled.\n * Used for static renders where we don't use act() and don't need layout feedback.\n */\nfunction withoutActWarnings(fn: () => void): void {\n const prev = (globalThis as any).IS_REACT_ACT_ENVIRONMENT\n ;(globalThis as any).IS_REACT_ACT_ENVIRONMENT = false\n try {\n fn()\n } finally {\n ;(globalThis as any).IS_REACT_ACT_ENVIRONMENT = prev\n }\n}\n","/**\n * Pure diff function for silvery-loop.\n *\n * Takes prev and next buffers, returns minimal ANSI patch.\n * This is an internal function used by the runtime.\n */\n\nimport { outputPhase } from \"../pipeline\"\nimport type { Buffer } from \"./types\"\n\n/**\n * Diff mode for ANSI output.\n */\nexport type DiffMode = \"fullscreen\" | \"inline\"\n\n/**\n * Compute the minimal ANSI diff between two buffers.\n *\n * @param prev Previous buffer (null on first render)\n * @param next Current buffer\n * @param mode Render mode (fullscreen or inline)\n * @returns ANSI escape sequence string to transform prev into next\n *\n * @example\n * ```typescript\n * import { diff, layout } from '@silvery/ag-term/runtime'\n *\n * const prev = layout(<Text>Hello</Text>, dims)\n * const next = layout(<Text>World</Text>, dims)\n * const patch = diff(prev, next)\n * process.stdout.write(patch)\n * ```\n */\nexport function diff(\n prev: Buffer | null,\n next: Buffer,\n mode: DiffMode = \"fullscreen\",\n scrollbackOffset = 0,\n termRows?: number,\n): string {\n const prevBuffer = prev?._buffer ?? null\n const nextBuffer = next._buffer\n\n return outputPhase(prevBuffer, nextBuffer, mode, scrollbackOffset, termRows)\n}\n\n/**\n * Render a buffer to ANSI string (no diff, full render).\n *\n * @param buffer Buffer to render\n * @param mode Render mode (fullscreen or inline)\n * @returns Full ANSI output\n */\nexport function render(buffer: Buffer, mode: DiffMode = \"fullscreen\"): string {\n return outputPhase(null, buffer._buffer, mode)\n}\n","import { type TerminalBuffer, bufferToStyledText, bufferToText } from \"../buffer\"\nimport type { AgNode } from \"@silvery/ag/types\"\nimport type { Buffer } from \"./types\"\n\nexport function createBuffer(termBuffer: TerminalBuffer, nodes: AgNode): Buffer {\n let _text: string | undefined\n let _ansi: string | undefined\n return {\n get text() {\n return (_text ??= bufferToText(termBuffer))\n },\n get ansi() {\n return (_ansi ??= bufferToStyledText(termBuffer))\n },\n nodes,\n _buffer: termBuffer,\n }\n}\n","/**\n * Create the silvery-loop runtime kernel.\n *\n * The runtime owns the event loop, diffing, and output. Users interact via:\n * - events() - AsyncIterable of all events (keys, resize, effects)\n * - schedule() - Queue effects for async execution\n * - render() - Output a buffer (diffing handled internally)\n *\n * NOTE: This runtime is designed for single-consumer use. Calling events()\n * multiple times concurrently will cause events to be split between consumers.\n * Each call returns a fresh AsyncIterable, but they share the underlying queue.\n *\n * @example\n * ```typescript\n * using runtime = createRuntime({ target: termTarget })\n *\n * for await (const event of runtime.events()) {\n * state = reducer(state, event)\n * runtime.render(layout(view(state), runtime.getDims()))\n * }\n * ```\n */\n\nimport { createOutputPhase } from \"../pipeline/output-phase\"\nimport { takeUntil } from \"@silvery/create/streams\"\nimport { diff } from \"./diff\"\nimport type { Buffer, Dims, Event, Runtime, RuntimeOptions } from \"./types\"\n\n// =============================================================================\n// Event Channel - unified async iterable for all internal events\n// =============================================================================\n\ninterface EventChannel {\n push(event: Event): void\n events(): AsyncIterable<Event>\n dispose(): void\n}\n\n/**\n * Create an event channel that bridges callbacks to AsyncIterable.\n *\n * This is the single point where callbacks (resize, effect completion)\n * are converted to the async iterable pattern. External sources like\n * keyboard events are already AsyncIterable and merged at a higher level.\n */\nfunction createEventChannel(signal: AbortSignal): EventChannel {\n const queue: Event[] = []\n let pendingResolve: ((event: Event | null) => void) | undefined\n let disposed = false\n\n // Resolve pending waiter on abort\n const onAbort = () => {\n if (pendingResolve) {\n pendingResolve(null)\n pendingResolve = undefined\n }\n }\n signal.addEventListener(\"abort\", onAbort, { once: true })\n\n return {\n push(event: Event): void {\n if (disposed || signal.aborted) return\n\n if (pendingResolve) {\n const r = pendingResolve\n pendingResolve = undefined\n r(event)\n } else {\n queue.push(event)\n }\n },\n\n events(): AsyncIterable<Event> {\n return {\n [Symbol.asyncIterator](): AsyncIterator<Event> {\n return {\n async next(): Promise<IteratorResult<Event>> {\n if (disposed || signal.aborted) {\n return { done: true, value: undefined }\n }\n\n // Return queued event if available\n if (queue.length > 0) {\n return { done: false, value: queue.shift()! }\n }\n\n // Wait for next event or abort\n const event = await new Promise<Event | null>((resolve) => {\n pendingResolve = resolve\n })\n\n if (event === null || disposed || signal.aborted) {\n return { done: true, value: undefined }\n }\n\n return { done: false, value: event }\n },\n }\n },\n }\n },\n\n dispose(): void {\n disposed = true\n signal.removeEventListener(\"abort\", onAbort)\n if (pendingResolve) {\n pendingResolve(null)\n pendingResolve = undefined\n }\n },\n }\n}\n\n// =============================================================================\n// Runtime Factory\n// =============================================================================\n\n/**\n * Create a runtime kernel.\n *\n * @param options Runtime configuration\n * @returns Runtime instance implementing Symbol.dispose\n */\nexport function createRuntime(options: RuntimeOptions): Runtime {\n const { target, signal: externalSignal, mode = \"fullscreen\" } = options\n\n // Inline mode needs persistent cursor tracking across frames.\n // If no outputPhaseFn provided, create one so prevCursorRow/prevOutputLines\n // persist between renders (bare diff() creates fresh state each call).\n const fallbackOutputPhase = mode === \"inline\" ? createOutputPhase({}) : undefined\n let outputPhaseFn = options.outputPhaseFn ?? fallbackOutputPhase\n\n // Internal abort controller for cleanup\n const controller = new AbortController()\n const signal = controller.signal\n\n // Wire external signal if provided - track for cleanup\n let externalAbortHandler: (() => void) | undefined\n if (externalSignal) {\n if (externalSignal.aborted) {\n controller.abort()\n } else {\n externalAbortHandler = () => controller.abort()\n externalSignal.addEventListener(\"abort\", externalAbortHandler, {\n once: true,\n })\n }\n }\n\n // Track previous buffer for diffing\n let prevBuffer: Buffer | null = null\n\n // Scrollback offset tracking (inline mode only)\n let scrollbackOffset = 0\n\n // Track if disposed\n let disposed = false\n\n // Unified event channel for resize and effect events\n const eventChannel = createEventChannel(signal)\n\n // Subscribe to resize events if supported\n let unsubscribeResize: (() => void) | undefined\n if (target.onResize) {\n unsubscribeResize = target.onResize((dims) => {\n eventChannel.push({ type: \"resize\", cols: dims.cols, rows: dims.rows })\n })\n }\n\n // Effect ID counter\n let effectId = 0\n\n return {\n events(): AsyncIterable<Event> {\n // Return channel events wrapped with takeUntil for cleanup\n return takeUntil(eventChannel.events(), signal)\n },\n\n schedule<T>(effect: () => Promise<T>, opts?: { signal?: AbortSignal }): void {\n if (disposed) return\n\n const id = `effect-${effectId++}`\n const effectSignal = opts?.signal\n\n // Check if already aborted\n if (effectSignal?.aborted) return\n\n // Execute effect asynchronously\n const execute = async () => {\n // Track abort handler for cleanup\n let abortHandler: (() => void) | undefined\n\n try {\n if (effectSignal) {\n // Create abort race with cleanup\n const aborted = new Promise<never>((_resolve, reject) => {\n abortHandler = () => reject(new Error(\"Effect aborted\"))\n effectSignal.addEventListener(\"abort\", abortHandler, {\n once: true,\n })\n })\n\n const result = await Promise.race([effect(), aborted])\n\n // Clean up abort listener after success\n if (abortHandler) {\n effectSignal.removeEventListener(\"abort\", abortHandler)\n }\n\n eventChannel.push({ type: \"effect\", id, result })\n } else {\n const result = await effect()\n eventChannel.push({ type: \"effect\", id, result })\n }\n } catch (error) {\n // Clean up abort listener on error too\n if (abortHandler && effectSignal) {\n effectSignal.removeEventListener(\"abort\", abortHandler)\n }\n\n // Check for abort by name (handles DOMException, AbortError, etc.)\n if (error instanceof Error && (error.message === \"Effect aborted\" || error.name === \"AbortError\")) {\n // Silently ignore aborted effects\n return\n }\n eventChannel.push({\n type: \"error\",\n error: error instanceof Error ? error : new Error(String(error)),\n })\n }\n }\n\n // Start immediately (microtask)\n queueMicrotask(() => {\n void execute()\n })\n },\n\n render(buffer: Buffer): void {\n if (disposed) return\n\n // Compute diff internally — pass terminal rows to cap output.\n // Inline mode: prevents scrollback corruption (cursor-up clamped at row 0).\n // Fullscreen mode: prevents buffer overflow that scrolls the terminal and\n // desynchronizes prevBuffer from actual terminal state (ghost pixel garble).\n const offset = scrollbackOffset\n scrollbackOffset = 0 // Consume the offset\n const termRows = target.getDims().rows\n\n // Use scoped output phase if provided (threads measurer/caps correctly),\n // otherwise fall back to raw diff() for backwards compatibility\n let patch: string\n if (outputPhaseFn) {\n const prevBuf = prevBuffer?._buffer ?? null\n const nextBuf = buffer._buffer\n patch = outputPhaseFn(prevBuf, nextBuf, mode, offset, termRows)\n } else {\n patch = diff(prevBuffer, buffer, mode, offset, termRows)\n }\n prevBuffer = buffer\n\n // Debug: capture raw ANSI output that's actually written to the terminal\n if (process.env.SILVERY_CAPTURE_RAW) {\n try {\n const fs = require(\"fs\")\n fs.appendFileSync(\"/tmp/silvery-runtime-raw.ansi\", patch)\n } catch {}\n }\n\n // Write to target\n target.write(patch)\n },\n\n addScrollbackLines(lines: number): void {\n if (mode !== \"inline\" || lines <= 0) return\n scrollbackOffset += lines\n },\n\n invalidate(): void {\n prevBuffer = null\n },\n\n setOutputPhaseFn(fn: RuntimeOptions[\"outputPhaseFn\"]): void {\n if (fn) outputPhaseFn = fn\n },\n\n resetInlineCursor(): void {\n // Reset inline cursor tracking — delegates to the output phase (either\n // the caller-provided one or the inline-mode fallback created above).\n const fn = outputPhaseFn as { resetInlineState?: () => void } | undefined\n fn?.resetInlineState?.()\n },\n\n getInlineCursorRow(): number {\n const fn = outputPhaseFn as { getInlineCursorRow?: () => number } | undefined\n return fn?.getInlineCursorRow?.() ?? -1\n },\n\n promoteScrollback(content: string, lines: number): void {\n const fn = outputPhaseFn as { promoteScrollback?: (c: string, l: number) => void } | undefined\n fn?.promoteScrollback?.(content, lines)\n },\n\n getDims(): Dims {\n return target.getDims()\n },\n\n [Symbol.dispose](): void {\n if (disposed) return\n disposed = true\n\n // Abort all pending operations\n controller.abort()\n\n // Remove external signal listener if still attached\n if (externalAbortHandler && externalSignal) {\n externalSignal.removeEventListener(\"abort\", externalAbortHandler)\n }\n\n // Unsubscribe from resize\n if (unsubscribeResize) {\n unsubscribeResize()\n }\n\n // Dispose event channel\n eventChannel.dispose()\n },\n }\n}\n","/**\n * Signal Store — Zustand StoreApi-compatible store backed by alien-signals.\n *\n * Drop-in replacement for Zustand's createStore(). Provides the same\n * StoreApi<T> interface so useApp()/StoreContext keep working unchanged.\n *\n * Also re-exports StateCreator for backward compatibility with code\n * that imported it from \"zustand\".\n */\n\nimport { signal } from \"alien-signals\"\n\n// =============================================================================\n// Types (matching Zustand's API surface)\n// =============================================================================\n\ntype SetStateInternal<T> = {\n (partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: false): void\n (state: T | ((state: T) => T), replace: true): void\n}\n\nexport interface StoreApi<T> {\n setState: SetStateInternal<T>\n getState: () => T\n getInitialState: () => T\n subscribe: (listener: (state: T, prevState: T) => void) => () => void\n}\n\nexport type StateCreator<\n T,\n Mis extends [StoreMutatorIdentifier, unknown][] = [],\n Mos extends [StoreMutatorIdentifier, unknown][] = [],\n U = T,\n> = ((setState: StoreApi<T>[\"setState\"], getState: StoreApi<T>[\"getState\"], store: StoreApi<T>) => U) & {\n $$storeMutators?: Mos\n}\n\n// Zustand compatibility stubs — unused but needed for type compat\nexport interface StoreMutators<_S, _A> {}\nexport type StoreMutatorIdentifier = keyof StoreMutators<unknown, unknown>\n\n// =============================================================================\n// createStore — signal-backed Zustand replacement\n// =============================================================================\n\nexport function createStore<T>(factory: StateCreator<T>): StoreApi<T> {\n const listeners = new Set<(state: T, prevState: T) => void>()\n const state$ = signal<T>(undefined as T)\n let initialState: T\n\n const setState: SetStateInternal<T> = (partial: unknown, replace?: boolean) => {\n const prev = state$()\n const raw =\n typeof partial === \"function\" ? (partial as (state: T) => T | Partial<T>)(prev) : (partial as T | Partial<T>)\n\n let next: T\n if (!replace && raw !== null && typeof raw === \"object\" && !Array.isArray(raw)) {\n next = { ...prev, ...(raw as Partial<T>) } as T\n } else {\n next = raw as T\n }\n\n if (Object.is(prev, next)) return\n\n state$(next)\n\n for (const listener of listeners) {\n listener(next, prev)\n }\n }\n\n const getState = (): T => state$()\n const getInitialState = (): T => initialState\n\n const subscribe = (listener: (state: T, prevState: T) => void): (() => void) => {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n }\n\n const api: StoreApi<T> = { setState, getState, getInitialState, subscribe }\n\n const created = factory(setState, getState, api)\n state$(created)\n initialState = created\n\n return api\n}\n","/**\n * Event handler composition for createApp runtime.\n *\n * Extracted from create-app.tsx to reduce nesting depth.\n * Contains: handler context creation, focus navigation dispatch,\n * mouse event dispatch, and key handler dispatch.\n *\n * All functions are pure or near-pure — they don't access the event loop's\n * mutable state (pendingRerender, isRendering, etc.), which stays in create-app.tsx.\n */\n\nimport type { StoreApi } from \"@silvery/create/signal-store\"\n\nimport { createKeyEvent, dispatchKeyEvent } from \"@silvery/ag/focus-events\"\nimport type { FocusManager } from \"@silvery/ag/focus-manager\"\nimport { findByTestID } from \"@silvery/ag/focus-queries\"\nimport { type MouseEventProcessorState, processMouseEvent, hitTest } from \"../mouse-events\"\nimport type { Container } from \"@silvery/ag-react/reconciler\"\nimport { getContainerRoot } from \"@silvery/ag-react/reconciler\"\nimport type { AgNode } from \"@silvery/ag/types\"\nimport type { Key } from \"./keys\"\nimport type { EventHandler, EventHandlerContext, EventHandlers } from \"./create-app\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Namespaced event from a provider.\n */\nexport interface NamespacedEvent {\n type: string\n provider: string\n event: string\n data: unknown\n}\n\n// ============================================================================\n// Handler Context\n// ============================================================================\n\n/**\n * Build the EventHandlerContext passed to user-defined event handlers.\n * Shared by runEventHandler() and press().\n *\n * When the store was created with `tea()` middleware, `dispatch` is\n * automatically wired from the store state.\n */\nexport function createHandlerContext<S>(\n store: StoreApi<S>,\n focusManager: FocusManager,\n container: Container,\n): EventHandlerContext<S> {\n // Detect tea() middleware: store state has a dispatch function\n const state = store.getState() as Record<string, unknown>\n const teaDispatch = typeof state.dispatch === \"function\" ? state.dispatch : undefined\n\n return {\n set: store.setState,\n get: store.getState,\n focusManager,\n focus(testID: string) {\n const root = getContainerRoot(container)\n focusManager.focusById(testID, root, \"programmatic\")\n },\n activateScope(scopeId: string) {\n const root = getContainerRoot(container)\n focusManager.activateScope(scopeId, root)\n },\n getFocusPath() {\n const root = getContainerRoot(container)\n return focusManager.getFocusPath(root)\n },\n dispatch: teaDispatch as EventHandlerContext<S>[\"dispatch\"],\n hitTest(x: number, y: number) {\n const root = getContainerRoot(container)\n return hitTest(root, x, y)\n },\n }\n}\n\n// ============================================================================\n// Focus Navigation\n// ============================================================================\n\n/**\n * Dispatch a key event through the focus system and handle default\n * focus navigation (Tab, Shift+Tab, Enter scope, Escape scope).\n *\n * Returns \"consumed\" if the focus system handled the event (caller should\n * render and return), or \"continue\" if the event should proceed to app handlers.\n */\nexport function handleFocusNavigation(\n input: string,\n parsedKey: Key,\n focusManager: FocusManager,\n container: Container,\n): \"consumed\" | \"continue\" {\n // Dispatch key event to focused node (capture + bubble phases)\n if (focusManager.activeElement) {\n const keyEvent = createKeyEvent(input, parsedKey, focusManager.activeElement)\n dispatchKeyEvent(keyEvent)\n\n // If focus system consumed the event, skip app handlers\n if (keyEvent.propagationStopped || keyEvent.defaultPrevented) {\n return \"consumed\"\n }\n }\n\n const root = getContainerRoot(container)\n\n // Tab: focus next (works even when nothing is focused — starts from first)\n if (parsedKey.tab && !parsedKey.shift) {\n focusManager.focusNext(root)\n return \"consumed\"\n }\n\n // Shift+Tab: focus previous (works even when nothing is focused — starts from last)\n if (parsedKey.tab && parsedKey.shift) {\n focusManager.focusPrev(root)\n return \"consumed\"\n }\n\n // Enter: if focused element has focusScope, enter that scope\n if (parsedKey.return && focusManager.activeElement) {\n const activeEl = focusManager.activeElement\n const props = activeEl.props as Record<string, unknown>\n const testID = typeof props.testID === \"string\" ? props.testID : null\n if (props.focusScope && testID) {\n focusManager.enterScope(testID)\n focusManager.focusNext(root, activeEl)\n return \"consumed\"\n }\n }\n\n // Escape: exit the current focus scope if one is open.\n //\n // Apps handle their own Escape routing via keybindings (close dialogs, exit\n // modes, etc.), so we only intercept Escape when there is an actual focus\n // scope to pop. Previously this also called focusManager.blur() as a\n // fallback, but that consumed Escape before app handlers could run — for\n // example preventing `console.close` from firing while the board has the\n // auto-focused \"board-area\" Box as activeElement. Apps that want the old\n // behaviour can implement it in their own key handler.\n if (parsedKey.escape) {\n if (focusManager.scopeStack.length > 0) {\n const scopeId = focusManager.scopeStack[focusManager.scopeStack.length - 1]!\n focusManager.exitScope()\n const scopeNode = findByTestID(root, scopeId)\n if (scopeNode) {\n focusManager.focus(scopeNode, \"keyboard\")\n }\n return \"consumed\"\n }\n }\n\n return \"continue\"\n}\n\n// ============================================================================\n// Mouse Event Dispatch\n// ============================================================================\n\n/**\n * Dispatch a DOM-level mouse event to the node tree.\n * Called from runEventHandler for mouse events.\n */\nexport function dispatchMouseEventToTree(\n event: NamespacedEvent,\n mouseEventState: MouseEventProcessorState,\n root: AgNode,\n): boolean {\n if (event.event !== \"mouse\" || !event.data) return false\n\n const mouseData = event.data as {\n button: number\n x: number\n y: number\n action: string\n delta?: number\n shift: boolean\n meta: boolean\n ctrl: boolean\n }\n\n return processMouseEvent(\n mouseEventState,\n {\n button: mouseData.button,\n x: mouseData.x,\n y: mouseData.y,\n action: mouseData.action as \"down\" | \"up\" | \"move\" | \"wheel\",\n delta: mouseData.delta,\n shift: mouseData.shift,\n meta: mouseData.meta,\n ctrl: mouseData.ctrl,\n },\n root,\n )\n}\n\n// ============================================================================\n// Event Handler Dispatch\n// ============================================================================\n\n/**\n * Invoke the namespaced handler for a single event (state mutation only, no render).\n * Returns true to continue, false to exit, or \"flush\" for a render barrier.\n *\n * Also dispatches DOM-level mouse events when applicable.\n */\nexport function invokeEventHandler<S>(\n event: NamespacedEvent,\n handlers: EventHandlers<S> | undefined,\n ctx: EventHandlerContext<S>,\n mouseEventState: MouseEventProcessorState,\n container: Container,\n): boolean | \"flush\" {\n // DOM-level mouse event dispatch FIRST — component handlers (onClick, etc.)\n // can call preventDefault() to suppress the app-level handler.\n const root = getContainerRoot(container)\n const prevented = dispatchMouseEventToTree(event, mouseEventState, root)\n\n // Skip app handler if a component called preventDefault()\n if (prevented) return true\n\n const namespacedHandler = handlers?.[event.type as keyof typeof handlers]\n\n if (namespacedHandler && typeof namespacedHandler === \"function\") {\n const result = (namespacedHandler as EventHandler<unknown, S>)(event.data, ctx)\n if (result === \"exit\") return false\n if (result === \"flush\") return \"flush\"\n }\n\n return true\n}\n\n/**\n * Dispatch a term:key event to app handlers (namespaced + legacy).\n * Returns \"exit\" if the handler signaled exit, undefined otherwise.\n */\nexport function dispatchKeyToHandlers<S>(\n input: string,\n parsedKey: Key,\n handlers: EventHandlers<S> | undefined,\n ctx: EventHandlerContext<S>,\n): \"exit\" | undefined {\n // Namespaced handler\n const namespacedHandler = handlers?.[\"term:key\" as keyof typeof handlers]\n if (namespacedHandler && typeof namespacedHandler === \"function\") {\n const result = (namespacedHandler as EventHandler<unknown, S>)({ input, key: parsedKey }, ctx)\n if (result === \"exit\") return \"exit\"\n }\n\n // Legacy handler\n if ((handlers as any)?.key) {\n const result = (handlers as any).key(input, parsedKey, ctx)\n if (result === \"exit\") return \"exit\"\n }\n\n return undefined\n}\n","/**\n * Terminal Lifecycle Events\n *\n * Handles suspend/resume (Ctrl+Z/SIGCONT) and interrupt (Ctrl+C) for TUI apps.\n * When stdin is in raw mode, the terminal does not generate SIGTSTP/SIGINT for\n * Ctrl+Z/Ctrl+C. This module intercepts the raw bytes and manages the full\n * terminal state save/restore cycle.\n *\n * Inspired by ncurses (endwin/refresh), bubbletea, and Textual.\n *\n * Protocols managed:\n * - Raw mode (stdin)\n * - Alternate screen buffer (DEC private mode 1049)\n * - Cursor visibility (DEC private mode 25)\n * - Mouse tracking (modes 1000, 1002, 1006)\n * - Kitty keyboard protocol (CSI > flags u / CSI < u)\n * - Bracketed paste (DEC private mode 2004)\n * - SGR attributes (reset via CSI 0 m)\n */\n\nimport { writeSync } from \"node:fs\"\nimport { enableKittyKeyboard, disableKittyKeyboard, enableMouse, disableMouse, resetCursorStyle } from \"../output\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Options for terminal lifecycle event handling.\n */\nexport interface TerminalLifecycleOptions {\n /** Handle Ctrl+Z by suspending the process. Default: true */\n suspendOnCtrlZ?: boolean\n /** Handle Ctrl+C by exiting the process. Default: true */\n exitOnCtrlC?: boolean\n /** Called before suspend. Return false to prevent. */\n onSuspend?: () => boolean | void\n /** Called after resume from suspend. */\n onResume?: () => void\n /** Called on Ctrl+C. Return false to prevent exit. */\n onInterrupt?: () => boolean | void\n}\n\n/**\n * Snapshot of terminal protocol state for save/restore across suspend/resume.\n */\nexport interface TerminalState {\n rawMode: boolean\n alternateScreen: boolean\n cursorHidden: boolean\n mouseEnabled: boolean\n kittyEnabled: boolean\n kittyFlags: number\n bracketedPaste: boolean\n focusReporting: boolean\n}\n\n// ============================================================================\n// State Capture\n// ============================================================================\n\n/**\n * Capture the current terminal protocol state.\n *\n * This builds a TerminalState from the options passed to run()/createApp(),\n * since terminal state is not directly queryable from the OS.\n */\nexport function captureTerminalState(opts: {\n alternateScreen?: boolean\n cursorHidden?: boolean\n mouse?: boolean\n kitty?: boolean\n kittyFlags?: number\n bracketedPaste?: boolean\n rawMode?: boolean\n focusReporting?: boolean\n}): TerminalState {\n return {\n rawMode: opts.rawMode ?? true,\n alternateScreen: opts.alternateScreen ?? false,\n cursorHidden: opts.cursorHidden ?? true,\n mouseEnabled: opts.mouse ?? false,\n kittyEnabled: opts.kitty ?? false,\n kittyFlags: opts.kittyFlags ?? 11, // DISAMBIGUATE(1) | REPORT_EVENTS(2) | REPORT_ALL_KEYS(8)\n bracketedPaste: opts.bracketedPaste ?? false,\n focusReporting: opts.focusReporting ?? false,\n }\n}\n\n// ============================================================================\n// Restore (before suspend / on exit)\n// ============================================================================\n\n/**\n * Restore terminal to normal state before suspending or exiting.\n *\n * Uses writeSync for reliability during signal handling (async write\n * may not complete before the process suspends).\n *\n * Order matters: disable protocols first, then show cursor, then exit\n * alternate screen, then disable raw mode.\n */\nexport function restoreTerminalState(stdout: NodeJS.WriteStream, stdin: NodeJS.ReadStream): void {\n // Step 1: Stop consuming stdin — prevent processing of in-flight events\n try {\n stdin.removeAllListeners(\"data\")\n stdin.pause()\n } catch {\n // Ignore — stdin may be closed\n }\n\n // Step 2: Send all protocol disable sequences\n const sequences = [\n \"\\x1b[0m\", // Reset SGR attributes\n \"\\x1b[?1004l\", // Disable focus reporting\n disableMouse(), // Disable all mouse tracking modes\n disableKittyKeyboard(), // Pop Kitty keyboard protocol\n \"\\x1b[?2004l\", // Disable bracketed paste\n resetCursorStyle(), // Reset cursor shape to terminal default (DECSCUSR 0)\n \"\\x1b[?25h\", // Show cursor\n \"\\x1b[?1049l\", // Exit alternate screen\n ].join(\"\")\n\n // Use writeSync for reliability during signal handlers — but only when stdout\n // is the real process.stdout. Mock stdouts have fd:1 which bypasses the mock.\n if (stdout === process.stdout) {\n try {\n writeSync((stdout as unknown as { fd: number }).fd, sequences)\n } catch {\n try {\n stdout.write(sequences)\n } catch {\n // Terminal may already be gone (e.g., SSH disconnect)\n }\n }\n } else {\n try {\n stdout.write(sequences)\n } catch {\n // Terminal may already be gone\n }\n }\n\n // Step 3: Drain in-flight stdin bytes — the terminal may have queued events\n // (Kitty key release, SGR mouse) before processing our disable sequences.\n // Discard them so they don't leak to the shell prompt.\n try {\n stdin.resume()\n while (stdin.read() !== null) {\n // discard buffered data\n }\n stdin.pause()\n } catch {\n // Ignore — best-effort drain\n }\n\n // Step 4: Disable raw mode on stdin\n if (stdin.isTTY && stdin.isRaw) {\n try {\n stdin.setRawMode(false)\n } catch {\n // Ignore - stdin may already be closed\n }\n }\n}\n\n// ============================================================================\n// Resume (after SIGCONT)\n// ============================================================================\n\n/**\n * Re-enter TUI mode after resuming from suspend (SIGCONT).\n *\n * Restores all protocols that were active before suspend, in the correct\n * order: raw mode first, then alternate screen, then protocols, then\n * trigger a full redraw via synthetic resize.\n */\nexport function resumeTerminalState(state: TerminalState, stdout: NodeJS.WriteStream, stdin: NodeJS.ReadStream): void {\n // Re-enable raw mode first (needed to receive key input)\n if (state.rawMode && stdin.isTTY) {\n try {\n stdin.setRawMode(true)\n stdin.resume()\n } catch {\n // Ignore - may fail if stdin is closed\n }\n }\n\n // Build the sequence of escape codes to restore TUI state\n const sequences: string[] = []\n\n if (state.alternateScreen) {\n sequences.push(\"\\x1b[?1049h\") // Enter alternate screen\n }\n\n // Clear screen and home cursor (always needed after resume to get a clean slate)\n sequences.push(\"\\x1b[2J\\x1b[H\")\n\n if (state.cursorHidden) {\n sequences.push(\"\\x1b[?25l\") // Hide cursor\n }\n\n if (state.kittyEnabled) {\n sequences.push(enableKittyKeyboard(state.kittyFlags as 1))\n }\n\n if (state.mouseEnabled) {\n sequences.push(enableMouse())\n }\n\n if (state.bracketedPaste) {\n sequences.push(\"\\x1b[?2004h\") // Enable bracketed paste\n }\n\n if (state.focusReporting) {\n sequences.push(\"\\x1b[?1004h\") // Enable focus reporting\n }\n\n // Write all sequences — use writeSync only for real process.stdout\n const joined = sequences.join(\"\")\n if (stdout === process.stdout) {\n try {\n writeSync((stdout as unknown as { fd: number }).fd, joined)\n } catch {\n try {\n stdout.write(joined)\n } catch {\n // Terminal may be gone\n }\n }\n } else {\n try {\n stdout.write(joined)\n } catch {\n // Terminal may be gone\n }\n }\n\n // Emit synthetic resize to trigger full redraw.\n // The screen was cleared, so the runtime needs to render a complete frame.\n stdout.emit(\"resize\")\n}\n\n// ============================================================================\n// Suspend Flow\n// ============================================================================\n\n/**\n * Execute the full suspend flow: save state, restore terminal, SIGTSTP,\n * and set up SIGCONT handler to resume.\n *\n * @param state - Terminal state snapshot to restore on resume\n * @param stdout - Output stream\n * @param stdin - Input stream\n * @param onResume - Optional callback after resume\n */\nexport function performSuspend(\n state: TerminalState,\n stdout: NodeJS.WriteStream,\n stdin: NodeJS.ReadStream,\n onResume?: () => void,\n): void {\n // Restore terminal to normal\n restoreTerminalState(stdout, stdin)\n\n // Register one-time SIGCONT handler BEFORE sending SIGTSTP\n process.once(\"SIGCONT\", () => {\n // Re-enter TUI mode\n resumeTerminalState(state, stdout, stdin)\n onResume?.()\n })\n\n // Actually suspend the process\n process.kill(process.pid, \"SIGTSTP\")\n}\n\n// ============================================================================\n// Raw byte constants\n// ============================================================================\n\n/** Ctrl+C raw byte (ETX - End of Text) */\nexport const CTRL_C = \"\\x03\"\n\n/** Ctrl+Z raw byte (SUB - Substitute) */\nexport const CTRL_Z = \"\\x1a\"\n","/**\n * Keypress performance instrumentation.\n *\n * Zero-overhead when TRACE is not set — all logging uses optional chaining.\n * When TRACE=silvery:perf is set, emits span timing for each keypress cycle\n * and a summary on exit.\n *\n * @example\n * ```bash\n * TRACE=silvery:perf bun km view ~/vault\n * # → SPAN silvery:perf:keypress (5ms) {key: \"j\"}\n * # → on exit: keypress summary: 42 presses, mean=4.2ms, p95=12.1ms, max=18.3ms, overruns=2\n * ```\n */\n\nimport { createLogger } from \"loggily\"\n\n/** Exported for ?. chaining in hot paths: `perfLog.span?.(\"keypress\", { key })` */\nexport const perfLog = createLogger(\"silvery:perf\")\n\n// ============================================================================\n// Budget tracking (only active when spans are created)\n// ============================================================================\n\nlet samples: Array<{ key: string; durationMs: number }> | null = null\nlet budgetOverruns = 0\n\n/**\n * Record a completed keypress and check budget.\n * Only records when tracing is active (samples array initialized by startTracking).\n * Call after the keypress cycle completes (render done).\n */\nexport function checkBudget(key: string, durationMs: number, budgetMs = 16) {\n if (samples) {\n samples.push({ key, durationMs })\n }\n if (durationMs > budgetMs) {\n budgetOverruns++\n perfLog.warn?.(`keypress over budget: ${key} took ${durationMs.toFixed(1)}ms (budget: ${budgetMs}ms)`)\n }\n}\n\n/** Call once when first span is created to start accumulating samples. */\nexport function startTracking() {\n if (!samples) samples = []\n}\n\n// ============================================================================\n// Exit summary\n// ============================================================================\n\n/**\n * Log a summary of all recorded keypress spans.\n *\n * Call when the app unmounts/exits. Only produces output when TRACE is\n * enabled and at least one span was recorded.\n */\nexport function logExitSummary() {\n if (!samples || samples.length === 0) return\n\n const durations = samples.map((s) => s.durationMs).sort((a, b) => a - b)\n const total = samples.length\n const mean = durations.reduce((sum, d) => sum + d, 0) / total\n const p95Index = Math.min(Math.floor(total * 0.95), total - 1)\n const p95 = durations[p95Index]!\n const max = durations[total - 1]!\n\n perfLog.info?.(\n `keypress summary: ${total} presses, mean=${mean.toFixed(1)}ms, p95=${p95.toFixed(1)}ms, max=${max.toFixed(1)}ms, overruns=${budgetOverruns}`,\n )\n\n // Reset for potential reuse (tests)\n samples = null\n budgetOverruns = 0\n}\n\n/**\n * Reset internal state. Useful for tests to ensure clean state between runs.\n */\nexport function resetPerfState() {\n samples = null\n budgetOverruns = 0\n}\n\n/**\n * Get current sample count. Useful for tests.\n */\nexport function getSampleCount(): number {\n return samples?.length ?? 0\n}\n","/**\n * createApp() - Layer 3 entry point for silvery-loop\n *\n * Provides signal-backed store integration with unified providers.\n * Providers are stores (getState/subscribe) + event sources (events()).\n *\n * @example\n * ```tsx\n * import { createApp, useApp } from '@silvery/create/create-app'\n * import { createTermProvider } from '@silvery/ag-term/runtime'\n *\n * const app = createApp(\n * // Store factory\n * ({ term }) => (set, get) => ({\n * count: 0,\n * increment: () => set(s => ({ count: s.count + 1 })),\n * }),\n * // Event handlers - namespaced as 'provider:event'\n * {\n * 'term:key': ({ input, key }, { set }) => {\n * if (input === 'j') set(s => ({ count: s.count + 1 }))\n * if (input === 'q') return 'exit'\n * },\n * 'term:resize': ({ cols, rows }, { set }) => {\n * // handle resize\n * },\n * }\n * )\n *\n * function Counter() {\n * const count = useApp(s => s.count)\n * return <Text>Count: {count}</Text>\n * }\n *\n * const term = createTermProvider(process.stdin, process.stdout)\n * await app.run(<Counter />, { term })\n *\n * // Frame iteration:\n * for await (const frame of app.run(<Counter />, { term })) {\n * expect(frame.text).toContain('Count:')\n * }\n * ```\n */\n\nimport { writeSync } from \"node:fs\"\nimport process from \"node:process\"\nimport React, { createContext, useContext, useEffect, useRef, type ReactElement } from \"react\"\nimport { type StateCreator, type StoreApi, createStore } from \"@silvery/create/signal-store\"\n\nimport { createTerm } from \"@silvery/ag-term/ansi\"\nimport {\n CacheBackendContext,\n CapabilityRegistryContext,\n FocusManagerContext,\n RuntimeContext,\n type RuntimeContextValue,\n StdoutContext,\n StderrContext,\n TermContext,\n} from \"@silvery/ag-react/context\"\nimport { SilveryErrorBoundary } from \"@silvery/ag-react/error-boundary\"\nimport { createFocusManager } from \"@silvery/ag/focus-manager\"\nimport { createCursorStore, CursorProvider } from \"@silvery/ag-react/hooks/useCursor\"\nimport { createFocusEvent, dispatchFocusEvent } from \"@silvery/ag/focus-events\"\nimport { executeRender } from \"@silvery/ag-term/pipeline\"\nimport { createAg, type Ag } from \"@silvery/ag-term/ag\"\nimport { createPipeline } from \"@silvery/ag-term/measurer\"\nimport {\n isTextSizingLikelySupported,\n detectTextSizingSupport,\n getCachedProbeResult,\n} from \"@silvery/ag-term/text-sizing\"\nimport { createWidthDetector, applyWidthConfig } from \"@silvery/ag-term\"\nimport { IncrementalRenderMismatchError } from \"@silvery/ag-term/scheduler\"\nimport { isAnyDirty } from \"@silvery/ag/epoch\"\nimport {\n createContainer,\n createFiberRoot,\n getContainerRoot,\n reconciler,\n setOnNodeRemoved,\n} from \"@silvery/ag-react/reconciler\"\nimport { map, merge, takeUntil } from \"@silvery/create/streams\"\nimport { createBuffer } from \"@silvery/ag-term/runtime/create-buffer\"\nimport { createRuntime } from \"@silvery/ag-term/runtime/create-runtime\"\nimport {\n createHandlerContext,\n dispatchKeyToHandlers,\n handleFocusNavigation,\n invokeEventHandler,\n type NamespacedEvent,\n} from \"@silvery/ag-term/runtime/event-handlers\"\nimport { keyToAnsi, keyToKittyAnsi, isModifierOnlyEvent } from \"@silvery/ag/keys\"\nimport { parseKey, type Key } from \"@silvery/ag-term/runtime/keys\"\nimport { ensureLayoutEngine } from \"@silvery/ag-term/runtime/layout\"\nimport {\n createMouseEventProcessor,\n updateKeyboardModifiers,\n findContainBoundary,\n selectionHitTest,\n} from \"@silvery/ag-term/mouse-events\"\nimport {\n enableKittyKeyboard,\n disableKittyKeyboard,\n KittyFlags,\n enableMouse,\n disableMouse,\n resetCursorStyle,\n enterAlternateScreen,\n leaveAlternateScreen,\n} from \"@silvery/ag-term/output\"\nimport { enableFocusReporting } from \"@silvery/ag-term/focus-reporting\"\nimport { detectKittyFromStdio } from \"@silvery/ag-term/kitty-detect\"\nimport { captureTerminalState, performSuspend } from \"@silvery/ag-term/runtime/terminal-lifecycle\"\nimport { type TermProvider, createTermProvider } from \"@silvery/ag-term/runtime/term-provider\"\nimport type { Buffer, Dims, Provider, RenderTarget } from \"@silvery/ag-term/runtime/types\"\nimport { createTerminalSelectionState, terminalSelectionUpdate, extractText } from \"@silvery/headless/selection\"\nimport { createSelectionBridge, type SelectionFeature } from \"@silvery/ag-term/features/selection\"\nimport { renderSelectionOverlay } from \"@silvery/ag-term/selection-renderer\"\nimport { createCapabilityRegistry, type CapabilityRegistry } from \"@silvery/create/internal/capability-registry\"\nimport { SELECTION_CAPABILITY } from \"@silvery/create/internal/capabilities\"\nimport { createVirtualScrollback } from \"@silvery/ag-term/virtual-scrollback\"\nimport { createSearchState, searchUpdate, renderSearchBar, type SearchMatch } from \"@silvery/ag-term/search-overlay\"\nimport { createOutputGuard, type OutputGuard } from \"@silvery/ag-term/ansi/output-guard\"\nimport { perfLog, checkBudget, logExitSummary, startTracking } from \"@silvery/ag-term/runtime/perf\"\nimport { createLogger } from \"loggily\"\n\nconst log = createLogger(\"silvery:app\")\n\n// ============================================================================\n// Feature-detection flags — hoisted to module scope.\n//\n// These env var checks were historically evaluated on every doRender() call,\n// adding ~10μs/frame overhead to production renders. They are all static for\n// the lifetime of the process, so we compute them once at module load.\n//\n// When the instrumentation flag is off (the common case), branches guarded by\n// these constants are dead-code eliminated by V8's optimizer — turning them\n// into no-ops on the hot path.\n// ============================================================================\nconst ENV = typeof process !== \"undefined\" ? process.env : undefined\nconst NO_INCREMENTAL = ENV?.SILVERY_NO_INCREMENTAL === \"1\"\nconst STRICT_MODE = (() => {\n const v = ENV?.SILVERY_STRICT\n return !!v && v !== \"0\" && v !== \"false\"\n})()\nconst CELL_DEBUG = (() => {\n const v = ENV?.SILVERY_CELL_DEBUG\n if (!v || !v.includes(\",\")) return null\n const [cx, cy] = v.split(\",\").map(Number)\n if (!Number.isFinite(cx) || !Number.isFinite(cy)) return null\n return { x: cx, y: cy }\n})()\n// INSTRUMENTED = any diagnostic is on. When false, the per-frame resets of\n// diagnostic globals can be skipped entirely — they are only consumed by the\n// STRICT/CELL_DEBUG paths. This is the primary hot-path win: when no\n// instrumentation is active (production), doRender skips ~8 global ops/frame.\nconst INSTRUMENTED = STRICT_MODE || CELL_DEBUG !== null\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Check if value is a Provider with events (full interface).\n */\nfunction isFullProvider(value: unknown): value is Provider<unknown, Record<string, unknown>> {\n if (value === null || value === undefined) return false\n // Term is a Proxy wrapping chalk, so typeof is \"function\" not \"object\"\n if (typeof value !== \"object\" && typeof value !== \"function\") return false\n return (\n \"getState\" in value &&\n \"subscribe\" in value &&\n \"events\" in value &&\n typeof (value as Provider).getState === \"function\" &&\n typeof (value as Provider).subscribe === \"function\" &&\n typeof (value as Provider).events === \"function\"\n )\n}\n\n/**\n * Check if value is a basic Provider (just getState/subscribe, Zustand-compatible).\n */\nfunction isBasicProvider(value: unknown): value is {\n getState(): unknown\n subscribe(l: (s: unknown) => void): () => void\n} {\n if (value === null || value === undefined) return false\n // Term is a Proxy wrapping chalk, so typeof is \"function\" not \"object\"\n if (typeof value !== \"object\" && typeof value !== \"function\") return false\n return (\n \"getState\" in value &&\n \"subscribe\" in value &&\n typeof (value as { getState: unknown }).getState === \"function\" &&\n typeof (value as { subscribe: unknown }).subscribe === \"function\"\n )\n}\n\n/**\n * Event handler context passed to handlers.\n *\n * When the store uses `tea()` middleware, `dispatch` is available with the\n * correct Op type inferred from the store. For non-tea stores it's `undefined`.\n */\nexport interface EventHandlerContext<S> {\n set: StoreApi<S>[\"setState\"]\n get: StoreApi<S>[\"getState\"]\n /** The tree-based focus manager */\n focusManager: import(\"@silvery/create/focus-manager\").FocusManager\n /** Convenience: focus a node by testID */\n focus(testID: string): void\n /** Activate a peer focus scope (saves/restores focus per scope) */\n activateScope(scopeId: string): void\n /** Get the focus path from focused node to root */\n getFocusPath(): string[]\n /**\n * Dispatch an operation through the tea() reducer.\n *\n * Available when the store was created with `tea()` middleware from `silvery/tea`.\n * Type-safe: the Op type is inferred from the store's TeaSlice.\n * For non-tea stores, this is `undefined`.\n */\n dispatch?: \"dispatch\" extends keyof S ? S[\"dispatch\"] : undefined\n /** Hit-test the render tree at (x, y). Returns the deepest SilveryNode at that point, or null. */\n hitTest(x: number, y: number): import(\"@silvery/create/types\").AgNode | null\n}\n\n/**\n * Generic event handler function.\n * Return 'exit' to exit the app.\n */\nexport type EventHandler<T, S> = (data: T, ctx: EventHandlerContext<S>) => void | \"exit\" | \"flush\"\n\n/**\n * Event handlers map.\n * Keys are namespaced as 'provider:event' (e.g., 'term:key', 'term:resize').\n */\nexport type EventHandlers<S> = {\n [event: `${string}:${string}`]: EventHandler<unknown, S> | undefined\n}\n\n/**\n * Options for app.run().\n */\nexport interface AppRunOptions {\n /** Terminal dimensions (default: from process.stdout) */\n cols?: number\n rows?: number\n /** Standard output (default: process.stdout) */\n stdout?: NodeJS.WriteStream\n /** Standard input (default: process.stdin) */\n stdin?: NodeJS.ReadStream\n /**\n * Plain writable sink for ANSI output. Headless mode with active output.\n * Requires cols and rows. Input via handle.press().\n */\n writable?: { write(data: string): void }\n /**\n * Subscribe to resize events in headless mode.\n * Called with a handler that should be invoked when dimensions change.\n * Returns an unsubscribe function.\n */\n onResize?: (handler: (dims: { cols: number; rows: number }) => void) => () => void\n /** Abort signal for external cleanup */\n signal?: AbortSignal\n /** Enter alternate screen buffer (clean slate, restore on exit). Default: false */\n alternateScreen?: boolean\n /** Use Kitty keyboard protocol encoding for press(). Default: false */\n kittyMode?: boolean\n /**\n * Enable Kitty keyboard protocol.\n * - `true`: auto-detect and enable with DISAMBIGUATE flag (1)\n * - number: enable with specific KittyFlags bitfield\n * - `false`/undefined: don't enable (default)\n */\n kitty?: boolean | number\n /**\n * Enable SGR mouse tracking (mode 1006).\n * When true, enables mouse events and disables on cleanup.\n * Default: false\n */\n mouse?: boolean\n /**\n * Enable virtual inline mode: alt screen with virtual scrollback buffer.\n * Provides scrollable history + search (Ctrl+F) while using fullscreen rendering.\n * Default: false\n */\n virtualInline?: boolean\n /**\n * Handle Ctrl+Z by suspending the process (save terminal state,\n * send SIGTSTP, restore on SIGCONT). Default: true\n */\n suspendOnCtrlZ?: boolean\n /**\n * Handle Ctrl+C by restoring terminal and exiting.\n * Default: true\n */\n exitOnCtrlC?: boolean\n /** Called before suspend. Return false to prevent. */\n onSuspend?: () => boolean | void\n /** Called after resume from suspend. */\n onResume?: () => void\n /** Called on Ctrl+C. Return false to prevent exit. */\n onInterrupt?: () => boolean | void\n /**\n * Enable Kitty text sizing protocol (OSC 66) for PUA characters.\n * When enabled, nerdfont/powerline icons are measured as 2-wide and\n * wrapped in OSC 66 sequences so the terminal renders them at the\n * correct width.\n * - `true`: force enable\n * - `\"auto\"`: use heuristic, then probe to verify (progressive enhancement)\n * - `\"probe\"`: start disabled, probe async, enable on confirmation\n * - `false`/undefined: disabled (default)\n */\n textSizing?: boolean | \"auto\" | \"probe\"\n /**\n * Enable DEC width mode detection (modes 1020-1023).\n * Queries the terminal for its actual character width settings (emoji,\n * CJK, private-use area) and updates the measurer accordingly.\n * - `true`: always run width detection probe\n * - `\"auto\"`: run probe when caps are provided (default for real terminals)\n * - `false`/undefined: disabled (default)\n */\n widthDetection?: boolean | \"auto\"\n /**\n * Enable terminal focus reporting (CSI ?1004h).\n * When enabled, the terminal sends focus-in/focus-out events that are\n * dispatched as 'term:focus' events with `{ focused: boolean }`.\n * Default: false\n */\n focusReporting?: boolean\n /**\n * Enable buffer-level text selection via mouse drag.\n * When enabled, left mouse drag selects text, and mouse up copies\n * selected text to clipboard via OSC 52.\n * Default: true when mouse is enabled\n */\n selection?: boolean\n /**\n * Terminal capabilities for width measurement and output suppression.\n * When provided, configures the render pipeline to use these caps\n * (scoped width measurer + output phase). Typically from term.caps.\n */\n caps?: import(\"@silvery/ag-term/terminal-caps\").TerminalCaps\n /**\n * Guard stdout/stderr in alt screen mode. When true (the default for\n * alternateScreen), intercepts process.stdout.write and process.stderr.write\n * so that only silvery's render pipeline can write to stdout. Non-silvery\n * stderr writes are redirected to DEBUG_LOG if set, otherwise suppressed.\n * This prevents display corruption from libraries that write directly to\n * process.stdout/stderr (e.g., loggily, debug).\n *\n * - `true`: enable output guard (default when alternateScreen is true)\n * - `false`: disable output guard\n */\n guardOutput?: boolean\n /**\n * Root component that wraps the element tree with additional providers.\n * Set by plugins (e.g., withInk) via the `app.Root` pattern.\n * The Root component receives children and wraps them with providers.\n */\n Root?: React.ComponentType<{ children: React.ReactNode }>\n /**\n * Capability registry from the composition layer (e.g., withDomEvents, withTerminal).\n * When provided, exposed to React components via CapabilityRegistryContext so\n * hooks like useSelection() can discover interaction features.\n */\n capabilityRegistry?: import(\"@silvery/ag-react/context\").CapabilityLookup\n /** Providers and plain values to inject */\n [key: string]: unknown\n}\n\n/**\n * Handle returned by app.run().\n *\n * Also AsyncIterable<Buffer> — iterate to get frames after each event:\n * ```typescript\n * for await (const frame of app.run(<App />)) {\n * expect(frame.text).toContain('expected')\n * }\n * ```\n */\nexport interface AppHandle<S> {\n /** Current rendered text (no ANSI) */\n readonly text: string\n /** Live reconciler root node (for locator queries) */\n readonly root: import(\"@silvery/ag/types\").AgNode\n /** Current terminal buffer (cell-level access) */\n readonly buffer: import(\"@silvery/ag-term/buffer\").TerminalBuffer | null\n /** Access to the Zustand store */\n readonly store: StoreApi<S>\n /** Wait until the app exits */\n waitUntilExit(): Promise<void>\n /** Unmount and cleanup */\n unmount(): void\n /** Dispose (alias for unmount) — enables `using` */\n [Symbol.dispose](): void\n /** Send a key press (simulates term:key event) */\n press(key: string): Promise<void>\n /** Iterate frames yielded after each event */\n [Symbol.asyncIterator](): AsyncIterator<Buffer>\n}\n\n/**\n * App definition returned by createApp().\n */\nexport interface AppDefinition<S> {\n run(element: ReactElement, options?: AppRunOptions): AppRunner<S>\n}\n\n/**\n * Result of app.run() — both a Promise<AppHandle> and an AsyncIterable<Buffer>.\n *\n * - `await app.run(el)` → AppHandle (backward compat)\n * - `for await (const frame of app.run(el))` → iterate frames\n */\nexport interface AppRunner<S> extends AsyncIterable<Buffer>, PromiseLike<AppHandle<S>> {}\n\n// ============================================================================\n// Store Context\n// ============================================================================\n\nexport const StoreContext = createContext<StoreApi<unknown> | null>(null)\n\n/**\n * Hook for accessing app state with selectors.\n *\n * @example\n * ```tsx\n * const count = useApp(s => s.count)\n * const { count, increment } = useApp(s => ({ count: s.count, increment: s.increment }))\n * ```\n */\nexport function useApp<S, T>(selector: (state: S) => T): T {\n const store = useContext(StoreContext) as StoreApi<S> | null\n if (!store) throw new Error(\"useApp must be used within createApp().run()\")\n\n const [state, setState] = React.useState(() => selector(store.getState()))\n const selectorRef = useRef(selector)\n selectorRef.current = selector\n\n useEffect(() => {\n return store.subscribe((newState) => {\n const next = selectorRef.current(newState)\n // Only update if the selected value actually changed (avoids\n // unnecessary re-renders when unrelated store slices change)\n setState((prev) => (Object.is(prev, next) ? prev : next))\n })\n }, [store])\n\n return state\n}\n\n/**\n * Shallow comparison for plain objects.\n * Returns true if objects have same keys with Object.is() equal values.\n */\nfunction shallowEqual<T>(a: T, b: T): boolean {\n if (Object.is(a, b)) return true\n if (typeof a !== \"object\" || typeof b !== \"object\" || a === null || b === null) {\n return false\n }\n const keysA = Object.keys(a as Record<string, unknown>)\n const keysB = Object.keys(b as Record<string, unknown>)\n if (keysA.length !== keysB.length) return false\n for (const key of keysA) {\n if (!Object.is((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])) {\n return false\n }\n }\n return true\n}\n\n/**\n * Hook for accessing app state with shallow comparison.\n *\n * Like useApp, but uses shallow object comparison instead of Object.is().\n * Use when your selector returns a new object on each call — this prevents\n * re-renders when all individual fields are unchanged.\n *\n * @example\n * ```tsx\n * const { cursor, mode } = useAppShallow(s => ({\n * cursor: s.cursorNodeId,\n * mode: s.viewMode,\n * }))\n * ```\n */\nexport function useAppShallow<S, T>(selector: (state: S) => T): T {\n const store = useContext(StoreContext) as StoreApi<S> | null\n if (!store) throw new Error(\"useAppShallow must be used within createApp().run()\")\n\n const [state, setState] = React.useState(() => selector(store.getState()))\n const selectorRef = useRef(selector)\n selectorRef.current = selector\n\n useEffect(() => {\n return store.subscribe((newState) => {\n const next = selectorRef.current(newState)\n setState((prev) => (shallowEqual(prev, next) ? prev : next))\n })\n }, [store])\n\n return state\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Create an app with Zustand store and provider integration.\n *\n * This is Layer 3 - it provides:\n * - Zustand store with fine-grained subscriptions\n * - Providers as unified stores + event sources\n * - Event handlers namespaced as 'provider:event'\n *\n * @param factory Store factory function that receives providers\n * @param handlers Optional event handlers (namespaced as 'provider:event')\n */\nexport function createApp<I extends Record<string, unknown>, S extends Record<string, unknown>>(\n factory: (inject: I) => StateCreator<S>,\n handlers?: EventHandlers<S & I>,\n): AppDefinition<S & I> {\n return {\n run(element: ReactElement, options: AppRunOptions = {}): AppRunner<S & I> {\n // Lazy-init: the actual setup happens once, on first access\n let handlePromise: Promise<AppHandle<S & I>> | null = null\n\n const init = (): Promise<AppHandle<S & I>> => {\n if (handlePromise) return handlePromise\n handlePromise = initApp(factory, handlers, element, options)\n return handlePromise\n }\n\n return {\n // PromiseLike — makes `await app.run(el)` work\n then<TResult1 = AppHandle<S & I>, TResult2 = never>(\n onfulfilled?: ((value: AppHandle<S & I>) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return init().then(onfulfilled, onrejected)\n },\n\n // AsyncIterable — makes `for await (const frame of app.run(el))` work\n [Symbol.asyncIterator](): AsyncIterator<Buffer> {\n let handle: AppHandle<S & I> | null = null\n let iterator: AsyncIterator<Buffer> | null = null\n let started = false\n\n return {\n async next(): Promise<IteratorResult<Buffer>> {\n if (!started) {\n started = true\n handle = await init()\n iterator = handle[Symbol.asyncIterator]()\n }\n return iterator!.next()\n },\n async return(): Promise<IteratorResult<Buffer>> {\n if (handle) handle.unmount()\n return { done: true, value: undefined as unknown as Buffer }\n },\n }\n },\n }\n },\n }\n}\n\n/**\n * Initialize the app — extracted from run() for clarity.\n */\nasync function initApp<I extends Record<string, unknown>, S extends Record<string, unknown>>(\n factory: (inject: I) => StateCreator<S>,\n handlers: EventHandlers<S & I> | undefined,\n element: ReactElement,\n options: AppRunOptions,\n): Promise<AppHandle<S & I>> {\n const {\n cols: explicitCols,\n rows: explicitRows,\n stdout: explicitStdout,\n stdin = process.stdin,\n signal: externalSignal,\n alternateScreen = false,\n kittyMode: explicitKittyMode,\n kitty: kittyOption,\n mouse: mouseOption = false,\n virtualInline: virtualInlineOption = false,\n suspendOnCtrlZ: suspendOption = true,\n exitOnCtrlC: exitOnCtrlCOption = true,\n onSuspend: onSuspendHook,\n onResume: onResumeHook,\n onInterrupt: onInterruptHook,\n textSizing: textSizingOption,\n widthDetection: widthDetectionOption,\n focusReporting: focusReportingOption = false,\n selection: selectionOption,\n caps: capsOption,\n guardOutput: guardOutputOption,\n Root: RootComponent,\n capabilityRegistry: capabilityRegistryOption,\n writable: explicitWritable,\n onResize: explicitOnResize,\n ...injectValues\n } = options\n\n // Derive kitty mode for press(): use explicit kittyMode if set, otherwise\n // auto-enable when kitty protocol is active (so press() encodes modifier keys correctly)\n const useKittyMode = explicitKittyMode ?? !!kittyOption\n\n const headless = (explicitCols != null && explicitRows != null && !explicitStdout) || explicitWritable != null\n const cols = explicitCols ?? process.stdout.columns ?? 80\n const rows = explicitRows ?? process.stdout.rows ?? 24\n const stdout = explicitStdout ?? process.stdout\n\n // Output guard: created after protocol setup (see below).\n // Only guard when using real process.stdout — mock stdouts don't benefit from\n // the guard (which patches process.stdout.write), and it would route render\n // output to the real stdout instead of the mock.\n const isRealStdout = stdout === process.stdout\n const shouldGuardOutput = guardOutputOption ?? (alternateScreen && !headless && isRealStdout)\n let outputGuard: OutputGuard | null = null\n\n // Initialize layout engine\n await ensureLayoutEngine()\n\n // Create abort controller for cleanup\n const controller = new AbortController()\n const signal = controller.signal\n\n // Wire external signal\n if (externalSignal) {\n if (externalSignal.aborted) {\n controller.abort()\n } else {\n externalSignal.addEventListener(\"abort\", () => controller.abort(), {\n once: true,\n })\n }\n }\n\n // Separate providers from plain values\n const providers: Record<string, Provider<unknown, Record<string, unknown>>> = {}\n const plainValues: Record<string, unknown> = {}\n const providerCleanups: (() => void)[] = []\n\n // Create term provider if not provided\n let termProvider: TermProvider | null = null\n if (!(\"term\" in injectValues) || !isFullProvider(injectValues.term)) {\n // In headless mode, provide mock streams so termProvider doesn't touch real stdin/stdout.\n // When onResize is provided, the mock supports resize events so the term provider\n // picks up dimension changes and triggers re-renders through the event loop.\n const resizeListeners = new Set<() => void>()\n const termStdout = headless\n ? ({\n columns: cols,\n rows,\n write: () => true,\n isTTY: false,\n on(event: string, handler: () => void) {\n if (event === \"resize\") resizeListeners.add(handler)\n return termStdout\n },\n off(event: string, handler: () => void) {\n if (event === \"resize\") resizeListeners.delete(handler)\n return termStdout\n },\n } as unknown as NodeJS.WriteStream)\n : stdout\n const termStdin = headless\n ? ({\n isTTY: false,\n on: () => termStdin,\n off: () => termStdin,\n setRawMode: () => {},\n resume: () => {},\n pause: () => {},\n setEncoding: () => {},\n } as unknown as NodeJS.ReadStream)\n : stdin\n termProvider = createTermProvider(termStdin, termStdout, { cols, rows })\n providers.term = termProvider as unknown as Provider<unknown, Record<string, unknown>>\n providerCleanups.push(() => termProvider![Symbol.dispose]())\n\n // Wire onResize to the mock termStdout so the term provider sees resize events.\n // This updates:\n // 1. currentDims — so getDims() returns correct values for doRender()\n // 2. mock termStdout columns/rows — so the term provider reads correct dimensions\n // 3. mock termStdout resize listeners — triggers term:resize through the provider's\n // event stream → event loop → doRender()\n if (headless && explicitOnResize) {\n const unsub = explicitOnResize((dims) => {\n currentDims = dims\n ;(termStdout as { columns: number; rows: number }).columns = dims.cols\n ;(termStdout as { columns: number; rows: number }).rows = dims.rows\n for (const listener of resizeListeners) listener()\n })\n providerCleanups.push(unsub)\n }\n }\n\n // Categorize injected values\n for (const [name, value] of Object.entries(injectValues)) {\n if (isFullProvider(value)) {\n providers[name] = value\n } else {\n plainValues[name] = value\n }\n }\n\n // Build inject object (providers + plain values)\n const inject = { ...providers, ...plainValues } as I\n\n // Subscribe to provider state changes\n const stateUnsubscribes: (() => void)[] = []\n\n // Create store\n const store = createStore<S & I>((set, get, api) => {\n // Get base state from factory\n const baseState = factory(inject)(\n set as StoreApi<S>[\"setState\"],\n get as StoreApi<S>[\"getState\"],\n api as StoreApi<S>,\n )\n\n // Merge provider references into state (for access via selectors)\n const mergedState: Record<string, unknown> = { ...baseState }\n\n for (const [name, provider] of Object.entries(providers)) {\n mergedState[name] = provider\n\n // Subscribe to provider state changes (basic providers only)\n if (isBasicProvider(provider)) {\n const unsub = provider.subscribe((_providerState) => {\n // Could flatten provider state here if desired\n // For now, just trigger a re-check\n })\n stateUnsubscribes.push(unsub)\n }\n }\n\n // Add plain values\n for (const [name, value] of Object.entries(plainValues)) {\n mergedState[name] = value\n }\n\n return mergedState as S & I\n })\n\n // Track current dimensions\n let currentDims: Dims = { cols, rows }\n\n // Subscribe to stdout resize events so currentDims stays in sync.\n // In headless mode this is handled by explicitOnResize above.\n // In non-headless mode, stdout resize events update currentDims directly\n // and notify mockTerm subscribers (so useSyncExternalStore re-renders).\n if (!headless) {\n const onStdoutResize = () => {\n currentDims = {\n cols: stdout.columns || 80,\n rows: stdout.rows || 24,\n }\n for (const listener of mockTermSubscribers) listener(currentDims)\n }\n stdout.on(\"resize\", onStdoutResize)\n providerCleanups.push(() => stdout.off(\"resize\", onStdoutResize))\n }\n\n let shouldExit = false\n let renderPaused = false\n let isRendering = false // Re-entrancy guard for store subscription\n let inEventHandler = false // True during processEvent/press — suppresses subscription renders\n let pendingRerender = false // Deferred render flag for re-entrancy\n\n // ========================================================================\n // ANSI Trace: SILVERY_TRACE=1 logs all stdout writes with decoded sequences\n // ========================================================================\n const _ansiTrace = !headless && process.env?.SILVERY_TRACE === \"1\"\n\n let _traceSeq = 0\n const _traceStart = performance.now()\n let _origStdoutWrite: typeof process.stdout.write | undefined\n\n if (_ansiTrace) {\n const fs =\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\") as typeof import(\"node:fs\")\n fs.writeFileSync(\"/tmp/silvery-trace.log\", `=== SILVERY TRACE START ===\\n`)\n\n _origStdoutWrite = stdout.write.bind(stdout) as typeof stdout.write\n\n const symbolize = (s: string): string =>\n s\n .replace(/\\x1b\\[\\?1049h/g, \"⟨ALT_ON⟩\")\n .replace(/\\x1b\\[\\?1049l/g, \"⟨ALT_OFF⟩\")\n .replace(/\\x1b\\[2J/g, \"⟨CLEAR⟩\")\n .replace(/\\x1b\\[H/g, \"⟨HOME⟩\")\n .replace(/\\x1b\\[\\?25l/g, \"⟨CUR_HIDE⟩\")\n .replace(/\\x1b\\[\\?25h/g, \"⟨CUR_SHOW⟩\")\n .replace(/\\x1b\\[\\?2026h/g, \"⟨SYNC_ON⟩\")\n .replace(/\\x1b\\[\\?2026l/g, \"⟨SYNC_OFF⟩\")\n .replace(/\\x1b\\[\\?2004h/g, \"⟨BPASTE_ON⟩\")\n .replace(/\\x1b\\[\\?2004l/g, \"⟨BPASTE_OFF⟩\")\n .replace(/\\x1b\\[0m/g, \"⟨RST⟩\")\n .replace(/\\x1b\\[(\\d+);(\\d+)H/g, \"⟨GO $1,$2⟩\")\n .replace(/\\x1b\\[38;5;(\\d+)m/g, \"⟨F$1⟩\")\n .replace(/\\x1b\\[48;5;(\\d+)m/g, \"⟨B$1⟩\")\n .replace(/\\x1b\\[38;2;(\\d+);(\\d+);(\\d+)m/g, \"⟨FR$1,$2,$3⟩\")\n .replace(/\\x1b\\[48;2;(\\d+);(\\d+);(\\d+)m/g, \"⟨BR$1,$2,$3⟩\")\n .replace(/\\x1b\\[1m/g, \"⟨BOLD⟩\")\n .replace(/\\x1b\\[2m/g, \"⟨DIM⟩\")\n .replace(/\\x1b\\[3m/g, \"⟨ITAL⟩\")\n .replace(/\\x1b\\[4m/g, \"⟨UL⟩\")\n .replace(/\\x1b\\[7m/g, \"⟨INV⟩\")\n .replace(/\\x1b\\[22m/g, \"⟨/BOLD⟩\")\n .replace(/\\x1b\\[23m/g, \"⟨/ITAL⟩\")\n .replace(/\\x1b\\[24m/g, \"⟨/UL⟩\")\n .replace(/\\x1b\\[27m/g, \"⟨/INV⟩\")\n .replace(/\\x1b\\[39m/g, \"⟨/FG⟩\")\n .replace(/\\x1b\\[49m/g, \"⟨/BG⟩\")\n // Catch remaining CSI sequences\n .replace(/\\x1b\\[([0-9;]*)([A-Za-z])/g, \"⟨CSI $1$2⟩\")\n // Catch remaining ESC sequences\n .replace(/\\x1b([^\\[])/, \"⟨ESC $1⟩\")\n\n const traceWrite = function (this: typeof stdout, chunk: unknown, ...args: unknown[]): boolean {\n const str = typeof chunk === \"string\" ? chunk : String(chunk)\n const seq = ++_traceSeq\n const ms = (performance.now() - _traceStart).toFixed(0)\n const decoded = symbolize(str)\n // Truncate for readability but keep enough to identify content\n const preview =\n decoded.length > 400 ? decoded.slice(0, 200) + ` ...[${decoded.length}ch]... ` + decoded.slice(-100) : decoded\n fs.appendFileSync(\n \"/tmp/silvery-trace.log\",\n `[${String(seq).padStart(4, \"0\")}] +${ms}ms (${str.length}b): ${preview}\\n`,\n )\n return (_origStdoutWrite as Function).call(this, chunk, ...args)\n } as typeof stdout.write\n\n stdout.write = traceWrite\n // Restore original stdout.write on cleanup (providerCleanups runs during cleanup())\n providerCleanups.push(() => {\n if (_origStdoutWrite) stdout.write = _origStdoutWrite\n })\n }\n\n // Create render target\n const target: RenderTarget = headless\n ? {\n write(frame: string) {\n if (explicitWritable) explicitWritable.write(frame)\n },\n getDims: () => currentDims,\n }\n : {\n write(frame: string): void {\n if (_perfLog) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `TARGET.write: ${frame.length} bytes (paused=${renderPaused})\\n`,\n )\n }\n if (!renderPaused) {\n if (outputGuard) {\n outputGuard.writeStdout(frame)\n } else {\n stdout.write(frame)\n }\n }\n },\n getDims(): Dims {\n return currentDims\n },\n onResize(handler: (dims: Dims) => void): () => void {\n const onResize = () => {\n currentDims = {\n cols: stdout.columns || 80,\n rows: stdout.rows || 24,\n }\n handler(currentDims)\n }\n stdout.on(\"resize\", onResize)\n return () => stdout.off(\"resize\", onResize)\n },\n }\n\n // Resolve textSizing from caps + option\n // For \"auto\": use heuristic first, probe to verify if heuristic says yes\n // For \"probe\": start disabled, probe async to determine\n // For true/false: use directly\n const heuristicSupported = capsOption?.textSizingSupported ?? isTextSizingLikelySupported()\n const shouldProbe = textSizingOption === \"probe\" || (textSizingOption === \"auto\" && heuristicSupported)\n // If we have a cached probe result, use it immediately instead of probing again\n const cachedProbe = shouldProbe ? getCachedProbeResult() : undefined\n let textSizingEnabled: boolean\n if (textSizingOption === true) {\n textSizingEnabled = true\n } else if (textSizingOption === \"probe\") {\n // \"probe\": start disabled unless cache says supported\n textSizingEnabled = cachedProbe?.supported ?? false\n } else if (textSizingOption === \"auto\") {\n if (cachedProbe !== undefined) {\n // Cache available: use definitive probe result\n textSizingEnabled = cachedProbe.supported\n } else {\n // No cache: use heuristic for first render, probe will verify\n textSizingEnabled = heuristicSupported\n }\n } else {\n textSizingEnabled = false\n }\n\n // Whether we still need to run the async probe (no cache hit)\n const needsProbe = shouldProbe && cachedProbe === undefined && !headless\n\n // Resolve width detection: \"auto\" enables when caps are provided and not headless\n const needsWidthDetection =\n !headless && (widthDetectionOption === true || (widthDetectionOption === \"auto\" && capsOption != null))\n\n // Track effective caps — may be updated by width detection and text sizing probes\n let effectiveCaps = capsOption ? { ...capsOption, textSizingSupported: textSizingEnabled } : undefined\n\n // Create pipeline config from caps (scoped width measurer + output phase)\n // Use `let` because the pipeline may be recreated after a probe changes textSizing\n let pipelineConfig = effectiveCaps ? createPipeline({ caps: effectiveCaps }) : undefined\n\n // Create runtime (pass scoped output phase to ensure measurer/caps are threaded)\n // mode must match alternateScreen: inline apps (alternateScreen=false) need\n // inline output phase rendering (relative cursor) + scrollback offset tracking.\n const runtime = createRuntime({\n target,\n signal,\n mode: alternateScreen ? \"fullscreen\" : \"inline\",\n outputPhaseFn: pipelineConfig?.outputPhaseFn,\n })\n\n // Cleanup state\n let cleanedUp = false\n let storeUnsubscribeFn: (() => void) | null = null\n // Track protocol state for cleanup and suspend/resume\n let kittyEnabled = false\n const defaultKittyFlags = KittyFlags.DISAMBIGUATE | KittyFlags.REPORT_EVENTS | KittyFlags.REPORT_ALL_KEYS\n let kittyFlags: number = defaultKittyFlags\n let mouseEnabled = false\n let focusReportingEnabled = false\n // Selection requires explicit opt-in — don't hijack mouse clicks by default\n const selectionEnabled = selectionOption ?? false\n let selectionState = createTerminalSelectionState()\n\n // --- Selection bridge ---\n // Listeners for the bridge's subscribe mechanism (used by useSelection)\n const selectionListeners = new Set<() => void>()\n\n /** Notify useSelection() subscribers that selection state changed. */\n function notifySelectionListeners(): void {\n for (const listener of selectionListeners) {\n listener()\n }\n }\n\n // Capability registry: use provided one or create our own so the bridge\n // can be registered and useSelection() works even without withDomEvents().\n const capabilityRegistry: CapabilityRegistry =\n (capabilityRegistryOption as CapabilityRegistry | undefined) ?? createCapabilityRegistry()\n\n // The bridge exposes create-app's selection state via the SelectionFeature\n // interface. React hooks (useSelection) and copy-mode read/write through it.\n let selectionBridge: SelectionFeature | undefined\n if (selectionEnabled) {\n selectionBridge = createSelectionBridge({\n getState: () => selectionState,\n subscribe: (listener) => {\n selectionListeners.add(listener)\n return () => {\n selectionListeners.delete(listener)\n }\n },\n setRange: (range) => {\n if (range === null) {\n const [next] = terminalSelectionUpdate({ type: \"clear\" }, selectionState)\n selectionState = next\n } else {\n // Start at anchor, extend to head, finish\n const [s1] = terminalSelectionUpdate(\n { type: \"start\", col: range.anchor.col, row: range.anchor.row, source: \"keyboard\" },\n selectionState,\n )\n const [s2] = terminalSelectionUpdate({ type: \"extend\", col: range.head.col, row: range.head.row }, s1)\n const [s3] = terminalSelectionUpdate({ type: \"finish\" }, s2)\n selectionState = s3\n }\n notifySelectionListeners()\n // Force re-render to show/clear overlay\n if (currentBuffer) {\n runtime.invalidate()\n }\n },\n clear: () => {\n const [next] = terminalSelectionUpdate({ type: \"clear\" }, selectionState)\n selectionState = next\n notifySelectionListeners()\n if (currentBuffer) {\n runtime.invalidate()\n }\n },\n })\n capabilityRegistry.register(SELECTION_CAPABILITY, selectionBridge)\n }\n\n // Virtual inline mode state\n const scrollback = virtualInlineOption ? createVirtualScrollback() : null\n let virtualScrollOffset = 0 // 0 = live (bottom), >0 = scrolled up\n let searchState = createSearchState()\n\n // Focus manager (tree-based focus system) with event dispatch wiring\n const focusManager = createFocusManager({\n onFocusChange(oldNode, newNode, _origin) {\n // Dispatch blur event on the old element\n if (oldNode) {\n const blurEvent = createFocusEvent(\"blur\", oldNode, newNode)\n dispatchFocusEvent(blurEvent)\n }\n // Dispatch focus event on the new element\n if (newNode) {\n const focusEvent = createFocusEvent(\"focus\", newNode, oldNode)\n dispatchFocusEvent(focusEvent)\n }\n },\n })\n\n // Wire up focus cleanup on node removal — when React unmounts a subtree,\n // the host-config calls this to clear focus if the active element was removed.\n setOnNodeRemoved((removedNode) => focusManager.handleSubtreeRemoved(removedNode))\n\n // Per-instance cursor state (replaces module-level globals)\n const cursorStore = createCursorStore()\n\n // Mouse event processor for DOM-level dispatch (with click-to-focus)\n const mouseEventState = createMouseEventProcessor({ focusManager })\n\n // Cleanup function - idempotent, can be called from exit() or finally\n const cleanup = () => {\n if (cleanedUp) return\n cleanedUp = true\n\n // Log keypress performance summary before teardown (only emits when TRACE was active)\n logExitSummary()\n\n // Unmount React tree first — this runs effect cleanups (clears intervals,\n // cancels subscriptions) before we tear down the infrastructure.\n try {\n reconciler.updateContainerSync(null, fiberRoot, null, () => {})\n reconciler.flushSyncWork()\n } catch {\n // Ignore — component tree may already be partially torn down\n }\n\n // Unregister node removal hook\n setOnNodeRemoved(null)\n\n // Unsubscribe from store\n if (storeUnsubscribeFn) {\n storeUnsubscribeFn()\n }\n\n // Unsubscribe from provider state changes\n stateUnsubscribes.forEach((unsub) => {\n try {\n unsub()\n } catch {\n // Ignore\n }\n })\n\n // Dispose output guard BEFORE terminal protocol cleanup — restores original\n // stdout/stderr write methods so the cleanup sequences go through unimpeded.\n if (outputGuard) {\n outputGuard.dispose()\n outputGuard = null\n }\n\n // === Terminal protocol cleanup ===\n //\n // Order is critical to avoid escape sequence leaks on exit:\n //\n // 1. Stop consuming stdin — remove data listeners so no more events process\n // 2. Send all protocol disable sequences via writeSync (synchronous, reliable)\n // 3. Drain any in-flight stdin bytes (terminal may have queued events before\n // processing our disable sequences — especially Kitty key release events)\n // 4. Disable raw mode and pause stdin\n //\n // Without the drain, Kitty release events (e.g., CSI 113;1:3u for 'q' release)\n // and SGR mouse events appear as garbled text on the shell prompt after exit.\n\n if (!headless && stdin.isTTY) {\n // Step 1: Stop consuming stdin — prevent any more event processing\n stdin.removeAllListeners(\"data\")\n stdin.pause()\n\n // Step 2: Send ALL protocol disable sequences unconditionally.\n // Sending a disable for an inactive protocol is harmless, and unconditional\n // cleanup is more robust than tracking enable/disable state.\n const sequences = [\n \"\\x1b[?1004l\", // Disable focus reporting\n disableMouse(), // Disable SGR mouse tracking (modes 1003, 1006)\n disableKittyKeyboard(), // Pop Kitty keyboard protocol\n \"\\x1b[?2004l\", // Disable bracketed paste\n \"\\x1b[0m\", // Reset SGR attributes\n resetCursorStyle(), // Reset cursor shape to terminal default (DECSCUSR 0)\n \"\\x1b[?25h\", // Show cursor\n alternateScreen ? \"\\x1b[?1049l\" : \"\", // Exit alternate screen\n ].join(\"\")\n\n // Use writeSync for reliability — async write may not flush before exit.\n // For mock/test stdouts, writeSync(fd) bypasses the mock, so fall back.\n const isRealStdout = stdout === process.stdout\n if (isRealStdout) {\n try {\n writeSync((stdout as unknown as { fd: number }).fd, sequences)\n } catch {\n try {\n stdout.write(sequences)\n } catch {\n /* terminal may be gone */\n }\n }\n\n // Step 3: Drain in-flight stdin bytes. The terminal may have already\n // queued events (Kitty release, mouse moves) before processing our\n // disable sequences. Read and discard them so they don't leak to shell.\n //\n // Known limitation: stdin.read() only gets Node's internal buffer.\n // Late-arriving bytes (Kitty release of 'q') in the kernel TTY buffer\n // may leak to the shell as garbled text (e.g., \"3;1:3u\").\n // See bead km-silvery.exit-kitty-leak for investigation.\n try {\n stdin.resume()\n while (stdin.read() !== null) {\n /* discard Node-buffered data */\n }\n stdin.pause()\n } catch {\n // Drain failed — best-effort, continue cleanup\n }\n } else {\n try {\n stdout.write(sequences)\n } catch {\n /* terminal may be gone */\n }\n }\n\n // Step 4: Disable raw mode\n try {\n stdin.setRawMode(false)\n } catch {\n // Ignore — stdin may be closed\n }\n } else if (!headless) {\n // Non-TTY cleanup: just send disable sequences\n const sequences = [\n \"\\x1b[?1004l\",\n disableMouse(),\n disableKittyKeyboard(),\n \"\\x1b[?2004l\",\n \"\\x1b[0m\",\n resetCursorStyle(),\n \"\\x1b[?25h\",\n alternateScreen ? \"\\x1b[?1049l\" : \"\",\n ].join(\"\")\n try {\n stdout.write(sequences)\n } catch {\n /* terminal may be gone */\n }\n }\n\n // Cleanup providers — stdin is already cleaned up above for TTY,\n // but provider cleanup handles other resources (resize listeners, etc.)\n providerCleanups.forEach((fn) => {\n try {\n fn()\n } catch {\n // Ignore\n }\n })\n\n // Dispose runtime\n runtime[Symbol.dispose]()\n }\n\n let exit: () => void // eslint-disable-line prefer-const -- forward declaration, assigned once at L1403\n\n // Create SilveryNode container.\n // onRender fires during React's resetAfterCommit — inside the commit phase.\n // Calling doRender from there would be re-entrant (doRender calls updateContainerSync\n // which triggers commit which calls onRender again). Always defer via microtask.\n // Without this callback, setInterval/setTimeout-driven setState never flushes to terminal.\n const container = createContainer(() => {\n if (shouldExit) return\n if (inEventHandler) {\n // During processEvent/press: just flag, caller's flush loop handles it.\n pendingRerender = true\n return\n }\n // Always defer — onRender fires during React commit, re-entry is unsafe.\n if (!pendingRerender) {\n pendingRerender = true\n queueMicrotask(() => {\n if (!pendingRerender) return\n pendingRerender = false\n if (!shouldExit && !isRendering) {\n isRendering = true\n try {\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n } finally {\n isRendering = false\n }\n }\n })\n }\n })\n\n // Create React fiber root\n const fiberRoot = createFiberRoot(container)\n\n // Track current buffer for text access\n let currentBuffer: Buffer\n\n // Create mock stdout for contexts\n const mockStdout = {\n columns: cols,\n rows: rows,\n write: () => true,\n isTTY: false,\n on: () => mockStdout,\n off: () => mockStdout,\n once: () => mockStdout,\n removeListener: () => mockStdout,\n addListener: () => mockStdout,\n } as unknown as NodeJS.WriteStream\n\n // Create mock term — override getState to return the app's actual dimensions\n // rather than process.stdout dimensions (which may differ in test/emulator contexts).\n // Also override subscribe to notify listeners on resize so useSyncExternalStore\n // (used by useTerm/useWindowSize) triggers re-renders when dimensions change.\n const baseMockTerm = createTerm({ color: \"truecolor\" })\n const mockTermSubscribers = new Set<(state: { cols: number; rows: number }) => void>()\n const mockTerm = Object.create(baseMockTerm, {\n getState: { value: (): { cols: number; rows: number } => currentDims },\n subscribe: {\n value: (listener: (state: { cols: number; rows: number }) => void): (() => void) => {\n mockTermSubscribers.add(listener)\n return () => mockTermSubscribers.delete(listener)\n },\n },\n }) as typeof baseMockTerm\n\n // RuntimeContext input listeners — allows components using hooks/useInput\n // (TextInput, TextArea, SelectList etc.) to work inside createApp apps.\n //\n // V1r apply chain: ordered dispatch, focus lane before fallback, explicit handled.\n // Raw Sets replaced with arrays for ordered iteration.\n const runtimeInputListeners: Array<(input: string, key: Key) => void> = []\n const runtimePasteListeners: Array<(text: string) => void> = []\n const runtimeFocusListeners: Array<(focused: boolean) => void> = []\n\n // Typed event bus — supports view → runtime events via emit()\n const runtimeEventListeners = new Map<string, Array<Function>>()\n runtimeEventListeners.set(\"input\", runtimeInputListeners as unknown as Array<Function>)\n runtimeEventListeners.set(\"paste\", runtimePasteListeners as unknown as Array<Function>)\n runtimeEventListeners.set(\"focus\", runtimeFocusListeners as unknown as Array<Function>)\n\n const runtimeContextValue: RuntimeContextValue = {\n on(event, handler) {\n let listeners = runtimeEventListeners.get(event)\n if (!listeners) {\n listeners = []\n runtimeEventListeners.set(event, listeners)\n }\n listeners.push(handler)\n return () => {\n const idx = listeners!.indexOf(handler)\n if (idx >= 0) listeners!.splice(idx, 1)\n }\n },\n emit(event, ...args) {\n const listeners = runtimeEventListeners.get(event)\n if (listeners) {\n for (const listener of listeners) {\n listener(...args)\n }\n }\n },\n exit: () => exit(),\n }\n\n // Wrap element with all required providers\n // SilveryErrorBoundary is always the outermost wrapper — catches render errors gracefully.\n // If a Root component is provided (e.g., from withInk), wrap the element with it\n // inside silvery's contexts so it can access Term, Stdout, FocusManager, Runtime.\n const Root = RootComponent ?? React.Fragment\n // Cache backend selection:\n // - inline: \"terminal\" — items promoted to real terminal scrollback\n // - fullscreen + virtualInline: \"virtual\" — items stored in HistoryBuffer,\n // viewable via virtual scroll overlay\n // - plain fullscreen: \"retain\" — items cached but kept in the render tree\n // (no scrollback to display unmounted items, virtualizer handles windowing)\n const cacheBackend = !alternateScreen ? \"terminal\" : virtualInlineOption ? \"virtual\" : \"retain\"\n const wrappedElement = (\n <SilveryErrorBoundary>\n <CursorProvider store={cursorStore}>\n <CacheBackendContext.Provider value={cacheBackend}>\n <TermContext.Provider value={mockTerm}>\n <StdoutContext.Provider\n value={{\n stdout: mockStdout,\n write: () => {},\n notifyScrollback: (lines: number) => runtime.addScrollbackLines(lines),\n promoteScrollback: (content: string, lines: number) => runtime.promoteScrollback(content, lines),\n resetInlineCursor: () => runtime.resetInlineCursor(),\n getInlineCursorRow: () => runtime.getInlineCursorRow(),\n }}\n >\n <StderrContext.Provider\n value={{\n stderr: process.stderr,\n write: (data: string) => {\n process.stderr.write(data)\n },\n }}\n >\n <FocusManagerContext.Provider value={focusManager}>\n <RuntimeContext.Provider value={runtimeContextValue}>\n <CapabilityRegistryContext.Provider value={capabilityRegistry}>\n <Root>\n <StoreContext.Provider value={store as StoreApi<unknown>}>{element}</StoreContext.Provider>\n </Root>\n </CapabilityRegistryContext.Provider>\n </RuntimeContext.Provider>\n </FocusManagerContext.Provider>\n </StderrContext.Provider>\n </StdoutContext.Provider>\n </TermContext.Provider>\n </CacheBackendContext.Provider>\n </CursorProvider>\n </SilveryErrorBoundary>\n )\n\n // Performance instrumentation — count renders per event\n let _renderCount = 0\n let _eventStart = 0\n const _perfLog = typeof process !== \"undefined\" && process.env?.DEBUG?.includes(\"silvery:perf\")\n\n // Incremental rendering via long-lived Ag instance.\n // The Ag manages its own prevBuffer for incremental rendering.\n // Set SILVERY_NO_INCREMENTAL=1 to disable (for debugging blank screen issues).\n // _noIncremental aliases the module-level NO_INCREMENTAL constant for readability.\n const _noIncremental = NO_INCREMENTAL\n\n // Long-lived Ag instance — created lazily on first doRender() after reconciler\n // produces the root node. Reused across all subsequent frames, avoiding per-frame\n // pipeline state allocation. The Ag manages its own prevBuffer for incremental\n // content rendering.\n let _ag: Ag | null = null\n // Track the last TerminalBuffer for dimension-change detection (the Ag manages\n // prevBuffer internally, but we need the dimensions for resize detection).\n let _lastTermBuffer: import(\"@silvery/ag-term/buffer\").TerminalBuffer | null = null\n\n // Helper to render and get text\n function doRender(): Buffer {\n _renderCount++\n if (_ansiTrace) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-trace.log\",\n `--- doRender #${_renderCount} (ag=${_ag ? \"reuse\" : \"create\"}, incremental=${!_noIncremental}) ---\\n`,\n )\n }\n const renderStart = performance.now()\n\n // Phase A: React reconciliation\n reconciler.updateContainerSync(wrappedElement, fiberRoot, null, () => {})\n reconciler.flushSyncWork()\n const reconcileMs = performance.now() - renderStart\n\n // Bench instrumentation: accumulate reconcile time. The pipeline accumulator\n // (set by silveryBenchStart) catches measure/layout/content/output; reconcile\n // lives outside pipeline/index.ts so we add it here.\n {\n const acc = (globalThis as any).__silvery_bench_phases\n if (acc) acc.reconcile += reconcileMs\n }\n\n // Phase B: Render pipeline (incremental when prevBuffer available)\n const pipelineStart = performance.now()\n const rootNode = getContainerRoot(container)\n const dims = runtime.getDims()\n\n const isInline = !alternateScreen\n\n // Create or reuse long-lived Ag instance. Created lazily because the root\n // AgNode is produced by the React reconciler in Phase A above.\n if (!_ag) {\n _ag = createAg(rootNode, { measurer: pipelineConfig?.measurer })\n }\n\n // Invalidate prevBuffer on dimension change (resize).\n // Both Ag-level (ag.resetBuffer()) and runtime-level (runtime.invalidate())\n // must be cleared — otherwise the ANSI diff compares different-sized buffers.\n //\n // In inline mode, only WIDTH changes trigger invalidation. Height changes are\n // normal (content grows/shrinks as items are added/frozen) and are handled\n // incrementally by the output phase. Invalidating on height causes the runtime's\n // prevBuffer to be null, which triggers the first-render clear path with \\x1b[J\n // — wiping the entire visible screen including shell prompt content above the app.\n if (_ag) {\n // Check dimension changes. On first render there's no prevBuffer to compare.\n const lastBuffer = _lastTermBuffer\n if (lastBuffer) {\n const widthChanged = dims.cols !== lastBuffer.width\n const heightChanged = !isInline && dims.rows !== lastBuffer.height\n if (widthChanged || heightChanged) {\n _ag.resetBuffer()\n runtime.invalidate()\n }\n }\n }\n\n // Clear diagnostic arrays before the render so we capture only this render's data.\n // INSTRUMENTED is hoisted from env vars at module load — when no diagnostic is\n // active (the hot path), all three global resets and the cell-debug setup\n // constant-fold out of the frame.\n if (INSTRUMENTED) {\n ;(globalThis as any).__silvery_content_all = undefined\n ;(globalThis as any).__silvery_node_trace = undefined\n // Cell debug: enable during real incremental render for SILVERY_STRICT diagnosis.\n // Set SILVERY_CELL_DEBUG=x,y to trace which nodes cover a specific cell.\n // The log is captured during the render and included in any mismatch error.\n ;(globalThis as any).__silvery_cell_debug =\n CELL_DEBUG !== null ? { x: CELL_DEBUG.x, y: CELL_DEBUG.y, log: [] as string[] } : undefined\n }\n\n // Early return: if reconciliation produced no dirty flags on the tree,\n // skip the pipeline entirely. This avoids cloning prevBuffer (which\n // resets dirty rows to 0), preserving the row-level dirty markers that\n // the runtime diff needs to detect actual changes.\n // Exception: dimension changes require re-layout even without dirty flags.\n const rootHasDirty = rootNode.layoutDirty || isAnyDirty(rootNode.dirtyBits, rootNode.dirtyEpoch)\n const dimsChanged =\n _lastTermBuffer != null && (dims.cols !== _lastTermBuffer.width || dims.rows !== _lastTermBuffer.height)\n if (!rootHasDirty && !dimsChanged && _lastTermBuffer && currentBuffer) {\n return currentBuffer\n }\n\n // When SILVERY_NO_INCREMENTAL is set, force fresh render every frame\n if (_noIncremental) {\n _ag.resetBuffer()\n }\n\n // Run layout + content render via the long-lived Ag instance.\n // The Ag manages prevBuffer internally for incremental rendering.\n // Output phase is NOT run here — the runtime handles it separately.\n _ag.layout(dims)\n const { buffer: termBuffer, prevBuffer: agPrevBuffer } = _ag.render()\n _lastTermBuffer = termBuffer\n const wasIncremental = !_noIncremental && agPrevBuffer !== null\n const pipelineMs = performance.now() - pipelineStart\n\n // Expose timing for diagnostics (previously done by executeRenderCore).\n // Output timing is 0 here — the runtime handles the output phase separately.\n ;(globalThis as any).__silvery_last_pipeline = {\n layout: pipelineMs,\n output: 0,\n total: pipelineMs,\n incremental: wasIncremental,\n }\n ;(globalThis as any).__silvery_render_count = ((globalThis as any).__silvery_render_count ?? 0) + 1\n\n // Bench instrumentation: accumulate pipeline-level timing.\n // ag.ts handles measure/layout/content accumulation; we add total here.\n {\n const acc = (globalThis as any).__silvery_bench_phases\n if (acc) {\n acc.total += pipelineMs\n acc.pipelineCalls += 1\n }\n }\n\n // SILVERY_STRICT: compare incremental render against fresh render.\n // createApp bypasses Scheduler/Renderer which have this check built-in,\n // so we add it here to catch incremental rendering bugs at runtime.\n // STRICT_MODE is hoisted to module scope — the env var is read once at load.\n if (STRICT_MODE && wasIncremental) {\n const { buffer: freshBuffer } = executeRender(\n rootNode,\n dims.cols,\n dims.rows,\n null,\n {\n skipLayoutNotifications: true,\n skipScrollStateUpdates: true,\n },\n pipelineConfig,\n )\n const { cellEquals, bufferToText } =\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"@silvery/ag-term/buffer\") as typeof import(\"@silvery/ag-term/buffer\")\n for (let y = 0; y < termBuffer.height; y++) {\n for (let x = 0; x < termBuffer.width; x++) {\n const a = termBuffer.getCell(x, y)\n const b = freshBuffer.getCell(x, y)\n if (!cellEquals(a, b)) {\n // Use cell debug log collected during the real incremental render\n let cellDebugInfo = \"\"\n const savedCellDbg = (globalThis as any).__silvery_cell_debug as\n | { x: number; y: number; log: string[] }\n | undefined\n if (savedCellDbg && savedCellDbg.x === x && savedCellDbg.y === y && savedCellDbg.log.length > 0) {\n cellDebugInfo = `\\nCELL DEBUG (${savedCellDbg.log.length} entries for (${x},${y})):\\n${savedCellDbg.log.join(\"\\n\")}\\n`\n } else if (savedCellDbg && savedCellDbg.x === x && savedCellDbg.y === y) {\n cellDebugInfo = `\\nCELL DEBUG: No nodes cover (${x},${y}) during incremental render\\n`\n } else {\n cellDebugInfo = `\\nCELL DEBUG: Target cell (${x},${y}) differs from debug cell (${savedCellDbg?.x},${savedCellDbg?.y})\\n`\n }\n\n // Re-run fresh render with write trap to capture what writes to the mismatched cell\n let trapInfo = \"\"\n const trap = { x, y, log: [] as string[] }\n ;(globalThis as any).__silvery_write_trap = trap\n try {\n executeRender(\n rootNode,\n dims.cols,\n dims.rows,\n null,\n {\n skipLayoutNotifications: true,\n skipScrollStateUpdates: true,\n },\n pipelineConfig,\n )\n } catch {\n // ignore\n }\n ;(globalThis as any).__silvery_write_trap = null\n if (trap.log.length > 0) {\n trapInfo = `\\nWRITE TRAP (${trap.log.length} writes to (${x},${y})):\\n${trap.log.join(\"\\n\")}\\n`\n } else {\n trapInfo = `\\nWRITE TRAP: NO WRITES to (${x},${y})\\n`\n }\n const incText = bufferToText(termBuffer)\n const freshText = bufferToText(freshBuffer)\n const cellStr = (c: typeof a) =>\n `char=${JSON.stringify(c.char)} fg=${c.fg} bg=${c.bg} ulColor=${c.underlineColor} wide=${c.wide} cont=${c.continuation} attrs={bold=${c.attrs.bold},dim=${c.attrs.dim},italic=${c.attrs.italic},ul=${c.attrs.underline},ulStyle=${c.attrs.underlineStyle},blink=${c.attrs.blink},inv=${c.attrs.inverse},hidden=${c.attrs.hidden},strike=${c.attrs.strikethrough}}`\n // Dump render phase stats for diagnosis\n const contentAll = (globalThis as any).__silvery_content_all as unknown[]\n const statsStr = contentAll\n ? `\\n--- render phase stats (${contentAll.length} calls) ---\\n` +\n contentAll\n .map(\n (s: any, i: number) =>\n ` #${i}: visited=${s.nodesVisited} rendered=${s.nodesRendered} skipped=${s.nodesSkipped} ` +\n `clearOps=${s.clearOps} cascade=\"${s.cascadeNodes}\" ` +\n `flags={C=${s.flagContentDirty} P=${s.flagStylePropsDirty} L=${s.flagLayoutChanged} ` +\n `S=${s.flagSubtreeDirty} Ch=${s.flagChildrenDirty} CP=${s.flagChildPositionChanged} AL=${s.flagAncestorLayoutChanged} noPrev=${s.noPrevBuffer}} ` +\n `scroll={containers=${s.scrollContainerCount} cleared=${s.scrollViewportCleared} reason=\"${s.scrollClearReason}\"} ` +\n `normalRepaint=\"${s.normalRepaintReason}\" ` +\n `prevBuf={null=${s._prevBufferNull} dimMismatch=${s._prevBufferDimMismatch} hasPrev=${s._hasPrevBuffer} ` +\n `layout=${s._layoutW}x${s._layoutH} prev=${s._prevW}x${s._prevH}}`,\n )\n .join(\"\\n\")\n : \"\"\n const msg =\n `SILVERY_STRICT (createApp): MISMATCH at (${x}, ${y}) on render #${_renderCount}\\n` +\n ` incremental: ${cellStr(a)}\\n` +\n ` fresh: ${cellStr(b)}` +\n statsStr +\n // Per-node trace\n (() => {\n const traces = (globalThis as any).__silvery_node_trace as unknown[][] | undefined\n if (!traces || traces.length === 0) return \"\"\n let out = \"\\n--- node trace ---\"\n for (let ti = 0; ti < traces.length; ti++) {\n out += `\\n renderPhase #${ti}:`\n for (const t of traces[ti] as any[]) {\n out += `\\n ${t.decision} ${t.id}(${t.type})@${t.depth} rect=${t.rect} prev=${t.prevLayout}`\n out += ` hasPrev=${t.hasPrev} ancClr=${t.ancestorCleared} flags=[${t.flags}] layout∆=${t.layoutChanged}`\n if (t.decision === \"RENDER\") {\n out += ` caa=${t.contentAreaAffected} crc=${t.contentRegionCleared} cnfr=${t.childrenNeedFreshRender}`\n out += ` childPrev=${t.childHasPrev} childAnc=${t.childAncestorCleared} skipBg=${t.skipBgFill} bg=${t.bgColor ?? \"none\"}`\n }\n }\n }\n return out\n })() +\n cellDebugInfo +\n trapInfo +\n `\\n--- incremental ---\\n${incText}\\n--- fresh ---\\n${freshText}`\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\"/tmp/silvery-perf.log\", msg + \"\\n\")\n // Also throw to make it visible\n throw new IncrementalRenderMismatchError(msg)\n }\n }\n }\n if (_perfLog) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `SILVERY_STRICT (createApp): render #${_renderCount} OK\\n`,\n )\n }\n }\n\n const buf = createBuffer(termBuffer, rootNode)\n if (_perfLog) {\n const renderDuration = performance.now() - renderStart\n const phases = (globalThis as any).__silvery_last_pipeline\n const detail = (globalThis as any).__silvery_content_detail\n const phaseStr = phases\n ? ` [measure=${phases.measure.toFixed(1)} layout=${phases.layout.toFixed(1)} content=${phases.content.toFixed(1)} output=${phases.output.toFixed(1)}]`\n : \"\"\n const detailStr = detail\n ? ` {visited=${detail.nodesVisited} rendered=${detail.nodesRendered} skipped=${detail.nodesSkipped} noPrev=${detail.noPrevBuffer ?? 0} dirty=${detail.flagContentDirty ?? 0} paint=${detail.flagStylePropsDirty ?? 0} layoutChg=${detail.flagLayoutChanged ?? 0} subtree=${detail.flagSubtreeDirty ?? 0} children=${detail.flagChildrenDirty ?? 0} childPos=${detail.flagChildPositionChanged ?? 0} scroll=${detail.scrollContainerCount ?? 0}/${detail.scrollViewportCleared ?? 0}${detail.scrollClearReason ? `(${detail.scrollClearReason})` : \"\"}}${detail.cascadeNodes ? ` CASCADE[minDepth=${detail.cascadeMinDepth} ${detail.cascadeNodes}]` : \"\"}`\n : \"\"\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `doRender #${_renderCount}: ${renderDuration.toFixed(1)}ms (reconcile=${reconcileMs.toFixed(1)}ms pipeline=${pipelineMs.toFixed(1)}ms ${dims.cols}x${dims.rows})${phaseStr}${detailStr}\\n`,\n )\n }\n return buf\n }\n\n // Initial render\n if (_ansiTrace) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\"/tmp/silvery-trace.log\", \"=== INITIAL RENDER ===\\n\")\n }\n currentBuffer = doRender()\n\n // Enter alternate screen if requested, then clear and hide cursor\n if (!headless) {\n if (_ansiTrace) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\"/tmp/silvery-trace.log\", \"=== ALT SCREEN + CLEAR ===\\n\")\n }\n if (alternateScreen) {\n stdout.write(\"\\x1b[?1049h\")\n stdout.write(\"\\x1b[2J\\x1b[H\")\n }\n stdout.write(\"\\x1b[?25l\")\n\n // Kitty keyboard protocol\n if (kittyOption != null && kittyOption !== false) {\n if (kittyOption === true) {\n // Auto-detect: probe terminal, enable if supported\n const result = await detectKittyFromStdio(stdout, stdin as NodeJS.ReadStream)\n if (result.supported) {\n stdout.write(enableKittyKeyboard(defaultKittyFlags))\n kittyEnabled = true\n kittyFlags = defaultKittyFlags\n }\n } else {\n // Explicit flags — enable directly without detection\n stdout.write(enableKittyKeyboard(kittyOption as 1))\n kittyEnabled = true\n kittyFlags = kittyOption as number\n }\n } else if (kittyOption == null) {\n // No option specified: legacy behavior — always enable Kitty with full fidelity\n stdout.write(enableKittyKeyboard(defaultKittyFlags))\n kittyEnabled = true\n kittyFlags = defaultKittyFlags\n }\n\n // Mouse tracking\n if (mouseOption) {\n stdout.write(enableMouse())\n mouseEnabled = true\n }\n\n // Focus reporting is deferred to after the event loop starts (see below).\n // Enabling it here would cause the terminal's immediate CSI I/O response\n // to arrive before the input parser's stdin listener is attached, leaking\n // raw escape sequences to the screen.\n }\n if (_ansiTrace) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\"/tmp/silvery-trace.log\", \"=== RUNTIME.RENDER (initial) ===\\n\")\n }\n runtime.render(currentBuffer)\n if (_perfLog) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `STARTUP: initial render done (render #${_renderCount}, incremental=${!_noIncremental})\\n`,\n )\n }\n\n // Activate output guard after protocol setup and initial render are done.\n // This intercepts process.stdout/stderr writes so that only silvery's\n // render pipeline can write to stdout — all other writes are suppressed\n // (stdout) or redirected to DEBUG_LOG (stderr).\n if (shouldGuardOutput) {\n outputGuard = createOutputGuard()\n }\n\n // Assign pause/resume now that doRender and runtime are available.\n // Update runtimeContextValue in-place so useApp()/useRuntime() sees the latest values.\n if (!headless) {\n runtimeContextValue.pause = () => {\n renderPaused = true\n // Temporarily dispose the output guard so console-mode writes\n // (e.g., log dump) reach the terminal directly.\n if (outputGuard) {\n outputGuard.dispose()\n outputGuard = null\n }\n if (alternateScreen) stdout.write(leaveAlternateScreen())\n }\n runtimeContextValue.resume = () => {\n if (alternateScreen) stdout.write(enterAlternateScreen())\n renderPaused = false\n // Re-create the output guard (disposed during pause)\n if (shouldGuardOutput && !outputGuard) {\n outputGuard = createOutputGuard()\n }\n // Reset diff state so next render outputs a full frame.\n // The screen was cleared when entering console mode, so\n // incremental diffing would produce an incomplete frame.\n runtime.invalidate()\n _ag?.resetBuffer()\n // Force full re-render to restore display, but only if we're not\n // already inside a doRender() call (e.g. when resume() is called\n // from a React effect cleanup during reconciliation).\n if (!isRendering) {\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n }\n // If isRendering is true, the outer doRender()/runtime.render() will\n // handle the re-render after effects complete, with renderPaused=false.\n }\n }\n\n // Exit promise\n let exitResolve: () => void\n let exitResolved = false\n const exitPromise = new Promise<void>((resolve) => {\n exitResolve = () => {\n if (!exitResolved) {\n exitResolved = true\n resolve()\n }\n }\n })\n\n // Now define exit function (needs exitResolve and cleanup)\n //\n // When called from within the event pump (key handler returns \"exit\"),\n // we send protocol disable sequences immediately but defer the full\n // cleanup (drain + raw mode) to the pump's finally block. This gives\n // the event loop time to receive late-arriving bytes (e.g., Kitty\n // keyboard release events) before we hand stdin back to the shell.\n //\n // When called from outside the pump (signal handler, direct call),\n // we do sync cleanup immediately (best-effort).\n exit = () => {\n if (shouldExit) return // Already exiting\n shouldExit = true\n\n // Immediately disable protocols that generate async responses.\n // This is the earliest possible moment — before the terminal\n // sends any more events in response to the exit key.\n if (!headless && stdout.isTTY) {\n const earlyDisable = [\n disableKittyKeyboard(), // Stop Kitty release events\n disableMouse(), // Stop mouse events\n \"\\x1b[?1004l\", // Stop focus reporting\n ].join(\"\")\n try {\n writeSync((stdout as unknown as { fd: number }).fd, earlyDisable)\n } catch {\n try {\n stdout.write(earlyDisable)\n } catch {\n /* terminal may be gone */\n }\n }\n }\n\n controller.abort()\n\n // If we're inside the event pump, defer cleanup — the pump's\n // finally block will call cleanupAfterDrain() with an async drain.\n // If we're outside (signal handler, etc.), do sync cleanup now.\n if (!inEventHandler) {\n cleanup()\n exitResolve()\n }\n // else: pump's finally block handles cleanup + exitResolve\n }\n runtimeContextValue.exit = exit\n\n // Frame listeners for async iteration\n let frameResolve: ((buffer: Buffer) => void) | null = null\n let framesDone = false\n\n // Notify frame listeners\n function emitFrame(buf: Buffer) {\n if (frameResolve) {\n const resolve = frameResolve\n frameResolve = null\n resolve(buf)\n }\n }\n\n // Subscribe to store for re-renders.\n //\n // Three cases:\n // 1. inEventHandler=true (during processEvent/press): ONLY flag pendingRerender.\n // The caller's flush loop will handle all deferred renders. No microtask.\n // 2. isRendering=true (during doRender effects): defer via pendingRerender flag.\n // Queue a microtask to render after the current render completes — but only\n // if NOT in an event handler (the flush loop handles it).\n // 3. Neither: render immediately (standalone setState from timeout/interval).\n storeUnsubscribeFn = store.subscribe(() => {\n if (shouldExit) return\n if (_ansiTrace) {\n const _case = inEventHandler ? \"1:event\" : isRendering ? \"2:rendering\" : \"3:standalone\"\n const stack = new Error().stack?.split(\"\\n\").slice(1, 5).join(\"\\n\") ?? \"\"\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-trace.log\",\n `=== SUBSCRIPTION (case ${_case}, render #${_renderCount + 1}) ===\\n${stack}\\n`,\n )\n }\n if (inEventHandler) {\n // During processEvent/press: just flag, caller's flush loop handles it.\n pendingRerender = true\n return\n }\n if (isRendering) {\n // During doRender (outside event handler): defer to microtask.\n if (!pendingRerender) {\n pendingRerender = true\n queueMicrotask(() => {\n if (!pendingRerender) return\n pendingRerender = false\n if (!shouldExit && !isRendering) {\n if (_perfLog) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `SUBSCRIPTION: deferred microtask render (case 2, render #${_renderCount + 1})\\n`,\n )\n }\n isRendering = true\n try {\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n } finally {\n isRendering = false\n }\n }\n })\n }\n return\n }\n if (_perfLog) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `SUBSCRIPTION: immediate render (case 3, render #${_renderCount + 1})\\n`,\n )\n }\n isRendering = true\n try {\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n } finally {\n isRendering = false\n }\n })\n\n // Create namespaced event streams from all providers\n function createProviderEventStream(\n name: string,\n provider: Provider<unknown, Record<string, unknown>>,\n ): AsyncIterable<NamespacedEvent> {\n return map(provider.events(), (event) => ({\n type: `${name}:${String(event.type)}`,\n provider: name,\n event: String(event.type),\n data: event.data,\n }))\n }\n\n /**\n * Write selection overlay to stdout after a render.\n * Appends inverse-video ANSI sequences over selected cells.\n */\n function writeSelectionOverlay(): void {\n if (!selectionEnabled || !selectionState.range || !currentBuffer) return\n const mode = alternateScreen ? \"fullscreen\" : \"inline\"\n const overlay = renderSelectionOverlay(selectionState.range, currentBuffer._buffer, mode, selectionState.scope)\n if (overlay) target.write(overlay)\n }\n\n /**\n * Push the current rendered frame to the virtual scrollback buffer.\n */\n function pushToScrollback(): void {\n if (!scrollback || !currentBuffer) return\n const lines = currentBuffer.text.split(\"\\n\")\n scrollback.push(lines)\n }\n\n /**\n * Render the virtual scrollback view (historical content) to the terminal.\n * When scrolled up, replaces the live app content with historical rows.\n */\n function renderVirtualScrollbackView(): void {\n if (!scrollback || virtualScrollOffset <= 0) return\n const dims = target.getDims()\n const rows = scrollback.getVisibleRows(virtualScrollOffset, dims.rows)\n\n // Clear screen and write rows using absolute positioning\n let out = \"\"\n for (let row = 0; row < rows.length; row++) {\n out += `\\x1b[${row + 1};1H\\x1b[2K${rows[row] ?? \"\"}`\n }\n\n // Scroll indicator at top-right\n const indicator = ` ↑ ${virtualScrollOffset} lines `\n const indicatorCol = Math.max(1, dims.cols - indicator.length + 1)\n out += `\\x1b[1;${indicatorCol}H\\x1b[7m${indicator}\\x1b[27m`\n\n target.write(out)\n }\n\n /**\n * Render search highlights for the current match with inverse video.\n */\n function renderSearchHighlights(): void {\n if (!searchState.active || searchState.currentMatch < 0) return\n const match = searchState.matches[searchState.currentMatch]\n if (!match) return\n\n const dims = target.getDims()\n // Calculate the screen row of the current match\n let screenRow: number\n if (scrollback && virtualScrollOffset > 0) {\n // In scrollback view: calculate relative position\n const totalLines = scrollback.totalLines\n const firstVisibleLine = totalLines - virtualScrollOffset - dims.rows\n screenRow = match.row - firstVisibleLine\n } else {\n screenRow = match.row\n }\n\n if (screenRow < 0 || screenRow >= dims.rows) return\n\n // Move to match position and render with inverse\n let out = `\\x1b[${screenRow + 1};${match.startCol + 1}H\\x1b[7m`\n // Emit the match text (we know the query length)\n for (let col = match.startCol; col <= match.endCol; col++) {\n if (currentBuffer && virtualScrollOffset <= 0) {\n out += currentBuffer._buffer.getCell(col, screenRow).char\n } else {\n out += searchState.query[col - match.startCol] ?? \" \"\n }\n }\n out += \"\\x1b[27m\"\n target.write(out)\n }\n\n /**\n * Render the search bar at the bottom of the screen.\n */\n function renderSearchBarOverlay(): void {\n if (!searchState.active) return\n const dims = target.getDims()\n const bar = renderSearchBar(searchState, dims.cols)\n // Position at the last row\n target.write(`\\x1b[${dims.rows};1H${bar}`)\n }\n\n /**\n * Search function for virtual scrollback — converts line matches to SearchMatch[].\n */\n function searchScrollback(query: string): SearchMatch[] {\n if (!scrollback || !query) return []\n const matchingLines = scrollback.search(query)\n const lowerQuery = query.toLowerCase()\n const matches: SearchMatch[] = []\n for (const lineIdx of matchingLines) {\n // Find exact column positions by getting the line text\n const rows = scrollback.getVisibleRows(scrollback.totalLines - lineIdx - 1, 1)\n const line = rows[0] ?? \"\"\n // Strip ANSI for column matching\n const plain = line.replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, \"\")\n let col = plain.toLowerCase().indexOf(lowerQuery)\n while (col !== -1) {\n matches.push({ row: lineIdx, startCol: col, endCol: col + query.length - 1 })\n col = plain.toLowerCase().indexOf(lowerQuery, col + 1)\n }\n }\n return matches\n }\n\n /**\n * Run a single event's handler (state mutation only, no render).\n * Returns true if processing should continue, false if app should exit.\n *\n * Intercepts mouse events for selection and virtual inline mode.\n */\n function runEventHandler(event: NamespacedEvent): boolean | \"flush\" {\n // Virtual inline: intercept search key events\n if (scrollback && searchState.active && event.type === \"term:key\") {\n const data = event.data as { input: string; key: Key }\n if (data.key.escape) {\n const [next] = searchUpdate({ type: \"close\" }, searchState)\n searchState = next\n virtualScrollOffset = 0 // Return to live view\n return true // Consume\n }\n if (data.key.return && !data.key.shift) {\n const [next, effects] = searchUpdate({ type: \"nextMatch\" }, searchState, searchScrollback)\n searchState = next\n for (const eff of effects) {\n if (eff.type === \"scrollTo\") {\n virtualScrollOffset = Math.max(0, scrollback.totalLines - eff.row - target.getDims().rows)\n }\n }\n return true\n }\n if (data.key.return && data.key.shift) {\n const [next, effects] = searchUpdate({ type: \"prevMatch\" }, searchState, searchScrollback)\n searchState = next\n for (const eff of effects) {\n if (eff.type === \"scrollTo\") {\n virtualScrollOffset = Math.max(0, scrollback.totalLines - eff.row - target.getDims().rows)\n }\n }\n return true\n }\n if (data.key.backspace) {\n const [next, effects] = searchUpdate({ type: \"backspace\" }, searchState, searchScrollback)\n searchState = next\n for (const eff of effects) {\n if (eff.type === \"scrollTo\") {\n virtualScrollOffset = Math.max(0, scrollback.totalLines - eff.row - target.getDims().rows)\n }\n }\n return true\n }\n if (data.key.leftArrow) {\n const [next] = searchUpdate({ type: \"cursorLeft\" }, searchState)\n searchState = next\n return true\n }\n if (data.key.rightArrow) {\n const [next] = searchUpdate({ type: \"cursorRight\" }, searchState)\n searchState = next\n return true\n }\n if (data.input && !data.key.ctrl && !data.key.meta) {\n const [next, effects] = searchUpdate({ type: \"input\", char: data.input }, searchState, searchScrollback)\n searchState = next\n for (const eff of effects) {\n if (eff.type === \"scrollTo\") {\n virtualScrollOffset = Math.max(0, scrollback.totalLines - eff.row - target.getDims().rows)\n }\n }\n return true\n }\n }\n\n // Virtual inline: Ctrl+F opens search\n if (scrollback && event.type === \"term:key\") {\n const data = event.data as { input: string; key: Key }\n if (data.input === \"f\" && data.key.ctrl) {\n const [next] = searchUpdate({ type: \"open\" }, searchState)\n searchState = next\n return true\n }\n }\n\n // Virtual inline: intercept wheel events for scrolling\n if (scrollback && event.event === \"mouse\" && event.data) {\n const mouseData = event.data as {\n button: number\n x: number\n y: number\n action: string\n delta?: number\n }\n if (mouseData.action === \"wheel\") {\n const scrollLines = 3\n if (mouseData.delta && mouseData.delta < 0) {\n // Scroll up (into history)\n virtualScrollOffset = Math.min(\n virtualScrollOffset + scrollLines,\n Math.max(0, scrollback.totalLines - target.getDims().rows),\n )\n } else {\n // Scroll down (toward live)\n virtualScrollOffset = Math.max(0, virtualScrollOffset - scrollLines)\n }\n return true // Consume wheel events\n }\n }\n\n // Selection: intercept mouse events\n if (selectionEnabled && event.event === \"mouse\" && event.data) {\n const mouseData = event.data as {\n button: number\n x: number\n y: number\n action: string\n }\n\n // Left button (button 0) drag for selection\n if (mouseData.button === 0) {\n if (mouseData.action === \"down\") {\n // Clear any existing selection first, then start new\n if (selectionState.range) {\n const [cleared] = terminalSelectionUpdate({ type: \"clear\" }, selectionState)\n selectionState = cleared\n }\n // Resolve contain boundary from the node under the cursor.\n // If the click lands inside a `userSelect=\"contain\"` subtree, the selection\n // range is clamped to that ancestor's scrollRect so drags can't leak into\n // adjacent siblings. selectionHitTest uses the selection-aware walk\n // (respects userSelect=\"none\" subtrees) rather than pointer hit test.\n const agRoot = getContainerRoot(container)\n const hit = agRoot ? selectionHitTest(agRoot, mouseData.x, mouseData.y) : null\n const scope = hit ? findContainBoundary(hit) : null\n const [next] = terminalSelectionUpdate(\n { type: \"start\", col: mouseData.x, row: mouseData.y, scope },\n selectionState,\n )\n selectionState = next\n notifySelectionListeners()\n // Force full re-render to clear old overlay (incremental render won't\n // overwrite the inverse-video ANSI the overlay wrote directly to stdout)\n if (currentBuffer) {\n runtime.invalidate()\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n writeSelectionOverlay()\n }\n // Don't consume — let the component tree also handle mousedown (for click-to-focus etc.)\n } else if (mouseData.action === \"move\" && selectionState.selecting) {\n const [next] = terminalSelectionUpdate({ type: \"extend\", col: mouseData.x, row: mouseData.y }, selectionState)\n selectionState = next\n notifySelectionListeners()\n // Re-render overlay to show updated selection\n if (currentBuffer) {\n runtime.render(currentBuffer)\n writeSelectionOverlay()\n }\n // Consume move events during selection — don't dispatch to component tree\n return true\n } else if (mouseData.action === \"up\" && selectionState.selecting) {\n const [next] = terminalSelectionUpdate({ type: \"finish\" }, selectionState)\n selectionState = next\n notifySelectionListeners()\n\n // Copy selected text via OSC 52\n if (next.range && currentBuffer) {\n const text = extractText(currentBuffer._buffer, next.range, { scope: next.scope })\n if (text.length > 0) {\n const base64 = globalThis.Buffer.from(text).toString(\"base64\")\n target.write(`\\x1b]52;c;${base64}\\x07`)\n }\n }\n // Re-render overlay with final selection\n if (currentBuffer) {\n runtime.render(currentBuffer)\n writeSelectionOverlay()\n }\n // Don't consume — let click handler run\n }\n }\n }\n\n // Selection: clear on any keypress\n if (selectionEnabled && event.type === \"term:key\" && selectionState.range) {\n const [next] = terminalSelectionUpdate({ type: \"clear\" }, selectionState)\n selectionState = next\n notifySelectionListeners()\n // Force full re-render to remove overlay\n if (currentBuffer) {\n runtime.invalidate()\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n }\n }\n\n // When scrolled up in virtual inline mode, don't dispatch events to component tree\n // (except for search which is handled above)\n if (scrollback && virtualScrollOffset > 0 && event.type === \"term:key\") {\n // Any non-search keypress returns to live view\n virtualScrollOffset = 0\n return true\n }\n\n const ctx = createHandlerContext(store, focusManager, container)\n return invokeEventHandler(event, handlers, ctx, mouseEventState, container)\n }\n\n /**\n * Process a batch of events — run all handlers, then render once.\n *\n * This is the key optimization for press-and-hold / auto-repeat keys.\n * When events arrive faster than renders (e.g., 30/sec auto-repeat vs\n * 50ms renders), we batch all pending handlers into a single render pass.\n *\n * For a batch of 3 'j' presses: handler1 → handler2 → handler3 → render.\n * The cursor moves 3 positions, but we only pay one render cost.\n */\n async function processEventBatch(events: NamespacedEvent[]): Promise<Buffer | null> {\n if (shouldExit || events.length === 0) return null\n _renderCount = 0\n _eventStart = performance.now()\n\n // Keypress performance span — wraps the entire batch cycle.\n // perfLog.span?.() short-circuits all argument evaluation when TRACE is off.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n using _perfSpan = perfLog.span?.(\n \"keypress\",\n (() => {\n startTracking()\n const keyEvents = events.filter((e) => e.type === \"term:key\")\n return {\n key:\n keyEvents.length > 0\n ? keyEvents.map((e) => (e.data as { input: string }).input).join(\",\")\n : (events[0]?.type ?? \"unknown\"),\n }\n })(),\n )\n\n // Intercept lifecycle keys (Ctrl+Z, Ctrl+C) BEFORE they reach app handlers.\n // These must be handled at the runtime level, not by individual components.\n if (!headless) {\n for (let i = events.length - 1; i >= 0; i--) {\n const event = events[i]!\n if (event.type !== \"term:key\") continue\n const data = event.data as { input: string; key: Key }\n\n // Ctrl+Z: suspend (parseKey returns input=\"z\" with key.ctrl=true)\n if (data.input === \"z\" && data.key.ctrl && suspendOption) {\n const prevented = onSuspendHook?.() === false\n if (!prevented) {\n // Remove this event from the batch\n events.splice(i, 1)\n const state = captureTerminalState({\n alternateScreen,\n cursorHidden: true,\n mouse: mouseEnabled,\n kitty: kittyEnabled,\n kittyFlags,\n bracketedPaste: true,\n rawMode: true,\n focusReporting: focusReportingEnabled,\n })\n performSuspend(state, stdout, stdin, () => {\n // After resume, trigger a full re-render\n runtime.invalidate()\n onResumeHook?.()\n })\n } else {\n events.splice(i, 1)\n }\n }\n\n // Ctrl+C: exit (parseKey returns input=\"c\" with key.ctrl=true)\n if (data.input === \"c\" && data.key.ctrl && exitOnCtrlCOption) {\n const prevented = onInterruptHook?.() === false\n if (!prevented) {\n exit()\n return null\n }\n events.splice(i, 1)\n }\n }\n if (events.length === 0) return null\n }\n\n // Suppress subscription renders — the flush loop below handles everything.\n inEventHandler = true\n isRendering = true\n\n // Input pipeline Stage 3: Event Loop — see docs/guide/input-architecture.md\n //\n // Event precedence (plugin-centric model):\n // 1. Raw: modifier tracking + keyboard state (always fires)\n // 2. Focused: focus tree dispatch via handleFocusNavigation (consumes if handled)\n // 3. Fallback: RuntimeContext listeners (useInput — only unhandled events)\n // 4. App handler (TEA update / commands)\n //\n // This ensures focused components (modals, TextInput) get events BEFORE global\n // hooks (useInput). A modal's onKeyDown for Escape fires before useInput's quit.\n for (const event of events) {\n if (event.type === \"term:key\") {\n const { input, key: parsedKey } = event.data as { input: string; key: Key }\n\n // Raw lane: Always update keyboard modifier state (Super/Cmd, Hyper) for\n // mouse events. SGR mouse protocol can't report these — Kitty fills the gap.\n updateKeyboardModifiers(mouseEventState, parsedKey)\n\n // Release and modifier-only events: bridge to RuntimeContext (useModifierKeys\n // needs them) but skip focus dispatch and app handlers.\n if (parsedKey.eventType === \"release\" || isModifierOnlyEvent(input, parsedKey)) {\n for (const listener of runtimeInputListeners) {\n listener(input, parsedKey)\n }\n if (shouldExit) {\n inEventHandler = false\n return null\n }\n continue\n }\n\n // Focused lane: dispatch through focus tree BEFORE useInput.\n // If a focused component handles the event (stopPropagation/preventDefault),\n // useInput never sees it — focused components have priority.\n let focusConsumed = false\n if (focusManager.activeElement) {\n const focusResult = handleFocusNavigation(input, parsedKey, focusManager, container)\n focusConsumed = focusResult === \"consumed\"\n }\n\n // Fallback lane: bridge to RuntimeContext listeners (useInput) only if\n // the focus tree didn't consume the event.\n if (!focusConsumed) {\n for (const listener of runtimeInputListeners) {\n listener(input, parsedKey)\n }\n }\n } else if (event.type === \"term:paste\") {\n const { text } = event.data as { text: string }\n for (const listener of runtimePasteListeners) {\n listener(text)\n }\n } else if (event.type === \"term:focus\") {\n const { focused } = event.data as { focused: boolean }\n for (const listener of runtimeFocusListeners) {\n listener(focused)\n }\n }\n\n // If a listener called exit() (e.g., useInput handler returned \"exit\"),\n // stop processing events immediately — don't render or flush.\n if (shouldExit) {\n inEventHandler = false\n return null\n }\n\n // Skip key events already handled: release/modifier-only were continued above,\n // focus-consumed events still reach the app handler for render barriers.\n if (event.type === \"term:key\") {\n const { input, key: k } = event.data as { input: string; key: Key }\n if (k.eventType === \"release\") continue\n if (isModifierOnlyEvent(input, k)) continue\n }\n\n const result = runEventHandler(event)\n if (result === false) {\n isRendering = false\n inEventHandler = false\n exit()\n return null\n }\n\n // Render barrier: if handler requested flush, render now before next event.\n // This ensures newly mounted components (e.g., InlineEditField) have their\n // refs set up before the next event handler runs.\n //\n // IMPORTANT: runtime.render() must be called here to keep the runtime's\n // prevBuffer in sync with the Ag's internal prevBuffer. Without this,\n // the post-batch doRender's dirty-row tracking would be stale relative\n // to runtime.prevBuffer, causing diffBuffers() to skip all rows and\n // produce an empty diff (0 bytes output).\n if (result === \"flush\") {\n pendingRerender = false\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n // Flush effects so mounted components can set up refs\n await Promise.resolve()\n if (pendingRerender) {\n pendingRerender = false\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n }\n }\n }\n\n // Clear deferred renders from handlers' setState calls — the explicit\n // doRender below picks up all state changes in one pass.\n pendingRerender = false\n\n // Explicit render — batches all handler state changes + flushes effects\n try {\n currentBuffer = doRender()\n } finally {\n isRendering = false\n }\n\n // Flush deferred re-renders from effects.\n // React's passive effects (useEffect) are scheduled during doRender\n // but flushed at the START of the next doRender (flushPassiveEffects).\n // The await drains the microtask queue so React's internally-queued\n // effect flush runs. Since inEventHandler=true, any setState from\n // effects just sets pendingRerender (no microtask render).\n let flushCount = 0\n const maxFlushes = 5\n while (flushCount < maxFlushes) {\n await Promise.resolve() // Drain microtask queue → passive effects flush\n if (!pendingRerender) break\n pendingRerender = false\n isRendering = true\n try {\n currentBuffer = doRender()\n } finally {\n isRendering = false\n }\n flushCount++\n }\n\n // The render phase's dirty rows are relative to the Ag's internal prevBuffer.\n // But runtime.render() diffs against its own prevBuffer, which may differ\n // when: (a) multiple doRender calls shifted the Ag's prevBuffer ahead, or\n // (b) the Z chord timeout causes the zoom render to arrive as a deferred\n // event where intermediate renders have updated the Ag's prevBuffer.\n // Always mark all rows dirty to ensure runtime.render() does a full diff.\n // The cost is negligible (diffBuffers still skips identical rows via\n // rowMetadataEquals/rowCharsEquals pre-check), but correctness is guaranteed.\n currentBuffer._buffer.markAllRowsDirty()\n\n inEventHandler = false\n const runtimeStart = performance.now()\n runtime.render(currentBuffer)\n // Post-render: push to scrollback, overlay selection/search\n pushToScrollback()\n if (virtualScrollOffset > 0) {\n renderVirtualScrollbackView()\n }\n writeSelectionOverlay()\n renderSearchHighlights()\n renderSearchBarOverlay()\n const runtimeMs = performance.now() - runtimeStart\n if (_perfLog) {\n const totalMs = performance.now() - _eventStart\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `EVENT batch(${events.length} ${events[0]?.type}): ${totalMs.toFixed(1)}ms total, ${_renderCount} doRender() calls, runtime.render=${runtimeMs.toFixed(1)}ms\\n---\\n`,\n )\n }\n // Budget check — warn if batch took longer than one frame (16ms)\n if (_perfSpan) {\n checkBudget(events[0]?.type ?? \"batch\", performance.now() - _eventStart)\n }\n return currentBuffer\n }\n\n // Start event loop\n //\n // Event coalescing: when events arrive faster than renders, we batch\n // consecutive handler calls into a single render pass. This prevents\n // the \"event backlog\" problem where auto-repeat keys queue up faster\n // than they can be rendered (e.g., 30/sec auto-repeat vs 50ms renders).\n //\n // Strategy: collect events into a shared queue, run all pending handlers,\n // render once. This means pressing and holding 'j' processes 2-3 cursor\n // moves per render instead of 1, keeping up with auto-repeat.\n const eventQueue: NamespacedEvent[] = []\n let eventQueueResolve: (() => void) | null = null\n\n const eventLoop = async () => {\n // Merge all provider event streams\n const providerEventStreams = Object.entries(providers).map(([name, provider]) =>\n createProviderEventStream(name, provider),\n )\n\n const allEvents = merge(...providerEventStreams)\n\n // Pump events from async iterable into the shared queue\n const pumpEvents = async () => {\n try {\n for await (const event of takeUntil(allEvents, signal)) {\n eventQueue.push(event)\n if (eventQueueResolve) {\n const resolve = eventQueueResolve\n eventQueueResolve = null\n resolve()\n }\n if (shouldExit) break\n }\n } finally {\n // Signal end of events\n if (eventQueueResolve) {\n const resolve = eventQueueResolve\n eventQueueResolve = null\n resolve()\n }\n }\n }\n\n // Run text sizing probe BEFORE stdin is consumed by the input parser.\n // The probe writes a test sequence to stdout and reads the CPR response\n // from stdin. This must happen before pumpEvents() attaches the stdin\n // data listener, otherwise the CPR response would be consumed as a key event.\n if (needsProbe) {\n try {\n // Set up temporary raw mode + stdin listener for probe\n const wasRaw = stdin.isRaw\n if (stdin.isTTY && !wasRaw) {\n stdin.setRawMode(true)\n stdin.resume()\n stdin.setEncoding(\"utf8\")\n }\n\n const probeRead = (): Promise<string> =>\n new Promise<string>((resolve) => {\n const onData = (data: string) => {\n stdin.off(\"data\", onData)\n resolve(data as string)\n }\n stdin.on(\"data\", onData)\n })\n\n const probeResult = await detectTextSizingSupport(\n (data) => (outputGuard ? outputGuard.writeStdout(data) : stdout.write(data)),\n probeRead,\n 500, // Short timeout — probe should be fast\n )\n\n // If probe result differs from initial heuristic, recreate pipeline\n if (probeResult.supported !== textSizingEnabled) {\n textSizingEnabled = probeResult.supported\n if (effectiveCaps) {\n effectiveCaps = { ...effectiveCaps, textSizingSupported: textSizingEnabled }\n pipelineConfig = createPipeline({ caps: effectiveCaps })\n // Update runtime's output phase to use the new measurer\n runtime.setOutputPhaseFn(pipelineConfig.outputPhaseFn)\n }\n // Invalidate pipeline and runtime diff state for full redraw.\n // Recreate Ag with updated measurer (text sizing support changed).\n _ag = null\n runtime.invalidate()\n // Force full re-render with updated measurer\n if (!isRendering) {\n isRendering = true\n try {\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n } finally {\n isRendering = false\n }\n }\n }\n\n // Restore raw mode if we changed it (pumpEvents will set it again)\n if (stdin.isTTY && !wasRaw) {\n stdin.setRawMode(false)\n stdin.pause()\n }\n } catch {\n // Probe failed — keep current textSizing setting (safe fallback)\n }\n }\n\n // Run DEC width detection probe BEFORE stdin is consumed by the input parser.\n // Queries DEC modes 1020-1023 for emoji/CJK/PUA width settings.\n // Must happen before pumpEvents() for the same reason as text-sizing probe.\n if (needsWidthDetection) {\n try {\n const wasRaw = stdin.isRaw\n if (stdin.isTTY && !wasRaw) {\n stdin.setRawMode(true)\n stdin.resume()\n stdin.setEncoding(\"utf8\")\n }\n\n const stdinHandlers: Array<(data: string) => void> = []\n const stdinListener = (data: string) => {\n for (const handler of stdinHandlers) handler(data)\n }\n stdin.on(\"data\", stdinListener)\n\n const detector = createWidthDetector({\n write: (data) => (outputGuard ? outputGuard.writeStdout(data) : stdout.write(data)),\n onData: (handler) => {\n stdinHandlers.push(handler)\n return () => {\n const idx = stdinHandlers.indexOf(handler)\n if (idx >= 0) stdinHandlers.splice(idx, 1)\n }\n },\n timeoutMs: 200,\n })\n\n const widthConfig = await detector.detect()\n detector.dispose()\n stdin.off(\"data\", stdinListener)\n\n // Apply detected width config to caps and recreate pipeline if changed\n if (effectiveCaps) {\n const updatedCaps = applyWidthConfig(effectiveCaps, widthConfig)\n const capsChanged =\n updatedCaps.textEmojiWide !== effectiveCaps.textEmojiWide ||\n updatedCaps.textSizingSupported !== effectiveCaps.textSizingSupported\n if (capsChanged) {\n effectiveCaps = updatedCaps\n pipelineConfig = createPipeline({ caps: effectiveCaps })\n runtime.setOutputPhaseFn(pipelineConfig.outputPhaseFn)\n // Recreate Ag with updated measurer (caps changed text sizing/emoji width)\n _ag = null\n runtime.invalidate()\n if (!isRendering) {\n isRendering = true\n try {\n currentBuffer = doRender()\n runtime.render(currentBuffer)\n } finally {\n isRendering = false\n }\n }\n }\n }\n\n if (stdin.isTTY && !wasRaw) {\n stdin.setRawMode(false)\n stdin.pause()\n }\n } catch {\n // Width detection failed — keep default caps (safe fallback)\n }\n }\n\n // Start pump in background — this synchronously runs the term-provider\n // generator body, which attaches the stdin data listener. After this call,\n // stdin is being consumed, so terminal responses won't leak as raw text.\n pumpEvents().catch((err: unknown) => log.error?.(`pumpEvents failed: ${err}`))\n\n // Enable focus reporting NOW — after stdin listener is attached.\n // Must be deferred from the init phase because the terminal's immediate\n // CSI I/O response would leak before the input parser was ready.\n if (focusReportingOption && !focusReportingEnabled) {\n enableFocusReporting((s) => (outputGuard ? outputGuard.writeStdout(s) : stdout.write(s)))\n focusReportingEnabled = true\n }\n\n try {\n while (!shouldExit && !signal.aborted) {\n // Wait for at least one event\n if (eventQueue.length === 0) {\n await new Promise<void>((resolve) => {\n eventQueueResolve = resolve\n signal.addEventListener(\"abort\", () => resolve(), { once: true })\n })\n }\n\n if (shouldExit || signal.aborted) break\n if (eventQueue.length === 0) continue\n\n // Drain-then-render: yield to the event loop repeatedly so the pump\n // (async-iterator chain: term-provider → merge → map → takeUntil →\n // pumpEvents) can push ALL pending events into eventQueue before we\n // process the batch. Each hop through the async iterator pipeline\n // costs several microtask ticks per event, so a single\n // `Promise.resolve()` yield is not enough to drain a burst of 10+\n // events from the term-provider's internal queue. We use\n // `setImmediate` (which runs after ALL pending microtasks) so that a\n // full async-iterator round-trip has time to complete. Then we loop\n // until the queue is stable across two consecutive yields, meaning\n // the pipeline has delivered everything it had ready.\n //\n // This ensures rapid keypresses (e.g., jumping from fold level 1 to\n // 10, or OS auto-repeat buffering \"jjjjj...\") coalesce into ONE\n // render cycle instead of N.\n //\n // Safety: bounded by maxDrainSpins to prevent pathological stalls\n // if an event source is producing faster than we can drain. Under\n // realistic auto-repeat (30-60 keys/sec), events arrive in a short\n // burst then go quiet — maxDrainSpins=32 is plenty of headroom.\n const maxDrainSpins = 32\n let drainSpins = 0\n const yieldToEventLoop = () => new Promise<void>((resolve) => setImmediate(resolve))\n // First mandatory yield — lets events already in-flight land.\n await yieldToEventLoop()\n let prevLen = eventQueue.length\n while (drainSpins < maxDrainSpins) {\n // eslint-disable-next-line no-await-in-loop -- intentional: sequential yields drain the async iterator pipeline\n await yieldToEventLoop()\n const curLen = eventQueue.length\n if (curLen === prevLen) break\n prevLen = curLen\n drainSpins++\n }\n if (_perfLog) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"node:fs\").appendFileSync(\n \"/tmp/silvery-perf.log\",\n `DRAIN: spins=${drainSpins}, batch=${eventQueue.length}\\n`,\n )\n }\n // Expose diagnostic counters on globalThis for test assertions.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const _g = globalThis as any\n _g.__silvery_last_drain_spins = drainSpins\n _g.__silvery_last_batch_size = eventQueue.length\n _g.__silvery_batch_count = (_g.__silvery_batch_count ?? 0) + 1\n\n // Process all pending events — run handlers without rendering\n const buf = await processEventBatch(eventQueue.splice(0))\n if (buf) emitFrame(buf)\n }\n } finally {\n // Mark frames as done and notify waiters\n framesDone = true\n if (frameResolve) {\n const resolve = frameResolve\n frameResolve = null\n // Signal completion — resolve with a sentinel that next() will detect\n resolve(null as unknown as Buffer)\n }\n\n // Async drain: give the event loop 1 tick + 15ms to receive any\n // late-arriving bytes (Kitty release events, mouse events) that were\n // in the kernel TTY buffer when we sent the disable sequences.\n // This is the async path — signal handlers use the sync fallback in exit().\n if (shouldExit && !cleanedUp && !headless && stdin.isTTY) {\n try {\n // Remove data listener but keep raw mode on — we're still consuming\n stdin.removeAllListeners(\"data\")\n stdin.resume()\n // Let the event loop tick to deliver kernel-buffered bytes\n await new Promise((resolve) => setTimeout(resolve, 15))\n // Drain whatever arrived\n while (stdin.read() !== null) {\n /* discard late arrivals */\n }\n stdin.pause()\n } catch {\n // Best-effort — continue to cleanup\n }\n }\n\n // Cleanup and resolve exit promise\n cleanup()\n exitResolve()\n }\n }\n\n // Start loop in background\n eventLoop().catch((err: unknown) => log.error?.(`eventLoop failed: ${err}`))\n\n // Return handle with async iteration\n const handle: AppHandle<S & I> = {\n get text() {\n return currentBuffer.text\n },\n get root() {\n return getContainerRoot(container)\n },\n get buffer() {\n return currentBuffer?._buffer ?? null\n },\n get store() {\n return store\n },\n waitUntilExit() {\n return exitPromise\n },\n unmount() {\n exit()\n },\n [Symbol.dispose]() {\n exit()\n },\n async press(rawKey: string) {\n // perfLog.span is always defined; the cost of performance.now() is negligible.\n const pressStart = performance.now()\n // Convert named keys to ANSI bytes (Kitty protocol when enabled)\n const ansiKey = useKittyMode ? keyToKittyAnsi(rawKey) : keyToAnsi(rawKey)\n const [input, parsedKey] = parseKey(ansiKey)\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n using _perfSpan = perfLog.span?.(\n \"keypress\",\n (() => {\n startTracking()\n return { key: input || rawKey }\n })(),\n )\n\n // Intercept lifecycle keys (Ctrl+C) — same as processEventBatch but for\n // headless/press() path. parseKey returns input=\"c\" with key.ctrl=true\n // for Ctrl+C (not the raw \"\\x03\" byte).\n if (input === \"c\" && parsedKey.ctrl && exitOnCtrlCOption) {\n const prevented = onInterruptHook?.() === false\n if (!prevented) {\n exit()\n return\n }\n }\n\n // Bridge to RuntimeContext listeners (useInput consumers)\n for (const listener of runtimeInputListeners) {\n listener(input, parsedKey)\n }\n\n // Suppress subscription renders — flush loop below handles everything.\n inEventHandler = true\n isRendering = true\n\n // Focus system: dispatch key event and handle default navigation\n const focusResult = handleFocusNavigation(input, parsedKey, focusManager, container)\n if (focusResult === \"consumed\") {\n pendingRerender = false\n isRendering = false\n inEventHandler = false\n doRender()\n await Promise.resolve()\n if (_perfSpan) checkBudget(input || rawKey, performance.now() - pressStart)\n return\n }\n\n // Dispatch to app handlers (namespaced + legacy)\n const handlerCtx = createHandlerContext(store, focusManager, container)\n if (dispatchKeyToHandlers(input, parsedKey, handlers, handlerCtx) === \"exit\") {\n isRendering = false\n inEventHandler = false\n exit()\n return\n }\n\n // Clear deferred renders — explicit render below batches all changes\n pendingRerender = false\n\n // Trigger re-render (batches handler state changes + flushes effects)\n try {\n currentBuffer = doRender()\n } finally {\n isRendering = false\n }\n // Flush deferred re-renders from effects.\n // await drains microtask queue → React passive effects flush.\n // Since inEventHandler=true, setState from effects just flags\n // pendingRerender (no microtask render).\n let flushCount = 0\n const maxFlushes = 5\n while (flushCount < maxFlushes) {\n await Promise.resolve()\n if (!pendingRerender) break\n pendingRerender = false\n isRendering = true\n try {\n currentBuffer = doRender()\n } finally {\n isRendering = false\n }\n flushCount++\n }\n // Mark all rows dirty — same safety net as processEventBatch (line 2443).\n // When the effect flush loop ran additional doRender calls, the final buffer's\n // dirty rows are relative to the Ag's internal prevBuffer (which advanced),\n // not the runtime's prevBuffer (which is from the last runtime.render()).\n // Without this, diffBuffers skips rows that changed relative to runtime's\n // prevBuffer but aren't marked dirty → garbled output.\n if (flushCount > 0) {\n currentBuffer._buffer.markAllRowsDirty()\n }\n inEventHandler = false\n runtime.render(currentBuffer)\n if (_perfSpan) checkBudget(input || rawKey, performance.now() - pressStart)\n },\n\n [Symbol.asyncIterator](): AsyncIterator<Buffer> {\n return {\n async next(): Promise<IteratorResult<Buffer>> {\n if (framesDone || shouldExit) {\n return { done: true, value: undefined as unknown as Buffer }\n }\n\n // Wait for next frame from event loop\n const buf = await new Promise<Buffer>((resolve) => {\n // If already done, resolve immediately\n if (framesDone || shouldExit) {\n resolve(null as unknown as Buffer)\n return\n }\n frameResolve = resolve\n })\n\n // null sentinel means done\n if (!buf) {\n return { done: true, value: undefined as unknown as Buffer }\n }\n\n return { done: false, value: buf }\n },\n async return(): Promise<IteratorResult<Buffer>> {\n exit()\n return { done: true, value: undefined as unknown as Buffer }\n },\n }\n },\n }\n\n return handle\n}\n","/**\n * usePasteCallback — subscribe to bracketed paste events.\n *\n * Simple callback-based paste hook for run() apps.\n * For component composition with PasteProvider, use usePaste() instead.\n *\n * @example\n * ```tsx\n * usePasteCallback((text) => {\n * insertText(text)\n * })\n * ```\n */\n\nimport { useContext, useEffect, useRef } from \"react\"\nimport { RuntimeContext } from \"../context\"\n\nexport type PasteCallback = (text: string) => void\n\nexport function usePasteCallback(handler: PasteCallback): void {\n const rt = useContext(RuntimeContext)\n\n const handlerRef = useRef(handler)\n handlerRef.current = handler\n\n useEffect(() => {\n if (!rt) return\n return rt.on(\"paste\", (text: string) => {\n handlerRef.current(text)\n })\n }, [rt])\n}\n","/**\n * run() - Layer 2 entry point for silvery-loop\n *\n * Thin wrapper over createApp() for simple React apps with keyboard input.\n * Use this when you want React component state (useState, useEffect)\n * with simple keyboard input via useInput().\n *\n * For stores and providers, use createApp() (Layer 3) directly.\n *\n * @example\n * ```tsx\n * import { run, useInput } from '@silvery/ag-term/runtime'\n *\n * function Counter() {\n * const [count, setCount] = useState(0)\n *\n * useInput((input, key) => {\n * if (input === 'j') setCount(c => c + 1)\n * if (key.upArrow) setCount(c => c + 1)\n * if (input === 'q') return 'exit'\n * })\n *\n * return <Text>Count: {count}</Text>\n * }\n *\n * await run(<Counter />)\n * ```\n */\n\nimport React, { type ReactElement } from \"react\"\n\nimport { createApp } from \"./create-app\"\nimport type { Term } from \"../ansi/term\"\nimport { detectTerminalCaps } from \"../terminal-caps\"\nimport { detectTheme } from \"@silvery/theme/detect\"\nimport { ThemeProvider } from \"@silvery/theme/ThemeContext\"\n\n// Re-export types from keys.ts\nexport type { Key, InputHandler } from \"./keys\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Options for run().\n *\n * run() auto-detects terminal capabilities and enables features by default.\n * Pass explicit values to override. For the full list of capabilities detected,\n * see {@link detectTerminalCaps} in terminal-caps.ts.\n *\n * **Mouse tracking note:** When `mouse` is enabled (the default), the terminal\n * captures mouse events and native text selection (copy/paste) requires holding\n * Shift (or Option on macOS in some terminals). Set `mouse: false` to restore\n * native copy/paste behavior.\n */\nexport interface RunOptions {\n /** Terminal dimensions (default: from process.stdout) */\n cols?: number\n rows?: number\n /** Standard output (default: process.stdout) */\n stdout?: NodeJS.WriteStream\n /** Standard input (default: process.stdin) */\n stdin?: NodeJS.ReadStream\n /**\n * Plain writable sink for ANSI output. Headless mode with active output.\n * Requires cols and rows. Input via handle.press().\n */\n writable?: { write(data: string): void }\n /** Abort signal for external cleanup */\n signal?: AbortSignal\n /**\n * Enable Kitty keyboard protocol for unambiguous key identification\n * (Cmd ⌘, Hyper ✦ modifiers, key release events).\n * - `true`: enable with DISAMBIGUATE flag (1)\n * - number: enable with specific KittyFlags bitfield\n * - `false`: don't enable\n * - Default: auto-detected from terminal (enabled for Ghostty, Kitty, WezTerm, foot)\n */\n kitty?: boolean | number\n /**\n * Enable SGR mouse tracking (mode 1006) for click, scroll, and drag events.\n * When enabled, native text selection requires holding Shift (or Option on macOS)\n * and native terminal scrolling is disabled.\n * Default: `true` in fullscreen mode, `false` in inline mode (where content\n * lives in terminal scrollback and natural scrolling is expected).\n */\n mouse?: boolean\n /**\n * Render mode:\n * - `\"fullscreen\"` — alt screen buffer (default)\n * - `\"inline\"` — scrollback-compatible, no alt screen\n * - `\"virtualInline\"` — alt screen with virtual scrollback (scrollable history + search)\n */\n mode?: \"fullscreen\" | \"inline\" | \"virtualInline\"\n /**\n * Enable Kitty text sizing protocol (OSC 66) for PUA characters.\n * Ensures nerdfont/powerline icons are measured and rendered at the correct width.\n * - `true`: force enable\n * - `\"auto\"`: use heuristic, then probe to verify (progressive enhancement)\n * - `\"probe\"`: start disabled, probe async, enable on confirmation\n * - `false`: disabled\n * - Default: \"auto\"\n */\n textSizing?: boolean | \"auto\" | \"probe\"\n /**\n * Enable DEC width mode detection (modes 1020-1023).\n * Queries the terminal for emoji/CJK/PUA width settings at startup.\n * - `true`: always run width detection probe\n * - `\"auto\"`: run probe when caps are provided (default)\n * - `false`: disabled\n * Default: \"auto\"\n */\n widthDetection?: boolean | \"auto\"\n /**\n * Enable terminal focus reporting (CSI ?1004h).\n * Dispatches 'term:focus' events with `{ focused: boolean }`.\n * Default: true\n */\n focusReporting?: boolean\n /**\n * Terminal capabilities for width measurement and output suppression.\n * Default: auto-detected via detectTerminalCaps()\n */\n caps?: import(\"../terminal-caps.js\").TerminalCaps\n /**\n * Handle Ctrl+Z by suspending the process. Default: true\n */\n suspendOnCtrlZ?: boolean\n /**\n * Handle Ctrl+C by restoring terminal and exiting. Default: true\n */\n exitOnCtrlC?: boolean\n /** Called before suspend. Return false to prevent. */\n onSuspend?: () => boolean | void\n /** Called after resume from suspend. */\n onResume?: () => void\n /** Called on Ctrl+C. Return false to prevent exit. */\n onInterrupt?: () => boolean | void\n}\n\n/**\n * Handle returned by run() for controlling the app.\n */\nexport interface RunHandle {\n /** Current rendered text (no ANSI) */\n readonly text: string\n /** Live reconciler root node (for locator queries) */\n readonly root: import(\"@silvery/ag/types\").AgNode\n /** Current terminal buffer (cell-level access) */\n readonly buffer: import(\"@silvery/ag-term/buffer\").TerminalBuffer | null\n /** Wait until the app exits */\n waitUntilExit(): Promise<void>\n /** Unmount and cleanup */\n unmount(): void\n /** Dispose (alias for unmount) — enables `using` */\n [Symbol.dispose](): void\n /** Send a key press */\n press(key: string): Promise<void>\n}\n\n// ============================================================================\n// Hooks (Layer 2 — uses RuntimeContext, works in both run() and createApp())\n// ============================================================================\n\n// All hooks re-exported from ag-react — single implementation, no duplication.\n// run.tsx has zero hook implementations. See km-silvery.zero-hooks-run.\nexport { useInput, type UseInputOptions } from \"@silvery/ag-react/hooks/useInput\"\nexport { useExit } from \"@silvery/ag-react/hooks/useExit\"\nexport {\n usePasteCallback as usePaste,\n type PasteCallback as PasteHandler,\n} from \"@silvery/ag-react/hooks/usePasteCallback\"\n\n// ============================================================================\n// run() — thin wrapper over createApp()\n// ============================================================================\n\n/**\n * Run a React component with the silvery-loop runtime.\n *\n * Accepts either a Term instance or RunOptions:\n * - `run(<App />, term)` — Term handles streams, createApp handles rendering\n * - `run(<App />, { cols, rows, ... })` — classic options API\n *\n * Internally delegates to createApp() with an empty store.\n * For stores and providers, use createApp() directly.\n */\nexport async function run(element: ReactElement, term: Term, termOptions?: Partial<RunOptions>): Promise<RunHandle>\nexport async function run(element: ReactElement, options?: RunOptions): Promise<RunHandle>\nexport async function run(\n element: ReactElement,\n optionsOrTerm: RunOptions | Term = {},\n termOptions?: Partial<RunOptions>,\n): Promise<RunHandle> {\n // Term path: pass Term as provider + its streams, auto-enable from Term caps\n if (isTerm(optionsOrTerm)) {\n const term = optionsOrTerm as Term\n const emulator = (term as unknown as Record<string, unknown>)._emulator as { feed(data: string): void } | undefined\n\n // Emulator-backed term: non-headless mode with stdout routing to emulator.\n // Create a mock stdin that forwards sendInput() data to the term provider's\n // input parser, so events flow through the full createApp pipeline.\n if (emulator) {\n const { EventEmitter } = await import(\"node:events\")\n const stdinEmitter = new EventEmitter()\n const mockStdin = Object.assign(stdinEmitter, {\n isTTY: true,\n isRaw: false,\n fd: 0,\n setRawMode(_mode: boolean) {\n mockStdin.isRaw = _mode\n return mockStdin\n },\n read() {\n return null\n },\n resume() {\n return mockStdin\n },\n pause() {\n return mockStdin\n },\n ref() {\n return mockStdin\n },\n unref() {\n return mockStdin\n },\n setEncoding() {\n return mockStdin\n },\n }) as unknown as NodeJS.ReadStream\n\n // Wire sendInput: when term.sendInput(data) is called, emit on mock stdin\n // so the term provider's parser processes it through the real pipeline.\n // The mixed-proxy's set/defineProperty traps forward to termBase,\n // so this override replaces the original sendInput with one that\n // feeds the mock stdin instead of the internal event queue.\n if ((term as any).sendInput) {\n ;(term as any).sendInput = (data: string) => {\n stdinEmitter.emit(\"data\", data)\n }\n }\n\n // Resolve alternateScreen from termOptions.mode (if provided).\n // The mode prop is consumed at the run() level for the options path,\n // but in the Term path it needs explicit conversion.\n const termMode = termOptions?.mode\n const altScreen = termMode === \"inline\" ? false : true\n\n const app = createApp(() => () => ({}))\n const handle = await app.run(element, {\n alternateScreen: altScreen,\n ...termOptions,\n stdin: mockStdin,\n stdout: term.stdout, // Feeds emulator — protocol escapes reach the emulator\n guardOutput: false, // Don't monkeypatch process.stdout in test/emulator context\n cols: term.cols ?? 80,\n rows: term.rows ?? 24,\n })\n return wrapHandle(handle)\n }\n\n // Real terminal: full setup\n const caps = term.caps ?? detectTerminalCaps()\n // Detect terminal colors via OSC — must happen before alt screen\n const theme = await detectTheme()\n const themed = <ThemeProvider theme={theme}>{element}</ThemeProvider>\n const app = createApp(() => () => ({}))\n const handle = await app.run(themed, {\n term,\n stdout: term.stdout,\n stdin: term.stdin,\n cols: term.cols ?? undefined,\n rows: term.rows ?? undefined,\n caps,\n alternateScreen: true,\n kitty: caps.kittyKeyboard,\n mouse: true,\n focusReporting: true,\n textSizing: \"auto\",\n widthDetection: \"auto\",\n })\n return wrapHandle(handle)\n }\n\n // Options path: auto-detect caps and derive defaults\n const { mode, ...rest } = optionsOrTerm as RunOptions\n const caps = rest.caps ?? detectTerminalCaps()\n const headless = rest.writable != null || (rest.cols != null && rest.rows != null && !rest.stdout)\n // Detect terminal colors via OSC — must happen before alt screen (skipped for headless)\n const themed = headless\n ? element\n : await detectTheme().then((theme) => <ThemeProvider theme={theme}>{element}</ThemeProvider>)\n const app = createApp(() => () => ({}))\n const handle = await app.run(themed, {\n ...rest,\n caps,\n alternateScreen: mode !== \"inline\",\n virtualInline: mode === \"virtualInline\",\n kitty: rest.kitty ?? caps.kittyKeyboard,\n mouse: rest.mouse ?? mode !== \"inline\",\n focusReporting: rest.focusReporting ?? mode !== \"inline\",\n textSizing: rest.textSizing ?? \"auto\",\n widthDetection: rest.widthDetection ?? \"auto\",\n })\n return wrapHandle(handle)\n}\n\n/** Duck-type check: Term has getState and events as functions.\n * Note: Term is a Proxy wrapping chalk, so typeof is \"function\" not \"object\". */\nfunction isTerm(obj: unknown): obj is Term {\n if (obj == null) return false\n if (typeof obj !== \"object\" && typeof obj !== \"function\") return false\n const o = obj as Record<string, unknown>\n return typeof o.getState === \"function\" && typeof o.events === \"function\"\n}\n\n/** Wrap AppHandle as RunHandle (subset of the full handle). */\nfunction wrapHandle(handle: {\n readonly text: string\n readonly root: import(\"@silvery/ag/types\").AgNode\n readonly buffer: import(\"@silvery/ag-term/buffer\").TerminalBuffer | null\n waitUntilExit(): Promise<void>\n unmount(): void\n [Symbol.dispose](): void\n press(key: string): Promise<void>\n}): RunHandle {\n return {\n get text() {\n return handle.text\n },\n get root() {\n return handle.root\n },\n get buffer() {\n return handle.buffer\n },\n waitUntilExit: () => handle.waitUntilExit(),\n unmount: () => handle.unmount(),\n [Symbol.dispose]: () => handle[Symbol.dispose](),\n press: (key: string) => handle.press(key),\n }\n}\n","/**\n * Time/tick source for silvery-loop.\n *\n * Creates an AsyncIterable that yields at regular intervals.\n * Used for animations, spinners, progress bars, etc.\n */\n\n/**\n * Create a tick source that yields at regular intervals.\n *\n * The tick source respects AbortSignal for cleanup and stops\n * when the signal is aborted.\n *\n * @param intervalMs Interval between ticks in milliseconds\n * @param signal Optional AbortSignal to stop the tick source\n * @returns AsyncIterable that yields tick numbers (0, 1, 2, ...)\n *\n * @example\n * ```typescript\n * const controller = new AbortController()\n * const ticks = createTick(100, controller.signal)\n *\n * for await (const tick of ticks) {\n * console.log(`Tick ${tick}`)\n * if (tick >= 10) controller.abort()\n * }\n * ```\n */\nexport function createTick(intervalMs: number, signal?: AbortSignal): AsyncIterable<number> {\n return {\n [Symbol.asyncIterator]: () => createTickIterator(intervalMs, signal),\n }\n}\n\n/**\n * Create the actual tick iterator.\n */\nfunction createTickIterator(intervalMs: number, signal?: AbortSignal): AsyncIterator<number> {\n let count = 0\n let timer: ReturnType<typeof setTimeout> | undefined\n let pendingResolve: ((result: IteratorResult<number>) => void) | undefined\n let done = false\n\n // Handle abort signal\n const onAbort = () => {\n done = true\n if (timer) {\n clearTimeout(timer)\n timer = undefined\n }\n if (pendingResolve) {\n pendingResolve({ done: true, value: undefined })\n pendingResolve = undefined\n }\n }\n\n if (signal) {\n if (signal.aborted) {\n done = true\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true })\n }\n }\n\n return {\n async next(): Promise<IteratorResult<number>> {\n if (done) {\n return { done: true, value: undefined }\n }\n\n return new Promise<IteratorResult<number>>((resolve, _reject) => {\n pendingResolve = resolve\n\n timer = setTimeout(() => {\n if (!done) {\n const value = count++\n pendingResolve = undefined\n resolve({ done: false, value })\n }\n }, intervalMs)\n })\n },\n\n async return(): Promise<IteratorResult<number>> {\n done = true\n if (timer) {\n clearTimeout(timer)\n timer = undefined\n }\n if (signal) {\n signal.removeEventListener(\"abort\", onAbort)\n }\n if (pendingResolve) {\n pendingResolve({ done: true, value: undefined })\n pendingResolve = undefined\n }\n return { done: true, value: undefined }\n },\n }\n}\n\n/**\n * Create a tick source that yields at approximately 60fps (~16ms).\n *\n * @param signal Optional AbortSignal to stop the tick source\n * @returns AsyncIterable that yields frame numbers\n */\nexport function createFrameTick(signal?: AbortSignal): AsyncIterable<number> {\n return createTick(16, signal)\n}\n\n/**\n * Create a tick source that yields once per second.\n *\n * @param signal Optional AbortSignal to stop the tick source\n * @returns AsyncIterable that yields second counts\n */\nexport function createSecondTick(signal?: AbortSignal): AsyncIterable<number> {\n return createTick(1000, signal)\n}\n\n/**\n * Create a tick source with adaptive timing based on render performance.\n *\n * This is useful for maintaining a target frame rate while allowing\n * for slower frames when needed.\n *\n * @param targetFps Target frames per second (default: 60)\n * @param signal Optional AbortSignal to stop the tick source\n * @returns AsyncIterable with timing information\n */\nexport function createAdaptiveTick(\n targetFps = 60,\n signal?: AbortSignal,\n): AsyncIterable<{ tick: number; elapsed: number; delta: number }> {\n const targetMs = 1000 / targetFps\n let lastTime = Date.now()\n let tick = 0\n\n return {\n [Symbol.asyncIterator]: () => {\n let done = false\n let timer: ReturnType<typeof setTimeout> | undefined\n let pendingResolve:\n | ((\n result: IteratorResult<{\n tick: number\n elapsed: number\n delta: number\n }>,\n ) => void)\n | undefined\n\n const onAbort = () => {\n done = true\n if (timer) {\n clearTimeout(timer)\n timer = undefined\n }\n if (pendingResolve) {\n pendingResolve({ done: true, value: undefined })\n pendingResolve = undefined\n }\n }\n\n if (signal) {\n if (signal.aborted) {\n done = true\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true })\n }\n }\n\n return {\n async next(): Promise<IteratorResult<{ tick: number; elapsed: number; delta: number }>> {\n if (done) {\n return { done: true, value: undefined }\n }\n\n return new Promise((resolve) => {\n pendingResolve = resolve\n const now = Date.now()\n const elapsed = now - lastTime\n const delay = Math.max(0, targetMs - elapsed)\n\n timer = setTimeout(() => {\n if (!done) {\n const currentTime = Date.now()\n const delta = currentTime - lastTime\n lastTime = currentTime\n pendingResolve = undefined\n resolve({\n done: false,\n value: { tick: tick++, elapsed: currentTime, delta },\n })\n }\n }, delay)\n })\n },\n\n async return(): Promise<IteratorResult<{ tick: number; elapsed: number; delta: number }>> {\n done = true\n if (timer) {\n clearTimeout(timer)\n timer = undefined\n }\n if (signal) {\n signal.removeEventListener(\"abort\", onAbort)\n }\n if (pendingResolve) {\n pendingResolve({ done: true, value: undefined })\n pendingResolve = undefined\n }\n return { done: true, value: undefined }\n },\n }\n },\n }\n}\n"],"mappings":"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"}
|