silvery 0.19.0 → 0.19.2

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src-CF-6UN01.mjs","names":["signalEffect","DEFAULT_SCROLL_PADDING","DEFAULT_OVERSCAN","DEFAULT_MAX_RENDERED","DEFAULT_OVERSCAN","DEFAULT_MAX_RENDERED","getTabOrder","extractText","noopSubscribe","ESC","CSI","RESET","moveCursor","_enableMouse","_disableMouse","_enableKittyKeyboard","_disableKittyKeyboard","BEL","signalEffect","resolveNode","signalEffect","useApp","ESC","SELECTION_CAPABILITY","noop","noop","findWordBoundary","clampScroll","DEFAULT_WIDTH","DEFAULT_CHAR","DEFAULT_WIDTH","DEFAULT_WIDTH","log","log","DEFAULT_CONFIG","BORDER_CHARS","ANSI_COLORS","resolveColor","isTerm","log","globalGetCursorState","isModifierOnly","log","process","render","isTerm","createStore","process","writeSelectionOverlay","writeSelectionOverlayFn","pushToScrollback","pushToScrollbackFn","renderVirtualScrollbackView","renderVirtualScrollbackViewFn","renderSearchHighlights","renderSearchHighlightsFn","renderSearchBarOverlay","renderSearchBarOverlayFn"],"sources":["../packages/ag-react/src/components/Box.tsx","../packages/ag-term/src/scroll-utils.ts","../packages/ag-react/src/hooks/useVirtualizer.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-react/src/providers/SearchProvider.tsx","../packages/ag-react/src/ui/components/ListView.tsx","../packages/ag-react/src/hooks/useVirtualization.ts","../packages/ag-react/src/ThemeContext.tsx","../packages/ag-react/src/components/Text.tsx","../packages/ag-react/src/ui/components/HorizontalVirtualList.tsx","../packages/ag-react/src/ui/components/SplitView.tsx","../packages/ag-term/src/pane-manager.ts","../packages/ag-react/src/components/Fill.tsx","../packages/ag-react/src/hooks/useModifierKeys.ts","../packages/ag-term/src/output.ts","../packages/ag-react/src/hooks/useMouseCursor.ts","../packages/ag-react/src/components/Link.tsx","../packages/ag-react/src/ui/components/ErrorBoundary.tsx","../packages/ag-react/src/hooks/useConsole.ts","../packages/ag-react/src/ui/components/Console.tsx","../packages/ag-react/src/ui/components/Screen.tsx","../packages/ag-react/src/hooks/useAgNode.ts","../packages/ag-react/src/hooks/useSignal.ts","../packages/ag-react/src/hooks/useBoxMetrics.ts","../packages/ag-react/src/hooks/useAnimation.ts","../packages/ag-react/src/hooks/useRuntime.ts","../packages/ag-react/src/hooks/useApp.ts","../packages/ag-react/src/hooks/useExit.ts","../packages/ag-react/src/hooks/useStdout.ts","../packages/ag-react/src/hooks/useStderr.ts","../packages/ag-react/src/hooks/useFocus.ts","../packages/ag-react/src/hooks/useFocusable.ts","../packages/ag-react/src/hooks/useFocusWithin.ts","../packages/ag-react/src/hooks/useFocusManager.ts","../packages/ag-react/src/contexts/InputLayerContext.tsx","../packages/ag-react/src/hooks/useTerminalFocused.ts","../packages/ag-term/src/scroll-region.ts","../packages/ag-react/src/hooks/usePaste.tsx","../packages/ag-term/src/copy-extraction.ts","../packages/ag-react/src/hooks/usePasteEvents.ts","../packages/ag-react/src/hooks/useSelection.ts","../packages/ag-react/src/hooks/useFindState.ts","../packages/ag-react/src/hooks/useCopyModeState.ts","../packages/ag-react/src/hooks/useDragState.ts","../packages/ag-react/src/hooks/useInteractiveState.ts","../packages/ag-react/src/hooks/useListItem.tsx","../packages/ag-react/src/hooks/useColorScheme.ts","../packages/ag-react/src/hooks/readline-ops.ts","../packages/ag-react/src/ui/components/useReadline.ts","../packages/ag-react/src/hooks/useCursor.ts","../packages/ag-react/src/ui/components/TextInput.tsx","../packages/create/src/text-cursor.ts","../packages/ag-react/src/ui/components/useTextArea.ts","../packages/ag-react/src/ui/components/TextArea.tsx","../packages/ag-react/src/ui/components/EditContextDisplay.tsx","../packages/ag-react/src/ui/components/CursorLine.tsx","../packages/ag-react/src/ui/components/ModalDialog.tsx","../packages/ag-react/src/ui/components/Backdrop.tsx","../packages/ag-react/src/ui/components/PickerList.tsx","../packages/ag-react/src/ui/components/PickerDialog.tsx","../packages/ag-react/src/ui/components/Toggle.tsx","../packages/ag-react/src/ui/components/_tone.ts","../packages/ag-react/src/ui/components/Button.tsx","../packages/ag-react/src/ui/components/SearchBar.tsx","../packages/ag-react/src/ui/components/Spinner.tsx","../packages/ag-react/src/ui/components/ProgressBar.tsx","../packages/ag-react/src/ui/components/SelectList.tsx","../packages/ag-react/src/components/Table.tsx","../packages/ag-react/src/ui/components/Badge.tsx","../packages/ag-react/src/ui/components/Divider.tsx","../packages/ag-react/src/ui/components/Typography.tsx","../packages/ag-react/src/ui/components/Heading.tsx","../packages/ag-react/src/ui/components/Form.tsx","../packages/ag-react/src/ui/components/Toast.tsx","../packages/ag-react/src/ui/components/InlineAlert.tsx","../packages/ag-react/src/ui/components/Banner.tsx","../packages/ag-react/src/ui/components/Alert.tsx","../packages/ag-react/src/ui/components/CommandPalette.tsx","../packages/ag-react/src/ui/components/TreeView.tsx","../packages/ag-react/src/ui/components/Breadcrumb.tsx","../packages/ag-react/src/ui/components/Tabs.tsx","../packages/ag-react/src/ui/components/Tooltip.tsx","../packages/ag-react/src/ui/components/Skeleton.tsx","../packages/ag-react/src/hooks/usePositionRegistry.tsx","../packages/ag-react/src/hooks/useGridPosition.ts","../packages/ag-react/src/ui/components/GridCell.tsx","../packages/ag-react/src/components/Transform.tsx","../packages/ag-react/src/components/Newline.tsx","../packages/ag-react/src/components/Spacer.tsx","../packages/ag-react/src/components/Static.tsx","../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-react/src/hooks/useTerm.ts","../packages/ag-react/src/hooks/useWindowSize.ts","../packages/ag-react/src/ThemeProvider.tsx","../packages/ag-term/src/hit-registry-core.ts","../packages/ag-term/src/hit-registry.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/pipeline/adapter-pipeline.ts","../packages/ag-term/src/adapters/canvas-adapter.ts","../packages/ag-term/src/adapters/dom-adapter.ts","../packages/ag-term/src/clipboard.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/cursor-query.ts","../packages/ag-term/src/terminal-colors.ts","../packages/ag-term/src/ansi/width-detection.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/term-def.ts","../packages/ag-term/src/mouse-events.ts","../packages/ag-term/src/non-tty.ts","../packages/ag-term/src/inspector.ts","../packages/ag-term/src/measurer.ts","../packages/ag-term/src/selection-renderer.ts","../packages/ag-term/src/virtual-scrollback.ts","../packages/ag-term/src/index.ts","../packages/commands/src/with-commands.ts","../packages/commands/src/with-keybindings.ts","../packages/test/src/compare-buffers.ts","../packages/ag-term/src/plugins/with-diagnostics.ts","../packages/ag-term/src/plugins/with-render.ts","../packages/ink/src/with-ink-cursor.ts","../packages/ink/src/with-ink-focus.ts","../packages/test/src/debug-mismatch.ts","../packages/ag-term/src/scheduler.ts","../packages/create/src/runtime/with-terminal-chain.ts","../packages/create/src/runtime/with-paste-chain.ts","../packages/create/src/runtime/with-input-chain.ts","../packages/create/src/runtime/with-focus-chain.ts","../packages/create/src/runtime/with-custom-events.ts","../packages/create/src/runtime/base-app.ts","../packages/ag-react/src/chain-bridge.ts","../packages/ag-react/src/render.tsx","../packages/ag-react/src/measureElement.ts","../packages/ag-react/src/accessibility.ts","../packages/ag-react/src/edit-context.ts","../packages/create/src/text-ops.ts","../packages/ag-react/src/hooks/use-edit-context.ts","../packages/ag-react/src/contexts/InputBoundary.tsx","../packages/create/src/tea/index.ts","../packages/create/src/effects.ts","../packages/ag-react/src/ui/hooks/useTea.ts","../packages/create/src/core/index.ts","../packages/create/src/store/index.ts","../packages/create/src/streams/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/renderer.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/wrap-with-themed-provider.tsx","../packages/ag-term/src/runtime/themed.tsx","../packages/ag-term/src/runtime/tick.ts","../packages/ag-react/src/ReactiveThemeProvider.tsx","../packages/ag-react/src/hooks/useActiveScheme.ts","../packages/ag-react/src/exports.ts"],"sourcesContent":["/**\n * Silvery Box Component\n *\n * The primary layout primitive for Silvery. Box is a flexbox container that can hold\n * other Box or Text components. It supports all standard flexbox properties,\n * dimensions, spacing, and borders.\n *\n * Box renders to an 'silvery-box' host element that the reconciler converts to an\n * SilveryNode with an associated Yoga layout node.\n *\n * Box provides NodeContext to its children, enabling useBoxRect/useScrollRect hooks.\n * It also supports forwardRef for imperative access and onLayout for layout callbacks.\n */\n\nimport {\n type ForwardedRef,\n type JSX,\n type ReactNode,\n forwardRef,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\"\nimport { effect as signalEffect } from \"@silvery/signals\"\nimport { NodeContext } from \"../context\"\nimport { getLayoutSignals } from \"@silvery/ag/layout-signals\"\nimport type { BoxProps as BoxPropsType, AgNode, Rect } from \"@silvery/ag/types\"\n\n// ============================================================================\n// Props\n// ============================================================================\n\nexport interface BoxProps extends BoxPropsType {\n /** Child elements */\n children?: ReactNode\n}\n\n/**\n * Methods exposed via ref on Box component.\n */\nexport interface BoxHandle {\n /** Get the underlying SilveryNode */\n getNode(): AgNode | null\n /** Get the current content-relative layout rect */\n getBoxRect(): Rect | null\n /** Get the current screen-relative layout rect */\n getScrollRect(): Rect | null\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * Flexbox container component for terminal UIs.\n *\n * Provides NodeContext to children, enabling useBoxRect/useScrollRect hooks.\n * Supports forwardRef for imperative access and onLayout for layout callbacks.\n *\n * @example\n * ```tsx\n * // Basic vertical layout (default)\n * <Box>\n * <Text>Line 1</Text>\n * <Text>Line 2</Text>\n * </Box>\n *\n * // Horizontal layout with spacing\n * <Box flexDirection=\"row\" gap={2}>\n * <Box width={10}><Text>Left</Text></Box>\n * <Box flexGrow={1}><Text>Center</Text></Box>\n * <Box width={10}><Text>Right</Text></Box>\n * </Box>\n *\n * // With border\n * <Box borderStyle=\"single\" borderColor=\"green\" padding={1}>\n * <Text>Boxed content</Text>\n * </Box>\n *\n * // With ref and onLayout\n * const boxRef = useRef<BoxHandle>(null);\n * <Box\n * ref={boxRef}\n * onLayout={(layout) => console.log('Size:', layout.width, layout.height)}\n * >\n * <Text>Content</Text>\n * </Box>\n * ```\n */\nexport const Box = forwardRef(function Box(\n props: BoxProps,\n ref: ForwardedRef<BoxHandle>,\n): JSX.Element {\n const { children, onLayout, ...restProps } = props\n const nodeRef = useRef<AgNode | null>(null)\n const [node, setNode] = useState<AgNode | null>(null)\n\n // Track the last layout we reported to onLayout to avoid duplicate calls\n const lastReportedLayout = useRef<Rect | null>(null)\n\n // After mount, ref points to the SilveryNode. Update state once to provide\n // the node to children via context. Only runs on mount ([] deps).\n useLayoutEffect(() => {\n if (nodeRef.current) {\n setNode(nodeRef.current)\n }\n }, [])\n\n // Wire up onLayout callback - subscribe via layout signals\n useLayoutEffect(() => {\n if (!onLayout || !node) return\n\n const signals = getLayoutSignals(node)\n const onLayoutRef = { current: onLayout }\n onLayoutRef.current = onLayout\n\n const dispose = signalEffect(() => {\n const layout = signals.boxRect()\n if (!layout) return\n\n // Only call onLayout if layout actually changed\n const last = lastReportedLayout.current\n if (\n !last ||\n last.x !== layout.x ||\n last.y !== layout.y ||\n last.width !== layout.width ||\n last.height !== layout.height\n ) {\n lastReportedLayout.current = layout\n onLayoutRef.current(layout)\n }\n })\n\n return dispose\n }, [node, onLayout])\n\n // Expose imperative methods via ref\n useImperativeHandle(\n ref,\n () => ({\n getNode: () => nodeRef.current,\n getBoxRect: () => nodeRef.current?.boxRect ?? null,\n getScrollRect: () => nodeRef.current?.scrollRect ?? null,\n }),\n [],\n )\n\n // Render silvery-box with ref, wrap children in NodeContext\n // The reconciler creates an SilveryNode, ref gives us access to it\n return (\n <silvery-box ref={nodeRef} {...restProps}>\n <NodeContext.Provider value={node}>{children}</NodeContext.Provider>\n </silvery-box>\n )\n})\n","/**\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(\n totalCount - visibleCount,\n selectedIndex - visibleCount + effectivePadding + 1,\n )\n }\n\n // Clamp to valid range\n return Math.max(0, Math.min(newOffset, totalCount - visibleCount))\n}\n","/**\n * useVirtualizer - Shared headless virtualization engine.\n *\n * Count-based API inspired by TanStack Virtual. Computes the visible range,\n * placeholder sizes, and scroll offsets for any scrollable view.\n *\n * Supports variable item heights via dynamic measurement: after each render,\n * consumers report actual item heights. The virtualizer uses measured heights\n * when available and falls back to `estimateHeight` for unmeasured items.\n * This eliminates the fixed-itemHeight problem where a single constant can't\n * accurately represent variable-height items (e.g., cards with 3-6 row heights).\n *\n * Two components consume this hook:\n * - VirtualView: items mount/unmount based on scroll position (in-tree)\n * - ScrollbackView: items transition through Live → Virtualized → Static (scrollback)\n *\n * The hook is headless — it doesn't render anything. Consumers decide what\n * to do with the visible range.\n */\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\nimport { calcEdgeBasedScrollOffset } from \"@silvery/ag-term/scroll-utils\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface VirtualizerConfig {\n /** Total number of items */\n count: number\n /** Estimated height of each item in rows (fixed number or per-index function).\n * Used as fallback for items that haven't been measured yet. */\n estimateHeight: number | ((index: number) => number)\n /** Viewport height in rows */\n viewportHeight: number\n /** Index to scroll to (declarative). When undefined, scroll state freezes. */\n scrollTo?: number\n /** Padding from edge before scrolling (in items). Default: 1 */\n scrollPadding?: number\n /** Extra items to render beyond viewport. Default: 5 */\n overscan?: number\n /** Maximum items to render at once. Default: 100 */\n maxRendered?: number\n /** Gap between items in rows. Default: 0 */\n gap?: number\n /** Get a stable key for an item by index. Falls back to index if not provided. */\n getItemKey?: (index: number) => string | number\n /** Called when the visible range reaches near the end of the list (infinite scroll). */\n onEndReached?: () => void\n /** How many items from the end to trigger onEndReached. Default: 5 */\n onEndReachedThreshold?: number\n}\n\nexport interface VirtualizerResult {\n /** Range of visible items [startIndex, endIndex) */\n range: { startIndex: number; endIndex: number }\n /** Height before visible range (for leading placeholder) */\n leadingHeight: number\n /** Height after visible range (for trailing placeholder) */\n trailingHeight: number\n /** Number of items hidden before viewport */\n hiddenBefore: number\n /** Number of items hidden after viewport */\n hiddenAfter: number\n /** Current scroll offset (item index of viewport top) */\n scrollOffset: number\n /** Imperatively scroll to an item index */\n scrollToItem: (index: number) => void\n /** Get the key for an item at index */\n getKey: (index: number) => string | number\n /** Report a measured height for an item. Call after layout with actual height.\n * Returns true if the measurement changed (consumers can use this to trigger re-render). */\n measureItem: (key: string | number, height: number) => boolean\n /** Read-only access to the measured heights cache */\n measuredHeights: ReadonlyMap<string | number, number>\n}\n\n// =============================================================================\n// Defaults\n// =============================================================================\n\nconst DEFAULT_SCROLL_PADDING = 1\nconst DEFAULT_OVERSCAN = 5\nconst DEFAULT_MAX_RENDERED = 100\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/** Get item height for a specific index, checking measured cache first.\n *\n * When measurements exist but this specific item hasn't been measured,\n * falls back to the average measured height (if provided) rather than\n * the original estimate. This prevents leading/trailing placeholders\n * from overshooting when estimateHeight diverges from actual heights.\n */\nexport function getHeight(\n index: number,\n estimateHeight: number | ((index: number) => number),\n measuredHeights?: ReadonlyMap<string | number, number>,\n getItemKey?: (index: number) => string | number,\n avgMeasuredHeight?: number,\n): number {\n if (measuredHeights && measuredHeights.size > 0) {\n const key = getItemKey ? getItemKey(index) : index\n const measured = measuredHeights.get(key)\n if (measured !== undefined) return measured\n // Use average measured height for unmeasured items — more accurate\n // than the original estimate which may diverge from actual heights.\n if (avgMeasuredHeight !== undefined) return avgMeasuredHeight\n }\n return typeof estimateHeight === \"function\" ? estimateHeight(index) : estimateHeight\n}\n\n/** Calculate average item height using measurements when available, sampling estimates otherwise. */\nexport function calcAverageHeight(\n count: number,\n estimateHeight: number | ((index: number) => number),\n measuredHeights?: ReadonlyMap<string | number, number>,\n): number {\n if (count === 0) return 1\n\n // If we have measurements, compute average from them (more accurate)\n if (measuredHeights && measuredHeights.size > 0) {\n let total = 0\n for (const h of measuredHeights.values()) {\n total += h\n }\n return total / measuredHeights.size\n }\n\n if (typeof estimateHeight === \"number\") return estimateHeight\n\n const sampleSize = Math.min(count, 10)\n let total = 0\n for (let i = 0; i < sampleSize; i++) {\n total += typeof estimateHeight === \"function\" ? estimateHeight(i) : estimateHeight\n }\n return total / sampleSize\n}\n\n/** Sum heights for a range of items, using measurements when available.\n * Optimized: when no measurements exist, uses multiplication instead of iteration.\n *\n * When measurements exist, unmeasured items in the range use the average\n * measured height as fallback (not the original estimate). This prevents\n * leading/trailing placeholders from overshooting when estimateHeight\n * diverges from actual heights — the root cause of blank rows at the top\n * (Bug 1) and inability to scroll to the bottom (Bug 2). */\nexport function sumHeights(\n startIndex: number,\n endIndex: number,\n estimateHeight: number | ((index: number) => number),\n gap: number,\n measuredHeights?: ReadonlyMap<string | number, number>,\n getItemKey?: (index: number) => string | number,\n): number {\n const itemCount = endIndex - startIndex\n if (itemCount <= 0) return 0\n\n const gapTotal = (itemCount - 1) * gap\n\n // Fast path: no measurements and fixed estimate — use multiplication\n if ((!measuredHeights || measuredHeights.size === 0) && typeof estimateHeight === \"number\") {\n return itemCount * estimateHeight + gapTotal\n }\n\n // Compute average measured height once for use as fallback for unmeasured items.\n // This is more accurate than the original estimate for items outside the render window.\n let avgMeasured: number | undefined\n if (measuredHeights && measuredHeights.size > 0) {\n let measuredTotal = 0\n for (const h of measuredHeights.values()) {\n measuredTotal += h\n }\n avgMeasured = measuredTotal / measuredHeights.size\n }\n\n // Slow path: per-item lookup (checks measurement cache, falls back to avg measured or estimate)\n let total = 0\n for (let i = startIndex; i < endIndex; i++) {\n total += getHeight(i, estimateHeight, measuredHeights, getItemKey, avgMeasured)\n }\n return total + gapTotal\n}\n\n// =============================================================================\n// Hook\n// =============================================================================\n\n/**\n * Headless virtualization engine.\n *\n * Computes which items should be visible given a viewport size and scroll\n * position. The scroll offset is computed synchronously during render\n * (not in useEffect) to avoid one-frame delays.\n *\n * Supports dynamic height measurement: after render, consumers call\n * `measureItem(key, height)` with actual heights. The virtualizer uses\n * measured heights for accurate placeholder sizes and visible count estimation,\n * falling back to `estimateHeight` for unmeasured items.\n *\n * When scrollTo is undefined, scroll state freezes at the last known position.\n * This is critical for multi-column layouts where only one column is active.\n */\nexport function useVirtualizer(config: VirtualizerConfig): VirtualizerResult {\n const {\n count,\n estimateHeight,\n viewportHeight,\n scrollTo,\n scrollPadding = DEFAULT_SCROLL_PADDING,\n overscan = DEFAULT_OVERSCAN,\n maxRendered = DEFAULT_MAX_RENDERED,\n gap = 0,\n getItemKey,\n } = config\n\n // ── Measurement cache ─────────────────────────────────────────────\n // Stores actual rendered heights keyed by item key. Survives re-renders\n // and accumulates as the user scrolls through the list.\n const measuredHeightsRef = useRef<Map<string | number, number>>(new Map())\n // Counter to trigger re-computation when measurements change\n const [measurementVersion, setMeasurementVersion] = useState(0)\n\n const measureItem = useCallback((key: string | number, height: number): boolean => {\n const cache = measuredHeightsRef.current\n const existing = cache.get(key)\n if (existing === height) return false\n cache.set(key, height)\n // Schedule a re-render so placeholder sizes update with new measurements.\n // This is batched by React — multiple measureItem calls in the same\n // layout effect produce a single re-render.\n setMeasurementVersion((v) => v + 1)\n return true\n }, [])\n\n const measuredHeights = measuredHeightsRef.current\n\n // Calculate average item height for estimating visible count.\n // Uses measured heights when available for more accurate estimation.\n const avgHeight = calcAverageHeight(count, estimateHeight, measuredHeights)\n // Use ceil to match rendering behavior: items that partially overflow\n // the viewport are still rendered (clipped by overflow=\"hidden\").\n const estimatedVisibleCount = Math.max(1, Math.ceil(viewportHeight / (avgHeight + gap)))\n\n // Selected index as ref — doesn't trigger re-renders when cursor moves\n // within the viewport.\n const selectedIndexRef = useRef(Math.max(0, Math.min(scrollTo ?? 0, count - 1)))\n\n // Scroll offset — computed synchronously during render.\n //\n // Previously this was useState + useEffect, which caused a one-frame delay:\n // passive effects don't flush within the same doRender() cycle, so the scroll\n // offset update was deferred until the next keypress.\n const scrollOffsetRef = useRef(\n calcEdgeBasedScrollOffset(\n selectedIndexRef.current,\n 0,\n estimatedVisibleCount,\n count,\n scrollPadding,\n ),\n )\n const [, setScrollOffset] = useState(() => scrollOffsetRef.current)\n\n // Synchronous scroll offset computation during render.\n if (scrollTo !== undefined) {\n const clampedIndex = Math.max(0, Math.min(scrollTo, count - 1))\n selectedIndexRef.current = clampedIndex\n const newOffset = calcEdgeBasedScrollOffset(\n clampedIndex,\n scrollOffsetRef.current,\n estimatedVisibleCount,\n count,\n scrollPadding,\n )\n if (newOffset !== scrollOffsetRef.current) {\n scrollOffsetRef.current = newOffset\n }\n }\n const effectiveScrollOffset = scrollOffsetRef.current\n\n // Sync state with ref (triggers re-render for dependents only when changed).\n useEffect(() => {\n setScrollOffset((prev) => (prev === effectiveScrollOffset ? prev : effectiveScrollOffset))\n }, [effectiveScrollOffset])\n\n // Imperative scroll function\n const scrollToItem = useCallback(\n (index: number) => {\n const clampedIndex = Math.max(0, Math.min(index, count - 1))\n selectedIndexRef.current = clampedIndex\n const newOffset = calcEdgeBasedScrollOffset(\n clampedIndex,\n scrollOffsetRef.current,\n estimatedVisibleCount,\n count,\n scrollPadding,\n )\n scrollOffsetRef.current = newOffset\n setScrollOffset(newOffset)\n },\n [count, estimatedVisibleCount, scrollPadding],\n )\n\n // Key resolver\n const getKey = useCallback(\n (index: number): string | number => {\n return getItemKey ? getItemKey(index) : index\n },\n [getItemKey],\n )\n\n // Calculate virtualization window.\n // Depends on measurementVersion to recompute placeholders when measurements arrive.\n const windowCalc = useMemo(() => {\n if (count === 0) {\n return {\n startIndex: 0,\n endIndex: 0,\n leadingHeight: 0,\n trailingHeight: 0,\n }\n }\n\n // For tiny lists (≤ visible + overscan), render everything\n const minWindowSize = estimatedVisibleCount + 2 * overscan\n if (count <= minWindowSize) {\n return {\n startIndex: 0,\n endIndex: count,\n leadingHeight: 0,\n trailingHeight: 0,\n }\n }\n\n // Render window = enough items to fill the viewport + overscan buffer,\n // capped at maxRendered.\n //\n // CRITICAL INVARIANT 1: the window is derived from effectiveScrollOffset\n // (the viewport top), NOT from the cursor. Cursor-centered windows fail\n // at edges — when the cursor is at index 0 with overscan=5, only the\n // lower half of the window renders (5 items), leaving blank viewport\n // rows. The cursor's role is to drive scrollOffset (via\n // calcEdgeBasedScrollOffset); it does not constrain the render window.\n //\n // CRITICAL INVARIANT 2: the window size is derived from HEIGHTS, not\n // from item counts. A count-based window (`estimatedVisibleCount +\n // 2*overscan`) fails when item heights are highly variable — e.g. the\n // first N items are short (3 rows) and the rest are tall (30 rows).\n // avgHeight weighs these evenly, so estimatedVisibleCount undercounts\n // how many of the (short) first items are needed to fill the viewport.\n // Symptom: viewport partially blank, `▼N` indicator at the bottom even\n // though items that would fit are NOT being rendered.\n //\n // The height-aware algorithm: expand `end` forward from `start`, summing\n // measured heights, until accumulated height ≥ viewport + overscanPixels.\n // Always include `estimatedVisibleCount + 2*overscan` items as a minimum\n // (so unmeasured-first-render still has a reasonable window).\n //\n // Window layout:\n // start = scrollOffset - overscan (items above viewport)\n // end = smallest index s.t. sumHeights(start, end) covers the\n // viewport + overscan pixels below it\n const overscanPixels = overscan * avgHeight\n const minItems = estimatedVisibleCount + 2 * overscan\n\n let start = Math.max(0, effectiveScrollOffset - overscan)\n // Target rendered height = viewport (above scrollOffset is already\n // measured by start reduction) + overscan both ends.\n const targetHeight = viewportHeight + 2 * overscanPixels\n\n // Expand `end` using actual heights (or estimates for unmeasured).\n let accumulated = 0\n let end = start\n while (end < count && accumulated < targetHeight) {\n accumulated += getHeight(end, estimateHeight, measuredHeights, getItemKey, avgHeight) + gap\n end++\n }\n // Minimum item count — protects against very small measured heights that\n // would otherwise truncate the window (and guards fresh renders where\n // nothing is measured yet).\n end = Math.min(count, Math.max(end, start + minItems))\n // Apply maxRendered cap last (safety bound).\n end = Math.min(end, start + maxRendered)\n\n // Adjust start if we hit the end — keep enough items to cover the\n // viewport when there are enough items to fill it.\n if (end === count) {\n // Pull `start` back so that start..count covers at least the viewport.\n let startFill = 0\n let newStart = end\n while (newStart > 0 && startFill < targetHeight) {\n newStart--\n startFill += getHeight(newStart, estimateHeight, measuredHeights, getItemKey, avgHeight) + gap\n }\n // Keep minimum count so the window never shrinks below item-count floor.\n start = Math.min(Math.max(0, newStart), Math.max(0, end - minItems))\n }\n\n // Calculate placeholder sizes using measured heights when available.\n // sumHeights checks the measurement cache per-item and falls back to estimates.\n const leadingSize = sumHeights(0, start, estimateHeight, gap, measuredHeights, getItemKey)\n const trailingSize = sumHeights(end, count, estimateHeight, gap, measuredHeights, getItemKey)\n\n return {\n startIndex: start,\n endIndex: end,\n leadingHeight: leadingSize,\n trailingHeight: trailingSize,\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- measuredHeights is a stable ref; measurementVersion triggers recomputation\n }, [\n count,\n effectiveScrollOffset,\n maxRendered,\n overscan,\n estimateHeight,\n avgHeight,\n gap,\n viewportHeight,\n measurementVersion,\n getItemKey,\n ])\n\n // ── onEndReached ─────────────────────────────────────────────────────\n // Fire once when the visible window reaches near the end. Resets when\n // count changes (i.e. new items were loaded).\n const onEndReachedRef = useRef(config.onEndReached)\n onEndReachedRef.current = config.onEndReached\n const firedForCountRef = useRef(-1)\n\n const threshold = config.onEndReachedThreshold ?? DEFAULT_OVERSCAN\n const { endIndex } = windowCalc\n\n useEffect(() => {\n if (!onEndReachedRef.current || count === 0) return\n if (endIndex >= count - threshold && firedForCountRef.current !== count) {\n firedForCountRef.current = count\n onEndReachedRef.current()\n }\n }, [endIndex, count, threshold])\n\n return {\n range: { startIndex: windowCalc.startIndex, endIndex: windowCalc.endIndex },\n leadingHeight: windowCalc.leadingHeight,\n trailingHeight: windowCalc.trailingHeight,\n hiddenBefore: windowCalc.startIndex,\n hiddenAfter: count - windowCalc.endIndex,\n scrollOffset: effectiveScrollOffset,\n scrollToItem,\n getKey,\n measureItem,\n measuredHeights,\n }\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 { ChainAppContext, RuntimeContext } from \"../context\"\nimport { isModifierOnlyEvent, type InputHandler, type Key } from \"@silvery/ag/keys\"\n// RuntimeContext import retained for the exit() side channel — the chain\n// owns input subscriptions but exit still flows through RuntimeContext until\n// lifecycle effects land.\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 // Input subscriptions flow through ChainAppContext — provided by root\n // `createApp()` and by `InputBoundary` for isolated scopes. In static mode\n // (no chain), this hook is a no-op.\n //\n // RuntimeContext is still used for `exit()` (exit is a side effect the\n // chain emits but this hook routes through rt so the existing exit path\n // stays authoritative).\n const chain = useContext(ChainAppContext)\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 the chain input store (press/repeat only —\n // withInputChain filters out release and modifier-only events). No-op if\n // absent (static/render-string mode).\n useEffect(() => {\n if (!isActive) return\n if (!chain) return\n return chain.input.register((input, key) => {\n // Skip modifier-only keys (Cmd, Shift, Ctrl, Alt pressed alone).\n // Handled by useModifierKeys, not useInput consumers.\n if (isModifierOnlyEvent(input, key as Key)) return\n const result = handlerRef.current(input, key as Key)\n if (result === \"exit\") {\n // Route exit through RuntimeContext. The chain also emits an `exit`\n // effect but the runner drains and discards effects in the current\n // wiring; rt.exit() is the canonical path until runEventBatch effect\n // handling lands.\n rt?.exit()\n return \"exit\"\n }\n return undefined\n })\n }, [isActive, chain, rt])\n\n // Release events bypass withInputChain's handler invocation — subscribe\n // via the raw-key observer so onRelease still fires.\n useEffect(() => {\n if (!isActive) return\n if (!chain) return\n return chain.rawKeys.register((input, key) => {\n if (key.eventType !== \"release\") return\n onReleaseRef.current?.(input, key as Key)\n })\n }, [isActive, chain])\n\n // Subscribe to paste events via the chain paste store.\n useEffect(() => {\n if (!isActive) return\n if (!chain) return\n return chain.paste.register((text) => {\n onPasteRef.current?.(text)\n })\n }, [isActive, chain])\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(\n history: HistoryBuffer,\n getLiveItems: () => LiveItemBlock[],\n): 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 =\n state.query.slice(0, state.cursorPosition) +\n action.char +\n 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 =\n 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 * SearchProvider — app-global search with pluggable Searchable registration.\n *\n * Components (e.g., ListView) register as searchable. SearchBar reads search state.\n * Ctrl+F opens search on the focused searchable. Pluggable: any component can\n * register by calling `registerSearchable()` from the context.\n *\n * Usage:\n * ```tsx\n * <SearchProvider>\n * <App />\n * <SearchBar />\n * </SearchProvider>\n * ```\n */\n\nimport React, {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\"\nimport {\n type SearchState,\n type SearchMatch,\n type SearchEffect,\n type SearchAction,\n createSearchState,\n searchUpdate,\n} from \"@silvery/ag-term/search-overlay\"\nimport { useInput } from \"../hooks/useInput\"\nimport type { ReactNode, ReactElement } from \"react\"\n\n// ============================================================================\n// Searchable interface — what components register\n// ============================================================================\n\n/** Minimal interface for a searchable component. */\nexport interface Searchable {\n search(query: string): SearchMatch[]\n reveal(match: SearchMatch): void\n}\n\n// ============================================================================\n// Context types\n// ============================================================================\n\nexport interface SearchContextValue {\n /** Whether the search bar is currently open */\n isActive: boolean\n /** The current search query */\n query: string\n /** All matches found by the current query */\n matches: SearchMatch[]\n /** Index of the currently highlighted match (-1 = none) */\n currentMatch: number\n /** Cursor position within the query string */\n cursorPosition: number\n /** Open the search bar */\n open(): void\n /** Close the search bar and clear results */\n close(): void\n /** Jump to the next match */\n next(): void\n /** Jump to the previous match */\n prev(): void\n /** Type a character into the search query */\n input(char: string): void\n /** Delete the character before the cursor */\n backspace(): void\n /** Move the query cursor left */\n cursorLeft(): void\n /** Move the query cursor right */\n cursorRight(): void\n /** Move the query cursor to the start */\n cursorToStart(): void\n /** Move the query cursor to the end */\n cursorToEnd(): void\n /** Delete the word before the cursor (Ctrl+W) */\n deleteWordBack(): void\n /** Delete everything before the cursor (Ctrl+U) */\n deleteToStart(): void\n /** Register a searchable component. Returns unregister function. */\n registerSearchable(id: string, searchable: Searchable): () => void\n /** Set which searchable is focused (for multi-pane routing). */\n setFocused(id: string | null): void\n}\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst SearchContext = createContext<SearchContextValue | null>(null)\n\n// ============================================================================\n// Provider\n// ============================================================================\n\nexport function SearchProvider({ children }: { children: ReactNode }): ReactElement {\n const [state, setState] = useState<SearchState>(createSearchState)\n const [focusedId, setFocusedId] = useState<string | null>(null)\n const [activeId, setActiveId] = useState<string | null>(null)\n const searchablesRef = useRef(new Map<string, Searchable>())\n\n const getActiveSearchable = useCallback((): Searchable | null => {\n const id = activeId ?? focusedId\n if (!id) {\n // Fall back to the only registered searchable (single-pane apps)\n const entries = searchablesRef.current\n if (entries.size === 1) return entries.values().next().value!\n return null\n }\n return searchablesRef.current.get(id) ?? null\n }, [activeId, focusedId])\n\n const getSearchFn = useCallback(() => {\n const searchable = getActiveSearchable()\n if (!searchable) return undefined\n return (query: string) => searchable.search(query)\n }, [getActiveSearchable])\n\n // ── Deferred effect processing ────────────────────────────────────\n // Effects from searchUpdate (like scrollTo) are collected during setState\n // updaters and processed in a useEffect after React completes the render.\n // This avoids \"Cannot update a component while rendering another\" warnings\n // that would occur if reveal() (which may trigger setState in other\n // components like ListView) was called inside a setState updater.\n const pendingEffectsRef = useRef<{ effects: SearchEffect[]; state: SearchState } | null>(null)\n\n // Process pending effects after state changes complete\n useEffect(() => {\n const pending = pendingEffectsRef.current\n if (!pending) return\n pendingEffectsRef.current = null\n\n const searchable = getActiveSearchable()\n if (!searchable) return\n for (const eff of pending.effects) {\n if (eff.type === \"scrollTo\") {\n const match =\n pending.state.currentMatch >= 0\n ? pending.state.matches[pending.state.currentMatch]\n : undefined\n if (match) {\n searchable.reveal(match)\n }\n }\n }\n })\n\n /** Dispatch a search action via the TEA state machine. */\n const dispatch = useCallback(\n (action: SearchAction) => {\n setState((prev) => {\n const searchFn = getSearchFn()\n const [next, effects] = searchUpdate(action, prev, searchFn)\n // Collect effects for deferred processing in useEffect\n if (effects.length > 0) {\n pendingEffectsRef.current = { effects, state: next }\n }\n return next\n })\n },\n [getSearchFn],\n )\n\n const registerSearchable = useCallback((id: string, searchable: Searchable): (() => void) => {\n searchablesRef.current.set(id, searchable)\n return () => {\n searchablesRef.current.delete(id)\n }\n }, [])\n\n const setFocused = useCallback((id: string | null) => {\n setFocusedId(id)\n }, [])\n\n const open = useCallback(() => {\n // Lock to current focused searchable when opening\n setActiveId(focusedId)\n dispatch({ type: \"open\" })\n }, [focusedId, dispatch])\n\n const close = useCallback(() => {\n setActiveId(null)\n dispatch({ type: \"close\" })\n }, [dispatch])\n\n const next = useCallback(() => {\n dispatch({ type: \"nextMatch\" })\n }, [dispatch])\n\n const prev = useCallback(() => {\n dispatch({ type: \"prevMatch\" })\n }, [dispatch])\n\n const input = useCallback(\n (char: string) => {\n dispatch({ type: \"input\", char })\n },\n [dispatch],\n )\n\n const backspace = useCallback(() => {\n dispatch({ type: \"backspace\" })\n }, [dispatch])\n\n const cursorLeft = useCallback(() => {\n setState((prev) => {\n const [next] = searchUpdate({ type: \"cursorLeft\" }, prev)\n return next\n })\n }, [])\n\n const cursorRight = useCallback(() => {\n setState((prev) => {\n const [next] = searchUpdate({ type: \"cursorRight\" }, prev)\n return next\n })\n }, [])\n\n const cursorToStart = useCallback(() => {\n setState((prev) => {\n const [next] = searchUpdate({ type: \"cursorToStart\" }, prev)\n return next\n })\n }, [])\n\n const cursorToEnd = useCallback(() => {\n setState((prev) => {\n const [next] = searchUpdate({ type: \"cursorToEnd\" }, prev)\n return next\n })\n }, [])\n\n const deleteWordBack = useCallback(() => {\n dispatch({ type: \"deleteWordBack\" })\n }, [dispatch])\n\n const deleteToStart = useCallback(() => {\n dispatch({ type: \"deleteToStart\" })\n }, [dispatch])\n\n const value = useMemo<SearchContextValue>(\n () => ({\n isActive: state.active,\n query: state.query,\n matches: state.matches,\n currentMatch: state.currentMatch,\n cursorPosition: state.cursorPosition,\n open,\n close,\n next,\n prev,\n input,\n backspace,\n cursorLeft,\n cursorRight,\n cursorToStart,\n cursorToEnd,\n deleteWordBack,\n deleteToStart,\n registerSearchable,\n setFocused,\n }),\n [\n state,\n open,\n close,\n next,\n prev,\n input,\n backspace,\n cursorLeft,\n cursorRight,\n cursorToStart,\n cursorToEnd,\n deleteWordBack,\n deleteToStart,\n registerSearchable,\n setFocused,\n ],\n )\n\n return React.createElement(\n SearchContext.Provider,\n { value },\n React.createElement(SearchBindings, { ctx: value }),\n children,\n )\n}\n\n// ============================================================================\n// Input Bindings\n// ============================================================================\n\nfunction SearchBindings({ ctx }: { ctx: SearchContextValue }) {\n useInput(\n (input, key) => {\n if (!ctx.isActive) {\n if (key.ctrl && input === \"f\") {\n ctx.open()\n return\n }\n return\n }\n if (key.escape) {\n ctx.close()\n return\n }\n if (key.return && !key.shift) {\n ctx.next()\n return\n }\n if (key.return && key.shift) {\n ctx.prev()\n return\n }\n if (key.backspace && key.meta) {\n // Alt+Backspace — delete word backward\n ctx.deleteWordBack()\n return\n }\n if (key.backspace) {\n ctx.backspace()\n return\n }\n // Ctrl+W — delete word backward\n if (key.ctrl && input === \"w\") {\n ctx.deleteWordBack()\n return\n }\n // Ctrl+U — delete to start of line\n if (key.ctrl && input === \"u\") {\n ctx.deleteToStart()\n return\n }\n // Ctrl+A — cursor to start\n if (key.ctrl && input === \"a\") {\n ctx.cursorToStart()\n return\n }\n // Ctrl+E — cursor to end\n if (key.ctrl && input === \"e\") {\n ctx.cursorToEnd()\n return\n }\n // Home — cursor to start\n if (key.home) {\n ctx.cursorToStart()\n return\n }\n // End — cursor to end\n if (key.end) {\n ctx.cursorToEnd()\n return\n }\n if (key.leftArrow) {\n ctx.cursorLeft()\n return\n }\n if (key.rightArrow) {\n ctx.cursorRight()\n return\n }\n if (input && !key.ctrl && !key.meta) {\n ctx.input(input)\n return\n }\n },\n { isActive: true },\n )\n return null\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useSearch(): SearchContextValue {\n const ctx = useContext(SearchContext)\n if (!ctx) {\n throw new Error(\"useSearch must be used within a SearchProvider\")\n }\n return ctx\n}\n\n/** Optional variant — returns null when no SearchProvider is in the tree. */\nexport function useSearchOptional(): SearchContextValue | null {\n return useContext(SearchContext)\n}\n","/**\n * ListView - Unified unmounted list component.\n *\n * Merges VirtualView's core (useVirtualizer, viewport rendering, placeholders)\n * with VirtualList's navigation (keyboard, mouse wheel, cursor state) into\n * a single component.\n *\n * @example\n * ```tsx\n * // Passive (parent controls scroll)\n * <ListView\n * items={logs}\n * height={20}\n * renderItem={(item, index) => <LogEntry data={item} />}\n * estimateHeight={() => 3}\n * />\n *\n * // Navigable (built-in j/k, arrows, PgUp/PgDn, Home/End, G, mouse wheel)\n * <ListView\n * items={items}\n * height={20}\n * nav\n * renderItem={(item, i, meta) => (\n * <Text>{meta.isCursor ? '> ' : ' '}{item.name}</Text>\n * )}\n * onSelect={(index) => openItem(items[index])}\n * />\n * ```\n */\n\nimport React, {\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\"\nimport { useVirtualizer } from \"../../hooks/useVirtualizer\"\nimport { useInput } from \"../../hooks/useInput\"\nimport { Box } from \"../../components/Box\"\nimport { CacheBackendContext, StdoutContext, TermContext } from \"../../context\"\nimport { renderStringSync } from \"../../render-string\"\nimport { createHistoryBuffer, createHistoryItem } from \"@silvery/ag-term/history-buffer\"\nimport type { HistoryBuffer } from \"@silvery/ag-term/history-buffer\"\nimport { createListDocument } from \"@silvery/ag-term/list-document\"\nimport type { LiveItemBlock } from \"@silvery/ag-term/list-document\"\nimport { createTextSurface } from \"@silvery/ag-term/text-surface\"\nimport type { TextSurface } from \"@silvery/ag-term/text-surface\"\nimport { composeViewport } from \"@silvery/ag-term/viewport-compositor\"\nimport type { ComposedViewport } from \"@silvery/ag-term/viewport-compositor\"\nimport { stripAnsi } from \"@silvery/ag-term/unicode\"\nimport { isLayoutEngineInitialized } from \"@silvery/ag-term/layout-engine\"\nimport { useSearchOptional } from \"../../providers/SearchProvider\"\nimport type { SearchMatch } from \"@silvery/ag-term/search-overlay\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** Metadata passed to renderItem in the third argument */\nexport interface ListItemMeta {\n /** Whether this item is at the cursor position (nav mode only) */\n isCursor: boolean\n}\n\n/** Cache configuration for ListView */\nexport interface ListViewCacheConfig<T> {\n /**\n * Cache backend mode:\n * - \"none\": No caching\n * - \"virtual\": In-memory HistoryBuffer ring buffer (fullscreen/panes)\n * - \"terminal\": Write to stdout as native scrollback via promoteScrollback (inline mode)\n * - \"auto\": Auto-select based on CacheBackendContext (set by runtime from rendering mode)\n */\n mode: \"none\" | \"virtual\" | \"terminal\" | \"auto\"\n /** Predicate for items that can be cached (removed from React tree). */\n isCacheable?: (item: T, index: number) => boolean\n /** Maximum rows in cache buffer. Default: 10_000 */\n capacity?: number\n}\n\n/** Search configuration for ListView */\nexport interface ListViewSearchConfig<T> {\n /** Extract searchable text from an item. When omitted, auto-extracts from rendered content. */\n getText?: (item: T) => string\n}\n\nexport interface ListViewProps<T> {\n /** Array of items to render */\n items: T[]\n\n /** Height of the viewport in rows */\n height: number\n\n /** Estimated height of each item in rows (fixed or per-index function). Default: 1 */\n estimateHeight?: number | ((index: number) => number)\n\n /** Render function for each item. Third arg provides cursor metadata. */\n renderItem: (item: T, index: number, meta: ListItemMeta) => React.ReactNode\n\n /** Index to scroll to (declarative). When undefined, scroll state freezes. Ignored when nav=true. */\n scrollTo?: number\n\n /** Extra items to render beyond viewport for smooth scrolling. Default: 5 */\n overscan?: number\n\n /** Maximum items to render at once. Default: 100 */\n maxRendered?: number\n\n /** Padding from edge before scrolling (in items). Default: 2 */\n scrollPadding?: number\n\n /** Show overflow indicators (▲N/▼N). Default: false */\n overflowIndicator?: boolean\n\n /** Key extractor (defaults to index) */\n getKey?: (item: T, index: number) => string | number\n\n /** Width of the viewport (optional, uses parent width if not specified) */\n width?: number\n\n /** Gap between items in rows. Default: 0 */\n gap?: number\n\n /** Render separator between items (alternative to gap) */\n renderSeparator?: () => React.ReactNode\n\n /** Mouse wheel handler for scrolling (passive mode only, nav handles its own) */\n onWheel?: (event: { deltaY: number }) => void\n\n /** Called when the visible range reaches near the end of the list (infinite scroll). */\n onEndReached?: () => void\n /** How many items from the end to trigger onEndReached. Default: 5 */\n onEndReachedThreshold?: number\n\n /**\n * Called when mouse enters an item. Defaults to moving the cursor to that\n * item (hover-to-focus). Provide a custom handler to override this behavior.\n * Only active when nav=true.\n */\n onItemHover?: (index: number) => void\n /**\n * Called when an item is clicked. Defaults to moving the cursor + firing\n * onSelect (click-to-confirm). Provide a custom handler to override.\n * Only active when nav=true.\n */\n onItemClick?: (index: number) => void\n\n /** Content rendered after all items inside the scroll container (e.g., hidden count indicator) */\n listFooter?: React.ReactNode\n\n /** Predicate for items already unmounted (cached, pushed to scrollback).\n * Only a contiguous prefix of matching items is removed from the list. */\n unmounted?: (item: T, index: number) => boolean\n\n // ── Navigable mode ──────────────────────────────────────────────\n\n /** Enable built-in keyboard (j/k, arrows, PgUp/PgDn, Home/End, G) and mouse wheel */\n nav?: boolean\n\n /** Currently focused cursor key (controlled). Managed internally when not provided. */\n cursorKey?: number\n\n /** Called when cursor position changes (keyboard or mouse wheel navigation) */\n onCursor?: (index: number) => void\n\n /** Called when Enter is pressed on the cursor item */\n onSelect?: (index: number) => void\n\n /** Whether this ListView is active for keyboard input. Default: true.\n * Set to false when another pane has focus in multi-pane layouts. */\n active?: boolean\n\n // ── History / Surface ─────────────────────────────────────────\n\n /** Surface identity for search/selection routing */\n surfaceId?: string\n\n /** Search configuration (true = auto-extract text from rendered content) */\n search?: boolean | ListViewSearchConfig<T>\n\n /** Cache configuration (true = auto-cache items above viewport) */\n cache?: boolean | ListViewCacheConfig<T>\n}\n\nexport interface ListViewHandle {\n /** Imperatively scroll to a specific item index */\n scrollToItem(index: number): void\n /** Get the history buffer (if history.mode === \"virtual\") */\n getHistoryBuffer(): HistoryBuffer | null\n /** Get the composed viewport (if history.mode === \"virtual\") */\n getComposedViewport(): ComposedViewport | null\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst DEFAULT_ESTIMATE_HEIGHT = 1\nconst DEFAULT_OVERSCAN = 5\nconst DEFAULT_MAX_RENDERED = 100\nconst DEFAULT_SCROLL_PADDING = 2\n/** Items to move per mouse wheel tick */\nconst WHEEL_STEP = 3\n\n// =============================================================================\n// Measurement\n// =============================================================================\n\n/**\n * Wrapper that measures its child's rendered height after layout.\n * Reports the measurement to the virtualizer via measureItem callback.\n * Uses Box's onLayout prop to get the actual rendered height.\n * Does NOT add any layout of its own — the child determines the height.\n */\nfunction MeasuredItem({\n itemKey,\n measureItem,\n children,\n}: {\n itemKey: string | number\n measureItem: (key: string | number, height: number) => boolean\n children: React.ReactNode\n}): React.ReactElement {\n // Use a ref to always have the latest key/measureItem without re-subscribing.\n // This avoids creating a new onLayout callback on every render.\n const keyRef = useRef(itemKey)\n keyRef.current = itemKey\n const measureRef = useRef(measureItem)\n measureRef.current = measureItem\n\n const handleLayout = useCallback((rect: { height: number }) => {\n if (rect.height > 0) {\n measureRef.current(keyRef.current, rect.height)\n }\n }, [])\n\n // Render children inside a transparent wrapper Box with onLayout.\n // The Box inherits the parent's column layout direction and doesn't\n // constrain the child — it simply provides a node for measurement.\n return (\n <Box flexDirection=\"column\" flexShrink={0} onLayout={handleLayout}>\n {children}\n </Box>\n )\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n// oxlint-disable-next-line complexity/complexity -- React component — JSX ternaries inflate score\nfunction ListViewInner<T>(\n {\n items,\n height,\n estimateHeight = DEFAULT_ESTIMATE_HEIGHT,\n renderItem,\n scrollTo: scrollToProp,\n overscan = DEFAULT_OVERSCAN,\n maxRendered = DEFAULT_MAX_RENDERED,\n scrollPadding = DEFAULT_SCROLL_PADDING,\n overflowIndicator,\n getKey,\n width,\n gap = 0,\n renderSeparator,\n onWheel: onWheelProp,\n onEndReached,\n onEndReachedThreshold,\n listFooter,\n unmounted,\n nav,\n cursorKey: cursorKeyProp,\n onCursor,\n onSelect,\n onItemHover,\n onItemClick,\n active,\n surfaceId,\n search: searchProp,\n cache: cacheProp,\n }: ListViewProps<T>,\n ref: React.ForwardedRef<ListViewHandle>,\n): React.ReactElement {\n // ── Term context for cache capture width ─────────────────────────\n const term = useContext(TermContext)\n\n // ── Cache backend context (set by runtime from rendering mode) ───\n const cacheBackendFromContext = useContext(CacheBackendContext)\n const stdoutCtx = useContext(StdoutContext)\n\n // ── Nav mode: controlled/uncontrolled cursor ─────────\n const isControlled = cursorKeyProp !== undefined\n const [uncontrolledCursor, setUncontrolledCursor] = useState(0)\n const activeCursor = nav ? (isControlled ? cursorKeyProp! : uncontrolledCursor) : -1\n\n const moveTo = useCallback(\n (next: number) => {\n const clamped = Math.max(0, Math.min(next, items.length - 1))\n if (!isControlled) setUncontrolledCursor(clamped)\n onCursor?.(clamped)\n },\n [isControlled, items.length, onCursor],\n )\n\n // Keyboard input for nav mode\n useInput(\n (input, key) => {\n if (!nav) return\n const cur = activeCursor\n if (input === \"j\" || key.downArrow) moveTo(cur + 1)\n else if (input === \"k\" || key.upArrow) moveTo(cur - 1)\n else if (input === \"G\" || key.end) moveTo(items.length - 1)\n else if (key.home) moveTo(0)\n else if (key.pageDown || (input === \"d\" && key.ctrl)) moveTo(cur + Math.floor(height / 2))\n else if (key.pageUp || (input === \"u\" && key.ctrl)) moveTo(cur - Math.floor(height / 2))\n else if (key.return) onSelect?.(cur)\n },\n { isActive: nav && active !== false },\n )\n\n // In nav mode, scrollTo is derived from cursor\n const scrollTo = nav ? activeCursor : scrollToProp\n\n // ── Resolve cache config ─────────────────────────────────────────\n // When cache=true, use \"auto\" mode which reads CacheBackendContext.\n // When cache={ mode: \"auto\" }, also reads context. Otherwise use the explicit mode.\n const cacheConfig =\n typeof cacheProp === \"object\" ? cacheProp : cacheProp ? { mode: \"auto\" as const } : undefined\n const rawCacheMode = cacheConfig?.mode ?? \"none\"\n // Resolve \"auto\" → context-driven backend selection\n const cacheMode =\n rawCacheMode === \"auto\"\n ? cacheBackendFromContext === \"terminal\"\n ? \"terminal\"\n : \"virtual\"\n : rawCacheMode\n const cacheBufferRef = useRef<HistoryBuffer | null>(null)\n if (cacheMode === \"virtual\" && !cacheBufferRef.current) {\n cacheBufferRef.current = createHistoryBuffer(cacheConfig?.capacity ?? 10_000)\n }\n const cacheBuffer = cacheBufferRef.current\n\n // ── Resolve search config ─────────────────────────────────────────\n const searchConfig = typeof searchProp === \"object\" ? searchProp : searchProp ? {} : undefined\n const getText = searchConfig?.getText ?? (searchConfig ? (item: T) => String(item) : undefined)\n\n // Compute cached prefix from isCacheable\n let cachedCount = 0\n if ((cacheMode === \"virtual\" || cacheMode === \"terminal\") && cacheConfig?.isCacheable) {\n for (let i = 0; i < items.length; i++) {\n if (!cacheConfig.isCacheable(items[i]!, i)) break\n cachedCount++\n }\n }\n\n // Push newly cached items to buffer or terminal scrollback\n const prevCachedRef = useRef(0)\n if (\n cachedCount > prevCachedRef.current &&\n (cacheMode === \"virtual\" || cacheMode === \"terminal\")\n ) {\n const captureWidth = width ?? term?.cols ?? 80\n const canCapture = isLayoutEngineInitialized()\n for (let i = prevCachedRef.current; i < cachedCount; i++) {\n const item = items[i]!\n const key = getKey?.(item, i) ?? i\n let ansi: string\n if (canCapture) {\n // Render the item's element through the pipeline to get real ANSI\n // (borders, padding, colors — everything the user saw)\n try {\n const element = renderItem(item, i, { isCursor: false })\n ansi = renderStringSync(element as React.ReactElement, {\n width: captureWidth,\n plain: false,\n trimTrailingWhitespace: true,\n trimEmptyLines: false,\n })\n } catch {\n // Fallback to plain text if render fails\n ansi = getText?.(item) ?? String(item)\n }\n } else {\n // Layout engine not ready — fallback to plain text\n ansi = getText?.(item) ?? String(item)\n }\n\n if (cacheMode === \"terminal\") {\n // Terminal mode: write to stdout as native scrollback via promoteScrollback.\n // The terminal IS the buffer — no need to store in HistoryBuffer.\n const lineCount = ansi.split(\"\\n\").length\n stdoutCtx?.promoteScrollback?.(`${ansi}\\x1b[K\\r\\n`, lineCount)\n } else if (cacheBuffer) {\n // Virtual mode: store in HistoryBuffer ring buffer\n cacheBuffer.push(createHistoryItem(key, ansi, captureWidth))\n }\n }\n prevCachedRef.current = cachedCount\n }\n\n // Merge cached prefix with external unmounted prop.\n // Only unmount cached items when the cache backend can display them:\n // - \"terminal\": items promoted to real terminal scrollback (inline mode)\n // - \"virtual\": items stored in HistoryBuffer for virtual scrollback viewer\n // - \"retain\": items cached but kept in the render tree (plain fullscreen\n // without virtual scrollback — unmounting would make items invisible)\n const shouldUnmountCached = cacheBackendFromContext !== \"retain\" && cachedCount > 0\n const effectiveUnmounted = useMemo(() => {\n if (!shouldUnmountCached) return unmounted\n if (!unmounted) {\n return (_item: T, index: number) => index < cachedCount\n }\n return (item: T, index: number) => {\n if (index < cachedCount) return true\n return unmounted(item, index)\n }\n }, [shouldUnmountCached, cachedCount, unmounted])\n\n // ── Virtual prefix computation ──────────────────────────────────────\n let unmountedCount = 0\n if (effectiveUnmounted) {\n for (let i = 0; i < items.length; i++) {\n if (!effectiveUnmounted(items[i]!, i)) break\n unmountedCount++\n }\n }\n\n // Slice items to exclude virtual prefix\n const activeItems = unmountedCount > 0 ? items.slice(unmountedCount) : items\n\n // Adjust scrollTo to account for virtual items\n const adjustedScrollTo =\n scrollTo !== undefined ? Math.max(0, scrollTo - unmountedCount) : undefined\n\n // ── Adapt estimateHeight for unmounted offset ──────────────────\n const adjustedEstimateHeight = useMemo(() => {\n if (typeof estimateHeight === \"number\") return estimateHeight\n if (unmountedCount > 0) {\n return (index: number) => estimateHeight(index + unmountedCount)\n }\n return estimateHeight\n }, [estimateHeight, unmountedCount])\n\n // ── useVirtualizer ──────────────────────────────────────────────\n const wrappedGetKey = useMemo(() => {\n if (!getKey) return undefined\n if (unmountedCount === 0) return (index: number) => getKey(activeItems[index]!, index)\n return (index: number) => getKey(activeItems[index]!, index + unmountedCount)\n }, [getKey, activeItems, unmountedCount])\n\n const {\n range,\n leadingHeight,\n trailingHeight,\n scrollOffset,\n scrollToItem,\n measureItem,\n measuredHeights,\n } = useVirtualizer({\n count: activeItems.length,\n estimateHeight: adjustedEstimateHeight,\n viewportHeight: height,\n scrollTo: adjustedScrollTo,\n scrollPadding,\n overscan,\n maxRendered,\n gap,\n getItemKey: wrappedGetKey,\n onEndReached,\n onEndReachedThreshold,\n })\n\n // ── Surface / search registration ────────────────────────────────\n const textSurfaceRef = useRef<TextSurface | null>(null)\n const composedViewportRef = useRef<ComposedViewport | null>(null)\n const searchCtx = useSearchOptional()\n\n // Stable refs for the effect closure to avoid re-running on every items change\n const itemsRef = useRef(items)\n itemsRef.current = items\n const unmountedCountRef = useRef(unmountedCount)\n unmountedCountRef.current = unmountedCount\n const getTextRef = useRef(getText)\n if (getText) getTextRef.current = getText\n const getKeyRef = useRef(getKey)\n getKeyRef.current = getKey\n\n // Stable ref to scrollToItem so the search reveal closure doesn't go stale\n const scrollToItemRef = useRef(scrollToItem)\n scrollToItemRef.current = scrollToItem\n\n // Create and maintain ListDocument + TextSurface when surfaceId is set\n useEffect(() => {\n if (!surfaceId || cacheMode !== \"virtual\" || !cacheBuffer) return\n\n const getLiveItems = (): LiveItemBlock[] => {\n const currentItems = itemsRef.current\n const currentUnmountedCount = unmountedCountRef.current\n const currentGetText = getTextRef.current\n const currentGetKey = getKeyRef.current\n const live: LiveItemBlock[] = []\n for (let i = currentUnmountedCount; i < currentItems.length; i++) {\n const item = currentItems[i]!\n const text = currentGetText?.(item) ?? String(item)\n const rows = text.split(\"\\n\")\n const plainTextRows = rows.map((r) => stripAnsi(r))\n live.push({\n key: currentGetKey?.(item, i) ?? i,\n itemIndex: i,\n rows,\n plainTextRows,\n })\n }\n return live\n }\n\n const document = createListDocument(cacheBuffer, getLiveItems)\n const surface = createTextSurface({\n id: surfaceId,\n document,\n viewportToDocument: (viewportRow: number) => viewportRow + cacheBuffer.totalRows,\n onReveal: () => {\n // Could be extended later for scroll-to-row\n },\n capabilities: {\n paneSafe: true,\n searchableHistory: true,\n selectableHistory: true,\n overlayHistory: true,\n },\n })\n\n textSurfaceRef.current = surface\n\n return () => {\n textSurfaceRef.current = null\n }\n }, [surfaceId, cacheMode, cacheBuffer])\n\n // ── Search registration ──────────────────────────────────────────\n // Register as Searchable in SearchProvider when `search` prop is set.\n // The search function scans all items' text for query matches.\n // The reveal function scrolls the matching item into view.\n useEffect(() => {\n if (!searchConfig || !searchCtx || !surfaceId) return\n\n const searchable = {\n search(query: string): SearchMatch[] {\n if (!query) return []\n const currentItems = itemsRef.current\n const currentGetText = getTextRef.current\n const lowerQuery = query.toLowerCase()\n const matches: SearchMatch[] = []\n let row = 0\n for (let i = 0; i < currentItems.length; i++) {\n const item = currentItems[i]!\n const text = currentGetText?.(item) ?? String(item)\n const lines = text.split(\"\\n\")\n for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {\n const line = lines[lineIdx]!\n const lowerLine = line.toLowerCase()\n let col = 0\n while (col < lowerLine.length) {\n const found = lowerLine.indexOf(lowerQuery, col)\n if (found === -1) break\n matches.push({ row: row + lineIdx, startCol: found, endCol: found + query.length })\n col = found + 1\n }\n }\n row += lines.length\n }\n return matches\n },\n reveal(match: SearchMatch): void {\n // Find which item contains this row\n const currentItems = itemsRef.current\n const currentGetText = getTextRef.current\n let row = 0\n for (let i = 0; i < currentItems.length; i++) {\n const item = currentItems[i]!\n const text = currentGetText?.(item) ?? String(item)\n const lineCount = text.split(\"\\n\").length\n if (match.row < row + lineCount) {\n // This item contains the match — scroll to it\n scrollToItemRef.current(Math.max(0, i - unmountedCountRef.current))\n return\n }\n row += lineCount\n }\n },\n }\n\n return searchCtx.registerSearchable(surfaceId, searchable)\n }, [searchConfig, searchCtx, surfaceId])\n\n // Compute composed viewport when history is active\n if (cacheMode === \"virtual\" && cacheBuffer) {\n composedViewportRef.current = composeViewport({\n history: cacheBuffer,\n viewportHeight: height,\n scrollOffset: 0, // At tail by default; scroll offset would come from external state\n })\n }\n\n // ── Ref ───────────────────────────────────────────────────────────\n // Wrap scrollToItem to accept original indices (before virtual adjustment)\n useImperativeHandle(\n ref,\n () => ({\n scrollToItem(index: number) {\n scrollToItem(Math.max(0, index - unmountedCount))\n },\n getHistoryBuffer(): HistoryBuffer | null {\n return cacheBufferRef.current\n },\n getComposedViewport(): ComposedViewport | null {\n return composedViewportRef.current\n },\n }),\n [scrollToItem, unmountedCount],\n )\n\n // ── Mouse wheel handler ─────────────────────────────────────────\n const onWheel = useMemo(() => {\n if (nav && active !== false) {\n return (e: { deltaY: number }) => {\n const delta = e.deltaY > 0 ? WHEEL_STEP : -WHEEL_STEP\n moveTo(activeCursor + delta)\n }\n }\n return onWheelProp\n }, [nav, active, activeCursor, moveTo, onWheelProp])\n\n // ── Empty state ─────────────────────────────────────────────────\n if (activeItems.length === 0) {\n return (\n <Box flexDirection=\"column\" height={height} width={width}>\n {/* Empty - nothing to render */}\n </Box>\n )\n }\n\n // ── Render ──────────────────────────────────────────────────────\n const { startIndex, endIndex } = range\n const visibleItems = activeItems.slice(startIndex, endIndex)\n\n // Calculate scrollTo index for silvery Box overflow=\"scroll\"\n const hasTopPlaceholder = leadingHeight > 0\n const currentScrollTarget =\n adjustedScrollTo !== undefined\n ? Math.max(0, Math.min(adjustedScrollTo, activeItems.length - 1))\n : scrollOffset\n const selectedIndexInSlice = currentScrollTarget - startIndex\n const isSelectedInSlice = selectedIndexInSlice >= 0 && selectedIndexInSlice < visibleItems.length\n const scrollToIndex = hasTopPlaceholder ? selectedIndexInSlice + 1 : selectedIndexInSlice\n const boxScrollTo = isSelectedInSlice ? Math.max(0, scrollToIndex) : undefined\n\n return (\n <Box\n flexDirection=\"column\"\n height={height}\n width={width}\n overflow=\"scroll\"\n scrollTo={boxScrollTo}\n overflowIndicator={overflowIndicator}\n onWheel={onWheel}\n >\n {/* Leading placeholder for virtual height */}\n {leadingHeight > 0 && <Box height={leadingHeight} flexShrink={0} />}\n\n {/* Render visible items with height measurement */}\n {visibleItems.map((item, i) => {\n const originalIndex = startIndex + i + unmountedCount\n const key = getKey ? getKey(item, originalIndex) : startIndex + i\n const isLast = i === visibleItems.length - 1\n const meta: ListItemMeta = { isCursor: originalIndex === activeCursor }\n // Use wrappedGetKey (index within activeItems) for measurement cache\n const measureKey = wrappedGetKey ? wrappedGetKey(startIndex + i) : startIndex + i\n\n // In nav mode, wrap each item with hover/click handlers so that\n // hovering moves the keyboard cursor and clicking confirms the selection.\n // renderItem is responsible for its own mouse handlers when\n // onItemHover/onItemClick are provided — the wrapper only adds nav\n // defaults when renderItem doesn't handle it itself (passive mode).\n const rendered = renderItem(item, originalIndex, meta)\n const itemNode =\n nav && active !== false && (onItemHover !== undefined || onItemClick !== undefined) ? (\n <Box\n onMouseEnter={\n onItemHover ? () => onItemHover(originalIndex) : () => moveTo(originalIndex)\n }\n onClick={\n onItemClick\n ? () => onItemClick(originalIndex)\n : () => {\n moveTo(originalIndex)\n onSelect?.(originalIndex)\n }\n }\n >\n {rendered}\n </Box>\n ) : (\n rendered\n )\n\n return (\n <React.Fragment key={key}>\n <MeasuredItem itemKey={measureKey} measureItem={measureItem}>\n {itemNode}\n </MeasuredItem>\n {!isLast && renderSeparator && renderSeparator()}\n {!isLast && gap > 0 && !renderSeparator && <Box height={gap} flexShrink={0} />}\n </React.Fragment>\n )\n })}\n\n {/* Footer content (e.g., filter hidden count) */}\n {listFooter}\n\n {/* Trailing placeholder for virtual height */}\n {trailingHeight > 0 && <Box height={trailingHeight} flexShrink={0} />}\n </Box>\n )\n}\n\n// Export with forwardRef - use type assertion for generic component\nexport const ListView = forwardRef(ListViewInner) as <T>(\n props: ListViewProps<T> & { ref?: React.ForwardedRef<ListViewHandle> },\n) => React.ReactElement\n","/**\n * useVirtualization Hook\n *\n * Items-based virtualization API for VirtualList and HorizontalVirtualList.\n * Thin adapter over the headless useVirtualizer engine.\n *\n * Key behaviors:\n * - When scrollTo is defined: actively track and scroll to that index\n * - When scrollTo is undefined: freeze scroll state (critical for multi-column layouts)\n */\nimport { useMemo } from \"react\"\nimport { useVirtualizer } from \"./useVirtualizer\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface VirtualizationConfig<T> {\n /** Array of items to virtualize */\n items: T[]\n\n /** Size of the viewport (height for vertical, width for horizontal) */\n viewportSize: number\n\n /** Size of each item (fixed number or function for variable sizes) */\n itemSize: number | ((item: T, index: number) => number)\n\n /** Index to keep visible (scrolls if off-screen) */\n scrollTo?: number\n\n /** Padding from edge before scrolling (in items) */\n scrollPadding?: number\n\n /** Extra items to render beyond viewport for smooth scrolling */\n overscan?: number\n\n /** Maximum items to render at once */\n maxRendered?: number\n\n /** Gap between items (for calculating visible count with variable sizes) */\n gap?: number\n}\n\nexport interface VirtualizationResult {\n /** First item index to render */\n startIndex: number\n\n /** Last item index to render (exclusive) */\n endIndex: number\n\n /** Current selected index (for scroll position calculation) */\n currentSelectedIndex: number\n\n /** Current scroll offset */\n scrollOffset: number\n\n /** Placeholder size before rendered items (for virtual scrolling) */\n leadingPlaceholderSize: number\n\n /** Placeholder size after rendered items */\n trailingPlaceholderSize: number\n\n /** Number of items hidden before viewport */\n hiddenBefore: number\n\n /** Number of items hidden after viewport */\n hiddenAfter: number\n\n /** Imperative scroll function */\n scrollToItem: (index: number) => void\n}\n\n// =============================================================================\n// Hook\n// =============================================================================\n\n/**\n * useVirtualization - items-based adapter over useVirtualizer.\n *\n * Accepts an items array with per-item sizing. Internally delegates to the\n * count-based useVirtualizer engine and maps the result back to the legacy API.\n */\nexport function useVirtualization<T>(config: VirtualizationConfig<T>): VirtualizationResult {\n const { items, viewportSize, itemSize, scrollTo, scrollPadding, overscan, maxRendered, gap } =\n config\n\n // Convert items-based itemSize to index-based estimateHeight.\n // Memoize the adapter function to avoid recreating on every render\n // when the items reference or itemSize function changes.\n const estimateHeight = useMemo(() => {\n if (typeof itemSize === \"number\") return itemSize\n return (index: number) => itemSize(items[index]!, index)\n }, [items, itemSize])\n\n const result = useVirtualizer({\n count: items.length,\n estimateHeight,\n viewportHeight: viewportSize,\n scrollTo,\n scrollPadding,\n overscan,\n maxRendered,\n gap,\n })\n\n // Compute currentSelectedIndex (not returned by useVirtualizer)\n const currentSelectedIndex =\n scrollTo !== undefined ? Math.max(0, Math.min(scrollTo, items.length - 1)) : result.scrollOffset\n\n return {\n startIndex: result.range.startIndex,\n endIndex: result.range.endIndex,\n currentSelectedIndex,\n scrollOffset: result.scrollOffset,\n leadingPlaceholderSize: result.leadingHeight,\n trailingPlaceholderSize: result.trailingHeight,\n hiddenBefore: result.hiddenBefore,\n hiddenAfter: result.hiddenAfter,\n scrollToItem: result.scrollToItem,\n }\n}\n","/**\n * ThemeContext — delivers a Theme to the component tree.\n *\n * Wrap your app (or a subtree) in `<ThemeProvider theme={…}>` to make\n * `$token` color props resolve against that theme. Components call\n * `useTheme()` to read the current theme.\n *\n * @example\n * ```tsx\n * import { ThemeProvider, defaultDarkTheme } from '@silvery/ag-react'\n *\n * <ThemeProvider theme={defaultDarkTheme}>\n * <App />\n * </ThemeProvider>\n * ```\n */\n\nimport { createContext, useContext } from \"react\"\nimport type { Theme, ActiveScheme } from \"@silvery/ansi\"\nimport { defaultDarkTheme } from \"@silvery/theme/schemes\"\n\n// ============================================================================\n// Context\n// ============================================================================\n\n/** @internal Exported for ThemeProvider and Text component — not public API. */\nexport const ThemeContext = createContext<Theme>(defaultDarkTheme)\n\n/**\n * Context that carries scheme detection metadata (name, source, confidence).\n *\n * Separate from ThemeContext because scheme metadata is orthogonal to the\n * theme token bag. Populated by `<ThemeProvider scheme={...}>` when the\n * caller passes detection provenance (e.g. from `runThemed`). Null when\n * no scheme metadata was injected.\n *\n * @internal Exported for ThemeProvider.\n */\nexport const ActiveSchemeContext = createContext<ActiveScheme | null>(null)\n\n// ============================================================================\n// Hook\n// ============================================================================\n\n/**\n * Read the current theme from context.\n *\n * Returns `defaultDarkTheme` when no `ThemeProvider` is present.\n */\nexport function useTheme(): Theme {\n return useContext(ThemeContext)\n}\n","/**\n * Silvery Text Component\n *\n * The primitive for rendering text content in Silvery. Text supports styling\n * (colors, bold, italic, etc.) and text wrapping/truncation modes.\n *\n * Text renders to an 'silvery-text' host element that the reconciler converts\n * to an SilveryNode containing the text content.\n *\n * Supports forwardRef for imperative access to the underlying node.\n */\n\nimport { type ForwardedRef, type JSX, type ReactNode, forwardRef, useContext } from \"react\"\nimport type { AgNode, TextProps as TextPropsType } from \"@silvery/ag/types\"\nimport type { KnownVariant, Theme } from \"@silvery/ansi\"\nimport { KNOWN_VARIANTS } from \"@silvery/ansi\"\nimport { ThemeContext } from \"../ThemeContext\"\n\n// ============================================================================\n// Runtime variant warning — fires once per (theme, variantName) pair.\n// Warns the developer that a variant lookup returned undefined (typo, etc.).\n// Does NOT throw — silent no-op rendering is still the correct behavior.\n// Only fires in development/test (process.env.NODE_ENV !== \"production\").\n// ============================================================================\n\n/** Per-theme set of variant names that have already triggered a warning. */\nconst _warnedVariants = new WeakMap<Theme, Set<string>>()\n\nfunction warnOnce(theme: Theme, variant: string): void {\n if (process.env.NODE_ENV === \"production\") return\n let warned = _warnedVariants.get(theme)\n if (warned === undefined) {\n warned = new Set<string>()\n _warnedVariants.set(theme, warned)\n }\n if (warned.has(variant)) return\n warned.add(variant)\n const known = KNOWN_VARIANTS.join(\", \")\n // eslint-disable-next-line no-console\n console.warn(\n `[silvery] <Text variant=\"${variant}\"> — unknown variant, defaulting to bare. ` +\n `Known: ${known}. ` +\n `Register custom variants via <ThemeProvider tokens={{ variants: { ${variant}: {...} } }}>.`,\n )\n}\n\n// ============================================================================\n// Props\n// ============================================================================\n\nexport interface TextProps extends TextPropsType {\n /** Text content (string, number, or nested Text elements) */\n children?: ReactNode\n /**\n * Typography variant — pulls defaults from `theme.variants[variant]`.\n * Caller props always win over variant values (variant is the *default*).\n *\n * @example\n * ```tsx\n * <Text variant=\"h1\">Title</Text>\n * // → uses theme.variants.h1 as defaults ({ color: \"$primary\", bold: true })\n *\n * <Text variant=\"h1\" color=\"$success\">Done</Text>\n * // → color=\"$success\" wins; bold still comes from variant\n * ```\n */\n variant?: KnownVariant\n}\n\n/**\n * Methods exposed via ref on Text component.\n */\nexport interface TextHandle {\n /** Get the underlying SilveryNode */\n getNode(): AgNode | null\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * Text rendering component for terminal UIs.\n *\n * Supports forwardRef for imperative access to the underlying node.\n *\n * @example\n * ```tsx\n * // Basic text\n * <Text>Hello, world!</Text>\n *\n * // Colored text\n * <Text color=\"green\">Success!</Text>\n * <Text color=\"#ff6600\">Orange text</Text>\n *\n * // Styled text\n * <Text bold>Important</Text>\n * <Text italic underline>Emphasized</Text>\n *\n * // Combined styles\n * <Text color=\"red\" bold inverse>Alert!</Text>\n *\n * // Nested text with different styles\n * <Text>\n * Normal <Text bold>bold</Text> normal\n * </Text>\n *\n * // Truncation modes\n * <Text wrap=\"truncate\">This long text will be truncated...</Text>\n * <Text wrap=\"truncate-middle\">Long...text</Text>\n *\n * // With ref\n * const textRef = useRef<TextHandle>(null);\n * <Text ref={textRef}>Hello</Text>\n *\n * // With variant — typography presets from theme\n * <Text variant=\"h1\">Page Title</Text>\n * <Text variant=\"body-muted\">Caption text</Text>\n * <Text variant=\"h1\" color=\"$success\">Done</Text> // caller color wins\n * ```\n */\nexport const Text = forwardRef(function Text(\n props: TextProps,\n ref: ForwardedRef<TextHandle>,\n): JSX.Element {\n const { children, variant, ...callerProps } = props\n const theme = useContext(ThemeContext)\n\n // Resolve variant defaults. Variant is the DEFAULT; caller props always win.\n //\n // Merge strategy (three passes):\n // 1. callerProps — base layer: all caller props including undefined values\n // (preserves non-style props like wrap, data-*, etc.)\n // 2. variantDefaults — overwrite only where callerProps was undefined\n // (fills in color/bold/etc when caller didn't specify)\n // 3. definedCallerProps — restore any explicitly provided caller overrides\n // (e.g. color=\"$success\", bold={false} win over variant)\n //\n // Example: `<Text variant=\"h1\">T</Text>` (color not passed → undefined)\n // callerProps = { color: undefined }\n // variantDefaults = { color: \"$primary\", bold: true }\n // definedCallerProps = {} (color=undefined excluded)\n // → { color: \"$primary\", bold: true } ✓\n //\n // Example: `<Text variant=\"h1\" color=\"$success\">T</Text>`\n // callerProps = { color: \"$success\" }\n // variantDefaults = { color: \"$primary\", bold: true }\n // definedCallerProps = { color: \"$success\" }\n // → { color: \"$success\", bold: true } ✓ (caller color wins)\n let styleProps = callerProps\n if (variant != null) {\n const resolved = theme.variants?.[variant]\n if (resolved === undefined) {\n warnOnce(theme, variant)\n }\n const variantDefaults = resolved ?? {}\n const definedCallerProps: Record<string, unknown> = {}\n for (const key of Object.keys(callerProps)) {\n const v = (callerProps as Record<string, unknown>)[key]\n if (v !== undefined) definedCallerProps[key] = v\n }\n styleProps = { ...callerProps, ...variantDefaults, ...definedCallerProps } as typeof callerProps\n }\n\n // For Text, we need to pass the ref through to the host element\n // The reconciler's getPublicInstance will return the SilveryNode\n // We wrap it in a TextHandle for type safety\n return (\n <silvery-text\n ref={(node: AgNode | null) => {\n // Handle both callback refs and RefObjects\n if (typeof ref === \"function\") {\n ref(node ? { getNode: () => node } : null)\n } else if (ref) {\n ref.current = node ? { getNode: () => node } : null\n }\n }}\n {...styleProps}\n >\n {children}\n </silvery-text>\n )\n})\n","/**\n * HorizontalVirtualList Component\n *\n * React-level virtualization for horizontal lists. Only renders items within the\n * visible viewport plus overscan. Items outside the viewport are not rendered —\n * scrolling is achieved by changing which items are in the render window.\n *\n * Uses the shared useVirtualization hook for scroll state management.\n *\n * @example\n * ```tsx\n * import { HorizontalVirtualList } from '@silvery/ag-react';\n *\n * <HorizontalVirtualList\n * items={columns}\n * width={80}\n * itemWidth={20}\n * scrollTo={selectedIndex}\n * renderItem={(column, index) => (\n * <Column key={column.id} column={column} isSelected={index === selected} />\n * )}\n * />\n * ```\n */\nimport React, { forwardRef, useImperativeHandle } from \"react\"\nimport { useVirtualization } from \"../../hooks/useVirtualization\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface HorizontalVirtualListProps<T> {\n /** Array of items to render */\n items: T[]\n\n /** Width of the list viewport in columns */\n width: number\n\n /** Width of each item (fixed number or function for variable widths) */\n itemWidth: number | ((item: T, index: number) => number)\n\n /** Index to keep visible (scrolls if off-screen) */\n scrollTo?: number\n\n /** Extra items to render left/right of viewport for smooth scrolling (default: 1) */\n overscan?: number\n\n /** Maximum items to render at once (default: 20) */\n maxRendered?: number\n\n /** Render function for each item */\n renderItem: (item: T, index: number) => React.ReactNode\n\n /** Show built-in overflow indicators (◀N/▶N) */\n overflowIndicator?: boolean\n\n /** Custom overflow indicator renderer. Replaces built-in indicators when provided. */\n renderOverflowIndicator?: (direction: \"before\" | \"after\", hiddenCount: number) => React.ReactNode\n\n /** Width in chars of each overflow indicator (default: 0). Reserves viewport space for indicators. */\n overflowIndicatorWidth?: number\n\n /** Optional key extractor (defaults to index) */\n getKey?: (item: T, index: number) => string | number\n\n /** Height of the list (optional, uses parent height if not specified) */\n height?: number\n\n /** Gap between items in columns (default: 0) */\n gap?: number\n\n /** Render separator between items (alternative to gap) */\n renderSeparator?: () => React.ReactNode\n}\n\nexport interface HorizontalVirtualListHandle {\n /** Scroll to a specific item index */\n scrollToItem(index: number): void\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst DEFAULT_OVERSCAN = 1\nconst DEFAULT_MAX_RENDERED = 20\n\n/**\n * Padding from edge before scrolling (in items).\n *\n * Horizontal lists use padding=1 since columns are wider and fewer fit on screen.\n * Vertical lists (VirtualList) use padding=2 for more context visibility.\n *\n * @see calcEdgeBasedScrollOffset in scroll-utils.ts for the algorithm\n */\nconst SCROLL_PADDING = 1\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n// /** Calculate average item width for estimating visible count. */\n// function calcAvgItemWidth<T>(items: T[], itemWidth: number | ((item: T, index: number) => number)): number {\n// if (typeof itemWidth === \"number\") return itemWidth\n// if (items.length === 0) return 1\n// const n = Math.min(items.length, 10)\n// let sum = 0\n// for (let i = 0; i < n; i++) sum += itemWidth(items[i], i)\n// return sum / n\n// }\n\n/**\n * Calculate total width of all items including gaps.\n */\nfunction calcTotalItemsWidth<T>(\n items: T[],\n itemWidth: number | ((item: T, index: number) => number),\n gap: number,\n): number {\n if (items.length === 0) return 0\n if (typeof itemWidth === \"number\") return items.length * itemWidth + (items.length - 1) * gap\n let total = 0\n for (let i = 0; i < items.length; i++) {\n total += itemWidth(items[i]!, i) + (i > 0 ? gap : 0)\n }\n return total\n}\n\n/**\n * Count how many items actually fit in the viewport starting from a given index.\n * More accurate than average-based estimation for variable widths.\n */\nfunction calcActualVisibleCount<T>(\n items: T[],\n startFrom: number,\n viewport: number,\n itemWidth: number | ((item: T, index: number) => number),\n gap: number,\n): number {\n // Count items that FULLY fit within the viewport (floor semantics).\n // Overflow indicators use this count for accurate \"hidden items\" reporting.\n // The rendering loop separately limits items to the viewport boundary.\n if (typeof itemWidth === \"number\") {\n return Math.max(1, Math.floor((viewport + gap) / (itemWidth + gap)))\n }\n let usedSize = 0\n let count = 0\n for (let i = startFrom; i < items.length; i++) {\n const size = itemWidth(items[i]!, i)\n const sizeWithGap = size + (count > 0 ? gap : 0)\n if (usedSize + sizeWithGap > viewport) break\n usedSize += sizeWithGap\n count++\n }\n return Math.max(1, count)\n}\n\n/**\n * Calculate the physical right edge position of a target item relative to\n * the viewport, given a scroll offset. Returns the pixel position past the\n * viewport right edge (positive = clipped, zero/negative = fully visible).\n */\nfunction calcItemOverflow<T>(\n items: T[],\n scrollOffset: number,\n targetIndex: number,\n viewport: number,\n itemWidth: number | ((item: T, index: number) => number),\n gap: number,\n): number {\n if (targetIndex < scrollOffset || targetIndex >= items.length) return 0\n // Sum widths from scrollOffset to targetIndex (inclusive)\n let pos = 0\n for (let i = scrollOffset; i <= targetIndex; i++) {\n const w = typeof itemWidth === \"number\" ? itemWidth : itemWidth(items[i]!, i)\n pos += w + (i > scrollOffset ? gap : 0)\n }\n return pos - viewport\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * HorizontalVirtualList - React-level virtualized horizontal list.\n *\n * Only renders items within the visible viewport plus overscan.\n *\n * Scroll state management (via useVirtualization hook):\n * - When scrollTo is defined: actively track and scroll to that index\n * - When scrollTo is undefined: completely freeze scroll state (do nothing)\n *\n * This freeze behavior is critical for multi-column layouts where only one\n * column is \"selected\" at a time. Non-selected columns must not recalculate\n * their scroll position.\n */\nfunction HorizontalVirtualListInner<T>(\n {\n items,\n width,\n itemWidth,\n scrollTo,\n overscan = DEFAULT_OVERSCAN,\n maxRendered = DEFAULT_MAX_RENDERED,\n renderItem,\n overflowIndicator,\n renderOverflowIndicator,\n overflowIndicatorWidth = 0,\n getKey,\n height,\n gap = 0,\n renderSeparator,\n }: HorizontalVirtualListProps<T>,\n ref: React.ForwardedRef<HorizontalVirtualListHandle>,\n): React.ReactElement {\n // Always reserve indicator space when an overflow indicator is configured.\n // This prevents layout shift: without reservation, the first render uses full width,\n // then a second render detects overflow and shrinks the viewport by 2 chars,\n // causing all columns to reflow visibly.\n const totalItemsWidth = calcTotalItemsWidth(items, itemWidth, gap)\n const allItemsFit = totalItemsWidth <= width\n const hasIndicatorRenderer = renderOverflowIndicator != null || overflowIndicator === true\n const indicatorReserved = hasIndicatorRenderer ? overflowIndicatorWidth * 2 : 0\n const effectiveViewport = Math.max(1, width - indicatorReserved)\n\n // Use shared virtualization hook for scroll state management\n const { startIndex, endIndex, scrollOffset, scrollToItem } = useVirtualization({\n items,\n viewportSize: effectiveViewport,\n itemSize: itemWidth,\n scrollTo,\n scrollPadding: SCROLL_PADDING,\n overscan,\n maxRendered,\n gap,\n })\n\n // Expose scrollToItem method via ref for imperative scrolling\n useImperativeHandle(ref, () => ({ scrollToItem }), [scrollToItem])\n\n // Empty state\n if (items.length === 0) {\n return (\n <Box flexDirection=\"row\" width={width} height={height}>\n {/* Empty - nothing to render */}\n </Box>\n )\n }\n\n // When all items fit, override scrollOffset to 0. useVirtualization may compute\n // a non-zero offset due to average-based estimation with variable widths\n // (e.g., collapsed=3 vs expanded=76 averages to 39.5, underestimating visible count).\n let displayScrollOffset = allItemsFit ? 0 : scrollOffset\n\n // Fix partial visibility: useVirtualization reserves space for both overflow\n // indicators (left + right), but at the edges only one shows. Recalculate\n // with the actual indicator overhead to see if items truly don't fit.\n // If they do fit with actual indicators, keep the current offset.\n // If they don't, bump the offset to fully reveal the cursor item.\n if (scrollTo !== undefined && !allItemsFit && scrollTo >= displayScrollOffset) {\n // Determine which indicators would show at the current offset\n const wouldShowLeft = hasIndicatorRenderer && displayScrollOffset > 0\n const prelimVisibleCount = calcActualVisibleCount(\n items,\n displayScrollOffset,\n effectiveViewport,\n itemWidth,\n gap,\n )\n const wouldShowRight =\n hasIndicatorRenderer && items.length - displayScrollOffset - prelimVisibleCount > 0\n // Actual viewport uses only the indicators that will actually render\n const actualIndicatorWidth =\n (wouldShowLeft ? overflowIndicatorWidth : 0) + (wouldShowRight ? overflowIndicatorWidth : 0)\n const actualViewport = Math.max(1, width - actualIndicatorWidth)\n\n const overflow = calcItemOverflow(\n items,\n displayScrollOffset,\n scrollTo,\n actualViewport,\n itemWidth,\n gap,\n )\n if (overflow > 0) {\n // Scroll right by 1 to push the partially clipped item into full view.\n const maxOffset = Math.max(0, items.length - 1)\n displayScrollOffset = Math.min(maxOffset, displayScrollOffset + 1)\n }\n }\n\n // Compute how many items actually fit starting from the display scroll offset.\n // Uses actual item widths rather than averages for accurate overflow detection.\n const visibleCount = calcActualVisibleCount(\n items,\n displayScrollOffset,\n effectiveViewport,\n itemWidth,\n gap,\n )\n\n // Viewport-based item window: render items from displayScrollOffset that fit in the\n // viewport, intersected with useVirtualization's render window (respects maxRendered).\n // Only render items that fully fit — partial items at the edge add no visual value\n // in terminal UI and can cause layout issues when flexShrink=0 items overflow.\n const vpStart = Math.max(startIndex, displayScrollOffset)\n const rawVpEnd = Math.min(endIndex, displayScrollOffset + visibleCount + overscan)\n let vpEnd = vpStart\n let usedWidth = 0\n for (let i = vpStart; i < rawVpEnd; i++) {\n const w = typeof itemWidth === \"number\" ? itemWidth : itemWidth(items[i]!, i)\n const gapWidth = vpEnd > vpStart ? gap : 0\n if (usedWidth > 0 && usedWidth + gapWidth + w > effectiveViewport) break\n usedWidth += w + gapWidth\n vpEnd = i + 1\n }\n const visibleItems = items.slice(vpStart, vpEnd)\n\n // Viewport-based overflow detection\n const overflowBefore = displayScrollOffset\n const overflowAfter = Math.max(0, items.length - displayScrollOffset - visibleCount)\n\n // Only render overflow indicators when there are actually hidden items in that direction.\n // Space is still reserved via indicatorReserved/effectiveViewport to prevent layout shift;\n // when an indicator is not shown, an empty spacer of the same width fills its slot.\n const hasCustomIndicator = renderOverflowIndicator != null\n const showIndicators = hasCustomIndicator || overflowIndicator === true\n const showLeftIndicator = showIndicators && overflowBefore > 0\n const showRightIndicator = showIndicators && overflowAfter > 0\n\n return (\n <Box flexDirection=\"row\" width={width} height={height}>\n {/* Left overflow indicator — outside overflow container to avoid being clipped */}\n {showLeftIndicator &&\n (hasCustomIndicator ? (\n renderOverflowIndicator(\"before\", overflowBefore)\n ) : (\n <Box flexShrink={0}>\n <Text color=\"$fg-on-accent\" backgroundColor=\"$bg-accent\">\n ◀{overflowBefore}\n </Text>\n </Box>\n ))}\n {/* Reserve indicator space when configured but not showing (prevents layout shift) */}\n {showIndicators && !showLeftIndicator && overflowIndicatorWidth > 0 && (\n <Box width={overflowIndicatorWidth} flexShrink={0} />\n )}\n\n {/* Overflow container — clips items that extend beyond the viewport */}\n <Box flexGrow={1} flexDirection=\"row\" overflow=\"hidden\">\n {/* Render visible items — flexShrink={0} prevents flex from shrinking\n overscan items; they render at full size and get clipped by overflow=\"hidden\" */}\n {visibleItems.map((item, i) => {\n const actualIndex = vpStart + i\n const key = getKey ? getKey(item, actualIndex) : actualIndex\n const isLast = i === visibleItems.length - 1\n\n return (\n <React.Fragment key={key}>\n <Box flexShrink={0}>{renderItem(item, actualIndex)}</Box>\n {!isLast && renderSeparator && renderSeparator()}\n {!isLast && gap > 0 && !renderSeparator && <Box width={gap} flexShrink={0} />}\n </React.Fragment>\n )\n })}\n </Box>\n\n {/* Right overflow indicator — outside overflow container to avoid being clipped */}\n {showRightIndicator &&\n (hasCustomIndicator ? (\n renderOverflowIndicator(\"after\", overflowAfter)\n ) : (\n <Box flexShrink={0}>\n <Text color=\"$fg-on-accent\" backgroundColor=\"$bg-accent\">\n {overflowAfter}▶\n </Text>\n </Box>\n ))}\n {/* Reserve indicator space when configured but not showing (prevents layout shift) */}\n {showIndicators && !showRightIndicator && overflowIndicatorWidth > 0 && (\n <Box width={overflowIndicatorWidth} flexShrink={0} />\n )}\n </Box>\n )\n}\n\n// Export with forwardRef - use type assertion for generic component\nexport const HorizontalVirtualList = forwardRef(HorizontalVirtualListInner) as <T>(\n props: HorizontalVirtualListProps<T> & {\n ref?: React.ForwardedRef<HorizontalVirtualListHandle>\n },\n) => React.ReactElement\n","/**\n * SplitView - Recursive binary-tree pane tiling component.\n *\n * Renders a layout tree of split panes using flexbox. Each leaf renders\n * via renderPane(id). Splits divide space according to ratio (0-1,\n * proportion given to first child).\n *\n * Horizontal splits use flexDirection=\"row\", vertical splits use\n * flexDirection=\"column\". Each pane gets a border with optional title\n * and focus highlight.\n */\n\nimport React from \"react\"\nimport type { LayoutNode } from \"@silvery/ag-term/pane-manager\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type { LayoutNode }\n\nexport interface SplitViewProps {\n /** Layout tree describing the split arrangement */\n layout: LayoutNode\n /** Render function for each leaf pane */\n renderPane: (id: string) => React.ReactNode\n /** Optional: ID of the focused pane (for border highlighting) */\n focusedPaneId?: string\n /** Optional: show borders around panes (default: true) */\n showBorders?: boolean\n /** Optional: border style for focused pane */\n focusedBorderColor?: string\n /** Optional: border style for unfocused panes */\n unfocusedBorderColor?: string\n /** Optional: render pane title in border */\n renderPaneTitle?: (id: string) => string\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst MIN_PANE_HEIGHT = 5\nconst DEFAULT_FOCUSED_COLOR = \"green\"\nconst DEFAULT_UNFOCUSED_COLOR = \"gray\"\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * SplitView renders a binary tree of panes.\n *\n * Each leaf renders via `renderPane(id)`. Splits divide space\n * according to `ratio` (0-1, proportion given to first child).\n */\nexport function SplitView(props: SplitViewProps): React.ReactElement {\n const {\n layout,\n renderPane,\n focusedPaneId,\n showBorders = true,\n focusedBorderColor = DEFAULT_FOCUSED_COLOR,\n unfocusedBorderColor = DEFAULT_UNFOCUSED_COLOR,\n renderPaneTitle,\n } = props\n\n return (\n <Box flexGrow={1} flexDirection=\"column\">\n <LayoutNodeView\n node={layout}\n renderPane={renderPane}\n focusedPaneId={focusedPaneId}\n showBorders={showBorders}\n focusedBorderColor={focusedBorderColor}\n unfocusedBorderColor={unfocusedBorderColor}\n renderPaneTitle={renderPaneTitle}\n />\n </Box>\n )\n}\n\n// ============================================================================\n// Internal Components\n// ============================================================================\n\ninterface LayoutNodeViewProps {\n node: LayoutNode\n renderPane: (id: string) => React.ReactNode\n focusedPaneId?: string\n showBorders: boolean\n focusedBorderColor: string\n unfocusedBorderColor: string\n renderPaneTitle?: (id: string) => string\n}\n\nfunction LayoutNodeView(props: LayoutNodeViewProps): React.ReactElement {\n const {\n node,\n renderPane,\n focusedPaneId,\n showBorders,\n focusedBorderColor,\n unfocusedBorderColor,\n renderPaneTitle,\n } = props\n\n if (node.type === \"leaf\") {\n return (\n <LeafPane\n id={node.id}\n renderPane={renderPane}\n isFocused={focusedPaneId === node.id}\n showBorders={showBorders}\n focusedBorderColor={focusedBorderColor}\n unfocusedBorderColor={unfocusedBorderColor}\n title={renderPaneTitle?.(node.id)}\n />\n )\n }\n\n // Split node: render two children with flex proportions\n // For horizontal splits, use percentage widths (flexGrow doesn't constrain\n // bordered content correctly in Flexily). For vertical splits, use flexGrow.\n const isHorizontal = node.direction === \"horizontal\"\n const firstPercent = `${Math.round(node.ratio * 100)}%`\n const secondPercent = `${100 - Math.round(node.ratio * 100)}%`\n const firstFlex = Math.round(node.ratio * 100)\n const secondFlex = 100 - firstFlex\n\n return (\n <Box flexGrow={1} flexDirection={isHorizontal ? \"row\" : \"column\"}>\n <Box\n width={isHorizontal ? firstPercent : undefined}\n flexGrow={isHorizontal ? undefined : firstFlex}\n flexShrink={1}\n minHeight={!isHorizontal ? MIN_PANE_HEIGHT : undefined}\n overflow=\"hidden\"\n >\n <LayoutNodeView\n node={node.first}\n renderPane={renderPane}\n focusedPaneId={focusedPaneId}\n showBorders={showBorders}\n focusedBorderColor={focusedBorderColor}\n unfocusedBorderColor={unfocusedBorderColor}\n renderPaneTitle={renderPaneTitle}\n />\n </Box>\n <Box\n width={isHorizontal ? secondPercent : undefined}\n flexGrow={isHorizontal ? undefined : secondFlex}\n flexShrink={1}\n minHeight={!isHorizontal ? MIN_PANE_HEIGHT : undefined}\n overflow=\"hidden\"\n >\n <LayoutNodeView\n node={node.second}\n renderPane={renderPane}\n focusedPaneId={focusedPaneId}\n showBorders={showBorders}\n focusedBorderColor={focusedBorderColor}\n unfocusedBorderColor={unfocusedBorderColor}\n renderPaneTitle={renderPaneTitle}\n />\n </Box>\n </Box>\n )\n}\n\ninterface LeafPaneProps {\n id: string\n renderPane: (id: string) => React.ReactNode\n isFocused: boolean\n showBorders: boolean\n focusedBorderColor: string\n unfocusedBorderColor: string\n title?: string\n}\n\nfunction LeafPane(props: LeafPaneProps): React.ReactElement {\n const {\n id,\n renderPane,\n isFocused,\n showBorders,\n focusedBorderColor,\n unfocusedBorderColor,\n title,\n } = props\n\n if (!showBorders) {\n return (\n <Box flexGrow={1} testID={`pane-${id}`}>\n {renderPane(id)}\n </Box>\n )\n }\n\n return (\n <Box\n flexGrow={1}\n flexBasis={0}\n overflow=\"hidden\"\n borderStyle=\"single\"\n borderColor={isFocused ? focusedBorderColor : unfocusedBorderColor}\n testID={`pane-${id}`}\n flexDirection=\"column\"\n >\n {title != null && (\n <Box>\n <Text color={isFocused ? focusedBorderColor : unfocusedBorderColor}>{title}</Text>\n </Box>\n )}\n <Box flexGrow={1}>{renderPane(id)}</Box>\n </Box>\n )\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 * Silvery Fill Component\n *\n * Repeats its children's text content to fill the parent's allocated width.\n * Single-pass rendering: generates a long repeated string that gets\n * hard-clipped by the Text element's wrap=\"clip\" mode. No useBoxRect,\n * no layout re-render cycle.\n *\n * Parent Box MUST use `flexBasis={0}` to prevent the long content from\n * inflating the flex item's minimum size.\n *\n * @example\n * ```tsx\n * // Dot leaders — parent needs flexGrow={1} flexBasis={0}\n * <Box>\n * <Text color=\"yellow\">hjkl</Text>\n * <Box flexGrow={1} flexBasis={0}>\n * <Fill><Text dimColor>.</Text></Fill>\n * </Box>\n * <Text>navigate</Text>\n * </Box>\n *\n * // Section header fill\n * <Box>\n * <Text dimColor>── </Text>\n * <Text bold color=\"cyan\">NAVIGATION</Text>\n * <Box flexGrow={1} flexBasis={0}>\n * <Fill><Text dimColor> ─</Text></Fill>\n * </Box>\n * </Box>\n * ```\n */\n\nimport React, {\n type JSX,\n type ReactNode,\n useMemo,\n Children,\n isValidElement,\n cloneElement,\n} from \"react\"\nimport { type Measurer, displayWidth } from \"@silvery/ag-term/unicode\"\n\n/** Maximum fill width in columns. Covers ultrawide terminals (8K at 5px font ≈ 1500 cols). */\nconst MAX_FILL_COLS = 500\n\nexport interface FillProps {\n /** Content to repeat (typically a styled Text element or plain string) */\n children: ReactNode\n /** Optional explicit measurer for width calculation (avoids module-level global) */\n measurer?: Measurer\n}\n\n/**\n * Extract plain text content from React children (strings, numbers, nested elements).\n */\nfunction extractText(children: ReactNode): string {\n let text = \"\"\n Children.forEach(children, (child) => {\n if (typeof child === \"string\") {\n text += child\n } else if (typeof child === \"number\") {\n text += String(child)\n } else if (isValidElement(child) && (child as React.ReactElement<any>).props.children != null) {\n text += extractText((child as React.ReactElement<any>).props.children as ReactNode)\n }\n })\n return text\n}\n\n/**\n * Clone the outermost child element, replace its text content, and set\n * wrap=\"clip\" to hard-truncate the long text without adding an ellipsis.\n * Falls back to plain text fragment for string children.\n */\nfunction renderWithText(children: ReactNode, text: string): JSX.Element {\n const childArray = Children.toArray(children)\n const firstChild = childArray[0]\n\n if (isValidElement(firstChild)) {\n return cloneElement(firstChild as React.ReactElement<any>, { wrap: \"clip\" }, text)\n }\n\n return <>{text}</>\n}\n\n/**\n * Repeats children's text content to fill parent width.\n *\n * Single-pass rendering: generates a long repeated string, truncated by the\n * Text element. No layout feedback needed — no useBoxRect, no re-render.\n *\n * Parent Box **must** use `flexBasis={0}` so the long text doesn't inflate\n * the flex minimum size; it gets truncated to the allocated width.\n */\nexport function Fill({ children, measurer }: FillProps): JSX.Element {\n const repeatedText = useMemo(() => {\n const pattern = extractText(children)\n if (!pattern) return null\n\n // Use explicit measurer when available, fall back to module-level convenience function\n const dw = measurer ? measurer.displayWidth.bind(measurer) : displayWidth\n const unitWidth = dw(pattern)\n if (unitWidth <= 0) return null\n\n const count = Math.ceil(MAX_FILL_COLS / unitWidth)\n return pattern.repeat(count)\n }, [children, measurer])\n\n if (!repeatedText) return <>{children}</>\n return renderWithText(children, repeatedText)\n}\n","/**\n * useModifierKeys — track held modifier key state.\n *\n * Returns which modifier keys (Cmd/Super, Ctrl, Alt, Shift) are currently held.\n * Tracks state from key events via Kitty protocol. The default Kitty flags\n * (DISAMBIGUATE | REPORT_EVENTS | REPORT_ALL_KEYS) enable modifier-only\n * press/release detection — no special configuration needed.\n *\n * The `enabled` option controls subscription — when false, the component\n * never re-renders from modifier changes. Use this to avoid re-rendering\n * many components when only one needs modifier state (e.g., only the\n * hovered link subscribes).\n *\n * @example\n * ```tsx\n * function Link({ href, children }) {\n * const [hovered, setHovered] = useState(false)\n * // Only subscribe when hovered — zero cost for non-hovered links\n * const { super: cmdHeld } = useModifierKeys({ enabled: hovered })\n * const armed = hovered && cmdHeld\n * return <Text underline={armed} onMouseEnter={() => setHovered(true)} ...>\n * }\n * ```\n */\n\nimport { useContext, useMemo, useSyncExternalStore } from \"react\"\nimport { ChainAppContext, type ChainAppContextValue } from \"../context\"\nimport type { Key } from \"@silvery/ag/keys\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ModifierState {\n /** Super/Cmd key (macOS Cmd, requires Kitty protocol) */\n super: boolean\n /** Ctrl key */\n ctrl: boolean\n /** Alt/Option key */\n alt: boolean\n /** Shift key */\n shift: boolean\n}\n\nexport interface UseModifierKeysOptions {\n /**\n * Enable or disable subscription to modifier changes.\n * When false, returns the current snapshot but never triggers re-renders.\n * @default true\n */\n enabled?: boolean\n}\n\n// ============================================================================\n// Global Singleton Store (per runtime)\n// ============================================================================\n\nconst INITIAL: ModifierState = { super: false, ctrl: false, alt: false, shift: false }\n\n/**\n * Last known modifier state (global, updated by any runtime's modifier store).\n * Read this from imperative code (event handlers, TEA update functions)\n * that can't use the useModifierKeys React hook.\n */\nexport let lastModifierState: Readonly<ModifierState> = INITIAL\n\ninterface ModifierStore {\n subscribe: (cb: () => void) => () => void\n getSnapshot: () => ModifierState\n}\n\nconst chainStores = new WeakMap<ChainAppContextValue, ModifierStore>()\n\nfunction buildStore(\n subscribeInput: (handler: (_input: string, key: Key) => void) => () => void,\n subscribeFocus: (handler: (focused: boolean) => void) => () => void,\n): ModifierStore {\n let state = INITIAL\n const listeners = new Set<() => void>()\n\n function notify() {\n for (const cb of listeners) cb()\n }\n\n subscribeInput((_input, key) => {\n const next: ModifierState = {\n super: !!key.super,\n ctrl: !!key.ctrl,\n alt: !!key.meta,\n shift: !!key.shift,\n }\n if (\n next.super !== state.super ||\n next.ctrl !== state.ctrl ||\n next.alt !== state.alt ||\n next.shift !== state.shift\n ) {\n state = next\n lastModifierState = next\n notify()\n }\n })\n\n subscribeFocus((focused) => {\n if (!focused && (state.super || state.ctrl || state.alt || state.shift)) {\n state = INITIAL\n lastModifierState = INITIAL\n notify()\n }\n })\n\n return {\n subscribe: (cb) => {\n listeners.add(cb)\n return () => listeners.delete(cb)\n },\n getSnapshot: () => state,\n }\n}\n\nfunction getOrCreateChainStore(chain: ChainAppContextValue): ModifierStore {\n let store = chainStores.get(chain)\n if (store) return store\n // useModifierKeys needs unfiltered key events — including release and\n // modifier-only — so subscribe to the chain's raw-key observer, not the\n // fallback input store (which filters release/modifier-only).\n store = buildStore(\n (h) => chain.rawKeys.register((input, key) => h(input, key as Key)),\n (h) => chain.focusEvents.register(h),\n )\n chainStores.set(chain, store)\n return store\n}\n\n/**\n * Read the current modifier state imperatively (outside React).\n * For use in event handlers, TEA update functions, etc.\n *\n * Accepts a {@link ChainAppContextValue} — the canonical subscription\n * surface after the TEA Phase 2 wiring. Returns {@link INITIAL} when the\n * chain has no cached modifier store yet.\n */\nexport function getModifierState(chain: ChainAppContextValue | null | undefined): ModifierState {\n if (!chain) return INITIAL\n const store = chainStores.get(chain)\n return store ? store.getSnapshot() : INITIAL\n}\n\n// ============================================================================\n// No-op subscribe (for disabled state)\n// ============================================================================\n\nconst noopUnsubscribe = () => {}\nconst noopSubscribe = (_cb: () => void) => noopUnsubscribe\n\n// ============================================================================\n// Hook\n// ============================================================================\n\n/**\n * Track which modifier keys are currently held.\n *\n * When `enabled` is false, the hook returns the current snapshot but does\n * not subscribe to changes — the component never re-renders from modifier\n * key events. This enables the \"only the hovered element subscribes\" pattern.\n */\n// TODO: When silvery state is signal-based, derive `armed` as a computed signal\n// (hovered && cmdHeld) so the Link component never re-renders — only the\n// underline style subscription triggers a targeted repaint.\nexport function useModifierKeys(opts?: UseModifierKeysOptions): ModifierState {\n const enabled = opts?.enabled ?? true\n const chain = useContext(ChainAppContext)\n\n // Memoize the store instance (stable across renders for same chain app).\n // No chain (static mode, no runtime) → null → returns INITIAL.\n const store = useMemo(() => {\n if (chain) return getOrCreateChainStore(chain)\n return null\n }, [chain])\n\n return useSyncExternalStore(\n enabled && store ? store.subscribe : noopSubscribe,\n store ? store.getSnapshot : () => INITIAL,\n () => INITIAL, // server snapshot\n )\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(\n stdout: NodeJS.WriteStream,\n message: string,\n opts?: { title?: string },\n): 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 =\n | \"default\"\n | \"text\"\n | \"pointer\"\n | \"crosshair\"\n | \"move\"\n | \"not-allowed\"\n | \"wait\"\n | \"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 * useMouseCursor — set the terminal mouse cursor shape.\n *\n * Uses OSC 22 to change the mouse pointer appearance. Resets to default\n * on unmount or when the shape changes to null/undefined.\n *\n * @example\n * ```tsx\n * function DraggableHandle() {\n * const [hovered, setHovered] = useState(false)\n * useMouseCursor(hovered ? \"move\" : null)\n * return <Box onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)}>\n * <Text>Drag me</Text>\n * </Box>\n * }\n * ```\n */\n\nimport { useEffect, useContext } from \"react\"\nimport { TermContext } from \"../context\"\nimport { setMouseCursorShape, resetMouseCursorShape } from \"@silvery/ag-term/output\"\nimport type { MouseCursorShape } from \"@silvery/ag-term/output\"\n\n/**\n * Set the terminal mouse cursor shape. Resets on unmount or shape change.\n * Pass null/undefined to use the default cursor.\n */\nexport function useMouseCursor(shape: MouseCursorShape | null | undefined): void {\n const term = useContext(TermContext)\n\n useEffect(() => {\n if (!shape || !term) return\n term.write(setMouseCursorShape(shape))\n return () => {\n term.write(resetMouseCursorShape())\n }\n }, [shape, term])\n}\n","/**\n * Link Component — OSC 8 Terminal Hyperlinks\n *\n * Renders clickable hyperlinks using the OSC 8 terminal escape sequence.\n * Text inside `<Link>` is underlined by default and wrapped in OSC 8 sequences,\n * making it clickable in supporting terminals (iTerm2, Ghostty, Kitty, etc.).\n *\n * Two arming variants:\n * - `arm-on-cmd-hover` (default): Arms on Cmd+hover (Kitty protocol) or Ctrl+click (SGR)\n * - `arm-on-hover`: Arms on plain hover (no modifier needed)\n *\n * On click (when armed), emits a `\"link:open\"` event via RuntimeContext. The app\n * handles the actual URL opening (keeps silvery runtime-agnostic).\n *\n * @example\n * ```tsx\n * <Link href=\"https://example.com\">Visit Example</Link>\n * <Link href=\"https://example.com\" variant=\"arm-on-hover\">Always Clickable</Link>\n * <Link href=\"km://node/abc123\" onClick={(e) => navigate(e)}>Internal Link</Link>\n * ```\n */\n\nimport { type ReactNode, useCallback, useContext, useState } from \"react\"\nimport type { TextProps } from \"./Text\"\nimport type { SilveryMouseEvent } from \"@silvery/ag-term/mouse-events\"\nimport { Text } from \"./Text\"\nimport { useModifierKeys } from \"../hooks/useModifierKeys\"\nimport { useMouseCursor } from \"../hooks/useMouseCursor\"\nimport { ChainAppContext } from \"../context\"\n\n// ============================================================================\n// OSC 8 Escape Sequences\n// ============================================================================\n\n/** Open an OSC 8 hyperlink. Format: ESC ] 8 ; params ; URI ST */\nfunction osc8Open(href: string): string {\n return `\\x1b]8;;${href}\\x1b\\\\`\n}\n\n/** Close an OSC 8 hyperlink. Format: ESC ] 8 ; ; ST */\nconst OSC8_CLOSE = \"\\x1b]8;;\\x1b\\\\\"\n\n// ============================================================================\n// Props\n// ============================================================================\n\nexport interface LinkProps extends Omit<TextProps, \"children\"> {\n /** The URL to link to (http/https for external, custom schemes for internal) */\n href: string\n /** Link text content */\n children?: ReactNode\n /**\n * How the link arms (shows underline + pointer cursor):\n * - `'arm-on-cmd-hover'` (default): Arms when hovered while holding Cmd/Super\n * - `'arm-on-hover'`: Arms on plain hover (no modifier needed)\n */\n variant?: \"arm-on-cmd-hover\" | \"arm-on-hover\"\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * Renders a terminal hyperlink using OSC 8 escape sequences.\n *\n * The text is wrapped in OSC 8 open/close sequences so supporting terminals\n * render it as a clickable link. The component also registers an onClick\n * handler for mouse-driven interaction within silvery.\n *\n * Supports Cmd+hover armed state: when hovered and Cmd is held, shows underline.\n * Only the hovered link subscribes to modifier keys — zero cost for others.\n */\nexport function Link({\n href,\n children,\n color = \"$link\",\n variant = \"arm-on-cmd-hover\",\n onClick,\n onMouseEnter,\n onMouseLeave,\n ...rest\n}: LinkProps) {\n const [hovered, setHovered] = useState(false)\n const chain = useContext(ChainAppContext)\n // Only subscribe to modifiers when hovered and variant needs it — zero cost for non-hovered links\n const needsModifier = variant === \"arm-on-cmd-hover\"\n const { super: cmdHeld } = useModifierKeys({ enabled: hovered && needsModifier })\n // Determine armed state based on variant\n const armed = hovered && (needsModifier ? cmdHeld : true)\n if (armed) rest.underline = true\n // Pointer cursor when armed\n useMouseCursor(armed ? \"pointer\" : null)\n\n // Click emits \"link:open\" when armed. For arm-on-cmd-hover, e.metaKey is accurate\n // thanks to keyboard modifier tracking merged into mouse events by silvery's runtime.\n const handleClick = useCallback(\n (e: SilveryMouseEvent) => {\n const isArmed = armed || (needsModifier && hovered && e.metaKey)\n if (isArmed) {\n chain?.events.emit(\"link:open\", href)\n e.preventDefault()\n }\n onClick?.(e)\n },\n [armed, needsModifier, hovered, href, onClick, chain],\n )\n\n return (\n <Text\n color={color}\n {...rest}\n onClick={handleClick}\n onMouseEnter={(e: SilveryMouseEvent) => {\n setHovered(true)\n onMouseEnter?.(e)\n }}\n onMouseLeave={(e: SilveryMouseEvent) => {\n setHovered(false)\n onMouseLeave?.(e)\n }}\n >\n {osc8Open(href)}\n {children}\n {OSC8_CLOSE}\n </Text>\n )\n}\n","/**\n * ErrorBoundary Component\n *\n * Catches JavaScript errors in child component tree and displays a fallback UI.\n * Follows React's error boundary pattern using class component lifecycle methods.\n */\n\nimport { Component, type ErrorInfo, type ReactNode } from \"react\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\n\n// ============================================================================\n// Props\n// ============================================================================\n\nexport interface ErrorBoundaryProps {\n /** Child components to render */\n children: ReactNode\n /**\n * Fallback UI to render when an error is caught.\n * Can be a ReactNode or a function that receives error details.\n */\n fallback?: ReactNode | ((error: Error, errorInfo: ErrorInfo) => ReactNode)\n /**\n * Called when an error is caught.\n * Use for logging or error reporting.\n */\n onError?: (error: Error, errorInfo: ErrorInfo) => void\n /**\n * Called when the error is reset (if resetKey or resetKeys change).\n */\n onReset?: () => void\n /**\n * When this key changes, the error boundary resets and tries to render children again.\n * Useful for \"retry\" functionality.\n */\n resetKey?: string | number\n /**\n * When any element in this array changes (shallow comparison), the error\n * boundary resets and re-mounts children. Useful when the recovery depends\n * on multiple values (e.g., route + data version).\n */\n resetKeys?: unknown[]\n}\n\ninterface ErrorBoundaryState {\n hasError: boolean\n error: Error | null\n errorInfo: ErrorInfo | null\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * Error boundary that catches render errors in its children.\n *\n * @example\n * ```tsx\n * // Basic usage with default fallback\n * <ErrorBoundary>\n * <MyComponent />\n * </ErrorBoundary>\n *\n * // Custom fallback\n * <ErrorBoundary fallback={<Text color=\"red\">Something went wrong</Text>}>\n * <MyComponent />\n * </ErrorBoundary>\n *\n * // Function fallback with error details\n * <ErrorBoundary\n * fallback={(error, errorInfo) => (\n * <Box flexDirection=\"column\">\n * <Text color=\"red\">Error: {error.message}</Text>\n * <Text dim>{errorInfo.componentStack}</Text>\n * </Box>\n * )}\n * >\n * <MyComponent />\n * </ErrorBoundary>\n *\n * // With error reporting\n * <ErrorBoundary\n * onError={(error, errorInfo) => {\n * logErrorToService(error, errorInfo);\n * }}\n * >\n * <MyComponent />\n * </ErrorBoundary>\n *\n * // With reset functionality\n * const [resetKey, setResetKey] = useState(0);\n * <ErrorBoundary\n * resetKey={resetKey}\n * fallback={\n * <Box>\n * <Text color=\"red\">Error!</Text>\n * <Text> Press r to retry</Text>\n * </Box>\n * }\n * onReset={() => console.log('Retrying...')}\n * >\n * <MyComponent />\n * </ErrorBoundary>\n * // On 'r' key: setResetKey(k => k + 1)\n * ```\n */\nexport class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\n override state: ErrorBoundaryState = {\n hasError: false,\n error: null,\n errorInfo: null,\n }\n\n static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {\n return { hasError: true, error }\n }\n\n override componentDidCatch(error: Error, errorInfo: ErrorInfo): void {\n this.setState({ errorInfo })\n this.props.onError?.(error, errorInfo)\n }\n\n override componentDidUpdate(prevProps: ErrorBoundaryProps): void {\n if (!this.state.hasError) return\n\n // Reset error state when resetKey changes\n const resetKeyChanged =\n this.props.resetKey !== undefined && prevProps.resetKey !== this.props.resetKey\n\n // Reset error state when any element in resetKeys changes (shallow comparison)\n const resetKeysChanged =\n this.props.resetKeys !== undefined &&\n (this.props.resetKeys.length !== prevProps.resetKeys?.length ||\n this.props.resetKeys.some((key, i) => key !== prevProps.resetKeys?.[i]))\n\n if (resetKeyChanged || resetKeysChanged) {\n this.props.onReset?.()\n this.setState({ hasError: false, error: null, errorInfo: null })\n }\n }\n\n override render(): ReactNode {\n if (this.state.hasError) {\n const { fallback } = this.props\n const { error, errorInfo } = this.state\n\n // If fallback is a function, call it with error details.\n // errorInfo may be null on the first render (getDerivedStateFromError runs\n // before componentDidCatch), so provide a minimal default.\n if (typeof fallback === \"function\" && error) {\n const info = errorInfo ?? ({ componentStack: null } as unknown as ErrorInfo)\n return fallback(error, info)\n }\n\n // If fallback is provided, use it\n if (fallback !== undefined) {\n return fallback as ReactNode\n }\n\n // Default fallback: red bordered box with error message\n return (\n <Box borderStyle=\"single\" borderColor=\"$fg-error\" padding={1} flexDirection=\"column\">\n <Text color=\"$fg-error\" bold>\n Error\n </Text>\n {error && <Text color=\"$fg-error\">{error.message}</Text>}\n {errorInfo?.componentStack && (\n <Text color=\"$fg-muted\" wrap=\"truncate\">\n {errorInfo.componentStack.split(\"\\n\").slice(0, 3).join(\"\\n\")}\n </Text>\n )}\n </Box>\n )\n }\n\n return this.props.children\n }\n}\n","import type { ConsoleEntry, PatchedConsole } from \"@silvery/ag-term/ansi\"\nimport { useEffect, useState } from \"react\"\n\n/**\n * Hook to subscribe to console entries from a PatchedConsole.\n * Re-renders at most every {@link debounceMs} ms to prevent infinite\n * render loops when pipeline debug logging is active (e.g. `-vv`).\n *\n * @example\n * ```tsx\n * import { useConsole, Box, Text } from '@silvery/ag-react'\n * import { patchConsole } from '@silvery/chalk'\n *\n * function ConsoleViewer({ patched }: { patched: PatchedConsole }) {\n * const entries = useConsole(patched)\n * return (\n * <Box flexDirection=\"column\">\n * {entries.map((entry, i) => (\n * <Text key={i}>{entry.args.join(' ')}</Text>\n * ))}\n * </Box>\n * )\n * }\n * ```\n */\nexport function useConsole(patched: PatchedConsole, debounceMs = 200): readonly ConsoleEntry[] {\n const [entries, setEntries] = useState<readonly ConsoleEntry[]>(patched.getSnapshot)\n\n useEffect(() => {\n let timer: ReturnType<typeof setTimeout> | null = null\n const unsub = patched.subscribe(() => {\n if (timer) return\n timer = setTimeout(() => {\n timer = null\n setEntries(patched.getSnapshot())\n }, debounceMs)\n })\n // Pick up entries that arrived before subscribe\n setEntries(patched.getSnapshot())\n return () => {\n unsub()\n if (timer) clearTimeout(timer)\n }\n }, [patched, debounceMs])\n\n return entries\n}\n","import type { ConsoleEntry, PatchedConsole } from \"@silvery/ag-term/ansi\"\nimport type { ReactElement, ReactNode } from \"react\"\nimport { useConsole } from \"../../hooks/useConsole\"\nimport { Text } from \"../../components/Text\"\nimport { ListView } from \"./ListView\"\n\nexport interface ConsoleProps {\n /** The patched console to render entries from */\n console: PatchedConsole\n\n /** Optional render function for custom entry rendering */\n children?: (entry: ConsoleEntry, index: number) => ReactNode\n\n /** Viewport height in rows. Default: 20 */\n height?: number\n\n /** Enable caching of entries scrolled out of view. Default: true */\n cache?: boolean\n\n /** Enable search (registers with SearchProvider). Default: true */\n search?: boolean\n\n /** Surface identity for search/selection routing */\n surfaceId?: string\n}\n\n/**\n * Format console entry args into a string.\n * Joins args with spaces, handling objects via JSON.stringify.\n */\nfunction formatArgs(args: unknown[]): string {\n return args\n .map((arg) => {\n if (typeof arg === \"string\") return arg\n if (typeof arg === \"number\" || typeof arg === \"boolean\") {\n return String(arg)\n }\n if (arg === null) return \"null\"\n if (arg === undefined) return \"undefined\"\n try {\n return JSON.stringify(arg)\n } catch {\n return String(arg)\n }\n })\n .join(\" \")\n}\n\n/**\n * Renders captured console output from a PatchedConsole.\n *\n * Thin composition over ListView — gets caching, search, and virtualization\n * for free. Follows output by default (scrollTo = last item).\n *\n * @example Default rendering\n * ```tsx\n * import { Console } from '@silvery/ag-react'\n * import { patchConsole } from '@silvery/chalk'\n *\n * using patched = patchConsole(console)\n * <Console console={patched} height={20} />\n * ```\n *\n * @example Custom rendering\n * ```tsx\n * <Console console={patched} height={20}>\n * {(entry, i) => (\n * <Text key={i} color={entry.stream === 'stderr' ? 'yellow' : 'green'}>\n * [{entry.method}] {entry.args.join(' ')}\n * </Text>\n * )}\n * </Console>\n * ```\n */\nexport function Console({\n console: patched,\n children,\n height = 20,\n cache = true,\n search = true,\n surfaceId,\n}: ConsoleProps): ReactElement {\n const entries = useConsole(patched)\n\n return (\n <ListView<ConsoleEntry>\n items={entries as ConsoleEntry[]}\n height={height}\n scrollTo={entries.length - 1}\n cache={cache}\n search={search ? { getText: (entry) => formatArgs(entry.args) } : false}\n surfaceId={surfaceId}\n renderItem={(entry, index) =>\n children ? (\n children(entry, index)\n ) : (\n <Text key={index} color={entry.stream === \"stderr\" ? \"red\" : undefined}>\n {formatArgs(entry.args)}\n </Text>\n )\n }\n />\n )\n}\n","/**\n * Screen - Fullscreen root component.\n *\n * Claims the full terminal dimensions for flexbox layout. This is the\n * declarative equivalent of the implicit fullscreen mode from run()/createApp().\n *\n * @example\n * ```tsx\n * <Screen>\n * <Sidebar />\n * <MainContent />\n * <StatusBar />\n * </Screen>\n * ```\n *\n * @example\n * ```tsx\n * // Fullscreen + scrollable region (log viewer, dashboard)\n * <Screen>\n * <Sidebar />\n * <VirtualView items={logs} renderItem={...} />\n * <StatusBar />\n * </Screen>\n * ```\n */\n\nimport { useState, useEffect, type ReactNode, type ReactElement } from \"react\"\nimport { Box } from \"../../components/Box\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ScreenProps {\n /** Children to render in the fullscreen area */\n children: ReactNode\n /** Flex direction for layout. Default: \"column\" (screens are typically vertical) */\n flexDirection?: \"row\" | \"column\" | \"row-reverse\" | \"column-reverse\"\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction getTermDims(): { width: number; height: number } {\n return {\n width: process.stdout.columns ?? 80,\n height: process.stdout.rows ?? 24,\n }\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Fullscreen root component.\n *\n * Provides a Box that fills the entire terminal. Tracks terminal resize\n * events to stay in sync with the actual terminal dimensions.\n */\nexport function Screen({ children, flexDirection = \"column\" }: ScreenProps): ReactElement {\n const [dims, setDims] = useState(getTermDims)\n\n useEffect(() => {\n const onResize = () => setDims(getTermDims())\n process.stdout.on(\"resize\", onResize)\n return () => {\n process.stdout.off(\"resize\", onResize)\n }\n }, [])\n\n return (\n <Box width={dims.width} height={dims.height} flexDirection={flexDirection}>\n {children}\n </Box>\n )\n}\n","/**\n * useAgNode — access the current component's AgNode and its reactive signals.\n *\n * Returns the AgNode and its rect signals (boxRect, scrollRect, screenRect).\n * Signals are alien-signals writable functions — call with no args to read.\n * Use inside an `effect()` from `@silvery/signals` for reactive subscriptions.\n *\n * Returns null if called outside a silvery component tree.\n */\n\nimport { useContext } from \"react\"\nimport { NodeContext } from \"../context\"\nimport { getLayoutSignals, type LayoutSignals } from \"@silvery/ag/layout-signals\"\nimport type { AgNode } from \"@silvery/ag/types\"\n\nexport interface AgNodeHandle {\n /** The underlying AgNode */\n readonly node: AgNode\n /** Reactive layout signals — rects + textContent + focused */\n readonly signals: LayoutSignals\n}\n\nexport function useAgNode(): AgNodeHandle | null {\n const node = useContext(NodeContext)\n if (!node) return null\n const signals = getLayoutSignals(node)\n return { node, signals }\n}\n","/**\n * useSignal — bridge alien-signals to React re-renders.\n *\n * Reads a signal value and subscribes to changes. When the signal\n * updates, the component re-renders with the new value.\n *\n * This is the Layer 2 bridge in the reactive stack:\n * Layer 0: alien-signals (signal, computed, effect)\n * Layer 1: getLayoutSignals (framework-agnostic)\n * Layer 2: useSignal (React bridge) ← this hook\n * Layer 3: useBoxRect, useScreenRect (semantic convenience)\n *\n * @example\n * ```tsx\n * import { useSignal } from \"silvery\"\n * import { useAgNode } from \"silvery\"\n *\n * function MyComponent() {\n * const ag = useAgNode()\n * const rect = useSignal(ag?.signals.boxRect ?? null)\n * if (rect) console.log(`${rect.width}x${rect.height}`)\n * }\n * ```\n */\n\nimport { useReducer, useLayoutEffect, useRef } from \"react\"\nimport { effect as signalEffect } from \"@silvery/signals\"\n\ntype ReadableSignal<T> = () => T\n\n/**\n * Read a signal's value and re-render when it changes.\n *\n * @param sig A signal or computed function, or null to skip.\n * @returns The current value of the signal, or undefined if null.\n */\nexport function useSignal<T>(sig: ReadableSignal<T> | null): T | undefined {\n const [, forceUpdate] = useReducer((x: number) => x + 1, 0)\n const sigRef = useRef(sig)\n sigRef.current = sig\n\n useLayoutEffect(() => {\n if (!sigRef.current) return\n\n const dispose = signalEffect(() => {\n // Read the signal to establish the reactive dependency\n sigRef.current?.()\n // Force React re-render\n forceUpdate()\n })\n\n return dispose\n }, [sig])\n\n if (!sig) return undefined\n return sig()\n}\n","/**\n * useBoxMetrics — Ink-compatible box metrics hook.\n *\n * Returns `{ width, height, left, top, hasMeasured }` for the nearest Box,\n * with parent-relative positioning (matching Ink 7.0 semantics).\n *\n * Two usage modes:\n * - **Without ref** (silvery idiom): reads from NodeContext — works for any\n * component rendered inside a `<Box>`.\n * - **With ref** (Ink idiom): reads from a BoxHandle ref attached to a `<Box>`.\n *\n * @example Context-based (silvery idiom)\n * ```tsx\n * function Inner() {\n * const { width, height, hasMeasured } = useBoxMetrics()\n * if (!hasMeasured) return <Text>Measuring...</Text>\n * return <Text>Size: {width}x{height}</Text>\n * }\n * ```\n *\n * @example Ref-based (Ink idiom)\n * ```tsx\n * function Outer() {\n * const ref = useRef<BoxHandle>(null)\n * const { width, height } = useBoxMetrics(ref)\n * return <Box ref={ref}><Text>{width}x{height}</Text></Box>\n * }\n * ```\n *\n * Bead: km-silvery.boxmetrics-parity\n */\n\nimport { useContext, useLayoutEffect, useReducer, type RefObject } from \"react\"\nimport { effect as signalEffect } from \"@silvery/signals\"\nimport { NodeContext } from \"../context\"\nimport { getLayoutSignals } from \"@silvery/ag/layout-signals\"\nimport type { AgNode } from \"@silvery/ag/types\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BoxMetrics {\n readonly width: number\n readonly height: number\n readonly left: number\n readonly top: number\n readonly hasMeasured: boolean\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst EMPTY_METRICS: BoxMetrics = {\n width: 0,\n height: 0,\n left: 0,\n top: 0,\n hasMeasured: false,\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Extract the AgNode from a ref that may point to a BoxHandle or an AgNode.\n * Box's forwardRef exposes a BoxHandle via useImperativeHandle with getNode().\n */\nfunction resolveNode(refValue: unknown): AgNode | null {\n if (!refValue) return null\n const obj = refValue as Record<string, unknown>\n // BoxHandle from silvery's Box component\n if (typeof obj.getNode === \"function\") {\n return (obj.getNode as () => AgNode | null)()\n }\n // Direct AgNode (has boxRect property)\n if (obj.boxRect !== undefined) {\n return refValue as AgNode\n }\n return null\n}\n\n/**\n * Compute parent-relative BoxMetrics from a node's boxRect.\n * Position is relative to the parent's boxRect origin, matching\n * Ink's getComputedLayout semantics.\n */\nfunction computeMetrics(node: AgNode): BoxMetrics {\n const rect = node.boxRect\n if (!rect) return EMPTY_METRICS\n\n // Parent-relative position (matches Ink/Yoga semantics: offset from parent content area)\n const parent = node.parent\n const parentRect = parent?.boxRect\n const parentProps = parent?.props as import(\"@silvery/ag/types\").BoxProps | undefined\n const padLeft = parentProps?.paddingLeft ?? parentProps?.paddingX ?? parentProps?.padding ?? 0\n const padTop = parentProps?.paddingTop ?? parentProps?.paddingY ?? parentProps?.padding ?? 0\n const borderLeft = parentProps?.borderStyle ? 1 : 0\n const borderTop = parentProps?.borderStyle ? 1 : 0\n const contentX = parentRect ? parentRect.x + padLeft + borderLeft : 0\n const contentY = parentRect ? parentRect.y + padTop + borderTop : 0\n return {\n width: rect.width,\n height: rect.height,\n left: parentRect ? rect.x - contentX : rect.x,\n top: parentRect ? rect.y - contentY : rect.y,\n hasMeasured: true,\n }\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/**\n * Returns box metrics for the nearest Box ancestor (context-based) or a\n * specific Box via ref (Ink-compatible).\n *\n * Subscribes to layout changes via the boxRect signal from layout-signals.\n * On first render before layout completes, returns zeros with hasMeasured=false.\n *\n * @param ref - Optional ref to a Box (BoxHandle). When omitted, reads from NodeContext.\n */\nexport function useBoxMetrics(ref?: RefObject<unknown>): BoxMetrics {\n const contextNode = useContext(NodeContext)\n const [, forceUpdate] = useReducer((x: number) => x + 1, 0)\n\n // Resolve the target node: ref-based or context-based\n const node = ref ? resolveNode(ref.current) : contextNode\n\n useLayoutEffect(() => {\n if (!node) return\n\n const signals = getLayoutSignals(node)\n const dispose = signalEffect(() => {\n signals.boxRect() // read to establish dependency\n forceUpdate()\n })\n return dispose\n }, [node])\n\n if (!node) return EMPTY_METRICS\n return computeMetrics(node)\n}\n","/**\n * useAnimation — Ink-compatible animation frame counter (Phase 1).\n *\n * Shared-scheduler architecture: all components using useAnimation at the same\n * interval share ONE setInterval timer. Callbacks are registered on mount and\n * unregistered on unmount. When the last callback for an interval is removed,\n * the timer is cleared.\n *\n * Matches Ink 7.0's `useAnimation(options?)` API with silvery extensions\n * (pause/resume).\n *\n * @example Spinner\n * ```tsx\n * function Spinner() {\n * const { frame } = useAnimation({ interval: 80 })\n * const chars = ['\\u280b', '\\u2819', '\\u2839', '\\u2838', '\\u283c', '\\u2834', '\\u2826', '\\u2827', '\\u2807', '\\u280f']\n * return <Text>{chars[frame % chars.length]}</Text>\n * }\n * ```\n *\n * @example Conditional animation\n * ```tsx\n * function Progress({ active }: { active: boolean }) {\n * const { frame, time } = useAnimation({ interval: 100, isActive: active })\n * return <Text>Frame {frame}, elapsed {time}ms</Text>\n * }\n * ```\n *\n * Bead: km-silvery.animation\n */\n\nimport { useEffect, useReducer, useRef } from \"react\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface UseAnimationOptions {\n /** Tick interval in milliseconds. Default: 100. */\n interval?: number\n /** Whether the animation is active. Default: true. */\n isActive?: boolean\n}\n\nexport interface UseAnimationResult {\n /** Number of ticks since start (or last reset). */\n frame: number\n /** Milliseconds elapsed since start (or last reset). */\n time: number\n /** Milliseconds since the previous tick. */\n delta: number\n /** Reset frame counter and time to zero. */\n reset: () => void\n /** Pause the animation (component stays registered but stops receiving ticks). */\n pause: () => void\n /** Resume a paused animation. */\n resume: () => void\n}\n\n// ============================================================================\n// Shared Scheduler (module-level singleton)\n// ============================================================================\n\ninterface TimerEntry {\n /** The setInterval handle. */\n handle: ReturnType<typeof setInterval>\n /** All registered callbacks for this interval. */\n callbacks: Set<() => void>\n}\n\n/**\n * Module-level shared scheduler. Groups callbacks by interval so that\n * multiple components using the same interval share ONE timer.\n *\n * The `_scheduler` export is for test introspection only.\n */\nfunction createScheduler() {\n const timers = new Map<number, TimerEntry>()\n\n function register(interval: number, callback: () => void): void {\n let entry = timers.get(interval)\n if (!entry) {\n const handle = setInterval(() => {\n // Snapshot callbacks to avoid mutation during iteration\n const cbs = timers.get(interval)?.callbacks\n if (cbs) {\n for (const cb of cbs) {\n cb()\n }\n }\n }, interval)\n entry = { handle, callbacks: new Set() }\n timers.set(interval, entry)\n }\n entry.callbacks.add(callback)\n }\n\n function unregister(interval: number, callback: () => void): void {\n const entry = timers.get(interval)\n if (!entry) return\n entry.callbacks.delete(callback)\n if (entry.callbacks.size === 0) {\n clearInterval(entry.handle)\n timers.delete(interval)\n }\n }\n\n return {\n register,\n unregister,\n /** Number of active setInterval timers (test introspection). */\n get activeTimerCount() {\n return timers.size\n },\n /** Total registered callbacks across all timers (test introspection). */\n get totalCallbackCount() {\n let count = 0\n for (const entry of timers.values()) {\n count += entry.callbacks.size\n }\n return count\n },\n }\n}\n\n/** Shared scheduler singleton — exported for test introspection only. */\nexport const _scheduler = createScheduler()\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/**\n * Ink-compatible animation hook. Returns a frame counter that increments\n * at the specified interval, with elapsed time tracking.\n *\n * All components using the same interval share a single setInterval timer.\n * When the last component unmounts, the timer is cleared.\n *\n * @param options - Animation options (all optional).\n */\nexport function useAnimation(options: UseAnimationOptions = {}): UseAnimationResult {\n const { interval = 100, isActive = true } = options\n\n // Use a reducer to force re-renders on tick\n const [, forceUpdate] = useReducer((x: number) => x + 1, 0)\n\n // Mutable state stored in refs to avoid stale closure issues\n const stateRef = useRef({\n frame: 0,\n time: 0,\n delta: 0,\n startTime: 0,\n lastTickTime: 0,\n paused: false,\n })\n\n // Initialize start time on first render\n if (stateRef.current.startTime === 0) {\n stateRef.current.startTime = Date.now()\n stateRef.current.lastTickTime = stateRef.current.startTime\n }\n\n // Register/unregister with shared scheduler\n useEffect(() => {\n if (!isActive) return\n\n const state = stateRef.current\n\n const tick = () => {\n if (state.paused) return\n const now = Date.now()\n state.frame++\n state.delta = now - state.lastTickTime\n state.time = now - state.startTime\n state.lastTickTime = now\n forceUpdate()\n }\n\n _scheduler.register(interval, tick)\n return () => {\n _scheduler.unregister(interval, tick)\n }\n }, [interval, isActive])\n\n const reset = () => {\n const now = Date.now()\n stateRef.current.frame = 0\n stateRef.current.time = 0\n stateRef.current.delta = 0\n stateRef.current.startTime = now\n stateRef.current.lastTickTime = now\n forceUpdate()\n }\n\n const pause = () => {\n stateRef.current.paused = true\n }\n\n const resume = () => {\n stateRef.current.paused = false\n stateRef.current.lastTickTime = Date.now()\n }\n\n return {\n frame: stateRef.current.frame,\n time: stateRef.current.time,\n delta: stateRef.current.delta,\n reset,\n pause,\n resume,\n }\n}\n","/**\n * useRuntime — access the typed runtime event bus.\n *\n * Returns RuntimeContextValue<E> in interactive mode, or null in static mode.\n * Use this for components that need to work in both modes.\n *\n * Generic parameter E extends BaseRuntimeEvents — type-safe access to\n * custom events defined by the app's runtime.\n *\n * @example\n * ```tsx\n * // Base usage (input + paste events)\n * function StatusBar() {\n * const rt = useRuntime()\n * if (!rt) return <Text>Static mode</Text>\n * return <Text>Interactive mode</Text>\n * }\n *\n * // Typed app events (view → runtime bidirectional bus)\n * interface BoardEvents extends BaseRuntimeEvents {\n * op: [BoardOp]\n * }\n * function BoardView() {\n * const rt = useRuntime<BoardEvents>()\n * rt?.emit(\"op\", { type: \"cursor_down\" })\n * }\n * ```\n */\n\nimport { useContext } from \"react\"\nimport { RuntimeContext, type BaseRuntimeEvents, type RuntimeContextValue } from \"../context\"\n\n/**\n * Access the runtime event bus, or null if in static mode.\n *\n * Use this for components that work in both static and interactive modes.\n * For input-only components, prefer useInput() which throws a clear error\n * when called outside a runtime.\n */\nexport function useRuntime<\n E extends BaseRuntimeEvents = BaseRuntimeEvents,\n>(): RuntimeContextValue<E> | null {\n return useContext(RuntimeContext) as RuntimeContextValue<E> | null\n}\n","/**\n * Silvery useApp Hook\n *\n * Provides access to app-level controls like exit, pause, resume.\n * Backed by RuntimeContext (unified input + app controls).\n *\n * Unlike useInput (which throws outside a runtime), useApp returns\n * no-op values in static mode — exit/pause/resume are safe to call\n * but do nothing. This allows components to be rendered statically\n * for testing or string output.\n */\n\nimport { useContext } from \"react\"\nimport { RuntimeContext } from \"../context\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface UseAppResult {\n /**\n * Exit the application.\n * Optionally pass an error to indicate the app exited due to an error.\n * No-op in static mode.\n */\n exit: (error?: Error) => void\n /**\n * Pause rendering output (for screen switching). Input still works.\n * Returns undefined if not supported.\n */\n pause?: () => void\n /**\n * Resume rendering after pause. Forces a full redraw.\n * Returns undefined if not supported.\n */\n resume?: () => void\n}\n\n// No-op fallback for static mode\nconst staticResult: UseAppResult = {\n exit: () => {},\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/**\n * Hook for accessing app-level controls.\n *\n * Returns no-op values in static mode (no RuntimeContext).\n * Use useRuntime() if you need to distinguish between modes.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { exit } = useApp();\n *\n * useInput((input) => {\n * if (input === 'q') {\n * exit();\n * }\n * });\n *\n * return <Text>Press q to quit</Text>;\n * }\n * ```\n */\nexport function useApp(): UseAppResult {\n const rt = useContext(RuntimeContext)\n\n if (!rt) {\n return staticResult\n }\n\n return {\n exit: rt.exit,\n pause: rt.pause,\n resume: rt.resume,\n }\n}\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 * Silvery useStdout Hook\n *\n * Provides access to the stdout stream.\n * Compatible with Ink's useStdout API.\n */\n\nimport { useContext } from \"react\"\nimport { StdoutContext } from \"../context\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface UseStdoutResult {\n /** The stdout stream */\n stdout: NodeJS.WriteStream\n /** Write to stdout */\n write: (data: string) => void\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/**\n * Hook for accessing the stdout stream.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { write } = useStdout();\n *\n * useEffect(() => {\n * write('Hello, world!\\n');\n * }, []);\n *\n * return <Text>Check stdout</Text>;\n * }\n * ```\n */\nexport function useStdout(): UseStdoutResult {\n const context = useContext(StdoutContext)\n\n if (!context) {\n throw new Error(\"useStdout must be used within an Silvery application\")\n }\n\n return {\n stdout: context.stdout,\n write: context.write,\n }\n}\n","/**\n * Silvery useStderr Hook\n *\n * Provides access to the stderr stream.\n * Compatible with Ink's useStderr API.\n */\n\nimport { useContext } from \"react\"\nimport { StderrContext } from \"../context\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface UseStderrResult {\n /** The stderr stream */\n stderr: NodeJS.WriteStream\n /** Write to stderr */\n write: (data: string) => void\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/**\n * Hook for accessing the stderr stream.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { write } = useStderr();\n *\n * useEffect(() => {\n * write('Debug info\\n');\n * }, []);\n *\n * return <Text>Check stderr</Text>;\n * }\n * ```\n */\nexport function useStderr(): UseStderrResult {\n const context = useContext(StderrContext)\n\n if (!context) {\n // Fall back to process.stderr when no provider is present\n return {\n stderr: process.stderr,\n write: (data: string) => {\n process.stderr.write(data)\n },\n }\n }\n\n return {\n stderr: context.stderr,\n write: context.write,\n }\n}\n","/**\n * useFocus — Ink-compatible focus hook.\n *\n * Matches Ink 7.0's `useFocus(options?)` signature exactly:\n * - Options: `{ isActive?: boolean, autoFocus?: boolean, id?: string }`\n * - Returns: `{ isFocused: boolean, focus: (id: string) => void }`\n *\n * Reuses silvery's tree-based FocusManager — does NOT duplicate focus\n * infrastructure. For silvery's richer API (focus origin, blur, scope-aware),\n * use `useFocusable()` instead.\n *\n * @example\n * ```tsx\n * function Panel() {\n * const { isFocused, focus } = useFocus({ id: \"panel\", autoFocus: true })\n * return (\n * <Box focusable testID=\"panel\">\n * <Text>{isFocused ? \"Focused!\" : \"Not focused\"}</Text>\n * </Box>\n * )\n * }\n * ```\n *\n * Bead: km-silvery.focus-parity\n */\n\nimport { useCallback, useContext, useEffect, useRef, useSyncExternalStore } from \"react\"\nimport { FocusManagerContext, NodeContext } from \"../context\"\nimport type { FocusSnapshot } from \"@silvery/ag/focus-manager\"\nimport type { AgNode } from \"@silvery/ag/types\"\n\n// Counter for auto-generated focus IDs when none provided\nlet autoIdCounter = 0\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface UseFocusOptions {\n /** Whether this component participates in focus. Default: true. */\n isActive?: boolean\n /** Whether to auto-focus on mount. Default: false. */\n autoFocus?: boolean\n /** Focus ID. When provided, overrides the node's testID for focus matching. */\n id?: string\n}\n\nexport interface UseFocusResult {\n /** Whether this component is currently focused. */\n isFocused: boolean\n /** Focus a specific component by ID. */\n focus: (id: string) => void\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/**\n * Ink-compatible focus hook. Reads focus state from the tree-based FocusManager\n * and returns `{ isFocused, focus }`.\n *\n * The `id` option overrides testID for focus identity matching. When `isActive`\n * is false, `isFocused` is always false regardless of actual focus state.\n *\n * @param options - Focus options (all optional).\n */\nexport function useFocus(options: UseFocusOptions = {}): UseFocusResult {\n const { isActive = true, autoFocus = false, id } = options\n const fm = useContext(FocusManagerContext)\n const node = useContext(NodeContext)\n\n // Stable auto-generated ID when no explicit id is provided.\n // Uses a ref so the ID persists across re-renders.\n const autoIdRef = useRef<string | null>(null)\n if (!autoIdRef.current && !id) {\n autoIdRef.current = `__useFocus_${++autoIdCounter}`\n }\n\n // Determine the focus ID: explicit id > auto-generated id\n // (node's testID is NOT used — useFocus manages its own identity)\n const focusId = id ?? autoIdRef.current\n\n // Register as a hook focusable with the FocusManager.\n // This adds the component to the Tab cycle alongside tree-based focusables.\n // If a tree-based focusable with the same testID exists, FocusManager's\n // buildTabEntries deduplicates — the tree entry takes priority.\n useEffect(() => {\n if (!fm || !focusId) return\n return fm.registerHookFocusable(focusId, { isActive, autoFocus })\n }, [fm, focusId]) // eslint-disable-line react-hooks/exhaustive-deps -- autoFocus only matters on mount\n\n // Update isActive when it changes (without re-registering)\n useEffect(() => {\n if (!fm || !focusId) return\n fm.setHookFocusableActive(focusId, isActive)\n }, [fm, focusId, isActive])\n\n // Subscribe to FocusManager state via useSyncExternalStore\n const subscribe = useCallback(\n (listener: () => void) => {\n if (!fm) return () => {}\n return fm.subscribe(listener)\n },\n [fm],\n )\n\n const getSnapshot = useCallback(() => {\n if (!fm) return null\n return fm.getSnapshot()\n }, [fm])\n\n const snapshot: FocusSnapshot | null = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n\n // isFocused: true only when isActive AND this component's focusId matches activeId\n const isFocused =\n isActive && focusId !== null && snapshot !== null && snapshot.activeId === focusId\n\n // Helper: get the render tree root from the current node\n const getRoot = useCallback((): AgNode | null => {\n if (!node) return null\n let root = node\n while (root.parent) {\n root = root.parent\n }\n return root\n }, [node])\n\n // focus(id) — programmatically focus a component by ID (Ink signature)\n const focus = useCallback(\n (targetId: string) => {\n if (!fm) return\n const root = getRoot()\n if (root) {\n fm.focusById(targetId, root, \"programmatic\")\n }\n },\n [fm, getRoot],\n )\n\n return { isFocused, focus }\n}\n","/**\n * Silvery useFocusable Hook\n *\n * Makes a component focusable within the new tree-based focus system.\n * Uses useSyncExternalStore for tear-free reads from FocusManager.\n */\n\nimport { useCallback, useContext, useEffect, useMemo, useSyncExternalStore } from \"react\"\nimport { FocusManagerContext, NodeContext } from \"../context\"\nimport type { FocusOrigin, FocusSnapshot } from \"@silvery/ag/focus-manager\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface UseFocusableResult {\n /** Whether this node is currently focused */\n focused: boolean\n /** Focus this node programmatically */\n focus: () => void\n /** Remove focus from this node */\n blur: () => void\n /** How focus was most recently acquired (keyboard, mouse, programmatic) */\n focusOrigin: FocusOrigin | null\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/**\n * Hook that makes the current component focusable in the tree-based focus system.\n *\n * Reads the FocusManager via useSyncExternalStore, compares the activeId\n * with the current node's testID to determine focused state.\n *\n * On mount, registers the node with the FocusManager. If the node has\n * autoFocus prop, calls fm.focus(node) on mount.\n *\n * @example\n * ```tsx\n * function FocusablePanel() {\n * const { focused, focus } = useFocusable()\n * return (\n * <Box testID=\"panel\" focusable borderStyle=\"single\" borderColor={focused ? 'green' : 'gray'}>\n * <Text>{focused ? 'Focused!' : 'Click to focus'}</Text>\n * </Box>\n * )\n * }\n * ```\n */\nexport function useFocusable(): UseFocusableResult {\n const fm = useContext(FocusManagerContext)\n const node = useContext(NodeContext)\n\n // Read testID from the current node's props\n const testID = node\n ? (((node.props as Record<string, unknown>).testID as string | undefined) ?? null)\n : null\n\n // Read autoFocus from the current node's props\n const autoFocus = node ? !!(node.props as Record<string, unknown>).autoFocus : false\n\n // Subscribe to FocusManager state via useSyncExternalStore\n const subscribe = useCallback(\n (listener: () => void) => {\n if (!fm) return () => {}\n return fm.subscribe(listener)\n },\n [fm],\n )\n\n const getSnapshot = useCallback(() => {\n if (!fm) return null\n return fm.getSnapshot()\n }, [fm])\n\n const snapshot: FocusSnapshot | null = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n\n // Derive focused state from snapshot\n const focused = testID !== null && snapshot !== null && snapshot.activeId === testID\n const focusOrigin = focused ? snapshot!.focusOrigin : null\n\n // Auto-focus on mount if autoFocus prop is set\n useEffect(() => {\n if (fm && node && autoFocus) {\n fm.focus(node, \"programmatic\")\n }\n }, [fm, node, autoFocus])\n\n // Clean up: if this node is focused when unmounting, blur it\n useEffect(() => {\n return () => {\n if (fm && fm.activeElement === node) {\n fm.blur()\n }\n }\n }, [fm, node])\n\n // Memoize focus/blur callbacks\n const focus = useMemo(() => {\n return () => {\n if (fm && node) {\n fm.focus(node, \"programmatic\")\n }\n }\n }, [fm, node])\n\n const blur = useMemo(() => {\n return () => {\n if (fm && focused) {\n fm.blur()\n }\n }\n }, [fm, focused])\n\n return {\n focused,\n focus,\n blur,\n focusOrigin,\n }\n}\n","/**\n * Silvery useFocusWithin Hook\n *\n * Returns true if the focus is within the subtree rooted at the given testID.\n * Uses useSyncExternalStore for tear-free reads from FocusManager.\n */\n\nimport { useCallback, useContext, useSyncExternalStore } from \"react\"\nimport { FocusManagerContext, NodeContext } from \"../context\"\nimport type { FocusSnapshot } from \"@silvery/ag/focus-manager\"\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/**\n * Hook that returns whether focus is within a subtree.\n *\n * Finds the node with the given testID and walks up from the focused node\n * to check if it passes through that subtree root.\n *\n * @param testID - The testID of the subtree root to check\n * @returns true if the currently focused node is within the subtree\n *\n * @example\n * ```tsx\n * function Sidebar() {\n * const hasFocus = useFocusWithin('sidebar')\n * return (\n * <Box testID=\"sidebar\" borderColor={hasFocus ? 'blue' : 'gray'}>\n * <FocusableItem testID=\"item1\" />\n * <FocusableItem testID=\"item2\" />\n * </Box>\n * )\n * }\n * ```\n */\nexport function useFocusWithin(testID: string): boolean {\n const fm = useContext(FocusManagerContext)\n const node = useContext(NodeContext)\n\n // Subscribe to FocusManager state\n const subscribe = useCallback(\n (listener: () => void) => {\n if (!fm) return () => {}\n return fm.subscribe(listener)\n },\n [fm],\n )\n\n const getSnapshot = useCallback(() => {\n if (!fm) return null\n return fm.getSnapshot()\n }, [fm])\n\n const snapshot: FocusSnapshot | null = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n\n // If no active focus, can't be within\n if (!snapshot?.activeId) return false\n\n // Walk up from the focused node to find the root node so we can use\n // findByTestID. We need the render tree root.\n // The current node context gives us a reference into the tree.\n if (!node) return false\n\n // Walk up to find the root of the render tree\n let root = node\n while (root.parent) {\n root = root.parent\n }\n\n // Use FocusManager's hasFocusWithin which walks from activeElement up\n return fm!.hasFocusWithin(root, testID)\n}\n","/**\n * Silvery useFocusManager Hook\n *\n * Provides methods to control focus management for all components.\n * Uses the tree-based FocusManager via FocusManagerContext.\n */\n\nimport { useCallback, useContext, useSyncExternalStore } from \"react\"\nimport { FocusManagerContext, NodeContext } from \"../context\"\nimport type { FocusSnapshot } from \"@silvery/ag/focus-manager\"\nimport type { AgNode } from \"@silvery/ag/types\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface UseFocusManagerResult {\n /** Currently focused node (null if nothing focused) */\n activeElement: AgNode | null\n /** testID of the currently focused node */\n activeId: string | null\n /** The currently active peer scope ID */\n activeScopeId: string | null\n /** Focus a specific node or node by testID */\n focus: (nodeOrId: AgNode | string) => void\n /** Focus the next focusable element in tab order */\n focusNext: () => void\n /** Focus the previous focusable element in tab order */\n focusPrev: () => void\n /** Clear focus */\n blur: () => void\n /** Activate a peer focus scope (WPF FocusScope model) */\n activateScope: (scopeId: string) => void\n /** Enable focus management (no-op, kept for Ink API compatibility) */\n enableFocus: () => void\n /** Disable focus management (no-op, kept for Ink API compatibility) */\n disableFocus: () => void\n /** Focus previous (alias for focusPrev, kept for Ink API compatibility) */\n focusPrevious: () => void\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/**\n * Hook for managing focus across all focusable components.\n *\n * Uses the tree-based FocusManager via FocusManagerContext.\n *\n * @example\n * ```tsx\n * function Navigation() {\n * const { focusNext, focusPrev } = useFocusManager()\n *\n * useInput((input, key) => {\n * if (key.tab) {\n * if (key.shift) {\n * focusPrev()\n * } else {\n * focusNext()\n * }\n * }\n * })\n *\n * return <Text>Tab to navigate</Text>\n * }\n * ```\n */\nexport function useFocusManager(): UseFocusManagerResult {\n const fm = useContext(FocusManagerContext)\n const node = useContext(NodeContext)\n\n // Subscribe to FocusManager state\n const subscribe = useCallback(\n (listener: () => void) => {\n if (!fm) return () => {}\n return fm.subscribe(listener)\n },\n [fm],\n )\n\n const getSnapshot = useCallback(() => {\n if (!fm) return null\n return fm.getSnapshot()\n }, [fm])\n\n const snapshot: FocusSnapshot | null = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n\n // Helper: get the render tree root from the current node\n const getRoot = useCallback((): AgNode | null => {\n if (!node) return null\n let root = node\n while (root.parent) {\n root = root.parent\n }\n return root\n }, [node])\n\n const focus = useCallback(\n (nodeOrId: AgNode | string) => {\n if (!fm) return\n if (typeof nodeOrId === \"string\") {\n const root = getRoot()\n if (root) {\n fm.focusById(nodeOrId, root, \"programmatic\")\n }\n } else {\n fm.focus(nodeOrId, \"programmatic\")\n }\n },\n [fm, getRoot],\n )\n\n const focusNext = useCallback(() => {\n if (!fm) return\n const root = getRoot()\n if (root) fm.focusNext(root)\n }, [fm, getRoot])\n\n const focusPrev = useCallback(() => {\n if (!fm) return\n const root = getRoot()\n if (root) fm.focusPrev(root)\n }, [fm, getRoot])\n\n const blur = useCallback(() => {\n if (!fm) return\n fm.blur()\n }, [fm])\n\n const activateScope = useCallback(\n (scopeId: string) => {\n if (!fm) return\n const root = getRoot()\n if (root) fm.activateScope(scopeId, root)\n },\n [fm, getRoot],\n )\n\n const noOp = useCallback(() => {}, [])\n\n if (fm) {\n return {\n activeElement: fm.activeElement,\n activeId: snapshot?.activeId ?? null,\n activeScopeId: snapshot?.activeScopeId ?? null,\n focus,\n focusNext,\n focusPrev,\n blur,\n activateScope,\n enableFocus: noOp,\n disableFocus: noOp,\n focusPrevious: focusPrev,\n }\n }\n\n // No FocusManagerContext available — return inert result (safe for standalone component tests)\n return {\n activeElement: null,\n activeId: null,\n activeScopeId: null,\n focus: noOp as (nodeOrId: AgNode | string) => void,\n focusNext: noOp,\n focusPrev: noOp,\n blur: noOp,\n activateScope: noOp as (scopeId: string) => void,\n enableFocus: noOp,\n disableFocus: noOp,\n focusPrevious: noOp,\n }\n}\n","/**\n * InputLayerContext - DOM-style Input Event Bubbling\n *\n * Provides a stack-based input handling system where:\n * 1. Layers register synchronously via useLayoutEffect (before paint)\n * 2. Input dispatches to layers in LIFO order (most recent first)\n * 3. Handlers return true to consume, false to bubble to next layer\n *\n * This solves the race condition where dialog input handlers register\n * asynchronously via useEffect, causing early keystrokes to be lost.\n *\n * @example\n * ```tsx\n * // Dialog with text input\n * function Dialog() {\n * useInputLayer('dialog-input', (input, key) => {\n * if (key.backspace) { ... return true } // consumed\n * if (input >= ' ') { ... return true } // consumed\n * return false // bubble (e.g., escape to parent)\n * })\n * }\n * ```\n *\n * @see docs/future/silvery-command-api-research.md\n */\n\nimport type React from \"react\"\nimport { createContext, useCallback, useContext, useLayoutEffect, useMemo, useRef } from \"react\"\nimport type { Key } from \"../hooks/useInput\"\nimport { useInput } from \"../hooks/useInput\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Input handler function type.\n * @param input - The input character(s)\n * @param key - Key modifier information\n * @returns true if the event was consumed, false to bubble to next layer\n */\nexport type InputLayerHandler = (input: string, key: Key) => boolean\n\n/**\n * A layer in the input stack.\n */\nexport interface InputLayer {\n /** Unique identifier for this layer (used for removal) */\n id: string\n /** Handler function */\n handler: InputLayerHandler\n}\n\n/**\n * Context value providing access to the input layer stack.\n */\nexport interface InputLayerContextValue {\n /**\n * Push a layer onto the stack.\n * Layers are processed in LIFO order (most recent first).\n */\n push: (layer: InputLayer) => void\n\n /**\n * Remove a layer from the stack by ID.\n */\n pop: (id: string) => void\n\n /**\n * Dispatch input to the layer stack.\n * Walks stack from top to bottom, stopping when a handler returns true.\n */\n dispatch: (input: string, key: Key) => void\n}\n\n// =============================================================================\n// Context\n// =============================================================================\n\nconst InputLayerContext = createContext<InputLayerContextValue | null>(null)\n\n// =============================================================================\n// Provider\n// =============================================================================\n\nexport interface InputLayerProviderProps {\n children: React.ReactNode\n}\n\n/**\n * Provides the input layer stack to child components.\n *\n * This provider:\n * 1. Maintains a stack of input layers\n * 2. Sets up a single useInput handler that dispatches to the stack\n * 3. Allows layers to be added/removed dynamically\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <InputLayerProvider>\n * <Board />\n * </InputLayerProvider>\n * )\n * }\n * ```\n */\nexport function InputLayerProvider({ children }: InputLayerProviderProps): React.JSX.Element {\n // Use ref to avoid re-renders when layers change\n const layersRef = useRef<InputLayer[]>([])\n\n const push = useCallback((layer: InputLayer) => {\n const existing = layersRef.current\n const existingIndex = existing.findIndex((l) => l.id === layer.id)\n\n if (existingIndex >= 0) {\n // Update existing layer in place (preserve position)\n const updated = [...existing]\n updated[existingIndex] = layer\n layersRef.current = updated\n } else {\n // Add new layer at the end\n layersRef.current = [...existing, layer]\n }\n }, [])\n\n const pop = useCallback((id: string) => {\n layersRef.current = layersRef.current.filter((l) => l.id !== id)\n }, [])\n\n const dispatch = useCallback((input: string, key: Key) => {\n // Walk stack from first-registered (start) to last-registered (end).\n //\n // In React's commit phase, useLayoutEffect setup runs child-first:\n // - For <Parent><Child/></Parent>:\n // - Child's useLayoutEffect fires first -> pushes child layer\n // - Parent's useLayoutEffect fires second -> pushes parent layer\n // - Stack: [child, parent]\n //\n // To have child handle first (like DOM event bubbling where the\n // innermost/focused element handles events first), we process\n // from START (index 0) to END.\n const layers = layersRef.current\n for (let i = 0; i < layers.length; i++) {\n const layer = layers[i]\n if (layer?.handler(input, key)) {\n // Handler consumed the event, stop bubbling\n return\n }\n }\n // Event bubbled through all layers without being consumed\n }, [])\n\n // Single useInput at the root that dispatches to layer stack\n useInput((input, key) => {\n dispatch(input, key)\n })\n\n const contextValue = useMemo(() => ({ push, pop, dispatch }), [push, pop, dispatch])\n\n return <InputLayerContext.Provider value={contextValue}>{children}</InputLayerContext.Provider>\n}\n\n// =============================================================================\n// Hooks\n// =============================================================================\n\n/**\n * Access the input layer context directly.\n *\n * Use this when you need programmatic access to dispatch or layer management.\n * Most components should use useInputLayer instead.\n *\n * @throws Error if used outside InputLayerProvider\n */\nexport function useInputLayerContext(): InputLayerContextValue {\n const ctx = useContext(InputLayerContext)\n if (!ctx) {\n throw new Error(\"useInputLayerContext must be used within an InputLayerProvider\")\n }\n return ctx\n}\n\n/**\n * Register an input layer with the stack.\n *\n * Uses useLayoutEffect for synchronous registration, ensuring the handler\n * is registered before any paint/commit phase. This solves the race condition\n * where async useEffect registration causes early keystrokes to be lost.\n *\n * @param id - Unique identifier for this layer\n * @param handler - Input handler function. Return true to consume, false to bubble.\n *\n * @example\n * ```tsx\n * function SearchInput() {\n * const [value, setValue] = useState('')\n *\n * useInputLayer('search-input', (input, key) => {\n * if (key.backspace && value.length > 0) {\n * setValue(v => v.slice(0, -1))\n * return true\n * }\n * if (input.length === 1 && input >= ' ') {\n * setValue(v => v + input)\n * return true\n * }\n * return false // Let escape, enter, etc. bubble\n * })\n *\n * return <Text>Search: {value}</Text>\n * }\n * ```\n */\nexport function useInputLayer(id: string, handler: InputLayerHandler): void {\n const ctx = useContext(InputLayerContext)\n\n // Use useLayoutEffect for synchronous registration\n // This ensures the handler is registered before the component's first paint\n useLayoutEffect(() => {\n if (!ctx) {\n // Not inside InputLayerProvider - silently no-op\n // This allows components to work in both layered and non-layered contexts\n return\n }\n\n ctx.push({ id, handler })\n return () => {\n ctx.pop(id)\n }\n }, [ctx, id, handler])\n}\n\n// =============================================================================\n// Exports\n// =============================================================================\n\nexport { InputLayerContext }\n","/**\n * useTerminalFocused — track terminal window focus state.\n *\n * Returns whether the terminal window is currently focused. Updates reactively\n * when the terminal receives CSI I (focus-in) or CSI O (focus-out) events.\n *\n * Requires `focusReporting: true` in the run() options. Without it, this hook\n * always returns `true` (optimistic default — assumes the terminal is focused).\n *\n * @example\n * ```tsx\n * function InputBox() {\n * const terminalFocused = useTerminalFocused()\n * return (\n * <TextInput\n * borderColor={terminalFocused ? \"$focusborder\" : \"$border\"}\n * isActive={terminalFocused}\n * />\n * )\n * }\n * ```\n */\n\nimport { useContext, useEffect, useState } from \"react\"\nimport { ChainAppContext } from \"../context\"\n\n/**\n * Track whether the terminal window is focused.\n *\n * Returns `true` when the terminal has focus, `false` when it doesn't.\n * Always returns `true` if focus reporting is not enabled (safe default).\n */\nexport function useTerminalFocused(): boolean {\n const [focused, setFocused] = useState(true)\n const chain = useContext(ChainAppContext)\n\n useEffect(() => {\n if (!chain) return\n return chain.focusEvents.register((isFocused) => {\n setFocused(isFocused)\n })\n }, [chain])\n\n return focused\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 (\n termProgram === \"iTerm.app\" ||\n termProgram === \"WezTerm\" ||\n termProgram === \"ghostty\" ||\n termProgram === \"vscode\"\n )\n return true\n\n // Known-good TERM values\n if (\n term.includes(\"xterm\") ||\n term.includes(\"screen\") ||\n term.includes(\"tmux\") ||\n term.includes(\"kitty\")\n )\n 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 * usePaste — React context for paste event handling.\n *\n * Components register a PasteHandler via PasteProvider. When a bracketed\n * paste event arrives, it is routed to the nearest ancestor PasteHandler.\n *\n * PasteEvents include source detection: \"internal\" when the paste matches\n * the last copy from this app (with rich ClipboardData), \"external\" otherwise.\n *\n * @example\n * ```tsx\n * const handler: PasteHandler = {\n * onPaste(event) {\n * if (event.source === \"internal\" && event.data?.markdown) {\n * insertMarkdown(event.data.markdown)\n * } else {\n * insertPlainText(event.text)\n * }\n * }\n * }\n *\n * <PasteProvider handler={handler}>\n * <MyEditor />\n * </PasteProvider>\n * ```\n */\n\nimport React, { createContext, useContext, type ReactNode } from \"react\"\nimport type { PasteEvent } from \"@silvery/ag-term/copy-extraction\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Handler for paste events. Register via PasteProvider.\n */\nexport interface PasteHandler {\n onPaste(event: PasteEvent): void\n}\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst PasteContext = createContext<PasteHandler | null>(null)\n\n// ============================================================================\n// Provider Component\n// ============================================================================\n\n/**\n * Registers a paste handler for its subtree.\n * When a bracketed paste event arrives, it is routed to the nearest\n * ancestor PasteHandler.\n */\nexport function PasteProvider({\n handler,\n children,\n}: {\n handler: PasteHandler\n children: ReactNode\n}) {\n return React.createElement(PasteContext.Provider, { value: handler }, children)\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\n/**\n * Access the nearest ancestor PasteHandler.\n * Returns null if no handler is in the tree above this component.\n */\nexport function usePaste(): PasteHandler | null {\n return useContext(PasteContext)\n}\n","/**\n * Copy Extraction\n *\n * Application-facing API that lets apps enrich copied content with\n * structured data (markdown, HTML, internal formats).\n *\n * When text is selected and copied, the nearest ancestor CopyProvider\n * gets a chance to enrich the plain text with semantic representations.\n * Plain text always goes to clipboard immediately; rich data is best-effort.\n *\n * Also manages the internal clipboard — a module-level store of the last\n * copy's rich data so paste events can detect internal copies.\n *\n * @example\n * ```tsx\n * const provider: SemanticCopyProvider = {\n * enrichCopy(event) {\n * return {\n * text: event.text,\n * markdown: `**${event.text}**`,\n * }\n * }\n * }\n *\n * <CopyProvider provider={provider}>\n * <MyComponent />\n * </CopyProvider>\n * ```\n */\n\nimport type { SelectionRange } from \"@silvery/headless/selection\"\n\n// ============================================================================\n// ClipboardData\n// ============================================================================\n\n/**\n * Rich clipboard data that can hold multiple representations of copied content.\n * At minimum, `text` is always present. Other fields provide semantic enrichment.\n */\nexport interface ClipboardData {\n /** Plain text — always present, always written to system clipboard */\n text: string\n /** Markdown representation */\n markdown?: string\n /** HTML representation */\n html?: string\n /** Opaque internal data for in-app paste (serialized JSON, etc.) */\n internal?: unknown\n}\n\n// ============================================================================\n// CopyEvent\n// ============================================================================\n\n/**\n * Event passed to SemanticCopyProvider.enrichCopy when copy is triggered.\n */\nexport interface CopyEvent {\n /** Plain text extracted from the terminal buffer */\n text: string\n /** Screen coordinates of the selection */\n range: SelectionRange\n}\n\n// ============================================================================\n// SemanticCopyProvider\n// ============================================================================\n\n/**\n * Provider interface for enriching clipboard content with structured data.\n *\n * Registered on React components via CopyProvider context. When copy happens,\n * the nearest ancestor provider handles enrichment.\n *\n * The enrichCopy method can:\n * - Return ClipboardData synchronously\n * - Return a Promise<ClipboardData> for async enrichment\n * - Return void to skip enrichment (plain text only)\n */\nexport interface SemanticCopyProvider {\n enrichCopy(event: CopyEvent): ClipboardData | Promise<ClipboardData> | void\n}\n\n// ============================================================================\n// PasteEvent\n// ============================================================================\n\n/**\n * Event delivered to paste handlers.\n *\n * Contains the pasted text and, when the paste text matches the last internal\n * copy, includes the rich clipboard data from that copy.\n */\nexport interface PasteEvent {\n /** Plain text from the terminal paste sequence */\n text: string\n /** \"internal\" when the paste matches the last copy from this app, \"external\" otherwise */\n source: \"internal\" | \"external\"\n /** Rich data from the last internal copy, if source is \"internal\" */\n data?: ClipboardData\n}\n\n// ============================================================================\n// Internal Clipboard\n// ============================================================================\n\n/**\n * Shared internal clipboard — stores the last copy's rich data so paste\n * events can detect internal copies and include structured data.\n */\nlet internalClipboard: ClipboardData | null = null\n\n/**\n * Get the current internal clipboard data.\n * Used by paste event handling to detect internal copies.\n */\nexport function getInternalClipboard(): ClipboardData | null {\n return internalClipboard\n}\n\n/**\n * Set the internal clipboard data.\n * Called after a copy operation to store rich data for paste detection.\n */\nexport function setInternalClipboard(data: ClipboardData | null): void {\n internalClipboard = data\n}\n\n// ============================================================================\n// Factory Functions\n// ============================================================================\n\n/**\n * Create a PasteEvent from bracketed paste text.\n * Checks against the internal clipboard to determine source.\n */\nexport function createPasteEvent(text: string, clipboard: ClipboardData | null): PasteEvent {\n if (clipboard && clipboard.text === text) {\n return {\n text,\n source: \"internal\",\n data: clipboard,\n }\n }\n\n return {\n text,\n source: \"external\",\n }\n}\n\n/**\n * Create a SemanticCopyProvider from a simple enrichment function.\n */\nexport function createCopyProvider(\n enrichCopy: (event: CopyEvent) => ClipboardData | Promise<ClipboardData> | void,\n): SemanticCopyProvider {\n return { enrichCopy }\n}\n","/**\n * usePasteEvents — bridges runtime paste events to PasteHandler context.\n *\n * Subscribes to the runtime's paste events, enriches with internal clipboard\n * data, and routes to the nearest ancestor PasteHandler.\n *\n * Call this hook in your app root to enable paste routing.\n *\n * @example\n * ```tsx\n * function AppRoot() {\n * usePasteEvents()\n * return <MyApp />\n * }\n * ```\n */\n\nimport { useContext, useEffect, useRef } from \"react\"\nimport { ChainAppContext } from \"../context\"\nimport { createPasteEvent, getInternalClipboard } from \"@silvery/ag-term/copy-extraction\"\nimport { usePaste } from \"./usePaste\"\n\n/**\n * Bridge runtime paste events to the nearest PasteHandler.\n *\n * When a bracketed paste event arrives from the terminal:\n * 1. Checks internal clipboard to detect internal copies\n * 2. Creates a PasteEvent with source and rich data\n * 3. Routes to nearest PasteHandler via context\n * 4. If no PasteHandler, the event is silently ignored\n */\nexport function usePasteEvents(): void {\n const chain = useContext(ChainAppContext)\n const pasteHandler = usePaste()\n\n // Use ref for handler to avoid teardown/setup on every render\n const handlerRef = useRef(pasteHandler)\n handlerRef.current = pasteHandler\n\n useEffect(() => {\n if (!chain) return\n return chain.paste.register((text: string) => {\n const handler = handlerRef.current\n if (!handler) return\n const clipboard = getInternalClipboard()\n const event = createPasteEvent(text, clipboard)\n handler.onPaste(event)\n })\n }, [chain])\n}\n","/**\n * useSelection — React hook for accessing the selection feature from the capability registry.\n *\n * Reads SELECTION_CAPABILITY from the app's CapabilityRegistry via context.\n * Returns `undefined` when the selection feature is not installed (e.g., simple\n * run() apps without pipe() composition, or when withDomEvents is not used).\n *\n * When installed, returns the current TerminalSelectionState and re-renders\n * reactively on selection changes via useSyncExternalStore.\n *\n * This is the recommended hook for reading selection state. It replaces the\n * older useTerminalSelection/TerminalSelectionProvider pattern with a simpler\n * capability-based lookup.\n */\n\nimport { useContext, useSyncExternalStore } from \"react\"\nimport { CapabilityRegistryContext } from \"../context\"\nimport type { TerminalSelectionState } from \"@silvery/headless/selection\"\n\n// ============================================================================\n// Capability symbol — must match the one in @silvery/create/internal/capabilities.\n// Duplicated here to avoid a dependency from ag-react → @silvery/create internals.\n// ============================================================================\n\nconst SELECTION_CAPABILITY = Symbol.for(\"silvery.selection\")\n\n// ============================================================================\n// SelectionFeature shape — minimal interface for the hook.\n// ============================================================================\n\ninterface SelectionFeatureReadonly {\n readonly state: TerminalSelectionState\n subscribe(listener: () => void): () => void\n}\n\n// ============================================================================\n// Fallbacks for useSyncExternalStore\n// ============================================================================\n\nconst noopSubscribe = (_listener: () => void) => () => {}\nconst getUndefined = () => undefined as TerminalSelectionState | undefined\n\n// ============================================================================\n// Hook\n// ============================================================================\n\n/**\n * Access the current selection state from the capability registry.\n *\n * Returns `undefined` when:\n * - No CapabilityRegistryContext is provided (simple run() apps)\n * - SELECTION_CAPABILITY is not registered (withDomEvents not used or selection disabled)\n *\n * Returns `TerminalSelectionState` when selection is installed:\n * - `state.range` — current SelectionRange or null (idle)\n * - `state.selecting` — true while mouse button is held\n * - `state.source` — \"mouse\" | \"keyboard\" | null\n */\nexport function useSelection(): TerminalSelectionState | undefined {\n const registry = useContext(CapabilityRegistryContext)\n const feature = registry?.get<SelectionFeatureReadonly>(SELECTION_CAPABILITY)\n\n return useSyncExternalStore(\n feature ? (listener) => feature.subscribe(listener) : noopSubscribe,\n feature ? () => feature.state : getUndefined,\n )\n}\n","/**\n * useFindState — observe the FindFeature via CapabilityRegistry.\n *\n * Reads FIND_CAPABILITY from CapabilityRegistryContext and subscribes\n * to state changes via useSyncExternalStore. Returns the current\n * FindState, or undefined when the feature is not installed.\n *\n * @example\n * ```tsx\n * function FindIndicator() {\n * const find = useFindState()\n * if (!find?.active) return null\n * return <Text>Match {find.currentIndex + 1}/{find.matches.length}</Text>\n * }\n * ```\n */\n\nimport { useContext, useSyncExternalStore, useCallback } from \"react\"\nimport { CapabilityRegistryContext } from \"../context\"\n\n// Well-known symbol — matches FIND_CAPABILITY in @silvery/create internals.\nconst FIND_CAPABILITY = Symbol.for(\"silvery.find\")\n\n/** Minimal interface matching FindFeature's observable contract. */\ninterface FindObservable {\n readonly state: unknown\n subscribe(listener: () => void): () => void\n}\n\n// Stable no-op for when the feature is absent\nconst noop = () => () => {}\n\n/**\n * Observe the FindFeature's state reactively.\n *\n * @returns The current FindState, or undefined if the\n * FindFeature is not installed in the app composition.\n */\nexport function useFindState() {\n const registry = useContext(CapabilityRegistryContext)\n const feature = registry?.get<FindObservable>(FIND_CAPABILITY)\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n if (!feature) return noop()\n return feature.subscribe(onStoreChange)\n },\n [feature],\n )\n\n const getSnapshot = useCallback(() => {\n return feature?.state\n }, [feature])\n\n return useSyncExternalStore(subscribe, getSnapshot) as\n | import(\"@silvery/headless/find\").FindState\n | undefined\n}\n","/**\n * useCopyModeState — observe the CopyModeFeature via CapabilityRegistry.\n *\n * Reads COPY_MODE_CAPABILITY from CapabilityRegistryContext and subscribes\n * to state changes via useSyncExternalStore. Returns the current\n * CopyModeState, or undefined when the feature is not installed.\n *\n * @example\n * ```tsx\n * function CopyModeIndicator() {\n * const copyMode = useCopyModeState()\n * if (!copyMode?.active) return null\n * return <Text>COPY {copyMode.visual ? \"VISUAL\" : \"\"}</Text>\n * }\n * ```\n */\n\nimport { useContext, useSyncExternalStore, useCallback } from \"react\"\nimport { CapabilityRegistryContext } from \"../context\"\n\n// Well-known symbol — matches COPY_MODE_CAPABILITY in @silvery/create internals.\nconst COPY_MODE_CAPABILITY = Symbol.for(\"silvery.copy-mode\")\n\n/** Minimal interface matching CopyModeFeature's observable contract. */\ninterface CopyModeObservable {\n readonly state: unknown\n subscribe(listener: () => void): () => void\n}\n\n// Stable no-op for when the feature is absent\nconst noop = () => () => {}\n\n/**\n * Observe the CopyModeFeature's state reactively.\n *\n * @returns The current CopyModeState, or undefined if the\n * CopyModeFeature is not installed in the app composition.\n */\nexport function useCopyModeState() {\n const registry = useContext(CapabilityRegistryContext)\n const feature = registry?.get<CopyModeObservable>(COPY_MODE_CAPABILITY)\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n if (!feature) return noop()\n return feature.subscribe(onStoreChange)\n },\n [feature],\n )\n\n const getSnapshot = useCallback(() => {\n return feature?.state\n }, [feature])\n\n return useSyncExternalStore(subscribe, getSnapshot) as\n | import(\"@silvery/headless/copy-mode\").CopyModeState\n | undefined\n}\n","/**\n * useDragState — observe the DragFeature via CapabilityRegistry.\n *\n * Reads DRAG_CAPABILITY from CapabilityRegistryContext and subscribes\n * to state changes via useSyncExternalStore. Returns the current\n * DragState (or null when no drag is active), or undefined when the\n * feature is not installed.\n *\n * @example\n * ```tsx\n * function DragIndicator() {\n * const drag = useDragState()\n * if (drag === undefined) return null // feature not installed\n * if (drag === null) return null // no active drag\n * return <Text>Dragging from ({drag.startPos.x},{drag.startPos.y})</Text>\n * }\n * ```\n */\n\nimport { useContext, useSyncExternalStore, useCallback } from \"react\"\nimport { CapabilityRegistryContext } from \"../context\"\n\n// Well-known symbol — matches DRAG_CAPABILITY in @silvery/create internals.\nconst DRAG_CAPABILITY = Symbol.for(\"silvery.drag\")\n\n/** Minimal interface matching DragFeature's observable contract. */\ninterface DragObservable {\n readonly state: unknown\n subscribe(listener: () => void): () => void\n}\n\n// Stable no-op for when the feature is absent\nconst noop = () => () => {}\n\n/**\n * Observe the DragFeature's state reactively.\n *\n * Note: DragFeature.state is `DragState | null` — null means no active drag.\n * This hook returns `undefined` when the DragFeature is not installed at all.\n *\n * @returns The current DragState | null, or undefined if the\n * DragFeature is not installed in the app composition.\n */\nexport function useDragState() {\n const registry = useContext(CapabilityRegistryContext)\n const feature = registry?.get<DragObservable>(DRAG_CAPABILITY)\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n if (!feature) return noop()\n return feature.subscribe(onStoreChange)\n },\n [feature],\n )\n\n const getSnapshot = useCallback(() => {\n if (!feature) return undefined\n return feature.state\n }, [feature])\n\n return useSyncExternalStore(subscribe, getSnapshot) as\n | import(\"@silvery/ag-term/drag-events\").DragState\n | null\n | undefined\n}\n","/**\n * useInteractiveState — read per-node interactive state in React components.\n *\n * Returns the current InteractiveState for the nearest ancestor AgNode,\n * or a default (all false) if no interactive state has been set.\n *\n * Interactive state is written synchronously by event processing (mouse,\n * focus, drag state machines), which triggers React re-renders. Reading\n * during render is sufficient — no signal subscription needed.\n */\n\nimport { useContext } from \"react\"\nimport { NodeContext } from \"../context\"\nimport type { InteractiveState } from \"@silvery/ag/types\"\n\n/** Default state returned when no interactive state exists on the node */\nconst DEFAULT_STATE: Readonly<InteractiveState> = Object.freeze({\n hovered: false,\n armed: false,\n selected: false,\n focused: false,\n dropTarget: false,\n})\n\n/**\n * Read the interactive state of the current node.\n *\n * Returns a frozen default (all false) if the node has no interactive state.\n * The returned object should be treated as read-only.\n *\n * @example\n * ```tsx\n * function Button({ children }) {\n * const { hovered, armed, focused } = useInteractiveState()\n * return (\n * <Box\n * backgroundColor={armed ? '$primary' : hovered ? '$muted-bg' : undefined}\n * outlineStyle={focused ? 'round' : undefined}\n * >\n * {children}\n * </Box>\n * )\n * }\n * ```\n */\nexport function useInteractiveState(): Readonly<InteractiveState> {\n const node = useContext(NodeContext)\n if (!node?.interactiveState) return DEFAULT_STATE\n return node.interactiveState\n}\n","/**\n * useListItem - Context hook for items inside a ListView.\n *\n * Provides freeze control and status information to items rendered\n * within a ListView. Items call `freeze()` to signal they are complete\n * and should be pushed to terminal cache.\n *\n * @example\n * ```tsx\n * function TaskItem({ task }: { task: Task }) {\n * const { freeze, isCached } = useListItem()\n *\n * useEffect(() => {\n * if (task.status === \"done\") freeze()\n * }, [task.status])\n *\n * return <Text>{task.title}</Text>\n * }\n * ```\n */\n\nimport { createContext, useContext, useMemo, type ReactElement, type ReactNode } from \"react\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Context value provided to each item inside a ListView. */\nexport interface ListItemContext {\n /** Signal that this item is complete and should freeze into cache.\n * Optionally pass a snapshot JSX element to use instead of re-rendering\n * the item's live children. */\n freeze: (snapshot?: ReactElement) => void\n /** Whether this item has already been frozen into cache. */\n isCached: boolean\n /** The index of this item in the items array. */\n index: number\n /** True when item is close to the cache boundary. */\n nearCache: boolean\n}\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst ListItemCtx = createContext<ListItemContext | null>(null)\n\n// ============================================================================\n// Hook\n// ============================================================================\n\n/**\n * Access the list item context from within a ListView item.\n *\n * Must be called from a component rendered as a child of ListView.\n * Throws if used outside of that context.\n */\nexport function useListItem(): ListItemContext {\n const ctx = useContext(ListItemCtx)\n if (!ctx) {\n throw new Error(\"useListItem() must be used inside a ListView item\")\n }\n return ctx\n}\n\n// ============================================================================\n// Provider (internal, used by ListView)\n// ============================================================================\n\ninterface ListItemProviderProps extends ListItemContext {\n children: ReactNode\n}\n\n/**\n * Wraps each item rendered by ListView with its context.\n * Internal — not exported from the package's public API.\n */\nexport function ListItemProvider({\n children,\n freeze,\n isCached,\n index,\n nearCache,\n}: ListItemProviderProps) {\n const value = useMemo(\n () => ({ freeze, isCached, index, nearCache }),\n [freeze, isCached, index, nearCache],\n )\n return <ListItemCtx.Provider value={value}>{children}</ListItemCtx.Provider>\n}\n","/**\n * useColorScheme — reactive terminal color scheme (dark/light/unknown).\n *\n * Reads from the BgModeDetector registered on the CapabilityRegistry\n * by withTerminal(). Updates when Mode 2031 reports a scheme change.\n *\n * Returns \"unknown\" when:\n * - No CapabilityRegistry is present (e.g., simple run() without pipe())\n * - No BgModeDetector was registered\n * - The terminal hasn't responded to Mode 2031 yet\n *\n * @example\n * ```tsx\n * function ThemeAwareApp() {\n * const scheme = useColorScheme()\n * const theme = scheme === \"light\" ? lightTheme : darkTheme\n * return (\n * <ThemeProvider theme={theme}>\n * <App />\n * </ThemeProvider>\n * )\n * }\n * ```\n */\n\nimport { useCallback, useContext, useSyncExternalStore } from \"react\"\nimport { CapabilityRegistryContext } from \"../context\"\n\n// =============================================================================\n// Types — duck-typed to avoid dependency on @silvery/ansi or @silvery/ag-term\n// =============================================================================\n\n/** Terminal color scheme: dark, light, or unknown (not yet detected). */\nexport type ColorScheme = \"dark\" | \"light\" | \"unknown\"\n\n/**\n * Minimal interface for the color scheme detector capability.\n * Matches the shape of BgModeDetector from @silvery/ansi without importing it.\n */\ninterface ColorSchemeDetectorLike {\n readonly scheme: ColorScheme\n subscribe(listener: (scheme: \"dark\" | \"light\") => void): () => void\n}\n\n/** Well-known symbol for the color scheme capability. */\nconst COLOR_SCHEME_CAPABILITY = Symbol.for(\"silvery.color-scheme\")\n\n/**\n * Hook that returns the current terminal color scheme reactively.\n *\n * Subscribes to the BgModeDetector via the capability registry.\n * Re-renders the component when the scheme changes (dark <-> light).\n */\nexport function useColorScheme(): ColorScheme {\n const registry = useContext(CapabilityRegistryContext)\n const detector = registry?.get<ColorSchemeDetectorLike>(COLOR_SCHEME_CAPABILITY) ?? null\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n if (!detector) return () => {}\n return detector.subscribe(onStoreChange)\n },\n [detector],\n )\n\n const getSnapshot = useCallback((): ColorScheme => {\n if (!detector) return \"unknown\"\n return detector.scheme\n }, [detector])\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n","/**\n * Shared Readline Operations\n *\n * Pure functions and shared state for readline-style editing, shared\n * across TextInput (via useReadline) and TextArea.\n *\n * - Kill ring: global ring buffer shared across all readline instances\n * - Word boundary helpers: find word start/end positions in a string\n * - handleReadlineKey: pure key→edit-result dispatcher for shared operations\n *\n * Operations handled by handleReadlineKey (identical in single/multi-line):\n * Cursor: Left/Right, Ctrl+B/F, Alt+B/F (word)\n * Kill: Ctrl+W, Alt+Backspace, Alt+D\n * Yank: Ctrl+Y, Alt+Y (cycle)\n * Char: Ctrl+T (transpose), Ctrl+H, Backspace/Delete, Ctrl+D, regular input\n *\n * NOT handled (context-dependent — callers handle before calling this):\n * Ctrl+A/E (beginning/end of text vs wrapped line)\n * Ctrl+K/U (kill to end/beginning of text vs wrapped line)\n * Home/End, Up/Down, PageUp/PageDown, Enter, Escape\n */\nimport type { Key } from \"@silvery/ag/keys\"\n\n// =============================================================================\n// Kill Ring\n// =============================================================================\n\n/** Maximum entries in the global kill ring */\nexport const MAX_KILL_RING_SIZE = 10\n\n/** Global kill ring shared across all readline instances */\nexport const killRing: string[] = []\n\n/** Add text to the front of the kill ring, evicting oldest if full */\nexport function addToKillRing(text: string): void {\n if (!text) return\n killRing.unshift(text)\n if (killRing.length > MAX_KILL_RING_SIZE) {\n killRing.pop()\n }\n}\n\n// =============================================================================\n// Word Boundary Helpers\n// =============================================================================\n\nfunction findWordBoundary(value: string, cursor: number, direction: 1 | -1): number {\n let pos = cursor\n const peek = direction > 0 ? (p: number) => value[p] : (p: number) => value[p - 1]\n const inBounds = direction > 0 ? (p: number) => p < value.length : (p: number) => p > 0\n while (inBounds(pos) && /\\s/.test(peek(pos) ?? \"\")) pos += direction\n while (inBounds(pos) && !/\\s/.test(peek(pos) ?? \"\")) pos += direction\n return pos\n}\n\n/** Find the start of the previous word (for Alt+B, Ctrl+W) */\nexport function findPrevWordStart(value: string, cursor: number): number {\n return findWordBoundary(value, cursor, -1)\n}\n\n/** Find the end of the next word (for Alt+F, Alt+D) */\nexport function findNextWordEnd(value: string, cursor: number): number {\n return findWordBoundary(value, cursor, 1)\n}\n\n// =============================================================================\n// Shared Key Handler\n// =============================================================================\n\n/** Yank state for Alt+Y kill-ring cycling */\nexport interface YankState {\n lastYankIndex: number\n yankStart: number\n yankEnd: number\n}\n\n/** Result from handleReadlineKey — new value/cursor plus updated yank state */\nexport interface ReadlineKeyResult {\n value: string\n cursor: number\n /** null = reset yank state (non-yank op), YankState = yank/cycle was performed */\n yankState: YankState | null\n}\n\n/**\n * Process a readline key event and return the new edit state.\n * Returns null if the key was not handled.\n *\n * Handles operations that are identical between single-line and multi-line:\n * cursor movement, word movement, word kill, yank, transpose, delete, char input.\n *\n * Does NOT handle context-dependent operations (callers handle these first):\n * Ctrl+A/E, Ctrl+K/U, Home/End, Up/Down, PageUp/PageDown, Enter, Escape.\n */\nexport function handleReadlineKey(\n input: string,\n key: Key,\n value: string,\n cursor: number,\n yankState: YankState | null,\n): ReadlineKeyResult | null {\n // =========================================================================\n // Cursor Movement\n // =========================================================================\n\n // Left / Ctrl+B\n if (key.leftArrow || (key.ctrl && input === \"b\")) {\n return { value, cursor: cursor > 0 ? cursor - 1 : cursor, yankState: null }\n }\n\n // Right / Ctrl+F\n if (key.rightArrow || (key.ctrl && input === \"f\")) {\n return { value, cursor: cursor < value.length ? cursor + 1 : cursor, yankState: null }\n }\n\n // Alt+B: word backwards\n if (key.meta && input === \"b\") {\n return { value, cursor: findPrevWordStart(value, cursor), yankState: null }\n }\n\n // Alt+F: word forwards\n if (key.meta && input === \"f\") {\n return { value, cursor: findNextWordEnd(value, cursor), yankState: null }\n }\n\n // =========================================================================\n // Kill Operations\n // =========================================================================\n\n // Ctrl+W: kill word backwards\n if (key.ctrl && input === \"w\") {\n if (cursor === 0) return { value, cursor, yankState: null }\n const newCursor = findPrevWordStart(value, cursor)\n addToKillRing(value.slice(newCursor, cursor))\n return {\n value: value.slice(0, newCursor) + value.slice(cursor),\n cursor: newCursor,\n yankState: null,\n }\n }\n\n // Alt+Backspace: same as Ctrl+W\n if (key.meta && key.backspace) {\n if (cursor === 0) return { value, cursor, yankState: null }\n const newCursor = findPrevWordStart(value, cursor)\n addToKillRing(value.slice(newCursor, cursor))\n return {\n value: value.slice(0, newCursor) + value.slice(cursor),\n cursor: newCursor,\n yankState: null,\n }\n }\n\n // Alt+D: kill word forwards\n if (key.meta && input === \"d\") {\n if (cursor >= value.length) return { value, cursor, yankState: null }\n const newEnd = findNextWordEnd(value, cursor)\n addToKillRing(value.slice(cursor, newEnd))\n return { value: value.slice(0, cursor) + value.slice(newEnd), cursor, yankState: null }\n }\n\n // =========================================================================\n // Yank Operations\n // =========================================================================\n\n // Ctrl+Y: yank from kill ring\n if (key.ctrl && input === \"y\") {\n if (killRing.length === 0) return { value, cursor, yankState }\n const text = killRing[0] ?? \"\"\n const newCursor = cursor + text.length\n return {\n value: value.slice(0, cursor) + text + value.slice(cursor),\n cursor: newCursor,\n yankState: { lastYankIndex: 0, yankStart: cursor, yankEnd: newCursor },\n }\n }\n\n // Alt+Y: cycle through kill ring (only after Ctrl+Y)\n if (key.meta && input === \"y\") {\n if (!yankState || killRing.length <= 1) return { value, cursor, yankState }\n const nextIndex = (yankState.lastYankIndex + 1) % killRing.length\n const text = killRing[nextIndex] ?? \"\"\n const newValue = value.slice(0, yankState.yankStart) + text + value.slice(yankState.yankEnd)\n const newCursor = yankState.yankStart + text.length\n return {\n value: newValue,\n cursor: newCursor,\n yankState: { lastYankIndex: nextIndex, yankStart: yankState.yankStart, yankEnd: newCursor },\n }\n }\n\n // =========================================================================\n // Character Operations\n // =========================================================================\n\n // Ctrl+T: transpose characters\n if (key.ctrl && input === \"t\") {\n if (cursor < 2) return { value, cursor, yankState: null }\n return {\n value:\n value.slice(0, cursor - 2) + value[cursor - 1] + value[cursor - 2] + value.slice(cursor),\n cursor,\n yankState: null,\n }\n }\n\n // Ctrl+H: delete char before cursor\n if (key.ctrl && input === \"h\") {\n if (cursor > 0) {\n return {\n value: value.slice(0, cursor - 1) + value.slice(cursor),\n cursor: cursor - 1,\n yankState: null,\n }\n }\n return { value, cursor, yankState: null }\n }\n\n // Backspace / Delete key\n if (key.backspace || key.delete) {\n if (cursor > 0) {\n return {\n value: value.slice(0, cursor - 1) + value.slice(cursor),\n cursor: cursor - 1,\n yankState: null,\n }\n }\n return { value, cursor, yankState: null }\n }\n\n // Ctrl+D: delete at cursor\n if (key.ctrl && input === \"d\") {\n if (cursor < value.length) {\n return { value: value.slice(0, cursor) + value.slice(cursor + 1), cursor, yankState: null }\n }\n return { value, cursor, yankState: null }\n }\n\n // =========================================================================\n // Regular Character Input\n // =========================================================================\n\n // Cmd/Super-modified keystrokes (Cmd+K, Cmd+Shift+K, etc.) are almost always\n // app-level shortcuts owned by the host (command palette, save, quit, etc.).\n // Never treat them as text insertion — drop them here so the parent useInput\n // listeners further up the tree can handle them. Bare keys and Shift-only\n // keys still insert normally (shifted punctuation, uppercase, etc.).\n if (key.super) {\n return null\n }\n\n // Use the actual typed character (key.text) when available, not the normalized\n // keybinding key. E.g., Shift+3 sends '#' but input is normalized to '3'.\n const char = key.text ?? input\n if (char.length >= 1 && char >= \" \") {\n return {\n value: value.slice(0, cursor) + char + value.slice(cursor),\n cursor: cursor + char.length,\n yankState: null,\n }\n }\n\n return null\n}\n","/**\n * useReadline Hook\n *\n * This hook lives in components/ because it's tightly coupled to TextInput.\n * It manages readline state (cursor position, history, kill ring) that TextInput renders.\n *\n * Full readline-style line editing for terminal text input.\n * Supports cursor movement, word operations, kill ring, and all standard shortcuts.\n *\n * Shortcuts:\n * - Ctrl+A: Move to beginning of line\n * - Ctrl+E: Move to end of line\n * - Ctrl+B / Left: Move cursor left\n * - Ctrl+F / Right: Move cursor right\n * - Alt+B: Move cursor back one word\n * - Alt+F: Move cursor forward one word\n * - Ctrl+W / Alt+Backspace: Delete word backwards (adds to kill ring)\n * - Alt+D: Delete word forwards (adds to kill ring)\n * - Ctrl+U: Delete to beginning (adds to kill ring)\n * - Ctrl+K: Delete to end (adds to kill ring)\n * - Ctrl+Y: Yank (paste from kill ring)\n * - Alt+Y: Cycle through kill ring (after Ctrl+Y)\n * - Ctrl+T: Transpose characters\n * - Ctrl+H / Backspace: Delete char before cursor\n * - Ctrl+D / Delete: Delete char at cursor (or exit if empty)\n *\n * Note: Alt key detection requires terminal support. Some terminals send\n * ESC followed by the key instead of a proper alt modifier.\n */\nimport { useCallback, useRef, useState } from \"react\"\nimport { useInput } from \"../../hooks/index\"\nimport {\n killRing,\n addToKillRing,\n handleReadlineKey,\n type YankState,\n} from \"../../hooks/readline-ops\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ReadlineState {\n /** Current text value */\n value: string\n /** Cursor position (0 = before first char, value.length = after last char) */\n cursor: number\n}\n\nexport interface UseReadlineOptions {\n /** Initial value */\n initialValue?: string\n /** Called when value changes */\n onChange?: (value: string) => void\n /** Whether input is active */\n isActive?: boolean\n /** Handle Enter key (default: false - let parent handle) */\n handleEnter?: boolean\n /** Called when Enter is pressed (requires handleEnter: true) */\n onSubmit?: (value: string) => void\n /** Handle Escape key (default: false - let parent handle) */\n handleEscape?: boolean\n /** Handle Up/Down arrows (default: false - let parent handle for history) */\n handleVerticalArrows?: boolean\n /** Called on Ctrl+D with empty input (default: undefined) */\n onEOF?: () => void\n}\n\nexport interface UseReadlineResult {\n /** Current text value */\n value: string\n /** Cursor position */\n cursor: number\n /** Text before cursor (for rendering) */\n beforeCursor: string\n /** Text after cursor (for rendering) */\n afterCursor: string\n /** Clear the input */\n clear: () => void\n /** Set value programmatically (cursor moves to end) */\n setValue: (value: string) => void\n /** Set both value and cursor position */\n setValueWithCursor: (value: string, cursor: number) => void\n /** Kill ring contents (for debugging/display) */\n killRing: string[]\n}\n\n// =============================================================================\n// Hook\n// =============================================================================\n\nexport function useReadline({\n initialValue = \"\",\n onChange,\n isActive = true,\n handleEnter = false,\n handleEscape = false,\n handleVerticalArrows = false,\n onEOF,\n onSubmit,\n}: UseReadlineOptions = {}): UseReadlineResult {\n const [state, setState] = useState<ReadlineState>({\n value: initialValue,\n cursor: initialValue.length,\n })\n\n // Mutable ref for synchronous reads in the event handler.\n // Without this, rapid keypresses between React renders all read the same\n // stale closure state and overwrite each other.\n const stateRef = useRef<ReadlineState>({ value: initialValue, cursor: initialValue.length })\n stateRef.current = state\n\n const yankStateRef = useRef<YankState | null>(null)\n\n /** Apply a ReadlineKeyResult to state */\n const applyResult = useCallback(\n (result: { value: string; cursor: number; yankState: YankState | null }, prevValue: string) => {\n yankStateRef.current = result.yankState\n if (result.value === prevValue && result.cursor === stateRef.current.cursor) return\n const next = { value: result.value, cursor: result.cursor }\n stateRef.current = next\n setState(next)\n if (result.value !== prevValue) onChange?.(result.value)\n },\n [onChange],\n )\n\n const clear = useCallback(() => {\n const next = { value: \"\", cursor: 0 }\n stateRef.current = next\n setState(next)\n onChange?.(\"\")\n yankStateRef.current = null\n }, [onChange])\n\n const setValue = useCallback(\n (value: string) => {\n const next = { value, cursor: value.length }\n stateRef.current = next\n setState(next)\n onChange?.(value)\n yankStateRef.current = null\n },\n [onChange],\n )\n\n const setValueWithCursor = useCallback(\n (value: string, cursor: number) => {\n const next = { value, cursor: Math.max(0, Math.min(cursor, value.length)) }\n stateRef.current = next\n setState(next)\n onChange?.(value)\n yankStateRef.current = null\n },\n [onChange],\n )\n\n useInput(\n (input, key) => {\n const { value, cursor } = stateRef.current\n\n // Let parent handle Enter/Escape/vertical arrows unless explicitly enabled\n if (key.return && !handleEnter) return\n if (key.return && handleEnter) {\n onSubmit?.(value)\n return\n }\n if (key.escape && !handleEscape) return\n if ((key.upArrow || key.downArrow) && !handleVerticalArrows) return\n\n // Single-line specific: Ctrl+D on empty input = EOF\n if (key.ctrl && input === \"d\" && value.length === 0) {\n onEOF?.()\n return\n }\n\n // Single-line specific: Ctrl+A/E move to beginning/end of entire text\n if (key.ctrl && input === \"a\") {\n applyResult({ value, cursor: 0, yankState: null }, value)\n return\n }\n if (key.ctrl && input === \"e\") {\n applyResult({ value, cursor: value.length, yankState: null }, value)\n return\n }\n\n // Single-line specific: Ctrl+U/K kill to beginning/end of entire text\n if (key.ctrl && input === \"u\") {\n if (cursor === 0) return\n addToKillRing(value.slice(0, cursor))\n applyResult({ value: value.slice(cursor), cursor: 0, yankState: null }, value)\n return\n }\n if (key.ctrl && input === \"k\") {\n if (cursor >= value.length) return\n addToKillRing(value.slice(cursor))\n applyResult({ value: value.slice(0, cursor), cursor, yankState: null }, value)\n return\n }\n\n // Shared readline operations (cursor movement, word ops, kill ring, yank, etc.)\n const result = handleReadlineKey(input, key, value, cursor, yankStateRef.current)\n if (result) applyResult(result, value)\n },\n { isActive },\n )\n\n return {\n value: state.value,\n cursor: state.cursor,\n beforeCursor: state.value.slice(0, state.cursor),\n afterCursor: state.value.slice(state.cursor),\n clear,\n setValue,\n setValueWithCursor,\n killRing: [...killRing],\n }\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 \"../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 * TextInput Component\n *\n * Full readline-style single-line text input with kill ring, word movement, and\n * all standard shortcuts. Built on useReadline hook.\n *\n * Usage:\n * ```tsx\n * const [value, setValue] = useState('')\n * <TextInput\n * value={value}\n * onChange={setValue}\n * onSubmit={(val) => console.log('Submitted:', val)}\n * placeholder=\"Type here...\"\n * />\n * ```\n *\n * Supported shortcuts:\n * - Ctrl+A/E: Beginning/end of line\n * - Ctrl+B/F, Left/Right: Move cursor\n * - Alt+B/F: Move by word\n * - Ctrl+W, Alt+Backspace: Delete word backwards (kill)\n * - Alt+D: Delete word forwards (kill)\n * - Ctrl+U/K: Delete to beginning/end (kill)\n * - Ctrl+Y: Yank (paste)\n * - Alt+Y: Cycle kill ring\n * - Ctrl+T: Transpose characters\n */\nimport { useCallback, useImperativeHandle, forwardRef, useState, useEffect, useRef } from \"react\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\nimport { useReadline } from \"./useReadline\"\nimport { useFocusable } from \"../../hooks/useFocusable\"\nimport { useCursor } from \"../../hooks/useCursor\"\nimport type { SilveryMouseEvent } from \"@silvery/ag-term/mouse-events\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface TextInputProps {\n /** Current value (controlled) */\n value?: string\n /** Initial value (uncontrolled) */\n defaultValue?: string\n /** Called when value changes */\n onChange?: (value: string) => void\n /** Called when Enter is pressed */\n onSubmit?: (value: string) => void\n /** Called on Ctrl+D with empty input */\n onEOF?: () => void\n /** Placeholder text when empty */\n placeholder?: string\n /** Placeholder color (default: \"$fg-muted\") */\n placeholderColor?: string\n /** Whether input is focused/active (overrides focus system) */\n isActive?: boolean\n /** Prompt prefix (e.g., \"$ \" or \"> \") */\n prompt?: string\n /** Prompt color (default: \"$fg-accent\") */\n promptColor?: string\n /** Text color */\n color?: string\n /** Cursor style: 'block' (inverse) or 'underline' */\n cursorStyle?: \"block\" | \"underline\"\n /** Show underline below input */\n showUnderline?: boolean\n /** Underline width (default: 40) */\n underlineWidth?: number\n /** Mask character for passwords */\n mask?: string\n /** Border style (e.g., \"round\", \"single\") — wraps input in bordered Box */\n borderStyle?: string\n /** Border color when unfocused (default: \"$border-default\") */\n borderColor?: string\n /** Border color when focused (default: \"$border-focus\") */\n focusBorderColor?: string\n /** Test ID for focus system identification */\n testID?: string\n}\n\nexport interface TextInputHandle {\n /** Clear the input */\n clear: () => void\n /** Get current value */\n getValue: () => string\n /** Set value programmatically */\n setValue: (value: string) => void\n /** Get kill ring contents */\n getKillRing: () => string[]\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport const TextInput = forwardRef<TextInputHandle, TextInputProps>(function TextInput(\n {\n value: controlledValue,\n defaultValue = \"\",\n onChange,\n onSubmit,\n onEOF,\n placeholder = \"\",\n placeholderColor = \"$fg-muted\",\n isActive: isActiveProp,\n prompt = \"\",\n promptColor = \"$fg-accent\",\n color,\n cursorStyle = \"block\",\n showUnderline = false,\n underlineWidth = 40,\n mask,\n borderStyle: borderStyleProp,\n borderColor: borderColorProp = \"$border-default\",\n focusBorderColor = \"$border-focus\",\n testID,\n },\n ref,\n) {\n // Focus system integration: prop overrides hook.\n // When testID is set, the component participates in the focus tree and\n // isActive derives from focus state. Without testID, default to true\n // for backward compatibility.\n const { focused } = useFocusable()\n const isActive = isActiveProp ?? (testID ? focused : true)\n\n // Track whether we're in controlled mode\n const isControlled = controlledValue !== undefined\n\n // Track value changes that originated from internal editing (keystroke → onChange).\n // When the parent feeds back the SAME value via controlledValue, we skip\n // readline.setValue() since readline already has the correct cursor position.\n // When the parent feeds back a DIFFERENT value (i.e. it transformed or replaced\n // the value inside onChange — e.g. a sigil-replacement rule), we treat that as\n // an external override and sync readline. Without this, parent-driven overrides\n // would silently drop because we'd match \"internal\" and skip the sync. See\n // km-tui.omnibox-use-silvery for the motivating use case (slippery sigil rule).\n const internalChangeRef = useRef(false)\n const lastEmittedValueRef = useRef<string | null>(null)\n\n // Use readline hook\n const readline = useReadline({\n initialValue: isControlled ? (controlledValue ?? \"\") : defaultValue,\n onChange: useCallback(\n (newValue: string) => {\n internalChangeRef.current = true\n lastEmittedValueRef.current = newValue\n onChange?.(newValue)\n },\n [onChange],\n ),\n isActive,\n handleEnter: !!onSubmit,\n onSubmit,\n onEOF,\n })\n\n // Sync controlled value to readline — only for external changes or parent overrides.\n // Internal changes (from editing) already have correct cursor position, UNLESS the\n // parent's onChange handler transformed the value into something different — in\n // that case we treat it as external and sync the new value into readline.\n const [lastControlledValue, setLastControlledValue] = useState(controlledValue)\n useEffect(() => {\n if (isControlled && controlledValue !== lastControlledValue) {\n const isEchoOfInternalEdit =\n internalChangeRef.current && controlledValue === lastEmittedValueRef.current\n if (isEchoOfInternalEdit) {\n // Parent echoed back exactly what we emitted — readline already has correct state.\n internalChangeRef.current = false\n } else {\n // External change OR parent override — sync readline (cursor goes to end).\n readline.setValue(controlledValue ?? \"\")\n internalChangeRef.current = false\n }\n setLastControlledValue(controlledValue)\n }\n }, [isControlled, controlledValue, lastControlledValue, readline])\n\n // Handle Enter separately for onSubmit\n const { value, cursor, clear, setValue, killRing } = readline\n\n // Imperative handle for parent control\n useImperativeHandle(ref, () => ({\n clear,\n getValue: () => value,\n setValue,\n getKillRing: () => killRing,\n }))\n\n // Compute display value (with optional mask)\n const displayValue = mask ? mask.repeat(value.length) : value\n const displayBeforeCursor = displayValue.slice(0, cursor)\n const displayAtCursor = displayValue[cursor] ?? \" \"\n const displayAfterCursor = displayValue.slice(cursor + 1)\n const showPlaceholder = !value && placeholder\n\n // Always show visual cursor (inverse/underline). When active, the hardware\n // cursor is also positioned via useCursor() for terminal blink support.\n const cursorEl =\n cursorStyle === \"underline\" ? (\n <Text underline>{displayAtCursor}</Text>\n ) : (\n <Text inverse>{displayAtCursor}</Text>\n )\n\n // Compute border+padding offset for cursor positioning.\n // useCursor reads scrollRect from the parent's NodeContext, but the text\n // content is rendered inside this component's Box (which may have border\n // and padding). We must add those offsets so the terminal cursor aligns\n // with the text content area.\n const borderColOffset = borderStyleProp ? 2 : 0 // border-left(1) + paddingX-left(1)\n const borderRowOffset = borderStyleProp ? 1 : 0 // border-top(1)\n\n useCursor({\n col: prompt.length + displayBeforeCursor.length + borderColOffset,\n row: borderRowOffset,\n visible: isActive,\n })\n\n // Click-to-position: map mouse click to cursor offset\n const handleMouseDown = useCallback(\n (e: SilveryMouseEvent) => {\n if (e.button !== 0) return\n const rect = e.currentTarget.scrollRect\n if (!rect) return\n const relativeX = e.clientX - rect.x - prompt.length\n const newCursor = Math.max(0, Math.min(relativeX, value.length))\n readline.setValueWithCursor(value, newCursor)\n },\n [prompt.length, value, readline],\n )\n\n const inputContent = (\n <Text color={color}>\n {prompt && <Text color={promptColor}>{prompt}</Text>}\n {showPlaceholder ? (\n // Placeholder uses the semantic $fg-muted token (overridable via\n // `placeholderColor`) rather than raw `dimColor`. SGR 2 has uneven\n // terminal support (e.g. Ghostty ignores it on some foregrounds), so\n // the semantic token — which resolves to a pre-dimmed truecolor hex —\n // produces a reliably muted appearance. See silvery styling guide §2\n // (\"dim is a rendering detail, not a design primitive\") and the\n // decision flowchart entry for placeholder text.\n <>\n {cursorStyle === \"underline\" ? (\n <Text underline color={placeholderColor}>\n {placeholder[0]}\n </Text>\n ) : (\n <Text inverse color={placeholderColor}>\n {placeholder[0]}\n </Text>\n )}\n <Text color={placeholderColor}>{placeholder.slice(1)}</Text>\n </>\n ) : (\n <>\n <Text>{displayBeforeCursor}</Text>\n {cursorEl}\n <Text>{displayAfterCursor}</Text>\n </>\n )}\n </Text>\n )\n\n if (borderStyleProp) {\n return (\n <Box\n focusable\n testID={testID}\n flexDirection=\"column\"\n borderStyle={borderStyleProp as any}\n borderColor={isActive ? focusBorderColor : borderColorProp}\n paddingX={1}\n onMouseDown={handleMouseDown}\n >\n {inputContent}\n {showUnderline && <Text color=\"$border-default\">{\"─\".repeat(underlineWidth)}</Text>}\n </Box>\n )\n }\n\n return (\n <Box focusable testID={testID} flexDirection=\"column\" onMouseDown={handleMouseDown}>\n {inputContent}\n {showUnderline && <Text color=\"$border-default\">{\"─\".repeat(underlineWidth)}</Text>}\n </Box>\n )\n})\n","/**\n * Text Cursor Utilities\n *\n * Pure functions for mapping between flat character offsets and visual\n * (row, col) positions in word-wrapped text. Uses the same wrapText()\n * function as the rendering pipeline, guaranteeing cursor positions\n * match what's displayed on screen.\n *\n * Architecture layer 0 — no state, no hooks, no components.\n * Used by: TextArea (layer 3), useTextEdit (layer 1), and apps\n * that need cursor math without the full component stack.\n *\n * @example\n * ```ts\n * import { cursorToRowCol, cursorMoveDown } from '@silvery/ag-react'\n *\n * const { row, col } = cursorToRowCol(\"hello world\", 5, 8)\n * // row=0, col=5 (fits in 8-wide line)\n *\n * const next = cursorMoveDown(\"hello world\\nfoo\", 3, 8)\n * // next = 12 (moved to row 1, col 3 → \"foo\"[3] = end)\n * ```\n */\nimport { type Measurer, wrapText } from \"@silvery/ag-term/unicode\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface WrappedLine {\n /** The text content of this visual line */\n line: string\n /** Character offset in the original text where this line starts */\n startOffset: number\n}\n\n// =============================================================================\n// Core Functions\n// =============================================================================\n\n/**\n * Convert a flat cursor offset to visual (row, col) in word-wrapped text.\n *\n * Uses wrapText() from unicode.ts — the same function the render pipeline\n * uses — so cursor positions always match what's displayed on screen.\n */\nexport function cursorToRowCol(\n text: string,\n cursor: number,\n wrapWidth: number,\n measurer?: Measurer,\n): { row: number; col: number } {\n if (wrapWidth <= 0) return { row: 0, col: 0 }\n return cursorToRowColFromLines(getWrappedLines(text, wrapWidth, measurer), cursor)\n}\n\n/** Internal: compute row/col from pre-computed wrapped lines. */\nfunction cursorToRowColFromLines(\n lines: WrappedLine[],\n cursor: number,\n): { row: number; col: number } {\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!\n const lineEnd = line.startOffset + line.line.length\n const isLast = i === lines.length - 1\n\n if (cursor <= lineEnd || isLast) {\n const col = Math.max(0, Math.min(cursor - line.startOffset, line.line.length))\n return { row: i, col }\n }\n }\n\n return { row: Math.max(0, lines.length - 1), col: 0 }\n}\n\n/**\n * Get all wrapped display lines with their starting character offsets.\n *\n * Each entry represents one visual line on screen. The startOffset can be\n * used to convert a (row, col) back to a flat cursor position:\n * `flatOffset = lines[row].startOffset + col`\n */\nexport function getWrappedLines(\n text: string,\n wrapWidth: number,\n measurer?: Measurer,\n): WrappedLine[] {\n if (wrapWidth <= 0) return [{ line: \"\", startOffset: 0 }]\n\n const logicalLines = text.split(\"\\n\")\n const result: WrappedLine[] = []\n let offset = 0\n // Use explicit measurer when available, fall back to module-level convenience function\n const wt = measurer ? measurer.wrapText.bind(measurer) : wrapText\n\n for (let li = 0; li < logicalLines.length; li++) {\n const line = logicalLines[li]!\n // Use trim=true to match the renderer's wrapping behavior.\n // The renderer uses wrapText(text, width, true, true), so cursor math\n // must produce the same visual lines to keep positions synchronized.\n const wrapped = wt(line, wrapWidth, false, true)\n const lines = wrapped.length === 0 ? [\"\"] : wrapped\n\n for (const wLine of lines) {\n // Skip whitespace in the original text that was trimmed:\n // - Leading spaces on continuation lines (trimmed by renderer)\n // - Trailing space at break point (consumed as separator by renderer)\n while (\n offset < text.length &&\n text[offset] === \" \" &&\n wLine.length > 0 &&\n text[offset] !== wLine[0]\n ) {\n offset++\n }\n result.push({ line: wLine, startOffset: offset })\n offset += wLine.length\n }\n // Skip any remaining trailing spaces before the newline\n while (offset < text.length && text[offset] === \" \") {\n offset++\n }\n offset++ // for \\n\n }\n\n return result\n}\n\n/**\n * Convert visual (row, col) to a flat cursor offset.\n *\n * Clamps col to the line length if the target column exceeds it\n * (important for stickyX behavior on short lines).\n */\nexport function rowColToCursor(\n text: string,\n row: number,\n col: number,\n wrapWidth: number,\n measurer?: Measurer,\n): number {\n const lines = getWrappedLines(text, wrapWidth, measurer)\n if (row < 0) return 0\n if (row >= lines.length) return text.length\n const line = lines[row]!\n return line.startOffset + Math.min(col, line.line.length)\n}\n\n/**\n * Move cursor up one visual line.\n *\n * Returns the new cursor offset, or null if already on the first visual line\n * (indicating a boundary — the caller should handle cross-block navigation).\n *\n * @param stickyX - Preferred column position for vertical movement.\n * When moving through lines of different lengths, the cursor tries to\n * stay at this column. Pass the col from the original position before\n * the first vertical move in a sequence.\n */\nexport function cursorMoveUp(\n text: string,\n cursor: number,\n wrapWidth: number,\n stickyX?: number,\n measurer?: Measurer,\n): number | null {\n if (wrapWidth <= 0) return cursor > 0 ? 0 : null\n\n const lines = getWrappedLines(text, wrapWidth, measurer)\n const { row, col } = cursorToRowColFromLines(lines, cursor)\n\n if (row === 0) return null // at first visual line — boundary\n\n const targetX = stickyX ?? col\n // Try successive lines upward: if the target position equals the current cursor\n // (happens at wrap boundaries), keep going up to make real progress.\n for (let prevRow = row - 1; prevRow >= 0; prevRow--) {\n const targetLine = lines[prevRow]!\n const next = targetLine.startOffset + Math.min(targetX, targetLine.line.length)\n if (next !== cursor) return next\n }\n return null // all preceding lines map to same position — boundary\n}\n\n/**\n * Move cursor down one visual line.\n *\n * Returns the new cursor offset, or null if already on the last visual line\n * (indicating a boundary — the caller should handle cross-block navigation).\n *\n * @param stickyX - Preferred column position for vertical movement.\n */\nexport function cursorMoveDown(\n text: string,\n cursor: number,\n wrapWidth: number,\n stickyX?: number,\n measurer?: Measurer,\n): number | null {\n if (wrapWidth <= 0) return cursor < text.length ? text.length : null\n\n const lines = getWrappedLines(text, wrapWidth, measurer)\n const { row, col } = cursorToRowColFromLines(lines, cursor)\n\n if (row >= lines.length - 1) return null // at last visual line — boundary\n\n const targetX = stickyX ?? col\n // Try successive lines: if the target position equals the current cursor\n // (happens at wrap boundaries where end-of-line-N == start-of-line-N+1),\n // advance to the next line to make real progress.\n for (let nextRow = row + 1; nextRow < lines.length; nextRow++) {\n const targetLine = lines[nextRow]!\n const next = targetLine.startOffset + Math.min(targetX, targetLine.line.length)\n if (next !== cursor) return next\n }\n return null // all remaining lines map to same position — boundary\n}\n\n/**\n * Count total visual lines after word wrapping.\n */\nexport function countVisualLines(text: string, wrapWidth: number, measurer?: Measurer): number {\n return getWrappedLines(text, wrapWidth, measurer).length\n}\n","/**\n * useTextArea Hook\n *\n * Headless multi-line text editing hook with word wrapping, scrolling,\n * cursor movement, selection, and kill ring. Extracted from TextArea\n * so custom UIs can reuse the editing logic without the default rendering.\n *\n * TextArea itself uses this hook internally.\n *\n * Usage:\n * ```tsx\n * function CustomEditor() {\n * const { width } = useBoxRect()\n * const ta = useTextArea({ height: 10, wrapWidth: width })\n *\n * // ta.value, ta.cursor, ta.wrappedLines, ta.selection, etc.\n * // ta.clear(), ta.setValue(), ta.getSelection()\n * // Input handling is done automatically via useInput.\n *\n * return <MyCustomRendering {...ta} />\n * }\n * ```\n *\n * Supported shortcuts (same as TextArea component):\n * - Arrow keys: Move cursor (clears selection)\n * - Shift+Arrow: Extend selection\n * - Shift+Home/End: Select to line boundaries\n * - Ctrl+Shift+Arrow: Word-wise selection\n * - Ctrl+A: Select all text\n * - Ctrl+E: End of line\n * - Home/End: Beginning/end of line\n * - Alt+B/F: Move by word (wraps across lines)\n * - Ctrl+W, Alt+Backspace: Delete word backwards (kill ring)\n * - Alt+D: Delete word forwards (kill ring)\n * - Ctrl+K: Kill to end of line (kill ring)\n * - Ctrl+U: Kill to beginning of line (kill ring)\n * - Ctrl+Y: Yank (paste from kill ring)\n * - Alt+Y: Cycle through kill ring (after Ctrl+Y)\n * - Ctrl+T: Transpose characters\n * - PageUp/PageDown: Scroll by viewport height\n * - Backspace/Delete: Delete characters (or selected text)\n * - Enter: Insert newline (replaces selection, or submit with submitKey)\n * - Typing with selection: Replaces selected text\n */\nimport { useCallback, useMemo, useRef, useState } from \"react\"\nimport { useInput } from \"../../hooks/useInput\"\nimport {\n addToKillRing,\n findNextWordEnd,\n findPrevWordStart,\n handleReadlineKey,\n type YankState,\n} from \"../../hooks/readline-ops\"\nimport { cursorToRowCol, getWrappedLines } from \"@silvery/create/text-cursor\"\nimport type { WrappedLine } from \"@silvery/create/text-cursor\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** Selection range as [start, end) character offsets */\nexport interface TextAreaSelection {\n start: number\n end: number\n}\n\nexport interface UseTextAreaOptions {\n /** Current value (controlled) */\n value?: string\n /** Initial value (uncontrolled) */\n defaultValue?: string\n /** Called when value changes */\n onChange?: (value: string) => void\n /** Called on submit (Ctrl+Enter by default, or Enter if submitKey=\"enter\") */\n onSubmit?: (value: string) => void\n /** Key to trigger submit: \"ctrl+enter\" (default), \"enter\", or \"meta+enter\" */\n submitKey?: \"ctrl+enter\" | \"enter\" | \"meta+enter\"\n /** Whether input is active (receives keystrokes) */\n isActive?: boolean\n /** Visible height in rows (for scroll clamping and PageUp/PageDown) */\n height: number\n /** Wrap width in columns (typically from useBoxRect) */\n wrapWidth: number\n /** Number of context lines to keep visible above/below cursor when scrolling (default: 1) */\n scrollMargin?: number\n /** When true, ignore all input */\n disabled?: boolean\n /** Maximum number of characters allowed */\n maxLength?: number\n}\n\nexport interface UseTextAreaResult {\n /** Current text value */\n value: string\n /** Cursor position (character offset) */\n cursor: number\n /** Cursor row in wrapped lines (0-indexed) */\n cursorRow: number\n /** Cursor column in current wrapped line (0-indexed) */\n cursorCol: number\n /** Visible cursor row relative to scroll offset */\n visibleCursorRow: number\n /** Current scroll offset (first visible row) */\n scrollOffset: number\n /** Wrapped lines for the current value and width */\n wrappedLines: WrappedLine[]\n /** Visible lines (wrappedLines sliced by scrollOffset and height) */\n visibleLines: WrappedLine[]\n /** Current selection range, or null if no selection */\n selection: TextAreaSelection | null\n /** Selection anchor position (where shift-select started), or null */\n selectionAnchor: number | null\n /** Clear the input */\n clear: () => void\n /** Set value programmatically (cursor moves to end) */\n setValue: (value: string) => void\n /** Get the current selection range, or null if no selection */\n getSelection: () => TextAreaSelection | null\n /** Set cursor position (character offset) and scroll to keep it visible */\n setCursor: (offset: number) => void\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/** Ensure scroll offset keeps the cursor row visible with margin context lines */\nexport function clampScroll(\n cursorRow: number,\n currentScroll: number,\n viewportHeight: number,\n totalLines: number,\n margin: number,\n): number {\n if (viewportHeight <= 0) return 0\n // Effective margin: disabled when content fits in viewport, and clamped so\n // the cursor can still reach the first/last row.\n const effectiveMargin =\n totalLines <= viewportHeight ? 0 : Math.min(margin, Math.floor((viewportHeight - 1) / 2))\n let scroll = currentScroll\n if (cursorRow < scroll + effectiveMargin) {\n scroll = cursorRow - effectiveMargin\n }\n if (cursorRow >= scroll + viewportHeight - effectiveMargin) {\n scroll = cursorRow - viewportHeight + 1 + effectiveMargin\n }\n return Math.max(0, Math.min(scroll, Math.max(0, totalLines - viewportHeight)))\n}\n\n// =============================================================================\n// Hook\n// =============================================================================\n\nexport function useTextArea({\n value: controlledValue,\n defaultValue = \"\",\n onChange,\n onSubmit,\n submitKey = \"ctrl+enter\",\n isActive = true,\n height,\n wrapWidth: rawWrapWidth,\n scrollMargin = 1,\n disabled,\n maxLength,\n}: UseTextAreaOptions): UseTextAreaResult {\n const isControlled = controlledValue !== undefined\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue)\n const [cursor, setCursor] = useState(defaultValue.length)\n const [scrollOffset, setScrollOffset] = useState(0)\n const stickyXRef = useRef<number | null>(null)\n\n const yankStateRef = useRef<YankState | null>(null)\n\n // Selection: anchor is where the selection started, cursor is the moving end.\n // When anchor is non-null, the selected range is [min(anchor, cursor), max(anchor, cursor)).\n const [selectionAnchor, setSelectionAnchor] = useState<number | null>(null)\n const selectionAnchorRef = useRef<number | null>(null)\n selectionAnchorRef.current = selectionAnchor\n\n const value = isControlled ? (controlledValue ?? \"\") : uncontrolledValue\n const wrapWidth = Math.max(1, rawWrapWidth)\n\n // Clamp cursor when controlled value shrinks (e.g., parent resets to \"\").\n const clampedCursor = Math.min(cursor, value.length)\n if (clampedCursor !== cursor) {\n setCursor(clampedCursor)\n }\n\n // Mutable ref for synchronous reads in the event handler.\n const stateRef = useRef({ value, cursor: clampedCursor })\n stateRef.current.value = value\n stateRef.current.cursor = clampedCursor\n\n const scrollRef = useRef(scrollOffset)\n scrollRef.current = scrollOffset\n\n const setCursorAndScroll = useCallback(\n (newCursor: number, text: string) => {\n stateRef.current.cursor = newCursor\n setCursor(newCursor)\n const { row } = cursorToRowCol(text, newCursor, wrapWidth)\n const totalLines = getWrappedLines(text, wrapWidth).length\n const newScroll = clampScroll(row, scrollRef.current, height, totalLines, scrollMargin)\n if (newScroll !== scrollRef.current) {\n setScrollOffset(newScroll)\n }\n },\n [wrapWidth, height, scrollMargin],\n )\n\n const updateValue = useCallback(\n (newValue: string, newCursor: number) => {\n // Enforce maxLength: allow deletions (shorter) but reject insertions beyond limit\n if (\n maxLength !== undefined &&\n newValue.length > maxLength &&\n newValue.length > stateRef.current.value.length\n ) {\n return\n }\n stateRef.current.value = newValue\n stateRef.current.cursor = newCursor\n if (!isControlled) {\n setUncontrolledValue(newValue)\n }\n setCursorAndScroll(newCursor, newValue)\n onChange?.(newValue)\n yankStateRef.current = null\n },\n [isControlled, maxLength, onChange, setCursorAndScroll],\n )\n\n /** Get the selection range as [start, end), or null if no selection */\n const getSelectionRange = useCallback((): TextAreaSelection | null => {\n const anchor = selectionAnchorRef.current\n if (anchor === null) return null\n const cur = stateRef.current.cursor\n if (anchor === cur) return null\n return { start: Math.min(anchor, cur), end: Math.max(anchor, cur) }\n }, [])\n\n /** Delete the selected text and return the new value and cursor position */\n const deleteSelection = useCallback((): { newValue: string; newCursor: number } | null => {\n const sel = getSelectionRange()\n if (!sel) return null\n const v = stateRef.current.value\n return { newValue: v.slice(0, sel.start) + v.slice(sel.end), newCursor: sel.start }\n }, [getSelectionRange])\n\n /** Move cursor with shift: extends selection. Without shift: clears selection. */\n const moveCursor = useCallback(\n (newCursor: number, text: string, shift: boolean) => {\n if (shift) {\n // Start or extend selection\n if (selectionAnchorRef.current === null) {\n const anchor = stateRef.current.cursor\n setSelectionAnchor(anchor)\n selectionAnchorRef.current = anchor\n }\n } else {\n // Clear selection\n if (selectionAnchorRef.current !== null) {\n setSelectionAnchor(null)\n selectionAnchorRef.current = null\n }\n }\n setCursorAndScroll(newCursor, text)\n },\n [setCursorAndScroll],\n )\n\n /** Replace selection (if any) with new text, or insert at cursor */\n const replaceSelectionWith = useCallback(\n (insertText: string) => {\n const sel = getSelectionRange()\n const { value } = stateRef.current\n if (sel) {\n const newValue = value.slice(0, sel.start) + insertText + value.slice(sel.end)\n const newCursor = sel.start + insertText.length\n setSelectionAnchor(null)\n selectionAnchorRef.current = null\n updateValue(newValue, newCursor)\n return true\n }\n return false\n },\n [getSelectionRange, updateValue],\n )\n\n const wrappedLines = useMemo(() => getWrappedLines(value, wrapWidth), [value, wrapWidth])\n\n const { row: cursorRow, col: cursorCol } = useMemo(\n () => cursorToRowCol(value, clampedCursor, wrapWidth),\n [value, clampedCursor, wrapWidth],\n )\n\n // =========================================================================\n // Input handling\n // =========================================================================\n\n useInput(\n (input, key) => {\n if (disabled) return\n\n const { value, cursor } = stateRef.current\n const lines = getWrappedLines(value, wrapWidth)\n const { row: cRow, col: cCol } = cursorToRowCol(value, cursor, wrapWidth)\n const hasSelection =\n selectionAnchorRef.current !== null && selectionAnchorRef.current !== cursor\n\n // Helper: clear selection state\n const clearSelection = () => {\n if (selectionAnchorRef.current !== null) {\n setSelectionAnchor(null)\n selectionAnchorRef.current = null\n }\n }\n\n // =================================================================\n // Submit\n // =================================================================\n if (submitKey === \"ctrl+enter\" && key.return && key.ctrl) {\n onSubmit?.(value)\n return\n }\n if (submitKey === \"enter\" && key.return && !key.ctrl) {\n onSubmit?.(value)\n return\n }\n if (submitKey === \"meta+enter\" && key.return && key.meta) {\n onSubmit?.(value)\n return\n }\n\n // =================================================================\n // Ctrl+A: Select all\n // =================================================================\n if (key.ctrl && input === \"a\") {\n stickyXRef.current = null\n setSelectionAnchor(0)\n selectionAnchorRef.current = 0\n setCursorAndScroll(value.length, value)\n yankStateRef.current = null\n return\n }\n\n // =================================================================\n // Enter (newline) — replaces selection if active\n // =================================================================\n if (key.return && submitKey !== \"enter\") {\n stickyXRef.current = null\n if (hasSelection) {\n replaceSelectionWith(\"\\n\")\n } else {\n updateValue(value.slice(0, cursor) + \"\\n\" + value.slice(cursor), cursor + 1)\n }\n return\n }\n\n // =================================================================\n // Shift+Arrow: extend selection\n // Ctrl+Shift+Arrow: word-wise selection\n // =================================================================\n\n // Shift+Left / Ctrl+Shift+Left\n if (key.leftArrow && key.shift) {\n stickyXRef.current = null\n const target = key.ctrl ? findPrevWordStart(value, cursor) : Math.max(0, cursor - 1)\n moveCursor(target, value, true)\n yankStateRef.current = null\n return\n }\n\n // Shift+Right / Ctrl+Shift+Right\n if (key.rightArrow && key.shift) {\n stickyXRef.current = null\n const target = key.ctrl\n ? findNextWordEnd(value, cursor)\n : Math.min(value.length, cursor + 1)\n moveCursor(target, value, true)\n yankStateRef.current = null\n return\n }\n\n // Shift+Up\n if (key.upArrow && key.shift) {\n if (cRow > 0) {\n const targetX = stickyXRef.current ?? cCol\n stickyXRef.current = targetX\n const targetLine = lines[cRow - 1]\n if (targetLine) {\n moveCursor(\n targetLine.startOffset + Math.min(targetX, targetLine.line.length),\n value,\n true,\n )\n }\n }\n yankStateRef.current = null\n return\n }\n\n // Shift+Down\n if (key.downArrow && key.shift) {\n if (cRow < lines.length - 1) {\n const targetX = stickyXRef.current ?? cCol\n stickyXRef.current = targetX\n const targetLine = lines[cRow + 1]\n if (targetLine) {\n moveCursor(\n targetLine.startOffset + Math.min(targetX, targetLine.line.length),\n value,\n true,\n )\n }\n }\n yankStateRef.current = null\n return\n }\n\n // Shift+Home\n if (key.home && key.shift) {\n stickyXRef.current = null\n const currentLine = lines[cRow]\n if (currentLine) moveCursor(currentLine.startOffset, value, true)\n yankStateRef.current = null\n return\n }\n\n // Shift+End\n if (key.end && key.shift) {\n stickyXRef.current = null\n const currentLine = lines[cRow]\n if (currentLine) moveCursor(currentLine.startOffset + currentLine.line.length, value, true)\n yankStateRef.current = null\n return\n }\n\n // =================================================================\n // Multi-line: Up/Down with stickyX (non-shift: collapse selection)\n // =================================================================\n if (key.upArrow) {\n if (cRow > 0) {\n const targetX = stickyXRef.current ?? cCol\n stickyXRef.current = targetX\n const targetLine = lines[cRow - 1]\n if (targetLine) {\n moveCursor(\n targetLine.startOffset + Math.min(targetX, targetLine.line.length),\n value,\n false,\n )\n }\n } else {\n clearSelection()\n }\n yankStateRef.current = null\n return\n }\n\n if (key.downArrow) {\n if (cRow < lines.length - 1) {\n const targetX = stickyXRef.current ?? cCol\n stickyXRef.current = targetX\n const targetLine = lines[cRow + 1]\n if (targetLine) {\n moveCursor(\n targetLine.startOffset + Math.min(targetX, targetLine.line.length),\n value,\n false,\n )\n }\n } else {\n clearSelection()\n }\n yankStateRef.current = null\n return\n }\n\n // =================================================================\n // Multi-line: Ctrl+Home/End (document start/end)\n // =================================================================\n if (key.ctrl && key.home) {\n stickyXRef.current = null\n moveCursor(0, value, false)\n yankStateRef.current = null\n return\n }\n\n if (key.ctrl && key.end) {\n stickyXRef.current = null\n moveCursor(value.length, value, false)\n yankStateRef.current = null\n return\n }\n\n // =================================================================\n // Multi-line: Home/End (beginning/end of wrapped line)\n // Note: Ctrl+A is now select-all, Ctrl+E still goes to end of line\n // =================================================================\n if (key.home) {\n stickyXRef.current = null\n const currentLine = lines[cRow]\n if (currentLine) moveCursor(currentLine.startOffset, value, false)\n yankStateRef.current = null\n return\n }\n\n if (key.end || (key.ctrl && input === \"e\")) {\n stickyXRef.current = null\n const currentLine = lines[cRow]\n if (currentLine) moveCursor(currentLine.startOffset + currentLine.line.length, value, false)\n yankStateRef.current = null\n return\n }\n\n // =================================================================\n // Multi-line: PageUp/PageDown\n // =================================================================\n if (key.pageUp) {\n stickyXRef.current = null\n const targetLine = lines[Math.max(0, cRow - height)]\n if (targetLine) {\n moveCursor(targetLine.startOffset + Math.min(cCol, targetLine.line.length), value, false)\n }\n yankStateRef.current = null\n return\n }\n\n if (key.pageDown) {\n stickyXRef.current = null\n const targetLine = lines[Math.min(lines.length - 1, cRow + height)]\n if (targetLine) {\n moveCursor(targetLine.startOffset + Math.min(cCol, targetLine.line.length), value, false)\n }\n yankStateRef.current = null\n return\n }\n\n // =================================================================\n // Multi-line: Ctrl+K/U (kill to end/beginning of wrapped line)\n // =================================================================\n if (key.ctrl && input === \"k\") {\n stickyXRef.current = null\n clearSelection()\n const currentLine = lines[cRow]\n if (!currentLine) return\n const lineEnd = currentLine.startOffset + currentLine.line.length\n if (cursor < lineEnd) {\n addToKillRing(value.slice(cursor, lineEnd))\n updateValue(value.slice(0, cursor) + value.slice(lineEnd), cursor)\n } else if (cursor < value.length) {\n addToKillRing(value.slice(cursor, cursor + 1))\n updateValue(value.slice(0, cursor) + value.slice(cursor + 1), cursor)\n }\n return\n }\n\n if (key.ctrl && input === \"u\") {\n stickyXRef.current = null\n clearSelection()\n const currentLine = lines[cRow]\n if (!currentLine) return\n const lineStart = currentLine.startOffset\n if (cursor > lineStart) {\n addToKillRing(value.slice(lineStart, cursor))\n updateValue(value.slice(0, lineStart) + value.slice(cursor), lineStart)\n }\n return\n }\n\n // =================================================================\n // Backspace/Delete with selection: delete selection\n // =================================================================\n if ((key.backspace || key.delete) && hasSelection) {\n stickyXRef.current = null\n const del = deleteSelection()\n if (del) {\n clearSelection()\n updateValue(del.newValue, del.newCursor)\n }\n yankStateRef.current = null\n return\n }\n\n // =================================================================\n // Character input with selection: replace selection\n // =================================================================\n if (hasSelection && input.length >= 1 && input >= \" \") {\n stickyXRef.current = null\n replaceSelectionWith(input)\n yankStateRef.current = null\n return\n }\n\n // =================================================================\n // Shared readline operations (cursor, word, kill ring, yank, etc.)\n // Non-shift movement/editing clears selection.\n // =================================================================\n const result = handleReadlineKey(input, key, value, cursor, yankStateRef.current)\n if (result) {\n stickyXRef.current = null\n // Any readline operation clears selection\n clearSelection()\n if (result.value === value && result.cursor === cursor) {\n yankStateRef.current = result.yankState\n return\n }\n if (result.value !== value) {\n // Enforce maxLength for readline insertions\n if (\n maxLength !== undefined &&\n result.value.length > maxLength &&\n result.value.length > value.length\n ) {\n yankStateRef.current = result.yankState\n return\n }\n stateRef.current.value = result.value\n stateRef.current.cursor = result.cursor\n if (!isControlled) setUncontrolledValue(result.value)\n setCursorAndScroll(result.cursor, result.value)\n onChange?.(result.value)\n } else {\n setCursorAndScroll(result.cursor, value)\n }\n yankStateRef.current = result.yankState\n }\n },\n { isActive },\n )\n\n // =========================================================================\n // Computed output\n // =========================================================================\n\n const visibleCursorRow = cursorRow - scrollOffset\n const selection =\n selectionAnchor !== null && selectionAnchor !== clampedCursor\n ? {\n start: Math.min(selectionAnchor, clampedCursor),\n end: Math.max(selectionAnchor, clampedCursor),\n }\n : null\n\n const visibleLines = wrappedLines.slice(scrollOffset, scrollOffset + height)\n\n const clear = useCallback(() => {\n updateValue(\"\", 0)\n setScrollOffset(0)\n setSelectionAnchor(null)\n }, [updateValue])\n\n const setValue = useCallback(\n (v: string) => {\n updateValue(v, v.length)\n setSelectionAnchor(null)\n },\n [updateValue],\n )\n\n const setCursorFn = useCallback(\n (offset: number) => {\n const clamped = Math.min(Math.max(0, offset), stateRef.current.value.length)\n setSelectionAnchor(null)\n selectionAnchorRef.current = null\n setCursorAndScroll(clamped, stateRef.current.value)\n },\n [setCursorAndScroll],\n )\n\n return {\n value,\n cursor: clampedCursor,\n cursorRow,\n cursorCol,\n visibleCursorRow,\n scrollOffset,\n wrappedLines,\n visibleLines,\n selection,\n selectionAnchor,\n clear,\n setValue,\n getSelection: getSelectionRange,\n setCursor: setCursorFn,\n }\n}\n","/**\n * TextArea Component\n *\n * Multi-line text input with word wrapping, scrolling, and cursor movement.\n * Uses useBoxRect for width-aware word wrapping and VirtualList-style\n * scroll tracking to keep the cursor visible.\n *\n * Built on useTextArea hook — use the hook directly for custom rendering.\n *\n * Includes full readline-style editing: word movement, word kill, kill ring\n * (yank/cycle), and character transpose -- shared with TextInput via readline-ops.\n *\n * Usage:\n * ```tsx\n * const [value, setValue] = useState('')\n * <TextArea\n * value={value}\n * onChange={setValue}\n * onSubmit={(val) => console.log('Submitted:', val)}\n * height={10}\n * placeholder=\"Type here...\"\n * />\n * ```\n *\n * Supported shortcuts:\n * - Arrow keys: Move cursor (clears selection)\n * - Shift+Arrow: Extend selection\n * - Shift+Home/End: Select to line boundaries\n * - Ctrl+Shift+Arrow: Word-wise selection\n * - Ctrl+A: Select all text\n * - Ctrl+E: End of line\n * - Home/End: Beginning/end of line\n * - Alt+B/F: Move by word (wraps across lines)\n * - Ctrl+W, Alt+Backspace: Delete word backwards (kill ring)\n * - Alt+D: Delete word forwards (kill ring)\n * - Ctrl+K: Kill to end of line (kill ring)\n * - Ctrl+U: Kill to beginning of line (kill ring)\n * - Ctrl+Y: Yank (paste from kill ring)\n * - Alt+Y: Cycle through kill ring (after Ctrl+Y)\n * - Ctrl+T: Transpose characters\n * - PageUp/PageDown: Scroll by viewport height\n * - Backspace/Delete: Delete characters (or selected text)\n * - Enter: Insert newline (replaces selection, or submit with submitKey)\n * - Typing with selection: Replaces selected text\n */\nimport { forwardRef, useCallback, useImperativeHandle, useRef } from \"react\"\nimport { useBoxRect } from \"../../hooks/useLayout\"\nimport { useFocusable } from \"../../hooks/useFocusable\"\nimport { useCursor } from \"../../hooks/useCursor\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\nimport { useTextArea } from \"./useTextArea\"\nimport type { WrappedLine } from \"@silvery/create/text-cursor\"\nimport type { SilveryMouseEvent } from \"@silvery/ag-term/mouse-events\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface TextAreaProps {\n /** Current value (controlled) */\n value?: string\n /** Initial value (uncontrolled) */\n defaultValue?: string\n /** Called when value changes */\n onChange?: (value: string) => void\n /** Called on submit (Ctrl+Enter by default, or Enter if submitKey=\"enter\") */\n onSubmit?: (value: string) => void\n /** Key to trigger submit: \"ctrl+enter\" (default), \"enter\", or \"meta+enter\" */\n submitKey?: \"ctrl+enter\" | \"enter\" | \"meta+enter\"\n /** Placeholder text when empty */\n placeholder?: string\n /** Whether input is focused/active (overrides focus system) */\n isActive?: boolean\n /** Visible height in rows (required) */\n height: number\n /** Cursor style: 'block' (inverse) or 'underline' */\n cursorStyle?: \"block\" | \"underline\"\n /** Number of context lines to keep visible above/below cursor when scrolling (default: 1) */\n scrollMargin?: number\n /** When true, ignore all input and dim the text */\n disabled?: boolean\n /** Maximum number of characters allowed */\n maxLength?: number\n /** Border style (e.g., \"round\", \"single\") — wraps input in bordered Box */\n borderStyle?: string\n /** Border color when unfocused (default: \"$border-default\") */\n borderColor?: string\n /** Border color when focused (default: \"$border-focus\") */\n focusBorderColor?: string\n /** Test ID for focus system identification */\n testID?: string\n}\n\n/** Selection range as [start, end) character offsets */\nexport { type TextAreaSelection } from \"./useTextArea\"\n\nexport interface TextAreaHandle {\n /** Clear the input */\n clear: () => void\n /** Get current value */\n getValue: () => string\n /** Set value programmatically */\n setValue: (value: string) => void\n /** Get the current selection range, or null if no selection */\n getSelection: () => import(\"./useTextArea\").TextAreaSelection | null\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport const TextArea = forwardRef<TextAreaHandle, TextAreaProps>(function TextArea(\n {\n value: controlledValue,\n defaultValue = \"\",\n onChange,\n onSubmit,\n submitKey = \"ctrl+enter\",\n placeholder = \"\",\n isActive: isActiveProp,\n height,\n cursorStyle = \"block\",\n scrollMargin = 1,\n disabled,\n maxLength,\n borderStyle: borderStyleProp,\n borderColor: borderColorProp = \"$border-default\",\n focusBorderColor = \"$border-focus\",\n testID,\n },\n ref,\n) {\n // Focus system integration: prop overrides hook.\n // When testID is set, the component participates in the focus tree and\n // isActive derives from focus state. Without testID, default to true\n // for backward compatibility.\n const { focused } = useFocusable()\n const isActive = isActiveProp ?? (testID ? focused : true)\n\n const { width: parentWidth } = useBoxRect()\n\n // When borderStyle is set, TextArea renders a Box with border + paddingX.\n // useBoxRect reads from the parent's NodeContext, so we must subtract\n // border (1+1) and padding (1+1) to get the actual content area width.\n const contentWidth = borderStyleProp ? Math.max(1, parentWidth - 4) : parentWidth\n\n const ta = useTextArea({\n value: controlledValue,\n defaultValue,\n onChange,\n onSubmit,\n submitKey,\n isActive,\n height,\n wrapWidth: contentWidth,\n scrollMargin,\n disabled,\n maxLength,\n })\n\n // Imperative handle\n useImperativeHandle(ref, () => ({\n clear: ta.clear,\n getValue: () => ta.value,\n setValue: ta.setValue,\n getSelection: ta.getSelection,\n }))\n\n // Click-to-position: map mouse click to cursor offset\n const wrappedLinesRef = useRef<WrappedLine[]>(ta.wrappedLines)\n wrappedLinesRef.current = ta.wrappedLines\n const scrollOffsetRef = useRef(ta.scrollOffset)\n scrollOffsetRef.current = ta.scrollOffset\n\n const handleMouseDown = useCallback(\n (e: SilveryMouseEvent) => {\n if (e.button !== 0) return\n const rect = e.currentTarget.scrollRect\n if (!rect) return\n\n const lines = wrappedLinesRef.current\n const scroll = scrollOffsetRef.current\n\n const relativeY = e.clientY - rect.y\n const row = relativeY + scroll\n const clampedRow = Math.min(Math.max(0, row), lines.length - 1)\n const wl = lines[clampedRow]\n if (!wl) return\n\n const relativeX = e.clientX - rect.x\n const col = Math.min(Math.max(0, relativeX), wl.line.length)\n const offset = Math.min(Math.max(0, wl.startOffset + col), ta.value.length)\n ta.setCursor(offset)\n },\n [ta],\n )\n\n // =========================================================================\n // Rendering\n // =========================================================================\n\n const showPlaceholder = !ta.value && placeholder\n\n const borderProps = borderStyleProp\n ? {\n borderStyle: borderStyleProp as any,\n borderColor: isActive ? focusBorderColor : borderColorProp,\n paddingX: 1 as const,\n }\n : {}\n\n // Compute border+padding offset for cursor positioning.\n // useCursor reads scrollRect from the parent's NodeContext, but the text\n // content is rendered inside this component's Box (which may have border\n // and padding). We must add those offsets so the terminal cursor aligns\n // with the text content area.\n const borderColOffset = borderStyleProp ? 2 : 0 // border-left(1) + paddingX-left(1)\n const borderRowOffset = borderStyleProp ? 1 : 0 // border-top(1)\n\n // Hide hardware cursor when selection is active (cursor shown as part of selection rendering)\n useCursor({\n col: ta.cursorCol + borderColOffset,\n row: ta.visibleCursorRow + borderRowOffset,\n visible: isActive && !disabled && !ta.selection,\n })\n\n if (showPlaceholder) {\n return (\n <Box focusable testID={testID} flexDirection=\"column\" height={height} {...borderProps}>\n <Text color=\"$fg-muted\">{placeholder}</Text>\n </Box>\n )\n }\n\n return (\n <Box\n focusable\n testID={testID}\n key={ta.scrollOffset}\n flexDirection=\"column\"\n height={height}\n {...borderProps}\n onMouseDown={handleMouseDown}\n >\n {ta.visibleLines.map((wl, i) => {\n const absoluteRow = ta.scrollOffset + i\n const isCursorRow = absoluteRow === ta.cursorRow\n const lineStart = wl.startOffset\n const lineEnd = lineStart + wl.line.length\n\n // Check if this line has any selection overlap\n const hasSelectionOnLine =\n ta.selection && lineStart < ta.selection.end && lineEnd > ta.selection.start\n\n if (disabled) {\n return (\n <Text key={absoluteRow} color=\"$fg-muted\">\n {wl.line || \" \"}\n </Text>\n )\n }\n\n if (hasSelectionOnLine) {\n // Compute selection overlap on this line (in line-local coordinates)\n const selStart = Math.max(0, ta.selection!.start - lineStart)\n const selEnd = Math.min(wl.line.length, ta.selection!.end - lineStart)\n\n const before = wl.line.slice(0, selStart)\n const selected = wl.line.slice(selStart, selEnd)\n const after = wl.line.slice(selEnd)\n\n return (\n <Text key={absoluteRow}>\n {before}\n <Text inverse>\n {selected || (selEnd === wl.line.length && isCursorRow ? \" \" : \"\")}\n </Text>\n {after}\n </Text>\n )\n }\n\n if (!isCursorRow) {\n return <Text key={absoluteRow}>{wl.line || \" \"}</Text>\n }\n\n const beforeCursor = wl.line.slice(0, ta.cursorCol)\n const atCursor = wl.line[ta.cursorCol] ?? \" \"\n const afterCursor = wl.line.slice(ta.cursorCol + 1)\n\n // Active: plain text (real cursor handles it). Inactive: fake cursor.\n const cursorEl = isActive ? (\n <Text>{atCursor}</Text>\n ) : cursorStyle === \"block\" ? (\n <Text inverse>{atCursor}</Text>\n ) : (\n <Text underline>{atCursor}</Text>\n )\n\n return (\n <Text key={absoluteRow}>\n {beforeCursor}\n {cursorEl}\n {afterCursor}\n </Text>\n )\n })}\n </Box>\n )\n})\n","/**\n * EditContextDisplay Component\n *\n * Pure rendering component for multi-line text display with scrolling.\n * Consumes the output of useEditContext (value + cursor position) and\n * handles word wrapping, viewport scrolling, and cursor highlighting.\n *\n * Unlike TextArea, this component has NO input handling — the command\n * system handles all input via useEditContext's EditTarget. This is the\n * rendering half of the edit context pattern.\n *\n * Usage:\n * ```tsx\n * const { value, cursor } = useEditContext({ ... })\n * const { width } = useBoxRect()\n *\n * <EditContextDisplay\n * value={value}\n * cursor={cursor}\n * height={10}\n * wrapWidth={width}\n * />\n * ```\n *\n * Scroll logic extracted from TextArea.tsx — same clampScroll pattern\n * that keeps cursor visible within the viewport.\n */\nimport React, { useCallback, useMemo, useRef } from \"react\"\nimport { cursorToRowCol, getWrappedLines } from \"@silvery/create/text-cursor\"\nimport type { WrappedLine } from \"@silvery/create/text-cursor\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\nimport type { SilveryMouseEvent } from \"@silvery/ag-term/mouse-events\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface EditContextDisplayProps {\n /** Current text value (from useEditContext) */\n value: string\n /** Cursor position as character offset (from useEditContext) */\n cursor: number\n /** Visible height in rows. When omitted, renders all lines (no scrolling). */\n height?: number\n /** Width for word wrapping. When omitted, renders without wrapping. */\n wrapWidth?: number\n /** Cursor style: 'block' (inverse) or 'underline' */\n cursorStyle?: \"block\" | \"underline\"\n /** Placeholder text when value is empty */\n placeholder?: string\n /** Whether to show the cursor (default: true) */\n showCursor?: boolean\n /** Called when the user clicks on the text — provides the character offset at the click position */\n onCursorClick?: (offset: number) => void\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/** Ensure scroll offset keeps the cursor row visible within the viewport. */\nfunction clampScroll(cursorRow: number, currentScroll: number, viewportHeight: number): number {\n if (viewportHeight <= 0) return 0\n let scroll = currentScroll\n if (cursorRow < scroll) {\n scroll = cursorRow\n }\n if (cursorRow >= scroll + viewportHeight) {\n scroll = cursorRow - viewportHeight + 1\n }\n return Math.max(0, scroll)\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport function EditContextDisplay({\n value,\n cursor,\n height,\n wrapWidth,\n cursorStyle = \"block\",\n placeholder = \"\",\n showCursor = true,\n onCursorClick,\n}: EditContextDisplayProps): React.ReactElement {\n // Scroll offset persists across renders via ref. No useState needed because\n // every cursor/value change triggers a re-render from the parent (props change),\n // and we compute the new scroll synchronously during that render.\n const scrollRef = useRef(0)\n\n // Effective wrap width: use provided wrapWidth, or a large value for no wrapping\n const effectiveWrapWidth = wrapWidth != null && wrapWidth > 0 ? wrapWidth : 10000\n\n // Clamp cursor to valid range\n const clampedCursor = Math.min(Math.max(0, cursor), value.length)\n\n // Compute wrapped lines and cursor position\n const wrappedLines = useMemo(\n () => getWrappedLines(value, effectiveWrapWidth),\n [value, effectiveWrapWidth],\n )\n\n const { row: cursorRow, col: cursorCol } = useMemo(\n () => cursorToRowCol(value, clampedCursor, effectiveWrapWidth),\n [value, clampedCursor, effectiveWrapWidth],\n )\n\n // Update scroll offset to keep cursor visible (ref-only, no state)\n const hasViewport = height != null && height > 0\n if (hasViewport) {\n scrollRef.current = clampScroll(cursorRow, scrollRef.current, height)\n }\n\n // =========================================================================\n // Placeholder\n // =========================================================================\n\n if (!value && placeholder) {\n if (hasViewport) {\n return (\n <Box flexDirection=\"column\" height={height} justifyContent=\"center\" alignItems=\"center\">\n <Text color=\"$fg-muted\">{placeholder}</Text>\n </Box>\n )\n }\n return (\n <Box flexDirection=\"column\">\n <Text color=\"$fg-muted\">{placeholder}</Text>\n </Box>\n )\n }\n\n // =========================================================================\n // Determine visible lines\n // =========================================================================\n\n const currentScroll = hasViewport ? scrollRef.current : 0\n const visibleLines = hasViewport\n ? wrappedLines.slice(currentScroll, currentScroll + height)\n : wrappedLines\n\n // =========================================================================\n // Mouse click handler\n // =========================================================================\n\n // Keep refs for the click handler to avoid stale closures\n const wrappedLinesRef = useRef<WrappedLine[]>(wrappedLines)\n wrappedLinesRef.current = wrappedLines\n const scrollRefForClick = scrollRef\n\n const handleMouseDown = useCallback(\n (e: SilveryMouseEvent) => {\n if (!onCursorClick || e.button !== 0) return\n const rect = e.currentTarget.scrollRect\n if (!rect) return\n\n const lines = wrappedLinesRef.current\n const scroll = scrollRefForClick.current\n\n const relativeY = e.clientY - rect.y\n const row = relativeY + scroll\n const clampedRow = Math.min(Math.max(0, row), lines.length - 1)\n const wl = lines[clampedRow]\n if (!wl) return\n\n const relativeX = e.clientX - rect.x\n const col = Math.min(Math.max(0, relativeX), wl.line.length)\n const offset = Math.min(Math.max(0, wl.startOffset + col), value.length)\n onCursorClick(offset)\n },\n [onCursorClick, value.length],\n )\n\n // =========================================================================\n // Render\n // =========================================================================\n\n return (\n <Box\n key={currentScroll}\n flexDirection=\"column\"\n height={hasViewport ? height : undefined}\n onMouseDown={onCursorClick ? handleMouseDown : undefined}\n >\n {visibleLines.map((wl, i) => {\n const absoluteRow = currentScroll + i\n const isCursorRow = absoluteRow === cursorRow && showCursor\n\n if (!isCursorRow) {\n return <Text key={absoluteRow}>{wl.line || \" \"}</Text>\n }\n\n // Render line with cursor highlight\n const beforeCursorText = wl.line.slice(0, cursorCol)\n const atCursor = wl.line[cursorCol] ?? \" \"\n const afterCursorText = wl.line.slice(cursorCol + 1)\n\n return (\n <Text key={absoluteRow}>\n {beforeCursorText}\n {cursorStyle === \"block\" ? (\n <Text inverse>{atCursor}</Text>\n ) : (\n <Text underline>{atCursor}</Text>\n )}\n {afterCursorText}\n </Text>\n )\n })}\n </Box>\n )\n}\n","/**\n * CursorLine Component\n *\n * Renders a single line of text with a visible cursor at a split point.\n * Extracts the duplicated cursor-rendering pattern found across km-tui\n * (inline edit, input box, search bar, etc.) into a reusable primitive.\n *\n * Usage:\n * ```tsx\n * <CursorLine beforeCursor=\"hel\" afterCursor=\"lo world\" />\n * <CursorLine beforeCursor=\"full text\" afterCursor=\"\" />\n * <CursorLine beforeCursor=\"\" afterCursor=\"start\" cursorStyle=\"underline\" />\n * ```\n */\nimport React, { useCallback } from \"react\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\nimport type { SilveryMouseEvent } from \"@silvery/ag-term/mouse-events\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface CursorLineProps {\n /** Text before the cursor position */\n beforeCursor: string\n /** Text after the cursor position (first char gets cursor highlight) */\n afterCursor: string\n /** Text color */\n color?: string\n /** Whether to show the cursor (default: true) */\n showCursor?: boolean\n /** Cursor style: 'block' (inverse) or 'underline' (default: block) */\n cursorStyle?: \"block\" | \"underline\"\n /** Called when the user clicks on the text — provides the character offset at the click position */\n onCursorClick?: (offset: number) => void\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Renders a single line with a visible cursor character.\n *\n * The cursor character is `afterCursor[0]` (or a space when afterCursor is\n * empty, indicating the cursor is at the end of the text). The character is\n * rendered with inverse video (block) or underline styling.\n */\nexport function CursorLine({\n beforeCursor,\n afterCursor,\n color,\n showCursor = true,\n cursorStyle = \"block\",\n onCursorClick,\n}: CursorLineProps): React.ReactElement {\n const totalLength = beforeCursor.length + afterCursor.length\n\n const handleMouseDown = useCallback(\n (e: SilveryMouseEvent) => {\n if (!onCursorClick || e.button !== 0) return\n const rect = e.currentTarget.scrollRect\n if (!rect) return\n const relativeX = e.clientX - rect.x\n const offset = Math.min(Math.max(0, relativeX), totalLength)\n onCursorClick(offset)\n },\n [onCursorClick, totalLength],\n )\n\n const textContent = (() => {\n if (!showCursor)\n return (\n <Text color={color}>\n {beforeCursor}\n {afterCursor}\n </Text>\n )\n\n const cursorChar = afterCursor[0] ?? \" \"\n const rest = afterCursor.slice(1)\n\n return (\n <Text color={color}>\n {beforeCursor}\n {cursorStyle === \"block\" ? (\n <Text inverse>{cursorChar}</Text>\n ) : (\n <Text underline>{cursorChar}</Text>\n )}\n {rest}\n </Text>\n )\n })()\n\n if (onCursorClick) {\n return <Box onMouseDown={handleMouseDown}>{textContent}</Box>\n }\n return textContent\n}\n","/**\n * ModalDialog Component\n *\n * Reusable modal dialog with consistent styling: double border, title bar,\n * optional footer, and solid background that covers board content.\n *\n * Moved from km-tui shared-components to silvery for reuse across apps.\n *\n * Usage:\n * ```tsx\n * <ModalDialog title=\"Settings\" width={60} footer=\"ESC to close\">\n * <Text>Dialog content here</Text>\n * </ModalDialog>\n *\n * <ModalDialog title=\"Help\" hotkey=\"?\" titleRight={<Text>1/3</Text>}>\n * <Text>Help content</Text>\n * </ModalDialog>\n * ```\n */\nimport React from \"react\"\nimport { Box, type BoxProps } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ModalDialogProps extends Omit<BoxProps, \"children\" | \"flexDirection\"> {\n /** Border color (default: $border-default). Accent is reserved for text input focus rings. */\n borderColor?: string\n /** Dialog title (rendered bold in titleColor or borderColor) */\n title?: string\n /** Title color override (default: $fg-accent). Separate from border for independent styling. */\n titleColor?: string\n /** Title alignment (default: center) */\n titleAlign?: \"center\" | \"flex-start\" | \"flex-end\"\n /** Toggle hotkey character (e.g., \"?\" for help). Renders [X] prefix in title. */\n hotkey?: string\n /** Content to render on the right side of the title bar (e.g., hotkey indicator, match count) */\n titleRight?: React.ReactNode\n /** Dialog width. Defaults to \"snug-content\" (tightest fit around content). */\n width?: number | string\n /** Dialog height (optional, omit for auto-height) */\n height?: number\n /** Footer hint text (rendered dimColor at bottom) */\n footer?: React.ReactNode\n /** Footer alignment (default: center) */\n footerAlign?: \"center\" | \"flex-start\" | \"flex-end\"\n /** Called when ESC is pressed (optional convenience handler) */\n onClose?: () => void\n /** Whether to create a focus scope (default: true, for future focus system integration) */\n focusScope?: boolean\n /**\n * Backdrop fade amount — fades everything OUTSIDE this dialog's rect, making\n * the modal's content stand out visually. Range [0, 1]. Default: 0.25.\n *\n * Calibrated against real-world scrim conventions:\n * - macOS sheet backdrop ≈ 0.20\n * - iOS action-sheet scrim ≈ 0.40\n * - Material 3 scrim = 0.32\n *\n * 0.25 lands in the middle — the backdrop is visibly dimmed but the UI\n * behind stays readable. An earlier default of 0.7 drowned the scene\n * because the asymmetric blend math used uniform amounts for fg/bg; with\n * the asymmetric path removed (see `pipeline/backdrop/`), 0.7 is now truly\n * too strong. Apps that want a heavier dim can opt in with `fade={0.4}`.\n *\n * Applied at render time via a cell-level color transform (see\n * `@silvery/ag-term/pipeline/backdrop`). Set `fade={0}` to disable.\n */\n fade?: number\n /** Dialog children */\n children: React.ReactNode\n}\n\nconst DEFAULT_FADE = 0.25\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Format a dialog title with a hotkey prefix.\n *\n * If the hotkey letter appears in the title (case-insensitive), highlights it inline:\n * hotkey=\"D\", title=\"Details\" -> [D]etails\n * If the hotkey is not found in the title, prepends it:\n * hotkey=\"?\", title=\"Help\" -> [?] Help\n *\n * Brackets are dim, the hotkey letter is bold/bright.\n */\nexport function formatTitleWithHotkey(\n title: string,\n hotkey: string,\n color?: string,\n): React.ReactElement {\n const idx = title.toLowerCase().indexOf(hotkey.toLowerCase())\n if (idx >= 0 && hotkey.length === 1 && hotkey.toLowerCase() !== hotkey.toUpperCase()) {\n // Letter found in title — highlight it inline: prefix + [X] + rest\n const before = title.slice(0, idx)\n const matched = title[idx]\n const after = title.slice(idx + 1)\n return (\n <Text color={color} bold>\n {before}\n <Text color=\"$fg-muted\" bold={false}>\n [\n </Text>\n <Text bold>{matched}</Text>\n <Text color=\"$fg-muted\" bold={false}>\n ]\n </Text>\n {after}\n </Text>\n )\n }\n // Hotkey not in title (or symbol) — prepend [X] Title\n return (\n <Text color={color} bold>\n <Text color=\"$fg-muted\" bold={false}>\n [\n </Text>\n <Text bold>{hotkey}</Text>\n <Text color=\"$fg-muted\" bold={false}>\n ]\n </Text>{\" \"}\n {title}\n </Text>\n )\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Reusable modal dialog with consistent styling.\n *\n * Features:\n * - Solid raised background (covers board content)\n * - Double border (configurable color). Cyan reserved for focus rings.\n * - Horizontal padding (2), vertical padding (1)\n * - Title: bold, colored, with spacer below\n * - Footer: centered, dimColor, with spacer above\n */\nexport function ModalDialog({\n borderColor = \"$border-default\",\n title,\n titleColor,\n titleAlign = \"center\",\n hotkey,\n titleRight,\n width,\n height,\n footer,\n footerAlign = \"center\",\n onClose: _onClose,\n focusScope: _focusScope = true,\n fade = DEFAULT_FADE,\n children,\n ...boxProps\n}: ModalDialogProps): React.ReactElement {\n const effectiveTitleColor = titleColor ?? \"$fg-accent\"\n // When titleRight is provided, use space-between layout for the title bar\n const effectiveTitleAlign = titleRight ? \"space-between\" : titleAlign\n\n // Backdrop fade: emit `data-backdrop-fade-excluded` so the pipeline's\n // backdrop pass fades everything OUTSIDE this dialog's screen rect.\n // `fade={0}` disables the effect entirely (no marker emitted).\n const fadeAttrs: Record<string, unknown> =\n Number.isFinite(fade) && fade > 0 ? { \"data-backdrop-fade-excluded\": clampFade(fade) } : {}\n\n return (\n <Box\n flexDirection=\"column\"\n width={width ?? \"snug-content\"}\n height={height}\n borderStyle=\"double\"\n borderColor={borderColor}\n backgroundColor={\"$bg-surface-raised\"}\n paddingX={2}\n paddingY={1}\n userSelect=\"contain\"\n {...fadeAttrs}\n {...boxProps}\n >\n {title && (\n <Box flexShrink={0} flexDirection=\"column\">\n <Box justifyContent={effectiveTitleAlign}>\n {hotkey ? (\n formatTitleWithHotkey(title, hotkey, effectiveTitleColor)\n ) : (\n <Text color={effectiveTitleColor} bold>\n {title}\n </Text>\n )}\n {titleRight}\n </Box>\n <Text> </Text>\n </Box>\n )}\n {/* Content area - flexGrow pushes footer to bottom, overflow hidden prevents title displacement */}\n <Box flexDirection=\"column\" flexGrow={1} overflow=\"hidden\">\n {children}\n </Box>\n {/* Footer with spacer line above */}\n {footer && (\n <>\n <Text> </Text>\n <Box justifyContent={footerAlign}>\n {typeof footer === \"string\" ? <Text color=\"$fg-muted\">{footer}</Text> : footer}\n </Box>\n </>\n )}\n </Box>\n )\n}\n\nfunction clampFade(n: number): number {\n if (!Number.isFinite(n) || n <= 0) return 0\n return n > 1 ? 1 : n\n}\n","/**\n * Backdrop — render-time fade effect.\n *\n * Wraps content that should appear faded (pushed toward the background) while\n * a modal or drag overlay is active. This is a render-time cell transform —\n * analogous to CSS `backdrop-filter: opacity(...)`. The wrapped children\n * render normally; the renderer applies `blend(fg, bg, amount)` in OKLab on\n * every cell covered by this node's screen rect after the content phase.\n *\n * At the ANSI 16 tier, the fade degrades to SGR 2 (dim). At the `none`\n * (monochrome) tier, the fade is a no-op — separation comes from the modal's\n * border and box-drawing characters.\n *\n * Usage:\n * ```tsx\n * <Backdrop fade={0.5}>\n * <Board />\n * </Backdrop>\n * <DragGhost /> {/* crisp, on top, not wrapped *\\/}\n * ```\n *\n * For modals, prefer the `fade` prop on `ModalDialog` / `PickerDialog` — it\n * fades everything OUTSIDE the dialog (via `data-backdrop-fade-excluded`)\n * without wrapping siblings.\n */\nimport React from \"react\"\nimport { Box, type BoxProps } from \"../../components/Box\"\n\nexport interface BackdropProps extends Omit<BoxProps, \"children\"> {\n /**\n * Fade amount in [0, 1]. 0 = crisp (no fade), 1 = fully blended into bg\n * (fg == bg, effectively invisible text). Default: 0.25.\n *\n * Calibrated to match scrim conventions (macOS ~0.20, Material 3 = 0.32).\n * Apps that want a heavier dim can opt in with `fade={0.4}` or higher.\n *\n * Interpreted by the `pipeline/backdrop` pass after content rendering — the\n * fade is NOT applied in React; it's a cell-level transform on the finished\n * buffer. Fade propagates through the subtree via the node's screen rect.\n */\n fade?: number\n /** Children to wrap. */\n children: React.ReactNode\n}\n\n/**\n * Wrap content in a render-time fade region. See {@link BackdropProps.fade}.\n *\n * `fade={0}` is a passthrough — no data attribute is emitted, no pass work.\n */\nexport function Backdrop({\n fade = 0.25,\n children,\n ...boxProps\n}: BackdropProps): React.ReactElement {\n const clamped = clamp01(fade)\n const attrs: Record<string, unknown> = clamped > 0 ? { \"data-backdrop-fade\": clamped } : {}\n return (\n <Box {...boxProps} {...attrs}>\n {children}\n </Box>\n )\n}\n\nfunction clamp01(n: number): number {\n if (!Number.isFinite(n)) return 0\n if (n < 0) return 0\n if (n > 1) return 1\n return n\n}\n","/**\n * PickerList Component\n *\n * Thin composition over ListView — scrolling result list with selection\n * highlighting. Extracted from PickerDialog so it can be composed\n * independently by callers that manage their own input (e.g., km-tui\n * command-system dialogs).\n *\n * Handles:\n * - Scroll offset calculation (centers selected item in view) — via ListView\n * - Visible items slicing — via ListView\n * - Empty state rendering\n * - Item rendering via renderItem callback\n *\n * Does NOT handle:\n * - Keyboard navigation (caller manages selectedIndex)\n * - Input/search (caller's responsibility)\n *\n * Usage:\n * ```tsx\n * <PickerList\n * items={filteredResults}\n * selectedIndex={selected}\n * renderItem={(item, sel) => <Text inverse={sel}>{item.name}</Text>}\n * getKey={(item) => item.id}\n * />\n * ```\n */\nimport React from \"react\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\nimport { ListView } from \"./ListView\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface PickerListProps<T> {\n /** Items to display */\n items: T[]\n /** Currently selected index (caller-managed) */\n selectedIndex: number\n /** Render function for each item. `selected` is true for the highlighted item. */\n renderItem: (item: T, selected: boolean) => React.ReactNode\n /** Unique key for each item */\n getKey: (item: T) => string\n /** Message when items list is empty (default: \"No items\") */\n emptyMessage?: string\n /** Maximum visible items before scrolling (default: 10) */\n maxVisible?: number\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Scrolling result list with selection highlighting.\n *\n * Delegates to ListView for virtualization, scroll offset, and viewport\n * management. When there are fewer items than maxVisible, all items are\n * shown without scrolling.\n */\nexport function PickerList<T>({\n items,\n selectedIndex,\n renderItem,\n getKey,\n emptyMessage = \"No items\",\n maxVisible = 10,\n}: PickerListProps<T>): React.ReactElement {\n if (items.length === 0) {\n return (\n <Box flexDirection=\"column\" flexGrow={1} flexShrink={1} overflow=\"hidden\">\n <Text color=\"$fg-muted\">{emptyMessage}</Text>\n </Box>\n )\n }\n\n const clampedIndex = Math.min(selectedIndex, items.length - 1)\n const effectiveHeight = Math.min(maxVisible, items.length)\n\n return (\n <Box flexDirection=\"column\" flexGrow={1} flexShrink={1} overflow=\"hidden\">\n <ListView\n items={items}\n height={effectiveHeight}\n nav\n active={false}\n cursorKey={clampedIndex}\n getKey={(item) => getKey(item)}\n renderItem={(item, _index, meta) => renderItem(item, meta.isCursor)}\n />\n </Box>\n )\n}\n","/**\n * PickerDialog Component\n *\n * Generic search-and-select dialog combining ModalDialog + text input + scrolling\n * result list. Handles keyboard routing: arrows for selection, Enter to confirm,\n * Esc to cancel, printable chars for filtering.\n *\n * Uses useReadline internally for full readline editing (kill ring, word movement).\n *\n * Usage:\n * ```tsx\n * <PickerDialog\n * title=\"Search\"\n * items={filteredResults}\n * renderItem={(item, selected) => (\n * <Text inverse={selected}>{item.name}</Text>\n * )}\n * getKey={(item) => item.id}\n * onSelect={(item) => navigateTo(item)}\n * onCancel={() => closeDialog()}\n * onChange={(query) => setFilter(query)}\n * placeholder=\"Type to search...\"\n * />\n * ```\n */\nimport React, { useCallback, useRef, useState } from \"react\"\nimport { useInput } from \"../../hooks/useInput\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\nimport { CursorLine } from \"./CursorLine\"\nimport { ModalDialog } from \"./ModalDialog\"\nimport { PickerList } from \"./PickerList\"\nimport { useReadline } from \"./useReadline\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface PickerDialogProps<T> {\n /** Dialog title */\n title: string\n /** Placeholder text when input is empty */\n placeholder?: string\n /** Items to display in the result list */\n items: T[]\n /** Render function for each item. `selected` is true for the highlighted item. */\n renderItem: (item: T, selected: boolean) => React.ReactNode\n /** Unique key for each item */\n getKey: (item: T) => string\n /** Called when an item is confirmed (Enter) */\n onSelect: (item: T) => void\n /** Called when the dialog is cancelled (Esc) */\n onCancel: () => void\n /** Called when the input text changes (for filtering) */\n onChange?: (query: string) => void\n /** Initial input value */\n initialValue?: string\n /** Message when items list is empty */\n emptyMessage?: string\n /** Maximum visible items before scrolling (default: 10) */\n maxVisible?: number\n /** Dialog width */\n width?: number\n /** Dialog height (auto-sized if omitted) */\n height?: number\n /** Footer content */\n footer?: React.ReactNode\n /** Input prompt prefix (e.g., \"/ \" or \"All > \") */\n prompt?: string\n /** Prompt color */\n promptColor?: string\n /** Whether the input is active (default: true) */\n isActive?: boolean\n /**\n * Backdrop fade amount for the underlying `ModalDialog`. Range [0, 1].\n * Default is the ModalDialog default (0.4). `fade={0}` disables fade.\n */\n fade?: number\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Generic search-and-select dialog.\n *\n * Keyboard routing:\n * - Printable chars, Ctrl shortcuts: readline text editing\n * - Up/Down arrows: navigate result list\n * - PgUp/PgDn: scroll by page\n * - Enter: confirm selected item\n * - Esc: cancel dialog\n */\nexport function PickerDialog<T>({\n title,\n placeholder,\n items,\n renderItem,\n getKey,\n onSelect,\n onCancel,\n onChange,\n initialValue = \"\",\n emptyMessage = \"No items\",\n maxVisible = 10,\n width,\n height,\n footer,\n prompt,\n promptColor,\n isActive = true,\n fade,\n}: PickerDialogProps<T>): React.ReactElement {\n const [selectedIndex, setSelectedIndex] = useState(0)\n\n // Refs for stable callbacks in useInput closures\n const onSelectRef = useRef(onSelect)\n onSelectRef.current = onSelect\n const onCancelRef = useRef(onCancel)\n onCancelRef.current = onCancel\n const itemsRef = useRef(items)\n itemsRef.current = items\n const selectedIndexRef = useRef(selectedIndex)\n selectedIndexRef.current = selectedIndex\n\n // Readline hook for text editing (kill ring, word movement, etc.)\n const readline = useReadline({\n initialValue,\n onChange: useCallback(\n (value: string) => {\n onChange?.(value)\n setSelectedIndex(0)\n },\n [onChange],\n ),\n isActive,\n handleEnter: false, // We handle Enter for item selection\n handleEscape: false, // We handle Esc for cancel\n handleVerticalArrows: false, // We handle Up/Down for list navigation\n })\n\n const clampedIndex = items.length > 0 ? Math.min(selectedIndex, items.length - 1) : 0\n if (clampedIndex !== selectedIndex) {\n setSelectedIndex(clampedIndex)\n }\n\n // Effective max visible for page navigation step size\n const effectiveMaxVisible = Math.min(maxVisible, items.length)\n\n // Navigation handler (separate from readline text editing)\n useInput(\n (_input, key) => {\n if (key.escape) {\n onCancelRef.current()\n return\n }\n if (key.return) {\n const currentItems = itemsRef.current\n const idx = selectedIndexRef.current\n const item = currentItems[Math.min(idx, currentItems.length - 1)]\n if (item) onSelectRef.current(item)\n return\n }\n if (key.upArrow) {\n setSelectedIndex((i) => Math.max(0, i - 1))\n return\n }\n if (key.downArrow) {\n setSelectedIndex((i) => Math.min(i + 1, Math.max(0, itemsRef.current.length - 1)))\n return\n }\n if (key.pageUp) {\n setSelectedIndex((i) => Math.max(0, i - effectiveMaxVisible))\n return\n }\n if (key.pageDown) {\n setSelectedIndex((i) =>\n Math.min(i + effectiveMaxVisible, Math.max(0, itemsRef.current.length - 1)),\n )\n return\n }\n },\n { isActive },\n )\n\n // Show placeholder when input is empty\n const showPlaceholder = !readline.value && placeholder\n\n return (\n <ModalDialog\n title={title}\n width={width}\n height={height}\n footer={footer}\n {...(fade !== undefined ? { fade } : {})}\n >\n {/* Search input */}\n <Box flexShrink={0} flexDirection=\"column\">\n <Box>\n {prompt && <Text color={promptColor}>{prompt}</Text>}\n {showPlaceholder ? (\n <Text color=\"$fg-muted\">{placeholder}</Text>\n ) : (\n <CursorLine\n beforeCursor={readline.beforeCursor}\n afterCursor={readline.afterCursor}\n showCursor={isActive}\n />\n )}\n </Box>\n <Text color=\"$border-default\">{\"─\".repeat(40)}</Text>\n </Box>\n\n {/* Result list (delegated to PickerList) */}\n <PickerList\n items={items}\n selectedIndex={clampedIndex}\n renderItem={renderItem}\n getKey={getKey}\n emptyMessage={emptyMessage}\n maxVisible={maxVisible}\n />\n </ModalDialog>\n )\n}\n","/**\n * Toggle Component\n *\n * A focusable checkbox-style toggle control. Integrates with the silvery focus\n * system and responds to Space key to toggle the value.\n *\n * Usage:\n * ```tsx\n * const [enabled, setEnabled] = useState(false)\n * <Toggle value={enabled} onChange={setEnabled} label=\"Dark mode\" />\n *\n * // With explicit active control (bypasses focus system)\n * <Toggle value={on} onChange={setOn} label=\"Option\" isActive={isEditing} />\n * ```\n */\nimport React from \"react\"\nimport { useFocusable } from \"../../hooks/useFocusable\"\nimport { useInput } from \"../../hooks/useInput\"\nimport { Box } from \"../../components/Box\"\nimport type { BoxProps } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ToggleProps extends Omit<BoxProps, \"children\" | \"onChange\"> {\n /** Whether the toggle is on */\n value: boolean\n /** Called when value changes */\n onChange: (value: boolean) => void\n /** Label text */\n label?: string\n /** Whether input is active (default: from focus system) */\n isActive?: boolean\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Focusable toggle (checkbox) control.\n *\n * Renders `[x]` when on, `[ ]` when off. When focused, the checkbox indicator\n * is rendered with inverse styling for visibility.\n */\nexport function Toggle({\n value,\n onChange,\n label,\n isActive,\n ...rest\n}: ToggleProps): React.ReactElement {\n const { focused } = useFocusable()\n\n // isActive prop overrides focus state (same pattern as TextInput)\n const active = isActive ?? focused\n\n useInput(\n (_input, key) => {\n // Space toggles the value\n if (_input === \" \" && !key.ctrl && !key.meta && !key.shift) {\n onChange(!value)\n }\n },\n { isActive: active },\n )\n\n const indicator = value ? \"[x]\" : \"[ ]\"\n\n return (\n <Box focusable {...rest}>\n <Text inverse={active}>{indicator}</Text>\n {label && <Text> {label}</Text>}\n </Box>\n )\n}\n","/**\n * Tone — shared primitive for Sterling-tone-bearing components.\n *\n * The `tone` axis is Sterling's status vocabulary + a component-layer\n * `destructive` intent alias. See hub/silvery/design/v10-terminal/design-system.md\n * §\"Intent vs role\" and sterling-preflight.md D1 — `destructive` lives at the\n * component layer (not the Theme) to prevent palette sprawl.\n *\n * Consumers: Button, Alert, Banner, InlineAlert (and Badge/Toast, which have\n * their own legacy-compatible variants on top of this surface).\n *\n * All helpers return Sterling flat tokens (`$fg-error`, `$bg-warning-subtle`,\n * etc.) — the tokens are populated by `@silvery/design` and resolved by the\n * theme at render time.\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Canonical tone axis — 5 Sterling status roles plus the `destructive`\n * component-layer intent alias. `accent` is the default \"primary\" emphasis.\n */\nexport type ToneKey = \"accent\" | \"error\" | \"warning\" | \"success\" | \"info\" | \"destructive\"\n\n// =============================================================================\n// Resolver\n// =============================================================================\n\n/**\n * `destructive` aliases to `error` per D1 — the Theme has no `destructive`\n * field. This resolver lives at the component layer so apps can write\n * `tone=\"destructive\"` for action-intent components and `tone=\"error\"` for\n * status components, both hitting the same pixels by default.\n */\nfunction resolveRole(tone: ToneKey): \"accent\" | \"error\" | \"warning\" | \"success\" | \"info\" {\n return tone === \"destructive\" ? \"error\" : tone\n}\n\n/**\n * Tone → Sterling flat-token mapping for fills (button backgrounds, filled\n * alert surfaces). Returns an object because callers usually need the paired\n * foreground, hover, and active tokens together — grouping them here keeps\n * the mapping DRY across components.\n */\nexport interface ToneFillTokens {\n /** Background fill (`$bg-<role>`). */\n bg: string\n /** Foreground on the filled background (`$fg-on-<role>`). */\n fgOn: string\n /** Hover-state fill (`$bg-<role>-hover`). */\n bgHover: string\n /** Active/pressed-state fill (`$bg-<role>-active`). */\n bgActive: string\n}\n\n/**\n * Get the full fill-token set for a tone. Used by `<Button>` and `<Alert>`\n * where the surface is filled with the tone color and foreground text sits\n * on top.\n */\nexport function toneFillTokens(tone: ToneKey): ToneFillTokens {\n const role = resolveRole(tone)\n return {\n bg: `$bg-${role}`,\n fgOn: `$fg-on-${role}`,\n bgHover: `$bg-${role}-hover`,\n bgActive: `$bg-${role}-active`,\n }\n}\n\n/**\n * Get the foreground-only token for a tone. Used by `<InlineAlert>` where\n * only the text color carries the tone (no bg fill).\n */\nexport function toneFgToken(tone: ToneKey): string {\n const role = resolveRole(tone)\n return `$fg-${role}`\n}\n\n/**\n * Get the subtle-surface token pair for a tone. Used by `<Banner>` where\n * the surface is tinted (not filled) so content stays legible without the\n * high-contrast \"on-role\" fg token.\n */\nexport interface ToneSubtleTokens {\n /** Tinted surface (`$bg-<role>-subtle`). */\n bg: string\n /** Foreground that reads well on the tinted surface (`$fg-<role>`). */\n fg: string\n}\n\nexport function toneSubtleTokens(tone: ToneKey): ToneSubtleTokens {\n const role = resolveRole(tone)\n return {\n bg: `$bg-${role}-subtle`,\n fg: `$fg-${role}`,\n }\n}\n\n/**\n * Single-character ASCII glyph conventionally associated with each tone.\n * Shared with Toast's existing mapping so Alert-family components render\n * consistent icons without each component inventing its own set.\n */\nexport const TONE_ICONS: Record<ToneKey, string> = {\n accent: \"*\",\n error: \"x\",\n destructive: \"x\",\n warning: \"!\",\n success: \"+\",\n info: \"i\",\n}\n\nexport function toneIcon(tone: ToneKey): string {\n return TONE_ICONS[tone]\n}\n","/**\n * Button Component\n *\n * A focusable button control. Integrates with the silvery focus system\n * and responds to Enter or Space key to activate.\n *\n * Sterling tone surface (Phase 2b): accepts a `tone` prop that resolves to\n * Sterling flat tokens (`$bg-<role>`, `$fg-on-<role>`). `destructive` is a\n * component-layer intent alias for `error` (Sterling preflight D1) — same\n * pixels by default, semantic correctness at the call site.\n *\n * Usage:\n * ```tsx\n * <Button label=\"Save\" onPress={() => save()} /> // tone=\"accent\"\n * <Button label=\"Delete\" tone=\"destructive\" onPress={remove} /> // intent\n * <Button label=\"Retry\" tone=\"warning\" onPress={retry} />\n * <Button label=\"OK\" tone=\"accent\" isActive={hasFocus} /> // explicit active\n * <Button label=\"Legacy\" color=\"red\" onPress={...} /> // raw color still works\n * ```\n *\n * `color` remains supported for callers that need a raw palette entry; `tone`\n * takes precedence when both are set, matching the Badge/Toast pattern.\n */\nimport React from \"react\"\nimport { useFocusable } from \"../../hooks/useFocusable\"\nimport { useInput } from \"../../hooks/useInput\"\nimport { Box } from \"../../components/Box\"\nimport type { BoxProps } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\nimport { type ToneKey, toneFillTokens } from \"./_tone\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ButtonProps extends Omit<BoxProps, \"children\"> {\n /** Button label */\n label: string\n /** Called when activated (Enter or Space) */\n onPress: () => void\n /** Whether input is active (default: from focus system) */\n isActive?: boolean\n /**\n * Sterling tone. Accepts status roles (`error` / `warning` / `success` /\n * `info`), the `accent` emphasis role, and the `destructive` intent alias\n * (resolves to `error`). Defaults to `accent` — the standard primary button.\n *\n * When both `tone` and `color` are set, `tone` wins.\n */\n tone?: ToneKey\n /**\n * Raw color override. Kept for backward compatibility with callers that\n * reach around the tone axis (e.g. per-category coloring). Ignored when\n * `tone` is set.\n */\n color?: string\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Focusable button control.\n *\n * Renders `[ label ]` with tone-resolved background fill and inverse-mapped\n * foreground when focused. Activates on Enter or Space key press.\n */\nexport function Button({\n label,\n onPress,\n isActive,\n tone,\n color,\n ...rest\n}: ButtonProps): React.ReactElement {\n const { focused } = useFocusable()\n\n // isActive prop overrides focus state (same pattern as TextInput)\n const active = isActive ?? focused\n\n useInput(\n (_input, key) => {\n if (key.return || (_input === \" \" && !key.ctrl && !key.meta && !key.shift)) {\n onPress()\n }\n },\n { isActive: active },\n )\n\n // Resolve tone → Sterling flat tokens. `tone` wins over `color`; `color`\n // stays as the escape hatch for callers that want a raw palette entry.\n const effectiveTone: ToneKey = tone ?? (color ? (\"accent\" as const) : (\"accent\" as const))\n const tokens = toneFillTokens(effectiveTone)\n\n // When a raw `color` is supplied AND no `tone` is set, fall back to the\n // legacy single-color rendering (no bg fill) — preserves existing callers.\n const legacyColorMode = color !== undefined && tone === undefined\n\n if (legacyColorMode) {\n return (\n <Box focusable {...rest}>\n <Text color={color} inverse={active}>\n {\"[ \"}\n {label}\n {\" ]\"}\n </Text>\n </Box>\n )\n }\n\n // Tone-driven rendering: fill background with `$bg-<role>` and use\n // `$fg-on-<role>` text. When active, swap to the `-active` bg so the\n // press state reads as a visible affordance.\n const bg = active ? tokens.bgActive : tokens.bg\n return (\n <Box focusable backgroundColor={bg} {...rest}>\n <Text color={tokens.fgOn} bold>\n {\"[ \"}\n {label}\n {\" ]\"}\n </Text>\n </Box>\n )\n}\n","/**\n * SearchBar — renders the search bar UI when search is active.\n *\n * Displays the query, match count, and navigation hints at the bottom\n * of the screen. Uses the SearchProvider context for state.\n *\n * Usage:\n * ```tsx\n * <SearchProvider>\n * <App />\n * <SearchBar />\n * </SearchProvider>\n * ```\n */\n\nimport React from \"react\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\nimport { useSearch } from \"../../providers/SearchProvider\"\nimport type { ReactElement } from \"react\"\n\nexport function SearchBar(): ReactElement | null {\n const { isActive, query, matches, currentMatch } = useSearch()\n\n if (!isActive) return null\n\n const matchInfo =\n matches.length > 0 ? `[${currentMatch + 1}/${matches.length}]` : query ? \"[no matches]\" : \"\"\n\n return React.createElement(\n Box,\n { flexDirection: \"row\" },\n React.createElement(Text, { inverse: true }, ` / ${query} ${matchInfo} `),\n )\n}\n","/**\n * Spinner Component\n *\n * An animated loading spinner with multiple built-in styles.\n *\n * Usage:\n * ```tsx\n * <Spinner />\n * <Spinner type=\"arc\" label=\"Loading...\" />\n * <Spinner type=\"bounce\" interval={120} />\n * ```\n */\nimport React, { useEffect, useState } from \"react\"\nimport { Text } from \"../../components/Text\"\nimport type { TextProps } from \"../../components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SpinnerProps extends Omit<TextProps, \"children\"> {\n /** Spinner style preset */\n type?: \"dots\" | \"line\" | \"arc\" | \"bounce\"\n /** Label text shown after spinner */\n label?: string\n /** Animation interval in ms (default: 80) */\n interval?: number\n}\n\n// =============================================================================\n// Frame Sequences\n// =============================================================================\n\nconst FRAMES: Record<NonNullable<SpinnerProps[\"type\"]>, readonly string[]> = {\n dots: [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"],\n line: [\"|\", \"/\", \"—\", \"\\\\\"],\n arc: [\"◜\", \"◠\", \"◝\", \"◞\", \"◡\", \"◟\"],\n bounce: [\"⠁\", \"⠂\", \"⠄\", \"⡀\", \"⢀\", \"⠠\", \"⠐\", \"⠈\"],\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport function Spinner({\n type = \"dots\",\n label,\n interval = 80,\n ...rest\n}: SpinnerProps): React.ReactElement {\n const [frameIndex, setFrameIndex] = useState(0)\n const frames = FRAMES[type]\n\n useEffect(() => {\n const timer = setInterval(() => {\n setFrameIndex((prev) => (prev + 1) % frames.length)\n }, interval)\n\n return () => clearInterval(timer)\n }, [frames.length, interval])\n\n const frame = frames[frameIndex % frames.length]!\n\n return (\n <Text {...rest}>\n {frame}\n {label ? ` ${label}` : \"\"}\n </Text>\n )\n}\n","/**\n * ProgressBar Component\n *\n * A terminal progress bar with determinate and indeterminate modes.\n *\n * Usage:\n * ```tsx\n * <ProgressBar value={0.5} />\n * <ProgressBar value={0.75} color=\"green\" label=\"Downloading...\" />\n * <ProgressBar /> // indeterminate (animated)\n * ```\n */\nimport React, { useEffect, useState } from \"react\"\nimport { useBoxRect } from \"../../hooks/useLayout\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ProgressBarProps {\n /** Progress value 0-1 (omit for indeterminate) */\n value?: number\n /** Width in columns (default: uses available width via useBoxRect) */\n width?: number\n /** Fill character (default: \"█\") */\n fillChar?: string\n /** Empty character (default: \"░\") */\n emptyChar?: string\n /** Show percentage label (default: true for determinate) */\n showPercentage?: boolean\n /** Label text */\n label?: string\n /** Color of the filled portion */\n color?: string\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst DEFAULT_FILL = \"█\"\nconst DEFAULT_EMPTY = \"░\"\nconst DEFAULT_WIDTH = 30\nconst INDETERMINATE_BLOCK_SIZE = 4\nconst INDETERMINATE_INTERVAL = 100\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport function ProgressBar({\n value,\n width: widthProp,\n fillChar = DEFAULT_FILL,\n emptyChar = DEFAULT_EMPTY,\n showPercentage,\n label,\n color,\n}: ProgressBarProps): React.ReactElement {\n // Only use layout feedback when width isn't explicitly provided\n const layoutRect = useBoxRect()\n const contentWidth = widthProp ? 0 : layoutRect.width\n const [bouncePos, setBouncePos] = useState(0)\n const [bounceDir, setBounceDir] = useState(1)\n\n const isDeterminate = value !== undefined\n const showPct = showPercentage ?? isDeterminate\n\n // Calculate available bar width\n const labelWidth = label ? label.length + 1 : 0\n const pctWidth = showPct ? 5 : 0 // \" 100%\"\n const availableWidth = widthProp ?? (contentWidth > 0 ? contentWidth : DEFAULT_WIDTH)\n const barWidth = Math.max(1, availableWidth - labelWidth - pctWidth)\n\n // Indeterminate animation\n useEffect(() => {\n if (isDeterminate) return\n\n const timer = setInterval(() => {\n setBouncePos((prev) => {\n const maxPos = barWidth - INDETERMINATE_BLOCK_SIZE\n if (maxPos <= 0) return 0\n\n const next = prev + bounceDir\n if (next >= maxPos) {\n setBounceDir(-1)\n return maxPos\n }\n if (next <= 0) {\n setBounceDir(1)\n return 0\n }\n return next\n })\n }, INDETERMINATE_INTERVAL)\n\n return () => clearInterval(timer)\n }, [isDeterminate, barWidth, bounceDir])\n\n let filledPart: string\n let emptyPart: string\n\n if (isDeterminate) {\n const clamped = Math.max(0, Math.min(1, value))\n const filled = Math.round(clamped * barWidth)\n filledPart = fillChar.repeat(filled)\n emptyPart = emptyChar.repeat(barWidth - filled)\n } else {\n // Indeterminate: sliding block\n const blockSize = Math.min(INDETERMINATE_BLOCK_SIZE, barWidth)\n const pos = Math.max(0, Math.min(bouncePos, barWidth - blockSize))\n filledPart = emptyChar.repeat(pos) + fillChar.repeat(blockSize)\n emptyPart = emptyChar.repeat(barWidth - pos - blockSize)\n }\n\n const pct = isDeterminate ? Math.round(Math.max(0, Math.min(1, value)) * 100) : 0\n\n return (\n <Box>\n {label && <Text>{label} </Text>}\n <Text color={color}>{filledPart}</Text>\n <Text color=\"$fg-muted\">{emptyPart}</Text>\n {showPct && <Text>{String(pct).padStart(4)}%</Text>}\n </Box>\n )\n}\n","/**\n * SelectList Component\n *\n * A keyboard-navigable single-select list. Thin composition over ListView\n * that adds disabled-item skipping, indicator styling, and onChange shorthand.\n *\n * Inherits from ListView: keyboard nav (j/k, arrows, PgUp/PgDn, Home/End, G),\n * mouse wheel, virtualised scrolling, cache, and search.\n *\n * Usage:\n * ```tsx\n * const items = [\n * { label: \"Apple\", value: \"apple\" },\n * { label: \"Banana\", value: \"banana\" },\n * { label: \"Cherry\", value: \"cherry\", disabled: true },\n * ]\n *\n * <SelectList items={items} onSelect={(opt) => console.log(opt.value)} />\n * ```\n */\nimport React, { useCallback, useState } from \"react\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\nimport { ListView } from \"./ListView\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SelectOption {\n label: string\n value: string\n disabled?: boolean\n}\n\nexport interface SelectListProps {\n /** List of options */\n items: SelectOption[]\n /** Controlled: current highlighted index */\n highlightedIndex?: number\n /** Called when highlight changes */\n onHighlight?: (index: number) => void\n /** Called when user confirms selection (Enter) */\n onSelect?: (option: SelectOption, index: number) => void\n /** Initial index for uncontrolled mode */\n initialIndex?: number\n /** Max visible items (rest scrolled) */\n maxVisible?: number\n /** Whether this list captures input (default: true) */\n isActive?: boolean\n /**\n * Selection indicator prefix shown on highlighted item (default: \"\").\n * When empty (default), the cursor row is communicated via full-row background\n * color ($cursor-bg / $cursor fg) — the omnibox-style UX.\n * Pass \"▸ \" (or any string) to use an arrow/glyph indicator instead of row bg.\n * Non-highlighted items then get equal-width spaces for alignment.\n */\n indicator?: string\n /**\n * Called when mouse enters an item row. Defaults to moving the keyboard cursor\n * to that row (hover-to-focus). Override to suppress or replace this behavior.\n */\n onItemHover?: (index: number) => void\n /**\n * Called when an item row is clicked. Defaults to moving the cursor + firing\n * onSelect (click-to-confirm). Override to replace this behavior.\n */\n onItemClick?: (index: number) => void\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction findNextEnabled(items: SelectOption[], current: number, direction: 1 | -1): number {\n const len = items.length\n if (len === 0) return current\n\n let next = current + direction\n for (let i = 0; i < len; i++) {\n if (next < 0) next = len - 1\n if (next >= len) next = 0\n if (!items[next]!.disabled) return next\n next += direction\n }\n\n return current\n}\n\nfunction findFirstEnabled(items: SelectOption[]): number {\n for (let i = 0; i < items.length; i++) {\n if (!items[i]!.disabled) return i\n }\n return 0\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport function SelectList({\n items,\n highlightedIndex: controlledIndex,\n onHighlight,\n onSelect,\n initialIndex,\n maxVisible,\n isActive = true,\n indicator = \"\",\n onItemHover,\n onItemClick,\n}: SelectListProps): React.ReactElement {\n // SelectList always controls ListView's cursor (for disabled-item skipping).\n // In uncontrolled mode, internal state tracks the cursor; in controlled mode,\n // the parent's highlightedIndex is the source of truth.\n const isControlled = controlledIndex !== undefined\n const [uncontrolledIndex, setUncontrolledIndex] = useState(\n initialIndex ?? findFirstEnabled(items),\n )\n const currentIndex = isControlled ? controlledIndex : uncontrolledIndex\n\n const setIndex = useCallback(\n (index: number) => {\n if (!isControlled) setUncontrolledIndex(index)\n onHighlight?.(index)\n },\n [isControlled, onHighlight],\n )\n\n // Intercept cursor moves to skip disabled items\n const handleCursor = useCallback(\n (nextIndex: number) => {\n const item = items[nextIndex]\n if (item?.disabled) {\n const direction = nextIndex >= currentIndex ? 1 : -1\n setIndex(findNextEnabled(items, currentIndex, direction as 1 | -1))\n } else {\n setIndex(nextIndex)\n }\n },\n [items, currentIndex, setIndex],\n )\n\n // Intercept Enter to block selection of disabled items\n const handleSelect = useCallback(\n (index: number) => {\n const item = items[index]\n if (item && !item.disabled) {\n onSelect?.(item, index)\n }\n },\n [items, onSelect],\n )\n\n const renderItem = useCallback(\n (item: SelectOption, index: number, meta: { isCursor: boolean }) => {\n // Fake cursor uses scheme cursorColor/cursorText ($bg-cursor + $fg-cursor)\n // so it matches the user's terminal cursor color — native feel per theme.\n // Disabled rows route through $fg-muted (not dimColor) per token system.\n\n // Default hover/click handlers: hover moves keyboard cursor; click\n // moves cursor + confirms selection (Enter-equivalent).\n const handleHover = onItemHover ? () => onItemHover(index) : () => handleCursor(index)\n const handleClick = onItemClick\n ? () => onItemClick(index)\n : () => {\n handleCursor(index)\n handleSelect(index)\n }\n\n if (!indicator) {\n // No-indicator mode (default): communicate selection via full-row bg.\n // The wrapping Box expands to 100% width so the bg fills the row.\n return (\n <Box\n key={item.value}\n width=\"100%\"\n backgroundColor={meta.isCursor ? \"$bg-cursor\" : undefined}\n onMouseEnter={handleHover}\n onClick={handleClick}\n >\n <Text\n color={item.disabled ? \"$fg-muted\" : meta.isCursor ? \"$fg-cursor\" : undefined}\n bold={meta.isCursor && !item.disabled}\n >\n {item.label}\n </Text>\n </Box>\n )\n }\n\n // Indicator mode (backward compat): arrow/glyph prefix, text-only bg.\n return (\n <Text\n key={item.value}\n color={item.disabled ? \"$fg-muted\" : meta.isCursor ? \"$fg-cursor\" : undefined}\n backgroundColor={meta.isCursor ? \"$bg-cursor\" : undefined}\n onMouseEnter={handleHover}\n onClick={handleClick}\n >\n {meta.isCursor ? indicator : \" \".repeat(indicator.length)}\n {item.label}\n </Text>\n )\n },\n [indicator, onItemHover, onItemClick, handleCursor, handleSelect],\n )\n\n return (\n <ListView\n items={items}\n height={maxVisible ?? items.length}\n estimateHeight={1}\n nav\n cursorKey={currentIndex}\n onCursor={handleCursor}\n onSelect={handleSelect}\n active={isActive}\n getKey={(item) => item.value}\n renderItem={renderItem}\n />\n )\n}\n","/**\n * Table Component\n *\n * A generic data table with auto-sizing columns, custom renderers, and alignment.\n * Thin composition over ListView — each data row is a ListView item, column headers\n * are rendered above. Gets nav/cache/search from ListView for free.\n *\n * @example\n * ```tsx\n * <Table\n * columns={[\n * { header: \"Name\", key: \"name\" },\n * { header: \"Age\", key: \"age\", align: \"right\" },\n * { header: \"Bio\", key: \"bio\", grow: true },\n * ]}\n * data={[\n * { name: \"Alice\", age: 30 },\n * { name: \"Bob\", age: 25 },\n * ]}\n * />\n * ```\n */\nimport React, { useMemo } from \"react\"\nimport { Box } from \"./Box\"\nimport { Text } from \"./Text\"\nimport { ListView } from \"../ui/components/ListView\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type Column<T> = {\n /** Column header text */\n header: string\n /** Key to read from data item (simple string access) */\n key?: keyof T & string\n /** Custom render function (takes precedence over key) */\n render?: (item: T, index: number) => React.ReactNode\n /** Text alignment: left (default) or right */\n align?: \"left\" | \"right\"\n /** Fixed width (overrides auto-sizing) */\n width?: number\n /** Allow this column to grow to fill remaining space */\n grow?: boolean\n}\n\nexport type TableProps<T> = {\n /** Data rows */\n data: T[]\n /** Column definitions */\n columns: Column<T>[]\n /** Header text color (default: \"$primary\") */\n headerColor?: string\n /** Show header row (default: true) */\n showHeader?: boolean\n /** Minimum column padding between columns (default: 2) */\n padding?: number\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction computeWidths<T>(columns: Column<T>[], data: T[], padding: number): number[] {\n return columns.map((col) => {\n if (col.width) return col.width\n if (col.grow) return 0 // will use flexGrow\n const cellValues = data.map((item, i) => {\n if (col.render) {\n const rendered = col.render(item, i)\n // Measure string renders for auto-width; React nodes fall back to header width\n return typeof rendered === \"string\" ? rendered : \"\"\n }\n return String((col.key ? item[col.key] : \"\") ?? \"\")\n })\n return Math.max(col.header.length, ...cellValues.map((v) => v.length)) + padding\n })\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport function Table<T>({\n data,\n columns,\n headerColor = \"$primary\",\n showHeader = true,\n padding = 2,\n}: TableProps<T>): React.ReactElement {\n const widths = useMemo(() => computeWidths(columns, data, padding), [columns, data, padding])\n\n const renderCell = (col: Column<T>, item: T, index: number, width: number) => {\n const rendered = col.render ? col.render(item, index) : null\n const content =\n rendered != null ? (\n typeof rendered === \"string\" ? (\n <Text>{rendered}</Text>\n ) : (\n rendered\n )\n ) : (\n <Text>{String((col.key ? item[col.key] : \"\") ?? \"\")}</Text>\n )\n\n return col.grow ? (\n <Box\n key={col.header}\n flexGrow={1}\n justifyContent={col.align === \"right\" ? \"flex-end\" : undefined}\n >\n {content}\n </Box>\n ) : (\n <Box\n key={col.header}\n width={width}\n justifyContent={col.align === \"right\" ? \"flex-end\" : undefined}\n >\n {content}\n </Box>\n )\n }\n\n const renderRow = (item: T, index: number) => (\n <Box>{columns.map((col, colIndex) => renderCell(col, item, index, widths[colIndex]!))}</Box>\n )\n\n // Viewport height = number of data rows (show all, no scrolling)\n // Minimum 1 to avoid zero-height viewport when data is empty\n const viewportHeight = Math.max(data.length, 1)\n\n return (\n <Box flexDirection=\"column\">\n {showHeader && (\n <Box>\n {columns.map((col, i) =>\n col.grow ? (\n <Box key={col.header} flexGrow={1}>\n <Text bold color={headerColor}>\n {col.header}\n </Text>\n </Box>\n ) : (\n <Box key={col.header} width={widths[i]}>\n <Text bold color={headerColor}>\n {col.header}\n </Text>\n </Box>\n ),\n )}\n </Box>\n )}\n {data.length > 0 && (\n <ListView items={data} height={viewportHeight} estimateHeight={1} renderItem={renderRow} />\n )}\n </Box>\n )\n}\n","/**\n * Badge Component\n *\n * A small inline label for status display.\n *\n * Tone surface (Sterling Phase 2b):\n * - `error` | `warning` | `success` | `info` — status tones (what's happening)\n * - `accent` — emphasis / brand (preferred over legacy `primary`)\n * - `destructive` — intent alias for `error` (semantic correctness without\n * palette sprawl; see design-system.md §\"Intent vs role\")\n * - `primary` — legacy synonym for `accent`, accepted during Phase 2b/2c\n * - `default` — base foreground\n *\n * Usage:\n * ```tsx\n * <Badge label=\"Active\" tone=\"success\" />\n * <Badge label=\"Delete\" tone=\"destructive\" />\n * <Badge label=\"New\" tone=\"accent\" />\n * <Badge label=\"Custom\" color=\"magenta\" />\n * ```\n */\nimport React from \"react\"\nimport { Text } from \"../../components/Text\"\nimport type { TextProps } from \"../../components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Tone values — Sterling statuses plus the `destructive` intent alias.\n * `primary` stays as a legacy synonym for `accent` while km-tui finishes\n * migrating; it resolves to the same Sterling token.\n */\nexport type BadgeTone =\n | \"default\"\n | \"accent\"\n | \"error\"\n | \"warning\"\n | \"success\"\n | \"info\"\n | \"destructive\"\n | \"primary\"\n\nexport interface BadgeProps extends Omit<TextProps, \"children\"> {\n /** Badge text */\n label: string\n /**\n * Sterling tone. Accepts status roles (`error`/`warning`/`success`/`info`),\n * the accent emphasis role, or the `destructive` intent alias. Legacy\n * `primary` stays as a synonym during Phase 2b/2c.\n */\n tone?: BadgeTone\n /** Legacy alias for `tone`. Prefer `tone`. */\n variant?: BadgeTone\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/**\n * Tone → Sterling flat token mapping. `destructive` aliases to `error` per\n * D1 (intent lives at the component layer, not as a Theme field).\n */\nconst TONE_COLORS: Record<BadgeTone, string> = {\n default: \"$fg\",\n accent: \"$fg-accent\",\n primary: \"$fg-accent\",\n error: \"$fg-error\",\n destructive: \"$fg-error\",\n warning: \"$fg-warning\",\n success: \"$fg-success\",\n info: \"$fg-info\",\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport function Badge({ label, tone, variant, color, ...rest }: BadgeProps): React.ReactElement {\n // `tone` wins over legacy `variant` when both are set.\n const effectiveTone: BadgeTone = tone ?? variant ?? \"default\"\n const resolvedColor = color ?? TONE_COLORS[effectiveTone]\n\n return (\n <Text color={resolvedColor} bold {...rest}>\n {\" \"}\n {label}{\" \"}\n </Text>\n )\n}\n","/**\n * Divider Component\n *\n * A horizontal separator line with optional centered title.\n *\n * Usage:\n * ```tsx\n * <Divider />\n * <Divider title=\"Section\" />\n * <Divider char=\"=\" width={40} />\n * ```\n */\nimport React from \"react\"\nimport { useBoxRect } from \"../../hooks/useLayout\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface DividerProps {\n /** Character to repeat (default: \"─\") */\n char?: string\n /** Title text centered in divider */\n title?: string\n /** Width (default: 100% via useBoxRect) */\n width?: number\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst DEFAULT_CHAR = \"─\"\nconst DEFAULT_WIDTH = 40\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport function Divider({\n char = DEFAULT_CHAR,\n title,\n width: widthProp,\n}: DividerProps): React.ReactElement {\n const { width: contentWidth } = useBoxRect()\n const totalWidth = widthProp ?? (contentWidth > 0 ? contentWidth : DEFAULT_WIDTH)\n\n if (!title) {\n return (\n <Box>\n <Text color=\"$border-default\">{char.repeat(totalWidth)}</Text>\n </Box>\n )\n }\n\n // Title with surrounding lines: \"───── Title ─────\"\n const titleWithPad = ` ${title} `\n const remaining = Math.max(0, totalWidth - titleWithPad.length)\n const leftLen = Math.floor(remaining / 2)\n const rightLen = remaining - leftLen\n\n return (\n <Box>\n <Text color=\"$border-default\">{char.repeat(leftLen)}</Text>\n <Text bold>{titleWithPad}</Text>\n <Text color=\"$border-default\">{char.repeat(rightLen)}</Text>\n </Box>\n )\n}\n","/**\n * Typography Preset Components\n *\n * Semantic text hierarchy for TUIs. Since terminals can't vary font size,\n * these presets use color + bold/dim/italic to create clear visual levels.\n *\n * All components accept an optional `color` prop to override the default.\n * Headings default to semantic theme colors; pass a custom color for\n * panel differentiation (e.g., <H1 color=\"$fg-success\">Panel A</H1>).\n *\n * ## Color inheritance\n *\n * Body-text components (P, Strong, Em, H3) inherit foreground color from\n * the nearest ancestor Box with a `color` or `theme` prop — just like CSS.\n * They do NOT hardcode `$fg`, so `<Box color=\"$fg-error\"><P>red text</P></Box>` works.\n *\n * `Box theme={}` auto-inherits `$fg` for all text and auto-fills `$bg`:\n * ```tsx\n * <Box theme={lightTheme}>\n * <P>This text uses the light theme's fg on its bg</P>\n * </Box>\n * ```\n *\n * Lists support nesting via UL/OL containers:\n * ```tsx\n * <UL>\n * <LI>First item</LI>\n * <LI>Second item\n * <UL>\n * <LI>Nested bullet</LI>\n * </UL>\n * </LI>\n * </UL>\n * ```\n */\nimport type { ReactNode } from \"react\"\nimport { createContext, useContext, Children, cloneElement, isValidElement } from \"react\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\nimport type { TextProps } from \"../../components/Text\"\n\nexport interface TypographyProps extends Omit<TextProps, \"children\"> {\n children?: ReactNode\n}\n\n// ============================================================================\n// Headings\n// ============================================================================\n\n/** Page title — $primary + bold. Maximum emphasis. */\nexport function H1({ children, color, ...rest }: TypographyProps) {\n return (\n <Text variant=\"h1\" color={color} {...rest}>\n {children}\n </Text>\n )\n}\n\n/** Section heading — $accent + bold. Contrasts with H1. */\nexport function H2({ children, color, ...rest }: TypographyProps) {\n return (\n <Text variant=\"h2\" color={color} {...rest}>\n {children}\n </Text>\n )\n}\n\n/** Group heading — bold, no color override. Same hue as theme's primary but no bold means lighter weight than H1. */\nexport function H3({ children, color, ...rest }: TypographyProps) {\n return (\n <Text variant=\"h3\" color={color} {...rest}>\n {children}\n </Text>\n )\n}\n\n// ============================================================================\n// Body Text\n// ============================================================================\n\n/** Paragraph — plain body text. Inherits foreground from parent. */\nexport function P({ children, color, ...rest }: TypographyProps) {\n return (\n <Text variant=\"body\" color={color} {...rest}>\n {children}\n </Text>\n )\n}\n\n/** Introductory/lead text — $muted + italic. Slightly elevated, slightly receded. */\nexport function Lead({ children, color, ...rest }: TypographyProps) {\n return (\n <Text variant=\"body-muted\" italic color={color} {...rest}>\n {children}\n </Text>\n )\n}\n\n/** Secondary/supporting text — $muted. Recedes from body text. */\nexport function Muted({ children, color, ...rest }: TypographyProps) {\n return (\n <Text variant=\"body-muted\" color={color} {...rest}>\n {children}\n </Text>\n )\n}\n\n/** Fine print — $muted + dim. Captions, footnotes, text that recedes even more than Muted. */\nexport function Small({ children, color, ...rest }: TypographyProps) {\n return (\n <Text variant=\"fine-print\" color={color} {...rest}>\n {children}\n </Text>\n )\n}\n\n/** Bold emphasis — inline strong text. Inherits foreground from parent. */\nexport function Strong({ children, color, ...rest }: TypographyProps) {\n return (\n <Text variant=\"strong\" color={color} {...rest}>\n {children}\n </Text>\n )\n}\n\n/** Italic emphasis — inline emphasized text. Inherits foreground from parent. */\nexport function Em({ children, color, ...rest }: TypographyProps) {\n return (\n <Text variant=\"em\" color={color} {...rest}>\n {children}\n </Text>\n )\n}\n\n// ============================================================================\n// Inline Elements\n// ============================================================================\n\n/** Inline code — $mutedbg background with padding. Inherits foreground from parent. */\nexport function Code({ children, color, ...rest }: TypographyProps) {\n return (\n <Text variant=\"code\" color={color} {...rest}>\n {` ${children} `}\n </Text>\n )\n}\n\n/** Keyboard shortcut badge — $mutedbg background + bold. Inherits foreground from parent. */\nexport function Kbd({ children, color, ...rest }: TypographyProps) {\n return (\n <Text variant=\"kbd\" color={color} {...rest}>\n {` ${children} `}\n </Text>\n )\n}\n\n// ============================================================================\n// Block Elements\n// ============================================================================\n\n/** Blockquote — │ border in $muted + italic content. Wrapped text stays indented. */\nexport function Blockquote({ children, color }: TypographyProps) {\n return (\n <Box>\n <Text color={color ?? \"$fg-muted\"}>│ </Text>\n <Box flexShrink={1}>\n <Text italic>{children}</Text>\n </Box>\n </Box>\n )\n}\n\n/** Code block — │ border in $border + monospace content. Distinct from Blockquote. */\nexport function CodeBlock({ children, color }: TypographyProps) {\n return (\n <Box>\n <Text color={color ?? \"$border-default\"}>│ </Text>\n <Box flexShrink={1}>\n <Text>{children}</Text>\n </Box>\n </Box>\n )\n}\n\n/** Horizontal rule — thin line across the available width. */\nexport function HR({ color, ...rest }: Omit<TypographyProps, \"children\">) {\n return (\n <Text color={color ?? \"$border-default\"} wrap=\"truncate\" {...rest}>\n {\"─\".repeat(200)}\n </Text>\n )\n}\n\n// ============================================================================\n// Lists\n// ============================================================================\n\ninterface ListContextValue {\n level: number\n ordered: boolean\n}\n\nconst ListContext = createContext<ListContextValue>({ level: 0, ordered: false })\n\n/** Unordered list container. Nest inside another UL/OL for indented sub-lists. */\nexport function UL({ children }: TypographyProps) {\n const parent = useContext(ListContext)\n return (\n <ListContext.Provider value={{ level: parent.level + 1, ordered: false }}>\n <Box flexDirection=\"column\">{children}</Box>\n </ListContext.Provider>\n )\n}\n\n/** Ordered list container. Auto-numbers LI children. Nest for sub-lists. */\nexport function OL({ children }: TypographyProps) {\n const parent = useContext(ListContext)\n let index = 0\n const numbered = Children.map(children, (child) => {\n if (isValidElement(child) && child.type === LI) {\n index++\n return cloneElement(child as React.ReactElement<{ _index?: number }>, { _index: index })\n }\n return child\n })\n return (\n <ListContext.Provider value={{ level: parent.level + 1, ordered: true }}>\n <Box flexDirection=\"column\">{numbered}</Box>\n </ListContext.Provider>\n )\n}\n\nconst BULLETS = [\"•\", \"◦\", \"▸\", \"-\"]\n\n/** List item with hanging indent. Use inside UL or OL. 2-char marker (bullet + space). */\nexport function LI({ children, color, _index }: TypographyProps & { _index?: number }) {\n const { level, ordered } = useContext(ListContext)\n const effectiveLevel = Math.max(level, 1)\n const indent = \" \".repeat(effectiveLevel - 1)\n const bullet = BULLETS[Math.min(effectiveLevel - 1, BULLETS.length - 1)]\n const marker = ordered && _index != null ? `${_index}. ` : `${bullet} `\n\n return (\n <Box>\n <Text color={color ?? \"$fg-muted\"}>\n {indent}\n {marker}\n </Text>\n <Box flexShrink={1}>\n <Text color={color}>{children}</Text>\n </Box>\n </Box>\n )\n}\n","/**\n * Heading Component — Semantic headings with OSC 66 text sizing.\n *\n * Uses the kitty text sizing protocol (OSC 66 s= parameter) for real\n * font size variation in terminals that support it (Kitty v0.40+).\n * Graceful degradation: on unsupported terminals, renders as bold text\n * at normal size with semantic theme colors — still readable hierarchy.\n *\n * @example\n * ```tsx\n * <Heading>Page Title</Heading> // h1: 2.0x, bold, $primary\n * <Heading level={2}>Section</Heading> // h2: 1.5x, bold, $accent\n * <Heading level={3}>Subsection</Heading> // h3: 1.25x, bold, $primary\n * <Heading level={4}>Group</Heading> // h4: 1.0x, bold only\n * <Heading level={5}>Minor</Heading> // h5: 0.9x, bold\n * <Heading level={6}>Smallest</Heading> // h6: 0.8x, bold\n * ```\n */\nimport type { ReactNode } from \"react\"\nimport { Text } from \"../../components/Text\"\nimport type { TextProps } from \"../../components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6\n\nexport interface HeadingProps extends Omit<TextProps, \"children\"> {\n /** Heading level (1-6). Default: 1 */\n level?: HeadingLevel\n children?: ReactNode\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** OSC 66 scale multiplier per heading level */\nconst LEVEL_SCALES: Record<HeadingLevel, number> = {\n 1: 2.0,\n 2: 1.5,\n 3: 1.25,\n 4: 1.0,\n 5: 0.9,\n 6: 0.8,\n}\n\n/** Default semantic color per heading level */\nconst LEVEL_COLORS: Record<HeadingLevel, string | undefined> = {\n 1: \"$fg-accent\",\n 2: \"$fg-accent\",\n 3: \"$fg-accent\",\n 4: undefined, // inherit fg\n 5: undefined,\n 6: \"$fg-muted\",\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport function Heading({ level = 1, children, color, ...rest }: HeadingProps) {\n const scale = LEVEL_SCALES[level]\n const defaultColor = LEVEL_COLORS[level]\n\n return (\n <Text bold textSize={scale} color={color ?? defaultColor} {...rest}>\n {children}\n </Text>\n )\n}\n","/**\n * Form + FormField Components\n *\n * Layout wrappers for form inputs. Form provides vertical grouping and\n * an optional submit handler. FormField provides label, error display,\n * and consistent spacing between fields.\n *\n * Usage:\n * ```tsx\n * <Form onSubmit={handleSubmit}>\n * <FormField label=\"Name\" error={errors.name}>\n * <TextInput value={name} onChange={setName} />\n * </FormField>\n * <FormField label=\"Email\">\n * <TextInput value={email} onChange={setEmail} />\n * </FormField>\n * </Form>\n * ```\n */\nimport React from \"react\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface FormProps {\n /** Called when Enter is pressed within the form (optional) */\n onSubmit?: () => void\n /** Gap between form fields (default: 1) */\n gap?: number\n /** Form children (typically FormField components) */\n children: React.ReactNode\n}\n\nexport interface FormFieldProps {\n /** Field label text */\n label: string\n /** Error message to display below the input */\n error?: string\n /** Optional description text below the label */\n description?: string\n /** Whether the field is required (shows * indicator) */\n required?: boolean\n /** Field input children */\n children: React.ReactNode\n}\n\n// =============================================================================\n// Components\n// =============================================================================\n\n/**\n * Vertical form layout container.\n *\n * Groups FormField children with consistent spacing. The optional `onSubmit`\n * callback is provided for parent-level form submission logic.\n */\nexport function Form({ onSubmit: _onSubmit, gap = 1, children }: FormProps): React.ReactElement {\n return (\n <Box flexDirection=\"column\" gap={gap}>\n {children}\n </Box>\n )\n}\n\n/**\n * Form field wrapper providing label, error display, and spacing.\n *\n * Renders a label above the input with optional required indicator,\n * description text, and error message in `$error` color.\n */\nexport function FormField({\n label,\n error,\n description,\n required,\n children,\n}: FormFieldProps): React.ReactElement {\n return (\n <Box flexDirection=\"column\">\n <Text color=\"$fg-muted\" bold>\n {label}\n {required && <Text color=\"$fg-error\"> *</Text>}\n </Text>\n {description && <Text color=\"$fg-muted\">{description}</Text>}\n <Box>{children}</Box>\n {error && <Text color=\"$fg-error\">{error}</Text>}\n </Box>\n )\n}\n","/**\n * Toast/Notification Component + useToast Hook\n *\n * Provides a toast notification system with auto-dismiss capability.\n * `useToast()` returns `{ toast, toasts, dismiss }`. Toasts render as a\n * vertical stack and auto-dismiss after a configurable duration.\n *\n * Usage:\n * ```tsx\n * function App() {\n * const { toast, toasts } = useToast()\n *\n * return (\n * <Box flexDirection=\"column\">\n * <Button label=\"Save\" onPress={() => {\n * toast({ title: \"Saved\", variant: \"success\", duration: 3000 })\n * }} />\n * <ToastContainer toasts={toasts} />\n * </Box>\n * )\n * }\n * ```\n */\nimport React, { useCallback, useEffect, useRef, useState } from \"react\"\nimport { Box, type BoxProps } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Toast tone — Sterling statuses plus the `destructive` intent alias. Phase 2b\n * adds `accent` and `destructive` to the surface; `default` stays for plain\n * notifications that don't carry semantic meaning.\n *\n * `destructive` resolves to `error` (see design-system.md §\"Intent vs role\");\n * apps write `tone=\"destructive\"` when the toast announces a destructive action\n * and `tone=\"error\"` when something failed.\n */\nexport type ToastVariant =\n | \"default\"\n | \"accent\"\n | \"success\"\n | \"error\"\n | \"warning\"\n | \"info\"\n | \"destructive\"\n\nexport interface ToastData {\n /** Unique toast ID (auto-generated if not provided) */\n id: string\n /** Toast title text */\n title: string\n /** Optional description text */\n description?: string\n /** Visual variant (default: \"default\") */\n variant: ToastVariant\n /** Auto-dismiss duration in ms (default: 3000, 0 = no auto-dismiss) */\n duration: number\n}\n\nexport interface ToastOptions {\n /** Toast title text */\n title: string\n /** Optional description text */\n description?: string\n /** Visual variant (default: \"default\") */\n variant?: ToastVariant\n /** Auto-dismiss duration in ms (default: 3000, 0 = no auto-dismiss) */\n duration?: number\n}\n\nexport interface UseToastResult {\n /** Show a new toast notification */\n toast: (options: ToastOptions) => string\n /** Currently visible toasts */\n toasts: ToastData[]\n /** Dismiss a specific toast by ID */\n dismiss: (id: string) => void\n /** Dismiss all toasts */\n dismissAll: () => void\n}\n\nexport interface ToastContainerProps extends Omit<BoxProps, \"children\"> {\n /** Toasts to render */\n toasts: ToastData[]\n /** Maximum visible toasts (default: 5) */\n maxVisible?: number\n}\n\nexport interface ToastItemProps extends Omit<BoxProps, \"children\"> {\n /** Toast data to render */\n toast: ToastData\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst DEFAULT_DURATION = 3000\n\nconst VARIANT_COLORS: Record<ToastVariant, string> = {\n default: \"$fg\",\n accent: \"$fg-accent\",\n success: \"$fg-success\",\n error: \"$fg-error\",\n warning: \"$fg-warning\",\n info: \"$fg-info\",\n destructive: \"$fg-error\",\n}\n\nconst VARIANT_ICONS: Record<ToastVariant, string> = {\n default: \"i\",\n accent: \"*\",\n success: \"+\",\n error: \"x\",\n warning: \"!\",\n info: \"i\",\n destructive: \"x\",\n}\n\n// =============================================================================\n// Hook\n// =============================================================================\n\nlet nextToastId = 0\n\n/**\n * Hook for managing toast notifications.\n *\n * Returns a `toast()` function to create notifications, the current list\n * of `toasts`, and `dismiss`/`dismissAll` functions for manual removal.\n * Toasts auto-dismiss after `duration` ms (default: 3000).\n */\nexport function useToast(): UseToastResult {\n const [toasts, setToasts] = useState<ToastData[]>([])\n const timersRef = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map())\n\n const dismiss = useCallback((id: string) => {\n setToasts((prev) => prev.filter((t) => t.id !== id))\n const timer = timersRef.current.get(id)\n if (timer) {\n clearTimeout(timer)\n timersRef.current.delete(id)\n }\n }, [])\n\n const dismissAll = useCallback(() => {\n setToasts([])\n for (const timer of timersRef.current.values()) {\n clearTimeout(timer)\n }\n timersRef.current.clear()\n }, [])\n\n const toast = useCallback(\n (options: ToastOptions): string => {\n const id = `toast-${++nextToastId}`\n const data: ToastData = {\n id,\n title: options.title,\n description: options.description,\n variant: options.variant ?? \"default\",\n duration: options.duration ?? DEFAULT_DURATION,\n }\n\n setToasts((prev) => [...prev, data])\n\n if (data.duration > 0) {\n const timer = setTimeout(() => {\n dismiss(id)\n }, data.duration)\n timersRef.current.set(id, timer)\n }\n\n return id\n },\n [dismiss],\n )\n\n // Cleanup timers on unmount\n useEffect(() => {\n const timers = timersRef.current\n return () => {\n for (const timer of timers.values()) {\n clearTimeout(timer)\n }\n timers.clear()\n }\n }, [])\n\n return { toast, toasts, dismiss, dismissAll }\n}\n\n// =============================================================================\n// Components\n// =============================================================================\n\n/**\n * Single toast notification item.\n *\n * Renders a bordered box with variant-colored icon, title, and optional\n * description text.\n */\nexport function ToastItem({ toast, ...boxProps }: ToastItemProps): React.ReactElement {\n const color = VARIANT_COLORS[toast.variant]\n const icon = VARIANT_ICONS[toast.variant]\n\n return (\n <Box\n borderStyle=\"single\"\n borderColor=\"$border-default\"\n paddingX={1}\n backgroundColor=\"$bg-surface-raised\"\n width=\"snug-content\"\n {...boxProps}\n >\n <Text color={color} bold>\n [{icon}]\n </Text>\n <Text> {toast.title}</Text>\n {toast.description && <Text color=\"$fg-muted\"> {toast.description}</Text>}\n </Box>\n )\n}\n\n/**\n * Container that renders a stack of toast notifications.\n *\n * Place at the bottom of your layout to show toasts as they appear.\n */\nexport function ToastContainer({\n toasts,\n maxVisible = 5,\n ...boxProps\n}: ToastContainerProps): React.ReactElement {\n const visible = toasts.slice(-maxVisible)\n\n return (\n <Box flexDirection=\"column\" {...boxProps}>\n {visible.map((t) => (\n <ToastItem key={t.id} toast={t} />\n ))}\n </Box>\n )\n}\n","/**\n * InlineAlert Component — low-urgency inline message\n *\n * Sterling Phase 2b — the lowest-urgency member of the Alert family. Renders\n * as a single inline row with a tone-colored icon and text. No background\n * fill, no dismiss affordance, no modal behavior — just a tone-tinted\n * message embedded in the flow.\n *\n * Urgency escalation is component choice, NOT a `priority` prop (Sterling\n * design-system.md §\"Urgency is not a design-system concern\"):\n *\n * <InlineAlert> low passive, in-flow\n * <Banner> medium dismissible, fills row width\n * <Alert> high modal, blocks flow\n *\n * Usage:\n * ```tsx\n * <InlineAlert tone=\"error\">Type-check failed in src/app.ts</InlineAlert>\n * <InlineAlert tone=\"warning\" icon=\"⚠\">Deprecated API</InlineAlert>\n * <InlineAlert tone=\"info\" showIcon={false}>Quiet notice</InlineAlert>\n * ```\n */\nimport React from \"react\"\nimport { Box, type BoxProps } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\nimport { type ToneKey, toneFgToken, toneIcon } from \"./_tone\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface InlineAlertProps extends Omit<BoxProps, \"children\"> {\n /**\n * Sterling tone. `destructive` aliases to `error` at the component layer.\n * Defaults to `info` — the neutral \"heads up\" tone for low-urgency\n * messages.\n */\n tone?: ToneKey\n /** Message content (text or React nodes). */\n children: React.ReactNode\n /** Whether to render the tone icon prefix (default: true). */\n showIcon?: boolean\n /**\n * Override the default tone icon glyph. Defaults to the shared\n * `TONE_ICONS` mapping (same as Toast).\n */\n icon?: string\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Inline low-urgency tone message.\n *\n * Pick `<InlineAlert>` when the message is passive context a user can read\n * or ignore without interrupting their task. Escalate to `<Banner>` or\n * `<Alert>` when the message demands action or blocks flow.\n */\nexport function InlineAlert({\n tone = \"info\",\n children,\n showIcon = true,\n icon,\n ...boxProps\n}: InlineAlertProps): React.ReactElement {\n const color = toneFgToken(tone)\n const glyph = icon ?? toneIcon(tone)\n\n return (\n <Box flexDirection=\"row\" gap={1} {...boxProps}>\n {showIcon && (\n <Text color={color} bold>\n {glyph}\n </Text>\n )}\n <Text color={color}>{children}</Text>\n </Box>\n )\n}\n","/**\n * Banner Component — medium-urgency dismissible row\n *\n * Sterling Phase 2b — the middle tier of the Alert family. Renders as a\n * full-width row with a tinted background (`$bg-<role>-subtle`), tone-colored\n * icon + text, and an optional dismiss affordance.\n *\n * Urgency pairing (Sterling design-system.md §\"Urgency is not a design-system\n * concern\"):\n *\n * <InlineAlert> low passive, in-flow\n * <Banner> medium dismissible, fills row width ← this component\n * <Alert> high modal, blocks flow\n *\n * Why \"subtle\" background (not solid fill): Banners sit in-flow above\n * primary content; a saturated fill would overwhelm the page. The subtle\n * role token reads as \"this row carries a tone\" without taking visual\n * priority from the content being announced. Escalation to \"loud\" is what\n * `<Alert>` (modal, solid fill) is for.\n *\n * Usage:\n * ```tsx\n * <Banner tone=\"warning\" onDismiss={close}>Deprecated API — migrate to useBoxRect</Banner>\n * <Banner tone=\"info\">System maintenance at 02:00 UTC</Banner>\n * <Banner tone=\"error\" onDismiss={close} dismissLabel=\"×\">\n * <Text>Connection lost — <Text underline>retry</Text></Text>\n * </Banner>\n * ```\n */\nimport React from \"react\"\nimport { useInput } from \"../../hooks/useInput\"\nimport { Box, type BoxProps } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\nimport { type ToneKey, toneSubtleTokens, toneIcon } from \"./_tone\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface BannerProps extends Omit<BoxProps, \"children\"> {\n /**\n * Sterling tone. `destructive` aliases to `error` at the component layer.\n * Defaults to `info` — the neutral banner tone.\n */\n tone?: ToneKey\n /** Banner content. */\n children: React.ReactNode\n /**\n * When set, renders a dismiss affordance on the right side and calls this\n * callback when the user presses Escape (if focused) or activates the\n * affordance. Omit to make the banner non-dismissible.\n */\n onDismiss?: () => void\n /** Label for the dismiss affordance (default: \"dismiss ×\"). */\n dismissLabel?: React.ReactNode\n /** Whether to render the tone icon prefix (default: true). */\n showIcon?: boolean\n /** Override the default tone icon glyph. */\n icon?: string\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Dismissible tone banner — medium-urgency message row.\n *\n * Width defaults to \"100%\" so the banner spans its parent. Consumers that\n * want a narrower banner can set `width` directly; the subtle bg fill will\n * respect the explicit width.\n */\nexport function Banner({\n tone = \"info\",\n children,\n onDismiss,\n dismissLabel = \"dismiss ×\",\n showIcon = true,\n icon,\n ...boxProps\n}: BannerProps): React.ReactElement {\n const tokens = toneSubtleTokens(tone)\n const glyph = icon ?? toneIcon(tone)\n\n // Escape dismisses when a handler is supplied. Scoped to the component via\n // `useInput`'s default active scope; apps that render a Banner outside a\n // focusable region can drive dismissal imperatively via the `onDismiss`\n // callback without depending on key delivery.\n useInput(\n (_input, key) => {\n if (key.escape) onDismiss?.()\n },\n { isActive: Boolean(onDismiss) },\n )\n\n return (\n <Box\n flexDirection=\"row\"\n backgroundColor={tokens.bg}\n paddingX={2}\n width=\"100%\"\n {...boxProps}\n >\n {showIcon && (\n <Text color={tokens.fg} bold>\n {glyph}{\" \"}\n </Text>\n )}\n <Text color={tokens.fg}>{children}</Text>\n {onDismiss && (\n <>\n <Box flexGrow={1} />\n <Text color={tokens.fg} dim>\n {dismissLabel}\n </Text>\n </>\n )}\n </Box>\n )\n}\n","/**\n * Alert Component — high-urgency modal\n *\n * Sterling Phase 2b — the highest-urgency member of the Alert family. Built\n * on top of ModalDialog with tone-aware border and title styling. Blocks\n * flow; the user must acknowledge/dismiss.\n *\n * Urgency pairing (Sterling design-system.md §\"Urgency is not a design-system\n * concern\"):\n *\n * <InlineAlert> low passive, in-flow\n * <Banner> medium dismissible, fills row width\n * <Alert> high modal, blocks flow ← this component\n *\n * Alert is a compound component: `<Alert.Title>`, `<Alert.Body>`,\n * `<Alert.Actions>`. Using the sub-components keeps the imperative API narrow\n * while giving callers layout control inside the dialog.\n *\n * Usage:\n * ```tsx\n * <Alert tone=\"error\" open onClose={close}>\n * <Alert.Title>Delete repository?</Alert.Title>\n * <Alert.Body>This action cannot be undone.</Alert.Body>\n * <Alert.Actions>\n * <Button tone=\"destructive\" label=\"Delete\" onPress={confirmDelete} />\n * <Button tone=\"accent\" label=\"Cancel\" onPress={close} />\n * </Alert.Actions>\n * </Alert>\n *\n * <Alert tone=\"warning\" open onClose={close}>\n * <Alert.Title>Unsaved changes</Alert.Title>\n * <Alert.Body>You have unsaved changes. Save before closing?</Alert.Body>\n * </Alert>\n * ```\n */\nimport React from \"react\"\nimport { useInput } from \"../../hooks/useInput\"\nimport { Box, type BoxProps } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\nimport { ModalDialog } from \"./ModalDialog\"\nimport { type ToneKey, toneFgToken, toneIcon } from \"./_tone\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface AlertProps extends Omit<BoxProps, \"children\" | \"flexDirection\" | \"width\" | \"height\"> {\n /**\n * Sterling tone. `destructive` aliases to `error` at the component layer.\n * Defaults to `error` — the prototypical high-urgency case (destructive\n * confirmation, fatal error). Callers ask for `warning` / `info` / `success`\n * when the modal conveys those tones instead.\n */\n tone?: ToneKey\n /** Whether the alert is open. Render nothing when `false`. */\n open?: boolean\n /** Called when the user dismisses (Escape key). */\n onClose?: () => void\n /** Alert content — compose `<Alert.Title>`, `<Alert.Body>`, `<Alert.Actions>`. */\n children: React.ReactNode\n /** Whether to render the tone icon in the title (default: true). */\n showIcon?: boolean\n /** Override the default tone icon glyph. */\n icon?: string\n /** Dialog width (default: passed through to ModalDialog, which snaps to content). */\n width?: number | string\n}\n\nexport interface AlertTitleProps {\n children: React.ReactNode\n}\n\nexport interface AlertBodyProps {\n children: React.ReactNode\n}\n\nexport interface AlertActionsProps {\n children: React.ReactNode\n}\n\n// =============================================================================\n// Sub-components\n// =============================================================================\n\nfunction AlertTitle({ children }: AlertTitleProps): React.ReactElement {\n return (\n <Text bold>\n {children}\n </Text>\n )\n}\n\nfunction AlertBody({ children }: AlertBodyProps): React.ReactElement {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text>{children}</Text>\n </Box>\n )\n}\n\nfunction AlertActions({ children }: AlertActionsProps): React.ReactElement {\n return (\n <Box flexDirection=\"row\" gap={1} marginTop={1} justifyContent=\"flex-end\">\n {children}\n </Box>\n )\n}\n\n// =============================================================================\n// Main component\n// =============================================================================\n\n/**\n * Modal high-urgency alert — blocks the user's flow until dismissed.\n *\n * Uses ModalDialog under the hood for the double border, backdrop fade, and\n * layout conventions. Adds tone-aware border + title-icon styling so the\n * modal reads as the matching status (error / warning / success / info).\n */\nfunction AlertRoot({\n tone = \"error\",\n open = true,\n onClose,\n children,\n showIcon = true,\n icon,\n width,\n ...boxProps\n}: AlertProps): React.ReactElement | null {\n const fgToken = toneFgToken(tone)\n const glyph = icon ?? toneIcon(tone)\n\n useInput(\n (_input, key) => {\n if (key.escape) onClose?.()\n },\n { isActive: Boolean(onClose) && open },\n )\n\n if (!open) return null\n\n // Render icon + content as a single column. The icon sits inline with the\n // first child (Title) so it reads like a leading glyph without constraining\n // the content width. ModalDialog's content box snaps to intrinsic content,\n // so the column layout here just flows naturally inside the dialog.\n return (\n <ModalDialog\n borderColor={fgToken}\n titleColor={fgToken}\n onClose={onClose}\n width={width}\n {...boxProps}\n >\n <Box flexDirection=\"column\">\n {showIcon && (\n <Text color={fgToken} bold>\n {glyph}{\" \"}\n </Text>\n )}\n {children}\n </Box>\n </ModalDialog>\n )\n}\n\n// =============================================================================\n// Exported compound component\n// =============================================================================\n\n/**\n * Alert — high-urgency modal dialog. Use `<Alert.Title>`, `<Alert.Body>`,\n * and `<Alert.Actions>` for structured content.\n */\nexport const Alert = Object.assign(AlertRoot, {\n Title: AlertTitle,\n Body: AlertBody,\n Actions: AlertActions,\n})\n","/**\n * CommandPalette Component\n *\n * A filterable command list with keyboard navigation. Takes an array of\n * commands with name, description, and optional shortcut. Users can type\n * to filter and navigate with arrow keys / j/k.\n *\n * Usage:\n * ```tsx\n * const commands = [\n * { name: \"Save\", description: \"Save current file\", shortcut: \"Ctrl+S\" },\n * { name: \"Quit\", description: \"Exit application\", shortcut: \"Ctrl+Q\" },\n * { name: \"Help\", description: \"Show help\" },\n * ]\n *\n * <CommandPalette\n * commands={commands}\n * onSelect={(cmd) => exec(cmd.name)}\n * placeholder=\"Type a command...\"\n * />\n * ```\n */\nimport React, { useCallback, useMemo, useState } from \"react\"\nimport { useInput } from \"../../hooks/useInput\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface CommandItem {\n /** Command display name */\n name: string\n /** Command description */\n description?: string\n /** Keyboard shortcut hint */\n shortcut?: string\n}\n\nexport interface CommandPaletteProps {\n /** Available commands */\n commands: CommandItem[]\n /** Called when a command is selected (Enter) */\n onSelect?: (command: CommandItem) => void\n /** Called when the palette is dismissed (Escape) */\n onClose?: () => void\n /** Placeholder text for the filter input (default: \"Search commands...\") */\n placeholder?: string\n /** Max visible results (default: 10) */\n maxVisible?: number\n /** Whether this component captures input (default: true) */\n isActive?: boolean\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/** Case-insensitive fuzzy match: all query characters appear in order. */\nfunction fuzzyMatch(query: string, text: string): boolean {\n const lower = text.toLowerCase()\n const q = query.toLowerCase()\n let qi = 0\n for (let i = 0; i < lower.length && qi < q.length; i++) {\n if (lower[i] === q[qi]) qi++\n }\n return qi === q.length\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Filterable command palette with keyboard navigation.\n *\n * Type to filter commands by name, navigate with Up/Down or j/k,\n * confirm with Enter, dismiss with Escape.\n */\nexport function CommandPalette({\n commands,\n onSelect,\n onClose,\n placeholder = \"Search commands...\",\n maxVisible = 10,\n isActive = true,\n}: CommandPaletteProps): React.ReactElement {\n const [query, setQuery] = useState(\"\")\n const [selectedIndex, setSelectedIndex] = useState(0)\n\n const filtered = useMemo(() => {\n if (!query) return commands\n return commands.filter(\n (cmd) =>\n fuzzyMatch(query, cmd.name) || (cmd.description && fuzzyMatch(query, cmd.description)),\n )\n }, [commands, query])\n\n const visible = filtered.slice(0, maxVisible)\n\n const clampIndex = useCallback(\n (idx: number) => Math.max(0, Math.min(idx, filtered.length - 1)),\n [filtered.length],\n )\n\n useInput(\n (input, key) => {\n // Navigation\n if (key.upArrow) {\n setSelectedIndex((prev) => clampIndex(prev - 1))\n return\n }\n if (key.downArrow) {\n setSelectedIndex((prev) => clampIndex(prev + 1))\n return\n }\n\n // Select\n if (key.return) {\n const cmd = filtered[selectedIndex]\n if (cmd) onSelect?.(cmd)\n return\n }\n\n // Dismiss\n if (key.escape) {\n onClose?.()\n return\n }\n\n // Backspace\n if (key.backspace || key.delete) {\n setQuery((prev) => {\n const next = prev.slice(0, -1)\n setSelectedIndex(0)\n return next\n })\n return\n }\n\n // Printable character\n if (input && input >= \" \" && !key.ctrl && !key.meta) {\n setQuery((prev) => {\n setSelectedIndex(0)\n return prev + input\n })\n }\n },\n { isActive },\n )\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor=\"$border-default\"\n backgroundColor=\"$bg-surface-raised\"\n paddingX={1}\n >\n {/* Search input */}\n <Box>\n <Text color=\"$fg-accent\" bold>\n {\">\"}{\" \"}\n </Text>\n <Text>{query || <Text color=\"$fg-muted\">{placeholder}</Text>}</Text>\n </Box>\n <Box>\n <Text color=\"$border-default\">{\"─\".repeat(30)}</Text>\n </Box>\n {/* Results */}\n {visible.length === 0 ? (\n <Text color=\"$fg-muted\">No matching commands</Text>\n ) : (\n visible.map((cmd, i) => {\n const isSelected = i === selectedIndex\n return (\n <Box key={cmd.name} gap={1}>\n <Text inverse={isSelected} color={isSelected ? \"$fg-accent\" : \"$fg\"}>\n {isSelected ? \">\" : \" \"} {cmd.name}\n </Text>\n {cmd.description && <Text color=\"$fg-muted\">{cmd.description}</Text>}\n {cmd.shortcut && (\n <Text color=\"$fg-muted\" bold>\n {cmd.shortcut}\n </Text>\n )}\n </Box>\n )\n })\n )}\n {/* Status */}\n {filtered.length > maxVisible && (\n <Text color=\"$fg-muted\">{filtered.length - maxVisible} more...</Text>\n )}\n </Box>\n )\n}\n","/**\n * TreeView Component\n *\n * Expandable/collapsible hierarchical data display with keyboard navigation.\n * Thin composition over ListView — flattens the tree, delegates navigation\n * and virtualization, adds expand/collapse and indentation.\n *\n * Usage:\n * ```tsx\n * const data: TreeNode[] = [\n * {\n * id: \"1\",\n * label: \"Documents\",\n * children: [\n * { id: \"1.1\", label: \"README.md\" },\n * { id: \"1.2\", label: \"notes.txt\" },\n * ],\n * },\n * { id: \"2\", label: \"config.json\" },\n * ]\n *\n * <TreeView data={data} renderNode={(node) => <Text>{node.label}</Text>} />\n * ```\n */\nimport React, { useCallback, useMemo, useRef, useState } from \"react\"\nimport { useInput } from \"../../hooks/useInput\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\nimport { ListView } from \"./ListView\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface TreeNode {\n /** Unique identifier for this node */\n id: string\n /** Display label */\n label: string\n /** Child nodes (optional) */\n children?: TreeNode[]\n}\n\nexport interface TreeViewProps {\n /** Hierarchical data to display */\n data: TreeNode[]\n /** Custom node renderer (default: renders label text) */\n renderNode?: (node: TreeNode, depth: number) => React.ReactNode\n /** Controlled: set of expanded node IDs */\n expandedIds?: Set<string>\n /** Called when expansion state changes */\n onToggle?: (nodeId: string, expanded: boolean) => void\n /** Whether nodes start expanded (default: false) */\n defaultExpanded?: boolean\n /** Whether this component captures input (default: true) */\n isActive?: boolean\n /** Indent per level in characters (default: 2) */\n indent?: number\n /** Height of the viewport in rows. When omitted, renders all items (no virtualization). */\n height?: number\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/** A flattened tree item with its source node and depth. */\ninterface FlatItem {\n node: TreeNode\n depth: number\n}\n\n/** Flatten tree into visible list based on expansion state. */\nfunction flattenTree(nodes: TreeNode[], expanded: Set<string>, depth: number = 0): FlatItem[] {\n const result: FlatItem[] = []\n for (const node of nodes) {\n result.push({ node, depth })\n if (node.children?.length && expanded.has(node.id)) {\n result.push(...flattenTree(node.children, expanded, depth + 1))\n }\n }\n return result\n}\n\n/** Collect all node IDs in the tree (for defaultExpanded). */\nfunction collectAllIds(nodes: TreeNode[]): Set<string> {\n const ids = new Set<string>()\n for (const node of nodes) {\n ids.add(node.id)\n if (node.children) {\n for (const id of collectAllIds(node.children)) {\n ids.add(id)\n }\n }\n }\n return ids\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Expandable/collapsible tree view built on ListView.\n *\n * ListView handles: cursor movement (j/k, arrows, PgUp/PgDn), scrolling,\n * mouse wheel, virtualization, and search.\n *\n * TreeView adds: tree flattening, indentation, expand/collapse (Enter,\n * Right on collapsed, Left on expanded).\n */\nexport function TreeView({\n data,\n renderNode,\n expandedIds: controlledExpanded,\n onToggle,\n defaultExpanded = false,\n isActive = true,\n indent = 2,\n height,\n}: TreeViewProps): React.ReactElement {\n const isControlled = controlledExpanded !== undefined\n\n const [uncontrolledExpanded, setUncontrolledExpanded] = useState<Set<string>>(() =>\n defaultExpanded ? collectAllIds(data) : new Set(),\n )\n\n const expanded = isControlled ? controlledExpanded : uncontrolledExpanded\n\n // Cursor tracked via ref (updated by ListView's onCursor) to avoid\n // re-renders on every cursor move. Only expand/collapse reads it.\n const cursorRef = useRef(0)\n\n const flatItems = useMemo(() => flattenTree(data, expanded), [data, expanded])\n\n const toggleNode = useCallback(\n (nodeId: string) => {\n const wasExpanded = expanded.has(nodeId)\n if (!isControlled) {\n setUncontrolledExpanded((prev) => {\n const next = new Set(prev)\n if (wasExpanded) next.delete(nodeId)\n else next.add(nodeId)\n return next\n })\n }\n onToggle?.(nodeId, !wasExpanded)\n },\n [expanded, isControlled, onToggle],\n )\n\n // Right arrow → expand collapsed branch, Left arrow → collapse expanded branch.\n // ListView handles j/k/↑/↓/PgUp/PgDn/Home/End/G; we add only tree-specific keys.\n useInput(\n (_input, key) => {\n if (flatItems.length === 0) return\n const cursor = Math.min(cursorRef.current, flatItems.length - 1)\n const item = flatItems[cursor]\n if (!item?.node.children?.length) return\n\n if (key.rightArrow && !expanded.has(item.node.id)) {\n toggleNode(item.node.id)\n } else if (key.leftArrow && expanded.has(item.node.id)) {\n toggleNode(item.node.id)\n }\n },\n { isActive },\n )\n\n // Enter on a branch node → toggle expand/collapse (via ListView's onSelect).\n const handleSelect = useCallback(\n (index: number) => {\n const item = flatItems[index]\n if (item?.node.children?.length) {\n toggleNode(item.node.id)\n }\n },\n [flatItems, toggleNode],\n )\n\n const handleCursor = useCallback((index: number) => {\n cursorRef.current = index\n }, [])\n\n const getKey = useCallback((item: FlatItem) => item.node.id, [])\n\n const renderTreeItem = useCallback(\n (item: FlatItem, _index: number, meta: { isCursor: boolean }) => {\n const hasChildren = !!item.node.children?.length\n const isExpanded = expanded.has(item.node.id)\n const prefix = hasChildren ? (isExpanded ? \"v \" : \"> \") : \" \"\n const padding = \" \".repeat(item.depth * indent)\n\n return (\n <Text inverse={meta.isCursor}>\n {padding}\n <Text color={hasChildren ? \"$fg-accent\" : \"$fg\"}>{prefix}</Text>\n {renderNode ? renderNode(item.node, item.depth) : <Text>{item.node.label}</Text>}\n </Text>\n )\n },\n [expanded, indent, renderNode],\n )\n\n if (flatItems.length === 0) {\n return (\n <Box>\n <Text color=\"$fg-muted\">No items</Text>\n </Box>\n )\n }\n\n return (\n <ListView\n items={flatItems}\n height={height ?? flatItems.length}\n nav\n active={isActive}\n onCursor={handleCursor}\n onSelect={handleSelect}\n renderItem={renderTreeItem}\n getKey={getKey}\n estimateHeight={1}\n />\n )\n}\n","/**\n * Breadcrumb Component\n *\n * Navigation breadcrumb trail with configurable separators.\n * Highlights the last item as the current/active page.\n *\n * Usage:\n * ```tsx\n * <Breadcrumb\n * items={[\n * { label: \"Home\" },\n * { label: \"Settings\" },\n * { label: \"Profile\" },\n * ]}\n * separator=\">\"\n * />\n * // Renders: Home > Settings > Profile\n * ```\n */\nimport React from \"react\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface BreadcrumbItem {\n /** Display label */\n label: string\n}\n\nexport interface BreadcrumbProps {\n /** Breadcrumb items (left to right) */\n items: BreadcrumbItem[]\n /** Separator character between items (default: \"/\") */\n separator?: string\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Horizontal breadcrumb trail.\n *\n * Renders items separated by a configurable separator character.\n * The last item is rendered in bold `$fg` as the current location;\n * preceding items are rendered in `$muted`.\n */\nexport function Breadcrumb({ items, separator = \"/\" }: BreadcrumbProps): React.ReactElement {\n if (items.length === 0) {\n return <Box />\n }\n\n return (\n <Box>\n {items.map((item, i) => {\n const isLast = i === items.length - 1\n\n return (\n <React.Fragment key={i}>\n {i > 0 && <Text color=\"$fg-muted\"> {separator} </Text>}\n <Text color={isLast ? \"$fg\" : \"$fg-muted\"} bold={isLast}>\n {item.label}\n </Text>\n </React.Fragment>\n )\n })}\n </Box>\n )\n}\n","/**\n * Tabs Component\n *\n * Tab bar with keyboard navigation and panel content switching.\n * Uses compound component pattern: Tabs > TabList + TabPanel.\n *\n * Usage:\n * ```tsx\n * <Tabs defaultValue=\"general\">\n * <TabList>\n * <Tab value=\"general\">General</Tab>\n * <Tab value=\"advanced\">Advanced</Tab>\n * <Tab value=\"about\">About</Tab>\n * </TabList>\n * <TabPanel value=\"general\">\n * <Text>General settings...</Text>\n * </TabPanel>\n * <TabPanel value=\"advanced\">\n * <Text>Advanced settings...</Text>\n * </TabPanel>\n * <TabPanel value=\"about\">\n * <Text>About this app...</Text>\n * </TabPanel>\n * </Tabs>\n * ```\n */\nimport React, { createContext, useCallback, useContext, useState } from \"react\"\nimport { useInput } from \"../../hooks/useInput\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface TabsProps {\n /** Default active tab value (uncontrolled) */\n defaultValue?: string\n /** Controlled active tab value */\n value?: string\n /** Called when the active tab changes */\n onChange?: (value: string) => void\n /** Whether tab input is active (default: true) */\n isActive?: boolean\n /** Tab children (TabList + TabPanel components) */\n children: React.ReactNode\n}\n\nexport interface TabListProps {\n /** Tab children */\n children: React.ReactNode\n}\n\nexport interface TabProps {\n /** Unique tab identifier */\n value: string\n /** Tab label children */\n children: React.ReactNode\n}\n\nexport interface TabPanelProps {\n /** Tab value this panel corresponds to */\n value: string\n /** Panel content */\n children: React.ReactNode\n}\n\n// =============================================================================\n// Context\n// =============================================================================\n\ninterface TabsContextValue {\n activeValue: string\n setActiveValue: (value: string) => void\n tabValues: string[]\n registerTab: (value: string) => void\n}\n\nconst TabsContext = createContext<TabsContextValue>({\n activeValue: \"\",\n setActiveValue: () => {},\n tabValues: [],\n registerTab: () => {},\n})\n\nfunction useTabsContext(): TabsContextValue {\n return useContext(TabsContext)\n}\n\n// =============================================================================\n// Components\n// =============================================================================\n\n/**\n * Root tabs container. Provides context for TabList, Tab, and TabPanel.\n *\n * Supports controlled (`value` + `onChange`) and uncontrolled (`defaultValue`) modes.\n * Navigate tabs with Left/Right arrow keys when the TabList is active.\n */\nexport function Tabs({\n defaultValue,\n value: controlledValue,\n onChange,\n isActive = true,\n children,\n}: TabsProps): React.ReactElement {\n const isControlled = controlledValue !== undefined\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue ?? \"\")\n const [tabValues, setTabValues] = useState<string[]>([])\n\n const activeValue = isControlled ? controlledValue : uncontrolledValue\n\n const setActiveValue = useCallback(\n (val: string) => {\n if (!isControlled) setUncontrolledValue(val)\n onChange?.(val)\n },\n [isControlled, onChange],\n )\n\n const registerTab = useCallback((val: string) => {\n setTabValues((prev) => (prev.includes(val) ? prev : [...prev, val]))\n }, [])\n\n // Keyboard navigation between tabs\n useInput(\n (_input, key) => {\n if (tabValues.length === 0) return\n\n const currentIdx = tabValues.indexOf(activeValue)\n if (currentIdx < 0) return\n\n if (key.rightArrow || _input === \"l\") {\n const next = (currentIdx + 1) % tabValues.length\n setActiveValue(tabValues[next]!)\n return\n }\n\n if (key.leftArrow || _input === \"h\") {\n const next = (currentIdx - 1 + tabValues.length) % tabValues.length\n setActiveValue(tabValues[next]!)\n return\n }\n },\n { isActive },\n )\n\n return (\n <TabsContext.Provider value={{ activeValue, setActiveValue, tabValues, registerTab }}>\n <Box flexDirection=\"column\" flexGrow={1}>\n {children}\n </Box>\n </TabsContext.Provider>\n )\n}\n\n/**\n * Horizontal tab bar container.\n *\n * Renders Tab children in a horizontal row with gap spacing.\n */\nexport function TabList({ children }: TabListProps): React.ReactElement {\n return (\n <Box flexDirection=\"row\" gap={1} borderBottom borderColor=\"$border-default\">\n {children}\n </Box>\n )\n}\n\n/**\n * Individual tab trigger.\n *\n * Renders the tab label with active/inactive styling. Active tab is bold\n * with `$primary` color; inactive tabs use `$muted`.\n *\n * Hover: a non-active hovered tab gets a subtle `$bg-muted` background to\n * signal interactivity. The active tab is already visually prominent — no\n * extra hover styling is applied to avoid double-styling.\n */\nexport function Tab({ value, children }: TabProps): React.ReactElement {\n const { activeValue, setActiveValue, registerTab } = useTabsContext()\n const isActive = activeValue === value\n const [isHovered, setIsHovered] = useState(false)\n\n // Register this tab's value for keyboard navigation\n React.useEffect(() => {\n registerTab(value)\n }, [value, registerTab])\n\n // Hover background: only for inactive tabs. Active tab already has $primary\n // fg + bold — adding a bg on top creates visual conflict.\n const hoverBg = !isActive && isHovered ? \"$bg-muted\" : undefined\n\n return (\n <Box\n onMouseDown={() => setActiveValue(value)}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n backgroundColor={hoverBg}\n >\n <Text color={isActive ? \"$fg-accent\" : \"$fg-muted\"} bold={isActive} underline={isActive}>\n {children}\n </Text>\n </Box>\n )\n}\n\n/**\n * Tab panel content container.\n *\n * Only renders its children when the corresponding tab is active.\n */\nexport function TabPanel({ value, children }: TabPanelProps): React.ReactElement | null {\n const { activeValue } = useTabsContext()\n\n if (activeValue !== value) return null\n\n return (\n <Box flexDirection=\"column\" flexGrow={1}>\n {children}\n </Box>\n )\n}\n","/**\n * Tooltip Component\n *\n * Shows contextual help text near the target element. In a terminal UI,\n * the tooltip renders inline below the target since there is no floating\n * layer. Visibility is controlled via the `show` prop.\n *\n * Usage:\n * ```tsx\n * <Tooltip content=\"Delete permanently\" show={isFocused}>\n * <Button label=\"Delete\" onPress={handleDelete} />\n * </Tooltip>\n *\n * // Always visible\n * <Tooltip content=\"This action cannot be undone\" show>\n * <Text>Dangerous action</Text>\n * </Tooltip>\n * ```\n */\nimport React from \"react\"\nimport { Box, type BoxProps } from \"../../components/Box\"\nimport { Small } from \"./Typography\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface TooltipProps extends Omit<BoxProps, \"children\"> {\n /** Tooltip text content */\n content: string\n /** Whether the tooltip is visible (default: false) */\n show?: boolean\n /** Tooltip children (target element) */\n children: React.ReactNode\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Contextual tooltip that appears below its children.\n *\n * Renders inline below the target element when `show` is true.\n * Tooltip text is rendered in `$muted` with dimColor for subtlety.\n */\nexport function Tooltip({\n content,\n show = false,\n children,\n ...boxProps\n}: TooltipProps): React.ReactElement {\n return (\n <Box flexDirection=\"column\" {...boxProps}>\n {children}\n {show && (\n <Box width=\"snug-content\">\n <Small>{content}</Small>\n </Box>\n )}\n </Box>\n )\n}\n","/**\n * Skeleton Component\n *\n * Loading placeholder with configurable dimensions and shape.\n * Renders a block of placeholder characters to indicate content\n * that is loading or not yet available.\n *\n * Usage:\n * ```tsx\n * <Skeleton width={20} />\n * <Skeleton width={30} height={3} />\n * <Skeleton width={10} shape=\"circle\" />\n * ```\n */\nimport React from \"react\"\nimport { Box } from \"../../components/Box\"\nimport { Text } from \"../../components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SkeletonProps {\n /** Width in columns (default: 20) */\n width?: number\n /** Height in rows (default: 1) */\n height?: number\n /** Placeholder character (default: \"░\") */\n char?: string\n /** Shape hint: \"line\" for single-line, \"block\" for multi-line (default: auto from height) */\n shape?: \"line\" | \"block\" | \"circle\"\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst DEFAULT_WIDTH = 20\nconst DEFAULT_CHAR = \"░\"\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Loading placeholder skeleton.\n *\n * Renders a dimmed block of placeholder characters. Use `width` and `height`\n * to match the expected content dimensions. The `circle` shape renders\n * a shorter, centered row for avatar-style placeholders.\n */\nexport function Skeleton({\n width = DEFAULT_WIDTH,\n height: heightProp,\n char = DEFAULT_CHAR,\n shape,\n}: SkeletonProps): React.ReactElement {\n const resolvedShape = shape ?? (heightProp && heightProp > 1 ? \"block\" : \"line\")\n const height = heightProp ?? (resolvedShape === \"circle\" ? 1 : 1)\n\n if (resolvedShape === \"circle\") {\n // Render a centered shorter line to suggest a circular avatar\n const circleWidth = Math.min(width, 6)\n const pad = Math.max(0, Math.floor((width - circleWidth) / 2))\n return (\n <Box>\n <Text color=\"$fg-muted\">\n {\" \".repeat(pad)}\n {char.repeat(circleWidth)}\n </Text>\n </Box>\n )\n }\n\n const line = char.repeat(width)\n const rows = Array.from({ length: height }, (_, i) => i)\n\n return (\n <Box flexDirection=\"column\">\n {rows.map((i) => (\n <Text key={i} color=\"$fg-muted\">\n {line}\n </Text>\n ))}\n </Box>\n )\n}\n","/**\n * Position Registry — 2D grid position tracking with auto-cleanup.\n *\n * Tracks screen positions of items in a 2D grid (sections × items).\n * Items auto-register on mount via useScrollRect and auto-unregister\n * on unmount via useEffect cleanup. Eliminates stale-entry bugs.\n *\n * @example\n * ```tsx\n * <PositionRegistryProvider>\n * {columns.map((col, i) => (\n * <VirtualList items={col.items} renderItem={(item, idx) => (\n * <GridCell sectionIndex={i} itemIndex={idx}>\n * <Card {...item} />\n * </GridCell>\n * )} />\n * ))}\n * </PositionRegistryProvider>\n * ```\n */\n\nimport { createContext, useContext, useMemo, type ReactNode } from \"react\"\nimport { createLogger } from \"loggily\"\n\nconst log = createLogger(\"silvery:position-registry\")\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ScrollRect {\n x: number\n y: number\n width: number\n height: number\n}\n\n/**\n * Position registry for 2D grid layouts.\n *\n * Items are keyed by (sectionIndex, itemIndex). Positions are screen-relative\n * (accounting for scroll offsets) via useScrollRect.\n */\nexport interface PositionRegistry {\n // === Registration ===\n\n /** Register an item's screen position. Called automatically by GridCell/useGridPosition. */\n register(sectionIndex: number, itemIndex: number, rect: ScrollRect): void\n\n /** Remove an item's entry. Called automatically on unmount. */\n unregister(sectionIndex: number, itemIndex: number): void\n\n // === Queries ===\n\n /** Get an item's screen position, or undefined if not registered. */\n getPosition(sectionIndex: number, itemIndex: number): ScrollRect | undefined\n\n /** Check if a section has any registered items. */\n hasSection(sectionIndex: number): boolean\n\n /** Get count of registered items in a section. */\n getItemCount(sectionIndex: number): number\n\n // === Cross-axis navigation ===\n\n /**\n * Find the item in a section closest to a target Y position.\n *\n * Algorithm:\n * 1. If targetY falls inside an item's bounding box, return that item\n * 2. Otherwise, find the item whose midpoint is closest to targetY\n * 3. Return -1 if no items registered or targetY is above all items\n */\n findItemAtY(sectionIndex: number, targetY: number): number\n\n /**\n * Find the insertion slot in a section closest to targetY.\n * Slot 0 = before first item, slot N = after item N-1.\n */\n findInsertionSlot(sectionIndex: number, targetY: number): number\n\n // === Lifecycle ===\n\n /** Clear all positions. */\n clear(): void\n\n /** Dump registry state for debugging. */\n dump(): string\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nfunction createPositionRegistry(): PositionRegistry {\n // Map: sectionIndex -> Map<itemIndex, { rect: ScrollRect }>\n const sections = new Map<number, Map<number, { rect: ScrollRect }>>()\n\n const registry: PositionRegistry = {\n // === Registration ===\n\n register(sectionIndex: number, itemIndex: number, rect: ScrollRect): void {\n let sectionMap = sections.get(sectionIndex)\n if (!sectionMap) {\n sectionMap = new Map()\n sections.set(sectionIndex, sectionMap)\n }\n\n sectionMap.set(itemIndex, { rect })\n\n log.debug?.(`register sec=${sectionIndex} item=${itemIndex} y=${rect.y} h=${rect.height}`)\n },\n\n unregister(sectionIndex: number, itemIndex: number): void {\n const sectionMap = sections.get(sectionIndex)\n if (sectionMap) {\n sectionMap.delete(itemIndex)\n if (sectionMap.size === 0) {\n sections.delete(sectionIndex)\n }\n log.debug?.(`unregister sec=${sectionIndex} item=${itemIndex}`)\n }\n },\n\n // === Queries ===\n\n getPosition(sectionIndex: number, itemIndex: number): ScrollRect | undefined {\n return sections.get(sectionIndex)?.get(itemIndex)?.rect\n },\n\n hasSection(sectionIndex: number): boolean {\n const sectionMap = sections.get(sectionIndex)\n return sectionMap !== undefined && sectionMap.size > 0\n },\n\n getItemCount(sectionIndex: number): number {\n return sections.get(sectionIndex)?.size ?? 0\n },\n\n // === Cross-axis navigation ===\n\n findItemAtY(sectionIndex: number, targetY: number): number {\n const sectionMap = sections.get(sectionIndex)\n if (!sectionMap || sectionMap.size === 0) return -1\n\n // First pass: intersection with item bounding box\n for (const [idx, entry] of sectionMap) {\n const top = entry.rect.y\n const bottom = top + entry.rect.height\n if (targetY >= top && targetY < bottom) return idx\n }\n\n // Second pass: closest midpoint\n let closestIdx = -1\n let closestDist = Infinity\n for (const [idx, entry] of sectionMap) {\n const mid = entry.rect.y + entry.rect.height / 2\n const dist = Math.abs(mid - targetY)\n if (dist < closestDist) {\n closestDist = dist\n closestIdx = idx\n }\n }\n\n // If above all items, return -1 (section header)\n const firstEntry = sectionMap.get(0)\n if (firstEntry && targetY < firstEntry.rect.y) return -1\n\n return closestIdx\n },\n\n findInsertionSlot(sectionIndex: number, targetY: number): number {\n const sectionMap = sections.get(sectionIndex)\n if (!sectionMap || sectionMap.size === 0) return 0\n\n const sorted = Array.from(sectionMap.entries()).sort((a, b) => a[0] - b[0])\n\n for (let i = 0; i < sorted.length; i++) {\n const entry = sorted[i]!\n if (targetY < entry[1].rect.y) return i\n }\n\n return sorted.length\n },\n\n // === Lifecycle ===\n\n clear(): void {\n sections.clear()\n log.debug?.(\"cleared all positions\")\n },\n\n dump(): string {\n const lines: string[] = []\n\n if (sections.size === 0) {\n lines.push(\"(no items registered)\")\n } else {\n for (const [secIdx, sectionMap] of sections) {\n const entries = Array.from(sectionMap.entries())\n .sort((a, b) => a[0] - b[0])\n .map(([idx, entry]) => `${idx}:y${entry.rect.y}:h${entry.rect.height}`)\n .join(\", \")\n lines.push(`sec[${secIdx}]: ${entries}`)\n }\n }\n\n return lines.join(\"\\n\")\n },\n }\n\n return registry\n}\n\n// =============================================================================\n// React Context\n// =============================================================================\n\nconst PositionRegistryContext = createContext<PositionRegistry | null>(null)\n\n/**\n * Provider that creates a PositionRegistry for descendant components.\n * Wrap the root of any 2D grid layout with this provider.\n */\nexport function PositionRegistryProvider({ children }: { children: ReactNode }) {\n const registry = useMemo(() => createPositionRegistry(), [])\n return (\n <PositionRegistryContext.Provider value={registry}>{children}</PositionRegistryContext.Provider>\n )\n}\n\n/**\n * Access the position registry from any descendant of PositionRegistryProvider.\n * Returns null if no provider is found (opt-in, no crash).\n */\nexport function usePositionRegistry(): PositionRegistry | null {\n return useContext(PositionRegistryContext)\n}\n\n// Export for testing\nexport { createPositionRegistry }\n","/**\n * useGridPosition — auto-register an item's screen position in the PositionRegistry.\n *\n * Uses useScrollRect for zero-rerender position tracking.\n * Automatically unregisters on unmount (prevents stale entries).\n *\n * @example\n * ```tsx\n * function Item({ sectionIndex, itemIndex }) {\n * useGridPosition(sectionIndex, itemIndex)\n * return <Box>...</Box>\n * }\n * ```\n */\n\nimport { useEffect, useRef } from \"react\"\nimport { useScrollRect } from \"./useLayout\"\nimport { usePositionRegistry } from \"./usePositionRegistry\"\n\n/**\n * Register the current component's screen position in the PositionRegistry.\n *\n * Must be called from within a Box (needs NodeContext for screen rect).\n * No-ops gracefully if no PositionRegistryProvider is present.\n */\nexport function useGridPosition(sectionIndex: number, itemIndex: number): void {\n const registry = usePositionRegistry()\n\n // Track current indices in refs so the cleanup function always has the latest values\n const sectionRef = useRef(sectionIndex)\n const itemRef = useRef(itemIndex)\n sectionRef.current = sectionIndex\n itemRef.current = itemIndex\n\n // Register position on every layout update (no re-renders)\n useScrollRect((rect) => {\n registry?.register(sectionRef.current, itemRef.current, rect)\n })\n\n // Unregister on unmount\n useEffect(() => {\n return () => {\n registry?.unregister(sectionRef.current, itemRef.current)\n }\n }, [registry])\n\n // If indices change, unregister old position\n useEffect(() => {\n return () => {\n registry?.unregister(sectionIndex, itemIndex)\n }\n }, [registry, sectionIndex, itemIndex])\n}\n","/**\n * GridCell — auto-registering wrapper for items in a 2D grid.\n *\n * Wraps a child component and automatically registers its screen position\n * in the PositionRegistry. Unregisters on unmount.\n *\n * @example\n * ```tsx\n * <VirtualList\n * items={column.items}\n * renderItem={(item, idx) => (\n * <GridCell sectionIndex={colIndex} itemIndex={idx}>\n * <Card {...item} />\n * </GridCell>\n * )}\n * />\n * ```\n */\n\nimport type { ReactNode } from \"react\"\nimport { Box } from \"../../components/Box\"\nimport { useGridPosition } from \"../../hooks/useGridPosition\"\n\nexport interface GridCellProps {\n /** Section index (e.g., column index in a kanban board). */\n sectionIndex: number\n /** Item index within the section. */\n itemIndex: number\n /** Child content to render. */\n children: ReactNode\n}\n\n/**\n * A thin wrapper that auto-registers its screen position in the PositionRegistry.\n *\n * Renders a transparent Box (no visual impact) around children.\n * Position tracking uses useScrollRect (zero re-renders).\n */\nexport function GridCell({ sectionIndex, itemIndex, children }: GridCellProps) {\n useGridPosition(sectionIndex, itemIndex)\n return <Box>{children}</Box>\n}\n","/**\n * Silvery Transform Component\n *\n * Applies a string transformation to each line of rendered text output.\n * Compatible with ink's Transform component.\n *\n * Transform must be applied only to Text children and should not change\n * the dimensions of the output — otherwise layout will be incorrect.\n *\n * @example\n * ```tsx\n * import { Transform, Text } from '@silvery/ag-react'\n *\n * // Uppercase all text\n * <Transform transform={output => output.toUpperCase()}>\n * <Text>Hello World</Text>\n * </Transform>\n *\n * // Add line numbers\n * <Transform transform={(line, index) => `${index + 1}: ${line}`}>\n * <Text>First line{'\\n'}Second line</Text>\n * </Transform>\n * ```\n */\n\nimport type { JSX, ReactNode } from \"react\"\n\n// ============================================================================\n// Props\n// ============================================================================\n\nexport interface TransformProps {\n /** Function that transforms each line of output */\n transform: (line: string, index: number) => string\n /** Text content (string, number, or nested Text elements) */\n children?: ReactNode\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * Transform applies a string transform to rendered text output.\n *\n * Works by passing `internal_transform` to the underlying `silvery-text` host\n * element, which the render pipeline applies to each formatted line.\n */\nexport function Transform({ transform, children }: TransformProps): JSX.Element | null {\n if (children === undefined || children === null) {\n return null\n }\n\n return <silvery-text internal_transform={transform}>{children}</silvery-text>\n}\n","/**\n * Silvery Newline Component\n *\n * Renders a newline character. Useful for adding vertical spacing in text.\n */\n\nimport type { JSX } from \"react\"\n\nexport interface NewlineProps {\n /** Number of newlines to render (default: 1) */\n count?: number\n}\n\n/**\n * Renders one or more newline characters.\n *\n * @example\n * ```tsx\n * <Text>Line 1</Text>\n * <Newline />\n * <Text>Line 3 (after blank line)</Text>\n *\n * <Newline count={2} />\n * ```\n */\nexport function Newline({ count = 1 }: NewlineProps): JSX.Element {\n return <silvery-text>{\"\\n\".repeat(count)}</silvery-text>\n}\n","/**\n * Silvery Spacer Component\n *\n * A flexible space that expands to fill available space. Useful for pushing\n * elements to opposite ends of a container.\n */\n\nimport type { JSX } from \"react\"\n\n/**\n * Fills available space in the parent container.\n *\n * @example\n * ```tsx\n * // Push \"Right\" to the end\n * <Box flexDirection=\"row\">\n * <Text>Left</Text>\n * <Spacer />\n * <Text>Right</Text>\n * </Box>\n *\n * // Center element with equal spacing\n * <Box flexDirection=\"row\">\n * <Spacer />\n * <Text>Centered</Text>\n * <Spacer />\n * </Box>\n * ```\n */\nexport function Spacer(): JSX.Element {\n return <silvery-box flexGrow={1} />\n}\n","/**\n * Silvery Static Component\n *\n * Renders items that are written to the terminal once and never updated.\n * Useful for logs, progress outputs, or any content that should remain\n * visible after being rendered.\n *\n * Write-once semantics: when the items array grows, only newly added items\n * (at the end) are rendered via the children callback. Previously rendered\n * items are preserved as frozen React elements. This matches Ink's Static\n * behavior where each item is rendered exactly once.\n *\n * In inline mode (when promoteScrollback is available), Static renders\n * items to ANSI strings via renderStringSync and promotes them to\n * terminal scrollback. Items leave the React tree once promoted — the\n * terminal owns them.\n *\n * In fullscreen/test mode, items remain in the React tree as frozen elements.\n */\n\nimport { useContext, useRef, type JSX, type ReactNode } from \"react\"\nimport { StdoutContext, TermContext } from \"../context\"\nimport { renderStringSync } from \"../render-string\"\nimport { isLayoutEngineInitialized } from \"@silvery/ag-term/layout-engine\"\n\nexport interface StaticProps<T> {\n /** Items to render */\n items: T[]\n /** Render function for each item */\n children: (item: T, index: number) => ReactNode\n /** Style to apply to the container */\n style?: Record<string, unknown>\n}\n\n/**\n * Renders a list of items that are written once and never updated.\n *\n * Static content is rendered above the main UI and remains visible\n * even as the main UI updates. Each item is rendered only once.\n *\n * In inline mode, items are promoted to terminal scrollback and removed\n * from the React tree. In fullscreen/test mode, items stay in the tree.\n *\n * @example\n * ```tsx\n * const [logs, setLogs] = useState<string[]>([]);\n *\n * // Logs appear above the main UI and stay visible\n * <Static items={logs}>\n * {(log, index) => <Text key={index}>{log}</Text>}\n * </Static>\n *\n * // Main UI continues below\n * <Box>\n * <Text>Current status: processing...</Text>\n * </Box>\n * ```\n */\nexport function Static<T>({ items, children, style }: StaticProps<T>): JSX.Element {\n const stdoutCtx = useContext(StdoutContext)\n const term = useContext(TermContext)\n const promoteScrollback = stdoutCtx?.promoteScrollback\n\n // Track previously rendered items to implement write-once semantics.\n // Once an item has been rendered, its React element is frozen and reused\n // on subsequent renders — the children callback is NOT called again for it.\n const renderedRef = useRef<ReactNode[]>([])\n // Track how many items have been promoted to terminal scrollback (inline mode only)\n const promotedCountRef = useRef(0)\n\n // Render only new items (items beyond what we've already rendered)\n const prevCount = renderedRef.current.length\n if (items.length > prevCount) {\n for (let i = prevCount; i < items.length; i++) {\n renderedRef.current.push(children(items[i]!, i))\n }\n } else if (items.length < prevCount) {\n // Items were removed — truncate the rendered cache\n renderedRef.current.length = items.length\n // Also adjust promoted count if items were removed below promoted threshold\n if (promotedCountRef.current > items.length) {\n promotedCountRef.current = items.length\n }\n }\n\n // In inline mode, promote new items to terminal scrollback\n if (promoteScrollback && isLayoutEngineInitialized()) {\n const renderWidth = term?.cols ?? 80\n const prevPromoted = promotedCountRef.current\n\n // Promote all rendered items that haven't been promoted yet\n for (let i = prevPromoted; i < renderedRef.current.length; i++) {\n const element = renderedRef.current[i]\n if (!element) continue\n try {\n const ansi = renderStringSync(element as React.ReactElement, {\n width: renderWidth,\n plain: false,\n trimTrailingWhitespace: true,\n trimEmptyLines: false,\n })\n // Each promoted item: ANSI content + erase-to-end-of-line + newline\n const lines = ansi.split(\"\\n\")\n const frozenContent = lines.map((line) => `${line}\\x1b[K`).join(\"\\r\\n\") + \"\\r\\n\"\n promoteScrollback(frozenContent, lines.length)\n } catch {\n // Fallback: promote plain text placeholder\n promoteScrollback(`[static item ${i}]\\x1b[K\\r\\n`, 1)\n }\n }\n\n promotedCountRef.current = renderedRef.current.length\n\n // In inline mode, only render items not yet promoted to scrollback\n const liveElements = renderedRef.current.slice(promotedCountRef.current)\n if (liveElements.length === 0) {\n // All items promoted — render empty container to maintain tree structure\n return <silvery-box flexDirection=\"column\" {...style} />\n }\n return (\n <silvery-box flexDirection=\"column\" {...style}>\n {liveElements}\n </silvery-box>\n )\n }\n\n // Fullscreen/test mode: render all items in the tree\n return (\n <silvery-box flexDirection=\"column\" {...style}>\n {renderedRef.current}\n </silvery-box>\n )\n}\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(\n line: string,\n): { 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(\n filePath: string,\n line: number,\n): 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<\n SilveryErrorBoundaryProps,\n SilveryErrorBoundaryState\n> {\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(\n \"silvery-text\",\n { backgroundColor: \"red\", color: \"white\" },\n \" ERROR \",\n ),\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(\n \"silvery-text\",\n { dimColor: true },\n `${filePath}:${origin.line}:${origin.column}`,\n ),\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(\n \"silvery-box\",\n { key: \"code\", marginTop: 1, flexDirection: \"column\" },\n ...codeLines,\n ),\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(\n \"silvery-text\",\n { dimColor: true, bold: true },\n parsed.function ?? \"\",\n ),\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(\n \"silvery-box\",\n { key: \"stack\", marginTop: 1, flexDirection: \"column\" },\n ...stackLines,\n ),\n )\n }\n\n return React.createElement(\n \"silvery-box\",\n { flexDirection: \"column\", padding: 1 },\n ...children,\n )\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\"\nimport { syncFocusedSignal } from \"./layout-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 = (\n oldNode: AgNode | null,\n newNode: AgNode | null,\n origin: FocusOrigin | null,\n) => 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) {\n setFocused(oldElement, false)\n syncFocusedSignal(oldElement, false)\n }\n setFocused(node, true)\n syncFocusedSignal(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) {\n setFocused(oldElement, false)\n syncFocusedSignal(oldElement, false)\n }\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) {\n setFocused(oldElement, false)\n syncFocusedSignal(oldElement, false)\n }\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) {\n setFocused(oldElement, false)\n syncFocusedSignal(oldElement, false)\n }\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 syncFocusedSignal(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\n .map((n) => (n.props as Record<string, unknown>).testID as string | undefined)\n .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\n | ((e: SilveryFocusEvent) => void)\n | undefined\n if (handler) {\n mutableEvent.currentTarget = node\n handler(event)\n }\n }\n}\n","import type { Term } from \"@silvery/ag-term/ansi\"\nimport { useCallback, useContext, useRef, useSyncExternalStore } from \"react\"\nimport { TermContext } from \"../context\"\n\n/**\n * Shallow equality comparison for object selectors.\n *\n * @example\n * ```tsx\n * const { cols, rows } = useTerm(t => ({ cols: t.cols, rows: t.rows }), shallow)\n * ```\n */\nexport function shallow<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) return false\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\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 return true\n}\n\n/**\n * Hook to access the Term in components.\n *\n * Without a selector, returns the Term object (not reactive to state changes).\n * With a selector, returns the selected value reactively via useSyncExternalStore.\n *\n * @example\n * ```tsx\n * // Non-reactive: access Term for styling, I/O, etc.\n * const term = useTerm()\n * term.green('Success!')\n *\n * // Reactive: re-renders only when cols changes\n * const cols = useTerm(t => t.cols)\n *\n * // Reactive with shallow comparison for object selectors\n * const { cols, rows } = useTerm(t => ({ cols: t.cols, rows: t.rows }), shallow)\n * ```\n */\nexport function useTerm(): Term\nexport function useTerm<T>(selector: (term: Term) => T, equalityFn?: (a: T, b: T) => boolean): T\nexport function useTerm<T>(\n selector?: (term: Term) => T,\n equalityFn?: (a: T, b: T) => boolean,\n): Term | T {\n const term = useContext(TermContext)\n if (!term) {\n throw new Error(\"useTerm must be used within a render(element, term) context\")\n }\n\n if (!selector) {\n return term\n }\n\n return useTermSelector(term, selector, equalityFn)\n}\n\nfunction useTermSelector<T>(\n term: Term,\n selector: (term: Term) => T,\n equalityFn?: (a: T, b: T) => boolean,\n): T {\n const prevRef = useRef<T | undefined>(undefined)\n const isEqual = equalityFn ?? Object.is\n\n const subscribe = useCallback((listener: () => void) => term.subscribe(listener), [term])\n\n const getSnapshot = useCallback((): T => {\n const next = selector(term)\n if (prevRef.current !== undefined && isEqual(prevRef.current, next)) {\n return prevRef.current\n }\n prevRef.current = next\n return next\n }, [term, selector, isEqual])\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n","import { shallow, useTerm } from \"./useTerm\"\n\n/**\n * Hook to get the current terminal window size.\n * Re-renders when the terminal is resized.\n *\n * Uses term.getState() which always returns defined values (defaults to 80x24),\n * unlike term.cols/rows which may be undefined for non-TTY streams.\n *\n * @example\n * ```tsx\n * import { useWindowSize, Box, Text } from '@silvery/ag-react'\n *\n * function StatusBar() {\n * const { columns, rows } = useWindowSize()\n * return <Text>{`${columns}x${rows}`}</Text>\n * }\n * ```\n */\nexport function useWindowSize(): { columns: number; rows: number } {\n return useTerm((t) => {\n const s = t.getState()\n return { columns: s.cols, rows: s.rows }\n }, shallow)\n}\n","/**\n * ThemeProvider — delivers a Theme to the React component tree.\n *\n * Sets React context so `useTheme()` returns the active theme. Supports two\n * prop forms for progressive disclosure:\n *\n * ```tsx\n * // v2 API (preferred) — sparse or full token bag, merged over defaults\n * <ThemeProvider tokens={{ primary: \"#5B8DEF\", \"priority-p0\": \"#FF5555\" }}>\n * <App />\n * </ThemeProvider>\n *\n * // Legacy API — pass a full, pre-derived Theme (backwards compatible)\n * <ThemeProvider theme={detectedTheme}>\n * <App />\n * </ThemeProvider>\n * ```\n *\n * The `tokens` prop accepts a partial or full token bag. Sparse bags merge\n * over the app's base theme (from context or defaults); full bags replace it.\n * Either way, the merged result becomes the active theme for `useTheme()` and\n * `$token` resolution.\n *\n * The `variants` sub-object is deep-merged — passing\n * `tokens={{ variants: { hero: {...} } }}` adds to (not replaces) the\n * existing variants map from the inherited theme.\n *\n * The legacy `theme` prop still works and is equivalent to passing the whole\n * theme object as `tokens`. Passing both is an error (developer typo).\n *\n * Pipeline $token resolution uses the same `theme` prop on the inner Box —\n * no separate `<Box theme={}>` wrapper needed. Nested ThemeProviders each\n * scope their own Box, so inner themes never bleed into outer subtrees.\n *\n * The inner Box uses `flexGrow={1} flexShrink={1} alignSelf=\"stretch\"` so it\n * fills its parent (critical when children use `position=\"absolute\"` — without\n * these, an all-absolute child tree gives the Box zero content height, which\n * propagates null height to the root layout node and crashes the render phase).\n */\n\nimport React, { useContext, useMemo } from \"react\"\nimport { ThemeContext, ActiveSchemeContext } from \"./ThemeContext\"\nimport type { Theme, ActiveScheme } from \"@silvery/ansi\"\nimport { Box } from \"./components/Box\"\n\n/** Partial token bag — merged over the base theme. Accepts any Theme key, custom $tokens via app-defined keys, or a full Theme. */\nexport type ThemeTokens =\n | Partial<Theme>\n | (Partial<Theme> & Record<string, string | string[] | undefined>)\n | Theme\n\nexport interface ThemeProviderProps {\n /**\n * v2 API — a partial or full token bag. Merged over the inherited theme\n * (or default-dark/light if none). App-specific tokens (`\"priority-p0\"`,\n * `\"my-app-brand\"`) live in the same bag as standard tokens.\n */\n tokens?: ThemeTokens\n /**\n * Legacy API — a full, pre-derived Theme. Equivalent to passing the\n * whole object as `tokens`. Prefer `tokens` for new code.\n */\n theme?: Theme\n /**\n * Optional scheme detection metadata. When provided, makes\n * `useActiveScheme()` return this object for all descendants. Populated\n * automatically by `runThemed()`. Omitting it leaves any parent\n * ActiveSchemeContext unchanged (may be null).\n */\n scheme?: ActiveScheme\n children: React.ReactNode\n}\n\nexport function ThemeProvider({\n tokens,\n theme,\n scheme,\n children,\n}: ThemeProviderProps): React.ReactElement {\n const parent = useContext(ThemeContext)\n const merged = useMemo(() => {\n if (tokens && theme) {\n throw new Error(\n \"ThemeProvider: pass either `tokens` or `theme`, not both. `theme` is the legacy API; prefer `tokens`.\",\n )\n }\n if (theme) return theme\n if (!tokens) return parent\n // Sparse merge: parent theme + tokens override.\n // `variants` is deep-merged so `tokens={{ variants: { hero: {...} } }}` adds\n // to the existing variants map rather than replacing it entirely.\n const t = tokens as Record<string, unknown>\n const result = { ...parent, ...tokens } as Theme\n if (\n t[\"variants\"] !== null &&\n typeof t[\"variants\"] === \"object\" &&\n !Array.isArray(t[\"variants\"])\n ) {\n result.variants = {\n ...parent.variants,\n ...(t[\"variants\"] as Record<string, unknown>),\n } as Theme[\"variants\"]\n }\n return result\n }, [tokens, theme, parent])\n // Wrap children in a Box with theme= prop so the render pipeline picks up the\n // theme via the AgNode tree (same mechanism as color=\"inherit\" cascade). The\n // render phase calls pushContextTheme/popContextTheme when it encounters a node\n // with a theme prop, so $token resolution always uses the nearest ancestor theme\n // without relying on any module-level global.\n // flexGrow/flexShrink/alignSelf: ensure this Box fills its parent (column flex).\n // Required because children with position=\"absolute\" contribute zero content\n // height, so an auto-sized Box with only absolute children gets height=null\n // from Flexily, corrupting the root node's boxRect and crashing the render phase.\n const inner = (\n <ThemeContext.Provider value={merged}>\n <Box theme={merged} flexGrow={1} flexShrink={1} alignSelf=\"stretch\">\n {children}\n </Box>\n </ThemeContext.Provider>\n )\n if (scheme !== undefined) {\n return <ActiveSchemeContext.Provider value={scheme}>{inner}</ActiveSchemeContext.Provider>\n }\n return inner\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 * Hit Registry for Mouse Input\n *\n * Provides a registry for tracking clickable regions in the terminal UI.\n * Components register their screen positions, and mouse clicks are resolved\n * to the appropriate target based on position and z-index.\n *\n * The registry uses z-index ordering to handle overlapping elements:\n * - Dialogs (z-index 100+) take priority over cards\n * - Cards (z-index 10) take priority over background\n * - Background elements (z-index 0) are lowest priority\n *\n * This module re-exports the pure core (types, class, constants) and adds\n * React hooks/context. Import from @silvery/ag-term/hit-registry-core for the\n * React-free subset.\n */\n\nimport { createContext, useContext, useEffect, useRef } from \"react\"\nimport type { Rect } from \"@silvery/ag/types\"\n\n// Re-export everything from core\nexport {\n HitRegistry,\n generateHitRegionId,\n resetHitRegionIdCounter,\n Z_INDEX,\n} from \"./hit-registry-core\"\nexport type { HitTarget, HitRegion } from \"./hit-registry-core\"\n\n// Import for local use\nimport {\n type HitTarget,\n type HitRegion,\n HitRegistry,\n generateHitRegionId,\n} from \"./hit-registry-core\"\n\n// ============================================================================\n// React Context\n// ============================================================================\n\n/**\n * Context for accessing the HitRegistry.\n * Components use this to register their hit regions.\n */\nexport const HitRegistryContext = createContext<HitRegistry | null>(null)\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * Hook to get the HitRegistry from context.\n *\n * @returns The HitRegistry instance, or null if not in a HitRegistryContext\n */\nexport function useHitRegistry(): HitRegistry | null {\n return useContext(HitRegistryContext)\n}\n\n/**\n * Hook to register a hit region based on component's screen position.\n *\n * Automatically registers on mount and when position changes,\n * and unregisters on unmount.\n *\n * @param target - The target to return when this region is clicked\n * @param rect - The screen rectangle (from useScrollRect or similar)\n * @param zIndex - Z-index for layering (default: 0)\n * @param enabled - Whether the region is active (default: true)\n *\n * @example\n * ```tsx\n * function Card({ nodeId }: { nodeId: string }) {\n * const rect = useScrollRect();\n *\n * useHitRegion(\n * { type: 'node', nodeId },\n * rect,\n * 10 // z-index for cards\n * );\n *\n * return <Box>...</Box>;\n * }\n * ```\n */\nexport function useHitRegion(\n target: HitTarget,\n rect: Rect | null,\n zIndex = 0,\n enabled = true,\n): void {\n const registry = useContext(HitRegistryContext)\n const idRef = useRef<string | null>(null)\n\n // Generate stable ID on first use\n if (idRef.current === null) {\n idRef.current = generateHitRegionId()\n }\n\n useEffect(() => {\n if (!registry || !rect || !enabled) {\n // Clean up if disabled or no registry\n if (idRef.current && registry) {\n registry.unregister(idRef.current)\n }\n return\n }\n\n const id = idRef.current!\n\n // Register the region\n registry.register(id, {\n x: rect.x,\n y: rect.y,\n width: rect.width,\n height: rect.height,\n target,\n zIndex,\n })\n\n // Cleanup on unmount or when dependencies change\n return () => {\n registry.unregister(id)\n }\n }, [registry, rect?.x, rect?.y, rect?.width, rect?.height, target, zIndex, enabled])\n}\n\n/**\n * Hook to register a hit region using a callback for screen position.\n *\n * Similar to useHitRegion but works with useScrollRect for\n * better performance in large lists (avoids re-renders).\n *\n * @param target - The target to return when this region is clicked\n * @param zIndex - Z-index for layering (default: 0)\n * @param enabled - Whether the region is active (default: true)\n * @returns A callback to pass to useScrollRect\n *\n * @example\n * ```tsx\n * function Card({ nodeId }: { nodeId: string }) {\n * const onLayout = useHitRegionCallback(\n * { type: 'node', nodeId },\n * 10 // z-index\n * );\n *\n * useScrollRect(onLayout);\n *\n * return <Box>...</Box>;\n * }\n * ```\n */\nexport function useHitRegionCallback(\n target: HitTarget,\n zIndex = 0,\n enabled = true,\n): (rect: Rect) => void {\n const registry = useContext(HitRegistryContext)\n const idRef = useRef<string | null>(null)\n\n // Generate stable ID on first use\n if (idRef.current === null) {\n idRef.current = generateHitRegionId()\n }\n\n // Cleanup on unmount\n useEffect(() => {\n const id = idRef.current\n return () => {\n if (id && registry) {\n registry.unregister(id)\n }\n }\n }, [registry])\n\n // Return callback that updates the region\n return (rect: Rect) => {\n if (!registry || !enabled) {\n if (idRef.current && registry) {\n registry.unregister(idRef.current)\n }\n return\n }\n\n registry.register(idRef.current!, {\n x: rect.x,\n y: rect.y,\n width: rect.width,\n height: rect.height,\n target,\n zIndex,\n })\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 | {\n type: \"start\"\n col: number\n row: number\n scope?: SelectionScope | null\n source?: \"mouse\" | \"keyboard\"\n }\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(\n buffer: TerminalBuffer,\n row: number,\n): { 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(\n buffer: TerminalBuffer,\n range: SelectionRange,\n options?: ExtractTextOptions,\n): 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(\n { type: \"start\", col, row, source: \"mouse\" },\n selectionState,\n )\n updateState(newState, effects)\n },\n\n handleMouseMove(col: number, row: number): void {\n if (!selectionState.selecting) return\n const [newState, effects] = terminalSelectionUpdate(\n { type: \"extend\", col, row, buffer: buffer! },\n selectionState,\n )\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(\n { type: \"finish\" },\n extendState,\n )\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 * Adapter-aware render pipeline.\n *\n * Runs the full measure → layout → scroll → render → flush pipeline\n * using the current RenderAdapter (terminal, canvas, DOM, etc.).\n *\n * Split out from pipeline/index.ts — the adapter path orchestrates\n * phases directly (no createAg) since it uses renderPhaseAdapter\n * and RenderBuffer instead of the terminal-specific renderPhase.\n */\n\nimport { createLogger } from \"loggily\"\nimport type { AgNode } from \"@silvery/ag/types\"\nimport type { ExecuteRenderOptions } from \"./index\"\nimport { type RenderBuffer, getRenderAdapter, hasRenderAdapter } from \"../render-adapter\"\nimport { renderPhaseAdapter } from \"./render-phase-adapter\"\nimport { clearBgConflictWarnings } from \"./render-phase\"\nimport {\n layoutPhase,\n notifyLayoutSubscribers,\n scrollrectPhase,\n scrollPhase,\n stickyPhase,\n} from \"./layout-phase\"\nimport { measurePhase } from \"./measure-phase\"\n\nconst log = createLogger(\"silvery:render\")\nconst baseLog = createLogger(\"@silvery/ag-react\")\n\n/**\n * Execute the full render pipeline using the current RenderAdapter.\n *\n * This version works with any adapter (terminal, canvas, etc.) and returns\n * a RenderBuffer instead of a TerminalBuffer.\n *\n * @param root The root SilveryNode\n * @param width Width in adapter units (cells for terminal, pixels for canvas)\n * @param height Height in adapter units\n * @param prevBuffer Previous buffer for diffing (null on first render)\n * @param options Render options\n * @returns Object with output (if any) and current buffer\n */\nexport function executeRenderAdapter(\n root: AgNode,\n width: number,\n height: number,\n prevBuffer: RenderBuffer | null,\n options: ExecuteRenderOptions | \"fullscreen\" | \"inline\" = \"fullscreen\",\n): { output: string | void; buffer: RenderBuffer } {\n if (!hasRenderAdapter()) {\n throw new Error(\"executeRenderAdapter called without a render adapter set\")\n }\n\n const opts: ExecuteRenderOptions = typeof options === \"string\" ? { mode: options } : options\n const { skipLayoutNotifications = false } = opts\n const start = Date.now()\n const adapter = getRenderAdapter()\n\n using render = baseLog.span?.(\"pipeline-adapter\", {\n width,\n height,\n adapter: adapter.name,\n })\n\n // Clear per-render caches\n clearBgConflictWarnings()\n\n // Phase 1: Measure\n {\n using _measure = render?.span(\"measure\")\n measurePhase(root)\n log.debug?.(`measure phase complete`)\n }\n\n // Phase 2: Layout\n {\n using _layout = render?.span(\"layout\")\n layoutPhase(root, width, height)\n log.debug?.(`layout phase complete`)\n }\n\n // Phase 2.5: Scroll calculation\n {\n using _scroll = render?.span(\"scroll\")\n scrollPhase(root)\n }\n\n // Phase 2.55: Sticky phase (non-scroll container sticky children)\n stickyPhase(root)\n\n // Phase 2.6: Screen rect calculation\n {\n using _scrollrect = render?.span(\"scrollRect\")\n scrollrectPhase(root)\n }\n\n // Phase 2.7: Notify layout subscribers\n if (!skipLayoutNotifications) {\n using _notify = render?.span(\"notify\")\n notifyLayoutSubscribers(root)\n }\n\n // Phase 3: Content render (adapter-aware)\n let buffer: RenderBuffer\n {\n using _content = render?.span(\"content\")\n buffer = renderPhaseAdapter(root)\n log.debug?.(`content phase complete`)\n }\n\n // Phase 4: Flush via adapter\n let output: string | void\n {\n using outputSpan = render?.span(\"output\")\n output = adapter.flush(buffer, prevBuffer)\n if (typeof output === \"string\" && outputSpan) {\n outputSpan.spanData.bytes = output.length\n }\n log.debug?.(`output phase complete`)\n }\n\n log.debug?.(`total pipeline: ${Date.now() - start}ms`)\n\n return { output, buffer }\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(\n attrs.underlineColor ?? style.fg,\n this.config.foregroundColor,\n )\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(\n px + wx + waveLength / 2,\n underlineY - amplitude,\n px + wx + waveLength,\n underlineY,\n )\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(\n `background-color: ${resolveColor(run.style.bg, this.config.backgroundColor)}`,\n )\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 * 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 * OSC 4 Terminal Color Palette — re-exported from @silvery/ansi (canonical location).\n */\n\nexport {\n queryPaletteColor,\n queryMultiplePaletteColors,\n setPaletteColor,\n parsePaletteResponse,\n} 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(\n ` Detected: color=${caps.colorLevel} dark=${caps.darkBackground} nerdfont=${caps.nerdfont}\\n`,\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(\n \"After 4:x (clean?)\",\n `${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(\n row(\"58;2;R;G;B TC UL\", `${sgr(4)}${CSI}58;2;255;128;0mThe quick brown fox${RESET}`) + \"\\n\",\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 * 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 * 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<\n T extends { textEmojiWide: boolean; textSizingSupported: boolean },\n>(caps: T, config: TerminalWidthConfig): 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:\n 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 * 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 * 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 * 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?.(\n `createMouseEvent(${type}) metaKey=${metaKey} keyboardMods.super=${keyboardMods?.super}`,\n )\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\n | ((e: SilveryMouseEvent) => void)\n | 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 &&\n timeDelta <= DOUBLE_CLICK_TIME_MS &&\n dx <= DOUBLE_CLICK_DISTANCE &&\n 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(\n prevPath: AgNode[],\n nextPath: AgNode[],\n): { 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(\n options?: MouseEventProcessorOptions,\n): 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(\n state: MouseEventProcessorState,\n parsed: ParsedMouse,\n root: AgNode,\n): 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 * 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(\n mode: ResolvedNonTTYMode,\n): (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 * 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 {\n isDirty,\n CONTENT_BIT,\n STYLE_PROPS_BIT,\n BG_BIT,\n SUBTREE_BIT,\n CHILDREN_BIT,\n} 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 doRender 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(\n rootNode: AgNode,\n options?: { depth?: number; showLayout?: boolean },\n): 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.layoutNode?.isDirty()) 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 * 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 { setActiveColorLevel } from \"./pipeline/state\"\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(\n caps\n ? { textEmojiWide: caps.textEmojiWide, textSizingEnabled: caps.textSizingSupported }\n : {},\n )\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 // Mirror colorLevel into module-scoped theme state so render-helpers\n // (parseColor, getTextStyle) can dispatch on tier without access to\n // OutputContext. At mono tier (\"none\"), $tokens resolve to null fg/bg and\n // getTextStyle injects per-token SGR attrs from DEFAULT_MONO_ATTRS so apps\n // keep hierarchy (bold / dim / italic / underline / inverse) when color is\n // unavailable. See hub/silvery/design/v10-terminal/theme-system-v2-plan.md#p4.\n if (caps?.colorLevel) setActiveColorLevel(caps.colorLevel)\n return { measurer, outputPhaseFn }\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 {\n type SelectionRange,\n type SelectionScope,\n normalizeRange,\n} 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(\n buffer: TerminalBuffer,\n changes: SelectionCellChange[],\n): 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 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 {\n createOsc52Backend,\n createInternalClipboardBackend,\n createCompositeClipboard,\n} 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 {\n AdvancedClipboard,\n AdvancedClipboardOptions,\n ClipboardEntry,\n} from \"./ansi/advanced-clipboard\"\n\n// =============================================================================\n// OSC 4 Palette Color Query/Set\n// =============================================================================\n\nexport {\n queryPaletteColor,\n setPaletteColor,\n parsePaletteResponse,\n queryMultiplePaletteColors,\n} 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 {\n runTermtest,\n TERMTEST_SECTIONS,\n type TermtestSection,\n type TermtestOptions,\n} 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 createBgModeDetector,\n parseBgModeResponse,\n ENABLE_BG_MODE_REPORTING,\n DISABLE_BG_MODE_REPORTING,\n type BgModeDetector,\n type BgModeDetectorOptions,\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 {\n createWidthMeasurer,\n createMeasurer,\n runWithMeasurer,\n type Measurer,\n type WidthMeasurer,\n} 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 {\n detectCursor,\n detectInput,\n detectColor,\n detectUnicode,\n detectExtendedUnderline,\n} 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 {\n createVirtualScrollback,\n type VirtualScrollback,\n type VirtualScrollbackOptions,\n} from \"./virtual-scrollback\"\n\n// =============================================================================\n// History Buffer\n// =============================================================================\n\nexport {\n createHistoryBuffer,\n createHistoryItem,\n type HistoryItem,\n type HistoryBuffer,\n} from \"./history-buffer\"\n\n// =============================================================================\n// Viewport Compositor\n// =============================================================================\n\nexport {\n composeViewport,\n type ViewportCompositorConfig,\n type ComposedViewport,\n} from \"./viewport-compositor\"\n\n// =============================================================================\n// List Document\n// =============================================================================\n\nexport {\n createListDocument,\n type ListDocument,\n type DocumentSource,\n type LiveItemBlock,\n} 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 * withCommands - SlateJS-style plugin for command system\n *\n * Adds a `cmd` object to the App for direct command invocation with metadata.\n *\n * @example\n * ```tsx\n * const app = withCommands(render(<Board />), {\n * registry: commandRegistry,\n * getContext: () => buildCommandContext(state),\n * handleAction: (action) => dispatch(action),\n * getKeybindings: () => keybindings,\n * })\n *\n * // Direct command invocation\n * await app.cmd.down()\n * await app.cmd['cursor_down']()\n *\n * // Command metadata\n * app.cmd.down.id // 'cursor_down'\n * app.cmd.down.name // 'Move Down'\n * app.cmd.down.help // 'Move cursor down'\n * app.cmd.down.keys // ['j', 'ArrowDown']\n *\n * // Introspection\n * app.cmd.all() // All commands with metadata\n * app.getState() // { screen, commands, focus } for AI\n * ```\n *\n * See docs/future/silvery-command-api-research.md for design rationale.\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Minimal app interface required by the command system.\n * The full App type (from @silvery/ag-term/app) satisfies this.\n * Using a minimal interface avoids a layer violation (commands → ag-term).\n */\nexport interface CommandableApp {\n /** Full rendered text (no ANSI codes) — used by getState() for AI introspection */\n readonly text: string\n /** Send a key press — used by withKeybindings to intercept and route to commands */\n press(key: string): Promise<unknown>\n}\n\n/**\n * Generic command definition interface.\n * Compatible with @km/commands CommandDef but doesn't require the dependency.\n */\nexport interface CommandDef<TContext = unknown, TAction = unknown> {\n id: string\n name: string\n description: string\n shortcuts?: string[]\n execute: (ctx: TContext) => TAction | TAction[] | null\n}\n\n/**\n * Generic keybinding interface.\n * Compatible with @km/commands Keybinding.\n */\nexport interface KeybindingDef {\n key: string\n commandId: string\n ctrl?: boolean\n alt?: boolean\n opt?: boolean\n shift?: boolean\n cmd?: boolean\n}\n\n/**\n * Generic command registry interface.\n */\nexport interface CommandRegistryLike<TContext = unknown, TAction = unknown> {\n get(id: string): CommandDef<TContext, TAction> | undefined\n getAll(): CommandDef<TContext, TAction>[]\n}\n\n/**\n * Command metadata exposed on the cmd object.\n */\nexport interface CommandInfo {\n id: string\n name: string\n description: string\n keys: readonly string[]\n}\n\n/**\n * A callable command with metadata.\n */\nexport interface Command {\n (): Promise<void>\n readonly id: string\n readonly name: string\n readonly help: string\n readonly keys: readonly string[]\n}\n\n/**\n * The cmd object added to the app.\n *\n * Provides both method-style (`cmd.down()`) and index-style (`cmd['cursor_down']()`)\n * access to commands. Uses Proxy for dynamic lookup.\n */\nexport interface Cmd {\n [key: string]: Command | (() => CommandInfo[]) | (() => string) | undefined\n /** Get all commands with metadata */\n all(): CommandInfo[]\n /** Get human/AI readable description of all commands */\n describe(): string\n}\n\n/**\n * Options for withCommands.\n *\n * @typeParam TContext - The context type passed to command execute()\n * @typeParam TAction - The action type returned by command execute()\n */\nexport interface WithCommandsOptions<TContext, TAction> {\n /** Command registry with get() and getAll() */\n registry: CommandRegistryLike<TContext, TAction>\n /** Build context for command execution */\n getContext: () => TContext\n /** Handle actions returned by command execution */\n handleAction: (action: TAction) => void\n /** Get keybindings for command metadata (optional) */\n getKeybindings?: () => KeybindingDef[]\n}\n\n/**\n * App state for AI introspection.\n */\nexport interface AppState {\n screen: string\n commands: CommandInfo[]\n focus?: { id: string; text: string }\n}\n\n/**\n * App with command system.\n * Generic over the app type — preserves the full type when used with App.\n */\nexport type AppWithCommands<TApp extends CommandableApp = CommandableApp> = TApp & {\n cmd: Cmd\n getState(): AppState\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Find command by short name or full id.\n *\n * Supports both:\n * - Exact id match: `cmd['cursor_down']`\n * - Short name match: `cmd.down` (matches 'cursor_down', 'navigation_down', etc.)\n */\nfunction findCommand<TContext, TAction>(\n registry: CommandRegistryLike<TContext, TAction>,\n key: string,\n): CommandDef<TContext, TAction> | undefined {\n // Try exact id match first\n const byId = registry.get(key)\n if (byId) return byId\n\n // Try short name (last segment after underscore or dot)\n const all = registry.getAll()\n return all.find((c) => {\n const shortName = c.id.split(/[._]/).pop()\n return shortName === key\n })\n}\n\n/**\n * Get keys bound to a command.\n */\nfunction getKeysForCommand(commandId: string, keybindings?: KeybindingDef[]): readonly string[] {\n if (!keybindings) return []\n return keybindings\n .filter((kb) => kb.commandId === commandId)\n .map((kb) => {\n const parts: string[] = []\n if (kb.cmd) parts.push(\"Cmd\")\n if (kb.ctrl) parts.push(\"Ctrl\")\n if (kb.alt || kb.opt) parts.push(\"Alt\")\n if (kb.shift) parts.push(\"Shift\")\n parts.push(kb.key)\n return parts.join(\"+\")\n })\n}\n\n/**\n * Format help text for all commands.\n */\nfunction formatHelp<TContext, TAction>(\n registry: CommandRegistryLike<TContext, TAction>,\n keybindings?: KeybindingDef[],\n): string {\n const commands = registry.getAll()\n const lines = commands.map((cmd) => {\n const keys = getKeysForCommand(cmd.id, keybindings)\n const keyStr = keys.length > 0 ? ` [${keys.join(\", \")}]` : \"\"\n return `${cmd.id}${keyStr}: ${cmd.description}`\n })\n return lines.join(\"\\n\")\n}\n\n/**\n * Add command system to an App.\n *\n * Supports two calling styles:\n * - Direct: `withCommands(app, options)` — returns enhanced app immediately\n * - Curried: `withCommands(options)` — returns a plugin for pipe() composition\n *\n * @example Direct\n * ```tsx\n * const app = withCommands(render(<Board />), {\n * registry: commandRegistry,\n * getContext: () => buildContext(state),\n * handleAction: (action) => dispatch(action),\n * })\n * ```\n *\n * @example Curried (pipe)\n * ```tsx\n * const app = pipe(\n * baseApp,\n * withCommands({\n * registry: commandRegistry,\n * getContext: () => buildContext(state),\n * handleAction: (action) => dispatch(action),\n * }),\n * )\n * ```\n */\n// Curried form: withCommands(options) => plugin\nexport function withCommands<TContext, TAction>(\n options: WithCommandsOptions<TContext, TAction>,\n): <TApp extends CommandableApp>(app: TApp) => AppWithCommands<TApp>\n// Direct form: withCommands(app, options) => enhancedApp\nexport function withCommands<TApp extends CommandableApp, TContext, TAction>(\n app: TApp,\n options: WithCommandsOptions<TContext, TAction>,\n): AppWithCommands<TApp>\nexport function withCommands<TContext, TAction>(\n appOrOptions: CommandableApp | WithCommandsOptions<TContext, TAction>,\n maybeOptions?: WithCommandsOptions<TContext, TAction>,\n): AppWithCommands | ((app: CommandableApp) => AppWithCommands) {\n // Curried form: first arg is options (no press/text/ansi = not an App)\n if (maybeOptions === undefined) {\n const options = appOrOptions as WithCommandsOptions<TContext, TAction>\n return (app: CommandableApp) => applyCommands(app, options)\n }\n // Direct form: first arg is app\n return applyCommands(appOrOptions as CommandableApp, maybeOptions)\n}\n\nfunction applyCommands<TContext, TAction>(\n app: CommandableApp,\n options: WithCommandsOptions<TContext, TAction>,\n): AppWithCommands {\n const { registry, getContext, handleAction, getKeybindings } = options\n\n const cmd = new Proxy({} as Cmd, {\n get(_, prop: string | symbol): unknown {\n // Handle symbol access (for JS internals)\n if (typeof prop === \"symbol\") return undefined\n\n // Introspection methods\n if (prop === \"all\") {\n return () => {\n const commands = registry.getAll()\n const keybindings = getKeybindings?.()\n return commands.map((c) => ({\n id: c.id,\n name: c.name,\n description: c.description,\n keys: getKeysForCommand(c.id, keybindings),\n }))\n }\n }\n\n if (prop === \"describe\") {\n return () => formatHelp(registry, getKeybindings?.())\n }\n\n // Look up command by short name or full id\n const def = findCommand(registry, prop)\n if (!def) return undefined\n\n // Build callable with metadata\n const fn = async () => {\n const ctx = getContext()\n const result = def.execute(ctx)\n if (result) {\n const actions = Array.isArray(result) ? result : [result]\n for (const action of actions) {\n handleAction(action)\n }\n }\n // Allow microtask to flush for test synchronization\n await Promise.resolve()\n }\n\n // Attach metadata\n const keybindings = getKeybindings?.()\n Object.defineProperties(fn, {\n id: { value: def.id, enumerable: true },\n name: { value: def.name, enumerable: true },\n help: { value: def.description, enumerable: true },\n keys: {\n value: getKeysForCommand(def.id, keybindings),\n enumerable: true,\n },\n })\n\n return fn as Command\n },\n\n has(_, prop): boolean {\n if (typeof prop === \"symbol\") return false\n if (prop === \"all\" || prop === \"describe\") return true\n return !!findCommand(registry, prop)\n },\n })\n\n // Build getState for AI introspection\n const getState = (): AppState => {\n const commands = registry.getAll()\n const keybindings = getKeybindings?.()\n return {\n screen: app.text,\n commands: commands.map((c) => ({\n id: c.id,\n name: c.name,\n description: c.description,\n keys: getKeysForCommand(c.id, keybindings),\n })),\n // Focus info would require DOM query - leave undefined for now\n focus: undefined,\n }\n }\n\n return Object.assign(app, { cmd, getState }) as AppWithCommands\n}\n","/**\n * withKeybindings - SlateJS-style plugin for keybinding wiring\n *\n * Intercepts `press()` calls and routes them to commands via keybinding resolution.\n * Commands not in the registry fall through to component useInput handlers.\n *\n * @example\n * ```tsx\n * const app = withKeybindings(withCommands(render(<Board />), cmdOpts), {\n * bindings: defaultKeybindings,\n * getKeyContext: () => ({ mode: 'normal', hasSelection: false, ... }),\n * })\n *\n * // Press 'j' → resolves to cursor_down → calls app.cmd.down()\n * await app.press('j')\n *\n * // Press 'x' (no binding) → passes through to useInput handlers\n * await app.press('x')\n * ```\n *\n * See docs/future/silvery-command-api-research.md for design rationale.\n */\n\nimport type { AppWithCommands, KeybindingDef } from \"./with-commands\"\nimport { parseHotkey } from \"./keys\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Context for keybinding resolution.\n * Used to match mode-specific bindings and conditional bindings.\n */\nexport interface KeybindingContext {\n mode: string\n hasSelection: boolean\n [key: string]: unknown\n}\n\n/**\n * Options for withKeybindings.\n */\nexport interface WithKeybindingsOptions {\n /** Keybindings to resolve */\n bindings: KeybindingDef[]\n /** Build context for keybinding resolution */\n getKeyContext: () => KeybindingContext\n}\n\n/**\n * Extended keybinding with mode support.\n */\nexport interface ExtendedKeybindingDef extends KeybindingDef {\n modes?: string[]\n when?: (ctx: KeybindingContext) => boolean\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n// parseKey replaced by parseHotkey from ./keys.js\n\n/**\n * Resolve a key press to a command ID using keybinding lookup.\n */\nfunction resolveKeybinding(\n key: string,\n modifiers: { ctrl: boolean; meta: boolean; shift: boolean; super: boolean },\n bindings: ExtendedKeybindingDef[],\n ctx: KeybindingContext,\n): string | null {\n for (const binding of bindings) {\n // Check key match\n if (binding.key !== key) continue\n\n // Check modifiers\n if (!!binding.ctrl !== !!modifiers.ctrl) continue\n if (!!binding.opt !== !!modifiers.meta) continue\n if (!!binding.cmd !== !!modifiers.super) continue\n\n // For single uppercase letters (A-Z), the shift key is implicit\n const isUppercaseLetter = key.length === 1 && key >= \"A\" && key <= \"Z\" && !binding.shift\n if (!isUppercaseLetter && !!binding.shift !== !!modifiers.shift) continue\n\n // alt removed — macOS uses opt (⌥), matched against modifiers.meta above\n\n // Check mode\n if (binding.modes && binding.modes.length > 0) {\n if (!binding.modes.includes(ctx.mode)) continue\n }\n\n // Check conditional\n if (binding.when && !binding.when(ctx)) continue\n\n return binding.commandId\n }\n return null\n}\n\n/**\n * Wire keybindings to command invocation.\n *\n * Intercepts `press()` and routes matching keys to commands.\n * Non-matching keys pass through to the original press handler.\n *\n * Supports two calling styles:\n * - Direct: `withKeybindings(app, options)` — returns enhanced app immediately\n * - Curried: `withKeybindings(options)` — returns a plugin for pipe() composition\n *\n * @example Direct\n * ```tsx\n * const app = withKeybindings(appWithCmd, {\n * bindings: defaultKeybindings,\n * getKeyContext: () => buildKeybindingContext(state),\n * })\n * ```\n *\n * @example Curried (pipe)\n * ```tsx\n * const app = pipe(\n * baseApp,\n * withCommands(cmdOpts),\n * withKeybindings({\n * bindings: defaultKeybindings,\n * getKeyContext: () => buildKeybindingContext(state),\n * }),\n * )\n * ```\n */\n// Curried form: withKeybindings(options) => plugin\nexport function withKeybindings(\n options: WithKeybindingsOptions,\n): <T extends AppWithCommands>(app: T) => T\n// Direct form: withKeybindings(app, options) => enhancedApp\nexport function withKeybindings<T extends AppWithCommands>(\n app: T,\n options: WithKeybindingsOptions,\n): T\nexport function withKeybindings<T extends AppWithCommands>(\n appOrOptions: T | WithKeybindingsOptions,\n maybeOptions?: WithKeybindingsOptions,\n): T | (<U extends AppWithCommands>(app: U) => U) {\n // Curried form: first arg is options (no press/text/ansi = not an App)\n if (maybeOptions === undefined) {\n const options = appOrOptions as WithKeybindingsOptions\n return <U extends AppWithCommands>(app: U) => applyKeybindings(app, options)\n }\n // Direct form: first arg is app\n return applyKeybindings(appOrOptions as T, maybeOptions)\n}\n\nfunction applyKeybindings<T extends AppWithCommands>(app: T, options: WithKeybindingsOptions): T {\n const { bindings, getKeyContext } = options\n const originalPress = app.press.bind(app)\n\n // Create a proxy to intercept press() while preserving all other properties\n return new Proxy(app, {\n get(target, prop, receiver) {\n if (prop === \"press\") {\n return async function interceptedPress(keyStr: string): Promise<T> {\n const { key, ...modifiers } = parseHotkey(keyStr)\n const ctx = getKeyContext()\n\n // Try to resolve to a command\n const commandId = resolveKeybinding(\n key,\n modifiers,\n bindings as ExtendedKeybindingDef[],\n ctx,\n )\n\n if (commandId) {\n const cmd = target.cmd[commandId]\n if (cmd) {\n await cmd()\n return receiver as T\n }\n }\n\n // Pass through to original press handler (for useInput)\n await originalPress(keyStr)\n return receiver as T\n }\n }\n return Reflect.get(target, prop, receiver)\n },\n })\n}\n","/**\n * Buffer comparison utility for differential rendering tests.\n *\n * Compares two terminal buffers cell-by-cell, returning the first\n * mismatch found (or null if buffers are identical).\n */\n\nimport { type Cell, type TerminalBuffer, cellEquals } from \"@silvery/ag-term/buffer\"\n\n/**\n * A single cell mismatch between two buffers.\n */\nexport interface BufferMismatch {\n /** Column of the mismatched cell */\n x: number\n /** Row of the mismatched cell */\n y: number\n /** Cell from buffer A (e.g., incremental render) */\n cellA: Cell\n /** Cell from buffer B (e.g., fresh render) */\n cellB: Cell\n}\n\n/**\n * Compare two terminal buffers cell-by-cell.\n *\n * @returns The first mismatch found, or null if buffers are identical.\n */\nexport function compareBuffers(a: TerminalBuffer, b: TerminalBuffer): BufferMismatch | null {\n const width = Math.max(a.width, b.width)\n const height = Math.max(a.height, b.height)\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const cellA = a.inBounds(x, y)\n ? a.getCell(x, y)\n : {\n char: \" \",\n fg: null,\n bg: null,\n underlineColor: null,\n attrs: {},\n wide: false,\n continuation: false,\n }\n const cellB = b.inBounds(x, y)\n ? b.getCell(x, y)\n : {\n char: \" \",\n fg: null,\n bg: null,\n underlineColor: null,\n attrs: {},\n wide: false,\n continuation: false,\n }\n\n if (!cellEquals(cellA, cellB)) {\n return { x, y, cellA, cellB }\n }\n }\n }\n\n return null\n}\n\n/**\n * Format a buffer mismatch for human-readable error output.\n */\nexport function formatMismatch(\n mismatch: BufferMismatch,\n context?: {\n incrementalText?: string\n freshText?: string\n seed?: number\n iteration?: number\n key?: string\n },\n): string {\n const { x, y, cellA, cellB } = mismatch\n const lines: string[] = [\n `Buffer mismatch at (${x}, ${y})`,\n ` incremental: char=${JSON.stringify(cellA.char)} fg=${JSON.stringify(cellA.fg)} bg=${JSON.stringify(cellA.bg)} attrs=${JSON.stringify(cellA.attrs)}`,\n ` fresh: char=${JSON.stringify(cellB.char)} fg=${JSON.stringify(cellB.fg)} bg=${JSON.stringify(cellB.bg)} attrs=${JSON.stringify(cellB.attrs)}`,\n ]\n\n if (context?.seed !== undefined) lines.push(` seed: ${context.seed}`)\n if (context?.iteration !== undefined) {\n lines.push(` iteration: ${context.iteration}`)\n }\n if (context?.key) lines.push(` key: ${JSON.stringify(context.key)}`)\n\n if (context?.incrementalText) {\n lines.push(\"\", \"--- incremental ---\", context.incrementalText)\n }\n if (context?.freshText) {\n lines.push(\"\", \"--- fresh ---\", context.freshText)\n }\n\n return lines.join(\"\\n\")\n}\n","/**\n * withDiagnostics - Plugin for buffer and rendering diagnostic checks\n *\n * Wraps the `cmd` object to check invariants after command execution:\n * - All commands: Check incremental vs fresh render\n * - Cursor moves: Also check buffer content stability\n * - Optional: ANSI replay verification (characters AND SGR styles)\n *\n * ## Design Note: Why wrap `cmd` instead of `sendInput`?\n *\n * The two approaches are complementary:\n *\n * 1. **`sendInput()` level** (in renderer.ts) — Already has `SILVERY_STRICT`\n * which catches ALL inputs regardless of how they arrive (raw key presses,\n * type(), press(), etc.). This is the right place for incremental render checks.\n *\n * 2. **`cmd` level** (this plugin) — Command-aware, can selectively check stability\n * for cursor moves only. Raw sendInput doesn't know which inputs are cursor\n * commands that should preserve content. Another option would be `withInput()`\n * which could wrap sendInput with awareness of what input was sent.\n *\n * This plugin focuses on the command-aware checks. For comprehensive incremental\n * render checking, use `SILVERY_STRICT=1` environment variable which enables all checks.\n *\n * @example\n * ```typescript\n * import { withDiagnostics } from '@silvery/ag-term/toolbelt';\n *\n * // All checks enabled by default when you call withDiagnostics()\n * const driver = withDiagnostics(createBoardDriver(repo, rootId));\n *\n * // Or disable specific checks\n * const driver = withDiagnostics(createBoardDriver(repo, rootId), {\n * checkReplay: false // skip ANSI replay check\n * });\n *\n * // Commands now run invariant checks automatically\n * await driver.cmd.down(); // Checks incremental + stability + replay\n * await driver.cmd.search(); // Checks incremental + replay\n * ```\n */\n\nimport { mkdir } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport { type Color, type TerminalBuffer, colorEquals } from \"../buffer\"\nimport { outputPhase } from \"../pipeline\"\nimport { compareBuffers, formatMismatch } from \"@silvery/test/compare-buffers\"\nimport type { BoxProps, AgNode } from \"@silvery/create/types\"\nimport type { App } from \"../app\"\nimport type { AppWithCommands, Cmd, Command } from \"@silvery/commands/with-commands\"\n\n/** App with both full App capabilities and command system */\ntype FullAppWithCommands = AppWithCommands<App>\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface DiagnosticOptions {\n /** Check incremental vs fresh render (default: true when plugin is used) */\n checkIncremental?: boolean\n /** Check buffer stability for cursor commands (default: true when plugin is used) */\n checkStability?: boolean\n /** Check ANSI replay produces correct result (default: true when plugin is used) */\n checkReplay?: boolean\n /** Check layout tree integrity after each command (default: true when plugin is used) */\n checkLayout?: boolean\n /** Lines to skip for stability check (e.g., [0, -1] for breadcrumb/statusbar) */\n skipLines?: number[]\n /** Capture screenshot on failure (default: false) */\n captureOnFailure?: boolean\n /** Directory for failure screenshots (default: /tmp/silvery-diagnostics) */\n screenshotDir?: string\n}\n\n/**\n * Text mismatch between before and after states\n */\ninterface TextMismatch {\n line: number\n before: string\n after: string\n}\n\n/**\n * ANSI replay mismatch (character content)\n */\ninterface ReplayMismatch {\n x: number\n y: number\n expected: string\n actual: string\n}\n\n/**\n * SGR style attributes tracked per cell in the virtual terminal.\n */\ninterface VTermStyle {\n fg: number | { r: number; g: number; b: number } | null\n bg: number | { r: number; g: number; b: number } | null\n bold: boolean\n dim: boolean\n italic: boolean\n underline: boolean\n blink: boolean\n inverse: boolean\n hidden: boolean\n strikethrough: boolean\n}\n\n/**\n * ANSI replay style mismatch\n */\ninterface StyleMismatch {\n x: number\n y: number\n char: string\n diffs: string[]\n}\n\n// =============================================================================\n// VirtualTerminal - ANSI Replay Simulator\n// =============================================================================\n\n/**\n * Virtual terminal simulator for testing ANSI replay equivalence.\n *\n * Parses ANSI sequences and applies them to a 2D grid tracking both\n * character content and SGR style attributes (fg, bg, bold, italic, etc.).\n * Used to verify the Replay Equivalence invariant: applying the ANSI\n * diff to the previous buffer state should produce the target buffer.\n *\n * Handles:\n * - Cursor positioning (H, G, A, B, C, D)\n * - Line clear (K)\n * - Wide characters (emojis, CJK)\n * - CR/LF\n * - SGR style sequences (m) — fg/bg colors, text attributes\n */\nexport class VirtualTerminal {\n private grid: string[][]\n private wideMarker: boolean[][]\n private styles: VTermStyle[][]\n private sgr: VTermStyle\n private cursorX = 0\n private cursorY = 0\n\n constructor(\n public readonly width: number,\n public readonly height: number,\n ) {\n this.grid = Array.from({ length: height }, () => Array(width).fill(\" \"))\n this.wideMarker = Array.from({ length: height }, () => Array(width).fill(false))\n this.styles = Array.from({ length: height }, () =>\n Array.from({ length: width }, () => createDefaultVTermStyle()),\n )\n this.sgr = createDefaultVTermStyle()\n }\n\n /**\n * Initialize grid from a TerminalBuffer (for incremental replay).\n * Loads both character content and style attributes.\n */\n loadFromBuffer(buffer: TerminalBuffer): void {\n for (let y = 0; y < Math.min(this.height, buffer.height); y++) {\n for (let x = 0; x < Math.min(this.width, buffer.width); x++) {\n if (buffer.isCellContinuation(x, y)) {\n this.wideMarker[y]![x] = true\n this.grid[y]![x] = \"\"\n // Continuation cells have default style\n this.styles[y]![x] = createDefaultVTermStyle()\n } else {\n this.grid[y]![x] = buffer.getCellChar(x, y)\n this.wideMarker[y]![x] = false\n // Load style from buffer cell\n const cell = buffer.getCell(x, y)\n this.styles[y]![x] = {\n fg: cell.fg,\n bg: cell.bg,\n bold: !!cell.attrs.bold,\n dim: !!cell.attrs.dim,\n italic: !!cell.attrs.italic,\n underline: !!cell.attrs.underline || !!cell.attrs.underlineStyle,\n blink: !!cell.attrs.blink,\n inverse: !!cell.attrs.inverse,\n hidden: !!cell.attrs.hidden,\n strikethrough: !!cell.attrs.strikethrough,\n }\n }\n }\n }\n }\n\n /**\n * Apply ANSI escape sequence string to the virtual terminal.\n */\n applyAnsi(ansi: string): void {\n let i = 0\n while (i < ansi.length) {\n if (ansi[i] === \"\\x1b\" && ansi[i + 1] === \"[\") {\n const match = ansi.slice(i).match(/^\\x1b\\[([0-9;:?]*)([A-Za-z])/)\n if (match) {\n this.handleCsi(match[1] || \"\", match[2]!)\n i += match[0].length\n continue\n }\n }\n\n // OSC sequences: \\x1b] ... (ST or BEL)\n // e.g. OSC 8 hyperlinks: \\x1b]8;;url\\x1b\\\\ or \\x1b]8;;url\\x07\n if (ansi[i] === \"\\x1b\" && ansi[i + 1] === \"]\") {\n // Find terminator: ST (\\x1b\\\\) or BEL (\\x07)\n let j = i + 2\n while (j < ansi.length) {\n if (ansi[j] === \"\\x07\") {\n j++\n break\n }\n if (ansi[j] === \"\\x1b\" && ansi[j + 1] === \"\\\\\") {\n j += 2\n break\n }\n j++\n }\n i = j\n continue\n }\n\n if (ansi[i] === \"\\r\") {\n this.cursorX = 0\n i++\n continue\n }\n\n if (ansi[i] === \"\\n\") {\n this.cursorY = Math.min(this.cursorY + 1, this.height - 1)\n i++\n continue\n }\n\n // Handle multi-byte Unicode characters\n const char = this.extractChar(ansi, i)\n if (this.cursorX < this.width && this.cursorY < this.height) {\n this.grid[this.cursorY]![this.cursorX] = char\n this.wideMarker[this.cursorY]![this.cursorX] = false\n // Apply current SGR state to the cell\n this.styles[this.cursorY]![this.cursorX] = { ...this.sgr }\n this.cursorX++\n\n // Wide characters take 2 columns\n if (this.isWideChar(char) && this.cursorX < this.width) {\n this.grid[this.cursorY]![this.cursorX] = \"\"\n this.wideMarker[this.cursorY]![this.cursorX] = true\n // Continuation cell gets default style\n this.styles[this.cursorY]![this.cursorX] = createDefaultVTermStyle()\n this.cursorX++\n }\n }\n i += char.length\n }\n }\n\n /**\n * Check if a character is wide (emoji, CJK, etc).\n */\n private isWideChar(char: string): boolean {\n if (char.length === 0) return false\n\n // Characters with VS16 (U+FE0F) are emoji presentation = 2 columns\n if (char.includes(\"\\uFE0F\")) return true\n\n const code = char.codePointAt(0) || 0\n\n // Emoji ranges\n if (code >= 0x1f300 && code <= 0x1f9ff) return true\n if (code >= 0x2600 && code <= 0x26ff) return true\n if (code >= 0x2700 && code <= 0x27bf) return true\n\n // CJK ranges\n if (code >= 0x4e00 && code <= 0x9fff) return true\n if (code >= 0x3000 && code <= 0x303f) return true\n if (code >= 0xff00 && code <= 0xffef) return true\n\n return false\n }\n\n /**\n * Extract a single Unicode character (which may be multiple bytes).\n * Includes VS16 (U+FE0F) if it follows, since VS16 is a presentation selector\n * that modifies the preceding character's rendering width.\n */\n private extractChar(str: string, start: number): string {\n const code = str.codePointAt(start)\n if (code === undefined) return str[start] || \"\"\n let char: string\n if (code > 0xffff) {\n char = String.fromCodePoint(code)\n } else {\n char = str[start] || \"\"\n }\n // Absorb VS16 (U+FE0F) if it follows — it's a presentation modifier\n const nextIdx = start + char.length\n if (nextIdx < str.length && str.codePointAt(nextIdx) === 0xfe0f) {\n char += \"\\uFE0F\"\n }\n return char\n }\n\n private handleCsi(params: string, cmd: string): void {\n switch (cmd) {\n case \"H\": {\n const parts = params.split(\";\")\n this.cursorY = Math.max(0, (Number.parseInt(parts[0] || \"1\", 10) || 1) - 1)\n this.cursorX = Math.max(0, (Number.parseInt(parts[1] || \"1\", 10) || 1) - 1)\n break\n }\n case \"G\": {\n this.cursorX = Math.max(0, (Number.parseInt(params || \"1\", 10) || 1) - 1)\n break\n }\n case \"A\": {\n const n = Number.parseInt(params || \"1\", 10) || 1\n this.cursorY = Math.max(0, this.cursorY - n)\n break\n }\n case \"B\": {\n const n = Number.parseInt(params || \"1\", 10) || 1\n this.cursorY = Math.min(this.height - 1, this.cursorY + n)\n break\n }\n case \"C\": {\n const n = Number.parseInt(params || \"1\", 10) || 1\n this.cursorX = Math.min(this.width - 1, this.cursorX + n)\n break\n }\n case \"D\": {\n const n = Number.parseInt(params || \"1\", 10) || 1\n this.cursorX = Math.max(0, this.cursorX - n)\n break\n }\n case \"K\": {\n const mode = Number.parseInt(params || \"0\", 10)\n // Erase in Line: fills cleared cells with current bg (per ECMA-48)\n if (mode === 0) {\n for (let x = this.cursorX; x < this.width; x++) {\n this.grid[this.cursorY]![x] = \" \"\n this.wideMarker[this.cursorY]![x] = false\n this.styles[this.cursorY]![x] = {\n ...createDefaultVTermStyle(),\n bg: this.sgr.bg,\n }\n }\n } else if (mode === 1) {\n for (let x = 0; x <= this.cursorX; x++) {\n this.grid[this.cursorY]![x] = \" \"\n this.wideMarker[this.cursorY]![x] = false\n this.styles[this.cursorY]![x] = {\n ...createDefaultVTermStyle(),\n bg: this.sgr.bg,\n }\n }\n } else if (mode === 2) {\n for (let x = 0; x < this.width; x++) {\n this.grid[this.cursorY]![x] = \" \"\n this.wideMarker[this.cursorY]![x] = false\n this.styles[this.cursorY]![x] = {\n ...createDefaultVTermStyle(),\n bg: this.sgr.bg,\n }\n }\n }\n break\n }\n case \"m\":\n // SGR (Select Graphic Rendition) — apply style changes\n this.applySgr(params)\n break\n case \"l\":\n case \"h\":\n // Private modes — ignore for replay\n break\n }\n }\n\n /**\n * Apply SGR parameters to the current style state.\n * Parses the semicolon-separated parameter string and updates this.sgr.\n */\n private applySgr(params: string): void {\n if (params === \"\" || params === \"0\") {\n // Reset all attributes\n Object.assign(this.sgr, createDefaultVTermStyle())\n return\n }\n\n const parts = params.split(\";\")\n let i = 0\n while (i < parts.length) {\n const code = parts[i]!\n // Handle subparameters (e.g., \"4:3\" for curly underline)\n const colonIdx = code.indexOf(\":\")\n if (colonIdx >= 0) {\n const mainCode = Number.parseInt(code.substring(0, colonIdx), 10)\n if (mainCode === 4) {\n const sub = Number.parseInt(code.substring(colonIdx + 1), 10)\n this.sgr.underline = sub > 0\n }\n i++\n continue\n }\n\n const n = Number.parseInt(code, 10)\n if (n === 0) {\n Object.assign(this.sgr, createDefaultVTermStyle())\n } else if (n === 1) {\n this.sgr.bold = true\n } else if (n === 2) {\n this.sgr.dim = true\n } else if (n === 3) {\n this.sgr.italic = true\n } else if (n === 4) {\n this.sgr.underline = true\n } else if (n === 5 || n === 6) {\n this.sgr.blink = true\n } else if (n === 7) {\n this.sgr.inverse = true\n } else if (n === 8) {\n this.sgr.hidden = true\n } else if (n === 9) {\n this.sgr.strikethrough = true\n } else if (n === 22) {\n this.sgr.bold = false\n this.sgr.dim = false\n } else if (n === 23) {\n this.sgr.italic = false\n } else if (n === 24) {\n this.sgr.underline = false\n } else if (n === 25) {\n this.sgr.blink = false\n } else if (n === 27) {\n this.sgr.inverse = false\n } else if (n === 28) {\n this.sgr.hidden = false\n } else if (n === 29) {\n this.sgr.strikethrough = false\n } else if (n >= 30 && n <= 37) {\n // Standard foreground colors (0-7)\n this.sgr.fg = n - 30\n } else if (n === 38) {\n // Extended foreground color\n if (i + 1 < parts.length && parts[i + 1] === \"5\" && i + 2 < parts.length) {\n // 256-color: \\x1b[38;5;Nm\n this.sgr.fg = Number.parseInt(parts[i + 2]!, 10)\n i += 2\n } else if (i + 1 < parts.length && parts[i + 1] === \"2\" && i + 4 < parts.length) {\n // True color: \\x1b[38;2;R;G;Bm\n this.sgr.fg = {\n r: Number.parseInt(parts[i + 2]!, 10),\n g: Number.parseInt(parts[i + 3]!, 10),\n b: Number.parseInt(parts[i + 4]!, 10),\n }\n i += 4\n }\n } else if (n === 39) {\n this.sgr.fg = null\n } else if (n >= 40 && n <= 47) {\n // Standard background colors (0-7)\n this.sgr.bg = n - 40\n } else if (n === 48) {\n // Extended background color\n if (i + 1 < parts.length && parts[i + 1] === \"5\" && i + 2 < parts.length) {\n // 256-color: \\x1b[48;5;Nm\n this.sgr.bg = Number.parseInt(parts[i + 2]!, 10)\n i += 2\n } else if (i + 1 < parts.length && parts[i + 1] === \"2\" && i + 4 < parts.length) {\n // True color: \\x1b[48;2;R;G;Bm\n this.sgr.bg = {\n r: Number.parseInt(parts[i + 2]!, 10),\n g: Number.parseInt(parts[i + 3]!, 10),\n b: Number.parseInt(parts[i + 4]!, 10),\n }\n i += 4\n }\n } else if (n === 49) {\n this.sgr.bg = null\n } else if (n >= 90 && n <= 97) {\n // Bright foreground colors (8-15)\n this.sgr.fg = n - 90 + 8\n } else if (n >= 100 && n <= 107) {\n // Bright background colors (8-15)\n this.sgr.bg = n - 100 + 8\n }\n // 58/59 (underline color) not tracked in diagnostic comparison\n i++\n }\n }\n\n /**\n * Get the character at a position.\n */\n getChar(x: number, y: number): string {\n if (this.wideMarker[y]?.[x]) return \"\"\n return this.grid[y]?.[x] ?? \" \"\n }\n\n /**\n * Get the style at a position.\n */\n getStyle(x: number, y: number): VTermStyle {\n return this.styles[y]?.[x] ?? createDefaultVTermStyle()\n }\n\n /**\n * Compare with a TerminalBuffer and return character mismatches.\n */\n compareToBuffer(buffer: TerminalBuffer): ReplayMismatch[] {\n const mismatches: ReplayMismatch[] = []\n for (let y = 0; y < Math.min(this.height, buffer.height); y++) {\n for (let x = 0; x < Math.min(this.width, buffer.width); x++) {\n if (buffer.isCellContinuation(x, y)) continue\n\n const expected = buffer.getCellChar(x, y)\n const actual = this.getChar(x, y)\n if (expected !== actual) {\n mismatches.push({ x, y, expected, actual })\n }\n }\n }\n return mismatches\n }\n\n /**\n * Compare styles with a TerminalBuffer and return style mismatches.\n * Checks fg, bg, bold, dim, italic, underline, blink, inverse, hidden, strikethrough.\n */\n compareStylesToBuffer(buffer: TerminalBuffer): StyleMismatch[] {\n const mismatches: StyleMismatch[] = []\n for (let y = 0; y < Math.min(this.height, buffer.height); y++) {\n for (let x = 0; x < Math.min(this.width, buffer.width); x++) {\n if (buffer.isCellContinuation(x, y)) continue\n\n const cell = buffer.getCell(x, y)\n const actual = this.getStyle(x, y)\n const diffs: string[] = []\n\n // Compare foreground color\n if (!colorEquals(actual.fg as Color, cell.fg)) {\n diffs.push(`fg: ${formatVTermColor(actual.fg)} vs ${formatVTermColor(cell.fg)}`)\n }\n // Compare background color\n if (!colorEquals(actual.bg as Color, cell.bg)) {\n diffs.push(`bg: ${formatVTermColor(actual.bg)} vs ${formatVTermColor(cell.bg)}`)\n }\n // Compare text attributes\n if (actual.bold !== !!cell.attrs.bold) {\n diffs.push(`bold: ${actual.bold} vs ${!!cell.attrs.bold}`)\n }\n if (actual.dim !== !!cell.attrs.dim) {\n diffs.push(`dim: ${actual.dim} vs ${!!cell.attrs.dim}`)\n }\n if (actual.italic !== !!cell.attrs.italic) {\n diffs.push(`italic: ${actual.italic} vs ${!!cell.attrs.italic}`)\n }\n // Underline: buffer can have underline or underlineStyle\n const expectedUnderline = !!cell.attrs.underline || !!cell.attrs.underlineStyle\n if (actual.underline !== expectedUnderline) {\n diffs.push(`underline: ${actual.underline} vs ${expectedUnderline}`)\n }\n if (actual.blink !== !!cell.attrs.blink) {\n diffs.push(`blink: ${actual.blink} vs ${!!cell.attrs.blink}`)\n }\n if (actual.inverse !== !!cell.attrs.inverse) {\n diffs.push(`inverse: ${actual.inverse} vs ${!!cell.attrs.inverse}`)\n }\n if (actual.hidden !== !!cell.attrs.hidden) {\n diffs.push(`hidden: ${actual.hidden} vs ${!!cell.attrs.hidden}`)\n }\n if (actual.strikethrough !== !!cell.attrs.strikethrough) {\n diffs.push(`strikethrough: ${actual.strikethrough} vs ${!!cell.attrs.strikethrough}`)\n }\n\n if (diffs.length > 0) {\n mismatches.push({ x, y, char: cell.char, diffs })\n }\n }\n }\n return mismatches\n }\n}\n\n/**\n * Create a default VTermStyle with all attributes reset to defaults.\n */\nfunction createDefaultVTermStyle(): VTermStyle {\n return {\n fg: null,\n bg: null,\n bold: false,\n dim: false,\n italic: false,\n underline: false,\n blink: false,\n inverse: false,\n hidden: false,\n strikethrough: false,\n }\n}\n\n/**\n * Format a color value for diagnostic display.\n */\nfunction formatVTermColor(c: number | { r: number; g: number; b: number } | null): string {\n if (c === null) return \"default\"\n if (typeof c === \"number\") return `${c}`\n return `rgb(${c.r},${c.g},${c.b})`\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** Commands that should preserve buffer content (only cursor position changes) */\nconst CURSOR_COMMANDS = new Set([\n // Full names\n \"cursor_up\",\n \"cursor_down\",\n \"cursor_left\",\n \"cursor_right\",\n // Short names\n \"up\",\n \"down\",\n \"left\",\n \"right\",\n])\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Parse SILVERY_STABILITY_SKIP_LINES environment variable.\n * Format: comma-separated integers, e.g., \"0,-1\"\n */\nfunction parseSkipLines(env?: string): number[] {\n if (!env) return []\n return env\n .split(\",\")\n .map((s) => Number.parseInt(s.trim(), 10))\n .filter((n) => !isNaN(n))\n}\n\n/**\n * Compare text content before and after a command.\n * Returns the first mismatch found, or null if content matches.\n *\n * @param before - Text before command execution\n * @param after - Text after command execution\n * @param skipLines - Line indices to skip (supports negative indices from end)\n */\nfunction compareText(before: string, after: string, skipLines: number[]): TextMismatch | null {\n const beforeLines = before.split(\"\\n\")\n const afterLines = after.split(\"\\n\")\n const maxLines = Math.max(beforeLines.length, afterLines.length)\n\n // Build set of lines to skip, resolving negative indices\n const skipSet = new Set<number>()\n for (const line of skipLines) {\n if (line >= 0) {\n skipSet.add(line)\n } else {\n // Negative index: -1 = last line, -2 = second to last, etc.\n skipSet.add(maxLines + line)\n }\n }\n\n for (let i = 0; i < maxLines; i++) {\n if (skipSet.has(i)) continue\n const b = beforeLines[i] ?? \"\"\n const a = afterLines[i] ?? \"\"\n if (b !== a) {\n return { line: i, before: b, after: a }\n }\n }\n return null\n}\n\n// =============================================================================\n// Layout Invariant Checks\n// =============================================================================\n\n/**\n * Check layout tree integrity. Returns violation messages, or empty array if valid.\n *\n * Checks:\n * - All rect dimensions are finite and non-negative (width >= 0, height >= 0)\n * - All positions are finite (x, y are valid numbers)\n * - No NaN values in computed layout\n * - Children don't overflow parent bounds (1px tolerance for rounding)\n * - Skips overflow check for nodes with overflow:hidden/scroll (they intentionally clip)\n */\nexport function checkLayoutInvariants(node: AgNode): string[] {\n const violations: string[] = []\n walkLayout(node, null, violations)\n return violations\n}\n\nfunction walkLayout(\n node: AgNode,\n parentRect: {\n x: number\n y: number\n width: number\n height: number\n clipped: boolean\n } | null,\n violations: string[],\n): void {\n const rect = node.boxRect\n if (!rect) return // No layout computed yet — skip\n\n const id = (node.props as BoxProps).id ?? node.type\n\n // Check finite and non-negative dimensions\n if (!Number.isFinite(rect.width) || rect.width < 0) {\n violations.push(`${id}: invalid width ${rect.width}`)\n }\n if (!Number.isFinite(rect.height) || rect.height < 0) {\n violations.push(`${id}: invalid height ${rect.height}`)\n }\n if (!Number.isFinite(rect.x)) {\n violations.push(`${id}: invalid x ${rect.x}`)\n }\n if (!Number.isFinite(rect.y)) {\n violations.push(`${id}: invalid y ${rect.y}`)\n }\n\n // Check children don't overflow parent (with 1px tolerance)\n if (parentRect && !parentRect.clipped) {\n const TOLERANCE = 1\n if (rect.x + rect.width > parentRect.x + parentRect.width + TOLERANCE) {\n violations.push(\n `${id}: overflows parent right (${rect.x + rect.width} > ${parentRect.x + parentRect.width})`,\n )\n }\n if (rect.y + rect.height > parentRect.y + parentRect.height + TOLERANCE) {\n violations.push(\n `${id}: overflows parent bottom (${rect.y + rect.height} > ${parentRect.y + parentRect.height})`,\n )\n }\n if (rect.x < parentRect.x - TOLERANCE) {\n violations.push(`${id}: overflows parent left (${rect.x} < ${parentRect.x})`)\n }\n if (rect.y < parentRect.y - TOLERANCE) {\n violations.push(`${id}: overflows parent top (${rect.y} < ${parentRect.y})`)\n }\n }\n\n // Determine if this node clips its children\n const overflow = (node.props as BoxProps).overflow\n const clipped = overflow === \"hidden\" || overflow === \"scroll\"\n\n const childParentRect = {\n x: rect.x,\n y: rect.y,\n width: rect.width,\n height: rect.height,\n clipped,\n }\n\n for (const child of node.children) {\n walkLayout(child, childParentRect, violations)\n }\n}\n\n// =============================================================================\n// Plugin Implementation\n// =============================================================================\n\n/**\n * Add diagnostic checking to an app with commands.\n *\n * Wraps the `cmd` proxy to intercept all command executions and run checks:\n * - **All commands**: Check that incremental render matches fresh render\n * - **Cursor commands**: Also check that buffer content didn't change\n *\n * **All checks are enabled by default** when you call this function.\n * The principle: if you wrapped with withDiagnostics(), you want diagnostics.\n *\n * @param app - App with command system (from withCommands)\n * @param options - Diagnostic check configuration (all enabled by default)\n * @returns App with wrapped cmd that runs diagnostic checks\n */\nexport function withDiagnostics<T extends FullAppWithCommands>(\n app: T,\n options: DiagnosticOptions = {},\n): T {\n // All checks enabled by default when plugin is used\n const {\n checkIncremental = true,\n checkStability = true,\n checkReplay = true,\n checkLayout = true,\n skipLines = parseSkipLines(process.env.SILVERY_STABILITY_SKIP_LINES),\n captureOnFailure = false,\n screenshotDir = \"/tmp/silvery-diagnostics\",\n } = options\n\n // If all checks are explicitly disabled, return app unchanged\n if (!checkIncremental && !checkStability && !checkReplay && !checkLayout) return app\n\n /** Capture screenshot on diagnostic failure (best-effort, never masks original error) */\n async function captureFailureScreenshot(\n commandId: string,\n checkType: string,\n ): Promise<string | null> {\n if (!captureOnFailure) return null\n try {\n await mkdir(screenshotDir, { recursive: true })\n const filename = `fail-${commandId}-${checkType}.png`\n const filepath = join(screenshotDir, filename)\n await app.screenshot(filepath)\n return filepath\n } catch {\n return null\n }\n }\n\n // Wrap the cmd proxy\n const wrappedCmd = new Proxy(app.cmd, {\n get(target, prop: string | symbol): unknown {\n // Handle symbol access (for JS internals)\n if (typeof prop === \"symbol\") return Reflect.get(target, prop)\n\n const original = Reflect.get(target, prop)\n\n // Pass through non-function properties and special methods\n if (typeof original !== \"function\") return original\n if (prop === \"all\" || prop === \"describe\") return original\n\n // Wrap command execution\n const command = original as Command\n const wrapped = async () => {\n // Capture state before command\n const beforeText = app.text\n const beforeBuffer = checkReplay ? app.lastBuffer() : null\n\n // Execute the original command\n await command()\n\n // Check 1: Incremental vs fresh render\n if (checkIncremental) {\n const incremental = app.lastBuffer()\n // freshRender() may throw if not available (non-test renderer)\n try {\n const fresh = app.freshRender()\n if (incremental && fresh) {\n const mismatch = compareBuffers(incremental, fresh)\n if (mismatch) {\n // Include full buffer text for debugging\n const incrementalText = app.text\n const freshText = fresh\n ? Array.from({ length: fresh.height }, (_, y) =>\n Array.from({ length: fresh.width }, (_, x) => fresh.getCellChar(x, y)).join(\n \"\",\n ),\n ).join(\"\\n\")\n : \"(no fresh buffer)\"\n const screenshotPath = await captureFailureScreenshot(command.id, \"incremental\")\n throw new Error(\n `SILVERY_DIAGNOSTIC: Incremental/fresh mismatch after ${command.id}\\n` +\n formatMismatch(mismatch, {\n key: command.id,\n incrementalText,\n freshText,\n }) +\n (screenshotPath ? `\\n Screenshot saved: ${screenshotPath}` : \"\"),\n )\n }\n }\n } catch (e) {\n // If freshRender isn't available, skip the check\n if (!(e instanceof Error) || !e.message.includes(\"only available in test renderer\")) {\n throw e\n }\n }\n }\n\n // Check 2: Content stability for cursor commands\n if (checkStability && CURSOR_COMMANDS.has(command.id)) {\n const afterText = app.text\n const mismatch = compareText(beforeText, afterText, skipLines)\n if (mismatch) {\n const screenshotPath = await captureFailureScreenshot(command.id, \"stability\")\n throw new Error(\n `SILVERY_DIAGNOSTIC: Content changed after cursor move ${command.id}\\n` +\n ` Line ${mismatch.line}: \"${mismatch.before}\" → \"${mismatch.after}\"` +\n (screenshotPath ? `\\n Screenshot saved: ${screenshotPath}` : \"\"),\n )\n }\n }\n\n // Check 3: ANSI replay produces correct result\n if (checkReplay && beforeBuffer) {\n const afterBuffer = app.lastBuffer()\n if (afterBuffer) {\n // Get the ANSI diff that would be sent to terminal\n const ansiDiff = outputPhase(beforeBuffer, afterBuffer)\n\n // Create virtual terminal initialized with previous state\n const vterm = new VirtualTerminal(afterBuffer.width, afterBuffer.height)\n vterm.loadFromBuffer(beforeBuffer)\n\n // Apply the ANSI diff\n vterm.applyAnsi(ansiDiff)\n\n // Compare character content\n const mismatches = vterm.compareToBuffer(afterBuffer)\n if (mismatches.length > 0) {\n const first5 = mismatches.slice(0, 5)\n const details = first5\n .map((m) => ` (${m.x},${m.y}): expected=\"${m.expected}\" actual=\"${m.actual}\"`)\n .join(\"\\n\")\n const screenshotPath = await captureFailureScreenshot(command.id, \"replay\")\n throw new Error(\n `SILVERY_DIAGNOSTIC: ANSI replay mismatch after ${command.id}\\n` +\n ` ${mismatches.length} cells differ:\\n${details}` +\n (mismatches.length > 5 ? `\\n ... and ${mismatches.length - 5} more` : \"\") +\n (screenshotPath ? `\\n Screenshot saved: ${screenshotPath}` : \"\"),\n )\n }\n\n // Compare SGR styles (fg, bg, bold, italic, underline, etc.)\n const styleMismatches = vterm.compareStylesToBuffer(afterBuffer)\n if (styleMismatches.length > 0) {\n const first5 = styleMismatches.slice(0, 5)\n const details = first5\n .map((m) => ` (${m.x},${m.y}) char=\"${m.char}\": ${m.diffs.join(\", \")}`)\n .join(\"\\n\")\n const screenshotPath = await captureFailureScreenshot(command.id, \"replay-style\")\n throw new Error(\n `SILVERY_DIAGNOSTIC: ANSI replay style mismatch after ${command.id}\\n` +\n ` ${styleMismatches.length} cells have style differences:\\n${details}` +\n (styleMismatches.length > 5\n ? `\\n ... and ${styleMismatches.length - 5} more`\n : \"\") +\n (screenshotPath ? `\\n Screenshot saved: ${screenshotPath}` : \"\"),\n )\n }\n }\n }\n\n // Check 4: Layout tree integrity\n if (checkLayout) {\n const root = app.getContainer()\n const violations = checkLayoutInvariants(root)\n if (violations.length > 0) {\n const details = violations\n .slice(0, 10)\n .map((v) => ` ${v}`)\n .join(\"\\n\")\n const screenshotPath = await captureFailureScreenshot(command.id, \"layout\")\n throw new Error(\n `SILVERY_DIAGNOSTIC: Layout invariant violation after ${command.id}\\n` +\n ` ${violations.length} violation(s):\\n${details}` +\n (violations.length > 10 ? `\\n ... and ${violations.length - 10} more` : \"\") +\n (screenshotPath ? `\\n Screenshot saved: ${screenshotPath}` : \"\"),\n )\n }\n }\n }\n\n // Copy metadata from original command\n Object.defineProperties(wrapped, {\n id: { value: command.id, enumerable: true },\n name: { value: command.name, enumerable: true },\n help: { value: command.help, enumerable: true },\n keys: { value: command.keys, enumerable: true },\n })\n\n return wrapped\n },\n\n has(target, prop): boolean {\n return Reflect.has(target, prop)\n },\n\n ownKeys(target): (string | symbol)[] {\n return Reflect.ownKeys(target)\n },\n\n getOwnPropertyDescriptor(target, prop): PropertyDescriptor | undefined {\n return Reflect.getOwnPropertyDescriptor(target, prop)\n },\n })\n\n // IMPORTANT: Use Object.assign instead of spread to preserve getters.\n // Spread `{ ...app }` snapshots getters (text, ansi, lastBuffer) as static\n // values from the initial render. Object.assign mutates the original object,\n // preserving its getters so they return current buffer state.\n return Object.assign(app, { cmd: wrappedCmd as Cmd }) as T\n}\n","/**\n * withRender(term) -- extends a Term with render pipeline capabilities.\n *\n * Creates term-scoped pipeline config (width measurer + output phase) from caps,\n * then returns an extended term with render() and renderStatic() methods.\n *\n * Usage:\n * const term = withRender(createTerm())\n * const { output, buffer } = term.render(root, 80, 24, null, { mode: \"fullscreen\" })\n * const html = await term.renderStatic(<Report />)\n */\n\nimport type { Term } from \"../ansi\"\nimport type { ReactElement } from \"react\"\nimport type { TerminalBuffer } from \"../buffer\"\nimport { createPipeline, type MeasuredTerm } from \"../measurer\"\nimport type { PipelineConfig, ExecuteRenderOptions } from \"../pipeline\"\nimport { outputPhase } from \"../pipeline/output-phase\"\nimport { createAg } from \"../ag\"\nimport { runWithMeasurer } from \"../unicode\"\nimport type { AgNode } from \"@silvery/create/types\"\n\n/**\n * Extended Term with render pipeline capabilities.\n *\n * Extends MeasuredTerm (Term + Measurer methods) with render/renderStatic.\n */\nexport interface RenderTerm extends MeasuredTerm {\n /** Pipeline configuration (measurer + output phase) */\n readonly pipelineConfig: PipelineConfig\n /**\n * Run the full render pipeline.\n */\n render(\n root: AgNode,\n width: number,\n height: number,\n prevBuffer: TerminalBuffer | null,\n options?: ExecuteRenderOptions | \"fullscreen\" | \"inline\",\n ): { output: string; buffer: TerminalBuffer }\n /**\n * Render a React element to a string using this terminal's caps.\n * Uses the term's width measurer for correct text measurement.\n */\n renderStatic(\n element: ReactElement,\n options?: { width?: number; height?: number; plain?: boolean },\n ): Promise<string>\n}\n\n/**\n * Extend a Term with render pipeline capabilities.\n *\n * Creates a pipeline config (width measurer + output phase) from the term's caps,\n * and adds render() and renderStatic() methods plus measurer methods.\n *\n * @param term - A Term instance (from createTerm)\n * @returns Extended term with render and measurement capabilities\n */\nexport function withRender(term: Term): RenderTerm {\n const pipelineConfig = createPipeline({ caps: term.caps })\n const { measurer } = pipelineConfig\n\n function renderPipeline(\n root: AgNode,\n width: number,\n height: number,\n prevBuffer: TerminalBuffer | null,\n options?: ExecuteRenderOptions | \"fullscreen\" | \"inline\",\n ): { output: string; buffer: TerminalBuffer } {\n const opts: ExecuteRenderOptions =\n typeof options === \"string\" ? { mode: options } : (options ?? {})\n const {\n mode = \"fullscreen\",\n skipLayoutNotifications = false,\n skipScrollStateUpdates = false,\n scrollbackOffset = 0,\n termRows,\n cursorPos,\n } = opts\n\n const doRender = () => {\n const ag = createAg(root, { measurer })\n ag.layout({ cols: width, rows: height }, { skipLayoutNotifications, skipScrollStateUpdates })\n const { buffer, overlay } = ag.render({ prevBuffer })\n\n const outputFn = pipelineConfig.outputPhaseFn ?? outputPhase\n let output = outputFn(prevBuffer, buffer, mode, scrollbackOffset, termRows, cursorPos)\n // Append Kitty emoji-scrim overlay when backdrop-fade produced one.\n if (overlay) output += overlay\n\n return { output, buffer }\n }\n\n return measurer ? runWithMeasurer(measurer, doRender) : doRender()\n }\n\n async function renderStaticFn(\n element: ReactElement,\n options?: { width?: number; height?: number; plain?: boolean },\n ): Promise<string> {\n const { renderString } = await import(\"@silvery/ag-react/render-string\")\n return renderString(element, { ...options, pipelineConfig })\n }\n\n // Return a proxy that extends the original term with measurer methods and render capabilities\n return Object.create(term, {\n // Measurer methods (from pipeline config)\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 // Pipeline config and render methods\n pipelineConfig: { value: pipelineConfig, enumerable: true },\n render: { value: renderPipeline, enumerable: true },\n renderStatic: { value: renderStaticFn, enumerable: true },\n }) as RenderTerm\n}\n","/**\n * withInkCursor() — Thin adapter bridging Ink's useCursor to silvery's CursorStore.\n *\n * Canonical home for InkCursorStoreCtx. Consumed by ink.ts (useCursor hook).\n *\n * @packageDocumentation\n */\nimport React, { createContext, Fragment } from \"react\"\nimport {\n createCursorStore,\n CursorProvider,\n type CursorStore,\n} from \"@silvery/ag-react/hooks/useCursor\"\nimport type { RunnableApp } from \"./with-ink\"\n\n// =============================================================================\n// Ink Cursor Store Context\n// =============================================================================\n\n/**\n * Context for passing cursor store to Ink compat useCursor hook.\n * This lets useCursor write directly to the store without going through\n * silvery's useCursor hook (which requires NodeContext for layout).\n */\nexport const InkCursorStoreCtx = createContext<CursorStore | null>(null)\n\n// =============================================================================\n// withInkCursor — App-level plugin for pipe() composition\n// =============================================================================\n\nexport interface WithInkCursorOptions {\n cursorStore?: CursorStore\n}\n\nexport interface AppWithInkCursor {\n readonly Root: React.ComponentType<{ children: React.ReactNode }>\n}\n\nexport function withInkCursor<T extends RunnableApp>(\n options: WithInkCursorOptions = {},\n): (app: T) => T & AppWithInkCursor {\n const cursorStore = options.cursorStore ?? createCursorStore()\n\n return (app: T): T & AppWithInkCursor => {\n const PrevRoot = app.Root ?? Fragment\n const InkCursorRoot = ({ children }: { children: React.ReactNode }) =>\n React.createElement(\n CursorProvider,\n { store: cursorStore },\n React.createElement(\n InkCursorStoreCtx.Provider,\n { value: cursorStore },\n React.createElement(PrevRoot, null, children),\n ),\n )\n\n return Object.assign(Object.create(app), {\n Root: InkCursorRoot,\n }) as T & AppWithInkCursor\n }\n}\n","/**\n * withInkFocus() — Plugin providing Ink's focus system.\n *\n * Canonical home for InkFocusContext, InkFocusProvider, and the Focusable type.\n * Consumed by ink.ts (useFocus/useFocusManager hooks).\n *\n * @packageDocumentation\n */\nimport React, { createContext, Fragment, useCallback, useEffect, useMemo, useState } from \"react\"\nimport type { EventEmitter } from \"node:events\"\nimport type { RunnableApp } from \"./with-ink\"\n\n// =============================================================================\n// Ink Focus Context & Provider\n// =============================================================================\n\n/** A focusable entry in the Ink focus system. */\ntype Focusable = { id: string; isActive: boolean }\n\nexport type InkFocusContextValue = {\n activeId: string | undefined\n isFocusEnabled: boolean\n add: (id: string, options: { autoFocus: boolean }) => void\n remove: (id: string) => void\n activate: (id: string) => void\n deactivate: (id: string) => void\n enableFocus: () => void\n disableFocus: () => void\n focusNext: () => void\n focusPrevious: () => void\n focus: (id: string) => void\n blur: () => void\n}\n\nexport const InkFocusContext = createContext<InkFocusContextValue>({\n activeId: undefined,\n isFocusEnabled: true,\n add() {},\n remove() {},\n activate() {},\n deactivate() {},\n enableFocus() {},\n disableFocus() {},\n focusNext() {},\n focusPrevious() {},\n focus() {},\n blur() {},\n})\n\n/**\n * Ink-compatible FocusProvider component.\n * Manages focus state: list of focusables, active focus ID, tab navigation.\n */\nexport function InkFocusProvider({\n children,\n inputEmitter,\n}: {\n children?: React.ReactNode\n inputEmitter?: EventEmitter\n}) {\n const [isFocusEnabled, setIsFocusEnabled] = useState(true)\n const [activeFocusId, setActiveFocusId] = useState<string | undefined>(undefined)\n const [, setFocusables] = useState<Focusable[]>([])\n const focusablesCountRef = React.useRef(0)\n\n const findNextFocusable = useCallback(\n (\n currentFocusables: Focusable[],\n currentActiveFocusId: string | undefined,\n ): string | undefined => {\n const activeIndex = currentFocusables.findIndex((f) => f.id === currentActiveFocusId)\n for (let i = activeIndex + 1; i < currentFocusables.length; i++) {\n if (currentFocusables[i]?.isActive) return currentFocusables[i]!.id\n }\n return undefined\n },\n [],\n )\n\n const findPreviousFocusable = useCallback(\n (\n currentFocusables: Focusable[],\n currentActiveFocusId: string | undefined,\n ): string | undefined => {\n const activeIndex = currentFocusables.findIndex((f) => f.id === currentActiveFocusId)\n for (let i = activeIndex - 1; i >= 0; i--) {\n if (currentFocusables[i]?.isActive) return currentFocusables[i]!.id\n }\n return undefined\n },\n [],\n )\n\n const focusNext = useCallback((): void => {\n setFocusables((currentFocusables) => {\n setActiveFocusId((currentActiveFocusId) => {\n const firstFocusableId = currentFocusables.find((f) => f.isActive)?.id\n const nextFocusableId = findNextFocusable(currentFocusables, currentActiveFocusId)\n return nextFocusableId ?? firstFocusableId\n })\n return currentFocusables\n })\n }, [findNextFocusable])\n\n const focusPrevious = useCallback((): void => {\n setFocusables((currentFocusables) => {\n setActiveFocusId((currentActiveFocusId) => {\n const lastFocusableId = currentFocusables.findLast((f) => f.isActive)?.id\n const previousFocusableId = findPreviousFocusable(currentFocusables, currentActiveFocusId)\n return previousFocusableId ?? lastFocusableId\n })\n return currentFocusables\n })\n }, [findPreviousFocusable])\n\n const enableFocus = useCallback((): void => {\n setIsFocusEnabled(true)\n }, [])\n const disableFocus = useCallback((): void => {\n setIsFocusEnabled(false)\n }, [])\n\n const focus = useCallback((id: string): void => {\n setFocusables((currentFocusables) => {\n if (currentFocusables.some((f) => f.id === id)) {\n setActiveFocusId(id)\n }\n return currentFocusables\n })\n }, [])\n\n const blur = useCallback((): void => {\n setActiveFocusId(undefined)\n }, [])\n\n const addFocusable = useCallback((id: string, { autoFocus }: { autoFocus: boolean }): void => {\n setFocusables((currentFocusables) => {\n focusablesCountRef.current = currentFocusables.length + 1\n return [...currentFocusables, { id, isActive: true }]\n })\n if (autoFocus) {\n setActiveFocusId((currentActiveFocusId) => {\n if (!currentActiveFocusId) return id\n return currentActiveFocusId\n })\n }\n }, [])\n\n const removeFocusable = useCallback((id: string): void => {\n setActiveFocusId((currentActiveFocusId) => {\n if (currentActiveFocusId === id) return undefined\n return currentActiveFocusId\n })\n setFocusables((currentFocusables) => {\n const filtered = currentFocusables.filter((f) => f.id !== id)\n focusablesCountRef.current = filtered.length\n return filtered\n })\n }, [])\n\n const activateFocusable = useCallback((id: string): void => {\n setFocusables((currentFocusables) =>\n currentFocusables.map((f) => (f.id === id ? { ...f, isActive: true } : f)),\n )\n }, [])\n\n const deactivateFocusable = useCallback((id: string): void => {\n setActiveFocusId((currentActiveFocusId) => {\n if (currentActiveFocusId === id) return undefined\n return currentActiveFocusId\n })\n setFocusables((currentFocusables) =>\n currentFocusables.map((f) => (f.id === id ? { ...f, isActive: false } : f)),\n )\n }, [])\n\n // Tab/Shift+Tab/Esc focus navigation via inputEmitter (raw escape sequences)\n useEffect(() => {\n if (!inputEmitter) return\n const tab = \"\\t\"\n const shiftTab = \"\\x1b[Z\"\n const escape = \"\\x1b\"\n const handleInput = (data: string | Buffer) => {\n const input = typeof data === \"string\" ? data : data.toString()\n if (!isFocusEnabled || focusablesCountRef.current === 0) return\n if (input === tab) focusNext()\n else if (input === shiftTab) focusPrevious()\n else if (input === escape) setActiveFocusId(undefined)\n }\n inputEmitter.on(\"input\", handleInput)\n return () => {\n inputEmitter.removeListener(\"input\", handleInput)\n }\n }, [isFocusEnabled, focusNext, focusPrevious, inputEmitter])\n\n const contextValue = useMemo(\n () => ({\n activeId: activeFocusId,\n isFocusEnabled,\n add: addFocusable,\n remove: removeFocusable,\n activate: activateFocusable,\n deactivate: deactivateFocusable,\n enableFocus,\n disableFocus,\n focusNext,\n focusPrevious,\n focus,\n blur,\n }),\n [\n activeFocusId,\n isFocusEnabled,\n addFocusable,\n removeFocusable,\n activateFocusable,\n deactivateFocusable,\n enableFocus,\n disableFocus,\n focusNext,\n focusPrevious,\n focus,\n blur,\n ],\n )\n\n return React.createElement(InkFocusContext.Provider, { value: contextValue }, children)\n}\n\n// =============================================================================\n// withInkFocus — App-level plugin for pipe() composition\n// =============================================================================\n\nexport interface WithInkFocusOptions {\n inputEmitter?: EventEmitter\n}\n\nexport interface AppWithInkFocus {\n readonly Root: React.ComponentType<{ children: React.ReactNode }>\n}\n\nexport function withInkFocus<T extends RunnableApp>(\n options: WithInkFocusOptions = {},\n): (app: T) => T & AppWithInkFocus {\n return (app: T): T & AppWithInkFocus => {\n const PrevRoot = app.Root ?? Fragment\n const InkFocusRoot = ({ children }: { children: React.ReactNode }) =>\n React.createElement(\n InkFocusProvider,\n { inputEmitter: options.inputEmitter },\n React.createElement(PrevRoot, null, children),\n )\n\n return Object.assign(Object.create(app), {\n Root: InkFocusRoot,\n }) as T & AppWithInkFocus\n }\n}\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 {\n isDirty,\n isAnyDirty,\n CONTENT_BIT,\n STYLE_PROPS_BIT,\n SUBTREE_BIT,\n CHILDREN_BIT,\n} 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 }\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 },\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)\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(\n `✓ Node index ${childIndex} is in visible range [${ss.firstVisibleChild}..${ss.lastVisibleChild}]`,\n )\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 (\n sibling.boxRect.x !== sibling.prevLayout.x ||\n sibling.boxRect.y !== sibling.prevLayout.y\n ) {\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(\n ctx: MismatchDebugContext,\n renderPhaseStats?: RenderPhaseStats,\n): string {\n const lines: string[] = []\n\n // Header\n lines.push(\n `SILVERY_STRICT: MISMATCH at (${ctx.position.x}, ${ctx.position.y}) on render #${ctx.renderNum}`,\n )\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}`,\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(\n ` nodesVisited: ${s.nodesVisited} nodesRendered: ${s.nodesRendered} nodesSkipped: ${s.nodesSkipped}`,\n )\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)\n 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(\n ` scrollContainers: ${s.scrollContainerCount} viewportCleared: ${s.scrollViewportCleared}`,\n )\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(\n lines: string[],\n scroll: NonNullable<NodeDebugInfo[\"scroll\"]>,\n indent = \" \",\n): 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 * 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 {\n getCursorState as globalGetCursorState,\n type CursorAccessors,\n} from \"@silvery/ag-react/hooks/useCursor\"\nimport { copyToClipboard as copyToClipboardImpl } from \"./clipboard\"\nimport { ANSI, notify as notifyTerminal, setCursorStyle, resetCursorStyle } from \"./output\"\nimport type { PipelineConfig } from \"./pipeline\"\nimport { outputPhase } from \"./pipeline/output-phase\"\nimport { createAg } from \"./ag\"\nimport { runWithMeasurer } from \"./unicode\"\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 =\n 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.doRender()\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.doRender()\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.doRender()\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.doRender()\n }\n }, delay)\n }\n\n /**\n * Execute the actual render.\n */\n private doRender(): 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?.(\n `render #${this.stats.renderCount + 1}: ${width}x${height}, nonTTYMode=${this.nonTTYMode}`,\n )\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 measurer = this.pipelineConfig?.measurer\n const doRender = () => {\n const ag = createAg(this.root, { measurer })\n ag.layout({ cols: width, rows: height })\n const { buffer, overlay } = ag.render({ prevBuffer: this.prevBuffer })\n\n const start = performance.now()\n const outputFn = this.pipelineConfig?.outputPhaseFn ?? outputPhase\n let ansiOutput: string\n try {\n ansiOutput = outputFn(\n this.prevBuffer,\n buffer,\n this.mode,\n scrollbackOffset,\n this.mode === \"inline\" ? (this.stdout.rows ?? 24) : undefined,\n inlineCursor,\n )\n } catch (e) {\n if (e instanceof Error) {\n ;(e as any).__silvery_buffer = buffer\n }\n throw e\n }\n // Append Kitty emoji-scrim overlay from the backdrop-fade pass.\n // No-op when backdrop inactive or cap disabled — zero-length string.\n if (overlay) ansiOutput += overlay\n const tOutput = performance.now() - start\n\n // Bench instrumentation: accumulate output-phase timing\n const acc = (globalThis as any).__silvery_bench_phases\n if (acc) {\n acc.output += tOutput\n acc.pipelineCalls += 1\n }\n\n return { output: ansiOutput, buffer, overlay }\n }\n const {\n output,\n buffer,\n overlay: incrementalOverlay,\n } = measurer ? runWithMeasurer(measurer, doRender) : doRender()\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 doFreshRender = () => {\n const freshAg = createAg(this.root, { measurer })\n freshAg.layout({ cols: width, rows: height }, { skipLayoutNotifications: true })\n return freshAg.render()\n }\n const { buffer: freshBuffer, overlay: freshOverlay } = measurer\n ? runWithMeasurer(measurer, doFreshRender)\n : doFreshRender()\n\n // STRICT overlay-plan comparison.\n //\n // Invariant: `applyBackdrop` is a pure function of (tree markers,\n // buffer cells, options) → overlay is deterministic. Incremental\n // and fresh paths MUST emit byte-identical overlay strings.\n //\n // A drift here signals non-determinism in marker collection order,\n // the emoji walk, or placement ID derivation — any of which would\n // cause scrim flicker or orphaned placements across frames.\n if (incrementalOverlay !== freshOverlay) {\n const msg = formatOverlayMismatch(\n incrementalOverlay,\n freshOverlay,\n renderNum,\n )\n if (process.env.DEBUG_LOG) {\n appendFileSync(process.env.DEBUG_LOG, msg + \"\\n\")\n }\n log.error?.(msg)\n throw new IncrementalRenderMismatchError(msg)\n }\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)\n .__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) /\n 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 =\n 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// STRICT Diagnostics Helpers\n// ============================================================================\n\n/**\n * Format a human-readable diff when the incremental and fresh Kitty overlays\n * disagree. The overlays are Kitty graphics protocol escape sequences that\n * place translucent \"scrim\" images over emoji cells inside faded regions\n * (see `pipeline/backdrop/`). They MUST be byte-identical across fresh and\n * incremental paths — any drift is a determinism bug in:\n *\n * - `collectBackdropMarkers` (tree walk order)\n * - `collectEmojiCellsInFadeRegion` (rect iteration)\n * - `backdropPlacementId` (per-cell ID derivation)\n * - `assertSingleAmount` (amount selection when markers disagree)\n *\n * The diff surfaces the placement IDs that differ — the Kitty protocol uses\n * `p=<id>` in each placement command, so grep-friendly IDs are the\n * quickest path from \"test failed\" to \"which cell drifted\".\n */\nfunction formatOverlayMismatch(incremental: string, fresh: string, renderNum: number): string {\n const incPlacements = extractPlacementIds(incremental)\n const freshPlacements = extractPlacementIds(fresh)\n\n const incSet = new Set(incPlacements)\n const freshSet = new Set(freshPlacements)\n\n const onlyIncremental = incPlacements.filter((id) => !freshSet.has(id))\n const onlyFresh = freshPlacements.filter((id) => !incSet.has(id))\n\n const lines: string[] = [\n `[SILVERY_STRICT] Kitty overlay mismatch at render #${renderNum}`,\n ` incremental length: ${incremental.length} bytes, placements: ${incPlacements.length}`,\n ` fresh length: ${fresh.length} bytes, placements: ${freshPlacements.length}`,\n ]\n if (onlyIncremental.length > 0) {\n lines.push(` only in incremental (moved/appeared): ${onlyIncremental.join(\", \")}`)\n }\n if (onlyFresh.length > 0) {\n lines.push(` only in fresh (missing/disappeared): ${onlyFresh.join(\", \")}`)\n }\n if (onlyIncremental.length === 0 && onlyFresh.length === 0) {\n lines.push(` placements match — drift is in scrim-image payload or ordering`)\n }\n\n // Truncated raw bytes for inspection. Full strings can be many KB (each\n // scrim image payload is base64-encoded) — cap to avoid log-flood.\n const cap = 400\n lines.push(\n ` incremental[0..${cap}]: ${JSON.stringify(incremental.slice(0, cap))}`,\n ` fresh [0..${cap}]: ${JSON.stringify(fresh.slice(0, cap))}`,\n )\n return lines.join(\"\\n\")\n}\n\n/**\n * Extract the list of `p=<id>` placement IDs from a Kitty overlay string, in\n * emission order. Used by the STRICT diagnostic to show which placements\n * moved/appeared/disappeared.\n */\nfunction extractPlacementIds(overlay: string): string[] {\n const ids: string[] = []\n const re = /p=(\\d+)/g\n let m: RegExpExecArray | null\n while ((m = re.exec(overlay)) !== null) {\n ids.push(m[1]!)\n }\n return ids\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 ag = createAg(root)\n ag.layout({ cols: width, rows: height })\n const { buffer } = ag.render()\n return outputPhase(null, buffer, \"fullscreen\")\n}\n","/**\n * withTerminalChain — apply-chain plugin for terminal observer + lifecycle.\n *\n * This is the substrate counterpart to `@silvery/ag-term/plugins/with-terminal`\n * (which wraps the test-harness `App`). Where that plugin cares about the\n * test-facing handle, this one plugs into the runtime apply chain that\n * `create-app.tsx`'s processEventBatch drives.\n *\n * ## Responsibilities\n *\n * - Observer lane: always update modifier state on `input:key` ops\n * (never consumes; always passes through to the next plugin).\n * - Resize handling: `term:resize` updates the stored dimensions\n * and schedules a `render` effect.\n * - Focus lifecycle: `term:focus` updates `focused` and clears\n * sticky modifiers on blur (a very common source of \"Ctrl stuck\n * down\" bugs after Alt-Tab).\n *\n * ## What this plugin does NOT do\n *\n * - It does NOT intercept key events (observer only).\n * - It does NOT implement raw-mode / alternate-screen / mouse /\n * kitty-keyboard setup — those belong to the terminal provider\n * (`createTermProvider`) that feeds ops *into* the chain.\n * - It does NOT handle Ctrl+C / Ctrl+Z — see\n * `runtime/lifecycle-effects.ts` for those.\n *\n * The separation matters: lifecycle-effects deals with keys that\n * *terminate* the app, while this plugin is a steady-state observer.\n */\n\nimport type { ApplyResult, Effect, Op } from \"../types\"\nimport type { BaseApp } from \"./base-app\"\n\n// ---------------------------------------------------------------------------\n// Store shape\n// ---------------------------------------------------------------------------\n\n/** Snapshot of the modifier state the terminal thinks is currently held. */\nexport interface ModifierState {\n readonly ctrl: boolean\n readonly shift: boolean\n readonly alt: boolean\n readonly meta: boolean\n readonly super: boolean\n readonly hyper: boolean\n}\n\n/** Store slice installed by {@link withTerminalChain}. */\nexport interface TerminalStore {\n /** Columns (from last resize or initial). */\n cols: number\n /** Rows. */\n rows: number\n /** Is the terminal focused right now (OSC 1004 focus reporting)? */\n focused: boolean\n /** Current modifier key state. */\n modifiers: ModifierState\n}\n\n// ---------------------------------------------------------------------------\n// Op payload types (shared with the runner and with other plugins)\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal key shape that withTerminalChain inspects on `input:key` ops.\n *\n * Deliberately structural (not imported from ag-term's `Key`) so this\n * package stays free of terminal imports. Any compatible shape works.\n */\nexport interface KeyShape {\n ctrl?: boolean\n shift?: boolean\n meta?: boolean\n super?: boolean\n hyper?: boolean\n alt?: boolean\n eventType?: \"press\" | \"repeat\" | \"release\" | undefined\n}\n\n/** Options accepted by {@link withTerminalChain}. */\nexport interface WithTerminalChainOptions {\n /** Initial columns. Default: 80. */\n cols?: number\n /** Initial rows. Default: 24. */\n rows?: number\n /** Initial focused state. Default: true. */\n focused?: boolean\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\n/**\n * Install the terminal observer + lifecycle plugin.\n *\n * The returned app exposes the terminal slice at `app.terminal`.\n * Plugins upstream (last in `pipe()`) see ops first; this plugin\n * should go near the BASE of the chain so focused/useInput plugins\n * handle actual key content before the terminal observer peeks at\n * modifier state.\n */\nexport function withTerminalChain(\n options: WithTerminalChainOptions = {},\n): <A extends BaseApp>(app: A) => A & { terminal: TerminalStore } {\n return <A extends BaseApp>(app: A): A & { terminal: TerminalStore } => {\n const store: TerminalStore = {\n cols: options.cols ?? 80,\n rows: options.rows ?? 24,\n focused: options.focused ?? true,\n modifiers: {\n ctrl: false,\n shift: false,\n alt: false,\n meta: false,\n super: false,\n hyper: false,\n },\n }\n const prev = app.apply\n app.apply = (op: Op): ApplyResult => {\n if (op.type === \"input:key\") {\n // Observer lane: peek the modifiers, never consume.\n const key = (op as { key?: KeyShape }).key\n if (key) {\n store.modifiers = {\n ctrl: !!key.ctrl,\n shift: !!key.shift,\n alt: !!key.alt || !!key.meta,\n meta: !!key.meta,\n super: !!key.super,\n hyper: !!key.hyper,\n }\n }\n return prev(op)\n }\n if (op.type === \"term:resize\") {\n const cols = (op as { cols?: number }).cols\n const rows = (op as { rows?: number }).rows\n if (typeof cols === \"number\") store.cols = cols\n if (typeof rows === \"number\") store.rows = rows\n const effects: Effect[] = [{ type: \"render\" }]\n // Chain downstream so anyone else (e.g. layout-aware plugins)\n // can also react.\n const downstream = prev(op)\n if (downstream !== false) effects.push(...downstream)\n return effects\n }\n if (op.type === \"term:focus\") {\n const focused = !!(op as { focused?: boolean }).focused\n store.focused = focused\n if (!focused) {\n // Clear sticky modifiers on blur so a stuck Ctrl from a\n // previous window doesn't bleed into the next session.\n store.modifiers = {\n ctrl: false,\n shift: false,\n alt: false,\n meta: false,\n super: false,\n hyper: false,\n }\n }\n return []\n }\n return prev(op)\n }\n return Object.assign(app, { terminal: store })\n }\n}\n","/**\n * withPasteChain — apply-chain plugin for bracketed-paste routing.\n *\n * Paste routing has priority:\n *\n * 1. Focused `onPaste` — if a focused element subscribes directly\n * (via a focus event listener) it should consume the paste first.\n * The chain expresses this via a pluggable `routeToFocused`\n * callback — in production this dispatches a `paste` DOM event\n * through the focus tree (see `create-app.tsx`); in tests it can\n * be stubbed.\n *\n * 2. Global `usePaste` handlers — registered by React components via\n * the `usePaste()` / `usePasteCallback()` / `usePasteEvents()`\n * hooks. Invoked in registration order, same contract as\n * `withInputChain`'s fallback store.\n *\n * A paste never produces an \"exit\" result; we return `[]` whenever at\n * least one handler (or the focused route) consumes the paste, so the\n * outer runner knows to render.\n */\n\nimport type { ApplyResult, Effect, Op } from \"../types\"\nimport type { BaseApp } from \"./base-app\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type PasteHandler = (text: string) => void\n\n/** Paste handler with richer metadata (mirrors `usePasteEvents`). */\nexport interface PasteEvent {\n /** The raw text that was pasted (as delivered by the terminal). */\n readonly text: string\n /** Whether the focus route consumed this event. */\n readonly focusedConsumed: boolean\n}\n\n/** Store slice installed by {@link withPasteChain}. */\nexport interface PasteStore {\n readonly handlers: ReadonlyArray<PasteHandler>\n register(handler: PasteHandler): () => void\n unregister(handler: PasteHandler): void\n}\n\n/** Options for {@link withPasteChain}. */\nexport interface WithPasteChainOptions {\n /**\n * If provided, called with the pasted text before any fallback\n * handlers run. Return `true` if the paste was consumed by a focused\n * element (fallback handlers will be skipped).\n *\n * Production wires this to `dispatchPasteEvent` through the focus\n * tree; tests typically leave it undefined.\n */\n routeToFocused?: (text: string) => boolean\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\n/**\n * Install the paste router plugin.\n *\n * On `term:paste` ops:\n * - Call `routeToFocused(text)` first (if provided). If it returns\n * true, we short-circuit: handled by focus tree, no fallback.\n * - Otherwise invoke every registered global handler in order.\n * - Always emit `[{type:\"render\"}]` when handled, so the runner\n * knows to paint the post-paste state.\n */\nexport function withPasteChain(\n options: WithPasteChainOptions = {},\n): <A extends BaseApp>(app: A) => A & { paste: PasteStore } {\n return <A extends BaseApp>(app: A): A & { paste: PasteStore } => {\n const handlers: PasteHandler[] = []\n const store: PasteStore = {\n handlers,\n register(handler) {\n handlers.push(handler)\n return () => {\n const i = handlers.indexOf(handler)\n if (i >= 0) handlers.splice(i, 1)\n }\n },\n unregister(handler) {\n for (let i = handlers.length - 1; i >= 0; i--) {\n if (handlers[i] === handler) handlers.splice(i, 1)\n }\n },\n }\n const prev = app.apply\n app.apply = (op: Op): ApplyResult => {\n if (op.type !== \"term:paste\") return prev(op)\n const text = (op as { text?: string }).text ?? \"\"\n let consumed = false\n if (options.routeToFocused) {\n try {\n consumed = !!options.routeToFocused(text)\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error(\"[withPasteChain] routeToFocused threw\", err)\n }\n }\n if (!consumed && handlers.length > 0) {\n for (const handler of handlers) {\n try {\n handler(text)\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error(\"[withPasteChain] handler threw\", err)\n }\n }\n consumed = true\n }\n if (!consumed) return prev(op)\n const effects: Effect[] = [{ type: \"render\" }]\n return effects\n }\n return Object.assign(app, { paste: store })\n }\n}\n","/**\n * withInputChain — apply-chain plugin for the fallback `useInput` store.\n *\n * Fallback = this plugin runs AFTER the focused dispatch plugin\n * (`withFocusChain`) in the apply chain. When focus consumes an event,\n * useInput never sees it — focused components have priority.\n *\n * ## Contract\n *\n * - Plugin owns a list of handlers registered by React components via\n * the `useInput()` hook. Handlers are invoked in registration order\n * (stable; matches the pre-refactor RuntimeContext behaviour).\n *\n * - On `input:key` ops:\n * - release / modifier-only events are observed (some useInput\n * callers still want to know about Ctrl up/down) but do not\n * mark the event as consumed.\n * - press / repeat events invoke every active handler. If any\n * handler returns \"exit\", we short-circuit with\n * `[{type:\"exit\"}]`. Otherwise we signal \"handled\" by returning\n * `[]` when at least one active handler exists.\n *\n * - If no active handlers exist, the plugin returns `false` (pass\n * through) so downstream plugins or the app handler can see the\n * event unchanged.\n *\n * ## Why \"active\" handlers?\n *\n * Components can `useInput(fn, { isActive: false })` to temporarily\n * suppress their handler without unmounting. This is used e.g. by\n * picker dialogs where the parent's global keys should be disabled\n * while the picker is open but the component remains mounted.\n */\n\nimport type { ApplyResult, Effect, Op } from \"../types\"\nimport type { BaseApp } from \"./base-app\"\nimport type { KeyShape } from \"./with-terminal-chain\"\n\n// ---------------------------------------------------------------------------\n// Handler + store types\n// ---------------------------------------------------------------------------\n\n/** Signature matches the classic `useInput(handler)` form. */\nexport type InputHandler = (input: string, key: KeyShape) => void | \"exit\"\n\ninterface InputEntry {\n handler: InputHandler\n active: boolean\n}\n\n/** Store slice installed by {@link withInputChain}. */\nexport interface InputStore {\n /** Internal array of registered entries (exposed for diagnostics). */\n readonly handlers: ReadonlyArray<InputEntry>\n /**\n * Register a new input handler. Returns an unregister function.\n * @param handler the callback\n * @param active whether the handler is currently active (default: true)\n */\n register(handler: InputHandler, active?: boolean): () => void\n /** Update the active flag for a previously-registered handler. */\n setActive(handler: InputHandler, active: boolean): void\n /** Remove a handler (primarily for tests; `register` returns an unregister fn). */\n unregister(handler: InputHandler): void\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isModifierOnly(input: string, key: KeyShape | undefined): boolean {\n if (!key) return false\n // A pure modifier event has no \"payload\" input character and one of\n // the modifier flags set.\n if (input && input.length > 0) return false\n return !!(key.ctrl || key.shift || key.meta || key.super || key.alt || key.hyper)\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\n/**\n * Install the fallback useInput store plugin.\n *\n * Pipe order note: place this plugin so it runs AFTER withFocusChain.\n * Because the last plugin in `pipe()` wraps the outermost layer, the\n * chain reads left-to-right with the base on the left:\n *\n * pipe(create(), withTerminalChain(), withInputChain(), withFocusChain())\n *\n * => apply order: focusChain -> inputChain -> terminalChain -> base\n *\n * => focused components consume before useInput, which is what we want.\n */\nexport function withInputChain<A extends BaseApp>(app: A): A & { input: InputStore } {\n const entries: InputEntry[] = []\n const store: InputStore = {\n handlers: entries,\n register(handler, active = true) {\n const entry: InputEntry = { handler, active }\n entries.push(entry)\n return () => {\n const i = entries.indexOf(entry)\n if (i >= 0) entries.splice(i, 1)\n }\n },\n setActive(handler, active) {\n for (const entry of entries) {\n if (entry.handler === handler) entry.active = active\n }\n },\n unregister(handler) {\n for (let i = entries.length - 1; i >= 0; i--) {\n if (entries[i]!.handler === handler) entries.splice(i, 1)\n }\n },\n }\n const prev = app.apply\n app.apply = (op: Op): ApplyResult => {\n if (op.type !== \"input:key\") return prev(op)\n const input = (op as { input?: string }).input ?? \"\"\n const key = (op as { key?: KeyShape }).key\n const isRelease = key?.eventType === \"release\"\n const modOnly = isModifierOnly(input, key)\n // Always let release / modifier-only events bubble down the chain —\n // e.g. `useModifierKeys` (a future plugin) wants to hear them — but\n // do NOT invoke useInput handlers and do NOT mark the event handled\n // at this layer.\n if (isRelease || modOnly) return prev(op)\n\n let hasActive = false\n const effects: Effect[] = []\n for (const entry of entries) {\n if (!entry.active) continue\n hasActive = true\n try {\n const result = entry.handler(input, key ?? ({} as KeyShape))\n if (result === \"exit\") {\n effects.push({ type: \"exit\" })\n return effects\n }\n } catch (err) {\n // Surface, but don't kill the event loop for a single bad handler.\n // eslint-disable-next-line no-console\n console.error(\"[withInputChain] handler threw\", err)\n }\n }\n if (hasActive) return effects\n return prev(op)\n }\n return Object.assign(app, { input: store })\n}\n","/**\n * withFocusChain — apply-chain plugin for focused-element key dispatch.\n *\n * This is the production form of the v1r prototype's `withFocus` plugin:\n * it owns the \"focused lane\" of the input pipeline. When the app has an\n * active focus target, keys go to it FIRST. If the target handles the\n * key (stopPropagation / preventDefault / handler returned `true`), we\n * signal \"handled\" to the chain and downstream plugins (`useInput`) skip\n * the event.\n *\n * ## Relation to `@silvery/ag-term/plugins/with-focus`\n *\n * The existing `withFocus` in ag-term wraps the *test harness* `App`\n * (`app.press()`). It's a higher-level construct that drives the same\n * runtime bits via a proxy around `press`.\n *\n * `withFocusChain` is the lower-level substrate: it plugs into the\n * runtime's apply chain so `processEventBatch` can replace its\n * ad-hoc `handleFocusNavigation + runtimeInputListeners` loop with a\n * single `app.dispatch({type:\"input:key\", ...})`.\n *\n * The two layer and stack:\n *\n * - ag-term/plugins/with-focus (test/harness) ──────────────┐\n * │\n * - runtime/with-focus-chain (apply chain) ◀──────────────┘\n *\n * ## Options\n *\n * The production runtime already has a rich `createFocusManager`. Rather\n * than re-implement it here, withFocusChain accepts a pluggable\n * `dispatchKey` function — typically `dispatchKeyEvent(createKeyEvent(...),\n * focusManager.activeElement)` or equivalent. It MUST return a boolean:\n *\n * - `true` — the focused tree consumed the key (stopPropagation)\n * - `false` — the focused tree didn't handle it; fall through\n *\n * This dependency-injection style keeps @silvery/create free of\n * terminal-specific imports while still letting create-app.tsx wire\n * in the real focus manager.\n */\n\nimport type { ApplyResult, Op } from \"../types\"\nimport type { BaseApp } from \"./base-app\"\nimport type { KeyShape } from \"./with-terminal-chain\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Caller-supplied: decide whether the focused tree consumed the key.\n *\n * Must return `true` iff the focused node's `onKeyDown` handler\n * invoked `stopPropagation()` / `preventDefault()` — identical to the\n * current `handleFocusNavigation` return.\n */\nexport type FocusKeyDispatch = (input: string, key: KeyShape) => boolean\n\n/**\n * Caller-supplied: is there an active focus target right now?\n *\n * Checked up-front so we skip the dispatch entirely when nothing is\n * focused (matches create-app's `if (focusManager.activeElement)`\n * short-circuit).\n */\nexport type HasActiveFocus = () => boolean\n\n/** Options for {@link withFocusChain}. */\nexport interface WithFocusChainOptions {\n dispatchKey: FocusKeyDispatch\n hasActiveFocus: HasActiveFocus\n /**\n * Optional: when true, releasing keys and modifier-only events are\n * forwarded to `dispatchKey` too. Default: false (matches\n * create-app.tsx's pre-refactor behaviour where such events skip\n * focused dispatch).\n */\n dispatchReleaseAndModifierOnly?: boolean\n}\n\n/** Store slice installed by {@link withFocusChain}. */\nexport interface FocusChainStore {\n /** Most recent result from `dispatchKey` (for diagnostics / tests). */\n lastConsumed: boolean\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isModifierOnly(input: string, key: KeyShape | undefined): boolean {\n if (!key) return false\n if (input && input.length > 0) return false\n return !!(key.ctrl || key.shift || key.meta || key.super || key.alt || key.hyper)\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\n/**\n * Install the focus-dispatch plugin.\n *\n * Place this plugin OUTERMOST in the input-handling chain so focused\n * targets see the key before the `useInput` fallback store.\n */\nexport function withFocusChain(\n options: WithFocusChainOptions,\n): <A extends BaseApp>(app: A) => A & { focusChain: FocusChainStore } {\n return <A extends BaseApp>(app: A): A & { focusChain: FocusChainStore } => {\n const store: FocusChainStore = { lastConsumed: false }\n const prev = app.apply\n app.apply = (op: Op): ApplyResult => {\n if (op.type !== \"input:key\") return prev(op)\n if (!options.hasActiveFocus()) return prev(op)\n const input = (op as { input?: string }).input ?? \"\"\n const key = (op as { key?: KeyShape }).key\n const isRelease = key?.eventType === \"release\"\n const modOnly = isModifierOnly(input, key)\n if ((isRelease || modOnly) && !options.dispatchReleaseAndModifierOnly) {\n return prev(op)\n }\n let consumed = false\n try {\n consumed = !!options.dispatchKey(input, key ?? ({} as KeyShape))\n } catch (err) {\n // A bad focused handler shouldn't break the event loop.\n // eslint-disable-next-line no-console\n console.error(\"[withFocusChain] dispatchKey threw\", err)\n }\n store.lastConsumed = consumed\n if (consumed) {\n // The focused tree consumed the key. Signal \"handled\" with a\n // render request so the runner repaints. Downstream plugins\n // (useInput fallback) are short-circuited.\n return [{ type: \"render\" }]\n }\n return prev(op)\n }\n return Object.assign(app, { focusChain: store })\n }\n}\n","/**\n * withCustomEvents — apply-chain plugin for app-defined custom events.\n *\n * Replaces the legacy `RuntimeContextValue.emit(\"foo\", …)` /\n * `RuntimeContextValue.on(\"foo\", …)` pair for anything that isn't one of\n * the built-in input / paste / focus events. Provides a typed bus where\n * components emit named events and handlers subscribe by channel name.\n *\n * ## Contract\n *\n * - Channel names are arbitrary strings chosen by the app. Payloads are\n * untyped `unknown[]` at the plugin boundary — callers usually narrow\n * via a branded wrapper (see `km-tui`'s `useLinkOpen`).\n *\n * - Handlers are invoked in registration order; a thrown handler is\n * surfaced to the console but does not short-circuit the bus.\n *\n * - The plugin never intercepts ops — it exposes its store via\n * `app.events` and returns `false` for every op so downstream plugins\n * still see them.\n *\n * ## Why a chain plugin?\n *\n * Before this existed, custom events rode on `RuntimeContextValue.on /\n * emit`. That surface is being trimmed to just `{exit: () => void}` as\n * part of the TEA Phase 2 wiring. Routing custom events through a\n * dedicated plugin keeps the chain the single authority for view ↔\n * runtime messaging, matching how input / paste / focus already work.\n *\n * @example\n * ```tsx\n * // Install\n * const app = pipe(\n * createBaseApp(),\n * withTerminalChain(),\n * withPasteChain(),\n * withInputChain,\n * withFocusChain(...),\n * withCustomEvents,\n * )\n *\n * // Emit from a component\n * app.events.emit(\"link:open\", \"https://example.com\")\n *\n * // Subscribe from a hook\n * useEffect(() => chain.events.on(\"link:open\", (href) => openExternal(href as string)), [chain])\n * ```\n */\n\nimport type { ApplyResult, Op } from \"../types\"\nimport type { BaseApp } from \"./base-app\"\n\n// ---------------------------------------------------------------------------\n// Store shape\n// ---------------------------------------------------------------------------\n\n/** Generic payload handler — custom events carry `unknown[]` payloads. */\nexport type CustomEventHandler = (...args: unknown[]) => void\n\n/** Store slice installed by {@link withCustomEvents}. */\nexport interface CustomEventStore {\n /** Subscribe to `channel`. Returns an unsubscribe function. */\n on(channel: string, handler: CustomEventHandler): () => void\n /** Emit `channel` with payload — invokes every registered handler in order. */\n emit(channel: string, ...args: unknown[]): void\n /**\n * Remove a specific handler. Prefer the cleanup function returned by\n * `on()`; this is primarily for tests and rare teardown paths.\n */\n off(channel: string, handler: CustomEventHandler): void\n}\n\n// ---------------------------------------------------------------------------\n// Plugin factory\n// ---------------------------------------------------------------------------\n\n/**\n * Install the custom-events plugin.\n *\n * Exposes the event bus at `app.events`. The plugin is observer-only —\n * it does not touch the op stream; every op passes straight through to\n * downstream plugins.\n */\nexport function withCustomEvents<A extends BaseApp>(app: A): A & { events: CustomEventStore } {\n const channels = new Map<string, CustomEventHandler[]>()\n const store: CustomEventStore = {\n on(channel, handler) {\n let handlers = channels.get(channel)\n if (!handlers) {\n handlers = []\n channels.set(channel, handlers)\n }\n handlers.push(handler)\n return () => {\n const list = channels.get(channel)\n if (!list) return\n const i = list.indexOf(handler)\n if (i >= 0) list.splice(i, 1)\n }\n },\n emit(channel, ...args) {\n const handlers = channels.get(channel)\n if (!handlers || handlers.length === 0) return\n // Snapshot — allow handlers to unsubscribe without mutating the\n // iteration set.\n for (const handler of handlers.slice()) {\n try {\n handler(...args)\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error(`[withCustomEvents] handler for \"${channel}\" threw`, err)\n }\n }\n },\n off(channel, handler) {\n const handlers = channels.get(channel)\n if (!handlers) return\n for (let i = handlers.length - 1; i >= 0; i--) {\n if (handlers[i] === handler) handlers.splice(i, 1)\n }\n },\n }\n const prev = app.apply\n app.apply = (op: Op): ApplyResult => prev(op)\n return Object.assign(app, { events: store })\n}\n","/**\n * BaseApp — the apply-chain substrate for silvery's runtime.\n *\n * This is the contract from the v1r prototype, shipped as production code.\n * Plugins wrap `apply()`; `dispatch()` drives the chain and drains the\n * resulting effect queue.\n *\n * ## Semantics\n *\n * `apply(op)` is synchronous and returns an {@link ApplyResult}:\n * - `false` — \"I did not handle this op; pass to the next plugin (or ignore)\"\n * - `Effect[]` — \"I handled this op; here are the follow-up effects to run\"\n *\n * `dispatch(op)` runs `apply(op)` inside a reentry guard, pushes any\n * emitted effects onto a shared queue, then drains the queue. The only\n * built-in effect understood by the base is `{type: \"dispatch\", op: Op}` —\n * which re-dispatches the nested op through the apply chain. All other\n * effects (`render`, `exit`, `suspend`, `render-barrier`) are interpreted\n * by the runner that owns this app (typically `create-app.tsx`'s\n * processEventBatch).\n *\n * ## Why a queue + drain loop?\n *\n * Plugins should be free to emit multiple effects and to chain them —\n * `dispatch` effects from one plugin can produce more effects from\n * another. The queue + drain guarantees:\n * 1. No reentrant `dispatch()` — a plugin that dispatches from inside\n * its own apply() throws. Follow-up dispatches must be Effects.\n * 2. Bounded iteration — each drain step consumes the whole batch\n * before accepting new effects, so we can detect runaways.\n *\n * ## Why a named sentinel effect queue?\n *\n * When the runner reads the queue (via {@link BaseApp.drainEffects}), it\n * gets an immutable snapshot so it can choose how to interpret the\n * effects. The base never implements `render` or `exit` — those belong\n * to whichever runner is driving.\n *\n * @see {@link Op}, {@link Effect}, {@link ApplyResult}\n */\n\nimport type { ApplyResult, Effect, Op } from \"../types\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * An app's `apply` function: takes an op, returns either `false` (not\n * handled) or an array of effects.\n */\nexport type Apply = (op: Op) => ApplyResult\n\n/**\n * The minimal contract every silvery runtime exposes.\n *\n * Plugins extend this by capturing `app.apply` and replacing it with a\n * new function that delegates to the captured one for ops it doesn't\n * handle. Stores/state slices ride as extra properties on the concrete\n * plugin-enhanced type.\n */\nexport interface BaseApp {\n /**\n * Entry point for an op. Runs the apply chain, captures emitted\n * effects, then drains. Throws on reentrant dispatch.\n */\n dispatch(op: Op): void\n\n /**\n * The apply chain. The base returns `false` (nothing handles\n * anything). Plugins wrap this — last plugin applied = outermost\n * wrapper = runs first.\n */\n apply: Apply\n\n /**\n * Pull (and clear) effects the runner should interpret. Returns any\n * effects that were left *after* built-in `dispatch` drain completed\n * and that the runner is expected to act on (render/exit/etc).\n *\n * Most runners call this after `dispatch()` returns to flush the\n * render/exit queue.\n */\n drainEffects(): Effect[]\n}\n\n// ---------------------------------------------------------------------------\n// create() — base of the chain\n// ---------------------------------------------------------------------------\n\n/**\n * Create a fresh {@link BaseApp}. The base handles nothing — all\n * behavior comes from plugins.\n *\n * Call order inside `dispatch(op)`:\n * 1. `apply(op)` (runs the plugin chain, inside reentry guard)\n * 2. Queue the returned effects (if handled)\n * 3. Drain loop — dispatch-type effects re-enter, others bubble up to\n * the runner via {@link BaseApp.drainEffects}\n */\nexport function createBaseApp(): BaseApp {\n let dispatching = false\n let draining = false\n /** Effects currently queued from the active dispatch() + its drain. */\n const effectQueue: Effect[] = []\n /** Effects the runner should interpret (everything not consumed by\n * the internal dispatch-drain, e.g. render/exit). */\n const pendingRunnerEffects: Effect[] = []\n\n const app: BaseApp = {\n dispatch(op) {\n if (dispatching) {\n throw new Error(`Reentrant dispatch: ${op.type}`)\n }\n dispatching = true\n try {\n const result = app.apply(op)\n if (result !== false) effectQueue.push(...result)\n } finally {\n dispatching = false\n }\n if (draining) return\n draining = true\n try {\n while (effectQueue.length > 0) {\n const batch = effectQueue.splice(0)\n for (const eff of batch) {\n if (eff.type === \"dispatch\") {\n // Re-dispatch is the only effect the base knows. The\n // nested op travels under `op` to avoid spread collisions\n // with the `type: \"dispatch\"` discriminator.\n const nested = (eff as { op?: Op }).op\n if (!nested || typeof nested.type !== \"string\") continue\n dispatching = true\n try {\n const nestedResult = app.apply(nested)\n if (nestedResult !== false) effectQueue.push(...nestedResult)\n } finally {\n dispatching = false\n }\n } else {\n pendingRunnerEffects.push(eff)\n }\n }\n }\n } finally {\n draining = false\n }\n },\n apply() {\n return false\n },\n drainEffects() {\n if (pendingRunnerEffects.length === 0) return []\n return pendingRunnerEffects.splice(0)\n },\n }\n return app\n}\n\n// ---------------------------------------------------------------------------\n// Plugin pattern (no helper — just the idiom)\n// ---------------------------------------------------------------------------\n\n/**\n * Every `with*` plugin follows the same three-line idiom:\n *\n * ```ts\n * export function withEcho<A extends BaseApp>(app: A): A {\n * const prev = app.apply\n * app.apply = (op) => {\n * if (op.type === \"echo\") {\n * // handle\n * return []\n * }\n * return prev(op) // delegate to downstream chain\n * }\n * return app\n * }\n * ```\n *\n * The last plugin installed is the OUTERMOST wrapper and runs first.\n * Always delegate via `prev(op)` for ops you don't handle.\n */\n","/**\n * chain-bridge — utilities for installing an apply-chain on a non-root\n * runtime (render.tsx's Silvery instance and InputBoundary's isolated\n * scope).\n *\n * createApp()'s `create-app.tsx` owns the canonical chain plumbing. The\n * helpers here let callers that don't go through createApp still expose\n * a {@link ChainAppContextValue} to their children so the ag-react\n * hooks (useInput / useModifierKeys / useTerminalFocused / usePaste*)\n * can subscribe through a single unified surface.\n *\n * The child BaseApp installs the same four plugins as create-app.tsx:\n *\n * pipe(createBaseApp(),\n * withTerminalChain,\n * withPasteChain,\n * withInputChain,\n * withFocusChain)\n *\n * withFocusChain is wired with a no-op dispatcher — the isolated scope\n * doesn't own a focus manager, so focused dispatch simply returns false\n * and keys flow straight to the input store.\n */\n\nimport type { Key } from \"@silvery/ag/keys\"\nimport {\n createBaseApp,\n withCustomEvents,\n withFocusChain,\n withInputChain,\n withPasteChain,\n withTerminalChain,\n type BaseApp,\n type CustomEventStore,\n type InputStore,\n type PasteStore,\n type TerminalStore,\n} from \"@silvery/create/plugins\"\nimport type {\n ChainAppContextValue,\n ChainFocusEvents,\n ChainFocusHandler,\n ChainRawKeyHandler,\n ChainRawKeyObserver,\n} from \"./context\"\n\n/**\n * BaseApp enhanced with the four chain plugins plus the raw-key observer\n * and focus-event slices that complete the {@link ChainAppContextValue}\n * surface.\n */\nexport interface ChildApp extends BaseApp {\n readonly input: InputStore\n readonly paste: PasteStore\n readonly terminal: TerminalStore\n readonly events: CustomEventStore\n readonly rawKeys: ChainRawKeyObserver & {\n /** Fire all registered raw-key observers. */\n notify(input: string, key: Key): void\n }\n readonly focusEvents: ChainFocusEvents & {\n /** Fire all registered focus-event observers. */\n notify(focused: boolean): void\n }\n}\n\n/**\n * Build a non-root BaseApp with the canonical plugin chain.\n *\n * The returned app is self-contained: ops dispatched through it don't\n * reach the root runtime. Callers own the lifecycle — there's no exit /\n * render effect handler wired up; effects are drained and discarded.\n */\nexport function createChildApp(): ChildApp {\n const base = createBaseApp()\n const terminal = withTerminalChain()(base)\n const paste = withPasteChain()(terminal)\n const input = withInputChain(paste)\n const focus = withFocusChain({\n dispatchKey: () => false,\n hasActiveFocus: () => false,\n })(input)\n const events = withCustomEvents(focus)\n\n // Raw-key observer slice — unfiltered access to every dispatched key\n // (used by useModifierKeys which needs release + modifier-only events).\n const rawKeyListeners: Array<(input: string, key: Key) => void> = []\n const rawKeys = {\n register(handler: ChainRawKeyHandler): () => void {\n rawKeyListeners.push(handler as (input: string, key: Key) => void)\n return () => {\n const i = rawKeyListeners.indexOf(handler as (input: string, key: Key) => void)\n if (i >= 0) rawKeyListeners.splice(i, 1)\n }\n },\n notify(input: string, key: Key): void {\n for (const h of rawKeyListeners) h(input, key)\n },\n }\n\n // Focus-events slice — isolated scopes typically don't emit focus\n // events, but useTerminalFocused still expects a register() that\n // returns a clean unsubscribe.\n const focusListeners: Array<ChainFocusHandler> = []\n const focusEvents = {\n register(handler: ChainFocusHandler): () => void {\n focusListeners.push(handler)\n return () => {\n const i = focusListeners.indexOf(handler)\n if (i >= 0) focusListeners.splice(i, 1)\n }\n },\n notify(focused: boolean): void {\n for (const h of focusListeners) h(focused)\n },\n }\n\n return Object.assign(events, { rawKeys, focusEvents }) as ChildApp\n}\n\n/**\n * Project a {@link ChildApp} to the surface exposed on\n * {@link ChainAppContextValue}.\n */\nexport function toChainAppContextValue(app: ChildApp): ChainAppContextValue {\n return {\n input: app.input,\n paste: app.paste,\n focusEvents: app.focusEvents,\n rawKeys: app.rawKeys,\n events: app.events,\n }\n}\n","/**\n * Silvery Render Entry Point\n *\n * The main render() function that initializes Silvery and renders a React element\n * to the terminal. This wires together:\n * - Yoga (layout engine)\n * - React reconciler\n * - Context providers (RuntimeContext, Stdout)\n * - Render scheduler (batching and diffing)\n *\n * Compatible with Ink's render API.\n */\n\nimport process from \"node:process\"\nimport { createLogger } from \"loggily\"\nimport { type Term, createTerm } from \"@silvery/ag-term/ansi\"\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n type ReactElement,\n type ReactNode,\n} from \"react\"\n\nconst log = createLogger(\"silvery:render\")\nimport {\n ChainAppContext,\n FocusManagerContext,\n RuntimeContext,\n type RuntimeContextValue,\n StdoutContext,\n StderrContext,\n TermContext,\n} from \"./context\"\nimport { createChildApp, toChainAppContextValue } from \"./chain-bridge\"\nimport { createCursorStore, CursorProvider, type CursorStore } from \"./hooks/useCursor\"\nimport { createFocusManager } from \"@silvery/ag/focus-manager\"\nimport { parseKey } from \"@silvery/ag/keys\"\nimport { type LayoutEngineType, isLayoutEngineInitialized } from \"@silvery/ag-term/layout-engine\"\nimport {\n enableBracketedPaste,\n disableBracketedPaste,\n parseBracketedPaste,\n} from \"@silvery/ag-term/bracketed-paste\"\nimport {\n ANSI,\n enterAlternateScreen,\n leaveAlternateScreen,\n enableKittyKeyboard,\n disableKittyKeyboard,\n resetWindowTitle,\n} from \"@silvery/ag-term/output\"\nimport {\n createContainer,\n createFiberRoot,\n getContainerRoot,\n reconciler,\n runWithDiscreteEvent,\n setOnNodeRemoved,\n} from \"./reconciler\"\nimport { renderStringSync } from \"./render-string\"\nimport { RenderScheduler } from \"@silvery/ag-term/scheduler\"\nimport { createOutputGuard, type OutputGuard } from \"@silvery/ag-term/ansi/output-guard\"\nimport {\n type ResolvedTermDef,\n type TermDef,\n isTerm,\n isTermDef,\n resolveFromTerm,\n resolveTermDef,\n} from \"@silvery/ag-term/term-def\"\nimport { splitRawInput } from \"@silvery/ag/keys\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Render mode for the terminal.\n */\nexport type RenderMode = \"fullscreen\" | \"inline\"\n\n/**\n * Non-TTY mode for rendering in non-interactive environments.\n *\n * - 'auto': Auto-detect based on environment (default)\n * - 'tty': Force TTY mode with cursor positioning\n * - 'line-by-line': Output lines without cursor repositioning\n * - 'static': Single final output only (no intermediate updates)\n * - 'plain': Strip all ANSI escape codes\n */\nexport type NonTTYMode = \"auto\" | \"tty\" | \"line-by-line\" | \"static\" | \"plain\"\n\n/**\n * Options for the render function.\n */\nexport interface RenderOptions {\n /** Standard output stream (default: process.stdout) */\n stdout?: NodeJS.WriteStream\n /** Standard input stream (default: process.stdin) */\n stdin?: NodeJS.ReadStream\n /** Exit when Ctrl+C is pressed (default: true) */\n exitOnCtrlC?: boolean\n /** Enable debug mode with verbose logging (default: false) */\n debug?: boolean\n /** Patch console methods to work with Silvery output (default: true) */\n patchConsole?: boolean\n /** Use alternate screen buffer (default: true for fullscreen mode, false for inline) */\n alternateScreen?: boolean\n /**\n * Render mode (default: 'fullscreen')\n * - 'fullscreen': Uses absolute cursor positioning and alternateScreen\n * - 'inline': Renders inline from current cursor position (for progress bars)\n */\n mode?: RenderMode\n /**\n * Non-TTY mode for non-interactive environments (default: 'auto')\n *\n * When running in a non-TTY environment (piped output, CI, TERM=dumb),\n * silvery will automatically detect this and use 'line-by-line' mode.\n * You can override this behavior by explicitly setting the mode.\n *\n * - 'auto': Detect based on environment (TTY -> 'tty', non-TTY -> 'line-by-line')\n * - 'tty': Force TTY mode with cursor positioning\n * - 'line-by-line': Simple newline-separated output, updates in place\n * - 'static': Only output final frame (no intermediate renders)\n * - 'plain': Strip all ANSI codes, output plain text\n */\n nonTTYMode?: NonTTYMode\n /**\n * Layout engine to use (default: 'flexily', or SILVERY_ENGINE env var)\n * - 'flexily': Pure JS, synchronous, smaller bundle\n * - 'yoga': Facebook's WASM-based flexbox (more mature)\n */\n layoutEngine?: LayoutEngineType\n}\n\n/**\n * The instance returned by render().\n */\nexport interface Instance {\n /** Re-render with a new element */\n rerender: (element: ReactNode) => void\n /** Unmount the component and clean up */\n unmount: () => void\n /** Dispose (alias for unmount) — enables `using` */\n [Symbol.dispose](): void\n /** Promise that resolves when the app exits */\n waitUntilExit: () => Promise<void>\n /** Clear the terminal output */\n clear: () => void\n /** Force an immediate render, bypassing scheduler batching */\n flush: () => void\n /** Pause rendering output (for screen switching). Input still works. */\n pause: () => void\n /** Resume rendering after pause. Forces a full redraw. */\n resume: () => void\n}\n\n/**\n * Handle returned by render() - thenable AND has fluent .run() method.\n *\n * Supports two usage patterns:\n * ```tsx\n * // Pattern 1: Get instance, then wait\n * const instance = await render(<App />, term)\n * await instance.waitUntilExit()\n *\n * // Pattern 2: Fluent - render and wait in one line\n * await render(<App />, term).run()\n * ```\n */\nexport class RenderHandle implements PromiseLike<Instance> {\n constructor(private readonly promise: Promise<Instance>) {}\n\n /** Make this thenable so `await render()` returns Instance */\n then<TResult1 = Instance, TResult2 = never>(\n onfulfilled?: ((value: Instance) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.promise.then(onfulfilled, onrejected)\n }\n\n /** Catch errors */\n catch<TResult = never>(\n onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null,\n ): Promise<Instance | TResult> {\n return this.promise.catch(onrejected)\n }\n\n /** Finally handler */\n finally(onfinally?: (() => void) | null): Promise<Instance> {\n return this.promise.finally(onfinally)\n }\n\n /**\n * Render and wait until exit - fluent API for common case.\n *\n * @example\n * ```tsx\n * await render(<App />, term).run()\n * ```\n */\n async run(): Promise<void> {\n const instance = await this.promise\n await instance.waitUntilExit()\n }\n}\n\n// ============================================================================\n// Global State\n// ============================================================================\n\n/** Map of stdout streams to instances (for reuse) */\nconst instances = new Map<NodeJS.WriteStream, SilveryInstance>()\n\n// ============================================================================\n// Layout Engine Initialization\n// ============================================================================\n\n/**\n * Initialize layout engine if not already initialized.\n * @param engineType - 'flexily' or 'yoga'. Falls back to SILVERY_ENGINE env, then 'flexily'.\n */\nasync function ensureLayoutEngineInitialized(engineType?: LayoutEngineType): Promise<void> {\n if (isLayoutEngineInitialized()) {\n log.debug?.(\"Layout engine already initialized\")\n return\n }\n\n log.debug?.(`Initializing layout engine (${engineType ?? \"default\"})...`)\n const startTime = Date.now()\n\n const { ensureDefaultLayoutEngine } = await import(\"@silvery/ag-term/layout-engine\")\n await ensureDefaultLayoutEngine(engineType)\n\n log.debug?.(`Layout engine initialized in ${Date.now() - startTime}ms`)\n}\n\n// ============================================================================\n// Internal App Component\n// ============================================================================\n\n// ============================================================================\n// ============================================================================\n// App Props — callback-based, no Node.js types in the callback interface\n// ============================================================================\n\ninterface AppProps {\n children: ReactNode\n /** Subscribe to raw input chunks. Returns cleanup. Called by setRawMode. */\n onInputSubscribe?: (handler: (chunk: string) => void) => () => void\n /** Whether Ctrl+C should exit the app */\n exitOnCtrlC: boolean\n /** Write function for stdout context */\n stdoutWrite: (data: string) => void\n /** The raw stdout stream (for StdoutContext.stdout) — still NodeJS.WriteStream for now */\n stdout: NodeJS.WriteStream\n onExit: (error?: Error) => void\n onPause?: () => void\n onResume?: () => void\n onScrollback?: (lines: number) => void\n /** Get the root AgNode for focus navigation. Provided by SilveryInstance. */\n getRoot?: () => import(\"@silvery/create/types\").AgNode | null\n /** Handle Tab/Shift+Tab/Escape focus cycling (default: true) */\n handleFocusCycling?: boolean\n}\n\n/**\n * Internal App component that provides all contexts.\n * This is a functional component that manages focus state and provides\n * all the context values needed by hooks.\n *\n * Input is callback-driven — no EventEmitter, no stdin/stdout dependency.\n * The caller (SilveryInstance for Node.js, renderToXterm for browser)\n * provides `onInputSubscribe` which connects the input source.\n */\nfunction SilveryApp({\n children,\n onInputSubscribe,\n exitOnCtrlC,\n stdoutWrite,\n stdout,\n onExit,\n onPause,\n onResume,\n onScrollback,\n getRoot: getRootProp,\n handleFocusCycling = true,\n}: AppProps): ReactElement {\n // Child apply-chain BaseApp — the ChainAppContext surface for hooks\n // inside this render tree. Built once per instance, mirrors the\n // plumbing in `create-app.tsx` (TEA Phase 2). Hooks subscribe via\n // ChainAppContext only — RuntimeContext is trimmed to {exit}.\n const childAppRef = useRef<ReturnType<typeof createChildApp> | null>(null)\n if (!childAppRef.current) childAppRef.current = createChildApp()\n const childApp = childAppRef.current\n\n // Exit handler\n const handleExit = useCallback(\n (error?: Error) => {\n onExit(error)\n },\n [onExit],\n )\n\n // Mutable refs for values accessed inside the input handler.\n // Using refs prevents handler identity from ever changing, which\n // would cascade through subscription effects, dropping keypresses during the gap.\n\n const exitOnCtrlCRef = useRef(exitOnCtrlC)\n exitOnCtrlCRef.current = exitOnCtrlC\n\n const handleExitRef = useRef(handleExit)\n handleExitRef.current = handleExit\n\n // Refs for focus manager and root getter — accessed inside input handler\n const focusManagerRef = useRef<import(\"@silvery/create/focus-manager\").FocusManager | null>(null)\n const getRootRef = useRef(getRootProp)\n getRootRef.current = getRootProp\n const handleFocusCyclingRef = useRef(handleFocusCycling)\n handleFocusCyclingRef.current = handleFocusCycling\n\n // Stable input chunk handler — created once, never changes identity.\n // All mutable state is read via refs to avoid dependency cascade.\n const handleChunkRef = useRef<((chunk: string) => void) | null>(null)\n if (handleChunkRef.current === null) {\n handleChunkRef.current = (rawChunk: string) => {\n log.debug?.(`handleChunk: ${JSON.stringify(rawChunk)}`)\n\n // Check for bracketed paste before splitting into individual keys.\n const pasteResult = parseBracketedPaste(rawChunk)\n if (pasteResult) {\n // Dispatch to the child chain — the canonical subscription\n // surface for hooks via ChainAppContext. The legacy\n // `rt.on(\"paste\", …)` subscriber list is gone (RuntimeContext\n // trimmed to {exit} only).\n childApp.dispatch({ type: \"term:paste\", text: pasteResult.content })\n childApp.drainEffects()\n return\n }\n\n // Split multi-character chunks into individual keypresses.\n // Raw input sources can deliver multiple characters in a single chunk\n // (rapid typing, paste, or auto-repeat during heavy renders).\n for (const keypress of splitRawInput(rawChunk)) {\n processSingleKey(keypress)\n }\n }\n\n function processSingleKey(chunk: string) {\n log.debug?.(`processSingleKey: ${JSON.stringify(chunk)}`)\n // Handle Ctrl+C\n if (chunk === \"\\x03\" && exitOnCtrlCRef.current) {\n handleExitRef.current()\n return\n }\n\n // Default Tab/Shift+Tab focus cycling and Escape blur.\n // Handled before dispatching to useInput handlers so it works\n // automatically when focusable components exist. Tab events are\n // consumed (not passed to useInput) — matching run() and createApp().\n if (handleFocusCyclingRef.current) {\n const fm = focusManagerRef.current\n const root = getRootRef.current?.()\n if (fm && root) {\n const [, key] = parseKey(chunk)\n if (key.tab && !key.shift) {\n fm.focusNext(root)\n reconciler.flushSyncWork()\n return\n }\n if (key.tab && key.shift) {\n fm.focusPrev(root)\n reconciler.flushSyncWork()\n return\n }\n if (key.escape && fm.activeElement) {\n fm.blur()\n reconciler.flushSyncWork()\n return\n }\n }\n }\n\n // Parse the key and dispatch to the child chain.\n const [input, key] = parseKey(chunk)\n\n // All input handling runs at discrete priority so React commits\n // synchronously. Without this, concurrent mode defers the commit\n // and onCommit → scheduleRender() never fires.\n runWithDiscreteEvent(() => {\n // Fire raw-key observers first (useModifierKeys, etc.) so modifier\n // state is up-to-date before the filtered input handlers run.\n childApp.rawKeys.notify(input, key)\n // Dispatch into the child apply chain — this reaches `useInput`\n // consumers via `chain.input.register`. Effects (render/exit)\n // are drained and discarded; render.tsx owns its own commit\n // lifecycle below.\n childApp.dispatch({ type: \"input:key\", input, key })\n childApp.drainEffects()\n })\n reconciler.flushSyncWork()\n }\n }\n const handleChunk = handleChunkRef.current\n\n // Subscribe to input source when available\n useEffect(() => {\n if (!onInputSubscribe) return\n return onInputSubscribe(handleChunk)\n }, [onInputSubscribe, handleChunk])\n\n const stdoutContextValue = useMemo(\n () => ({\n stdout,\n write: stdoutWrite,\n notifyScrollback: onScrollback,\n }),\n [stdout, stdoutWrite, onScrollback],\n )\n\n // RuntimeContext — trimmed to lifecycle controls (exit + optional\n // pause/resume for console suspension). Input / paste / focus flow\n // through ChainAppContext.\n const runtimeContextValue = useMemo<RuntimeContextValue>(\n () => ({\n exit: handleExit,\n pause: onPause,\n resume: onResume,\n }),\n [handleExit, onPause, onResume],\n )\n\n // ChainAppContext — canonical subscription surface for ag-react hooks.\n const chainAppContextValue = useMemo(() => toChainAppContextValue(childApp), [childApp])\n\n // Focus manager (tree-based focus system)\n const focusManager = useMemo(() => createFocusManager(), [])\n // Store in ref so the stable input handler closure can access it\n focusManagerRef.current = focusManager\n\n // Wire up focus cleanup on node removal\n useEffect(() => {\n setOnNodeRemoved((removedNode) => focusManager.handleSubtreeRemoved(removedNode))\n return () => setOnNodeRemoved(null)\n }, [focusManager])\n\n return (\n <StdoutContext.Provider value={stdoutContextValue}>\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 <ChainAppContext.Provider value={chainAppContextValue}>\n {children}\n </ChainAppContext.Provider>\n </RuntimeContext.Provider>\n </FocusManagerContext.Provider>\n </StderrContext.Provider>\n </StdoutContext.Provider>\n )\n}\n\n// ============================================================================\n// Internal Instance Class\n// ============================================================================\n\n/**\n * Internal class that manages a single Silvery render instance.\n */\nclass SilveryInstance {\n private readonly stdout: NodeJS.WriteStream\n private readonly stdin: NodeJS.ReadStream\n private readonly exitOnCtrlC: boolean\n private readonly debug: boolean\n private readonly alternateScreen: boolean\n private readonly mode: RenderMode\n private readonly nonTTYMode: NonTTYMode\n\n private scheduler: RenderScheduler | null = null\n private cursorStore: CursorStore\n private container: ReturnType<typeof createContainer> | null = null\n private fiberRoot: any = null\n private lastElement: ReactNode = null\n private isUnmounted = false\n\n private exitPromise: Promise<void> | null = null\n private resolveExit: (() => void) | null = null\n private rejectExit: ((error: Error) => void) | null = null\n\n private resizeCleanup: (() => void) | null = null\n private signalCleanup: (() => void) | null = null\n private outputGuard: OutputGuard | null = null\n\n constructor(options: Required<Omit<RenderOptions, \"patchConsole\" | \"layoutEngine\">>) {\n log.debug?.(\"SilveryInstance constructor start\")\n const startTime = Date.now()\n\n this.stdout = options.stdout\n this.stdin = options.stdin\n this.exitOnCtrlC = options.exitOnCtrlC\n this.debug = options.debug\n this.alternateScreen = options.alternateScreen\n this.mode = options.mode\n this.nonTTYMode = options.nonTTYMode\n\n // Set up exit promise\n this.exitPromise = new Promise<void>((resolve, reject) => {\n this.resolveExit = resolve\n this.rejectExit = reject\n })\n\n // Enter alternate screen if requested\n if (this.alternateScreen) {\n this.stdout.write(enterAlternateScreen())\n }\n\n // Enable Kitty keyboard protocol for enhanced key reporting\n if (this.stdout.isTTY) {\n this.stdout.write(enableKittyKeyboard())\n }\n\n // Enable bracketed paste mode so pasted text is distinguishable from typing\n if (this.stdout.isTTY) {\n enableBracketedPaste(this.stdout)\n }\n\n // Per-instance cursor state (replaces module-level globals)\n this.cursorStore = createCursorStore()\n\n // Activate output guard after protocol setup so that terminal escape\n // sequences (alt screen, kitty keyboard, etc.) go through raw stdout.\n // The guard intercepts process.stdout/stderr writes so only silvery's\n // render pipeline can write to stdout — all other writes are suppressed\n // (stdout) or redirected to DEBUG_LOG (stderr).\n // Only activate when writing to the real process.stdout — mock streams\n // in tests don't need guarding and intercepting process.stdout would\n // break the test infrastructure.\n if (this.alternateScreen && this.stdout === process.stdout) {\n this.outputGuard = createOutputGuard()\n }\n\n // Set up container\n this.container = createContainer(() => {\n this.scheduler?.scheduleRender()\n })\n\n // Create the React fiber root\n this.fiberRoot = createFiberRoot(this.container)\n\n // Set up scheduler — route render output through the guard when active\n this.scheduler = new RenderScheduler({\n stdout: this.stdout,\n root: getContainerRoot(this.container),\n debug: this.debug,\n mode: this.mode,\n nonTTYMode: this.nonTTYMode,\n cursorAccessors: this.cursorStore.accessors,\n writeOutput: this.outputGuard\n ? (data: string) => this.outputGuard!.writeStdout(data)\n : undefined,\n })\n\n // Set up resize listener\n this.setupResizeListener()\n\n // Set up signal handlers\n this.setupSignalHandlers()\n\n log.debug?.(`SilveryInstance constructor complete in ${Date.now() - startTime}ms`)\n }\n\n /**\n * Render a React element.\n */\n render(element: ReactNode): void {\n log.debug?.(\"SilveryInstance.render() start\")\n const startTime = Date.now()\n\n if (this.isUnmounted || !this.fiberRoot) return\n this.lastElement = element\n\n const tree = (\n <CursorProvider store={this.cursorStore}>\n <SilveryApp\n onInputSubscribe={this.subscribeToInput}\n exitOnCtrlC={this.exitOnCtrlC}\n stdoutWrite={(data: string) => {\n if (this.outputGuard) {\n this.outputGuard.writeStdout(data)\n } else {\n this.stdout.write(data)\n }\n }}\n stdout={this.stdout}\n onExit={this.handleExit}\n onPause={this.pause}\n onResume={this.resume}\n onScrollback={this.handleScrollback}\n getRoot={() => (this.container ? getContainerRoot(this.container) : null)}\n >\n {element}\n </SilveryApp>\n </CursorProvider>\n )\n\n // Use synchronous update to ensure React commits the work immediately\n // This is necessary because the async updateContainer doesn't flush work\n // in environments like Bun where the event loop may not be pumped\n log.debug?.(\"SilveryInstance.render() calling updateContainerSync\")\n reconciler.updateContainerSync(tree, this.fiberRoot, null, null)\n log.debug?.(\n `SilveryInstance.render() updateContainerSync complete in ${Date.now() - startTime}ms`,\n )\n\n log.debug?.(\"SilveryInstance.render() calling flushSyncWork\")\n const flushStart = Date.now()\n reconciler.flushSyncWork()\n log.debug?.(\n `SilveryInstance.render() flushSyncWork complete in ${Date.now() - flushStart}ms (total: ${Date.now() - startTime}ms)`,\n )\n }\n\n /**\n * Rerender with a new element.\n */\n rerender = (element: ReactNode): void => {\n this.render(element)\n }\n\n /**\n * Force an immediate render, bypassing scheduler batching.\n * Re-renders the current element synchronously, then runs the pipeline.\n * Useful for streaming reporters that need incremental output after\n * external state changes (e.g., store updates via useSyncExternalStore).\n */\n flush = (): void => {\n if (this.isUnmounted || !this.lastElement) return\n // Re-render the current element synchronously. This forces React to\n // re-evaluate the tree, picking up any external store changes\n // (useSyncExternalStore) that were notified since the last render.\n this.render(this.lastElement)\n // Force the render pipeline to run immediately\n this.scheduler?.forceRender()\n };\n\n /**\n * Unmount the component.\n */\n [Symbol.dispose] = (): void => this.unmount()\n\n unmount = (): void => {\n if (this.isUnmounted) return\n this.isUnmounted = true\n\n // Final render\n this.scheduler?.forceRender()\n\n // Clean up resources\n this.resizeCleanup?.()\n this.signalCleanup?.()\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 (this.outputGuard) {\n this.outputGuard.dispose()\n this.outputGuard = null\n }\n\n // Disable Kitty keyboard protocol and bracketed paste before leaving\n if (this.stdout.isTTY) {\n disableBracketedPaste(this.stdout)\n this.stdout.write(disableKittyKeyboard())\n }\n\n // Reset window title so the terminal reverts to its default\n if (this.stdout.isTTY) {\n resetWindowTitle(this.stdout)\n }\n\n // Leave alternate screen if we entered it (leaveAlternateScreen includes\n // SYNC_END as a safety belt). For inline mode, show cursor and move to\n // next line. Otherwise emit SYNC_END as a safety belt.\n if (this.alternateScreen) {\n this.stdout.write(leaveAlternateScreen())\n } else if (this.mode === \"inline\") {\n // Show cursor and move to line after content\n this.stdout.write(`${ANSI.SYNC_END}${ANSI.CURSOR_SHOW}\\n`)\n } else {\n this.stdout.write(ANSI.SYNC_END)\n }\n\n // Destroy the stdin stream to stop its internal I/O watcher from pulling\n // data from fd 0. Without this, the stream's internal _read() continues\n // consuming bytes from the kernel buffer even after all JS-level listeners\n // are removed. A child process with inherited stdio would never see those\n // bytes — they're trapped in the parent's stream buffer.\n //\n // Note: destroy() on process.stdin does NOT close fd 0 — the kernel fd\n // remains open for child processes to inherit. It only tears down the\n // Node.js/Bun stream wrapper.\n const { stdin } = this\n stdin.removeAllListeners(\"readable\")\n stdin.removeAllListeners(\"data\")\n stdin.destroy()\n // Unref stdin so it doesn't keep the event loop alive after unmount.\n // Without this, the process hangs after exit() in inline mode.\n if (typeof stdin.unref === \"function\") stdin.unref()\n\n if (this.fiberRoot) {\n reconciler.updateContainer(null, this.fiberRoot, null, () => {})\n }\n\n // Dispose scheduler\n this.scheduler?.dispose()\n\n // Remove from instances\n instances.delete(this.stdout)\n\n // Resolve exit promise\n this.resolveExit?.()\n }\n\n /**\n * Wait for the app to exit.\n */\n waitUntilExit = (): Promise<void> => {\n return this.exitPromise ?? Promise.resolve()\n }\n\n /**\n * Clear the terminal output.\n */\n clear = (): void => {\n this.scheduler?.clear()\n }\n\n /**\n * Pause rendering output. Scheduled and forced renders become no-ops.\n * Input handling continues normally. Used for screen-switching.\n */\n pause = (): void => {\n this.scheduler?.pause()\n }\n\n /**\n * Resume rendering after pause. Forces a full redraw.\n */\n resume = (): void => {\n this.scheduler?.resume()\n // If nothing was pending, still force a full redraw\n this.scheduler?.forceRender()\n }\n\n /**\n * Handle exit.\n */\n private handleExit = (error?: Error): void => {\n if (this.isUnmounted) return\n\n if (error) {\n this.rejectExit?.(error)\n }\n\n this.unmount()\n }\n\n /**\n * Handle scrollback lines written to stdout by useScrollback.\n */\n private handleScrollback = (lines: number): void => {\n this.scheduler?.addScrollbackLines(lines)\n }\n\n /**\n * Set up resize listener.\n */\n private setupResizeListener(): void {\n const handleResize = () => {\n // Clear and force full redraw on resize\n this.scheduler?.clear()\n this.scheduler?.forceRender()\n }\n\n this.stdout.on(\"resize\", handleResize)\n this.resizeCleanup = () => {\n this.stdout.off(\"resize\", handleResize)\n }\n }\n\n /**\n * Set up signal handlers for graceful exit.\n */\n private setupSignalHandlers(): void {\n const handleSignal = () => {\n this.unmount()\n }\n\n process.on(\"SIGINT\", handleSignal)\n process.on(\"SIGTERM\", handleSignal)\n\n this.signalCleanup = () => {\n process.off(\"SIGINT\", handleSignal)\n process.off(\"SIGTERM\", handleSignal)\n }\n }\n\n /**\n * Subscribe to stdin input. Sets up raw mode and readable listener.\n * Returns cleanup function that tears down raw mode.\n *\n * This is the Node.js-specific input adapter — it bridges stdin\n * into the platform-agnostic callback that SilveryApp expects.\n */\n private subscribeToInput = (handler: (chunk: string) => void): (() => void) => {\n const { stdin } = this\n const isRawModeSupported = stdin.isTTY === true\n\n log.debug?.(\n `subscribeToInput: stdin=${stdin === process.stdin ? \"process.stdin\" : \"other\"}, isTTY=${stdin.isTTY}, isRawModeSupported=${isRawModeSupported}`,\n )\n\n if (!isRawModeSupported) {\n log.debug?.(\"subscribeToInput: raw mode not supported, skipping\")\n return () => {}\n }\n\n // Set up readable handler that reads chunks and passes to handler\n const handleReadable = () => {\n let chunk: string | null\n while ((chunk = stdin.read() as string | null) !== null) {\n log.debug?.(`subscribeToInput: stdin.read() returned: ${JSON.stringify(chunk)}`)\n handler(chunk)\n }\n }\n\n stdin.setEncoding(\"utf8\")\n stdin.ref()\n stdin.setRawMode(true)\n stdin.on(\"readable\", handleReadable)\n log.debug?.(`subscribeToInput: enabled raw mode, stdin.isRaw=${stdin.isRaw}`)\n\n return () => {\n log.debug?.(\"subscribeToInput: cleanup — disabling raw mode\")\n stdin.setRawMode(false)\n stdin.off(\"readable\", handleReadable)\n stdin.unref()\n }\n }\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Render a React element to the terminal.\n *\n * The second argument determines the render mode:\n * - **Term instance**: Interactive mode with full terminal capabilities\n * - **TermDef with events/stdin**: Interactive mode\n * - **TermDef without events**: Static mode (renders until stable, then returns)\n * - **Omitted**: Static mode with default dimensions (80x24)\n *\n * @example Interactive rendering with Term\n * ```tsx\n * import { render, Box, Text, createTerm } from '@silvery/ag-react';\n *\n * using term = createTerm();\n * const { waitUntilExit } = await render(<App />, term);\n * await waitUntilExit();\n * ```\n *\n * @example Static rendering (no terminal needed)\n * ```tsx\n * import { render, Box, Text } from '@silvery/ag-react';\n *\n * // Renders once, returns when stable\n * const { lastFrame } = await render(<Summary stats={stats} />);\n * console.log(lastFrame);\n *\n * // With custom dimensions\n * await render(<Report />, { width: 120, height: 40 });\n * ```\n *\n * @example Interactive with TermDef\n * ```tsx\n * import { render, Box, Text } from '@silvery/ag-react';\n *\n * await render(<App />, {\n * stdin: process.stdin,\n * stdout: process.stdout,\n * });\n * ```\n *\n * @param element - React element to render\n * @param termOrDef - Term instance, TermDef config, or omitted for static mode\n * @param options - Additional render options (merged with TermDef if provided)\n * @returns RenderHandle - thenable for Instance, or use .run() for fluent API\n *\n * @example\n * ```tsx\n * // Get instance first\n * const instance = await render(<App />, term)\n * await instance.waitUntilExit()\n *\n * // Or use fluent .run()\n * await render(<App />, term).run()\n * ```\n */\nexport function render(\n element: ReactElement,\n termOrDef?: Term | TermDef,\n options?: RenderOptions,\n): RenderHandle {\n return new RenderHandle(renderAsync(element, termOrDef, options))\n}\n\n/**\n * Internal async render implementation.\n */\nasync function renderAsync(\n element: ReactElement,\n termOrDef?: Term | TermDef,\n options?: RenderOptions,\n): Promise<Instance> {\n // Resolve termOrDef to get configuration\n let resolved: ResolvedTermDef\n let term: Term\n\n if (!termOrDef) {\n // No term/def provided - static mode with defaults\n resolved = resolveTermDef({})\n term = createTerm({ color: resolved.colors ?? undefined })\n } else if (isTerm(termOrDef)) {\n // Full Term instance provided\n resolved = resolveFromTerm(termOrDef)\n term = termOrDef\n } else if (isTermDef(termOrDef)) {\n // TermDef provided\n resolved = resolveTermDef(termOrDef)\n term = createTerm({\n stdout: termOrDef.stdout,\n stdin: termOrDef.stdin,\n color: resolved.colors ?? undefined,\n })\n } else {\n throw new Error(\"Invalid second argument: expected Term, TermDef, or undefined\")\n }\n\n // Merge options\n const mergedOptions: RenderOptions = {\n ...options,\n stdout: options?.stdout ?? resolved.stdout ?? term.stdout,\n stdin: options?.stdin ?? (resolved.isStatic ? undefined : term.stdin),\n }\n\n return renderImpl(element, mergedOptions, term, resolved)\n}\n\n/**\n * Internal render implementation.\n */\nasync function renderImpl(\n element: ReactElement,\n options: RenderOptions,\n term: Term,\n resolved: ResolvedTermDef,\n): Promise<Instance> {\n log.debug?.(`render() called, isStatic=${resolved.isStatic}`)\n const renderStart = Date.now()\n\n // Ensure layout engine is initialized\n await ensureLayoutEngineInitialized(options.layoutEngine)\n log.debug?.(`render(): layout engine ready in ${Date.now() - renderStart}ms`)\n\n // Auto-connect to React DevTools if DEBUG_DEVTOOLS=1\n if (process.env.DEBUG_DEVTOOLS) {\n const { autoConnectDevTools } = await import(\"@silvery/ag-term/devtools\")\n await autoConnectDevTools()\n }\n\n // For static mode, use renderString-style rendering\n if (resolved.isStatic) {\n return renderStaticImpl(element, term, resolved)\n }\n\n // Merge with defaults for interactive mode\n // alternateScreen defaults to true for fullscreen mode (clean slate, restore on exit)\n const mode = options.mode ?? (\"fullscreen\" as RenderMode)\n const resolvedOptions = {\n stdout: options.stdout ?? process.stdout,\n stdin: options.stdin ?? process.stdin,\n exitOnCtrlC: options.exitOnCtrlC ?? true,\n debug: options.debug ?? false,\n patchConsole: options.patchConsole ?? true,\n alternateScreen: options.alternateScreen ?? mode === \"fullscreen\",\n mode,\n nonTTYMode: options.nonTTYMode ?? (\"auto\" as NonTTYMode),\n }\n\n // Get or create instance for this stdout\n let instance = instances.get(resolvedOptions.stdout)\n if (!instance) {\n log.debug?.(\"render(): creating new SilveryInstance\")\n instance = new SilveryInstance(resolvedOptions)\n instances.set(resolvedOptions.stdout, instance)\n log.debug?.(`render(): SilveryInstance created in ${Date.now() - renderStart}ms`)\n }\n\n // Wrap element with TermContext\n const wrappedElement = <TermContext.Provider value={term}>{element}</TermContext.Provider>\n\n // Render the element\n log.debug?.(\"render(): calling instance.render()\")\n instance.render(wrappedElement)\n log.debug?.(`render(): instance.render() complete, total: ${Date.now() - renderStart}ms`)\n\n // Wrap rerender to also include contexts\n const rerender = (newElement: ReactNode) =>\n instance.rerender(<TermContext.Provider value={term}>{newElement}</TermContext.Provider>)\n\n return {\n rerender,\n unmount: instance.unmount,\n [Symbol.dispose]: instance.unmount,\n waitUntilExit: instance.waitUntilExit,\n clear: instance.clear,\n flush: instance.flush,\n pause: instance.pause,\n resume: instance.resume,\n }\n}\n\n/**\n * Render in static mode (no events, render until stable).\n * Internal implementation for render() when no events are present.\n */\nasync function renderStaticImpl(\n element: ReactElement,\n term: Term,\n resolved: ResolvedTermDef,\n): Promise<Instance> {\n log.debug?.(`renderStatic() called, dimensions: ${resolved.width}x${resolved.height}`)\n\n // Import renderString functionality\n const { renderStringSync } = await import(\"./render-string.js\")\n\n // Wrap element with contexts for static rendering\n const wrappedElement = <TermContext.Provider value={term}>{element}</TermContext.Provider>\n\n // Render to string\n const output = renderStringSync(wrappedElement, {\n width: resolved.width,\n height: resolved.height,\n plain: resolved.colors === null,\n })\n\n // Write output if we have a stdout\n if (resolved.stdout) {\n resolved.stdout.write(output)\n resolved.stdout.write(\"\\n\")\n }\n\n // Return a minimal Instance for static mode\n let lastFrame = output\n return {\n rerender: (newElement: ReactNode) => {\n const newWrapped = <TermContext.Provider value={term}>{newElement}</TermContext.Provider>\n lastFrame = renderStringSync(newWrapped as ReactElement, {\n width: resolved.width,\n height: resolved.height,\n plain: resolved.colors === null,\n })\n if (resolved.stdout) {\n resolved.stdout.write(lastFrame)\n resolved.stdout.write(\"\\n\")\n }\n },\n unmount: () => {},\n [Symbol.dispose]() {},\n waitUntilExit: () => Promise.resolve(),\n clear: () => {},\n pause: () => {},\n resume: () => {},\n // Extra property for accessing last rendered frame\n get lastFrame() {\n return lastFrame\n },\n } as Instance & { lastFrame: string }\n}\n\n/**\n * Synchronous render function for use when layout engine is already initialized.\n *\n * @example\n * ```tsx\n * import { renderSync, Box, Text, initYogaEngine, setLayoutEngine, createTerm } from '@silvery/ag-react';\n *\n * const engine = await initYogaEngine();\n * setLayoutEngine(engine);\n * using term = createTerm();\n * renderSync(<App />, term);\n * ```\n *\n * @param element - React element to render\n * @param termOrDef - Term instance or TermDef config\n * @param options - Additional render options\n * @returns An Instance object with control methods\n */\nexport function renderSync(\n element: ReactElement,\n termOrDef?: Term | TermDef,\n options?: RenderOptions,\n): Instance {\n if (!isLayoutEngineInitialized()) {\n throw new Error(\n \"Layout engine is not initialized. Call render() (async) first, or initialize manually with setLayoutEngine().\",\n )\n }\n\n // Resolve termOrDef\n let resolved: ResolvedTermDef\n let term: Term\n\n if (!termOrDef) {\n resolved = resolveTermDef({})\n term = createTerm({ color: resolved.colors ?? undefined })\n } else if (isTerm(termOrDef)) {\n resolved = resolveFromTerm(termOrDef)\n term = termOrDef\n } else if (isTermDef(termOrDef)) {\n resolved = resolveTermDef(termOrDef)\n term = createTerm({\n stdout: termOrDef.stdout,\n stdin: termOrDef.stdin,\n color: resolved.colors ?? undefined,\n })\n } else {\n throw new Error(\"Invalid second argument: expected Term, TermDef, or undefined\")\n }\n\n // For static mode, use sync string rendering\n if (resolved.isStatic) {\n const wrappedElement = <TermContext.Provider value={term}>{element}</TermContext.Provider>\n const lastFrame = renderStringSync(wrappedElement, {\n width: resolved.width,\n height: resolved.height,\n plain: resolved.colors === null,\n })\n if (resolved.stdout) {\n resolved.stdout.write(lastFrame)\n resolved.stdout.write(\"\\n\")\n }\n return {\n rerender: () => {},\n unmount: () => {},\n [Symbol.dispose]() {},\n waitUntilExit: () => Promise.resolve(),\n clear: () => {},\n flush: () => {},\n pause: () => {},\n resume: () => {},\n }\n }\n\n // Merge options for interactive mode\n const mergedOptions: RenderOptions = {\n ...options,\n stdout: options?.stdout ?? resolved.stdout ?? term.stdout,\n stdin: options?.stdin ?? term.stdin,\n }\n\n // alternateScreen defaults to true for fullscreen mode\n const mode = mergedOptions.mode ?? (\"fullscreen\" as RenderMode)\n const resolvedOptions = {\n stdout: mergedOptions.stdout ?? process.stdout,\n stdin: mergedOptions.stdin ?? process.stdin,\n exitOnCtrlC: mergedOptions.exitOnCtrlC ?? true,\n debug: mergedOptions.debug ?? false,\n patchConsole: mergedOptions.patchConsole ?? true,\n alternateScreen: mergedOptions.alternateScreen ?? mode === \"fullscreen\",\n mode,\n nonTTYMode: mergedOptions.nonTTYMode ?? (\"auto\" as NonTTYMode),\n }\n\n // Get or create instance for this stdout\n let instance = instances.get(resolvedOptions.stdout)\n if (!instance) {\n instance = new SilveryInstance(resolvedOptions)\n instances.set(resolvedOptions.stdout, instance)\n }\n\n // Wrap element with contexts\n const wrappedElement = <TermContext.Provider value={term}>{element}</TermContext.Provider>\n\n // Render the element\n instance.render(wrappedElement)\n\n // Wrap rerender to also include contexts\n const rerender = (newElement: ReactNode) =>\n instance!.rerender(<TermContext.Provider value={term}>{newElement}</TermContext.Provider>)\n\n return {\n rerender,\n unmount: instance.unmount,\n [Symbol.dispose]: instance.unmount,\n waitUntilExit: instance.waitUntilExit,\n clear: instance.clear,\n flush: instance.flush,\n pause: instance.pause,\n resume: instance.resume,\n }\n}\n\n// ============================================================================\n// Convenience Functions\n// ============================================================================\n\n/**\n * Static render - convenience function for one-shot renders.\n *\n * This is equivalent to `render(element)` without a Term - it renders\n * once and returns immediately without starting an event loop.\n *\n * @example\n * ```tsx\n * import { renderStatic, Box, Text } from '@silvery/ag-react';\n *\n * // Render a summary to stdout\n * await renderStatic(<Summary stats={stats} />);\n *\n * // With options\n * await renderStatic(<Report />, { width: 120 });\n * ```\n *\n * @param element - React element to render\n * @param options - Optional width, height, and other static render options\n * @returns Promise that resolves when rendering is complete\n */\nexport async function renderStatic(\n element: ReactElement,\n options?: {\n width?: number\n height?: number\n plain?: boolean\n layoutEngine?: LayoutEngineType\n },\n): Promise<string> {\n await ensureLayoutEngineInitialized(options?.layoutEngine)\n const { renderStringSync } = await import(\"./render-string.js\")\n return renderStringSync(element, options)\n}\n\n// Re-export layout engine management for convenience\nexport {\n setLayoutEngine,\n isLayoutEngineInitialized,\n type LayoutEngineType,\n} from \"@silvery/ag-term/layout-engine\"\n\n// Re-export adapters for custom engine initialization\nexport {\n createYogaEngine,\n initYogaEngine,\n YogaLayoutEngine,\n} from \"@silvery/ag-term/adapters/yoga-adapter\"\nexport {\n createFlexilyZeroEngine as createFlexilyEngine,\n FlexilyZeroLayoutEngine as FlexilyLayoutEngine,\n} from \"@silvery/ag-term/adapters/flexily-zero-adapter\"\n","/**\n * Silvery measureElement\n *\n * Backward-compatible API for measuring element dimensions.\n * This is provided for Ink compatibility - prefer using the useBoxRect() hook instead.\n *\n * @example\n * ```tsx\n * import { measureElement, Box } from '@silvery/ag-react';\n * import { useRef, useEffect, useState } from 'react';\n *\n * function MyComponent() {\n * const ref = useRef(null);\n * const [width, setWidth] = useState(0);\n *\n * useEffect(() => {\n * if (ref.current) {\n * const { width } = measureElement(ref.current);\n * setWidth(width);\n * }\n * }, []);\n *\n * return <Box ref={ref}><Text>Width: {width}</Text></Box>;\n * }\n * ```\n *\n * Note: The useBoxRect() hook is preferred as it automatically re-renders\n * when dimensions change:\n *\n * ```tsx\n * function MyComponent() {\n * const { width } = useBoxRect();\n * return <Text>Width: {width}</Text>;\n * }\n * ```\n */\n\nimport type { AgNode } from \"@silvery/ag/types\"\n\n/**\n * Output from measureElement.\n */\nexport interface MeasureElementOutput {\n /** Element width in terminal columns */\n width: number\n /** Element height in terminal rows */\n height: number\n}\n\n/**\n * Resolve a ref value to a AgNode. Handles both direct AgNode refs\n * and BoxHandle refs (from silvery's Box component which uses useImperativeHandle).\n * Ink users pass ref.current which resolves to a BoxHandle, not a AgNode directly.\n */\nfunction resolveNode(nodeOrHandle: any): AgNode | null {\n if (!nodeOrHandle) return null\n // BoxHandle from silvery's Box component (has getNode method)\n if (typeof nodeOrHandle.getNode === \"function\") {\n return nodeOrHandle.getNode()\n }\n // Direct AgNode\n return nodeOrHandle as AgNode\n}\n\n/**\n * Measure the dimensions of a Silvery element.\n *\n * @param nodeOrHandle - The SilveryNode or BoxHandle to measure (obtained via ref)\n * @returns The computed width and height of the element\n *\n * Note: Returns { width: 0, height: 0 } if the element hasn't been laid out yet.\n * For automatic re-rendering on dimension changes, use the useBoxRect() hook instead.\n */\nexport function measureElement(nodeOrHandle: AgNode | unknown): MeasureElementOutput {\n const node = resolveNode(nodeOrHandle)\n if (!node) {\n return { width: 0, height: 0 }\n }\n\n // Prefer boxRect (set by silvery pipeline after layout phase)\n // This is the canonical source of truth after a render\n if (node.boxRect) {\n return {\n width: node.boxRect.width,\n height: node.boxRect.height,\n }\n }\n\n // Fall back to layoutNode for backward compatibility\n // (handles case where measureElement is called before silvery pipeline runs)\n const width = node.layoutNode?.getComputedWidth() ?? 0\n const height = node.layoutNode?.getComputedHeight() ?? 0\n\n return {\n // Handle NaN from Yoga (returned before calculateLayout is called)\n width: Number.isNaN(width) ? 0 : width,\n height: Number.isNaN(height) ? 0 : height,\n }\n}\n","/**\n * Screen Reader Mode — ARIA-based accessible text rendering.\n *\n * Walks a React element tree and produces plain text output with\n * ARIA roles, labels, and states for screen reader consumption.\n *\n * Rules:\n * - `aria-hidden` → skip element entirely\n * - `display=\"none\"` → skip element entirely\n * - `aria-label` → use label instead of children text\n * - `aria-role` → prefix with \"role: \"\n * - `aria-state` → prepend active states as \"(state) \"\n * - Row direction → space-separated children\n * - Column direction → newline-separated children\n * - Plain text content (no ANSI codes)\n */\n\nimport React, { type ReactNode } from \"react\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * ARIA state flags that can be set on elements via `aria-state` prop.\n */\nexport interface AriaState {\n busy?: boolean\n checked?: boolean\n disabled?: boolean\n expanded?: boolean\n multiline?: boolean\n multiselectable?: boolean\n readonly?: boolean\n required?: boolean\n selected?: boolean\n}\n\n// =============================================================================\n// Public API\n// =============================================================================\n\n/**\n * Walk a React element tree and produce accessible text output.\n *\n * @param node - React node to render as screen reader text\n * @returns Plain text with ARIA annotations\n */\nexport function renderScreenReaderOutput(node: React.ReactNode): string {\n return walkNode(node, \"row\")\n}\n\n// =============================================================================\n// Internal\n// =============================================================================\n\n/**\n * Recursively walk a React node and produce screen reader text.\n * @param node - React node to walk\n * @param parentDirection - flex direction of the parent container\n */\nfunction walkNode(node: React.ReactNode, parentDirection: \"row\" | \"column\"): string {\n // Null, undefined, boolean → empty\n if (node == null || typeof node === \"boolean\") {\n return \"\"\n }\n\n // String or number → literal text\n if (typeof node === \"string\" || typeof node === \"number\") {\n return String(node)\n }\n\n // Arrays/fragments → join children\n if (Array.isArray(node)) {\n const parts = node\n .map((child) => walkNode(child as ReactNode, parentDirection))\n .filter((s) => s !== \"\")\n const sep = parentDirection === \"column\" ? \"\\n\" : \" \"\n return parts.join(sep)\n }\n\n // React element\n if (React.isValidElement(node)) {\n const props = node.props as Record<string, any>\n\n // aria-hidden → skip entirely\n if (props[\"aria-hidden\"]) {\n return \"\"\n }\n\n // display=\"none\" → skip entirely\n if (props.display === \"none\") {\n return \"\"\n }\n\n // Determine this element's flex direction\n const direction: \"row\" | \"column\" = props.flexDirection === \"column\" ? \"column\" : \"row\"\n\n // Build the content: aria-label overrides children\n let content: string\n if (props[\"aria-label\"] != null) {\n content = String(props[\"aria-label\"])\n } else {\n // Walk children\n const children = props.children\n content = walkChildren(children, direction)\n }\n\n // Build ARIA state prefix\n const statePrefix = buildStatePrefix(props[\"aria-state\"])\n\n // Build role prefix\n const role = props[\"aria-role\"]\n\n // Assemble output\n if (role && statePrefix) {\n return `${role}: ${statePrefix}${content}`\n }\n if (role) {\n return `${role}: ${content}`\n }\n if (statePrefix) {\n return `${statePrefix}${content}`\n }\n\n return content\n }\n\n return \"\"\n}\n\n/**\n * Walk children of a React element, joining with direction-appropriate separator.\n */\nfunction walkChildren(children: React.ReactNode, direction: \"row\" | \"column\"): string {\n if (children == null) return \"\"\n\n // Single child\n if (!Array.isArray(children)) {\n // React.Children.toArray normalizes fragments, filters nulls\n const childArray = React.Children.toArray(children)\n if (childArray.length <= 1) {\n return walkNode(children, direction)\n }\n const parts = childArray.map((child) => walkNode(child, direction)).filter((s) => s !== \"\")\n const sep = direction === \"column\" ? \"\\n\" : \" \"\n return parts.join(sep)\n }\n\n // Array of children\n const parts = children\n .map((child) => walkNode(child as ReactNode, direction))\n .filter((s) => s !== \"\")\n const sep = direction === \"column\" ? \"\\n\" : \" \"\n return parts.join(sep)\n}\n\n/**\n * Build the state prefix string from aria-state object.\n * Active (truthy) states become \"(stateName) \" prefix.\n */\nfunction buildStatePrefix(state: AriaState | undefined): string {\n if (!state) return \"\"\n\n const activeStates: string[] = []\n // Check each state in a consistent order\n const stateNames: (keyof AriaState)[] = [\n \"busy\",\n \"checked\",\n \"disabled\",\n \"expanded\",\n \"multiline\",\n \"multiselectable\",\n \"readonly\",\n \"required\",\n \"selected\",\n ]\n\n for (const name of stateNames) {\n if (state[name]) {\n activeStates.push(`(${name})`)\n }\n }\n\n if (activeStates.length === 0) return \"\"\n return activeStates.join(\" \") + \" \"\n}\n","/**\n * Terminal Edit Context\n *\n * W3C EditContext-aligned interface for terminal text editing.\n * Factory function returns a plain object -- no class, per km principles.\n *\n * Uses text-cursor.ts for all cursor <-> visual position math.\n * Uses text-ops.ts for invertible text operations.\n *\n * Architecture layer 1 -- stateful, but no hooks, no components.\n *\n * @example\n * ```ts\n * import { createTermEditContext } from '@silvery/ag-react'\n *\n * using ctx = createTermEditContext({ text: \"hello world\", wrapWidth: 40 })\n * ctx.onTextUpdate((op) => undoStack.push(op))\n *\n * ctx.insertChar(\"!\") // \"hello world!\"\n * ctx.moveCursor(\"left\") // cursor before \"!\"\n * ctx.deleteForward() // \"hello world\"\n * ```\n */\n\nimport {\n cursorToRowCol,\n cursorMoveUp,\n cursorMoveDown,\n getWrappedLines,\n countVisualLines,\n} from \"@silvery/create/text-cursor\"\nimport type { TextOp } from \"@silvery/create/text-ops\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** Platform-agnostic text editing contract, aligned with W3C EditContext. */\nexport interface EditContextLike {\n /** Current text content (readonly -- mutate via updateText). */\n readonly text: string\n /** Start of the selection range (or cursor position if no selection). */\n readonly selectionStart: number\n /** End of the selection range (equals selectionStart when no selection). */\n readonly selectionEnd: number\n\n /**\n * Replace text in [rangeStart, rangeEnd) with newText.\n * Adjusts selection if it falls within the affected range.\n * Returns the TextOp describing the mutation (for undo).\n */\n updateText(rangeStart: number, rangeEnd: number, newText: string): TextOp\n\n /** Move the selection/cursor. If end is omitted, collapses to a caret. */\n updateSelection(start: number, end?: number): void\n\n /** Subscribe to text mutations. Returns an unsubscribe function. */\n onTextUpdate(handler: (op: TextOp) => void): () => void\n\n /** Subscribe to selection changes. Returns an unsubscribe function. */\n onSelectionChange(handler: (start: number, end: number) => void): () => void\n}\n\n/** Terminal-specific extensions over EditContextLike. */\nexport interface TermEditContext extends EditContextLike {\n /** Current wrap width for visual line calculations. */\n readonly wrapWidth: number\n\n /**\n * Sticky column for vertical cursor movement. Set when the cursor moves\n * up/down, cleared on horizontal movement. Allows the cursor to return\n * to its preferred column when moving through lines of varying length.\n */\n readonly stickyX: number | null\n\n /**\n * Move the cursor one step in the given direction.\n * Returns false if the cursor is already at the boundary (first/last\n * visual line for up/down, position 0/end for left/right).\n */\n moveCursor(direction: \"up\" | \"down\" | \"left\" | \"right\"): boolean\n\n /**\n * Check whether the cursor is at the boundary in the given direction.\n * \"up\" = cursor is on the first visual line.\n * \"down\" = cursor is on the last visual line.\n */\n atBoundary(direction: \"up\" | \"down\"): boolean\n\n /** Insert a character (or string) at the current cursor position. */\n insertChar(char: string): TextOp\n\n /** Delete one character before the cursor. Null if at position 0. */\n deleteBackward(): TextOp | null\n\n /** Delete one character after the cursor. Null if at end of text. */\n deleteForward(): TextOp | null\n\n /** Delete the word before the cursor. Null if at position 0. */\n deleteWord(): TextOp | null\n\n /** Delete from cursor to start of the current visual line. Null if at line start. */\n deleteToStart(): TextOp | null\n\n /** Delete from cursor to end of the current visual line. Null if at line end. */\n deleteToEnd(): TextOp | null\n\n /** Get the current text content. */\n getContent(): string\n\n /** Get the cursor offset (alias for selectionStart). */\n getCursorOffset(): number\n\n /** Set the cursor offset (collapses selection). */\n setCursorOffset(offset: number): void\n\n /** Count of visual lines after word wrapping. */\n getVisualLineCount(): number\n\n /** Cursor position as visual (row, col). */\n getCursorRowCol(): { row: number; col: number }\n\n /** Update the wrap width (e.g., on terminal resize). */\n setWrapWidth(width: number): void\n\n /** Cleanup: clears all subscriber arrays. */\n [Symbol.dispose](): void\n}\n\n/** Options for createTermEditContext. */\nexport interface TermEditContextOptions {\n /** Initial text content. Defaults to \"\". */\n text?: string\n /** Initial selection start. Defaults to 0. */\n selectionStart?: number\n /** Initial selection end. Defaults to selectionStart. */\n selectionEnd?: number\n /** Wrap width for visual line calculations. Defaults to 80. */\n wrapWidth?: number\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\n/**\n * Create a terminal edit context.\n *\n * Returns a plain TermEditContext object with mutable internal state.\n * All cursor-to-visual-position math delegates to text-cursor.ts.\n */\nexport function createTermEditContext(options?: TermEditContextOptions): TermEditContext {\n // Internal mutable state\n let _text = options?.text ?? \"\"\n let _selectionStart = options?.selectionStart ?? 0\n let _selectionEnd = options?.selectionEnd ?? _selectionStart\n let _wrapWidth = options?.wrapWidth ?? 80\n let _stickyX: number | null = null\n\n // Subscriber arrays\n let _textHandlers: Array<(op: TextOp) => void> = []\n let _selectionHandlers: Array<(start: number, end: number) => void> = []\n\n // ---------------------------------------------------------------------------\n // Helpers\n // ---------------------------------------------------------------------------\n\n function clampOffset(offset: number): number {\n return Math.max(0, Math.min(offset, _text.length))\n }\n\n function fireTextUpdate(op: TextOp): void {\n for (const handler of _textHandlers) handler(op)\n }\n\n function fireSelectionChange(): void {\n for (const handler of _selectionHandlers) handler(_selectionStart, _selectionEnd)\n }\n\n /**\n * Scan backward from offset to find the start of a word-delete range.\n * Skips trailing whitespace, then deletes through word characters.\n */\n function wordDeleteStart(offset: number): number {\n let pos = offset\n // Skip whitespace backward\n while (pos > 0 && isWhitespace(_text[pos - 1]!)) pos--\n // Skip word characters backward\n while (pos > 0 && !isWhitespace(_text[pos - 1]!)) pos--\n return pos\n }\n\n /**\n * Find the start of the current visual line for the given cursor offset.\n */\n function visualLineStart(offset: number): number {\n const lines = getWrappedLines(_text, _wrapWidth)\n const { row } = cursorToRowCol(_text, offset, _wrapWidth)\n const line = lines[row]\n return line ? line.startOffset : 0\n }\n\n /**\n * Find the end of the current visual line for the given cursor offset.\n */\n function visualLineEnd(offset: number): number {\n const lines = getWrappedLines(_text, _wrapWidth)\n const { row } = cursorToRowCol(_text, offset, _wrapWidth)\n const line = lines[row]\n if (!line) return _text.length\n return line.startOffset + line.line.length\n }\n\n // ---------------------------------------------------------------------------\n // EditContextLike implementation\n // ---------------------------------------------------------------------------\n\n function updateText(rangeStart: number, rangeEnd: number, newText: string): TextOp {\n const start = clampOffset(rangeStart)\n const end = clampOffset(rangeEnd)\n\n if (start > end) {\n throw new RangeError(`updateText: rangeStart (${start}) > rangeEnd (${end})`)\n }\n\n const deletedText = _text.slice(start, end)\n _text = _text.slice(0, start) + newText + _text.slice(end)\n\n // Build the TextOp describing this mutation (for undo).\n let op: TextOp\n if (deletedText.length > 0 && newText.length === 0) {\n op = { type: \"delete\", offset: start, text: deletedText }\n } else if (deletedText.length === 0 && newText.length > 0) {\n op = { type: \"insert\", offset: start, text: newText }\n } else {\n // Replace: both deletion and insertion. Use a replace op so that\n // invertTextOp can produce the correct inverse (swap deleted/text).\n op = { type: \"replace\", offset: start, text: newText, deleted: deletedText }\n }\n\n // Adjust selection: place cursor at end of inserted text\n _selectionStart = start + newText.length\n _selectionEnd = _selectionStart\n\n fireTextUpdate(op)\n fireSelectionChange()\n return op\n }\n\n function updateSelection(start: number, end?: number): void {\n _selectionStart = clampOffset(start)\n _selectionEnd = end !== undefined ? clampOffset(end) : _selectionStart\n fireSelectionChange()\n }\n\n function onTextUpdate(handler: (op: TextOp) => void): () => void {\n _textHandlers.push(handler)\n return () => {\n _textHandlers = _textHandlers.filter((h) => h !== handler)\n }\n }\n\n function onSelectionChange(handler: (start: number, end: number) => void): () => void {\n _selectionHandlers.push(handler)\n return () => {\n _selectionHandlers = _selectionHandlers.filter((h) => h !== handler)\n }\n }\n\n // ---------------------------------------------------------------------------\n // TermEditContext extensions\n // ---------------------------------------------------------------------------\n\n function moveCursor(direction: \"up\" | \"down\" | \"left\" | \"right\"): boolean {\n const hasSelection = _selectionStart !== _selectionEnd\n\n switch (direction) {\n case \"left\": {\n _stickyX = null\n if (hasSelection) {\n // Collapse to the left edge of the selection\n const left = Math.min(_selectionStart, _selectionEnd)\n _selectionStart = left\n _selectionEnd = left\n fireSelectionChange()\n return true\n }\n if (_selectionStart === 0) return false\n _selectionStart = _selectionStart - 1\n _selectionEnd = _selectionStart\n fireSelectionChange()\n return true\n }\n case \"right\": {\n _stickyX = null\n if (hasSelection) {\n // Collapse to the right edge of the selection\n const right = Math.max(_selectionStart, _selectionEnd)\n _selectionStart = right\n _selectionEnd = right\n fireSelectionChange()\n return true\n }\n if (_selectionStart >= _text.length) return false\n _selectionStart = _selectionStart + 1\n _selectionEnd = _selectionStart\n fireSelectionChange()\n return true\n }\n case \"up\": {\n // Collapse selection first, then move up\n if (hasSelection) {\n const left = Math.min(_selectionStart, _selectionEnd)\n _selectionStart = left\n _selectionEnd = left\n }\n if (_stickyX === null) {\n const { col } = cursorToRowCol(_text, _selectionStart, _wrapWidth)\n _stickyX = col\n }\n const next = cursorMoveUp(_text, _selectionStart, _wrapWidth, _stickyX)\n if (next === null) {\n if (hasSelection) fireSelectionChange()\n return false\n }\n _selectionStart = next\n _selectionEnd = next\n fireSelectionChange()\n return true\n }\n case \"down\": {\n // Collapse selection first, then move down\n if (hasSelection) {\n const right = Math.max(_selectionStart, _selectionEnd)\n _selectionStart = right\n _selectionEnd = right\n }\n if (_stickyX === null) {\n const { col } = cursorToRowCol(_text, _selectionStart, _wrapWidth)\n _stickyX = col\n }\n const next = cursorMoveDown(_text, _selectionStart, _wrapWidth, _stickyX)\n if (next === null) {\n if (hasSelection) fireSelectionChange()\n return false\n }\n _selectionStart = next\n _selectionEnd = next\n fireSelectionChange()\n return true\n }\n }\n }\n\n function atBoundary(direction: \"up\" | \"down\"): boolean {\n if (direction === \"up\") {\n const { row } = cursorToRowCol(_text, _selectionStart, _wrapWidth)\n return row === 0\n }\n const { row } = cursorToRowCol(_text, _selectionStart, _wrapWidth)\n const totalLines = countVisualLines(_text, _wrapWidth)\n return row >= totalLines - 1\n }\n\n function insertChar(char: string): TextOp {\n _stickyX = null\n return updateText(_selectionStart, _selectionEnd, char)\n }\n\n function deleteBackward(): TextOp | null {\n _stickyX = null\n if (_selectionStart !== _selectionEnd) {\n const start = Math.min(_selectionStart, _selectionEnd)\n const end = Math.max(_selectionStart, _selectionEnd)\n return updateText(start, end, \"\")\n }\n if (_selectionStart === 0) return null\n return updateText(_selectionStart - 1, _selectionStart, \"\")\n }\n\n function deleteForward(): TextOp | null {\n _stickyX = null\n if (_selectionStart !== _selectionEnd) {\n const start = Math.min(_selectionStart, _selectionEnd)\n const end = Math.max(_selectionStart, _selectionEnd)\n return updateText(start, end, \"\")\n }\n if (_selectionStart >= _text.length) return null\n return updateText(_selectionStart, _selectionStart + 1, \"\")\n }\n\n function deleteWord(): TextOp | null {\n _stickyX = null\n if (_selectionStart !== _selectionEnd) {\n const start = Math.min(_selectionStart, _selectionEnd)\n const end = Math.max(_selectionStart, _selectionEnd)\n return updateText(start, end, \"\")\n }\n if (_selectionStart === 0) return null\n const start = wordDeleteStart(_selectionStart)\n if (start === _selectionStart) return null\n return updateText(start, _selectionStart, \"\")\n }\n\n function deleteToStart(): TextOp | null {\n _stickyX = null\n if (_selectionStart !== _selectionEnd) {\n const start = Math.min(_selectionStart, _selectionEnd)\n const end = Math.max(_selectionStart, _selectionEnd)\n return updateText(start, end, \"\")\n }\n const lineStart = visualLineStart(_selectionStart)\n if (lineStart === _selectionStart) return null\n return updateText(lineStart, _selectionStart, \"\")\n }\n\n function deleteToEnd(): TextOp | null {\n _stickyX = null\n if (_selectionStart !== _selectionEnd) {\n const start = Math.min(_selectionStart, _selectionEnd)\n const end = Math.max(_selectionStart, _selectionEnd)\n return updateText(start, end, \"\")\n }\n const lineEnd = visualLineEnd(_selectionStart)\n if (lineEnd === _selectionStart) return null\n return updateText(_selectionStart, lineEnd, \"\")\n }\n\n function getContent(): string {\n return _text\n }\n\n function getCursorOffset(): number {\n return _selectionStart\n }\n\n function setCursorOffset(offset: number): void {\n _stickyX = null\n updateSelection(offset)\n }\n\n function getVisualLineCount(): number {\n return countVisualLines(_text, _wrapWidth)\n }\n\n function getCursorRowCol(): { row: number; col: number } {\n return cursorToRowCol(_text, _selectionStart, _wrapWidth)\n }\n\n function setWrapWidth(width: number): void {\n if (width <= 0) {\n throw new RangeError(`setWrapWidth: width must be positive, got ${width}`)\n }\n _wrapWidth = width\n }\n\n function dispose(): void {\n _textHandlers = []\n _selectionHandlers = []\n }\n\n // ---------------------------------------------------------------------------\n // Return plain object\n // ---------------------------------------------------------------------------\n\n return {\n get text() {\n return _text\n },\n get selectionStart() {\n return _selectionStart\n },\n get selectionEnd() {\n return _selectionEnd\n },\n get wrapWidth() {\n return _wrapWidth\n },\n get stickyX() {\n return _stickyX\n },\n\n updateText,\n updateSelection,\n onTextUpdate,\n onSelectionChange,\n\n moveCursor,\n atBoundary,\n insertChar,\n deleteBackward,\n deleteForward,\n deleteWord,\n deleteToStart,\n deleteToEnd,\n getContent,\n getCursorOffset,\n setCursorOffset,\n getVisualLineCount,\n getCursorRowCol,\n setWrapWidth,\n\n [Symbol.dispose]: dispose,\n }\n}\n\n// =============================================================================\n// Helpers (below exports per inverted pyramid)\n// =============================================================================\n\nfunction isWhitespace(ch: string): boolean {\n return ch === \" \" || ch === \"\\t\" || ch === \"\\n\" || ch === \"\\r\"\n}\n","/**\n * Text Operations -- invertible, composable text mutations.\n *\n * Every text change produces a TextOp that can be inverted (for undo)\n * and composed (for merging consecutive typing). Foundation for\n * operations-based undo/redo without document snapshots.\n *\n * Architecture layer 0 -- no state, no hooks, no components.\n *\n * @example\n * ```ts\n * import { applyTextOp, invertTextOp, mergeTextOps } from '@silvery/ag-react'\n *\n * const op: TextOp = { type: \"insert\", offset: 5, text: \"hello\" }\n * const result = applyTextOp(\"world\", op) // \"worlhellod\"\n * const inv = invertTextOp(op) // { type: \"delete\", offset: 5, text: \"hello\" }\n * ```\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * An invertible text operation.\n *\n * - `insert`: text was inserted at `offset`\n * - `delete`: text was deleted starting at `offset` (the deleted content is\n * stored in `text` so the operation can be inverted)\n * - `replace`: text at `offset` was replaced — `deleted` holds the old text,\n * `text` holds the new text. The inverse swaps them.\n */\nexport type TextOp =\n | { type: \"insert\"; offset: number; text: string }\n | { type: \"delete\"; offset: number; text: string }\n | { type: \"replace\"; offset: number; text: string; deleted: string }\n\n// =============================================================================\n// Core Functions\n// =============================================================================\n\n/**\n * Apply a text operation to a string, returning the modified string.\n *\n * Throws if the offset is out of bounds or if a delete operation's stored\n * text doesn't match what's actually in the string at that position.\n */\nexport function applyTextOp(text: string, op: TextOp): string {\n if (op.offset < 0 || op.offset > text.length) {\n throw new RangeError(\n `TextOp offset ${op.offset} out of bounds for text of length ${text.length}`,\n )\n }\n\n if (op.type === \"insert\") {\n return text.slice(0, op.offset) + op.text + text.slice(op.offset)\n }\n\n if (op.type === \"replace\") {\n const end = op.offset + op.deleted.length\n if (end > text.length) {\n throw new RangeError(\n `TextOp replace extends past end: offset=${op.offset}, deleteLen=${op.deleted.length}, textLen=${text.length}`,\n )\n }\n const actual = text.slice(op.offset, end)\n if (actual !== op.deleted) {\n throw new Error(\n `TextOp replace mismatch at offset ${op.offset}: expected ${JSON.stringify(op.deleted)}, got ${JSON.stringify(actual)}`,\n )\n }\n return text.slice(0, op.offset) + op.text + text.slice(end)\n }\n\n // delete\n const end = op.offset + op.text.length\n if (end > text.length) {\n throw new RangeError(\n `TextOp delete extends past end: offset=${op.offset}, deleteLen=${op.text.length}, textLen=${text.length}`,\n )\n }\n const actual = text.slice(op.offset, end)\n if (actual !== op.text) {\n throw new Error(\n `TextOp delete mismatch at offset ${op.offset}: expected ${JSON.stringify(op.text)}, got ${JSON.stringify(actual)}`,\n )\n }\n return text.slice(0, op.offset) + text.slice(end)\n}\n\n/**\n * Invert a text operation (insert becomes delete and vice versa).\n *\n * The inverse of an insert at offset N is a delete of the same text at\n * offset N; the inverse of a delete is an insert.\n */\nexport function invertTextOp(op: TextOp): TextOp {\n if (op.type === \"insert\") {\n return { type: \"delete\", offset: op.offset, text: op.text }\n }\n if (op.type === \"replace\") {\n return { type: \"replace\", offset: op.offset, text: op.deleted, deleted: op.text }\n }\n return { type: \"insert\", offset: op.offset, text: op.text }\n}\n\n/**\n * Attempt to merge two consecutive text operations into one.\n *\n * Returns the merged operation, or `null` if the operations can't be merged.\n *\n * Merge rules:\n * - Two inserts where `b` starts exactly where `a` ends -> single insert\n * - Two deletes where `b` ends exactly where `a` starts (backspace sequence)\n * -> single delete covering both ranges\n * - Two deletes where `b` starts at `a`'s offset (forward-delete sequence)\n * -> single delete covering both ranges\n * - Otherwise -> null (can't merge)\n */\nexport function mergeTextOps(a: TextOp, b: TextOp): TextOp | null {\n // insert + insert: b inserts right after a's inserted text\n if (a.type === \"insert\" && b.type === \"insert\") {\n if (b.offset === a.offset + a.text.length) {\n return { type: \"insert\", offset: a.offset, text: a.text + b.text }\n }\n return null\n }\n\n // delete + delete\n if (a.type === \"delete\" && b.type === \"delete\") {\n // Backspace sequence: b deletes the character just before a's range.\n // After a deletes at offset X, the next backspace deletes at offset X-1.\n if (b.offset + b.text.length === a.offset) {\n return { type: \"delete\", offset: b.offset, text: b.text + a.text }\n }\n // Forward-delete sequence: b deletes at the same position as a (because\n // after a removed its text, the next character slid into the same offset).\n if (b.offset === a.offset) {\n return { type: \"delete\", offset: a.offset, text: a.text + b.text }\n }\n return null\n }\n\n // insert + delete that exactly cancels the insert\n if (a.type === \"insert\" && b.type === \"delete\") {\n if (b.offset === a.offset && b.text === a.text) {\n return null // operations cancel out\n }\n }\n\n return null\n}\n","/**\n * useEditContext Hook\n *\n * React hook wrapping createTermEditContext. Drop-in replacement for\n * useSlateEdit and useLineEdit — same consumer interface, unified\n * EditContext backend.\n *\n * Registers as active edit context on mount, cleans up on unmount.\n * Auto-saves on unmount if value changed and not explicitly cancelled.\n */\n\nimport { useState, useCallback, useEffect, useLayoutEffect, useMemo, useRef } from \"react\"\nimport { createTermEditContext } from \"../edit-context\"\nimport type { TermEditContext } from \"../edit-context\"\nimport type { TextOp } from \"@silvery/create/text-ops\"\nimport { rowColToCursor, countVisualLines } from \"@silvery/create/text-cursor\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface UseEditContextOptions {\n /** Initial text value */\n initialValue?: string\n /** Called when value changes (every keystroke) */\n onChange?: (value: string) => void\n /** Called when Enter is pressed (text.confirm command) */\n onConfirm?: (value: string) => void\n /** Called when exiting edit mode (Escape) */\n onCancel?: () => void\n /** Called when save() is invoked (auto-save on block navigate) */\n onSave?: (value: string) => void\n /** Called when Enter creates a new tree node (split at boundary) */\n onSplitAtBoundary?: (offset: number) => void\n /** Called when Backspace at start needs a tree merge */\n onMergeBackward?: () => void\n /** Available width for visual line wrapping */\n wrapWidth?: number\n /** Initial cursor position */\n initialCursorPos?: \"start\" | \"end\" | number\n /** Preferred cursor column preserved across block boundaries (visual column index) */\n stickyX?: number\n /** Called on each text operation (for undo log) */\n onTextOp?: (op: TextOp) => void\n}\n\n/**\n * BlockEditTarget-compatible interface.\n *\n * Methods match the existing BlockEditTarget type from km-tui so that\n * board-actions.ts can call them without changes. This is defined here\n * (in silvery, a lower layer) to avoid importing from km-tui.\n */\nexport interface EditTarget {\n insertChar(char: string): void\n deleteBackward(): void\n deleteForward(): void\n cursorLeft(): void\n cursorRight(): void\n cursorUp(): boolean\n cursorDown(): boolean\n cursorStart(): void\n cursorEnd(): void\n deleteWord(): void\n deleteToStart(): void\n deleteToEnd(): void\n confirm(): void\n cancel(): void\n save(): void\n getCursorOffset(): number\n getContent(): string\n insertBreak(): boolean\n replaceContent(content: string, cursor: number): void\n /** Set cursor position from external source (e.g. mouse click) */\n setCursorOffset(offset: number): void\n}\n\nexport interface UseEditContextResult {\n /** Current text value */\n value: string\n /** Cursor position (character offset) */\n cursor: number\n /** Text before cursor (for rendering) */\n beforeCursor: string\n /** Text after cursor (for rendering) */\n afterCursor: string\n /** Clear the input */\n clear: () => void\n /** Set value programmatically */\n setValue: (value: string) => void\n /** The underlying TermEditContext (for advanced usage) */\n editContext: TermEditContext\n /** BlockEditTarget-compatible object for command system integration */\n target: EditTarget\n /** Set cursor position from external source (e.g. mouse click) */\n setCursorOffset: (offset: number) => void\n}\n\n/**\n * Shared mutable ref for the active edit context.\n * Only one edit context is active at a time (inline edit or search).\n * Set by useEditContext on mount, cleared on unmount.\n */\nexport const activeEditContextRef: { current: TermEditContext | null } = {\n current: null,\n}\n\n/**\n * Shared mutable ref for the active edit target.\n * Stores the EditTarget wrapper (BlockEditTarget-compatible methods).\n * board-actions.ts reads this to dispatch text editing commands.\n * Set by useEditContext on mount, cleared on unmount.\n */\nexport const activeEditTargetRef: { current: EditTarget | null } = {\n current: null,\n}\n\n// =============================================================================\n// Hook\n// =============================================================================\n\n/**\n * Hook wrapping createTermEditContext for React components.\n *\n * Implements the same BlockEditTarget interface as useSlateEdit and\n * useLineEdit, so it's a drop-in replacement for the command system.\n *\n * Key behaviors:\n * - Creates a stable TermEditContext on mount\n * - Registers as active edit context (via activeEditContextRef)\n * - Subscribes to text updates for re-rendering\n * - Auto-saves on unmount if value changed and not explicitly cancelled\n * - Exposes BlockEditTarget-compatible methods for board-actions.ts\n *\n * WARNING — Auto-save on unmount:\n * This hook fires onConfirm on unmount if cancelledRef is not set.\n * For inline editing fields (navigate away = save), this is correct.\n * For DIALOGS, this causes double-confirm bugs — the dialog closes\n * (unmount), then auto-save fires onConfirm again.\n *\n * Dialog components MUST use useDialogInput (km-tui) instead of this\n * hook directly. useDialogInput never passes onConfirm/onCancel here,\n * making the auto-save inert. See km-qaco9 for the full root cause.\n */\nexport function useEditContext({\n initialValue = \"\",\n onChange,\n onConfirm,\n onCancel,\n onSave,\n onSplitAtBoundary,\n onMergeBackward,\n wrapWidth,\n initialCursorPos,\n stickyX,\n onTextOp,\n}: UseEditContextOptions = {}): UseEditContextResult {\n // Stable refs for callbacks (avoid stale closures)\n const onChangeRef = useRef(onChange)\n onChangeRef.current = onChange\n const onConfirmRef = useRef(onConfirm)\n onConfirmRef.current = onConfirm\n const onCancelRef = useRef(onCancel)\n onCancelRef.current = onCancel\n const onSaveRef = useRef(onSave)\n onSaveRef.current = onSave\n const onSplitRef = useRef(onSplitAtBoundary)\n onSplitRef.current = onSplitAtBoundary\n const onMergeBackwardRef = useRef(onMergeBackward)\n onMergeBackwardRef.current = onMergeBackward\n const onTextOpRef = useRef(onTextOp)\n onTextOpRef.current = onTextOp\n\n // Track whether cancel was called (suppresses auto-save on unmount)\n const cancelledRef = useRef(false)\n const initialValueRef = useRef(initialValue)\n\n // Force re-render counter — bumped on every text/cursor change\n const [_version, setVersion] = useState(0)\n const forceRender = useCallback(() => setVersion((v) => v + 1), [])\n\n // Create TermEditContext (stable across renders)\n const ctx = useMemo(() => {\n const effectiveWrapWidth = wrapWidth ?? Infinity\n let cursorPos: number\n if (stickyX != null && initialValue.length > 0) {\n if (typeof initialCursorPos === \"number\") {\n cursorPos = Math.max(0, Math.min(initialCursorPos, initialValue.length))\n } else {\n const targetRow =\n initialCursorPos === \"start\"\n ? 0\n : Math.max(0, countVisualLines(initialValue, effectiveWrapWidth) - 1)\n cursorPos = rowColToCursor(initialValue, targetRow, stickyX, effectiveWrapWidth)\n }\n } else {\n cursorPos =\n typeof initialCursorPos === \"number\"\n ? Math.max(0, Math.min(initialCursorPos, initialValue.length))\n : initialCursorPos === \"start\"\n ? 0\n : initialValue.length\n }\n return createTermEditContext({\n text: initialValue,\n selectionStart: cursorPos,\n selectionEnd: cursorPos,\n wrapWidth: effectiveWrapWidth,\n })\n }, []) // eslint-disable-line react-hooks/exhaustive-deps -- intentionally stable\n\n // Subscribe to text updates for onChange, onTextOp, and re-rendering\n useLayoutEffect(() => {\n const unsub = ctx.onTextUpdate((op: TextOp) => {\n onChangeRef.current?.(ctx.text)\n onTextOpRef.current?.(op)\n forceRender()\n })\n return unsub\n }, [ctx, forceRender])\n\n // Update wrapWidth when it changes\n useEffect(() => {\n if (wrapWidth !== undefined) {\n ctx.setWrapWidth(wrapWidth)\n }\n }, [wrapWidth, ctx])\n\n // Build BlockEditTarget-compatible object for board-actions.ts\n const target = useMemo(\n () => ({\n insertChar(char: string) {\n ctx.insertChar(char)\n },\n deleteBackward() {\n ctx.deleteBackward()\n },\n deleteForward() {\n ctx.deleteForward()\n },\n cursorLeft() {\n ctx.moveCursor(\"left\")\n forceRender()\n },\n cursorRight() {\n ctx.moveCursor(\"right\")\n forceRender()\n },\n cursorUp(): boolean {\n const moved = ctx.moveCursor(\"up\")\n if (moved) forceRender()\n return moved\n },\n cursorDown(): boolean {\n const moved = ctx.moveCursor(\"down\")\n if (moved) forceRender()\n return moved\n },\n cursorStart() {\n ctx.setCursorOffset(0)\n forceRender()\n },\n cursorEnd() {\n ctx.setCursorOffset(ctx.text.length)\n forceRender()\n },\n deleteWord() {\n ctx.deleteWord()\n },\n deleteToStart() {\n ctx.deleteToStart()\n },\n deleteToEnd() {\n ctx.deleteToEnd()\n },\n confirm() {\n cancelledRef.current = true\n onConfirmRef.current?.(ctx.text)\n },\n cancel() {\n cancelledRef.current = true\n onCancelRef.current?.()\n },\n save() {\n const fn = onSaveRef.current ?? onConfirmRef.current\n fn?.(ctx.text)\n initialValueRef.current = ctx.text\n },\n getCursorOffset() {\n return ctx.selectionStart\n },\n getContent() {\n return ctx.text\n },\n insertBreak(): boolean {\n // Signal that this editor supports outliner-style Enter (split/new sibling).\n // The actual node creation is handled by TEXT_LINEBREAK_* actions in board-actions.ts.\n return !!onSplitRef.current\n },\n replaceContent(content: string, cursor: number) {\n // Replace entire text and set cursor position\n ctx.updateText(0, ctx.text.length, content)\n ctx.setCursorOffset(cursor)\n initialValueRef.current = content\n forceRender()\n },\n setCursorOffset(offset: number) {\n ctx.setCursorOffset(Math.min(Math.max(0, offset), ctx.text.length))\n forceRender()\n },\n }),\n [ctx, forceRender],\n )\n\n // Register as active edit context + target on mount, clean up on unmount.\n // useLayoutEffect ensures registration happens before the next input event.\n useLayoutEffect(() => {\n activeEditContextRef.current = ctx\n activeEditTargetRef.current = target\n return () => {\n if (activeEditContextRef.current === ctx) {\n activeEditContextRef.current = null\n }\n if (activeEditTargetRef.current === target) {\n activeEditTargetRef.current = null\n }\n // Auto-save on unmount if value was modified and not explicitly cancelled\n if (!cancelledRef.current) {\n const currentValue = ctx.text\n if (currentValue !== initialValueRef.current) {\n onConfirmRef.current?.(currentValue)\n }\n }\n }\n }, [ctx, target])\n\n // Derive display values from TermEditContext\n const text = ctx.text\n const cursorPos = ctx.selectionStart\n const beforeCursor = text.slice(0, cursorPos)\n const afterCursor = text.slice(cursorPos)\n\n return {\n value: text,\n cursor: cursorPos,\n beforeCursor,\n afterCursor,\n clear: useCallback(() => {\n ctx.updateText(0, ctx.text.length, \"\")\n ctx.setCursorOffset(0)\n onChangeRef.current?.(\"\")\n forceRender()\n }, [ctx, forceRender]),\n setValue: useCallback(\n (value: string) => {\n ctx.updateText(0, ctx.text.length, value)\n ctx.setCursorOffset(value.length)\n onChangeRef.current?.(value)\n forceRender()\n },\n [ctx, forceRender],\n ),\n editContext: ctx,\n target,\n setCursorOffset: useCallback(\n (offset: number) => {\n ctx.setCursorOffset(Math.min(Math.max(0, offset), ctx.text.length))\n forceRender()\n },\n [ctx, forceRender],\n ),\n }\n}\n","/**\n * InputBoundary - Input Isolation for Embedded Components\n *\n * Creates an isolated input scope where child components' useInput and\n * useInputLayer handlers only fire when the boundary is active (focused).\n * When active, the boundary consumes all input from the parent's layer stack\n * and forwards it into the isolated scope.\n *\n * This solves the problem where embedding interactive components (e.g., in\n * a storybook viewer) causes both parent and child input handlers to fire\n * simultaneously.\n *\n * ## Architecture (TEA Phase 2)\n *\n * The boundary hosts its own child `BaseApp` built from the same apply-chain\n * plugins as the root runtime (`withTerminalChain`, `withPasteChain`,\n * `withInputChain`, `withFocusChain`). Child hooks (`useInput`,\n * `useModifierKeys`, `useTerminalFocused`, `usePasteEvents`,\n * `usePasteCallback`) subscribe via `ChainAppContext` just like root-level\n * hooks do — no `rt.on` fallback path is required.\n *\n * Forwarded input is dispatched as `input:key` ops on the child app. The\n * boundary exposes its own raw-key observer and focus events store so\n * `useModifierKeys`/`useTerminalFocused` stay functional inside the\n * isolated scope.\n *\n * @example\n * ```tsx\n * function StoryViewer() {\n * const [focused, setFocused] = useState(false)\n *\n * // Parent navigation (j/k) only works when boundary is NOT focused\n * useInputLayer('viewer', (input, key) => {\n * if (input === 'j') { nextStory(); return true }\n * if (input === 'k') { prevStory(); return true }\n * return false\n * })\n *\n * return (\n * <Box>\n * <InputBoundary active={focused} onEscape={() => setFocused(false)}>\n * <EmbeddedInteractiveComponent />\n * </InputBoundary>\n * </Box>\n * )\n * }\n * ```\n */\n\nimport type React from \"react\"\nimport { useCallback, useId, useMemo, useRef } from \"react\"\nimport { ChainAppContext, RuntimeContext, type RuntimeContextValue } from \"../context\"\nimport type { Key } from \"../hooks/useInput\"\nimport { keyToName } from \"@silvery/ag/keys\"\nimport { createChildApp, toChainAppContextValue, type ChildApp } from \"../chain-bridge\"\nimport { InputLayerProvider, useInputLayer } from \"./InputLayerContext\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface InputBoundaryProps {\n /** Whether the boundary is active (focused). When true, input flows to children. */\n active: boolean\n /** Called when the escape key is pressed while the boundary is active. */\n onEscape?: () => void\n /** Key to exit the boundary (default: Escape). Set to null to disable. */\n exitKey?: string | null\n /** Children to render inside the isolated input scope. */\n children: React.ReactNode\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Creates an isolated input scope for embedded interactive components.\n *\n * When `active` is true:\n * - Registers a consuming layer in the parent's input layer stack\n * - Dispatches every forwarded key as an `input:key` op on the child BaseApp\n * - Parent useInputLayer handlers do NOT fire (consumed by boundary)\n * - Parent useInput handlers still fire (useInput bypasses the layer stack)\n *\n * When `active` is false:\n * - Children's useInput/useInputLayer handlers do NOT fire\n * - Parent input flows normally\n *\n * The `onEscape` callback (or custom `exitKey`) is intercepted BEFORE\n * dispatch, allowing the parent to deactivate the boundary.\n */\nexport function InputBoundary({\n active,\n onEscape,\n exitKey = \"Escape\",\n children,\n}: InputBoundaryProps): React.JSX.Element {\n // Child BaseApp (stable across renders). Built lazily on first render so\n // we don't pay the cost for inactive boundaries until they matter.\n const childAppRef = useRef<ChildApp | null>(null)\n if (!childAppRef.current) childAppRef.current = createChildApp()\n const childApp = childAppRef.current\n\n // Register a consuming layer in the parent when active.\n // This layer intercepts ALL input and dispatches to the child chain.\n const activeRef = useRef(active)\n activeRef.current = active\n\n const onEscapeRef = useRef(onEscape)\n onEscapeRef.current = onEscape\n\n const exitKeyRef = useRef(exitKey)\n exitKeyRef.current = exitKey\n\n const handler = useCallback(\n (input: string, key: Key): boolean => {\n if (!activeRef.current) return false\n\n // Check exit key before dispatch\n const currentExitKey = exitKeyRef.current\n if (currentExitKey !== null) {\n const name = keyToName(key)\n if (name === currentExitKey || (!name && input === currentExitKey)) {\n onEscapeRef.current?.()\n return true\n }\n }\n\n // Fire raw-key observers first (useModifierKeys needs unfiltered keys).\n childApp.rawKeys.notify(input, key)\n\n // Dispatch into the child apply chain. The chain produces effects\n // (exit/render/...) which we drain and discard — the boundary owns\n // neither exit nor render (those belong to the root runner). Handler\n // return value of \"exit\" is surfaced by withInputChain as an exit\n // effect; we ignore it here since embedded scopes don't terminate.\n childApp.dispatch({ type: \"input:key\", input, key })\n childApp.drainEffects()\n return true\n },\n [childApp],\n )\n\n const layerId = useId()\n useInputLayer(`input-boundary-${layerId}`, handler)\n\n // ChainAppContext value — the canonical subscription surface for child\n // hooks. Identical shape to the root `chainAppContextValue` in create-app.\n const chainAppContextValue = useMemo(() => toChainAppContextValue(childApp), [childApp])\n\n // RuntimeContext retains a minimal handle — only `exit` (no-op inside\n // the boundary). Custom app events use the child chain's events store\n // (`chain.events.emit / on` via ChainAppContext).\n const runtimeContextValue = useMemo<RuntimeContextValue>(\n () => ({\n exit: () => {},\n }),\n [],\n )\n\n return (\n <RuntimeContext.Provider value={runtimeContextValue}>\n <ChainAppContext.Provider value={chainAppContextValue}>\n <InputLayerProvider>{children}</InputLayerProvider>\n </ChainAppContext.Provider>\n </RuntimeContext.Provider>\n )\n}\n","/**\n * silvery/tea — Zustand middleware for TEA (The Elm Architecture) effects.\n *\n * A ~30-line middleware that extends Zustand reducers to optionally return\n * [state, effects]. Gradual adoption: return plain state (Level 3) or\n * [state, effects] (Level 4) on a per-case basis.\n *\n * @example\n * ```ts\n * import { createStore } from \"zustand\"\n * import { tea, collect } from \"@silvery/create/tea\"\n *\n * // Define effects as plain data\n * const log = (msg: string) => ({ type: \"log\" as const, msg })\n * const httpPost = (url: string, body: unknown) => ({ type: \"http\" as const, url, body })\n *\n * type MyEffect = ReturnType<typeof log> | ReturnType<typeof httpPost>\n *\n * interface State {\n * count: number\n * }\n *\n * type Op = { type: \"increment\" } | { type: \"save\" }\n *\n * // Reducer: return state (no effects) or [state, effects]\n * function reducer(state: State, op: Op): TeaResult<State, MyEffect> {\n * switch (op.type) {\n * case \"increment\":\n * return { ...state, count: state.count + 1 } // Level 3: plain state\n * case \"save\":\n * return [{ ...state }, [httpPost(\"/api\", state), log(\"saved\")]] // Level 4: [state, effects]\n * }\n * }\n *\n * // Effect runners (swappable: production, test, replay)\n * const runners: EffectRunners<MyEffect, Op> = {\n * log: (e) => console.log(e.msg),\n * http: async (e, dispatch) => {\n * const res = await fetch(e.url, { method: \"POST\", body: JSON.stringify(e.body) })\n * dispatch({ type: \"loaded\", data: await res.json() })\n * },\n * }\n *\n * // Wire up\n * const store = createStore(tea({ count: 0 }, reducer, { runners }))\n * store.getState().dispatch({ type: \"increment\" })\n *\n * // Test: collect() normalizes output for assertions\n * const [state, effects] = collect(reducer(initial, { type: \"save\" }))\n * expect(effects).toContainEqual(httpPost(\"/api\", initial))\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { StateCreator } from \"../signal-store.ts\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** An effect is a plain object with a `type` discriminant. */\nexport type EffectLike = { type: string }\n\n/** Reducer result: plain state (no effects) or [state, effects]. */\nexport type TeaResult<S, E extends EffectLike = EffectLike> = S | readonly [S, E[]]\n\n/** A reducer that takes state + operation and returns TeaResult. */\nexport type TeaReducer<S, Op, E extends EffectLike = EffectLike> = (\n state: S,\n op: Op,\n) => TeaResult<S, E>\n\n/**\n * Effect runners keyed by effect `type`.\n *\n * Each runner receives the effect and a dispatch function for round-trip\n * communication (Elm's Cmd Msg pattern).\n */\nexport type EffectRunners<E extends EffectLike, Op = unknown> = {\n [K in E[\"type\"]]?: (\n effect: Extract<E, { type: K }>,\n dispatch: (op: Op) => void,\n ) => void | Promise<void>\n}\n\n/** Options for the tea() middleware. */\nexport interface TeaOptions<E extends EffectLike, Op> {\n /** Effect runners. Keyed by effect type. Unmatched effects are silently dropped. */\n runners?: EffectRunners<E, Op>\n}\n\n/**\n * The store shape produced by the tea() middleware.\n *\n * `dispatch(op)` runs the reducer, updates state, and executes effects.\n * All domain state fields from S are spread at the top level alongside dispatch.\n */\nexport type TeaSlice<S, Op> = S & {\n /** Dispatch an operation through the reducer. */\n dispatch: (op: Op) => void\n}\n\n// =============================================================================\n// Core: tea() middleware\n// =============================================================================\n\n/**\n * Zustand state creator that adds TEA-style dispatch + effects.\n *\n * The reducer can return plain state (Level 3) or `[state, effects]` (Level 4).\n * Array.isArray detects which — safe because Zustand state is always an object.\n *\n * Effects are executed after state update. Each effect is routed to a runner\n * by its `type` field. Runners receive a `dispatch` callback for round-trip\n * communication (Elm's Cmd Msg pattern).\n */\nexport function tea<S extends object, Op, E extends EffectLike = EffectLike>(\n initialState: S,\n reducer: TeaReducer<S, Op, E>,\n options?: TeaOptions<E, Op>,\n): StateCreator<TeaSlice<S, Op>> {\n return (set, get) => {\n const dispatch = (op: Op): void => {\n // Extract domain state (everything except dispatch)\n const { dispatch: _, ...currentState } = get()\n const result = reducer(currentState as unknown as S, op)\n\n // Detect: plain state vs [state, effects]\n const [newState, effects] = Array.isArray(result)\n ? (result as [S, E[]])\n : [result as S, [] as E[]]\n\n // Update Zustand store (spread domain state, keep dispatch)\n set(newState as Partial<TeaSlice<S, Op>>)\n\n // Execute effects\n if (effects.length > 0 && options?.runners) {\n for (const effect of effects) {\n const runner = options.runners[effect.type as E[\"type\"]]\n if (runner) {\n ;(runner as (e: E, d: (op: Op) => void) => void)(effect, dispatch)\n }\n }\n }\n }\n\n return {\n ...initialState,\n dispatch,\n } as TeaSlice<S, Op>\n }\n}\n\n// =============================================================================\n// Test helper: collect()\n// =============================================================================\n\n/**\n * Normalize a reducer result to `[state, effects]` tuple.\n *\n * Use in tests to uniformly assert on both state and effects regardless of\n * whether the reducer returned plain state or a tuple.\n *\n * @example\n * ```ts\n * const [state, effects] = collect(reducer(initial, { type: \"save\" }))\n * expect(state.saving).toBe(true)\n * expect(effects).toContainEqual(httpPost(\"/api\", initial))\n *\n * // Also works for Level 3 (no effects):\n * const [state2, effects2] = collect(reducer(initial, { type: \"increment\" }))\n * expect(state2.count).toBe(1)\n * expect(effects2).toEqual([])\n * ```\n */\nexport function collect<S, E extends EffectLike = EffectLike>(result: TeaResult<S, E>): [S, E[]] {\n if (Array.isArray(result)) {\n return result as [S, E[]]\n }\n return [result as S, []]\n}\n","/**\n * Built-in TEA effects for timers and dispatch.\n *\n * Effect constructors create plain data objects. Effect runners execute them.\n * The update function returns effects as data — the runtime executes them.\n *\n * @example\n * ```ts\n * function update(state, msg) {\n * switch (msg.type) {\n * case \"start\":\n * return [{ ...state, phase: \"thinking\" }, [fx.delay(1200, { type: \"done\" })]]\n * case \"tick\":\n * return { ...state, count: state.count + 1 } // no effects\n * case \"done\":\n * return [{ ...state, phase: \"idle\" }, [fx.cancel(\"ticker\")]]\n * }\n * }\n * ```\n */\n\nimport type { EffectLike, EffectRunners } from \"./tea\"\n\n// =============================================================================\n// Effect Types\n// =============================================================================\n\n/** Fire a message after a delay. */\nexport interface DelayEffect<Msg = unknown> extends EffectLike {\n type: \"delay\"\n ms: number\n msg: Msg\n id?: string\n}\n\n/** Fire a message repeatedly on an interval. */\nexport interface IntervalEffect<Msg = unknown> extends EffectLike {\n type: \"interval\"\n ms: number\n msg: Msg\n id: string\n}\n\n/** Cancel a named timer (delay or interval). */\nexport interface CancelEffect extends EffectLike {\n type: \"cancel\"\n id: string\n}\n\n/** All built-in effect types. */\nexport type TimerEffect<Msg = unknown> = DelayEffect<Msg> | IntervalEffect<Msg> | CancelEffect\n\n// =============================================================================\n// Effect Constructors (the fx namespace)\n// =============================================================================\n\n/** Fire `msg` after `ms` milliseconds. Optionally named for cancellation. */\nfunction delay<Msg>(ms: number, msg: Msg, id?: string): DelayEffect<Msg> {\n return { type: \"delay\", ms, msg, id }\n}\n\n/** Fire `msg` every `ms` milliseconds. Must be named (for cancellation). */\nfunction interval<Msg>(ms: number, msg: Msg, id: string): IntervalEffect<Msg> {\n return { type: \"interval\", ms, msg, id }\n}\n\n/** Cancel a named delay or interval. */\nfunction cancel(id: string): CancelEffect {\n return { type: \"cancel\", id }\n}\n\n/**\n * Built-in effect constructors.\n *\n * ```ts\n * return [newState, [fx.delay(1000, { type: \"tick\" }), fx.cancel(\"old\")]]\n * ```\n */\nexport const fx = { delay, interval, cancel } as const\n\n// =============================================================================\n// Effect Runners\n// =============================================================================\n\n/**\n * Create timer effect runners that manage named timers.\n *\n * Returns runners + a cleanup function. Call cleanup on unmount to\n * cancel all active timers.\n *\n * @example\n * ```ts\n * const { runners, cleanup } = createTimerRunners<MyMsg>()\n * const [state, send] = useTea(init, update, runners)\n * useEffect(() => cleanup, [])\n * ```\n */\nexport function createTimerRunners<Msg>(): {\n runners: EffectRunners<TimerEffect<Msg>, Msg>\n cleanup: () => void\n} {\n const timers = new Map<string, ReturnType<typeof setTimeout> | ReturnType<typeof setInterval>>()\n let nextAnon = 0\n\n function clearTimer(id: string): void {\n const existing = timers.get(id)\n if (existing !== undefined) {\n clearTimeout(existing as ReturnType<typeof setTimeout>)\n clearInterval(existing as ReturnType<typeof setInterval>)\n timers.delete(id)\n }\n }\n\n const runners: EffectRunners<TimerEffect<Msg>, Msg> = {\n delay(effect, dispatch) {\n const id = effect.id ?? `__anon_${nextAnon++}`\n clearTimer(id)\n const timer = setTimeout(() => {\n timers.delete(id)\n dispatch(effect.msg as Msg)\n }, effect.ms)\n timers.set(id, timer)\n },\n\n interval(effect, dispatch) {\n clearTimer(effect.id)\n const timer = setInterval(() => {\n dispatch(effect.msg as Msg)\n }, effect.ms)\n timers.set(effect.id, timer)\n },\n\n cancel(effect) {\n clearTimer(effect.id)\n },\n }\n\n function cleanup(): void {\n for (const [id] of timers) {\n clearTimer(id)\n }\n }\n\n return { runners, cleanup }\n}\n","/**\n * useTea — React hook for TEA (The Elm Architecture) state machines.\n *\n * Like useReducer, but the reducer can return [state, effects].\n * Effects are plain data objects executed by runners. Built-in timer\n * runners handle delay/interval/cancel. All timers auto-cleanup on unmount.\n *\n * @example\n * ```tsx\n * import { useTea } from \"silvery\"\n * import { fx } from \"@silvery/create/effects\"\n *\n * type State = { count: number; running: boolean }\n * type Msg = { type: \"start\" } | { type: \"tick\" } | { type: \"stop\" }\n *\n * function update(state: State, msg: Msg) {\n * switch (msg.type) {\n * case \"start\":\n * return [{ ...state, running: true }, [fx.interval(100, { type: \"tick\" }, \"counter\")]]\n * case \"tick\":\n * return { ...state, count: state.count + 1 }\n * case \"stop\":\n * return [{ ...state, running: false }, [fx.cancel(\"counter\")]]\n * }\n * }\n *\n * function Counter() {\n * const [state, send] = useTea({ count: 0, running: false }, update)\n * return <Text>Count: {state.count}</Text>\n * }\n * ```\n */\n\nimport { useCallback, useEffect, useRef, useReducer } from \"react\"\nimport type { EffectLike, EffectRunners, TeaResult } from \"@silvery/create/tea\"\nimport { collect } from \"@silvery/create/tea\"\nimport { createTimerRunners, type TimerEffect } from \"@silvery/create/effects\"\n\n// =============================================================================\n// Hook\n// =============================================================================\n\n/**\n * TEA state machine hook with automatic timer management.\n *\n * The update function can return plain state (no effects) or `[state, effects]`.\n * Timer effects (delay, interval, cancel) are handled automatically.\n * Additional effect runners can be provided for custom effects.\n *\n * All timers are cleaned up automatically on unmount.\n *\n * @param initialState - Initial state value\n * @param update - Pure update function: `(state, msg) => state | [state, effects]`\n * @param customRunners - Optional additional effect runners for non-timer effects\n * @returns `[state, send]` tuple — send dispatches a message through the update function\n */\nexport function useTea<S, Msg, E extends EffectLike = TimerEffect<Msg>>(\n initialState: S | (() => S),\n update: (state: S, msg: Msg) => TeaResult<S, E>,\n customRunners?: EffectRunners<E, Msg>,\n): [S, (msg: Msg) => void] {\n // Create timer runners once (stable across renders)\n const timerRef = useRef<ReturnType<typeof createTimerRunners<Msg>> | null>(null)\n if (timerRef.current === null) {\n timerRef.current = createTimerRunners<Msg>()\n }\n const { runners: timerRunners, cleanup } = timerRef.current\n\n // Keep custom runners ref-stable\n const customRunnersRef = useRef(customRunners)\n customRunnersRef.current = customRunners\n\n // Pending effects queue — effects from the reducer can't be executed\n // during render, so we queue them and execute in a useEffect.\n const pendingEffectsRef = useRef<E[]>([])\n\n // Use React's useReducer for state — it integrates with React's scheduler\n const [state, reactDispatch] = useReducer(\n (prevState: S, msg: Msg): S => {\n const result = update(prevState, msg)\n const [newState, effects] = collect(result)\n if (effects.length > 0) {\n pendingEffectsRef.current.push(...effects)\n }\n return newState\n },\n undefined,\n () => (typeof initialState === \"function\" ? (initialState as () => S)() : initialState),\n )\n\n // Execute effects outside of render (React rules)\n const sendRef = useRef<(msg: Msg) => void>(() => {})\n\n const executeEffects = useCallback(() => {\n if (pendingEffectsRef.current.length === 0) return\n const effects = pendingEffectsRef.current.splice(0)\n for (const effect of effects) {\n // Try timer runners first\n const timerRunner = timerRunners[effect.type as keyof typeof timerRunners]\n if (timerRunner) {\n ;(timerRunner as (e: any, d: (msg: Msg) => void) => void)(effect, sendRef.current)\n continue\n }\n // Try custom runners\n const customRunner = customRunnersRef.current?.[effect.type as E[\"type\"]]\n if (customRunner) {\n ;(customRunner as (e: any, d: (msg: Msg) => void) => void)(effect, sendRef.current)\n }\n }\n }, [timerRunners])\n\n // The send function: dispatch to React, then execute effects\n const send = useCallback(\n (msg: Msg) => {\n reactDispatch(msg)\n // Effects are queued by the reducer — execute them after React processes the update.\n // We use queueMicrotask to ensure effects run after the reducer but before the next paint.\n queueMicrotask(executeEffects)\n },\n [reactDispatch, executeEffects],\n )\n sendRef.current = send\n\n // Execute any effects from the initial render\n useEffect(() => {\n executeEffects()\n }, [executeEffects])\n\n // Cleanup all timers on unmount\n useEffect(() => cleanup, [cleanup])\n\n return [state, send]\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 =\n | { type: \"none\" }\n | { type: \"batch\"; effects: Effect[] }\n | { 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 {\n FocusManager,\n FocusManagerOptions,\n FocusChangeCallback,\n FocusSnapshot,\n} from \"../focus-manager.js\"\n\n// =============================================================================\n// Focus Events (pure, no React)\n// =============================================================================\n\nexport {\n createKeyEvent,\n createFocusEvent,\n dispatchKeyEvent,\n dispatchFocusEvent,\n} 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<\n Model,\n Msg\n> {\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>(\n ...sources: AsyncIterable<T>[]\n): 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(\n idx: number,\n ): 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>(\n source: AsyncIterable<T>,\n fn: (value: T) => U,\n): 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>(\n source: AsyncIterable<T>,\n signal: AbortSignal,\n): 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>(\n source: AsyncIterable<T>,\n count: number,\n): 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>(\n items: T[],\n delayMs: number,\n): 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>(\n source: AsyncIterable<T>,\n ms: number,\n): 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>(\n source: AsyncIterable<T>,\n ms: number,\n): 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>(\n source: AsyncIterable<T>,\n size: number,\n): 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>(\n source: AsyncIterable<T>,\n size: number,\n): 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>(\n ...sources: AsyncIterable<T>[]\n): 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 * 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 { createAg } from \"../ag\"\nimport {\n createContainer,\n createFiberRoot,\n getContainerRoot,\n reconciler,\n} 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 ag = createAg(root)\n ag.layout({ cols: width, rows: height }, { skipLayoutNotifications })\n const { buffer: termBuffer } = ag.render()\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(\n termBuffer: TerminalBuffer,\n nodes: AgNode,\n overlay?: string,\n): 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 overlay,\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 (\n error instanceof Error &&\n (error.message === \"Effect aborted\" || error.name === \"AbortError\")\n ) {\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 // Append Kitty graphics overlay (scrim placements for emoji in the\n // backdrop-fade region). The overlay is a self-contained save-cursor /\n // CUP / place / restore-cursor block — appending it after the main diff\n // keeps the output phase's cursor tracking intact.\n if (buffer.overlay && buffer.overlay.length > 0) {\n patch += buffer.overlay\n }\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> = ((\n setState: StoreApi<T>[\"setState\"],\n getState: StoreApi<T>[\"getState\"],\n store: StoreApi<T>,\n) => 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\"\n ? (partial as (state: T) => T | Partial<T>)(prev)\n : (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 {\n enableKittyKeyboard,\n disableKittyKeyboard,\n enableMouse,\n disableMouse,\n resetCursorStyle,\n} 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(\n state: TerminalState,\n stdout: NodeJS.WriteStream,\n stdin: NodeJS.ReadStream,\n): 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?.(\n `keypress over budget: ${key} took ${durationMs.toFixed(1)}ms (budget: ${budgetMs}ms)`,\n )\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 * renderer.ts — `doRender` + render-guardrail helpers extracted from\n * `create-app.tsx` for clarity and test-ability.\n *\n * These pieces used to live as closures inside `createApp()` and captured\n * a large amount of the surrounding state. They are now factored into:\n *\n * - `createRenderer(opts)` — returns the `doRender` function plus the\n * render-adjacent overlay helpers. Each closure maintains its own\n * `_ag` / `_lastTermBuffer` / `_renderCount` state.\n *\n * The factory approach (rather than a class) keeps fit with silvery's\n * no-classes house style and makes it trivial to unit-test a renderer\n * against a mock runtime if we ever want to.\n *\n * See `create-app.tsx` for the full integration; this file is not\n * intended to be used outside that module (no public barrel export).\n */\n\nimport { tmpdir } from \"node:os\"\nimport { writeFileSync } from \"node:fs\"\nimport { reconciler, getContainerRoot } from \"@silvery/ag-react/reconciler\"\nimport { createAg, type Ag } from \"../ag\"\nimport { runWithMeasurer } from \"../unicode\"\nimport { createBuffer } from \"./create-buffer\"\nimport { isAnyDirty } from \"@silvery/ag/epoch\"\nimport { IncrementalRenderMismatchError } from \"../scheduler\"\nimport { createSearchState, renderSearchBar, type SearchMatch } from \"../search-overlay\"\nimport { renderSelectionOverlay } from \"../selection-renderer\"\nimport type { Buffer, Dims, RenderTarget } from \"./types\"\nimport type { PipelineConfig } from \"../pipeline\"\nimport type { createVirtualScrollback } from \"../virtual-scrollback\"\nimport type { TerminalBuffer } from \"../buffer\"\nimport type { createFiberRoot, createContainer } from \"@silvery/ag-react/reconciler\"\nimport type { TerminalSelectionState } from \"@silvery/headless/selection\"\n\ntype Scrollback = ReturnType<typeof createVirtualScrollback>\ntype Container = ReturnType<typeof createContainer>\ntype FiberRoot = ReturnType<typeof createFiberRoot>\ntype SearchState = ReturnType<typeof createSearchState>\n\n// ---------------------------------------------------------------------------\n// Renderer factory\n// ---------------------------------------------------------------------------\n\n/**\n * Immutable options + runtime-state accessors the renderer needs. Many\n * fields are functions rather than plain values so the renderer observes\n * live-updating runtime state (e.g. `getSearchState()` sees the current\n * search after the caller mutates it).\n */\nexport interface RendererOptions {\n /** React element (pre-wrapped with providers) to reconcile into the container. */\n wrappedElement: import(\"react\").ReactElement\n fiberRoot: FiberRoot\n container: Container\n runtime: {\n getDims(): Dims\n invalidate(): void\n }\n /** True when running against an alternate screen (fullscreen mode). */\n alternateScreen: boolean\n /** Pipeline config (measurer, etc.); may be absent for tests. */\n pipelineConfig: PipelineConfig | undefined\n /** When set, skip incremental rendering and always render fresh. */\n noIncremental: boolean\n /** SILVERY_STRICT guardrail toggle. */\n strictMode: boolean\n /** Resolved cell-debug coords (SILVERY_CELL_DEBUG=x,y) if provided. */\n cellDebug: { x: number; y: number } | null\n /** True iff any diagnostic flag is on (STRICT_MODE || cellDebug). */\n instrumented: boolean\n /** SILVERY_TRACE_ANSI — writes step-by-step ANSI traces to /tmp. */\n ansiTrace: boolean\n /** Shared perfLog flag — writes render timings to /tmp/silvery-perf.log. */\n perfLog: boolean\n}\n\n/** Return type for the renderer factory — closures over the internal _ag / _renderCount state. */\nexport interface Renderer {\n doRender(): Buffer\n /** Current render count (for diagnostics). */\n renderCount(): number\n /** Reset the render counter (used at the start of an event batch). */\n resetCount(): void\n /** True when the next render will skip incremental (noIncremental env on). */\n isIncrementalOff(): boolean\n /** Forcibly reset the internal Ag instance — called on alt-screen switches / resume. */\n resetAg(): void\n}\n\n/**\n * Build a `doRender` closure wired to the supplied runtime. The closure\n * manages its own long-lived `Ag` instance and tracks the last\n * TerminalBuffer for dimension-change detection.\n */\nexport function createRenderer(opts: RendererOptions): Renderer {\n let _renderCount = 0\n let _ag: Ag | null = null\n let _lastTermBuffer: TerminalBuffer | null = null\n let lastCurrentBuffer: Buffer | null = null\n\n function doRender(): Buffer {\n _renderCount++\n if (opts.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=${!opts.noIncremental}) ---\\n`,\n )\n }\n const renderStart = performance.now()\n\n // Phase A: React reconciliation\n reconciler.updateContainerSync(opts.wrappedElement, opts.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 { __silvery_bench_phases?: { reconcile: number } })\n .__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(opts.container)\n const dims = opts.runtime.getDims()\n\n const isInline = !opts.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: opts.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 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 opts.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 (opts.instrumented) {\n const g = globalThis as Record<string, unknown>\n g.__silvery_content_all = undefined\n g.__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 g.__silvery_cell_debug =\n opts.cellDebug !== null\n ? { x: opts.cellDebug.x, y: opts.cellDebug.y, log: [] as string[] }\n : 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 =\n rootNode.layoutNode?.isDirty() || isAnyDirty(rootNode.dirtyBits, rootNode.dirtyEpoch)\n const dimsChanged =\n _lastTermBuffer != null &&\n (dims.cols !== _lastTermBuffer.width || dims.rows !== _lastTermBuffer.height)\n if (!rootHasDirty && !dimsChanged && _lastTermBuffer && lastCurrentBuffer) {\n return lastCurrentBuffer\n }\n\n // When SILVERY_NO_INCREMENTAL is set, force fresh render every frame\n if (opts.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 agResult = _ag.render()\n const { buffer: termBuffer, prevBuffer: agPrevBuffer } = agResult\n const overlay = agResult.overlay\n _lastTermBuffer = termBuffer\n const wasIncremental = !opts.noIncremental && agPrevBuffer !== null\n const pipelineMs = performance.now() - pipelineStart\n\n // Expose timing for diagnostics.\n // Output timing is 0 here — the runtime handles the output phase separately.\n ;(\n globalThis as {\n __silvery_last_pipeline?: {\n layout: number\n output: number\n total: number\n incremental: boolean\n }\n }\n ).__silvery_last_pipeline = {\n layout: pipelineMs,\n output: 0,\n total: pipelineMs,\n incremental: wasIncremental,\n }\n ;(globalThis as { __silvery_render_count?: number }).__silvery_render_count =\n ((globalThis as { __silvery_render_count?: number }).__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 = (\n globalThis as { __silvery_bench_phases?: { total: number; pipelineCalls: number } }\n ).__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 if (opts.strictMode && wasIncremental) {\n const doFreshRender = () => {\n const freshAg = createAg(rootNode, { measurer: opts.pipelineConfig?.measurer })\n freshAg.layout(\n { cols: dims.cols, rows: dims.rows },\n { skipLayoutNotifications: true, skipScrollStateUpdates: true },\n )\n return freshAg.render()\n }\n const measurer = opts.pipelineConfig?.measurer\n const { buffer: freshBuffer } = measurer\n ? runWithMeasurer(measurer, doFreshRender)\n : doFreshRender()\n const { cellEquals, bufferToText } =\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"../buffer\") as typeof import(\"../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 = (\n globalThis as { __silvery_cell_debug?: { x: number; y: number; log: string[] } }\n ).__silvery_cell_debug\n if (\n savedCellDbg &&\n savedCellDbg.x === x &&\n savedCellDbg.y === y &&\n savedCellDbg.log.length > 0\n ) {\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 ;(\n globalThis as {\n __silvery_write_trap?: { x: number; y: number; log: string[] } | null\n }\n ).__silvery_write_trap = trap\n try {\n if (measurer) {\n runWithMeasurer(measurer, doFreshRender)\n } else {\n doFreshRender()\n }\n } catch {\n // ignore\n }\n ;(\n globalThis as {\n __silvery_write_trap?: { x: number; y: number; log: string[] } | null\n }\n ).__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 const contentAll = (globalThis as { __silvery_content_all?: unknown[] })\n .__silvery_content_all\n const statsStr = contentAll\n ? `\\n--- render phase stats (${contentAll.length} calls) ---\\n` +\n contentAll\n .map((s: unknown, i: number) => {\n const x = s as Record<string, unknown>\n return (\n ` #${i}: visited=${x.nodesVisited} rendered=${x.nodesRendered} skipped=${x.nodesSkipped} ` +\n `clearOps=${x.clearOps} cascade=\"${x.cascadeNodes}\" ` +\n `flags={C=${x.flagContentDirty} P=${x.flagStylePropsDirty} L=${x.flagLayoutChanged} ` +\n `S=${x.flagSubtreeDirty} Ch=${x.flagChildrenDirty} CP=${x.flagChildPositionChanged} AL=${x.flagAncestorLayoutChanged} noPrev=${x.noPrevBuffer}} ` +\n `scroll={containers=${x.scrollContainerCount} cleared=${x.scrollViewportCleared} reason=\"${x.scrollClearReason}\"} ` +\n `normalRepaint=\"${x.normalRepaintReason}\" ` +\n `prevBuf={null=${x._prevBufferNull} dimMismatch=${x._prevBufferDimMismatch} hasPrev=${x._hasPrevBuffer} ` +\n `layout=${x._layoutW}x${x._layoutH} prev=${x._prevW}x${x._prevH}}`\n )\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 (() => {\n const traces = (globalThis as { __silvery_node_trace?: unknown[][] })\n .__silvery_node_trace\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 Record<string, unknown>[]) {\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 // Dump full diagnostics to temp file — alt screen hides stderr\n let dumpPath: string | undefined\n try {\n dumpPath = `${tmpdir()}/silvery-strict-failure-${Date.now()}.txt`\n writeFileSync(dumpPath, msg)\n } catch {}\n throw new IncrementalRenderMismatchError(\n dumpPath ? `${msg.split(\"\\n\")[0]}\\n dump: ${dumpPath}` : msg,\n )\n }\n }\n }\n if (opts.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, overlay)\n lastCurrentBuffer = buf\n if (opts.perfLog) {\n const renderDuration = performance.now() - renderStart\n const phases = (\n globalThis as {\n __silvery_last_pipeline?: {\n measure?: number\n layout: number\n content?: number\n output: number\n total: number\n }\n }\n ).__silvery_last_pipeline\n const detail = (\n globalThis as {\n __silvery_content_detail?: Record<string, number | string | undefined>\n }\n ).__silvery_content_detail\n const phaseStr = phases\n ? ` [measure=${(phases.measure ?? 0).toFixed(1)} layout=${phases.layout.toFixed(1)} content=${(phases.content ?? 0).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 return {\n doRender,\n renderCount: () => _renderCount,\n resetCount: () => {\n _renderCount = 0\n },\n isIncrementalOff: () => opts.noIncremental,\n resetAg: () => {\n _ag = null\n _lastTermBuffer = null\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Render-adjacent overlay helpers\n//\n// These sit on top of the current `currentBuffer` / runtime state and are\n// called after `doRender()` + `runtime.render()` to overlay selection,\n// scrollback, and search bits. Each takes only the bits of state it needs\n// via a small options object.\n// ---------------------------------------------------------------------------\n\nexport interface SelectionOverlayOptions {\n selectionEnabled: boolean\n selectionState: TerminalSelectionState\n currentBuffer: Buffer | null\n alternateScreen: boolean\n target: RenderTarget\n}\n\nexport function writeSelectionOverlay(opts: SelectionOverlayOptions): void {\n const { selectionEnabled, selectionState, currentBuffer, alternateScreen, target } = opts\n if (!selectionEnabled || !selectionState.range || !currentBuffer) return\n const mode = alternateScreen ? \"fullscreen\" : \"inline\"\n const overlay = renderSelectionOverlay(\n selectionState.range,\n currentBuffer._buffer,\n mode,\n selectionState.scope,\n )\n if (overlay) target.write(overlay)\n}\n\nexport interface PushToScrollbackOptions {\n scrollback: Scrollback | null\n currentBuffer: Buffer | null\n}\n\nexport function pushToScrollback(opts: PushToScrollbackOptions): void {\n if (!opts.scrollback || !opts.currentBuffer) return\n const lines = opts.currentBuffer.text.split(\"\\n\")\n opts.scrollback.push(lines)\n}\n\nexport interface VirtualScrollbackViewOptions {\n scrollback: Scrollback | null\n virtualScrollOffset: number\n target: RenderTarget\n}\n\nexport function renderVirtualScrollbackView(opts: VirtualScrollbackViewOptions): void {\n const { scrollback, virtualScrollOffset, target } = opts\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\nexport interface SearchHighlightsOptions {\n searchState: SearchState\n scrollback: Scrollback | null\n virtualScrollOffset: number\n currentBuffer: Buffer | null\n target: RenderTarget\n}\n\nexport function renderSearchHighlights(opts: SearchHighlightsOptions): void {\n const { searchState, scrollback, virtualScrollOffset, currentBuffer, target } = opts\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 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 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\nexport interface SearchBarOverlayOptions {\n searchState: SearchState\n target: RenderTarget\n}\n\nexport function renderSearchBarOverlay(opts: SearchBarOverlayOptions): void {\n const { searchState, target } = opts\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 * Build a `searchScrollback(query)` fn bound to the given scrollback.\n * Returns SearchMatch[] — used as the update-effect pump for the\n * reducer-style search state machine.\n */\nexport function createSearchScrollback(\n scrollback: Scrollback | null,\n): (query: string) => SearchMatch[] {\n return (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 const rows = scrollback.getVisibleRows(scrollback.totalLines - lineIdx - 1, 1)\n const line = rows[0] ?? \"\"\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 * 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, writeFileSync } from \"node:fs\"\nimport { tmpdir } from \"node:os\"\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 \"../ansi\"\nimport {\n CacheBackendContext,\n CapabilityRegistryContext,\n ChainAppContext,\n type ChainAppContextValue,\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 { createPipeline } from \"../measurer\"\nimport {\n isTextSizingLikelySupported,\n detectTextSizingSupport,\n getCachedProbeResult,\n} from \"../text-sizing\"\nimport { createWidthDetector, applyWidthConfig } from \"../ansi/width-detection\"\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 { createRuntime } from \"./create-runtime\"\nimport {\n createHandlerContext,\n dispatchKeyToHandlers,\n handleFocusNavigation,\n invokeEventHandler,\n type NamespacedEvent,\n} from \"./event-handlers\"\nimport { keyToAnsi, keyToKittyAnsi, isModifierOnlyEvent } from \"@silvery/ag/keys\"\nimport { parseKey, type Key } from \"./keys\"\nimport { ensureLayoutEngine } from \"./layout\"\nimport {\n createMouseEventProcessor,\n updateKeyboardModifiers,\n findContainBoundary,\n selectionHitTest,\n} from \"../mouse-events\"\nimport {\n enableKittyKeyboard,\n disableKittyKeyboard,\n KittyFlags,\n enableMouse,\n disableMouse,\n resetCursorStyle,\n enterAlternateScreen,\n leaveAlternateScreen,\n} from \"../output\"\nimport { enableFocusReporting } from \"../focus-reporting\"\nimport { detectKittyFromStdio } from \"../kitty-detect\"\nimport { captureTerminalState, performSuspend } from \"./terminal-lifecycle\"\nimport { type TermProvider, createTermProvider } from \"./term-provider\"\nimport type { Buffer, Dims, Provider, RenderTarget } from \"./types\"\nimport {\n createTerminalSelectionState,\n terminalSelectionUpdate,\n extractText,\n} from \"@silvery/headless/selection\"\nimport { createSelectionBridge, type SelectionFeature } from \"../features/selection\"\nimport {\n createCapabilityRegistry,\n type CapabilityRegistry,\n} from \"@silvery/create/internal/capability-registry\"\nimport { SELECTION_CAPABILITY } from \"@silvery/create/internal/capabilities\"\nimport {\n createBaseApp,\n withCustomEvents,\n withTerminalChain,\n withPasteChain,\n withInputChain,\n withFocusChain,\n type BaseApp,\n type CustomEventStore,\n type InputStore,\n type PasteStore,\n type TerminalStore,\n type FocusChainStore,\n} from \"@silvery/create/plugins\"\nimport { createVirtualScrollback } from \"../virtual-scrollback\"\nimport { createSearchState, searchUpdate } from \"../search-overlay\"\nimport { createOutputGuard, type OutputGuard } from \"../ansi/output-guard\"\nimport { perfLog, checkBudget, logExitSummary, startTracking } from \"./perf\"\nimport { createLogger } from \"loggily\"\nimport {\n createRenderer,\n createSearchScrollback,\n pushToScrollback as pushToScrollbackFn,\n renderSearchBarOverlay as renderSearchBarOverlayFn,\n renderSearchHighlights as renderSearchHighlightsFn,\n renderVirtualScrollbackView as renderVirtualScrollbackViewFn,\n writeSelectionOverlay as writeSelectionOverlayFn,\n} from \"./renderer\"\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(\"../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(\"../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 =\n (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\n ? decoded.slice(0, 200) + ` ...[${decoded.length}ch]... ` + decoded.slice(-100)\n : 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 =\n 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 &&\n (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\n ? { ...capsOption, textSizingSupported: textSizingEnabled }\n : 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\n // Errors caught by SilveryErrorBoundary — flushed to stderr on cleanup so\n // the user sees them after the alt screen exits. Also dumped to a temp file\n // (path included in the stderr message) for full stack/component trace.\n const caughtErrors: Array<{ error: Error; dumpPath?: string }> = []\n function recordBoundaryError(error: Error) {\n let dumpPath: string | undefined\n try {\n dumpPath = `${tmpdir()}/silvery-render-error-${Date.now()}.txt`\n writeFileSync(dumpPath, `${error.message}\\n\\n${error.stack ?? \"(no stack)\"}\\n`)\n } catch {}\n caughtErrors.push({ error, dumpPath })\n log.error?.(\n `React render error caught by SilveryErrorBoundary: ${error.message}${dumpPath ? ` (dump: ${dumpPath})` : \"\"}`,\n )\n }\n // Track protocol state for cleanup and suspend/resume\n let kittyEnabled = false\n const defaultKittyFlags =\n 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(\n { type: \"extend\", col: range.head.col, row: range.head.row },\n s1,\n )\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 // Flush any React render errors caught by SilveryErrorBoundary to stderr.\n // The boundary renders them inside the alt screen — once we leave alt\n // screen the message is gone. Print here so the user actually sees what\n // crashed, with a path to the full dump for stack/component info.\n if (caughtErrors.length > 0) {\n try {\n const lines: string[] = []\n lines.push(\"\")\n lines.push(\n `silvery: ${caughtErrors.length} React render error${caughtErrors.length === 1 ? \"\" : \"s\"} caught during this session:`,\n )\n for (const { error, dumpPath } of caughtErrors) {\n lines.push(` - ${error.message}${dumpPath ? ` (dump: ${dumpPath})` : \"\"}`)\n }\n lines.push(\"\")\n process.stderr.write(lines.join(\"\\n\"))\n } catch {\n // Best-effort — stderr may already be torn down\n }\n }\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 // Apply-chain substrate (TEA Phase 2) — see\n // @silvery/create/runtime/{base-app,with-*-chain,event-loop}.\n //\n // Input / paste / terminal-focus events flow through the chain directly\n // (see `processEventBatch` and the `press()` path). The chain exposes\n // plugin stores on `ChainAppContext` that ag-react hooks subscribe to.\n //\n // withFocusChain.dispatchKey does the focus-tree dispatch inline — the\n // legacy `handleFocusNavigation(…) + runtimeInputListeners` decision\n // point is now a single chain call per event.\n const baseApp = createBaseApp()\n const terminalChainApp = withTerminalChain({\n cols: currentDims.cols,\n rows: currentDims.rows,\n })(baseApp)\n const pasteChainApp = withPasteChain({})(terminalChainApp)\n const inputChainApp = withInputChain(pasteChainApp)\n const focusChainApp = withFocusChain({\n dispatchKey: (input, key) => {\n const focusResult = handleFocusNavigation(input, key as Key, focusManager, container)\n return focusResult === \"consumed\"\n },\n hasActiveFocus: () => focusManager.activeElement !== null,\n })(inputChainApp)\n // Custom events — replaces the legacy RuntimeContext.on/emit surface\n // for app-defined channels (e.g. km-tui's `link:open`).\n const app = withCustomEvents(focusChainApp)\n // Focus event slice — mirrors the withTerminalChain `focused` snapshot\n // into a pub/sub store shaped like InputStore/PasteStore. Used by the\n // ChainAppContext `focusEvents` accessor (hooks useTerminalFocused,\n // useModifierKeys).\n const focusEventListeners: Array<(focused: boolean) => void> = []\n const appFocusEvents = {\n register(handler: (focused: boolean) => void): () => void {\n focusEventListeners.push(handler)\n return () => {\n const i = focusEventListeners.indexOf(handler)\n if (i >= 0) focusEventListeners.splice(i, 1)\n }\n },\n notify(focused: boolean): void {\n for (const h of focusEventListeners) h(focused)\n },\n }\n\n // Raw-key observer slice — hooks that need unfiltered access to key events\n // (useModifierKeys is the canonical consumer). Fired for every key event\n // including release and modifier-only, regardless of focus consumption.\n const rawKeyListeners: Array<(input: string, key: Key) => void> = []\n const appRawKeys = {\n register(handler: (input: string, key: Key) => void): () => void {\n rawKeyListeners.push(handler)\n return () => {\n const i = rawKeyListeners.indexOf(handler)\n if (i >= 0) rawKeyListeners.splice(i, 1)\n }\n },\n notify(input: string, key: Key): void {\n for (const h of rawKeyListeners) h(input, key)\n },\n }\n // Expose on the BaseApp so ag-react hooks can reach the slice once migrated.\n // Keep typing loose here — BaseApp extensions are added by plugins.\n type AppWithChains = BaseApp & {\n input: InputStore\n paste: PasteStore\n terminal: TerminalStore\n focusChain: FocusChainStore\n events: CustomEventStore\n focusEvents: typeof appFocusEvents\n rawKeys: typeof appRawKeys\n }\n const chainApp: AppWithChains = Object.assign(app, {\n focusEvents: appFocusEvents,\n rawKeys: appRawKeys,\n })\n\n // ChainAppContext value — the ag-react-visible slice of the chain.\n const chainAppContextValue: ChainAppContextValue = {\n input: chainApp.input,\n paste: chainApp.paste,\n focusEvents: chainApp.focusEvents,\n rawKeys: chainApp.rawKeys,\n events: chainApp.events,\n }\n\n // Runtime handle — trimmed to `exit()` only. Input / paste / focus\n // subscriptions live on `ChainAppContext` (see chainAppContextValue\n // above); app-defined view ↔ runtime events ride on\n // `ChainAppContext.events` (withCustomEvents).\n const runtimeContextValue: RuntimeContextValue = {\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 onError={recordBoundaryError}>\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) =>\n 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 <ChainAppContext.Provider value={chainAppContextValue}>\n <CapabilityRegistryContext.Provider value={capabilityRegistry}>\n <Root>\n <StoreContext.Provider value={store as StoreApi<unknown>}>\n {element}\n </StoreContext.Provider>\n </Root>\n </CapabilityRegistryContext.Provider>\n </ChainAppContext.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 _eventStart = 0\n const _perfLog: boolean = !!(\n typeof process !== \"undefined\" && process.env?.DEBUG?.includes(\"silvery:perf\")\n )\n\n // Renderer factory — owns the long-lived Ag instance, prevBuffer tracking,\n // SILVERY_STRICT comparison, and perf logging. See renderer.ts.\n const rendererCellDebug =\n CELL_DEBUG && typeof CELL_DEBUG.x === \"number\" && typeof CELL_DEBUG.y === \"number\"\n ? { x: CELL_DEBUG.x, y: CELL_DEBUG.y }\n : null\n const renderer = createRenderer({\n wrappedElement,\n fiberRoot,\n container,\n runtime,\n alternateScreen,\n pipelineConfig,\n noIncremental: NO_INCREMENTAL,\n strictMode: STRICT_MODE,\n cellDebug: rendererCellDebug,\n instrumented: INSTRUMENTED,\n ansiTrace: _ansiTrace,\n perfLog: _perfLog,\n })\n const doRender = renderer.doRender\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(\n \"/tmp/silvery-trace.log\",\n \"=== RUNTIME.RENDER (initial) ===\\n\",\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 #${renderer.renderCount()}, incremental=${!renderer.isIncrementalOff()})\\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 renderer.resetAg()\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 #${renderer.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 #${renderer.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 #${renderer.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 // Overlay helpers — thin wrappers over the pure functions in ./renderer.ts.\n // These bridge the closure state (currentBuffer, selectionState, scrollback,\n // virtualScrollOffset, searchState) into the pure functional API.\n const writeSelectionOverlay = (): void =>\n writeSelectionOverlayFn({\n selectionEnabled,\n selectionState,\n currentBuffer: currentBuffer ?? null,\n alternateScreen,\n target,\n })\n const pushToScrollback = (): void =>\n pushToScrollbackFn({ scrollback, currentBuffer: currentBuffer ?? null })\n const renderVirtualScrollbackView = (): void =>\n renderVirtualScrollbackViewFn({ scrollback, virtualScrollOffset, target })\n const renderSearchHighlights = (): void =>\n renderSearchHighlightsFn({\n searchState,\n scrollback,\n virtualScrollOffset,\n currentBuffer: currentBuffer ?? null,\n target,\n })\n const renderSearchBarOverlay = (): void => renderSearchBarOverlayFn({ searchState, target })\n const searchScrollback = createSearchScrollback(scrollback)\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(\n 0,\n scrollback.totalLines - eff.row - target.getDims().rows,\n )\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(\n 0,\n scrollback.totalLines - eff.row - target.getDims().rows,\n )\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(\n 0,\n scrollback.totalLines - eff.row - target.getDims().rows,\n )\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(\n { type: \"input\", char: data.input },\n searchState,\n searchScrollback,\n )\n searchState = next\n for (const eff of effects) {\n if (eff.type === \"scrollTo\") {\n virtualScrollOffset = Math.max(\n 0,\n scrollback.totalLines - eff.row - target.getDims().rows,\n )\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(\n { type: \"extend\", col: mouseData.x, row: mouseData.y },\n selectionState,\n )\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 renderer.resetCount()\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 // All key/paste/focus events flow through the apply chain:\n //\n // withFocusChain → withInputChain → withPasteChain → withTerminalChain\n //\n // withFocusChain owns the focused-target dispatch (formerly\n // handleFocusNavigation). withInputChain runs useInput fallbacks only\n // when focus didn't consume. withTerminalChain observes modifiers and\n // resize. The chain's effects (render / exit) are drained and re-routed\n // into this runner's render pipeline.\n //\n // Mouse / resize / other namespaced events bypass the chain and go\n // straight to `runEventHandler` (app handlers), same as before.\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 // Raw-key observer: fire unconditionally (useModifierKeys tracks state\n // from every key event, including release and modifier-only).\n chainApp.rawKeys.notify(input, parsedKey)\n\n // Dispatch into the chain. withInputChain filters release / modifier-only\n // events internally so useInput handlers aren't spammed; withFocusChain\n // drives focus precedence via the injected handleFocusNavigation.\n chainApp.dispatch({ type: \"input:key\", input, key: parsedKey })\n // Drain chain effects — render/exit are re-emitted via the legacy\n // render orchestration below (doRender + flush loop). Capture exit\n // intent so we can short-circuit before the app handler fires.\n const chainEffects = chainApp.drainEffects()\n for (const eff of chainEffects) {\n if (eff.type === \"exit\") shouldExit = true\n }\n if (shouldExit) {\n inEventHandler = false\n return null\n }\n // Release / modifier-only events skip the app handler path (matches\n // pre-refactor behaviour: those never produced app-level commands).\n if (parsedKey.eventType === \"release\" || isModifierOnlyEvent(input, parsedKey)) {\n continue\n }\n } else if (event.type === \"term:paste\") {\n const { text } = event.data as { text: string }\n chainApp.dispatch({ type: \"term:paste\", text })\n chainApp.drainEffects()\n } else if (event.type === \"term:focus\") {\n const { focused } = event.data as { focused: boolean }\n chainApp.dispatch({ type: \"term:focus\", focused })\n chainApp.drainEffects()\n // withTerminalChain is an observer — fan out to the chain\n // focusEvents store so useTerminalFocused / useModifierKeys\n // subscribers see the transition.\n chainApp.focusEvents.notify(focused)\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 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, ${renderer.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 renderer.resetAg()\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 renderer.resetAg()\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) => {\n cleanup() // exit alt screen so error is visible in normal terminal\n const errObj = err instanceof Error ? err : new Error(String(err))\n const msg = errObj.message\n const stack = errObj.stack ?? \"(no stack)\"\n\n // Dump the full error + stack to a temp file — alt screen clears\n // stderr, and for deep stacks (e.g. \"Maximum call stack size exceeded\")\n // the user needs the recursive frame to diagnose. Same pattern as the\n // SILVERY_STRICT mismatch dump and the React render-error dump.\n let dumpPath: string | undefined\n try {\n dumpPath = `${tmpdir()}/silvery-eventloop-failure-${Date.now()}.txt`\n writeFileSync(dumpPath, `${msg}\\n\\n${stack}\\n`)\n } catch {\n // Best-effort\n }\n\n const summaryLine = dumpPath\n ? `eventLoop failed: ${msg.split(\"\\n\")[0]}\\n dump: ${dumpPath}`\n : `eventLoop failed: ${msg.split(\"\\n\")[0]}`\n log.error?.(summaryLine)\n process.stderr.write(`\\n${summaryLine}\\n`)\n process.exitCode = 1\n })\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 // Raw-key observer: fire unconditionally (useModifierKeys tracks state\n // from every key event, including release and modifier-only).\n chainApp.rawKeys.notify(input, parsedKey)\n\n // Suppress subscription renders — flush loop below handles everything.\n inEventHandler = true\n isRendering = true\n\n // Dispatch into the apply chain: withFocusChain handles the focus-tree\n // dispatch (formerly handleFocusNavigation), withInputChain fires the\n // useInput fallback when focus didn't consume. Same precedence as the\n // batched path.\n chainApp.dispatch({ type: \"input:key\", input, key: parsedKey })\n const pressEffects = chainApp.drainEffects()\n let focusConsumed = false\n for (const eff of pressEffects) {\n if (eff.type === \"exit\") shouldExit = true\n if (eff.type === \"render\") {\n // withFocusChain emits a single `render` effect when the focused\n // tree consumed the key. We use that as the \"focus consumed\"\n // signal — a single render here short-circuits the rest of the\n // press() pipeline, matching the pre-refactor behaviour.\n focusConsumed = true\n }\n }\n if (shouldExit) {\n isRendering = false\n inEventHandler = false\n return\n }\n if (focusConsumed) {\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 { ChainAppContext } from \"../context\"\n\nexport type PasteCallback = (text: string) => void\n\nexport function usePasteCallback(handler: PasteCallback): void {\n const chain = useContext(ChainAppContext)\n\n const handlerRef = useRef(handler)\n handlerRef.current = handler\n\n useEffect(() => {\n if (!chain) return\n return chain.paste.register((text) => {\n handlerRef.current(text)\n })\n }, [chain])\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, pickColorLevel, type ColorTier } from \"@silvery/ansi\"\nimport { nord, catppuccinLatte } from \"@silvery/theme/schemes\"\nimport { ThemeProvider } from \"@silvery/ag-react/ThemeProvider\"\nimport type { TerminalCaps } from \"../terminal-caps\"\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 * Force the color tier end-to-end, bypassing auto-detection.\n *\n * When set, the pipeline's `caps.colorLevel` is overridden for the full\n * run (affects inline hex quantization, mono attribute fallback, SGR\n * encoding, backdrop blend targets), AND the active Theme is pre-quantized\n * via {@link pickColorLevel} so token hex values match.\n *\n * Useful for:\n * - bypassing under-reporting terminals (force `\"truecolor\"`),\n * - testing low-end degradation (force `\"ansi16\"` or `\"mono\"`),\n * - accessibility / CI output (force `\"mono\"`).\n *\n * Priority (highest wins): `NO_COLOR` env → `FORCE_COLOR` env →\n * `colorLevel` → auto-detect.\n *\n * Tiers:\n * - `\"mono\"` — monochrome (attribute fallback: bold/dim/inverse).\n * - `\"ansi16\"` — 16-slot palette (SGR 30-37, 90-97).\n * - `\"256\"` — xterm-256 palette.\n * - `\"truecolor\"` — 24-bit RGB (no quantization).\n */\n colorLevel?: ColorTier\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(\"../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(\n element: ReactElement,\n term: Term,\n termOptions?: Partial<RunOptions>,\n): 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\n | { feed(data: string): void }\n | 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 detectedCaps = term.caps ?? detectTerminalCaps()\n // Honor termOptions.colorLevel + env vars (same precedence as the options path).\n const termTier = resolveColorTier(termOptions?.colorLevel, detectedCaps)\n const caps: TerminalCaps = termTier\n ? { ...detectedCaps, colorLevel: tierToCapsLevel(termTier) }\n : detectedCaps\n // Detect terminal colors via OSC — must happen before alt screen.\n // When colorLevel is forced, pre-quantize the detected theme.\n const theme = await detectTheme({ fallbackDark: nord, fallbackLight: catppuccinLatte })\n const resolvedTheme = termTier ? pickColorLevel(theme, termTier) : theme\n const themed = <ThemeProvider theme={resolvedTheme}>{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, colorLevel: colorLevelOption, ...rest } = optionsOrTerm as RunOptions\n const detectedCaps = rest.caps ?? detectTerminalCaps()\n // Resolve effective tier (env wins over the user override, auto-detect loses).\n const effectiveTier = resolveColorTier(colorLevelOption, detectedCaps)\n const caps: TerminalCaps = effectiveTier\n ? { ...detectedCaps, colorLevel: tierToCapsLevel(effectiveTier) }\n : detectedCaps\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 // When colorLevel is forced, pre-quantize the detected theme to the chosen tier so\n // token hex values match what the pipeline will actually emit.\n const themed = headless\n ? element\n : await detectTheme({ fallbackDark: nord, fallbackLight: catppuccinLatte }).then((theme) => {\n const resolvedTheme = effectiveTier ? pickColorLevel(theme, effectiveTier) : theme\n return <ThemeProvider theme={resolvedTheme}>{element}</ThemeProvider>\n })\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/**\n * Map the public `ColorTier` spelling (used by `run({ colorLevel })` and\n * `pickColorLevel`) to the internal caps spelling (`\"none\" | \"basic\" | \"256\"\n * | \"truecolor\"`). The pipeline lives in caps-space.\n */\nfunction tierToCapsLevel(tier: ColorTier): TerminalCaps[\"colorLevel\"] {\n switch (tier) {\n case \"mono\":\n return \"none\"\n case \"ansi16\":\n return \"basic\"\n case \"256\":\n return \"256\"\n case \"truecolor\":\n return \"truecolor\"\n }\n}\n\n/** Inverse of {@link tierToCapsLevel} — caps-space to tier-space. */\nfunction capsLevelToTier(level: TerminalCaps[\"colorLevel\"]): ColorTier {\n switch (level) {\n case \"none\":\n return \"mono\"\n case \"basic\":\n return \"ansi16\"\n case \"256\":\n return \"256\"\n case \"truecolor\":\n return \"truecolor\"\n }\n}\n\n/**\n * Resolve the effective color tier, honoring env-var precedence.\n *\n * Priority (highest wins):\n * 1. `NO_COLOR` (any value) → \"mono\"\n * 2. `FORCE_COLOR` (0/false → \"mono\"; 1 → \"ansi16\"; 2 → \"256\"; 3 → \"truecolor\")\n * 3. Explicit `colorLevel` option\n * 4. `undefined` — caller keeps detected caps.colorLevel unchanged\n *\n * Returns `undefined` when nothing overrides auto-detection (callers keep\n * `detectedCaps` as-is).\n */\nfunction resolveColorTier(\n optionTier: ColorTier | undefined,\n _detectedCaps: TerminalCaps,\n): ColorTier | undefined {\n // NO_COLOR wins over everything.\n if (process.env.NO_COLOR !== undefined) return \"mono\"\n // FORCE_COLOR wins over the option but not over NO_COLOR.\n const force = process.env.FORCE_COLOR\n if (force !== undefined) {\n if (force === \"0\" || force === \"false\") return \"mono\"\n if (force === \"1\") return \"ansi16\"\n if (force === \"2\") return \"256\"\n if (force === \"3\") return \"truecolor\"\n // Unknown truthy FORCE_COLOR → basic (mirrors @silvery/ansi detectColor)\n return \"ansi16\"\n }\n return optionTier\n}\n\n// Expose the mapping helpers for tests / advanced callers that need to\n// bridge the two spellings (ColorTier ⇄ caps.colorLevel).\nexport { tierToCapsLevel, capsLevelToTier }\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(\"../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 * wrapWithThemedProvider — shared internal for theme boot helpers.\n *\n * Detects the terminal's color scheme (or accepts an explicit theme),\n * builds `ActiveScheme` metadata, and wraps a React element in\n * `<ThemeProvider theme={theme} scheme={scheme}>`. Returns the wrapped\n * element plus the detection result for callers that want provenance.\n *\n * Both `runThemed` (simple run() form) and any future pipe-chain themed\n * boot helpers delegate to this function for the wrap step, keeping the\n * detection + wrapping logic in one place.\n *\n * @example\n * ```tsx\n * // Run-form (what runThemed does):\n * const { element } = await wrapWithThemedProvider(<App />, { catalog })\n * return run(element, runOpts)\n *\n * // Pipe-chain form (future createThemedApp would do):\n * const { element, result } = await wrapWithThemedProvider(<App />, opts)\n * console.log(`${result.source} — ${result.matchedName ?? \"no match\"}`)\n * return pipe(createApp(store), withReact(element), ...)\n * ```\n */\n\nimport React, { type ReactElement } from \"react\"\nimport {\n detectScheme,\n type ColorScheme,\n type DetectSchemeOptions,\n type DetectSchemeResult,\n} from \"@silvery/ansi\"\nimport { ThemeProvider, type ThemeTokens } from \"@silvery/ag-react\"\nimport type { ActiveScheme } from \"@silvery/ansi\"\n\n// Re-export for callers that want to compose without re-importing.\nexport type { DetectSchemeResult }\n\nexport interface ThemedProviderOptions extends DetectSchemeOptions {\n /**\n * Additional token overrides applied after detection — merged over the\n * detected theme via an inner `<ThemeProvider tokens={...}>`. Use for\n * app brand colors (`{ brand: \"#5B8DEF\" }`) or app-specific custom tokens\n * (`{ \"priority-p0\": \"#FF5555\" }`).\n */\n tokens?: ThemeTokens\n}\n\nexport interface WrapWithThemedProviderResult {\n /** The element wrapped in ThemeProvider (pass to run() or withReact()). */\n element: ReactElement\n /** Full detection provenance — scheme, theme, source, confidence, slotSources. */\n result: DetectSchemeResult\n}\n\n/**\n * Detect the terminal's color scheme, derive a theme, and wrap `element`\n * in `<ThemeProvider>`.\n *\n * Builds an `ActiveScheme` metadata object so `useActiveScheme()` returns\n * detection provenance to all descendants. Token overrides are applied as\n * an inner `<ThemeProvider tokens={...}>` so they merge over the detected\n * theme rather than replacing it.\n *\n * Detection cascade (full — see `detectScheme`):\n * 1. `opts.override` — explicit ColorScheme\n * 2. `SILVERY_COLOR` env var\n * 3. OSC probe (OSC 10/11/4/12/17/19)\n * 4. Fingerprint match against `opts.catalog`\n * 5. Fallback to defaultDarkScheme / defaultLightScheme\n *\n * @param element - Your root React element (the app)\n * @param opts - Detection + token override options\n * @returns Wrapped element + full detection result\n */\nexport async function wrapWithThemedProvider(\n element: ReactElement,\n opts: ThemedProviderOptions = {},\n): Promise<WrapWithThemedProviderResult> {\n const detectedResult = await detectScheme({\n override: opts.override,\n catalog: opts.catalog,\n timeoutMs: opts.timeoutMs,\n darkFallback: opts.darkFallback,\n enforce: opts.enforce,\n wcag: opts.wcag,\n })\n\n const { theme, scheme: detectedScheme, source, confidence, matchedName } = detectedResult\n\n // Build ActiveScheme metadata for useActiveScheme() hook.\n // Maps DetectSchemeResult.source (\"probed\" | \"bg-mode\" → \"probe\") to\n // ActiveScheme.source (\"probe\" | \"fingerprint\" | \"fallback\" | \"override\").\n const activeScheme: ActiveScheme = {\n name: detectedScheme.name ?? theme.name ?? \"unknown\",\n source: mapDetectSource(source),\n ...(source === \"fingerprint\" && {\n confidence,\n matchedName,\n }),\n }\n\n // Inner tokens layer: merged over the detected theme.\n const inner = opts.tokens ? (\n <ThemeProvider tokens={opts.tokens}>{element}</ThemeProvider>\n ) : (\n element\n )\n\n const wrapped = (\n <ThemeProvider theme={theme} scheme={activeScheme}>\n {inner}\n </ThemeProvider>\n )\n\n return { element: wrapped, result: detectedResult }\n}\n\n/**\n * Map `DetectSchemeResult.source` to `ActiveScheme.source`.\n *\n * `detectScheme` uses fine-grained values (\"probed\", \"bg-mode\") that\n * aren't part of the `ActiveScheme` consumer-facing vocabulary. Both\n * collapse to \"probe\" — they represent a successful or partial terminal\n * query rather than a catalog fingerprint or explicit override.\n */\nfunction mapDetectSource(source: DetectSchemeResult[\"source\"]): ActiveScheme[\"source\"] {\n switch (source) {\n case \"fingerprint\":\n return \"fingerprint\"\n case \"override\":\n return \"override\"\n case \"fallback\":\n return \"fallback\"\n case \"probed\":\n case \"bg-mode\":\n return \"probe\"\n }\n}\n\n/** Re-export types so consumers have everything they need. */\nexport type { ColorScheme, ThemedProviderOptions as ThemedProviderOpts }\n","/**\n * runThemed — one-call scheme detection + ThemeProvider + run.\n *\n * The \"just render this React TUI with a detected theme\" shortcut. Composes\n * `detectScheme` (OSC probe + fingerprint + fallback) with `ThemeProvider`\n * (React context) and `run` (silvery-loop runtime). Apps that need custom\n * composition (store, pipe, withFocus) keep using `createApp + pipe`.\n *\n * Delegates the wrap step to `wrapWithThemedProvider` — the shared internal\n * used by any themed boot helper that wants detection + ThemeProvider without\n * coupling to the `run()` call shape.\n *\n * @example\n * ```tsx\n * import { runThemed } from \"silvery/runtime\"\n * import { builtinPalettes } from \"@silvery/theme/schemes\"\n *\n * const handle = await runThemed(<App />, {\n * catalog: Object.values(builtinPalettes),\n * })\n * await handle.waitUntilExit()\n * ```\n *\n * Pass `tokens` to override specific theme values:\n *\n * ```tsx\n * await runThemed(<App />, {\n * tokens: { brand: \"#5B8DEF\", \"priority-p0\": \"#FF5555\" },\n * })\n * ```\n */\n\nimport type { ReactElement } from \"react\"\nimport type { ColorScheme } from \"@silvery/ansi\"\nimport { run, type RunHandle, type RunOptions } from \"./run\"\nimport { wrapWithThemedProvider, type ThemedProviderOptions } from \"./wrap-with-themed-provider\"\n\nexport interface RunThemedOptions extends ThemedProviderOptions {\n /** Forwarded to `run()` — terminal + rendering options. */\n run?: RunOptions\n}\n\n/**\n * Detect the terminal's color scheme, wrap the element in a ThemeProvider,\n * and run the silvery-loop runtime. Returns the standard `RunHandle`.\n *\n * Detection cascade (full — see `detectScheme`):\n * 1. `opts.override` — explicit ColorScheme\n * 2. `SILVERY_COLOR` env var\n * 3. OSC probe (OSC 10/11/4/12/17/19)\n * 4. Fingerprint match against `opts.catalog`\n * 5. Fallback to defaultDarkScheme / defaultLightScheme\n *\n * @param element - Your root React element (the app)\n * @param opts - Detection + token overrides + run options\n * @returns RunHandle — same shape as `run()`\n */\nexport async function runThemed(\n element: ReactElement,\n opts: RunThemedOptions = {},\n): Promise<RunHandle> {\n const { element: wrapped } = await wrapWithThemedProvider(element, opts)\n return run(wrapped, opts.run)\n}\n\n/** Re-export types so consumers of `runThemed` have everything they need. */\nexport type { ColorScheme }\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","/**\n * ReactiveThemeProvider — auto-switches theme based on terminal color scheme.\n *\n * Wraps ThemeProvider and subscribes to the BgModeDetector (Mode 2031).\n * When the terminal reports a dark↔light change, the theme automatically\n * switches to the corresponding variant.\n *\n * @example\n * ```tsx\n * import { ReactiveThemeProvider } from '@silvery/ag-react'\n * import { nord, catppuccinLatte } from '@silvery/theme'\n *\n * <ReactiveThemeProvider dark={nordTheme} light={latteTheme}>\n * <App />\n * </ReactiveThemeProvider>\n * ```\n *\n * When no color scheme is detected (unknown), falls back to the `dark` theme.\n */\n\nimport React from \"react\"\nimport { ThemeProvider } from \"./ThemeProvider\"\nimport { useColorScheme } from \"./hooks/useColorScheme\"\nimport type { Theme } from \"@silvery/ansi\"\nimport { defaultDarkTheme, defaultLightTheme } from \"@silvery/theme\"\n\nexport interface ReactiveThemeProviderProps {\n /** Theme to use when the terminal is in dark mode. Default: Nord. */\n dark?: Theme\n /** Theme to use when the terminal is in light mode. Default: Catppuccin Latte. */\n light?: Theme\n /** Initial theme to use before detection completes. Default: dark theme. */\n initial?: Theme\n children: React.ReactNode\n}\n\n/**\n * Inner component that subscribes to color scheme changes.\n * Separated so ThemeProvider re-renders only when scheme actually changes.\n */\nfunction ReactiveThemeInner({\n dark = defaultDarkTheme,\n light = defaultLightTheme,\n initial,\n children,\n}: ReactiveThemeProviderProps): React.ReactElement {\n const scheme = useColorScheme()\n\n let theme: Theme\n if (scheme === \"light\") {\n theme = light\n } else if (scheme === \"dark\") {\n theme = dark\n } else {\n // \"unknown\" — use initial or fall back to dark\n theme = initial ?? dark\n }\n\n return <ThemeProvider theme={theme}>{children}</ThemeProvider>\n}\n\n/**\n * Theme provider that reacts to Mode 2031 color scheme changes.\n *\n * Automatically switches between dark and light themes when the terminal\n * reports a scheme change. Uses `useColorScheme()` internally.\n */\nexport function ReactiveThemeProvider(props: ReactiveThemeProviderProps): React.ReactElement {\n return <ReactiveThemeInner {...props} />\n}\n","/**\n * useActiveScheme — read scheme detection metadata from context.\n *\n * Returns an `ActiveScheme` object describing how the active terminal color\n * scheme was determined: whether it was probed from OSC queries, matched via\n * fingerprinting, a forced override, or a fallback. Includes the matched\n * catalog name and confidence score for fingerprint detections.\n *\n * Returns `null` when no `<ThemeProvider scheme={...}>` is present in the\n * tree — i.e. the scheme was not passed down from `runThemed` or a manually\n * constructed provider.\n *\n * @example\n * ```tsx\n * function DetectionBadge() {\n * const scheme = useActiveScheme()\n * if (!scheme) return null\n * if (scheme.source === \"fingerprint\") {\n * return <Text color=\"$muted\">Theme: {scheme.matchedName} ({Math.round((scheme.confidence ?? 0) * 100)}%)</Text>\n * }\n * return <Text color=\"$muted\">Theme: {scheme.name} ({scheme.source})</Text>\n * }\n * ```\n */\n\nimport { useContext } from \"react\"\nimport { ActiveSchemeContext } from \"../ThemeContext\"\nimport type { ActiveScheme } from \"@silvery/ansi\"\n\n/**\n * Hook that returns the active scheme detection metadata, or `null` if no\n * scheme metadata was injected by an ancestor `ThemeProvider`.\n */\nexport function useActiveScheme(): ActiveScheme | null {\n return useContext(ActiveSchemeContext)\n}\n","/**\n * Silvery - Next-gen Terminal UI Renderer with Layout Feedback\n *\n * React-based terminal UI framework. Ink-compatible API with components\n * that know their size via useBoxRect/useScrollRect hooks.\n *\n * ## Import Syntax\n *\n * All exports are NAMED exports (no default export):\n *\n * ```tsx\n * // Components and hooks\n * import { Box, Text, useBoxRect, useInput, useApp, render, createTerm, term } from '@silvery/ag-react'\n *\n * // Testing utilities\n * import { createRenderer, createLocator } from '@silvery/test'\n * ```\n *\n * ## Quick Example\n *\n * ```tsx\n * import { render, Box, Text, useInput, useApp, createTerm } from '@silvery/ag-react'\n *\n * function App() {\n * const { exit } = useApp()\n * useInput((input, key) => {\n * if (input === 'q') exit();\n * })\n * return <Box><Text>Press q to quit</Text></Box>\n * }\n *\n * // Interactive rendering with Term\n * using term = createTerm()\n * await render(<App />, term)\n * ```\n *\n * Static rendering (no terminal needed):\n *\n * ```tsx\n * import { render, Box, Text } from '@silvery/ag-react'\n *\n * // Renders once and returns when stable\n * await render(<Box><Text>Hello</Text></Box>)\n *\n * // With custom dimensions\n * await render(<Report />, { width: 120 })\n * ```\n *\n * @packageDocumentation\n */\n\n// =============================================================================\n// Components\n// =============================================================================\n\n/**\n * Re-export Box component - flexbox container for layout.\n *\n * @example\n * ```tsx\n * import { Box, Text } from '@silvery/ag-react';\n *\n * <Box flexDirection=\"row\" gap={2}>\n * <Box width={10}><Text>Left</Text></Box>\n * <Box flexGrow={1}><Text>Center</Text></Box>\n * </Box>\n * ```\n */\nexport { Box } from \"./components/Box\"\nexport { Console } from \"./ui/components\"\nexport { ListView } from \"./ui/components\"\nexport type {\n ListViewProps,\n ListViewHandle,\n ListItemMeta,\n ListViewCacheConfig,\n ListViewSearchConfig,\n} from \"./ui/components\"\nexport { HorizontalVirtualList } from \"./ui/components\"\nexport type { HorizontalVirtualListProps, HorizontalVirtualListHandle } from \"./ui/components\"\nexport { SplitView } from \"./ui/components\"\nexport type { SplitViewProps } from \"./ui/components\"\nexport type { LayoutNode as SplitLayoutNode } from \"@silvery/ag-term/pane-manager\"\nexport {\n createLeaf,\n splitPane,\n removePane,\n getPaneIds,\n findAdjacentPane,\n resizeSplit,\n swapPanes,\n getTabOrder as getSplitTabOrder,\n} from \"@silvery/ag-term/pane-manager\"\n\n/**\n * Re-export Text component - renders text content.\n *\n * @example\n * ```tsx\n * import { Text } from '@silvery/ag-react';\n * import chalk from 'chalk';\n *\n * <Text>Plain text</Text>\n * <Text color=\"green\">Colored text</Text>\n * <Text>{chalk.bold('Chalk works too')}</Text>\n * ```\n */\nexport { Text } from \"./components/Text\"\n\nexport { Link } from \"./components/Link\"\nexport type { LinkProps } from \"./components/Link\"\nexport { Transform } from \"./components/Transform\"\nexport type { TransformProps } from \"./components/Transform\"\nexport { Fill } from \"./components/Fill\"\nexport type { FillProps } from \"./components/Fill\"\nexport { Newline } from \"./components/Newline\"\nexport { Spacer } from \"./components/Spacer\"\nexport { Static } from \"./components/Static\"\n// Viewport Architecture (Phase 2)\nexport { Screen } from \"./ui/components\"\nexport type { ScreenProps } from \"./ui/components\"\n\n/**\n * Re-export ErrorBoundary component - catches render errors in children.\n *\n * @example\n * ```tsx\n * import { ErrorBoundary, Box, Text } from '@silvery/ag-react';\n *\n * <ErrorBoundary fallback={<Text color=\"red\">Error!</Text>}>\n * <MyComponent />\n * </ErrorBoundary>\n * ```\n */\nexport { ErrorBoundary } from \"./ui/components\"\nexport type { ErrorBoundaryProps } from \"./ui/components\"\n\n// Lightweight runtime-level error boundary (used as default root in createApp/run)\nexport { SilveryErrorBoundary } from \"./error-boundary\"\nexport type { SilveryErrorBoundaryProps } from \"./error-boundary\"\n\n// Input Components\nexport { TextInput } from \"./ui/components\"\nexport type { TextInputProps, TextInputHandle } from \"./ui/components\"\n\nexport { TextArea } from \"./ui/components\"\nexport type { TextAreaProps, TextAreaHandle, TextAreaSelection } from \"./ui/components\"\n\nexport { useTextArea, clampScroll } from \"./ui/components\"\nexport type { UseTextAreaOptions, UseTextAreaResult } from \"./ui/components\"\n\nexport { EditContextDisplay } from \"./ui/components\"\nexport type { EditContextDisplayProps } from \"./ui/components\"\n\n// Display Components\nexport { CursorLine } from \"./ui/components\"\nexport type { CursorLineProps } from \"./ui/components\"\n\n// Dialog Components\nexport { ModalDialog, formatTitleWithHotkey } from \"./ui/components\"\nexport type { ModalDialogProps } from \"./ui/components\"\n\nexport { Backdrop } from \"./ui/components\"\nexport type { BackdropProps } from \"./ui/components\"\n\nexport { PickerDialog } from \"./ui/components\"\nexport type { PickerDialogProps } from \"./ui/components\"\n\nexport { PickerList } from \"./ui/components\"\nexport type { PickerListProps } from \"./ui/components\"\n\n// Typography Presets\nexport {\n H1,\n H2,\n H3,\n P,\n Lead,\n Muted,\n Small,\n Strong,\n Em,\n Code,\n Kbd,\n Blockquote,\n CodeBlock,\n HR,\n UL,\n OL,\n LI,\n} from \"./ui/components\"\nexport type { TypographyProps } from \"./ui/components\"\n\n// Heading (OSC 66 text sizing)\nexport { Heading } from \"./ui/components\"\nexport type { HeadingProps, HeadingLevel } from \"./ui/components\"\n\n// Focusable Controls\nexport { Toggle } from \"./ui/components\"\nexport type { ToggleProps } from \"./ui/components\"\n\nexport { Button } from \"./ui/components\"\nexport type { ButtonProps } from \"./ui/components\"\n\n// Input Hooks\nexport { useReadline } from \"./ui/components\"\nexport type { ReadlineState, UseReadlineOptions, UseReadlineResult } from \"./ui/components\"\n\n// Widget Components\nexport { Spinner } from \"./ui/components\"\nexport type { SpinnerProps } from \"./ui/components\"\n\nexport { ProgressBar } from \"./ui/components\"\nexport type { ProgressBarProps } from \"./ui/components\"\n\nexport { SelectList } from \"./ui/components\"\nexport type { SelectListProps, SelectOption } from \"./ui/components\"\n\nexport { Table } from \"./components/Table\"\nexport type { TableProps, Column, Column as TableColumn } from \"./components/Table\"\n\nexport { Badge } from \"./ui/components\"\nexport type { BadgeProps } from \"./ui/components\"\n\nexport { Divider } from \"./ui/components\"\nexport type { DividerProps } from \"./ui/components\"\n\n// Form Components\nexport { Form, FormField } from \"./ui/components\"\nexport type { FormProps, FormFieldProps } from \"./ui/components\"\n\n// Toast / Notification\nexport { useToast, ToastContainer, ToastItem } from \"./ui/components\"\nexport type {\n ToastData,\n ToastOptions,\n ToastVariant,\n UseToastResult,\n ToastContainerProps,\n ToastItemProps,\n} from \"./ui/components\"\n\n// Alert family — InlineAlert (low) / Banner (medium) / Alert (high). See\n// hub/silvery/design/v10-terminal/design-system.md §\"Urgency is not a\n// design-system concern\" — urgency is component choice, not a priority prop.\nexport { InlineAlert } from \"./ui/components\"\nexport type { InlineAlertProps } from \"./ui/components\"\n\nexport { Banner } from \"./ui/components\"\nexport type { BannerProps } from \"./ui/components\"\n\nexport { Alert } from \"./ui/components\"\nexport type {\n AlertProps,\n AlertTitleProps,\n AlertBodyProps,\n AlertActionsProps,\n} from \"./ui/components\"\n\n// Shared tone surface (ToneKey + resolvers) — building block for tone-bearing\n// components. Apps writing their own tone components can reuse these helpers.\nexport { toneFillTokens, toneSubtleTokens, toneFgToken, toneIcon, TONE_ICONS } from \"./ui/components\"\nexport type { ToneKey, ToneFillTokens, ToneSubtleTokens } from \"./ui/components\"\n\n// Command Palette\nexport { CommandPalette } from \"./ui/components\"\nexport type { CommandPaletteProps, CommandItem } from \"./ui/components\"\n\n// Tree View\nexport { TreeView } from \"./ui/components\"\nexport type { TreeViewProps, TreeNode } from \"./ui/components\"\n\n// Breadcrumb\nexport { Breadcrumb } from \"./ui/components\"\nexport type { BreadcrumbProps, BreadcrumbItem } from \"./ui/components\"\n\n// Tabs\nexport { Tabs, TabList, Tab, TabPanel } from \"./ui/components\"\nexport type { TabsProps, TabListProps, TabProps, TabPanelProps } from \"./ui/components\"\n\n// Tooltip\nexport { Tooltip } from \"./ui/components\"\nexport type { TooltipProps } from \"./ui/components\"\n\n// Skeleton\nexport { Skeleton } from \"./ui/components\"\nexport type { SkeletonProps } from \"./ui/components\"\n\n// Image Component\nexport { Image } from \"./ui/image/index\"\nexport type { ImageProps } from \"./ui/image/index\"\n\n// Image Protocol Encoders\nexport { encodeKittyImage, deleteKittyImage, isKittyGraphicsSupported } from \"./ui/image/index\"\nexport type { KittyImageOptions } from \"./ui/image/index\"\nexport { encodeSixel, isSixelSupported } from \"./ui/image/index\"\nexport type { SixelImageData } from \"./ui/image/index\"\n\n// =============================================================================\n// Hooks\n// =============================================================================\n\n/**\n * Layout hooks - the main feature of silvery over ink.\n *\n * @example\n * ```tsx\n * import { useBoxRect, Box, Text } from '@silvery/ag-react';\n *\n * function ResponsiveCard() {\n * // Components know their size - no width prop threading needed\n * const { width, height } = useBoxRect();\n * return <Text>{`Size: ${width}x${height}`}</Text>;\n * }\n * ```\n */\nexport { useBoxRect, useScrollRect, useScreenRect } from \"./hooks/useLayout\"\n\n/**\n * Access the current component's AgNode and its reactive rect signals.\n *\n * Returns `{ node, signals }` where `signals` contains `boxRect`, `scrollRect`,\n * and `screenRect` as alien-signals writable functions. Call `signal()` to read\n * the current value. Use inside an `effect()` from `@silvery/signals` for\n * reactive subscriptions.\n *\n * Returns `null` if called outside a silvery component tree.\n *\n * @example\n * ```tsx\n * function DebugOverlay() {\n * const handle = useAgNode()\n * if (!handle) return null\n * const rect = handle.signals.boxRect()\n * return <Text>{`Position: ${rect?.x},${rect?.y}`}</Text>\n * }\n * ```\n */\nexport { useAgNode } from \"./hooks/useAgNode\"\nexport type { AgNodeHandle } from \"./hooks/useAgNode\"\n\n/**\n * Bridge alien-signals to React re-renders.\n *\n * Reads a signal value and re-renders the component when it changes.\n * Layer 2 of the reactive stack — used by advanced consumers who want\n * direct signal access, or as the foundation for Layer 3 hooks.\n *\n * @example\n * ```tsx\n * const ag = useAgNode()\n * const rect = useSignal(ag?.signals.boxRect ?? null)\n * ```\n */\nexport { useSignal } from \"./hooks/useSignal\"\n\n/**\n * Ink-compatible box metrics hook.\n *\n * Returns `{ width, height, left, top, hasMeasured }` for the nearest silvery\n * Box. With a ref, mirrors Ink 7.0's `useBoxMetrics(ref)`. Without a ref,\n * uses NodeContext (silvery idiom). `left` / `top` are parent-relative.\n *\n * @example Ink-compatible\n * ```tsx\n * const ref = useRef(null)\n * const { width, height, hasMeasured } = useBoxMetrics(ref)\n * return <Box ref={ref}>...</Box>\n * ```\n *\n * @example Silvery idiom\n * ```tsx\n * const { width } = useBoxMetrics()\n * ```\n */\nexport { useBoxMetrics } from \"./hooks/useBoxMetrics\"\nexport type { BoxMetrics } from \"./hooks/useBoxMetrics\"\n\n/**\n * Keyboard input hook.\n *\n * @example\n * ```tsx\n * import { useInput } from '@silvery/ag-react';\n *\n * useInput((input, key) => {\n * if (input === 'q') exit();\n * if (key.upArrow) moveUp();\n * if (key.return) submit();\n * });\n * ```\n */\nexport { useInput } from \"./hooks/useInput\"\n\n/**\n * App control hook - provides exit function.\n *\n * @example\n * ```tsx\n * import { useApp } from '@silvery/ag-react';\n *\n * const { exit } = useApp();\n * exit(); // Clean exit\n * exit(new Error('Failed')); // Exit with error\n * ```\n */\nexport { useApp } from \"./hooks/useApp\"\nexport { useExit } from \"./hooks/useExit\"\n\nexport { useStdout } from \"./hooks/useStdout\"\nexport { useStderr } from \"./hooks/useStderr\"\nexport { useFocusManager } from \"./hooks/useFocusManager\"\n\n// Focus system (tree-based)\n/**\n * Ink-compatible focus hook.\n *\n * Returns `{ isFocused, focus }`. Options: `{ isActive, autoFocus, id }`.\n * Uses silvery's FocusManager under the hood. For the richer silvery-specific\n * API (focus origin, blur, scope-aware), use `useFocusable()`.\n *\n * @example\n * ```tsx\n * const { isFocused, focus } = useFocus({ id: \"panel\", autoFocus: true })\n * ```\n */\nexport { useFocus } from \"./hooks/useFocus\"\nexport type { UseFocusOptions, UseFocusResult } from \"./hooks/useFocus\"\n\nexport { createFocusManager } from \"@silvery/ag/focus-manager\"\nexport type {\n FocusManager,\n FocusManagerOptions,\n FocusChangeCallback,\n FocusOrigin,\n FocusSnapshot,\n} from \"@silvery/ag/focus-manager\"\nexport {\n findFocusableAncestor,\n findEnclosingScope,\n getTabOrder,\n findByTestID,\n findSpatialTarget,\n getExplicitFocusLink,\n} from \"@silvery/ag/focus-queries\"\nexport {\n createKeyEvent,\n createFocusEvent,\n dispatchKeyEvent,\n dispatchFocusEvent,\n} from \"@silvery/ag/focus-events\"\nexport type { SilveryKeyEvent, SilveryFocusEvent, FocusEventProps } from \"@silvery/ag/focus-events\"\nexport { useFocusable } from \"./hooks/useFocusable\"\nexport type { UseFocusableResult } from \"./hooks/useFocusable\"\nexport { useFocusWithin } from \"./hooks/useFocusWithin\"\n// useFocus + types already exported above (line 370-371)\n\n// Terminal focus state\nexport { useTerminalFocused } from \"./hooks/useTerminalFocused\"\n\n// Modifier key tracking\nexport { useModifierKeys, getModifierState, lastModifierState } from \"./hooks/useModifierKeys\"\nexport type { ModifierState, UseModifierKeysOptions } from \"./hooks/useModifierKeys\"\n\n// Mouse cursor shape\nexport { useMouseCursor } from \"./hooks/useMouseCursor\"\n\nexport { useTerm, shallow } from \"./hooks/useTerm\"\nexport { useWindowSize } from \"./hooks/useWindowSize\"\nexport { useConsole } from \"./hooks/useConsole\"\nexport {\n useCursor,\n resetCursorState,\n getCursorState,\n subscribeCursor,\n createCursorStore,\n CursorProvider,\n} from \"./hooks/useCursor\"\nexport type { CursorPosition, CursorState, CursorAccessors, CursorStore } from \"./hooks/useCursor\"\nexport { PasteProvider, usePaste } from \"./hooks/usePaste\"\nexport type { PasteHandler } from \"./hooks/usePaste\"\nexport { usePasteEvents } from \"./hooks/usePasteEvents\"\n\n// Re-export copy extraction types from ag-term for convenience\nexport type {\n CopyEvent,\n SemanticCopyProvider,\n ClipboardData,\n PasteEvent,\n} from \"@silvery/ag-term/copy-extraction\"\nexport {\n createPasteEvent,\n createCopyProvider,\n getInternalClipboard,\n} from \"@silvery/ag-term/copy-extraction\"\nexport { useVirtualizer } from \"./hooks/useVirtualizer\"\nexport type { VirtualizerConfig, VirtualizerResult } from \"./hooks/useVirtualizer\"\nexport { useListItem } from \"./hooks/useListItem\"\nexport type { ListItemContext } from \"./hooks/useListItem\"\nexport { useInteractiveState } from \"./hooks/useInteractiveState\"\nexport { useSelection } from \"./hooks/useSelection\"\nexport { useFindState } from \"./hooks/useFindState\"\nexport { useCopyModeState } from \"./hooks/useCopyModeState\"\nexport { useDragState } from \"./hooks/useDragState\"\n\n// App-level Providers (Phase 4)\nexport { SearchProvider, useSearch, useSearchOptional } from \"./providers/SearchProvider\"\nexport type { Searchable, SearchContextValue } from \"./providers/SearchProvider\"\nexport { SearchBar } from \"./ui/components\"\n\n/**\n * Re-export React concurrent features for TUI responsiveness.\n *\n * @example\n * ```tsx\n * import { useTransition, useDeferredValue } from '@silvery/ag-react';\n *\n * function Search() {\n * const [query, setQuery] = useState('');\n * const deferredQuery = useDeferredValue(query);\n * const [isPending, startTransition] = useTransition();\n *\n * // Typing stays responsive while filtering is deferred\n * const filtered = useMemo(() => filterItems(deferredQuery), [deferredQuery]);\n *\n * // Heavy updates can be marked as low-priority\n * const handleChange = (value: string) => {\n * setQuery(value);\n * startTransition(() => {\n * loadMoreData(value);\n * });\n * };\n * }\n * ```\n */\nexport { useTransition, useDeferredValue, useId } from \"react\"\n\n// Runtime hook\nexport { useRuntime } from \"./hooks/useRuntime\"\n\n// Contexts for advanced usage (usually hooks are preferred)\nexport {\n CacheBackendContext,\n TermContext,\n FocusManagerContext,\n RuntimeContext,\n StderrContext,\n CapabilityRegistryContext,\n ChainAppContext,\n} from \"./context\"\nexport type {\n CacheBackend,\n RuntimeContextValue,\n BaseRuntimeEvents,\n CapabilityLookup,\n ChainAppContextValue,\n ChainInputStore,\n ChainPasteStore,\n ChainFocusEvents,\n ChainRawKeyObserver,\n ChainInputHandler,\n ChainPasteHandler,\n ChainFocusHandler,\n ChainRawKeyHandler,\n ChainKey,\n} from \"./context\"\n\n// Theming\nexport { ThemeProvider } from \"./ThemeProvider\"\nexport { useTheme } from \"./ThemeContext\"\nexport type { ThemeProviderProps, ThemeTokens } from \"./ThemeProvider\"\n// Sterling-aware themes: `ansi16DarkTheme`, `ansi16LightTheme`, `detectTheme`\n// come from @silvery/theme so every shipped Theme has Sterling flat tokens\n// (`border-default`, `fg-muted`, `bg-surface-default`, …) baked in. Without\n// this, `$border-default` would resolve to undefined → parseColor → null →\n// the terminal's default foreground (\"borders look white\" regression).\nexport {\n defaultDarkTheme,\n defaultLightTheme,\n builtinThemes,\n getThemeByName,\n ansi16DarkTheme,\n ansi16LightTheme,\n detectTheme,\n} from \"@silvery/theme\"\nexport { resolveThemeColor, deriveTheme, quantizeHex, pickColorLevel } from \"@silvery/ansi\"\nexport type { Theme, AnsiPrimary, DetectThemeOptions, ColorTier } from \"@silvery/ansi\"\nexport { generateTheme } from \"@silvery/ansi\"\n\n// =============================================================================\n// Re-exports from term/ansi\n// =============================================================================\n\n// Term primitives (so consumers don't need to import from term directly)\nexport { createTerm, term, patchConsole } from \"@silvery/ag-term/ansi\"\nexport type {\n Term,\n StyleChain,\n PatchedConsole,\n PatchConsoleOptions,\n ConsoleStats,\n ColorLevel,\n ConsoleEntry,\n} from \"@silvery/ag-term/ansi\"\n\n// Hit Registry (mouse support)\nexport {\n HitRegistry,\n HitRegistryContext,\n useHitRegistry,\n useHitRegion,\n useHitRegionCallback,\n resetHitRegionIdCounter,\n Z_INDEX,\n} from \"@silvery/ag-term/hit-registry\"\n\n// =============================================================================\n// Render Functions\n// =============================================================================\n\n/**\n * Render functions and layout engine management.\n *\n * NOTE: render() is async - it initializes the layout engine on first call.\n * Use renderSync() if you've already initialized the layout engine.\n *\n * @example\n * ```tsx\n * import { render, Box, Text, createTerm } from '@silvery/ag-react';\n *\n * // Interactive render with Term\n * using term = createTerm();\n * const { waitUntilExit, unmount, rerender } = await render(<App />, term);\n * await waitUntilExit();\n *\n * // Static render (no terminal needed)\n * await render(<Summary />);\n * await render(<Report />, { width: 120 });\n *\n * // Sync render (layout engine must be initialized)\n * import { renderSync, initYogaEngine, setLayoutEngine, createTerm } from '@silvery/ag-react';\n * const engine = await initYogaEngine();\n * setLayoutEngine(engine);\n * using term = createTerm();\n * renderSync(<App />, term);\n * ```\n */\nexport {\n render,\n renderSync,\n renderStatic,\n setLayoutEngine,\n isLayoutEngineInitialized,\n // Yoga adapter\n createYogaEngine,\n initYogaEngine,\n YogaLayoutEngine,\n // Flexily adapter\n createFlexilyEngine,\n FlexilyLayoutEngine,\n} from \"./render\"\nexport { renderString, renderStringSync, type RenderStringOptions } from \"./render-string\"\nexport { measureElement } from \"./measureElement\"\n\n// Accessibility (screen reader mode)\nexport { renderScreenReaderOutput } from \"./accessibility\"\nexport type { AriaState } from \"./accessibility\"\n\n// Term utilities (TermDef/resolveTermDef are internal — use createTerm() instead)\nexport { isTerm, createInputEvents } from \"@silvery/ag-term/term-def\"\n\n// ANSI escape sequences for terminal control\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 \"@silvery/ag-term/output\"\nexport type { CursorShape, MouseCursorShape } from \"@silvery/ag-term/output\"\n\n// Bracketed paste mode (DEC private mode 2004)\nexport {\n enableBracketedPaste,\n disableBracketedPaste,\n parseBracketedPaste,\n PASTE_START,\n PASTE_END,\n} from \"@silvery/ag-term/bracketed-paste\"\nexport type { BracketedPasteResult } from \"@silvery/ag-term/bracketed-paste\"\n\n// OSC 52 clipboard support\nexport {\n copyToClipboard,\n requestClipboard,\n parseClipboardResponse,\n} from \"@silvery/ag-term/clipboard\"\n\n// OSC 4 palette color query/set\nexport {\n queryPaletteColor,\n setPaletteColor,\n parsePaletteResponse,\n queryMultiplePaletteColors,\n} from \"@silvery/ag-term/osc-palette\"\n\n// OSC 133 semantic prompt markers\nexport { OSC133 } from \"@silvery/ag-term/osc-markers\"\n\n// Kitty protocol detection\nexport {\n detectKittySupport,\n detectKittyFromStdio,\n type KittyDetectResult,\n} from \"@silvery/ag-term/kitty-detect\"\n\n// Terminal capability detection\nexport { detectTerminalCaps, defaultCaps, type TerminalCaps } from \"@silvery/ag-term/terminal-caps\"\n\n// Terminal capability visual test\nexport {\n runTermtest,\n TERMTEST_SECTIONS,\n type TermtestSection,\n type TermtestOptions,\n} from \"@silvery/ag-term/termtest\"\n\n// Output-phase capability configuration (suppress unsupported SGR codes)\nexport {\n setOutputCaps,\n createOutputPhase,\n type OutputPhaseFn,\n type OutputCaps,\n} from \"@silvery/ag-term/pipeline/output-phase\"\n\n// Pipeline configuration\nexport { type PipelineConfig, type PipelineContext } from \"@silvery/ag-term/pipeline\"\n\n// withRender plugin\nexport { withRender, type RenderTerm } from \"@silvery/ag-term/plugins/with-render\"\n\n// Text sizing protocol (OSC 66) — PUA character width control\nexport {\n textSized,\n isPrivateUseArea,\n isTextSizingLikelySupported,\n detectTextSizingSupport,\n} from \"@silvery/ag-term/text-sizing\"\n\n// CSI 6n cursor position query\nexport { queryCursorPosition, queryCursorFromStdio } from \"@silvery/ag-term/cursor-query\"\n\n// OSC 10/11/12 terminal color queries\nexport {\n queryForegroundColor,\n queryBackgroundColor,\n queryCursorColor,\n setForegroundColor,\n setBackgroundColor,\n setCursorColor,\n resetForegroundColor,\n resetBackgroundColor,\n resetCursorColor,\n detectColorScheme,\n} from \"@silvery/ag-term/terminal-colors\"\n\n// DA1/DA2/DA3 + XTVERSION device attribute queries\nexport {\n queryPrimaryDA,\n querySecondaryDA,\n queryTertiaryDA,\n queryTerminalVersion,\n queryDeviceAttributes,\n type DeviceAttributes,\n} from \"@silvery/ag-term/device-attrs\"\n\n// Focus reporting (CSI ?1004h)\nexport {\n enableFocusReporting,\n disableFocusReporting,\n parseFocusEvent,\n} from \"@silvery/ag-term/focus-reporting\"\n\n// DECRQM mode query\nexport { queryMode, queryModes, DecMode } from \"@silvery/ag-term/mode-query\"\n\n// CSI 14t/18t pixel and text area size queries\nexport { queryTextAreaPixels, queryTextAreaSize, queryCellSize } from \"@silvery/ag-term/pixel-size\"\n\n// Layout engine types\nexport type { LayoutEngine, LayoutConstants } from \"@silvery/ag-term/layout-engine\"\nexport type { LayoutNode, MeasureFunc, MeasureMode } from \"@silvery/ag/layout-types\"\n\n// Render adapter types (RenderAdapter itself is internal — use term.paint() instead)\nexport type {\n RenderBuffer,\n RenderStyle,\n TextMeasurer,\n TextMeasureResult,\n TextMeasureStyle,\n BorderChars,\n} from \"@silvery/ag-term/render-adapter\"\n\n// Canvas adapter\nexport { createCanvasAdapter, CanvasRenderBuffer } from \"@silvery/ag-term/adapters/canvas-adapter\"\nexport type { CanvasAdapterConfig } from \"@silvery/ag-term/adapters/canvas-adapter\"\n\n// DOM adapter\nexport {\n createDOMAdapter,\n DOMRenderBuffer,\n injectDOMStyles,\n} from \"@silvery/ag-term/adapters/dom-adapter\"\nexport type { DOMAdapterConfig } from \"@silvery/ag-term/adapters/dom-adapter\"\n\n// App types (unified render API)\nexport type { App } from \"@silvery/ag-term/app\"\nexport type { AutoLocator, FilterOptions } from \"@silvery/test/auto-locator\"\nexport type { BoundTerm } from \"@silvery/ag-term/bound-term\"\n\n// Types\nexport type { BoxProps, BoxHandle } from \"./components/Box\"\nexport type { TextProps, TextHandle } from \"./components/Text\"\nexport type { Rect } from \"@silvery/ag/types\"\nexport type { Key, InputHandler, UseInputOptions } from \"./hooks/useInput\"\nexport {\n keyToName,\n keyToModifiers,\n parseHotkey,\n matchHotkey,\n parseKeypress,\n parseKey,\n emptyKey,\n} from \"@silvery/ag/keys\"\nexport type { ParsedKeypress, ParsedHotkey } from \"@silvery/ag/keys\"\nexport type { UseAppResult } from \"./hooks/useApp\"\nexport type { UseStdoutResult } from \"./hooks/useStdout\"\nexport type { UseStderrResult } from \"./hooks/useStderr\"\nexport type { UseFocusManagerResult } from \"./hooks/useFocusManager\"\nexport type { RenderOptions, Instance, RenderMode, NonTTYMode } from \"./render\"\nexport type { MeasureElementOutput } from \"./measureElement\"\nexport type {\n AgNode,\n // Event types\n Event,\n KeyEvent,\n MouseEvent,\n ResizeEvent,\n FocusEvent,\n BlurEvent,\n SignalEvent,\n CustomEvent,\n EventSource,\n} from \"@silvery/ag/types\"\nexport type { HitTarget, HitRegion } from \"@silvery/ag-term/hit-registry\"\n\n// Mouse parsing (SGR mode 1006)\nexport { parseMouseSequence, isMouseSequence, type ParsedMouse } from \"@silvery/ag-term/mouse\"\n\n// Mouse events (DOM-level)\nexport {\n hitTest,\n createMouseEvent,\n createWheelEvent,\n dispatchMouseEvent,\n processMouseEvent,\n createMouseEventProcessor,\n checkDoubleClick,\n createDoubleClickState,\n computeEnterLeave,\n type SilveryMouseEvent,\n type SilveryWheelEvent,\n type MouseEventProcessorOptions,\n type MouseEventProcessorState,\n} from \"@silvery/ag-term/mouse-events\"\nexport type { MouseEventProps } from \"@silvery/ag/mouse-event-types\"\n\n// Non-TTY utilities\nexport { isTTY, resolveNonTTYMode, stripAnsi } from \"@silvery/ag-term/non-tty\"\nexport type { NonTTYOptions, ResolvedNonTTYMode } from \"@silvery/ag-term/non-tty\"\n\n// =============================================================================\n// DevTools\n// =============================================================================\n\n/**\n * React DevTools integration.\n *\n * Optional connection to React DevTools standalone for debugging component trees.\n * Requires `react-devtools-core` (optional peer dependency).\n *\n * @example\n * ```ts\n * // Manual connection\n * import { connectDevTools } from '@silvery/ag-react';\n * await connectDevTools();\n *\n * // Or use env var: DEBUG_DEVTOOLS=1 bun run app.ts\n * ```\n */\nexport { connectDevTools, isDevToolsConnected } from \"@silvery/ag-term/devtools\"\n\n// =============================================================================\n// Inspector (silvery-native debug introspection)\n// =============================================================================\n\n/**\n * silvery Inspector — render pipeline debug introspection.\n *\n * Distinct from React DevTools. Provides silvery-specific info: render stats,\n * component tree with layout rects, dirty flags, focus path.\n *\n * @example\n * ```ts\n * // Manual\n * import { enableInspector } from '@silvery/ag-react';\n * enableInspector({ logFile: '/tmp/silvery-inspector.log' });\n *\n * // Or use env var: SILVERY_DEV=1 bun run app.ts\n * // With log file: SILVERY_DEV=1 SILVERY_DEV_LOG=/tmp/silvery.log bun run app.ts\n * ```\n */\nexport {\n enableInspector,\n disableInspector,\n isInspectorEnabled,\n inspectTree,\n inspectFrame,\n autoEnableInspector,\n} from \"@silvery/ag-term/inspector\"\nexport type { InspectorOptions } from \"@silvery/ag-term/inspector\"\n\n// =============================================================================\n// Unicode Text Utilities\n// =============================================================================\n\n/**\n * Unicode-aware text manipulation functions.\n * Handle ANSI codes, wide characters (CJK), and emoji correctly.\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 \"@silvery/ag-term/unicode\"\n\nexport type { StyledSegment } from \"@silvery/ag-term/unicode\"\n\n// Width measurer factory\nexport {\n createWidthMeasurer,\n createMeasurer,\n runWithMeasurer,\n type Measurer,\n type WidthMeasurer,\n} from \"@silvery/ag-term/unicode\"\n\n// Measurer composition (term + measurement)\nexport { withMeasurer, createPipeline, type MeasuredTerm } from \"@silvery/ag-term/measurer\"\n\n// =============================================================================\n// Text Cursor Utilities\n// =============================================================================\n\n/**\n * Pure functions for mapping between flat character offsets and visual\n * positions in word-wrapped text. Uses the same wrapText() as the\n * rendering pipeline, guaranteeing cursor positions match display.\n *\n * Architecture layer 0 — no state, no hooks, no components.\n *\n * @example\n * ```ts\n * import { cursorToRowCol, cursorMoveDown } from '@silvery/ag-react'\n *\n * const { row, col } = cursorToRowCol(\"hello world\", 5, 8)\n * const next = cursorMoveDown(\"hello world\\nfoo\", 3, 8)\n * ```\n */\nexport {\n cursorToRowCol,\n getWrappedLines,\n rowColToCursor,\n cursorMoveUp,\n cursorMoveDown,\n countVisualLines,\n} from \"@silvery/create/text-cursor\"\nexport type { WrappedLine } from \"@silvery/create/text-cursor\"\n\n// =============================================================================\n// Edit Context\n// =============================================================================\n\n/**\n * Terminal Edit Context -- W3C EditContext-aligned interface for terminal\n * text editing, plus invertible text operations for undo/redo.\n *\n * @example\n * ```ts\n * import { createTermEditContext, applyTextOp, invertTextOp } from '@silvery/ag-react'\n *\n * using ctx = createTermEditContext({ text: \"hello\", wrapWidth: 40 })\n * ctx.onTextUpdate((op) => undoStack.push(op))\n * ctx.insertChar(\"!\") // \"hello!\"\n *\n * const op: TextOp = { type: \"insert\", offset: 0, text: \"hi \" }\n * const result = applyTextOp(\"world\", op) // \"hi world\"\n * const inv = invertTextOp(op) // delete \"hi \" at 0\n * ```\n */\nexport { createTermEditContext } from \"./edit-context\"\nexport type { EditContextLike, TermEditContext, TermEditContextOptions } from \"./edit-context\"\n\nexport { applyTextOp, invertTextOp, mergeTextOps } from \"@silvery/create/text-ops\"\nexport type { TextOp } from \"@silvery/create/text-ops\"\n\nexport { useEditContext, activeEditContextRef, activeEditTargetRef } from \"./hooks/use-edit-context\"\nexport type {\n UseEditContextOptions,\n UseEditContextResult,\n EditTarget,\n} from \"./hooks/use-edit-context\"\n\n// =============================================================================\n// Scroll Utilities\n// =============================================================================\n\n/**\n * Scroll utility for edge-based scrolling.\n *\n * @example\n * ```tsx\n * import { calcEdgeBasedScrollOffset } from '@silvery/ag-react';\n *\n * const newOffset = calcEdgeBasedScrollOffset(\n * selectedIndex,\n * currentOffset,\n * visibleCount,\n * totalCount,\n * padding // optional, default: 1\n * );\n * ```\n */\nexport { calcEdgeBasedScrollOffset } from \"@silvery/ag-term/scroll-utils\"\n\n// Scroll Region Optimization (DECSTBM)\nexport {\n setScrollRegion,\n resetScrollRegion,\n scrollUp,\n scrollDown,\n moveCursor,\n supportsScrollRegions,\n} from \"@silvery/ag-term/scroll-region\"\nexport type { ScrollRegionConfig } from \"@silvery/ag-term/scroll-region\"\n\n// =============================================================================\n// Plugin Composition (SlateJS-style)\n// =============================================================================\n\n/**\n * Plugin composition for command systems.\n *\n * @example\n * ```tsx\n * import { withCommands, withKeybindings, render } from '@silvery/ag-react';\n *\n * const app = withKeybindings(withCommands(render(<Board />), {\n * registry: commandRegistry,\n * getContext: () => buildContext(state),\n * handleAction: (action) => dispatch(action),\n * getKeybindings: () => keybindings,\n * }), {\n * bindings: keybindings,\n * getKeyContext: () => ({ mode: 'normal', hasSelection: false }),\n * });\n *\n * await app.cmd.down(); // Direct command invocation\n * await app.press('j'); // Key → command → action\n * console.log(app.cmd.down.help); // Command metadata\n * ```\n */\nexport { withCommands } from \"@silvery/commands/with-commands\"\nexport type {\n WithCommandsOptions,\n CommandDef,\n CommandRegistryLike,\n CommandInfo,\n Command,\n Cmd,\n AppWithCommands,\n AppState,\n KeybindingDef,\n} from \"@silvery/commands/with-commands\"\n\nexport { withKeybindings } from \"@silvery/commands/with-keybindings\"\nexport type {\n WithKeybindingsOptions,\n KeybindingContext,\n ExtendedKeybindingDef,\n} from \"@silvery/commands/with-keybindings\"\n\n// Diagnostic tools - prefer importing from '@silvery/ag-term/toolbelt' for new code\nexport { withDiagnostics, VirtualTerminal } from \"@silvery/ag-term/plugins/with-diagnostics\"\nexport type { DiagnosticOptions } from \"@silvery/ag-term/plugins/with-diagnostics\"\n\n// Scheduler errors (for catching incremental render mismatches)\nexport { IncrementalRenderMismatchError } from \"@silvery/ag-term/scheduler\"\n\n// =============================================================================\n// Input Layer Stack\n// =============================================================================\n\n/**\n * Input layer stack for DOM-style event bubbling.\n *\n * Layers register synchronously via useLayoutEffect and receive input\n * in child-first order (like DOM event bubbling from target to ancestors).\n *\n * @example\n * ```tsx\n * import { InputLayerProvider, useInputLayer } from '@silvery/ag-react';\n *\n * function App() {\n * return (\n * <InputLayerProvider>\n * <Board>\n * <Dialog />\n * </Board>\n * </InputLayerProvider>\n * );\n * }\n *\n * function Dialog() {\n * useInputLayer('dialog-input', (input, key) => {\n * if (key.backspace) { ... return true } // consumed\n * if (input >= ' ') { ... return true } // consumed\n * return false // bubble (e.g., escape to parent)\n * });\n * }\n * ```\n *\n * @see docs/future/silvery-command-api-research.md\n */\nexport {\n InputLayerProvider,\n InputLayerContext,\n useInputLayer,\n useInputLayerContext,\n} from \"./contexts/InputLayerContext\"\nexport type {\n InputLayerHandler,\n InputLayer,\n InputLayerContextValue,\n InputLayerProviderProps,\n} from \"./contexts/InputLayerContext\"\n\nexport { InputBoundary } from \"./contexts/InputBoundary\"\nexport type { InputBoundaryProps } from \"./contexts/InputBoundary\"\n\n// =============================================================================\n// Position Registry (2D Grid Virtualization)\n// =============================================================================\n\n/**\n * Position tracking for 2D virtualized grid layouts.\n *\n * Items auto-register on mount and auto-unregister on unmount,\n * eliminating stale-entry bugs in virtualized lists.\n *\n * @example\n * ```tsx\n * import { PositionRegistryProvider, GridCell, usePositionRegistry } from '@silvery/ag-react';\n *\n * <PositionRegistryProvider>\n * {columns.map((col, i) => (\n * <ListView items={col.items} renderItem={(item, idx) => (\n * <GridCell sectionIndex={i} itemIndex={idx}>\n * <Card {...item} />\n * </GridCell>\n * )} />\n * ))}\n * </PositionRegistryProvider>\n * ```\n */\nexport {\n PositionRegistryProvider,\n usePositionRegistry,\n createPositionRegistry,\n} from \"./hooks/usePositionRegistry\"\nexport type { PositionRegistry, ScrollRect } from \"./hooks/usePositionRegistry\"\nexport { useGridPosition } from \"./hooks/useGridPosition\"\nexport { GridCell } from \"./ui/components\"\nexport type { GridCellProps } from \"./ui/components\"\n\n// =============================================================================\n// Animation\n// =============================================================================\n\n/**\n * Animation utilities for smooth terminal UI animations (~30fps).\n *\n * @example\n * ```tsx\n * import { useAnimation, easings } from '@silvery/ag-react';\n *\n * function FadeIn() {\n * const { value } = useAnimation({ duration: 300, easing: \"easeOut\" });\n * return <Text dimColor={value < 1}>Hello</Text>;\n * }\n * ```\n *\n * Note: `useAnimatedTransition` is the animation interpolation hook.\n * React's `useTransition` (concurrent mode) is exported separately above.\n */\nexport {\n easings,\n resolveEasing,\n useAnimation,\n useInterval,\n useTimeout,\n useLatest,\n} from \"./ui/animation\"\nexport { useAnimatedTransition } from \"./ui/animation\"\nexport type {\n EasingFn,\n EasingName,\n UseAnimationOptions,\n UseAnimationResult,\n UseTransitionOptions,\n} from \"./ui/animation\"\n\n// =============================================================================\n// TEA State Machines\n// =============================================================================\n\n/**\n * TEA (The Elm Architecture) for React.\n *\n * `useTea` is like `useReducer` but the reducer can return `[state, effects]`.\n * Effects are plain data — timer effects (delay, interval, cancel) are built-in.\n * All timers auto-cleanup on unmount. Pure update functions are testable with `collect()`.\n *\n * ```tsx\n * import { useTea } from \"silvery\"\n * import { fx, collect } from \"silvery\"\n *\n * function update(state, msg) {\n * switch (msg.type) {\n * case \"start\": return [{ ...state, phase: \"go\" }, [fx.delay(1000, { type: \"done\" })]]\n * case \"done\": return { ...state, phase: \"idle\" }\n * }\n * }\n *\n * // In React:\n * const [state, send] = useTea(initialState, update)\n *\n * // In tests (no React, no timers):\n * const [newState, effects] = collect(update(state, { type: \"start\" }))\n * expect(effects).toContainEqual(fx.delay(1000, { type: \"done\" }))\n * ```\n */\nexport { useTea } from \"./ui/hooks/useTea\"\nexport { fx, collect } from \"@silvery/create\"\nexport type { TeaResult, EffectLike, TimerEffect } from \"@silvery/create\"\n\n// Reactive theme\nexport { useColorScheme } from \"./hooks/useColorScheme\"\nexport { ReactiveThemeProvider } from \"./ReactiveThemeProvider\"\nexport { useActiveScheme } from \"./hooks/useActiveScheme\"\nexport type { ActiveScheme } from \"@silvery/ansi\"\nexport { ActiveSchemeContext } from \"./ThemeContext\"\n"],"mappings":"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"}