silvery 0.17.0 → 0.17.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.
- package/dist/UPNG-AVSMjiFE.mjs +5076 -0
- package/dist/UPNG-AVSMjiFE.mjs.map +1 -0
- package/dist/__vite-browser-external-2447137e-D3GdsvS_.mjs +6 -0
- package/dist/__vite-browser-external-2447137e-D3GdsvS_.mjs.map +1 -0
- package/dist/animation-C_PTO0uH.mjs +304 -0
- package/dist/animation-C_PTO0uH.mjs.map +1 -0
- package/dist/ansi-CXLE_pt1.mjs +71 -0
- package/dist/ansi-CXLE_pt1.mjs.map +1 -0
- package/dist/ansi-zmNzgkPB.d.mts +49 -0
- package/dist/ansi-zmNzgkPB.d.mts.map +1 -0
- package/dist/apng-DCWY913R.mjs +3 -0
- package/dist/apng-ENBAJk-H.mjs +70 -0
- package/dist/apng-ENBAJk-H.mjs.map +1 -0
- package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
- package/dist/backend-CkIkIHR-.mjs +13396 -0
- package/dist/backend-CkIkIHR-.mjs.map +1 -0
- package/dist/backends-CkvbG3js.mjs +1181 -0
- package/dist/backends-CkvbG3js.mjs.map +1 -0
- package/dist/backends-CyJqNLeK.mjs +3 -0
- package/dist/chunk-BSw8zbkd.mjs +37 -0
- package/dist/cli-B-k7Bm56.mjs +4 -0
- package/dist/context-QreF3UHr.mjs +64 -0
- package/dist/context-QreF3UHr.mjs.map +1 -0
- package/dist/derive-D7bFJdfU.d.mts +28 -0
- package/dist/derive-D7bFJdfU.d.mts.map +1 -0
- package/dist/devtools-CscuKaDK.mjs +89 -0
- package/dist/devtools-CscuKaDK.mjs.map +1 -0
- package/dist/devtools-D4oGc6LY.mjs +2 -0
- package/dist/eta-DLiVPaSD.mjs +110 -0
- package/dist/eta-DLiVPaSD.mjs.map +1 -0
- package/dist/flexily-zero-adapter-DmG4Ge8t.mjs +3376 -0
- package/dist/flexily-zero-adapter-DmG4Ge8t.mjs.map +1 -0
- package/dist/flexily-zero-adapter-GHwEW11s.mjs +2 -0
- package/dist/gif-BaJNREpP.mjs +3 -0
- package/dist/gif-Bp6fIyN3.mjs +73 -0
- package/dist/gif-Bp6fIyN3.mjs.map +1 -0
- package/dist/gifenc-GiVCZ9-3.mjs +730 -0
- package/dist/gifenc-GiVCZ9-3.mjs.map +1 -0
- package/dist/image-Dx7gYjkq.mjs +346 -0
- package/dist/image-Dx7gYjkq.mjs.map +1 -0
- package/dist/index-CBcSpGSM.d.mts +3416 -0
- package/dist/index-CBcSpGSM.d.mts.map +1 -0
- package/dist/index-DCVL3jHo.d.mts +634 -0
- package/dist/index-DCVL3jHo.d.mts.map +1 -0
- package/dist/index-p-wBs_wH.d.mts +175 -0
- package/dist/index-p-wBs_wH.d.mts.map +1 -0
- package/dist/index.d.mts +7296 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +9399 -0
- package/dist/index.mjs.map +1 -0
- package/dist/key-mapping-BsUHe_nk.mjs +3 -0
- package/dist/key-mapping-DsyfLEdC.mjs +132 -0
- package/dist/key-mapping-DsyfLEdC.mjs.map +1 -0
- package/dist/layout-engine-B3dsnVLU.mjs +50 -0
- package/dist/layout-engine-B3dsnVLU.mjs.map +1 -0
- package/dist/layout-engine-D_lSR4i9.mjs +2 -0
- package/dist/multi-progress-C0-rkn86.d.mts +180 -0
- package/dist/multi-progress-C0-rkn86.d.mts.map +1 -0
- package/dist/multi-progress-CQVB9lES.mjs +219 -0
- package/dist/multi-progress-CQVB9lES.mjs.map +1 -0
- package/dist/node-Dedx-6xF.mjs +1085 -0
- package/dist/node-Dedx-6xF.mjs.map +1 -0
- package/dist/pipeline-DDOPrjuY.mjs +4387 -0
- package/dist/pipeline-DDOPrjuY.mjs.map +1 -0
- package/dist/progress-bar-COPSBlT9.mjs +155 -0
- package/dist/progress-bar-COPSBlT9.mjs.map +1 -0
- package/dist/reconciler-2lp5VXK7.mjs +16506 -0
- package/dist/reconciler-2lp5VXK7.mjs.map +1 -0
- package/dist/render-string-BXvxTg5P.mjs +201 -0
- package/dist/render-string-BXvxTg5P.mjs.map +1 -0
- package/dist/render-string-hvfpVtoP.mjs +2 -0
- package/dist/resvg-js-V6oMi8CY.mjs +203 -0
- package/dist/resvg-js-V6oMi8CY.mjs.map +1 -0
- package/dist/runtime-BjDHNTxJ.mjs +8723 -0
- package/dist/runtime-BjDHNTxJ.mjs.map +1 -0
- package/dist/runtime.d.mts +2 -0
- package/dist/runtime.mjs +3 -0
- package/dist/spinner-Cgej6Vnb.d.mts +127 -0
- package/dist/spinner-Cgej6Vnb.d.mts.map +1 -0
- package/dist/spinner-DSByknyx.mjs +298 -0
- package/dist/spinner-DSByknyx.mjs.map +1 -0
- package/dist/src-9B5k0JmY.mjs +1629 -0
- package/dist/src-9B5k0JmY.mjs.map +1 -0
- package/dist/src-C9f3hiVG.mjs +3620 -0
- package/dist/src-C9f3hiVG.mjs.map +1 -0
- package/dist/src-fJVbhdn-.mjs +816 -0
- package/dist/src-fJVbhdn-.mjs.map +1 -0
- package/dist/theme.d.mts +115 -0
- package/dist/theme.d.mts.map +1 -0
- package/dist/theme.mjs +8 -0
- package/dist/theme.mjs.map +1 -0
- package/dist/types-Bhj5QkIQ.mjs +13 -0
- package/dist/types-Bhj5QkIQ.mjs.map +1 -0
- package/dist/types-CDgkE-Rw.d.mts +241 -0
- package/dist/types-CDgkE-Rw.d.mts.map +1 -0
- package/dist/ui/animation.d.mts +2 -0
- package/dist/ui/animation.mjs +2 -0
- package/dist/ui/ansi.d.mts +2 -0
- package/dist/ui/ansi.mjs +2 -0
- package/dist/ui/cli.d.mts +5 -0
- package/dist/ui/cli.mjs +7 -0
- package/dist/ui/display.d.mts +35 -0
- package/dist/ui/display.d.mts.map +1 -0
- package/dist/ui/display.mjs +123 -0
- package/dist/ui/display.mjs.map +1 -0
- package/dist/ui/image.d.mts +2 -0
- package/dist/ui/image.mjs +2 -0
- package/dist/ui/input.d.mts +184 -0
- package/dist/ui/input.d.mts.map +1 -0
- package/dist/ui/input.mjs +285 -0
- package/dist/ui/input.mjs.map +1 -0
- package/dist/ui/progress.d.mts +249 -0
- package/dist/ui/progress.d.mts.map +1 -0
- package/dist/ui/progress.mjs +858 -0
- package/dist/ui/progress.mjs.map +1 -0
- package/dist/ui/react.d.mts +280 -0
- package/dist/ui/react.d.mts.map +1 -0
- package/dist/ui/react.mjs +413 -0
- package/dist/ui/react.mjs.map +1 -0
- package/dist/ui/utils.d.mts +86 -0
- package/dist/ui/utils.d.mts.map +1 -0
- package/dist/ui/utils.mjs +2 -0
- package/dist/ui/wrappers.d.mts +3 -0
- package/dist/ui/wrappers.mjs +2 -0
- package/dist/ui.d.mts +6 -0
- package/dist/ui.mjs +7 -0
- package/dist/useLatest-BMIYXd6e.d.mts +154 -0
- package/dist/useLatest-BMIYXd6e.d.mts.map +1 -0
- package/dist/useLayout-BG2cGl15.mjs +139 -0
- package/dist/useLayout-BG2cGl15.mjs.map +1 -0
- package/dist/with-text-input-CmHf_9d6.d.mts +284 -0
- package/dist/with-text-input-CmHf_9d6.d.mts.map +1 -0
- package/dist/wrapper-Dqh0zi2W.mjs +3527 -0
- package/dist/wrapper-Dqh0zi2W.mjs.map +1 -0
- package/dist/wrappers-hhL8EQ_n.mjs +810 -0
- package/dist/wrappers-hhL8EQ_n.mjs.map +1 -0
- package/dist/yoga-adapter-BJ9SOhTY.mjs +245 -0
- package/dist/yoga-adapter-BJ9SOhTY.mjs.map +1 -0
- package/dist/yoga-adapter-Daq6-dw1.mjs +2 -0
- package/package.json +48 -75
- package/CHANGELOG.md +0 -319
- package/dist/chalk.js +0 -4
- package/dist/index.js +0 -270
- package/dist/ink.js +0 -142
- package/dist/runtime.js +0 -135
- package/dist/theme.js +0 -7
- package/dist/ui/animation.js +0 -3
- package/dist/ui/ansi.js +0 -3
- package/dist/ui/cli.js +0 -9
- package/dist/ui/display.js +0 -4
- package/dist/ui/image.js +0 -4
- package/dist/ui/input.js +0 -3
- package/dist/ui/progress.js +0 -9
- package/dist/ui/react.js +0 -4
- package/dist/ui/utils.js +0 -3
- package/dist/ui/wrappers.js +0 -15
- package/dist/ui.js +0 -18
- package/src/index.ts +0 -73
- package/src/runtime.ts +0 -4
- package/src/theme.ts +0 -4
- package/src/ui/animation.ts +0 -2
- package/src/ui/ansi.ts +0 -2
- package/src/ui/cli.ts +0 -3
- package/src/ui/display.ts +0 -2
- package/src/ui/image.ts +0 -2
- package/src/ui/input.ts +0 -2
- package/src/ui/progress.ts +0 -2
- package/src/ui/react.ts +0 -2
- package/src/ui/utils.ts +0 -2
- package/src/ui/wrappers.ts +0 -2
- package/src/ui.ts +0 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["DEFAULT_SCROLL_PADDING","DEFAULT_OVERSCAN","DEFAULT_MAX_RENDERED","DEFAULT_OVERSCAN","DEFAULT_MAX_RENDERED","noopSubscribe","resolveNode","noop","noop","clampScroll","DEFAULT_WIDTH","VARIANT_COLORS","DEFAULT_CHAR","DEFAULT_WIDTH","log","process","render","useId","reactRender"],"sources":["../packages/ag-react/src/components/Box.tsx","../packages/ag-react/src/hooks/useVirtualizer.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/components/Text.tsx","../packages/ag-react/src/ui/components/HorizontalVirtualList.tsx","../packages/ag-react/src/ui/components/SplitView.tsx","../packages/ag-react/src/components/Fill.tsx","../packages/ag-react/src/hooks/useModifierKeys.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/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/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-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/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/PickerList.tsx","../packages/ag-react/src/ui/components/PickerDialog.tsx","../packages/ag-react/src/ui/components/Toggle.tsx","../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/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/hooks/useTerm.ts","../packages/ag-react/src/hooks/useWindowSize.ts","../packages/ag-react/src/ThemeProvider.tsx","../packages/ag-term/src/hit-registry.ts","../packages/ag-react/src/runtime-subscribers.ts","../packages/ag-react/src/render.tsx","../packages/ag-react/src/measureElement.ts","../packages/ag-react/src/accessibility.ts","../packages/ag-term/src/plugins/with-render.ts","../packages/ag-react/src/edit-context.ts","../packages/create/src/text-ops.ts","../packages/ag-react/src/hooks/use-edit-context.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-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/ink/src/with-ink-cursor.ts","../packages/ink/src/with-ink-focus.ts","../packages/ag-react/src/ReactiveThemeProvider.tsx","../packages/ag-react/src/exports.ts","../src/index.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 { NodeContext } from \"../context\"\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(props: BoxProps, ref: ForwardedRef<BoxHandle>): 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 to layout changes\n useLayoutEffect(() => {\n if (!onLayout || !node) return\n\n // Create subscriber callback\n const handleLayoutChange = () => {\n const layout = node.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 onLayout(layout)\n }\n }\n\n // Subscribe to layout changes\n node.layoutSubscribers.add(handleLayoutChange)\n\n // Call immediately if we already have layout\n if (node.boxRect) {\n handleLayoutChange()\n }\n\n return () => {\n node.layoutSubscribers.delete(handleLayoutChange)\n }\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 * 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(selectedIndexRef.current, 0, estimatedVisibleCount, count, scrollPadding),\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 = visible items + overscan buffer, capped at maxRendered\n const renderCount = Math.min(estimatedVisibleCount + 2 * overscan, maxRendered)\n\n // Center the render window around the selected item\n const viewportCenter = selectedIndexRef.current\n const halfWindow = Math.floor(renderCount / 2)\n let start = Math.max(0, viewportCenter - halfWindow)\n const end = Math.min(count, start + renderCount)\n\n // Adjust start if we hit the end\n if (end === count) {\n start = Math.max(0, end - renderCount)\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 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 * 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, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } 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 = pending.state.currentMatch >= 0 ? pending.state.matches[pending.state.currentMatch] : 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 \"@silvery/ag-react/hooks/useVirtualizer\"\nimport { useInput } from \"@silvery/ag-react/hooks/useInput\"\nimport { Box } from \"@silvery/ag-react/components/Box\"\nimport { CacheBackendContext, StdoutContext, TermContext } from \"@silvery/ag-react/context\"\nimport { renderStringSync } from \"@silvery/ag-react/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 \"@silvery/ag-react/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 /** 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 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 = 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\" ? (cacheBackendFromContext === \"terminal\" ? \"terminal\" : \"virtual\") : 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 (cachedCount > prevCachedRef.current && (cacheMode === \"virtual\" || cacheMode === \"terminal\")) {\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 = 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 { range, leadingHeight, trailingHeight, scrollOffset, scrollToItem, measureItem, 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 ? Math.max(0, Math.min(adjustedScrollTo, activeItems.length - 1)) : 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 return (\n <React.Fragment key={key}>\n <MeasuredItem itemKey={measureKey} measureItem={measureItem}>\n {renderItem(item, originalIndex, meta)}\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 } = 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 * 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 } from \"react\"\nimport type { AgNode, TextProps as TextPropsType } from \"@silvery/ag/types\"\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\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 */\nexport const Text = forwardRef(function Text(props: TextProps, ref: ForwardedRef<TextHandle>): JSX.Element {\n const { children, ...styleProps } = props\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 \"@silvery/ag-react/hooks/useVirtualization\"\nimport { Box } from \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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(items, displayScrollOffset, effectiveViewport, itemWidth, gap)\n const wouldShowRight = 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(items, displayScrollOffset, scrollTo, actualViewport, itemWidth, gap)\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(items, displayScrollOffset, effectiveViewport, itemWidth, gap)\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=\"$inverse\" backgroundColor=\"$inverse-bg\">\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=\"$inverse\" backgroundColor=\"$inverse-bg\">\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 \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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 { node, renderPane, focusedPaneId, showBorders, focusedBorderColor, unfocusedBorderColor, 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 { id, renderPane, isFocused, showBorders, focusedBorderColor, unfocusedBorderColor, title } = 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 * 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, { type JSX, type ReactNode, useMemo, Children, isValidElement, cloneElement } 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 { RuntimeContext, type RuntimeContextValue } 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 stores = new WeakMap<RuntimeContextValue, ModifierStore>()\n\nfunction getOrCreateStore(rt: RuntimeContextValue): ModifierStore {\n let store = stores.get(rt)\n if (store) return store\n\n let state = INITIAL\n const listeners = new Set<() => void>()\n\n function notify() {\n for (const cb of listeners) cb()\n }\n\n // Track modifiers from every key event\n rt.on(\"input\", (_input: string, key: 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 // Reset on terminal focus loss (avoids stuck modifiers)\n rt.on(\"focus\", (focused: boolean) => {\n if (!focused && (state.super || state.ctrl || state.alt || state.shift)) {\n state = INITIAL\n lastModifierState = INITIAL\n notify()\n }\n })\n\n store = {\n subscribe: (cb) => {\n listeners.add(cb)\n return () => listeners.delete(cb)\n },\n getSnapshot: () => state,\n }\n stores.set(rt, 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 */\nexport function getModifierState(rt: RuntimeContextValue): ModifierState {\n const store = stores.get(rt)\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 rt = useContext(RuntimeContext)\n\n // Memoize the store instance (stable across renders for same runtime)\n const store = useMemo(() => (rt ? getOrCreateStore(rt) : null), [rt])\n\n return useSyncExternalStore(\n enabled && store ? store.subscribe : noopSubscribe,\n store ? store.getSnapshot : () => INITIAL,\n () => INITIAL, // server snapshot\n )\n}\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 { RuntimeContext } 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 rt = useContext(RuntimeContext)\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 ;(rt as any)?.emit(\"link:open\", href)\n e.preventDefault()\n }\n onClick?.(e)\n },\n [armed, needsModifier, hovered, href, onClick, rt],\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 \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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 = 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=\"$error\" padding={1} flexDirection=\"column\">\n <Text color=\"$error\" bold>\n Error\n </Text>\n {error && <Text color=\"$error\">{error.message}</Text>}\n {errorInfo?.componentStack && (\n <Text dim 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 \"@silvery/ag-react/hooks/useConsole\"\nimport { Text } from \"@silvery/ag-react/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 \"@silvery/ag-react/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 * 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 { NodeContext } from \"../context\"\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 on the target node's layoutSubscribers set.\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 node.layoutSubscribers.add(forceUpdate)\n return () => {\n node.layoutSubscribers.delete(forceUpdate)\n }\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<E extends BaseRuntimeEvents = BaseRuntimeEvents>(): 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 * 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 = 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 ? (((node.props as Record<string, unknown>).testID as string | undefined) ?? null) : 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 { useState, useEffect } from \"react\"\nimport { useRuntime } from \"./useRuntime\"\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 rt = useRuntime()\n\n useEffect(() => {\n if (!rt) return\n return rt.on(\"focus\", (isFocused: boolean) => {\n setFocused(isFocused)\n })\n }, [rt])\n\n return focused\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({ handler, children }: { handler: PasteHandler; children: ReactNode }) {\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 { RuntimeContext } 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 rt = useContext(RuntimeContext)\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 (!rt) return\n\n return rt.on(\"paste\", (text: string) => {\n const handler = handlerRef.current\n if (!handler) return\n\n const clipboard = getInternalClipboard()\n const event = createPasteEvent(text, clipboard)\n handler.onPaste(event)\n })\n }, [rt])\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 import(\"@silvery/headless/find\").FindState | 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 import(\"@silvery/headless/copy-mode\").CopyModeState | 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({ children, freeze, isCached, index, nearCache }: ListItemProviderProps) {\n const value = useMemo(() => ({ freeze, isCached, index, nearCache }), [freeze, isCached, index, nearCache])\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 ColorSchemeDetector 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 ColorSchemeDetector 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 ColorSchemeDetector 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 ColorSchemeDetector 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: 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 // 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 \"@silvery/ag-react/hooks\"\nimport { killRing, addToKillRing, handleReadlineKey, type YankState } from \"@silvery/ag-react/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 * 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 \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/components/Text\"\nimport { useReadline } from \"./useReadline\"\nimport { useFocusable } from \"@silvery/ag-react/hooks/useFocusable\"\nimport { useCursor } from \"@silvery/ag-react/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 /** Whether input is focused/active (overrides focus system) */\n isActive?: boolean\n /** Prompt prefix (e.g., \"$ \" or \"> \") */\n prompt?: string\n /** Prompt color */\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\") */\n borderColor?: string\n /** Border color when focused (default: \"$focusborder\") */\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 isActive: isActiveProp,\n prompt = \"\",\n promptColor = \"$control\",\n color,\n cursorStyle = \"block\",\n showUnderline = false,\n underlineWidth = 40,\n mask,\n borderStyle: borderStyleProp,\n borderColor: borderColorProp = \"$border\",\n focusBorderColor = \"$focusborder\",\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 const internalChangeRef = useRef(false)\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 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.\n // Internal changes (from editing) already have correct cursor position.\n const [lastControlledValue, setLastControlledValue] = useState(controlledValue)\n useEffect(() => {\n if (isControlled && controlledValue !== lastControlledValue) {\n if (internalChangeRef.current) {\n // Change originated from our own editing — readline already has correct state\n internalChangeRef.current = false\n } else {\n // External change — sync readline (cursor goes to end)\n readline.setValue(controlledValue ?? \"\")\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\" ? <Text underline>{displayAtCursor}</Text> : <Text inverse>{displayAtCursor}</Text>\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 <>\n {cursorStyle === \"underline\" ? (\n <Text underline dimColor>\n {placeholder[0]}\n </Text>\n ) : (\n <Text inverse dimColor>\n {placeholder[0]}\n </Text>\n )}\n <Text dimColor>{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 dimColor>{\"─\".repeat(underlineWidth)}</Text>}\n </Box>\n )\n }\n\n return (\n <Box focusable testID={testID} flexDirection=\"column\" onMouseDown={handleMouseDown}>\n {inputContent}\n {showUnderline && <Text dimColor>{\"─\".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(lines: WrappedLine[], cursor: number): { 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(text: string, wrapWidth: number, measurer?: Measurer): 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 (offset < text.length && text[offset] === \" \" && wLine.length > 0 && text[offset] !== wLine[0]) {\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(text: string, row: number, col: number, wrapWidth: number, measurer?: Measurer): 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 \"@silvery/ag-react/hooks/useInput\"\nimport {\n addToKillRing,\n findNextWordEnd,\n findPrevWordStart,\n handleReadlineKey,\n type YankState,\n} from \"@silvery/ag-react/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 = 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 (maxLength !== undefined && newValue.length > maxLength && newValue.length > stateRef.current.value.length) {\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 = 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 ? findNextWordEnd(value, cursor) : 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(targetLine.startOffset + Math.min(targetX, targetLine.line.length), value, true)\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(targetLine.startOffset + Math.min(targetX, targetLine.line.length), value, true)\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(targetLine.startOffset + Math.min(targetX, targetLine.line.length), value, false)\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(targetLine.startOffset + Math.min(targetX, targetLine.line.length), value, false)\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 (maxLength !== undefined && result.value.length > maxLength && result.value.length > value.length) {\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 \"@silvery/ag-react/hooks/useLayout\"\nimport { useFocusable } from \"@silvery/ag-react/hooks/useFocusable\"\nimport { useCursor } from \"@silvery/ag-react/hooks/useCursor\"\nimport { Box } from \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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\") */\n borderColor?: string\n /** Border color when focused (default: \"$focusborder\") */\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\",\n focusBorderColor = \"$focusborder\",\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 dimColor>{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 = ta.selection && lineStart < ta.selection.end && lineEnd > ta.selection.start\n\n if (disabled) {\n return (\n <Text key={absoluteRow} dimColor>\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>{selected || (selEnd === wl.line.length && isCursorRow ? \" \" : \"\")}</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 \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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(() => getWrappedLines(value, effectiveWrapWidth), [value, effectiveWrapWidth])\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 dimColor>{placeholder}</Text>\n </Box>\n )\n }\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>{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 ? wrappedLines.slice(currentScroll, currentScroll + height) : 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\" ? <Text inverse>{atCursor}</Text> : <Text underline>{atCursor}</Text>}\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 \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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\" ? <Text inverse>{cursorChar}</Text> : <Text underline>{cursorChar}</Text>}\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 \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ModalDialogProps extends Omit<BoxProps, \"children\" | \"flexDirection\"> {\n /** Border color (default: $border). Cyan 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: $primary). 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 /** Dialog children */\n children: React.ReactNode\n}\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(title: string, hotkey: string, color?: string): 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 dimColor bold={false}>\n [\n </Text>\n <Text bold>{matched}</Text>\n <Text dimColor 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 dimColor bold={false}>\n [\n </Text>\n <Text bold>{hotkey}</Text>\n <Text dimColor 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\",\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 children,\n ...boxProps\n}: ModalDialogProps): React.ReactElement {\n const effectiveTitleColor = titleColor ?? \"$primary\"\n // When titleRight is provided, use space-between layout for the title bar\n const effectiveTitleAlign = titleRight ? \"space-between\" : titleAlign\n\n return (\n <Box\n flexDirection=\"column\"\n width={width ?? \"snug-content\"}\n height={height}\n borderStyle=\"double\"\n borderColor={borderColor}\n backgroundColor={\"$popover-bg\"}\n paddingX={2}\n paddingY={1}\n userSelect=\"contain\"\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}>{typeof footer === \"string\" ? <Text dimColor>{footer}</Text> : footer}</Box>\n </>\n )}\n </Box>\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 \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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 dimColor>{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 \"@silvery/ag-react/hooks/useInput\"\nimport { Box } from \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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\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}: 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) => Math.min(i + effectiveMaxVisible, Math.max(0, itemsRef.current.length - 1)))\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 title={title} width={width} height={height} footer={footer}>\n {/* Search input */}\n <Box flexShrink={0} flexDirection=\"column\">\n <Box>\n {prompt && <Text color={promptColor}>{prompt}</Text>}\n {showPlaceholder ? (\n <Text dimColor>{placeholder}</Text>\n ) : (\n <CursorLine beforeCursor={readline.beforeCursor} afterCursor={readline.afterCursor} showCursor={isActive} />\n )}\n </Box>\n <Text dimColor>{\"─\".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 \"@silvery/ag-react/hooks/useFocusable\"\nimport { useInput } from \"@silvery/ag-react/hooks/useInput\"\nimport { Box } from \"@silvery/ag-react/components/Box\"\nimport type { BoxProps } from \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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({ value, onChange, label, isActive, ...rest }: 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 * 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 * Usage:\n * ```tsx\n * <Button label=\"Save\" onPress={() => save()} />\n * <Button label=\"Cancel\" onPress={() => close()} color=\"red\" />\n *\n * // With explicit active control (bypasses focus system)\n * <Button label=\"OK\" onPress={confirm} isActive={hasFocus} />\n * ```\n */\nimport React from \"react\"\nimport { useFocusable } from \"@silvery/ag-react/hooks/useFocusable\"\nimport { useInput } from \"@silvery/ag-react/hooks/useInput\"\nimport { Box } from \"@silvery/ag-react/components/Box\"\nimport type { BoxProps } from \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/components/Text\"\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 /** Button color */\n color?: string\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\n/**\n * Focusable button control.\n *\n * Renders `[ label ]` with inverse styling when focused. Activates on\n * Enter or Space key press.\n */\nexport function Button({ label, onPress, isActive, color, ...rest }: 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 return (\n <Box focusable {...rest}>\n <Text color={color} inverse={active}>\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, Text } from \"@silvery/ag-react\"\nimport { useSearch } from \"@silvery/ag-react/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 = 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 \"@silvery/ag-react/components/Text\"\nimport type { TextProps } from \"@silvery/ag-react/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({ type = \"dots\", label, interval = 80, ...rest }: 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 \"@silvery/ag-react/hooks/useLayout\"\nimport { Box } from \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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 dimColor>{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 { Text } from \"@silvery/ag-react/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 /** Selection indicator prefix shown on highlighted item (default: \"▸ \"). Non-highlighted items get equal-width spaces. Pass \"\" to hide. */\n indicator?: string\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}: 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(initialIndex ?? findFirstEnabled(items))\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 <Text key={item.value} inverse={meta.isCursor} dimColor={item.disabled}>\n {indicator ? (meta.isCursor ? indicator : \" \".repeat(indicator.length)) : \"\"}\n {item.label}\n </Text>\n ),\n [indicator],\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 key={col.header} flexGrow={1} justifyContent={col.align === \"right\" ? \"flex-end\" : undefined}>\n {content}\n </Box>\n ) : (\n <Box key={col.header} width={width} justifyContent={col.align === \"right\" ? \"flex-end\" : undefined}>\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 && <ListView items={data} height={viewportHeight} estimateHeight={1} renderItem={renderRow} />}\n </Box>\n )\n}\n","/**\n * Badge Component\n *\n * A small inline label for status display.\n *\n * Usage:\n * ```tsx\n * <Badge label=\"Active\" variant=\"success\" />\n * <Badge label=\"Warning\" variant=\"warning\" />\n * <Badge label=\"Custom\" color=\"magenta\" />\n * ```\n */\nimport React from \"react\"\nimport { Text } from \"@silvery/ag-react/components/Text\"\nimport type { TextProps } from \"@silvery/ag-react/components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface BadgeProps extends Omit<TextProps, \"children\"> {\n /** Badge text */\n label: string\n /** Color variant */\n variant?: \"default\" | \"primary\" | \"success\" | \"warning\" | \"error\"\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst VARIANT_COLORS: Record<NonNullable<BadgeProps[\"variant\"]>, string> = {\n default: \"$fg\",\n primary: \"$primary\",\n success: \"$success\",\n warning: \"$warning\",\n error: \"$error\",\n}\n\n// =============================================================================\n// Component\n// =============================================================================\n\nexport function Badge({ label, variant = \"default\", color, ...rest }: BadgeProps): React.ReactElement {\n const resolvedColor = color ?? VARIANT_COLORS[variant]\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 \"@silvery/ag-react/hooks/useLayout\"\nimport { Box } from \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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({ char = DEFAULT_CHAR, title, width: widthProp }: 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 dimColor>{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 dimColor>{char.repeat(leftLen)}</Text>\n <Text bold>{titleWithPad}</Text>\n <Text dimColor>{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=\"$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=\"$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 \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/components/Text\"\nimport type { TextProps } from \"@silvery/ag-react/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 bold color={color ?? \"$primary\"} {...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 bold color={color ?? \"$accent\"} {...rest}>\n {children}\n </Text>\n )\n}\n\n/** Group heading — $primary, not bold. Same hue as H1 but lighter weight. */\nexport function H3({ children, color, ...rest }: TypographyProps) {\n return (\n <Text color={color ?? \"$primary\"} {...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 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 italic color={color ?? \"$muted\"} {...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 color={color ?? \"$muted\"} {...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 dimColor color={color ?? \"$muted\"} {...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 bold 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 italic 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 backgroundColor=\"$mutedbg\" 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 backgroundColor=\"$mutedbg\" bold 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 ?? \"$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\"}>│ </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\"} 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 ?? \"$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 \"@silvery/ag-react/components/Text\"\nimport type { TextProps } from \"@silvery/ag-react/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: \"$primary\",\n 2: \"$accent\",\n 3: \"$primary\",\n 4: undefined, // inherit fg\n 5: undefined,\n 6: \"$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 \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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({ label, error, description, required, children }: FormFieldProps): React.ReactElement {\n return (\n <Box flexDirection=\"column\">\n <Text color=\"$muted\" bold>\n {label}\n {required && <Text color=\"$error\"> *</Text>}\n </Text>\n {description && <Text color=\"$disabledfg\">{description}</Text>}\n <Box>{children}</Box>\n {error && <Text color=\"$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 \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/components/Text\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type ToastVariant = \"default\" | \"success\" | \"error\" | \"warning\" | \"info\"\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 success: \"$success\",\n error: \"$error\",\n warning: \"$warning\",\n info: \"$info\",\n}\n\nconst VARIANT_ICONS: Record<ToastVariant, string> = {\n default: \"i\",\n success: \"+\",\n error: \"x\",\n warning: \"!\",\n info: \"i\",\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\"\n paddingX={1}\n backgroundColor=\"$popover-bg\"\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=\"$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({ toasts, maxVisible = 5, ...boxProps }: 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 * 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 \"@silvery/ag-react/hooks/useInput\"\nimport { Box } from \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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) => 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((idx: number) => Math.max(0, Math.min(idx, filtered.length - 1)), [filtered.length])\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 flexDirection=\"column\" borderStyle=\"single\" borderColor=\"$border\" backgroundColor=\"$popover-bg\" paddingX={1}>\n {/* Search input */}\n <Box>\n <Text color=\"$primary\" bold>\n {\">\"}{\" \"}\n </Text>\n <Text>{query || <Text color=\"$disabledfg\">{placeholder}</Text>}</Text>\n </Box>\n <Box>\n <Text color=\"$border\">{\"─\".repeat(30)}</Text>\n </Box>\n {/* Results */}\n {visible.length === 0 ? (\n <Text color=\"$disabledfg\">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 ? \"$primary\" : \"$fg\"}>\n {isSelected ? \">\" : \" \"} {cmd.name}\n </Text>\n {cmd.description && <Text color=\"$muted\">{cmd.description}</Text>}\n {cmd.shortcut && (\n <Text color=\"$disabledfg\" bold>\n {cmd.shortcut}\n </Text>\n )}\n </Box>\n )\n })\n )}\n {/* Status */}\n {filtered.length > maxVisible && <Text color=\"$disabledfg\">{filtered.length - maxVisible} more...</Text>}\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 \"@silvery/ag-react/hooks/useInput\"\nimport { Box } from \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/components/Text\"\nimport { ListView } from \"@silvery/ag-react/ui/components/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 ? \"$primary\" : \"$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=\"$disabledfg\">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 \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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=\"$disabledfg\"> {separator} </Text>}\n <Text color={isLast ? \"$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 \"@silvery/ag-react/hooks/useInput\"\nimport { Box } from \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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\">\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 */\nexport function Tab({ value, children }: TabProps): React.ReactElement {\n const { activeValue, setActiveValue, registerTab } = useTabsContext()\n const isActive = activeValue === value\n\n // Register this tab's value for keyboard navigation\n React.useEffect(() => {\n registerTab(value)\n }, [value, registerTab])\n\n return (\n <Box onMouseDown={() => setActiveValue(value)}>\n <Text color={isActive ? \"$primary\" : \"$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 \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/components/Text\"\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({ content, show = false, children, ...boxProps }: TooltipProps): React.ReactElement {\n return (\n <Box flexDirection=\"column\" {...boxProps}>\n {children}\n {show && (\n <Box width=\"snug-content\">\n <Text color=\"$muted\" dimColor>\n {content}\n </Text>\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 \"@silvery/ag-react/components/Box\"\nimport { Text } from \"@silvery/ag-react/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=\"$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=\"$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 <PositionRegistryContext.Provider value={registry}>{children}</PositionRegistryContext.Provider>\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 \"@silvery/ag-react/components/Box\"\nimport { useGridPosition } from \"@silvery/ag-react/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","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])) 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>(selector?: (term: Term) => T, equalityFn?: (a: T, b: T) => boolean): 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>(term: Term, selector: (term: Term) => T, equalityFn?: (a: T, b: T) => boolean): 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.\n * For pipeline $token resolution and automatic fg/bg, use `Box theme={}`:\n *\n * ```tsx\n * // Themed subtree — Box theme handles fg, bg, and $tokens automatically\n * <Box theme={lightTheme} borderStyle=\"single\">\n * <Text color=\"$primary\">Uses light theme</Text>\n * </Box>\n *\n * // Root app — ThemeProvider for useTheme(), terminal matches detected theme\n * <ThemeProvider theme={detectedTheme}>\n * <App />\n * </ThemeProvider>\n * ```\n */\n\nimport React from \"react\"\nimport { ThemeContext } from \"@silvery/theme/ThemeContext\"\nimport type { Theme } from \"@silvery/theme/types\"\n\nexport interface ThemeProviderProps {\n theme: Theme\n children: React.ReactNode\n}\n\nexport function ThemeProvider({ theme, children }: ThemeProviderProps): React.ReactElement {\n return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>\n}\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 { HitRegistry, generateHitRegionId, resetHitRegionIdCounter, Z_INDEX } from \"./hit-registry-core\"\nexport type { HitTarget, HitRegion } from \"./hit-registry-core\"\n\n// Import for local use\nimport { type HitTarget, type HitRegion, HitRegistry, generateHitRegionId } 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(target: HitTarget, rect: Rect | null, zIndex = 0, enabled = true): 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(target: HitTarget, zIndex = 0, enabled = true): (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 * RuntimeContext subscriber types — shared by render.tsx and InputBoundary.tsx.\n *\n * These are the internal callback types for the RuntimeContext event bus.\n * NOT the public useInput/usePaste hook types — those are in their respective hook files.\n *\n * See docs/guide/input-architecture.md for the full event pipeline.\n */\n\nimport type { Key } from \"@silvery/ag/keys\"\n\n/** Internal callback type for RuntimeContext \"input\" event subscribers. */\nexport type InputCallback = (input: string, key: Key) => void\n\n/** Internal callback type for RuntimeContext \"paste\" event subscribers. */\nexport type PasteCallback = (text: string) => void\n\nexport interface SubscriberList {\n input: Set<InputCallback>\n paste: Set<PasteCallback>\n}\n\nexport function createSubscriberList(): SubscriberList {\n return { input: new Set(), paste: new Set() }\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, { useCallback, useEffect, useMemo, useRef, type ReactElement, type ReactNode } from \"react\"\n\nconst log = createLogger(\"silvery:render\")\nimport {\n FocusManagerContext,\n RuntimeContext,\n type RuntimeContextValue,\n StdoutContext,\n StderrContext,\n TermContext,\n} from \"./context\"\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 { enableBracketedPaste, disableBracketedPaste, parseBracketedPaste } 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// Lightweight subscriber list — replaces EventEmitter from node:events\n// ============================================================================\n\nimport {\n type InputCallback,\n type PasteCallback,\n type SubscriberList,\n createSubscriberList,\n} from \"./runtime-subscribers\"\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 // Subscriber list — lightweight replacement for EventEmitter\n const subscribersRef = useRef<SubscriberList | null>(null)\n if (!subscribersRef.current) {\n subscribersRef.current = createSubscriberList()\n }\n const subscribers = subscribersRef.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 for (const handler of subscribers.paste) {\n handler(pasteResult.content)\n }\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 subscribers\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 for (const handler of subscribers.input) {\n handler(input, key)\n }\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 — direct subscriber list, no EventEmitter\n const runtimeContextValue = useMemo<RuntimeContextValue>(\n () => ({\n on(event, handler) {\n if (event === \"input\") {\n const typed = handler as InputCallback\n subscribers.input.add(typed)\n return () => {\n subscribers.input.delete(typed)\n }\n }\n if (event === \"paste\") {\n const typed = handler as unknown as PasteCallback\n subscribers.paste.add(typed)\n return () => {\n subscribers.paste.delete(typed)\n }\n }\n return () => {} // Unknown event — no-op cleanup\n },\n emit() {\n // render() runtime doesn't support view → runtime events\n },\n exit: handleExit,\n pause: onPause,\n resume: onResume,\n }),\n [subscribers, handleExit, onPause, onResume],\n )\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}>{children}</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 ? (data: string) => this.outputGuard!.writeStdout(data) : 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?.(`SilveryInstance.render() updateContainerSync complete in ${Date.now() - startTime}ms`)\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(element: ReactElement, termOrDef?: Term | TermDef, options?: RenderOptions): 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(element: ReactElement, term: Term, resolved: ResolvedTermDef): 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(element: ReactElement, termOrDef?: Term | TermDef, options?: RenderOptions): 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 { setLayoutEngine, isLayoutEngineInitialized, type LayoutEngineType } from \"@silvery/ag-term/layout-engine\"\n\n// Re-export adapters for custom engine initialization\nexport { createYogaEngine, initYogaEngine, YogaLayoutEngine } 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.map((child) => walkNode(child as ReactNode, parentDirection)).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.map((child) => walkNode(child as ReactNode, direction)).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 * 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 \"@silvery/ag-term/ansi\"\nimport type { ReactElement } from \"react\"\nimport type { TerminalBuffer } from \"@silvery/ag-term/buffer\"\nimport { createPipeline, type MeasuredTerm } from \"@silvery/ag-term/measurer\"\nimport { executeRender, type ExecuteRenderOptions, type PipelineConfig } from \"@silvery/ag-term/pipeline\"\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(element: ReactElement, options?: { width?: number; height?: number; plain?: boolean }): 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 return executeRender(root, width, height, prevBuffer, options, pipelineConfig)\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 * 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(`TextOp offset ${op.offset} out of bounds for text of length ${text.length}`)\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\" ? 0 : 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 * 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(options: WithKeybindingsOptions): <T extends AppWithCommands>(app: T) => T\n// Direct form: withKeybindings(app, options) => enhancedApp\nexport function withKeybindings<T extends AppWithCommands>(app: T, options: WithKeybindingsOptions): 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(key, modifiers, bindings as ExtendedKeybindingDef[], ctx)\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 \"@silvery/ag-term/buffer\"\nimport { outputPhase } from \"@silvery/ag-term/pipeline\"\nimport { compareBuffers, formatMismatch } from \"@silvery/test/compare-buffers\"\nimport type { BoxProps, AgNode } from \"@silvery/create/types\"\nimport type { App } from \"@silvery/ag-term/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 }, () => Array.from({ length: width }, () => createDefaultVTermStyle()))\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(`${id}: overflows parent right (${rect.x + rect.width} > ${parentRect.x + parentRect.width})`)\n }\n if (rect.y + rect.height > parentRect.y + parentRect.height + TOLERANCE) {\n violations.push(`${id}: overflows parent bottom (${rect.y + rect.height} > ${parentRect.y + parentRect.height})`)\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>(app: T, options: DiagnosticOptions = {}): 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(commandId: string, checkType: string): 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 ).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.map((m) => ` (${m.x},${m.y}) char=\"${m.char}\": ${m.diffs.join(\", \")}`).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 ... and ${styleMismatches.length - 5} more` : \"\") +\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 * 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 * @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, useLayoutEffect, useMemo, useRef } from \"react\"\nimport { RuntimeContext, type RuntimeContextValue } from \"../context\"\nimport type { Key } from \"../hooks/useInput\"\nimport { keyToAnsi, keyToName, parseKey } from \"@silvery/ag/keys\"\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// Subscriber list — lightweight replacement for EventEmitter\n// =============================================================================\n\nimport {\n type InputCallback,\n type PasteCallback,\n type SubscriberList,\n createSubscriberList,\n} from \"../runtime-subscribers\"\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Reconstruct raw terminal data from parsed (input, key) pair.\n * This allows forwarding input from the parent layer stack to the\n * isolated child's RuntimeContext subscriber list.\n */\nfunction toRawData(input: string, key: Key): string {\n const name = keyToName(key)\n if (name) {\n const mods: string[] = []\n if (key.ctrl) mods.push(\"Control\")\n if (key.shift) mods.push(\"Shift\")\n if (key.meta) mods.push(\"Meta\")\n if (key.super) mods.push(\"Super\")\n if (key.hyper) mods.push(\"Hyper\")\n mods.push(name)\n return keyToAnsi(mods.join(\"+\"))\n }\n // Regular character with ctrl modifier\n if (key.ctrl) return keyToAnsi(`Control+${input}`)\n return input\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 * - Forwards all input into the isolated child scope\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 * forwarding, 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 // Create an isolated subscriber list for children (replaces EventEmitter)\n const subscribersRef = useRef<SubscriberList | null>(null)\n if (!subscribersRef.current) {\n subscribersRef.current = createSubscriberList()\n }\n const subscribers = subscribersRef.current\n\n // Register a consuming layer in the parent when active.\n // This layer intercepts ALL input and forwards to the isolated subscriber list.\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 forwarding\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 // Forward to isolated scope — parse from raw data so subscribers\n // get the same (input, key) format as the parent RuntimeContext\n const raw = toRawData(input, key)\n const [parsedInput, parsedKey] = parseKey(raw)\n for (const h of subscribers.input) {\n h(parsedInput, parsedKey)\n }\n return true\n },\n [subscribers],\n )\n\n const layerId = useId()\n useInputLayer(`input-boundary-${layerId}`, handler)\n\n // RuntimeContext — direct subscriber list for the isolated scope\n const runtimeContextValue = useMemo<RuntimeContextValue>(\n () => ({\n on(event, handler) {\n if (event === \"input\") {\n const typed = handler as InputCallback\n subscribers.input.add(typed)\n return () => {\n subscribers.input.delete(typed)\n }\n }\n if (event === \"paste\") {\n const typed = handler as unknown as PasteCallback\n subscribers.paste.add(typed)\n return () => {\n subscribers.paste.delete(typed)\n }\n }\n return () => {} // Unknown event — no-op cleanup\n },\n emit() {\n // InputBoundary doesn't support view → runtime events\n },\n exit: () => {}, // InputBoundary doesn't control app exit\n }),\n [subscribers],\n )\n\n // Clean up subscriber lists on unmount\n useLayoutEffect(() => {\n return () => {\n subscribers.input.clear()\n subscribers.paste.clear()\n }\n }, [subscribers])\n\n return (\n <RuntimeContext.Provider value={runtimeContextValue}>\n <InputLayerProvider>{children}</InputLayerProvider>\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> = (state: S, op: Op) => 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\"]]?: (effect: Extract<E, { type: K }>, dispatch: (op: Op) => void) => 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) ? (result as [S, E[]]) : [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 * 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 { createCursorStore, CursorProvider, type CursorStore } 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 (currentFocusables: Focusable[], currentActiveFocusId: string | undefined): 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 (currentFocusables: Focusable[], currentActiveFocusId: string | undefined): 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) => currentFocusables.map((f) => (f.id === id ? { ...f, isActive: true } : f)))\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) => currentFocusables.map((f) => (f.id === id ? { ...f, isActive: false } : f)))\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 * ReactiveThemeProvider — auto-switches theme based on terminal color scheme.\n *\n * Wraps ThemeProvider and subscribes to the ColorSchemeDetector (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/theme/types\"\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 * 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 \"@silvery/ag-react/ui/components\"\nexport { ListView } from \"@silvery/ag-react/ui/components\"\nexport type {\n ListViewProps,\n ListViewHandle,\n ListItemMeta,\n ListViewCacheConfig,\n ListViewSearchConfig,\n} from \"@silvery/ag-react/ui/components\"\nexport { HorizontalVirtualList } from \"@silvery/ag-react/ui/components\"\nexport type { HorizontalVirtualListProps, HorizontalVirtualListHandle } from \"@silvery/ag-react/ui/components\"\nexport { SplitView } from \"@silvery/ag-react/ui/components\"\nexport type { SplitViewProps } from \"@silvery/ag-react/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 \"@silvery/ag-react/ui/components\"\nexport type { ScreenProps } from \"@silvery/ag-react/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 \"@silvery/ag-react/ui/components\"\nexport type { ErrorBoundaryProps } from \"@silvery/ag-react/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 \"@silvery/ag-react/ui/components\"\nexport type { TextInputProps, TextInputHandle } from \"@silvery/ag-react/ui/components\"\n\nexport { TextArea } from \"@silvery/ag-react/ui/components\"\nexport type { TextAreaProps, TextAreaHandle, TextAreaSelection } from \"@silvery/ag-react/ui/components\"\n\nexport { useTextArea, clampScroll } from \"@silvery/ag-react/ui/components\"\nexport type { UseTextAreaOptions, UseTextAreaResult } from \"@silvery/ag-react/ui/components\"\n\nexport { EditContextDisplay } from \"@silvery/ag-react/ui/components\"\nexport type { EditContextDisplayProps } from \"@silvery/ag-react/ui/components\"\n\n// Display Components\nexport { CursorLine } from \"@silvery/ag-react/ui/components\"\nexport type { CursorLineProps } from \"@silvery/ag-react/ui/components\"\n\n// Dialog Components\nexport { ModalDialog, formatTitleWithHotkey } from \"@silvery/ag-react/ui/components\"\nexport type { ModalDialogProps } from \"@silvery/ag-react/ui/components\"\n\nexport { PickerDialog } from \"@silvery/ag-react/ui/components\"\nexport type { PickerDialogProps } from \"@silvery/ag-react/ui/components\"\n\nexport { PickerList } from \"@silvery/ag-react/ui/components\"\nexport type { PickerListProps } from \"@silvery/ag-react/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 \"@silvery/ag-react/ui/components\"\nexport type { TypographyProps } from \"@silvery/ag-react/ui/components\"\n\n// Heading (OSC 66 text sizing)\nexport { Heading } from \"@silvery/ag-react/ui/components\"\nexport type { HeadingProps, HeadingLevel } from \"@silvery/ag-react/ui/components\"\n\n// Focusable Controls\nexport { Toggle } from \"@silvery/ag-react/ui/components\"\nexport type { ToggleProps } from \"@silvery/ag-react/ui/components\"\n\nexport { Button } from \"@silvery/ag-react/ui/components\"\nexport type { ButtonProps } from \"@silvery/ag-react/ui/components\"\n\n// Input Hooks\nexport { useReadline } from \"@silvery/ag-react/ui/components\"\nexport type { ReadlineState, UseReadlineOptions, UseReadlineResult } from \"@silvery/ag-react/ui/components\"\n\n// Widget Components\nexport { Spinner } from \"@silvery/ag-react/ui/components\"\nexport type { SpinnerProps } from \"@silvery/ag-react/ui/components\"\n\nexport { ProgressBar } from \"@silvery/ag-react/ui/components\"\nexport type { ProgressBarProps } from \"@silvery/ag-react/ui/components\"\n\nexport { SelectList } from \"@silvery/ag-react/ui/components\"\nexport type { SelectListProps, SelectOption } from \"@silvery/ag-react/ui/components\"\n\nexport { Table } from \"./components/Table\"\nexport type { TableProps, Column, Column as TableColumn } from \"./components/Table\"\n\nexport { Badge } from \"@silvery/ag-react/ui/components\"\nexport type { BadgeProps } from \"@silvery/ag-react/ui/components\"\n\nexport { Divider } from \"@silvery/ag-react/ui/components\"\nexport type { DividerProps } from \"@silvery/ag-react/ui/components\"\n\n// Form Components\nexport { Form, FormField } from \"@silvery/ag-react/ui/components\"\nexport type { FormProps, FormFieldProps } from \"@silvery/ag-react/ui/components\"\n\n// Toast / Notification\nexport { useToast, ToastContainer, ToastItem } from \"@silvery/ag-react/ui/components\"\nexport type {\n ToastData,\n ToastOptions,\n ToastVariant,\n UseToastResult,\n ToastContainerProps,\n ToastItemProps,\n} from \"@silvery/ag-react/ui/components\"\n\n// Command Palette\nexport { CommandPalette } from \"@silvery/ag-react/ui/components\"\nexport type { CommandPaletteProps, CommandItem } from \"@silvery/ag-react/ui/components\"\n\n// Tree View\nexport { TreeView } from \"@silvery/ag-react/ui/components\"\nexport type { TreeViewProps, TreeNode } from \"@silvery/ag-react/ui/components\"\n\n// Breadcrumb\nexport { Breadcrumb } from \"@silvery/ag-react/ui/components\"\nexport type { BreadcrumbProps, BreadcrumbItem } from \"@silvery/ag-react/ui/components\"\n\n// Tabs\nexport { Tabs, TabList, Tab, TabPanel } from \"@silvery/ag-react/ui/components\"\nexport type { TabsProps, TabListProps, TabProps, TabPanelProps } from \"@silvery/ag-react/ui/components\"\n\n// Tooltip\nexport { Tooltip } from \"@silvery/ag-react/ui/components\"\nexport type { TooltipProps } from \"@silvery/ag-react/ui/components\"\n\n// Skeleton\nexport { Skeleton } from \"@silvery/ag-react/ui/components\"\nexport type { SkeletonProps } from \"@silvery/ag-react/ui/components\"\n\n// Image Component\nexport { Image } from \"@silvery/ag-react/ui/image\"\nexport type { ImageProps } from \"@silvery/ag-react/ui/image\"\n\n// Image Protocol Encoders\nexport { encodeKittyImage, deleteKittyImage, isKittyGraphicsSupported } from \"@silvery/ag-react/ui/image\"\nexport type { KittyImageOptions } from \"@silvery/ag-react/ui/image\"\nexport { encodeSixel, isSixelSupported } from \"@silvery/ag-react/ui/image\"\nexport type { SixelImageData } from \"@silvery/ag-react/ui/image\"\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 * 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 { createKeyEvent, createFocusEvent, dispatchKeyEvent, dispatchFocusEvent } 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 { CopyEvent, SemanticCopyProvider, ClipboardData, PasteEvent } from \"@silvery/ag-term/copy-extraction\"\nexport { createPasteEvent, createCopyProvider, getInternalClipboard } 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 \"@silvery/ag-react/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} from \"./context\"\nexport type { CacheBackend, RuntimeContextValue, BaseRuntimeEvents, CapabilityLookup } from \"./context\"\n\n// Theming\nexport { ThemeProvider } from \"./ThemeProvider\"\nexport { useTheme } from \"@silvery/theme/ThemeContext\"\nexport type { ThemeProviderProps } from \"./ThemeProvider\"\nexport {\n defaultDarkTheme,\n defaultLightTheme,\n ansi16DarkTheme,\n ansi16LightTheme,\n builtinThemes,\n getThemeByName,\n resolveThemeColor,\n generateTheme,\n detectTheme,\n deriveTheme,\n} from \"@silvery/theme\"\nexport type { Theme, AnsiPrimary, DetectThemeOptions } from \"@silvery/theme\"\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 { copyToClipboard, requestClipboard, parseClipboardResponse } 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 { detectKittySupport, detectKittyFromStdio, type KittyDetectResult } 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 { runTermtest, TERMTEST_SECTIONS, type TermtestSection, type TermtestOptions } 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 { enableFocusReporting, disableFocusReporting, parseFocusEvent } 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 { createDOMAdapter, DOMRenderBuffer, injectDOMStyles } 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 { UseEditContextOptions, UseEditContextResult, EditTarget } 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 { PositionRegistryProvider, usePositionRegistry, createPositionRegistry } from \"./hooks/usePositionRegistry\"\nexport type { PositionRegistry, ScrollRect } from \"./hooks/usePositionRegistry\"\nexport { useGridPosition } from \"./hooks/useGridPosition\"\nexport { GridCell } from \"@silvery/ag-react/ui/components\"\nexport type { GridCellProps } from \"@silvery/ag-react/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 \"@silvery/ag-react/ui/animation\"\nexport { useAnimatedTransition } from \"@silvery/ag-react/ui/animation\"\nexport type {\n EasingFn,\n EasingName,\n UseAnimationOptions,\n UseAnimationResult,\n UseTransitionOptions,\n} from \"@silvery/ag-react/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 \"@silvery/ag-react/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\"\n","// silvery — multi-target rendering framework for React\n// Re-exports from @silvery/* packages\n\nexport const VERSION = \"0.0.1\"\n\n// Re-export everything from @silvery/ag-react — local `render` below shadows the re-exported one\nexport * from \"@silvery/ag-react\"\n\nimport type { ReactElement } from \"react\"\nimport { render as reactRender, type RenderOptions } from \"@silvery/ag-react\"\nimport type { Term } from \"@silvery/ag-react\"\nimport type { TermDef } from \"@silvery/ag-term/term-def\"\n\n/**\n * Render a React element to the terminal.\n *\n * Zero-ceremony entry point — auto-detects the terminal and starts an\n * interactive app when stdin is a TTY. No need to create a Term first.\n *\n * @example Hello World (2 lines)\n * ```tsx\n * import { render, Text } from \"silvery\"\n * await render(<Text>Hello!</Text>).run()\n * ```\n *\n * @example Interactive counter\n * ```tsx\n * import { useState } from \"react\"\n * import { render, Box, Text, useInput } from \"silvery\"\n *\n * function Counter() {\n * const [count, setCount] = useState(0)\n * useInput((input) => {\n * if (input === \"j\") setCount((c) => c + 1)\n * })\n * return (\n * <Box borderStyle=\"round\" padding={1}>\n * <Text>Count: {count}</Text>\n * </Box>\n * )\n * }\n *\n * await render(<Counter />).run()\n * ```\n *\n * @example Static render (explicit)\n * ```tsx\n * import { render, Text } from \"silvery\"\n * await render(<Text>Report</Text>, { width: 120 })\n * ```\n *\n * When called without a Term or TermDef:\n * - **TTY detected** → interactive mode (stdin + stdout auto-wired)\n * - **No TTY** → static mode (renders once and returns)\n *\n * Pass a Term or TermDef explicitly to override auto-detection.\n */\nexport function render(\n element: ReactElement,\n termOrDef?: Term | TermDef,\n options?: RenderOptions,\n): ReturnType<typeof reactRender> {\n // When no term/def is provided and we're in a TTY, auto-wire stdin/stdout\n // so the app runs interactively (useInput works, app stays alive until exit).\n if (!termOrDef && process.stdin?.isTTY && process.stdout?.isTTY) {\n const ttyDef: TermDef = {\n stdin: process.stdin,\n stdout: process.stdout,\n }\n return reactRender(element, ttyDef, options)\n }\n return reactRender(element, termOrDef, options)\n}\n"],"mappings":"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"}
|