silvery 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +41 -145
  2. package/dist/chalk.js +3 -0
  3. package/dist/chalk.js.map +11 -0
  4. package/dist/index.js +340 -0
  5. package/dist/index.js.map +282 -0
  6. package/dist/ink.js +129 -0
  7. package/dist/ink.js.map +140 -0
  8. package/dist/runtime.js +394 -0
  9. package/dist/runtime.js.map +286 -0
  10. package/dist/theme.js +343 -0
  11. package/dist/theme.js.map +286 -0
  12. package/dist/ui/animation.js +3 -0
  13. package/dist/ui/animation.js.map +15 -0
  14. package/dist/ui/ansi.js +3 -0
  15. package/dist/ui/ansi.js.map +10 -0
  16. package/dist/ui/cli.js +8 -0
  17. package/dist/ui/cli.js.map +14 -0
  18. package/dist/ui/display.js +4 -0
  19. package/dist/ui/display.js.map +10 -0
  20. package/dist/ui/image.js +4 -0
  21. package/dist/ui/image.js.map +15 -0
  22. package/dist/ui/input.js +3 -0
  23. package/dist/ui/input.js.map +11 -0
  24. package/dist/ui/progress.js +8 -0
  25. package/dist/ui/progress.js.map +20 -0
  26. package/dist/ui/react.js +3 -0
  27. package/dist/ui/react.js.map +15 -0
  28. package/dist/ui/utils.js +3 -0
  29. package/dist/ui/utils.js.map +10 -0
  30. package/dist/ui/wrappers.js +14 -0
  31. package/dist/ui/wrappers.js.map +19 -0
  32. package/dist/ui.js +17 -0
  33. package/dist/ui.js.map +20 -0
  34. package/package.json +67 -15
  35. package/src/index.ts +67 -1
  36. package/src/runtime.ts +4 -0
  37. package/src/theme.ts +4 -0
  38. package/src/ui/animation.ts +2 -0
  39. package/src/ui/ansi.ts +2 -0
  40. package/src/ui/cli.ts +2 -0
  41. package/src/ui/display.ts +2 -0
  42. package/src/ui/image.ts +2 -0
  43. package/src/ui/input.ts +2 -0
  44. package/src/ui/progress.ts +2 -0
  45. package/src/ui/react.ts +2 -0
  46. package/src/ui/utils.ts +2 -0
  47. package/src/ui/wrappers.ts +2 -0
  48. package/src/ui.ts +4 -0
  49. package/examples/CLAUDE.md +0 -75
  50. package/examples/_banner.tsx +0 -60
  51. package/examples/cli.ts +0 -228
  52. package/examples/index.md +0 -101
  53. package/examples/inline/inline-nontty.tsx +0 -98
  54. package/examples/inline/inline-progress.tsx +0 -79
  55. package/examples/inline/inline-simple.tsx +0 -63
  56. package/examples/inline/scrollback.tsx +0 -185
  57. package/examples/interactive/_input-debug.tsx +0 -110
  58. package/examples/interactive/_stdin-test.ts +0 -71
  59. package/examples/interactive/_textarea-bare.tsx +0 -45
  60. package/examples/interactive/aichat/components.tsx +0 -468
  61. package/examples/interactive/aichat/index.tsx +0 -207
  62. package/examples/interactive/aichat/script.ts +0 -460
  63. package/examples/interactive/aichat/state.ts +0 -326
  64. package/examples/interactive/aichat/types.ts +0 -19
  65. package/examples/interactive/app-todo.tsx +0 -198
  66. package/examples/interactive/async-data.tsx +0 -208
  67. package/examples/interactive/cli-wizard.tsx +0 -332
  68. package/examples/interactive/clipboard.tsx +0 -183
  69. package/examples/interactive/components.tsx +0 -463
  70. package/examples/interactive/data-explorer.tsx +0 -506
  71. package/examples/interactive/dev-tools.tsx +0 -379
  72. package/examples/interactive/explorer.tsx +0 -747
  73. package/examples/interactive/gallery.tsx +0 -652
  74. package/examples/interactive/inline-bench.tsx +0 -136
  75. package/examples/interactive/kanban.tsx +0 -267
  76. package/examples/interactive/layout-ref.tsx +0 -185
  77. package/examples/interactive/outline.tsx +0 -171
  78. package/examples/interactive/paste-demo.tsx +0 -198
  79. package/examples/interactive/scroll.tsx +0 -77
  80. package/examples/interactive/search-filter.tsx +0 -240
  81. package/examples/interactive/task-list.tsx +0 -279
  82. package/examples/interactive/terminal.tsx +0 -798
  83. package/examples/interactive/textarea.tsx +0 -103
  84. package/examples/interactive/theme.tsx +0 -336
  85. package/examples/interactive/transform.tsx +0 -256
  86. package/examples/interactive/virtual-10k.tsx +0 -413
  87. package/examples/kitty/canvas.tsx +0 -519
  88. package/examples/kitty/generate-samples.ts +0 -236
  89. package/examples/kitty/image-component.tsx +0 -273
  90. package/examples/kitty/images.tsx +0 -604
  91. package/examples/kitty/input.tsx +0 -371
  92. package/examples/kitty/keys.tsx +0 -378
  93. package/examples/kitty/paint.tsx +0 -1017
  94. package/examples/layout/dashboard.tsx +0 -551
  95. package/examples/layout/live-resize.tsx +0 -290
  96. package/examples/layout/overflow.tsx +0 -51
  97. package/examples/playground/README.md +0 -69
  98. package/examples/playground/build.ts +0 -61
  99. package/examples/playground/index.html +0 -420
  100. package/examples/playground/playground-app.tsx +0 -416
  101. package/examples/runtime/elm-counter.tsx +0 -206
  102. package/examples/runtime/hello-runtime.tsx +0 -73
  103. package/examples/runtime/pipe-composition.tsx +0 -184
  104. package/examples/runtime/run-counter.tsx +0 -78
  105. package/examples/runtime/runtime-counter.tsx +0 -197
  106. package/examples/screenshots/generate.tsx +0 -563
  107. package/examples/scrollback-perf.tsx +0 -230
  108. package/examples/viewer.tsx +0 -654
  109. package/examples/web/build.ts +0 -365
  110. package/examples/web/canvas-app.tsx +0 -80
  111. package/examples/web/canvas.html +0 -89
  112. package/examples/web/dom-app.tsx +0 -81
  113. package/examples/web/dom.html +0 -113
  114. package/examples/web/showcase-app.tsx +0 -107
  115. package/examples/web/showcase.html +0 -34
  116. package/examples/web/showcases/index.tsx +0 -56
  117. package/examples/web/viewer-app.tsx +0 -555
  118. package/examples/web/viewer.html +0 -30
  119. package/examples/web/xterm-app.tsx +0 -105
  120. package/examples/web/xterm.html +0 -118
@@ -0,0 +1,140 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../packages/ag-term/src/ansi/detection.ts", "../packages/ag/src/keys.ts", "../packages/ag-term/src/runtime/keys.ts", "../packages/ag-term/src/mouse.ts", "../packages/ag-term/src/bracketed-paste.ts", "../packages/ag-term/src/focus-reporting.ts", "../packages/ag-term/src/runtime/term-provider.ts", "../packages/ag-term/src/ansi/term.ts", "../packages/ag-term/src/ansi/utils.ts", "../packages/ag-term/src/ansi/underline.ts", "../packages/ag-term/src/ansi/hyperlink.ts", "../packages/ag-term/src/ansi/index.ts", "../packages/ag-term/src/ansi/sgr-codes.ts", "../packages/ag-term/src/buffer.ts", "../packages/ag-term/src/text-sizing.ts", "../packages/ag-term/src/unicode.ts", "../packages/ag-react/src/context.ts", "../../../node_modules/.bun/yoga-wasm-web@0.3.3/node_modules/yoga-wasm-web/dist/wrapAsm-f766f97f.js", "../../../node_modules/.bun/yoga-wasm-web@0.3.3/node_modules/yoga-wasm-web/dist/index.js", "../../../node_modules/.bun/yoga-wasm-web@0.3.3/node_modules/yoga-wasm-web/dist/node.js", "../packages/ag-term/src/adapters/yoga-adapter.ts", "../packages/ag-term/src/adapters/flexily-zero-adapter.ts", "../packages/ag-term/src/layout-engine.ts", "../packages/ag-term/src/pipeline/collect-text.ts", "../packages/ag-term/src/pipeline/helpers.ts", "../packages/ag-term/src/pipeline/measure-phase.ts", "../packages/ag-term/src/pipeline/measure-stats.ts", "../packages/ag/src/types.ts", "../packages/ag-term/src/pipeline/layout-phase.ts", "../packages/theme/src/color.ts", "../packages/theme/src/contrast.ts", "../packages/theme/src/derive.ts", "../packages/theme/src/palettes/catppuccin.ts", "../packages/theme/src/palettes/nord.ts", "../packages/theme/src/palettes/dracula.ts", "../packages/theme/src/palettes/solarized.ts", "../packages/theme/src/palettes/tokyo-night.ts", "../packages/theme/src/palettes/one-dark.ts", "../packages/theme/src/palettes/gruvbox.ts", "../packages/theme/src/palettes/rose-pine.ts", "../packages/theme/src/palettes/kanagawa.ts", "../packages/theme/src/palettes/everforest.ts", "../packages/theme/src/palettes/monokai.ts", "../packages/theme/src/palettes/snazzy.ts", "../packages/theme/src/palettes/material.ts", "../packages/theme/src/palettes/palenight.ts", "../packages/theme/src/palettes/ayu.ts", "../packages/theme/src/palettes/nightfox.ts", "../packages/theme/src/palettes/horizon.ts", "../packages/theme/src/palettes/moonfly.ts", "../packages/theme/src/palettes/nightfly.ts", "../packages/theme/src/palettes/oxocarbon.ts", "../packages/theme/src/palettes/sonokai.ts", "../packages/theme/src/palettes/edge.ts", "../packages/theme/src/palettes/modus.ts", "../packages/theme/src/palettes/index.ts", "../packages/theme/src/state.ts", "../packages/theme/src/resolve.ts", "../packages/ag-term/src/pipeline/render-helpers.ts", "../packages/ag-term/src/pipeline/render-text.ts", "../packages/ag-term/src/pipeline/render-box.ts", "../packages/ag-term/src/pipeline/cascade-predicates.ts", "../packages/ag-term/src/pipeline/content-phase.ts", "../packages/ag-term/src/pipeline/content-phase-adapter.ts", "../packages/ag-term/src/errors.ts", "../packages/ag-term/src/pipeline/diff-buffers.ts", "../packages/ag-term/src/pipeline/output-phase.ts", "../packages/ag-term/src/pipeline/index.ts", "../packages/ag-term/src/pipeline.ts", "../packages/ag-react/src/reconciler/helpers.ts", "../packages/ag-react/src/reconciler/nodes.ts", "../packages/ag-react/src/reconciler/host-config.ts", "../packages/ag-react/src/reconciler/index.ts", "../packages/ag-react/src/reconciler/string-reconciler.ts", "../packages/ag-react/src/render-string.tsx", "../packages/ink/src/ink-utils.ts", "../packages/ag-term/src/ansi-sanitize.ts", "../packages/ink/src/ink-sanitize.ts", "../packages/ink/src/ink-components.ts", "../packages/ag-react/src/components/Box.tsx", "../packages/ag-react/src/components/Static.tsx", "../packages/ag-react/src/hooks/useScrollback.ts", "../packages/ag-term/src/osc-markers.ts", "../packages/ag-react/src/components/Text.tsx", "../packages/ag-react/src/components/Newline.tsx", "../packages/ag-react/src/components/Spacer.tsx", "../packages/ag-react/src/components/Transform.tsx", "../packages/ink/src/ink-hooks.ts", "../packages/ink/src/with-ink-cursor.ts", "../packages/ag-react/src/hooks/useCursor.ts", "../packages/ag-react/src/hooks/useLayout.ts", "../packages/ink/src/with-ink-focus.ts", "../packages/ink/src/ink-stdin.ts", "../packages/ag-react/src/hooks/useTerm.ts", "../packages/ag-react/src/hooks/useWindowSize.ts", "../packages/ag-react/src/hooks/useInput.ts", "../packages/ag-react/src/hooks/useApp.ts", "../packages/ag-react/src/hooks/useStdout.ts", "../packages/ag-react/src/hooks/useStderr.ts", "../packages/ag-term/src/index.ts", "../packages/ag-term/src/output.ts", "../packages/ag-term/src/clipboard.ts", "../packages/ag-term/src/kitty-manager.ts", "../packages/ag-term/src/terminal-caps.ts", "../packages/ag-term/src/termtest.ts", "../packages/ag-term/src/term-def.ts", "../packages/ag-term/src/mouse-events.ts", "../packages/tea/src/focus-queries.ts", "../packages/ag/src/tree-utils.ts", "../packages/ag-term/src/non-tty.ts", "../packages/ag-term/src/inspector.ts", "../packages/ag-term/src/measurer.ts", "../packages/ag-term/src/virtual-scrollback.ts", "../packages/ag-term/src/history-buffer.ts", "../packages/ag-term/src/text-surface.ts", "../packages/ink/src/ink-render.ts", "../packages/ag-react/src/error-boundary.tsx", "../packages/ag-react/src/accessibility.ts", "../packages/ag-react/src/render.tsx", "../packages/ag/src/focus-manager.ts", "../packages/ag-term/src/scheduler.ts", "../packages/test/src/debug-mismatch.ts", "../packages/ag-term/src/renderer.ts", "../packages/test/src/auto-locator.ts", "../packages/ag-term/src/bound-term.ts", "../packages/ag-term/src/app.ts", "../packages/ag-term/src/screenshot.ts", "../packages/test/src/debug.ts", "../packages/ag-react/src/measureElement.ts", "../packages/ink/src/ink.ts", "../packages/ink/src/ink-measure-text.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * Terminal capability detection.\n *\n * Detects:\n * - Cursor control (can reposition cursor)\n * - Input capability (can read raw keystrokes)\n * - Color level (basic, 256, truecolor)\n * - Unicode support (can render unicode symbols)\n * - Extended underline support (curly, dotted, etc)\n * - Terminal capabilities profile (TerminalCaps)\n */\n\nimport { spawnSync } from \"child_process\"\nimport type { ColorLevel } from \"./types\"\n\n// =============================================================================\n// Cursor Detection\n// =============================================================================\n\n/**\n * Detect if terminal supports cursor control (repositioning).\n * Returns false for dumb terminals and piped output.\n */\nexport function detectCursor(stdout: NodeJS.WriteStream): boolean {\n // Not a TTY - no cursor control\n if (!stdout.isTTY) return false\n\n // Dumb terminal - no cursor control\n if (process.env.TERM === \"dumb\") return false\n\n return true\n}\n\n// =============================================================================\n// Input Detection\n// =============================================================================\n\n/**\n * Detect if terminal can read raw keystrokes.\n * Requires stdin to be a TTY with raw mode support.\n */\nexport function detectInput(stdin: NodeJS.ReadStream): boolean {\n // Not a TTY - no raw input\n if (!stdin.isTTY) return false\n\n // Check if setRawMode is available\n return typeof stdin.setRawMode === \"function\"\n}\n\n// =============================================================================\n// Color Detection\n// =============================================================================\n\n/**\n * Known CI environments that may not support colors well.\n */\nconst CI_ENVS = [\"CI\", \"GITHUB_ACTIONS\", \"GITLAB_CI\", \"JENKINS_URL\", \"BUILDKITE\", \"CIRCLECI\", \"TRAVIS\"]\n\n/**\n * Detect color level supported by terminal.\n * Returns null if no color support.\n *\n * Checks (in order):\n * 1. NO_COLOR env var - forces no color\n * 2. FORCE_COLOR env var - forces color level\n * 3. COLORTERM=truecolor - truecolor support\n * 4. TERM patterns - detect from terminal type\n * 5. CI detection - basic colors in CI\n */\nexport function detectColor(stdout: NodeJS.WriteStream): ColorLevel | null {\n // NO_COLOR takes precedence (see https://no-color.org/)\n if (process.env.NO_COLOR !== undefined) {\n return null\n }\n\n // FORCE_COLOR overrides detection\n const forceColor = process.env.FORCE_COLOR\n if (forceColor !== undefined) {\n if (forceColor === \"0\" || forceColor === \"false\") return null\n if (forceColor === \"1\") return \"basic\"\n if (forceColor === \"2\") return \"256\"\n if (forceColor === \"3\") return \"truecolor\"\n // Any other truthy value defaults to basic\n return \"basic\"\n }\n\n // Non-TTY without FORCE_COLOR - no colors\n if (!stdout.isTTY) {\n return null\n }\n\n // Dumb terminal\n if (process.env.TERM === \"dumb\") {\n return null\n }\n\n // COLORTERM=truecolor indicates 24-bit support\n const colorTerm = process.env.COLORTERM\n if (colorTerm === \"truecolor\" || colorTerm === \"24bit\") {\n return \"truecolor\"\n }\n\n // Check TERM for color hints\n const term = process.env.TERM ?? \"\"\n\n // Known truecolor terminals\n if (\n term.includes(\"truecolor\") ||\n term.includes(\"24bit\") ||\n term.includes(\"xterm-ghostty\") ||\n term.includes(\"xterm-kitty\") ||\n term.includes(\"wezterm\")\n ) {\n return \"truecolor\"\n }\n\n // 256-color terminals\n if (term.includes(\"256color\") || term.includes(\"256\")) {\n return \"256\"\n }\n\n // Modern macOS terminals typically support truecolor\n const termProgram = process.env.TERM_PROGRAM\n if (termProgram === \"iTerm.app\" || termProgram === \"Apple_Terminal\") {\n return termProgram === \"iTerm.app\" ? \"truecolor\" : \"256\"\n }\n\n // Ghostty, WezTerm, Kitty via TERM_PROGRAM\n if (termProgram === \"Ghostty\" || termProgram === \"WezTerm\") {\n return \"truecolor\"\n }\n\n // Kitty via env var\n if (process.env.KITTY_WINDOW_ID) {\n return \"truecolor\"\n }\n\n // xterm-color variants get basic colors\n if (term.includes(\"xterm\") || term.includes(\"color\") || term.includes(\"ansi\")) {\n return \"basic\"\n }\n\n // CI environments usually support basic colors\n if (CI_ENVS.some((env) => process.env[env] !== undefined)) {\n return \"basic\"\n }\n\n // Windows Terminal (modern)\n if (process.env.WT_SESSION) {\n return \"truecolor\"\n }\n\n // Default: basic colors if TTY\n return \"basic\"\n}\n\n// =============================================================================\n// Unicode Detection\n// =============================================================================\n\n/**\n * Detect if terminal can render unicode symbols.\n * Based on TERM, locale, and known terminal apps.\n */\nexport function detectUnicode(): boolean {\n // CI environments - often UTF-8 capable but be conservative\n if (process.env.CI) {\n // GitHub Actions is UTF-8\n if (process.env.GITHUB_ACTIONS) return true\n // Other CI - check LANG\n }\n\n // Check locale for UTF-8\n const lang = process.env.LANG ?? process.env.LC_ALL ?? process.env.LC_CTYPE ?? \"\"\n if (lang.toLowerCase().includes(\"utf-8\") || lang.toLowerCase().includes(\"utf8\")) {\n return true\n }\n\n // Windows Terminal\n if (process.env.WT_SESSION) {\n return true\n }\n\n // Modern terminal programs\n const termProgram = process.env.TERM_PROGRAM ?? \"\"\n if ([\"iTerm.app\", \"Ghostty\", \"WezTerm\", \"Apple_Terminal\"].includes(termProgram)) {\n return true\n }\n\n // Kitty\n if (process.env.KITTY_WINDOW_ID) {\n return true\n }\n\n // Check TERM for modern terminals\n const term = process.env.TERM ?? \"\"\n if (term.includes(\"xterm\") || term.includes(\"rxvt\") || term.includes(\"screen\") || term.includes(\"tmux\")) {\n return true\n }\n\n // Default: assume no unicode for safety\n return false\n}\n\n// =============================================================================\n// Extended Underline Detection\n// =============================================================================\n\n/**\n * Known terminals with extended underline support.\n */\nconst EXTENDED_UNDERLINE_TERMS = [\"xterm-ghostty\", \"xterm-kitty\", \"wezterm\", \"xterm-256color\"]\n\n/**\n * Known terminal programs with extended underline support.\n */\nconst EXTENDED_UNDERLINE_PROGRAMS = [\"Ghostty\", \"iTerm.app\", \"WezTerm\"]\n\n/**\n * Detect if terminal supports extended underline styles.\n * (curly, dotted, dashed, double)\n *\n * Extended underlines use SGR 4:x (style) and SGR 58;2;r;g;b (color).\n * These are NOT supported by Terminal.app, which misinterprets them\n * as background colors causing visual artifacts.\n */\nexport function detectExtendedUnderline(): boolean {\n const term = process.env.TERM ?? \"\"\n const termProgram = process.env.TERM_PROGRAM ?? \"\"\n\n // Apple Terminal doesn't support extended underlines - check FIRST\n // because it often sets TERM=xterm-256color which would otherwise match\n if (termProgram === \"Apple_Terminal\") {\n return false\n }\n\n // Check TERM variable for known modern terminals\n if (EXTENDED_UNDERLINE_TERMS.some((t) => term.includes(t))) {\n return true\n }\n\n // Check TERM_PROGRAM for known terminal applications\n if (EXTENDED_UNDERLINE_PROGRAMS.some((p) => termProgram.includes(p))) {\n return true\n }\n\n // Kitty sets KITTY_WINDOW_ID\n if (process.env.KITTY_WINDOW_ID) {\n return true\n }\n\n // Default to false for unknown terminals\n return false\n}\n\n// =============================================================================\n// Terminal Capabilities Profile\n// =============================================================================\n\nexport interface TerminalCaps {\n /** Terminal program name (from TERM_PROGRAM) */\n program: string\n /** TERM value */\n term: string\n /** Color support level */\n colorLevel: \"none\" | \"basic\" | \"256\" | \"truecolor\"\n /** Kitty keyboard protocol supported */\n kittyKeyboard: boolean\n /** Kitty graphics protocol (inline images) */\n kittyGraphics: boolean\n /** Sixel graphics supported */\n sixel: boolean\n /** OSC 52 clipboard */\n osc52: boolean\n /** OSC 8 hyperlinks */\n hyperlinks: boolean\n /** OSC 9/99 notifications */\n notifications: boolean\n /** Bracketed paste mode */\n bracketedPaste: boolean\n /** SGR mouse tracking */\n mouse: boolean\n /** Synchronized output (DEC 2026) */\n syncOutput: boolean\n /** Unicode/emoji support */\n unicode: boolean\n /** SGR 4:x underline style subparameters (curly, dotted, dashed) */\n underlineStyles: boolean\n /** SGR 58 underline color */\n underlineColor: boolean\n /** Text-presentation emoji (⚠, ☑, ⭐) rendered as 2-wide.\n * Modern terminals (Ghostty, iTerm, Kitty) render these at emoji width (2 cells).\n * Terminal.app renders them at text width (1 cell). */\n textEmojiWide: boolean\n /** OSC 66 text sizing protocol likely supported (Kitty 0.40+, Ghostty) */\n textSizingSupported: boolean\n /** Heuristic: likely dark background (for theme selection) */\n darkBackground: boolean\n /** Heuristic: likely has Nerd Font installed (for icon selection) */\n nerdfont: boolean\n}\n\n/**\n * Default capabilities (assumes modern terminal with full support).\n */\nexport function defaultCaps(): TerminalCaps {\n return {\n program: \"\",\n term: \"\",\n colorLevel: \"truecolor\",\n kittyKeyboard: false,\n kittyGraphics: false,\n sixel: false,\n osc52: false,\n hyperlinks: false,\n notifications: false,\n bracketedPaste: true,\n mouse: true,\n syncOutput: false,\n unicode: true,\n underlineStyles: true,\n underlineColor: true,\n textEmojiWide: true,\n textSizingSupported: false,\n darkBackground: true,\n nerdfont: false,\n }\n}\n\n/**\n * Cached result of macOS dark mode detection.\n * Computed lazily on first access to avoid spawnSync at module load time.\n */\nlet cachedMacOSDarkMode: boolean | undefined\n\n/**\n * Check if macOS is in dark mode by reading the system appearance preference.\n * Uses `defaults read -g AppleInterfaceStyle` — returns \"Dark\" when dark mode\n * is active, exits non-zero when light mode. ~2ms via spawnSync.\n *\n * Result is cached after first call to avoid repeated process spawns.\n */\nfunction detectMacOSDarkMode(): boolean {\n if (cachedMacOSDarkMode !== undefined) return cachedMacOSDarkMode\n\n try {\n const result = spawnSync(\"defaults\", [\"read\", \"-g\", \"AppleInterfaceStyle\"], {\n encoding: \"utf-8\",\n timeout: 500,\n })\n cachedMacOSDarkMode = result.stdout?.trim() === \"Dark\"\n } catch {\n cachedMacOSDarkMode = false\n }\n\n return cachedMacOSDarkMode\n}\n\n/** Detect terminal capabilities from environment variables.\n * Synchronous. Minimal I/O: may run `defaults` on macOS for Apple_Terminal.\n */\nexport function detectTerminalCaps(): TerminalCaps {\n const program = process.env.TERM_PROGRAM ?? \"\"\n const term = process.env.TERM ?? \"\"\n const colorTerm = process.env.COLORTERM ?? \"\"\n const noColor = process.env.NO_COLOR !== undefined\n\n const isAppleTerminal = program === \"Apple_Terminal\"\n\n let colorLevel: TerminalCaps[\"colorLevel\"] = \"none\"\n if (!noColor) {\n if (isAppleTerminal) {\n colorLevel = \"256\"\n } else if (colorTerm === \"truecolor\" || colorTerm === \"24bit\") {\n colorLevel = \"truecolor\"\n } else if (term.includes(\"256color\")) {\n colorLevel = \"256\"\n } else if (process.stdout?.isTTY) {\n colorLevel = \"basic\"\n }\n }\n\n const isKitty = term === \"xterm-kitty\"\n const isITerm = program === \"iTerm.app\"\n const isGhostty = program === \"ghostty\"\n const isWezTerm = program === \"WezTerm\"\n const isAlacritty = program === \"Alacritty\"\n const isFoot = term === \"foot\" || term === \"foot-extra\"\n const isModern = isKitty || isITerm || isGhostty || isWezTerm || isFoot\n\n // Kitty v0.40+ supports OSC 66 text sizing\n let isKittyWithTextSizing = false\n if (isKitty) {\n const version = process.env.TERM_PROGRAM_VERSION ?? \"\"\n const parts = version.split(\".\")\n const major = Number(parts[0]) || 0\n const minor = Number(parts[1]) || 0\n isKittyWithTextSizing = major > 0 || (major === 0 && minor >= 40)\n }\n\n let darkBackground = !isAppleTerminal\n const colorFgBg = process.env.COLORFGBG\n if (colorFgBg) {\n const parts = colorFgBg.split(\";\")\n const bg = parseInt(parts[parts.length - 1] ?? \"\", 10)\n if (!isNaN(bg)) {\n darkBackground = bg < 7\n }\n } else if (isAppleTerminal) {\n darkBackground = detectMacOSDarkMode()\n }\n\n let nerdfont = isModern || isAlacritty\n const nfEnv = process.env.NERDFONT\n if (nfEnv === \"0\" || nfEnv === \"false\") nerdfont = false\n else if (nfEnv === \"1\" || nfEnv === \"true\") nerdfont = true\n\n const underlineExtensions = isModern || isAlacritty\n\n return {\n program,\n term,\n colorLevel,\n kittyKeyboard: isKitty || isGhostty || isWezTerm || isFoot,\n kittyGraphics: isKitty || isGhostty,\n sixel: isFoot || isWezTerm,\n osc52: isModern || isAlacritty,\n hyperlinks: isModern || isAlacritty,\n notifications: isITerm || isKitty,\n bracketedPaste: true,\n mouse: true,\n syncOutput: isModern || isAlacritty,\n unicode: true,\n underlineStyles: underlineExtensions,\n underlineColor: underlineExtensions,\n textEmojiWide: !isAppleTerminal,\n textSizingSupported: isKittyWithTextSizing, // Ghostty parses OSC 66 but doesn't render it (v1.3.0)\n darkBackground,\n nerdfont,\n }\n}\n",
6
+ "/**\n * Keyboard Constants and Utilities\n *\n * Single source of truth for all key parsing, mapping, and matching in silvery.\n *\n * - KEY_MAP: Playwright key names -> ANSI sequences (for sending input)\n * - CODE_TO_KEY: ANSI escape suffixes -> key names (for parsing input)\n * - Key interface: structured key object with boolean flags\n * - parseKeypress(): raw terminal input -> ParsedKeypress\n * - parseKey(): raw terminal input -> [input, Key]\n * - keyToAnsi(): Playwright key string -> ANSI sequence\n * - keyToName(): Key object -> named key string\n * - keyToModifiers(): Key object -> modifier flags\n * - parseHotkey(): \"ctrl+shift+a\" -> ParsedHotkey\n * - matchHotkey(): match ParsedHotkey against Key\n *\n * @example\n * ```tsx\n * import { keyToAnsi } from '@silvery/test'\n *\n * // Convert key names to ANSI\n * keyToAnsi('Enter') // '\\r'\n * keyToAnsi('ArrowUp') // '\\x1b[A'\n * keyToAnsi('Control+c') // '\\x03'\n * keyToAnsi('a') // 'a'\n * ```\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Key object describing which special keys/modifiers were pressed.\n */\nexport interface Key {\n /** Up arrow key was pressed */\n upArrow: boolean\n /** Down arrow key was pressed */\n downArrow: boolean\n /** Left arrow key was pressed */\n leftArrow: boolean\n /** Right arrow key was pressed */\n rightArrow: boolean\n /** Page Down key was pressed */\n pageDown: boolean\n /** Page Up key was pressed */\n pageUp: boolean\n /** Home key was pressed */\n home: boolean\n /** End key was pressed */\n end: boolean\n /** Return (Enter) key was pressed */\n return: boolean\n /** Escape key was pressed */\n escape: boolean\n /** Ctrl key was pressed */\n ctrl: boolean\n /** Shift key was pressed */\n shift: boolean\n /** Tab key was pressed */\n tab: boolean\n /** Backspace key was pressed */\n backspace: boolean\n /** Delete key was pressed */\n delete: boolean\n /** Meta key (Alt/Option on macOS, Alt on other platforms) was pressed */\n meta: boolean\n /** Super key (Cmd on macOS, Win on Windows) was pressed. Requires Kitty protocol. */\n super: boolean\n /** Hyper key was pressed. Requires Kitty protocol. */\n hyper: boolean\n /** CapsLock is active. Requires Kitty protocol. */\n capsLock: boolean\n /** NumLock is active. Requires Kitty protocol. */\n numLock: boolean\n /** Kitty event type. Only set with Kitty flag 2 (report events). */\n eventType?: \"press\" | \"repeat\" | \"release\"\n}\n\n/**\n * Input handler callback type.\n * Return 'exit' to exit the app.\n */\nexport type InputHandler = (input: string, key: Key) => void | \"exit\"\n\n/**\n * Parsed hotkey from a string like \"ctrl+shift+a\" or \"Control+ArrowUp\".\n */\nexport interface ParsedHotkey {\n key: string\n ctrl: boolean\n meta: boolean\n shift: boolean\n alt: boolean\n super: boolean\n hyper: boolean\n}\n\n// ============================================================================\n// Key -> ANSI Mapping (for sending input)\n// ============================================================================\n\n/**\n * Playwright-compatible key names -> ANSI sequences.\n * Keys that are modifier-only (Control, Shift, etc.) are null.\n */\nconst KEY_MAP: Record<string, string | null> = {\n // Navigation (Playwright names)\n ArrowUp: \"\\x1b[A\",\n ArrowDown: \"\\x1b[B\",\n ArrowLeft: \"\\x1b[D\",\n ArrowRight: \"\\x1b[C\",\n Home: \"\\x1b[H\",\n End: \"\\x1b[F\",\n PageUp: \"\\x1b[5~\",\n PageDown: \"\\x1b[6~\",\n\n // Editing\n Enter: \"\\r\",\n Tab: \"\\t\",\n Backspace: \"\\x7f\",\n Delete: \"\\x1b[3~\",\n Escape: \"\\x1b\",\n Space: \" \",\n\n // Modifiers (prefix only, not standalone sequences)\n Control: null,\n Shift: null,\n Alt: null,\n Meta: null,\n Super: null,\n Hyper: null,\n}\n\nconst MODIFIER_ALIASES: Record<string, string> = {\n ctrl: \"Control\",\n control: \"Control\",\n \"⌃\": \"Control\",\n shift: \"Shift\",\n \"⇧\": \"Shift\",\n alt: \"Alt\",\n meta: \"Meta\",\n opt: \"Alt\",\n option: \"Alt\",\n \"⌥\": \"Alt\",\n cmd: \"Super\",\n command: \"Super\",\n super: \"Super\",\n \"⌘\": \"Super\",\n hyper: \"Hyper\",\n \"✦\": \"Hyper\",\n}\n\n/** Modifier symbols that can be used as prefixes without + separator (e.g. ⌘J, ⌃⇧J) */\nconst MODIFIER_SYMBOLS = new Set([\"⌃\", \"⇧\", \"⌥\", \"⌘\", \"✦\"])\n\nfunction normalizeModifier(mod: string): string {\n return MODIFIER_ALIASES[mod.toLowerCase()] ?? mod\n}\n\n/**\n * Convert Playwright-style key string to ANSI sequence.\n *\n * Supports:\n * - Single characters: 'a', 'A', '1', etc.\n * - Named keys: 'Enter', 'ArrowUp', 'Escape', etc.\n * - Modifier combos: 'Control+c', 'Shift+Tab', 'Control+Shift+a'\n * - Lowercase modifier aliases: 'ctrl+c', 'shift+Tab', 'alt+x'\n *\n * @example\n * ```tsx\n * keyToAnsi('Enter') // '\\r'\n * keyToAnsi('ArrowUp') // '\\x1b[A'\n * keyToAnsi('Control+c') // '\\x03'\n * keyToAnsi('j') // 'j'\n * ```\n */\nexport function keyToAnsi(key: string): string {\n // Split on + for combos: 'Control+Shift+a' -> ['Control', 'Shift', 'a']\n const parts = key.split(\"+\")\n const mainKey = parts.pop()!\n // Normalize modifier aliases: ctrl->Control, shift->Shift, alt->Alt, meta->Meta\n const modifiers = parts.map(normalizeModifier)\n\n // Super/Hyper modifiers require Kitty keyboard protocol encoding\n // (standard ANSI cannot represent Cmd/Super)\n if (modifiers.includes(\"Super\") || modifiers.includes(\"Hyper\")) {\n return keyToKittyAnsi(key)\n }\n\n // Single char without modifiers\n if (!modifiers.length && mainKey.length === 1) {\n return mainKey\n }\n\n // Ctrl+letter -> control code (ASCII 1-26)\n if (modifiers.includes(\"Control\") && mainKey.length === 1) {\n const code = mainKey.toLowerCase().charCodeAt(0) - 96\n if (code >= 1 && code <= 26) return String.fromCharCode(code)\n }\n\n // Ctrl+/ -> \\x1f (Unit Separator, standard terminal convention)\n if (modifiers.includes(\"Control\") && mainKey === \"/\") {\n return \"\\x1f\"\n }\n\n // Ctrl+Enter -> \\n (legacy terminal: \\r = Enter, \\n = Ctrl+Enter/Ctrl+J)\n if (modifiers.includes(\"Control\") && mainKey === \"Enter\") {\n return \"\\n\"\n }\n\n // Alt+key -> ESC prefix (standard terminal convention)\n // Alt/Meta/Option keys send ESC followed by the key\n if ((modifiers.includes(\"Alt\") || modifiers.includes(\"Meta\")) && mainKey.length === 1) {\n return `\\x1b${mainKey}`\n }\n\n // Shift+Tab -> backtab (universally \\x1b[Z across all terminal emulators)\n if (modifiers.includes(\"Shift\") && mainKey === \"Tab\") {\n return \"\\x1b[Z\"\n }\n\n // Modified arrow/function keys -> xterm-style CSI 1;modifier sequences\n // E.g. Shift+ArrowUp -> \\x1b[1;2A, Ctrl+ArrowDown -> \\x1b[1;5B\n const ARROW_SUFFIX: Record<string, string> = {\n ArrowUp: \"A\",\n ArrowDown: \"B\",\n ArrowRight: \"C\",\n ArrowLeft: \"D\",\n Home: \"H\",\n End: \"F\",\n }\n if (modifiers.length > 0 && mainKey in ARROW_SUFFIX) {\n let mod = 1\n if (modifiers.includes(\"Shift\")) mod += 1\n if (modifiers.includes(\"Alt\") || modifiers.includes(\"Meta\")) mod += 2\n if (modifiers.includes(\"Control\")) mod += 4\n if (modifiers.includes(\"Super\")) mod += 8\n if (modifiers.includes(\"Hyper\")) mod += 16\n return `\\x1b[1;${mod}${ARROW_SUFFIX[mainKey]}`\n }\n\n // Look up base key in map\n const base = KEY_MAP[mainKey]\n if (base !== undefined && base !== null) return base\n\n // Fallback: return as-is (single char or unknown key)\n return mainKey\n}\n\n// ============================================================================\n// ANSI -> Key Mapping (for parsing input)\n// ============================================================================\n\n/**\n * ANSI escape code suffix -> key name mapping.\n * Used by useInput to parse incoming key sequences.\n *\n * The key is the escape sequence suffix (after ESC or ESC[).\n * Multiple terminal emulators may use different sequences for the same key.\n */\nexport const CODE_TO_KEY: Record<string, string> = {\n // Arrow keys (xterm ESC [ letter)\n \"[A\": \"up\",\n \"[B\": \"down\",\n \"[C\": \"right\",\n \"[D\": \"left\",\n \"[E\": \"clear\",\n \"[F\": \"end\",\n \"[H\": \"home\",\n\n // Arrow keys (xterm/gnome ESC O letter)\n OA: \"up\",\n OB: \"down\",\n OC: \"right\",\n OD: \"left\",\n OE: \"clear\",\n OF: \"end\",\n OH: \"home\",\n\n // Function keys (xterm/gnome ESC O letter)\n OP: \"f1\",\n OQ: \"f2\",\n OR: \"f3\",\n OS: \"f4\",\n\n // Function keys (xterm/rxvt ESC [ number ~)\n \"[11~\": \"f1\",\n \"[12~\": \"f2\",\n \"[13~\": \"f3\",\n \"[14~\": \"f4\",\n \"[15~\": \"f5\",\n \"[17~\": \"f6\",\n \"[18~\": \"f7\",\n \"[19~\": \"f8\",\n \"[20~\": \"f9\",\n \"[21~\": \"f10\",\n \"[23~\": \"f11\",\n \"[24~\": \"f12\",\n\n // Function keys (Cygwin/libuv)\n \"[[A\": \"f1\",\n \"[[B\": \"f2\",\n \"[[C\": \"f3\",\n \"[[D\": \"f4\",\n \"[[E\": \"f5\",\n\n // Navigation keys (xterm/rxvt ESC [ number ~)\n \"[1~\": \"home\",\n \"[2~\": \"insert\",\n \"[3~\": \"delete\",\n \"[4~\": \"end\",\n \"[5~\": \"pageup\",\n \"[6~\": \"pagedown\",\n\n // Navigation keys (putty)\n \"[[5~\": \"pageup\",\n \"[[6~\": \"pagedown\",\n\n // Navigation keys (rxvt)\n \"[7~\": \"home\",\n \"[8~\": \"end\",\n\n // Arrow keys with shift (rxvt lowercase)\n \"[a\": \"up\",\n \"[b\": \"down\",\n \"[c\": \"right\",\n \"[d\": \"left\",\n \"[e\": \"clear\",\n\n // Navigation keys with shift (rxvt $)\n \"[2$\": \"insert\",\n \"[3$\": \"delete\",\n \"[5$\": \"pageup\",\n \"[6$\": \"pagedown\",\n \"[7$\": \"home\",\n \"[8$\": \"end\",\n\n // Arrow keys with ctrl (rxvt O lowercase)\n Oa: \"up\",\n Ob: \"down\",\n Oc: \"right\",\n Od: \"left\",\n Oe: \"clear\",\n\n // Navigation keys with ctrl (rxvt ^)\n \"[2^\": \"insert\",\n \"[3^\": \"delete\",\n \"[5^\": \"pageup\",\n \"[6^\": \"pagedown\",\n \"[7^\": \"home\",\n \"[8^\": \"end\",\n\n // Shift+Tab\n \"[Z\": \"tab\",\n}\n\n// ============================================================================\n// Key Parsing Constants\n// ============================================================================\n\nconst NON_ALPHANUMERIC_KEYS = [\n ...Object.values(CODE_TO_KEY),\n \"backspace\",\n \"tab\",\n \"delete\",\n // Note: 'return', 'enter', 'escape', and 'space' are intentionally NOT included.\n // Users may need the raw character as input ('\\r' for return, '\\x1b' for escape).\n // Use key.return / key.escape boolean flags to detect these keys.\n]\n\nconst SHIFT_CODES = new Set([\"[a\", \"[b\", \"[c\", \"[d\", \"[e\", \"[2$\", \"[3$\", \"[5$\", \"[6$\", \"[7$\", \"[8$\", \"[Z\"])\n\nconst CTRL_CODES = new Set([\"Oa\", \"Ob\", \"Oc\", \"Od\", \"Oe\", \"[2^\", \"[3^\", \"[5^\", \"[6^\", \"[7^\", \"[8^\"])\n\nconst META_KEY_CODE_RE = /^(?:\\x1b)([a-zA-Z0-9])$/\nconst FN_KEY_RE = /^(?:\\x1b+)(O|N|\\[|\\[\\[)(?:(\\d+)(?:;(\\d+))?([~^$])|(?:1;)?(\\d+)?([a-zA-Z]))/\n\n// ============================================================================\n// Kitty Keyboard Protocol\n// ============================================================================\n\n/**\n * Matches Kitty keyboard protocol sequences:\n * CSI codepoint[:shifted[:base]][;modifiers[:event_type][;text_codepoints]] u\n *\n * Groups:\n * 1: codepoint\n * 2: shifted_codepoint (optional)\n * 3: base_layout_key (optional)\n * 4: modifiers (optional, defaults to 1)\n * 5: event_type (optional)\n * 6: text_codepoints (colon-separated, optional — requires REPORT_TEXT flag)\n */\nconst KITTY_RE = /^\\x1b\\[(\\d+)(?::(\\d+))?(?::(\\d+))?(?:;(\\d+)(?::(\\d+))?(?:;([\\d:]+))?)?u$/\n\n/** Matches xterm modifyOtherKeys format: CSI 27 ; modifier ; keycode ~ */\nconst MODIFY_OTHER_KEYS_RE = /^\\x1b\\[27;(\\d+);(\\d+)~$/\n\n/**\n * Kitty-enhanced special key sequences:\n * CSI number ; modifiers : eventType {letter|~}\n * These are legacy CSI sequences enhanced with the :eventType field.\n * Examples: \\x1b[1;1:1A (up arrow press), \\x1b[3;1:3~ (delete release)\n */\nconst KITTY_SPECIAL_RE = /^\\x1b\\[(\\d+);(\\d+):(\\d+)([A-Za-z~])$/\n\n/** Letter-terminated special key names (CSI 1 ; mods letter) */\nconst KITTY_SPECIAL_LETTER_KEYS: Record<string, string> = {\n A: \"up\",\n B: \"down\",\n C: \"right\",\n D: \"left\",\n E: \"clear\",\n F: \"end\",\n H: \"home\",\n P: \"f1\",\n Q: \"f2\",\n R: \"f3\",\n S: \"f4\",\n}\n\n/** Number-terminated special key names (CSI number ; mods ~) */\nconst KITTY_SPECIAL_NUMBER_KEYS: Record<number, string> = {\n 2: \"insert\",\n 3: \"delete\",\n 5: \"pageup\",\n 6: \"pagedown\",\n 7: \"home\",\n 8: \"end\",\n 11: \"f1\",\n 12: \"f2\",\n 13: \"f3\",\n 14: \"f4\",\n 15: \"f5\",\n 17: \"f6\",\n 18: \"f7\",\n 19: \"f8\",\n 20: \"f9\",\n 21: \"f10\",\n 23: \"f11\",\n 24: \"f12\",\n}\n\n/** Valid Unicode codepoint range, excluding surrogates */\nfunction isValidCodepoint(cp: number): boolean {\n return cp >= 0 && cp <= 0x10_ffff && !(cp >= 0xd8_00 && cp <= 0xdf_ff)\n}\n\n/** Safely convert codepoint to string, returning '?' for invalid values */\nfunction safeFromCodePoint(cp: number): string {\n return isValidCodepoint(cp) ? String.fromCodePoint(cp) : \"?\"\n}\n\n/** Maps Kitty codepoints to key names for non-printable/functional keys */\nconst KITTY_CODEPOINT_MAP: Record<number, string> = {\n // Standard control keys\n 8: \"backspace\",\n 9: \"tab\",\n 13: \"return\",\n 27: \"escape\",\n 127: \"delete\",\n // Function keys F13-F35 (F1-F12 use legacy CSI sequences in Kitty mode)\n 57376: \"f13\",\n 57377: \"f14\",\n 57378: \"f15\",\n 57379: \"f16\",\n 57380: \"f17\",\n 57381: \"f18\",\n 57382: \"f19\",\n 57383: \"f20\",\n 57384: \"f21\",\n 57385: \"f22\",\n 57386: \"f23\",\n 57387: \"f24\",\n 57388: \"f25\",\n 57389: \"f26\",\n 57390: \"f27\",\n 57391: \"f28\",\n 57392: \"f29\",\n 57393: \"f30\",\n 57394: \"f31\",\n 57395: \"f32\",\n 57396: \"f33\",\n 57397: \"f34\",\n 57398: \"f35\",\n // Lock/misc keys\n 57358: \"capslock\",\n 57359: \"scrolllock\",\n 57360: \"numlock\",\n 57361: \"printscreen\",\n 57362: \"pause\",\n 57363: \"menu\",\n // Keypad keys\n 57399: \"kp0\",\n 57400: \"kp1\",\n 57401: \"kp2\",\n 57402: \"kp3\",\n 57403: \"kp4\",\n 57404: \"kp5\",\n 57405: \"kp6\",\n 57406: \"kp7\",\n 57407: \"kp8\",\n 57408: \"kp9\",\n 57409: \"kpdecimal\",\n 57410: \"kpdivide\",\n 57411: \"kpmultiply\",\n 57412: \"kpsubtract\",\n 57413: \"kpadd\",\n 57414: \"kpenter\",\n 57415: \"kpequal\",\n 57416: \"kpseparator\",\n 57417: \"kpleft\",\n 57418: \"kpright\",\n 57419: \"kpup\",\n 57420: \"kpdown\",\n 57421: \"kppageup\",\n 57422: \"kppagedown\",\n 57423: \"kphome\",\n 57424: \"kpend\",\n 57425: \"kpinsert\",\n 57426: \"kpdelete\",\n 57427: \"kpbegin\",\n // Media keys\n 57428: \"mediaplay\",\n 57429: \"mediapause\",\n 57430: \"mediaplaypause\",\n 57431: \"mediareverse\",\n 57432: \"mediastop\",\n 57433: \"mediafastforward\",\n 57434: \"mediarewind\",\n 57435: \"mediatracknext\",\n 57436: \"mediatrackprevious\",\n 57437: \"mediarecord\",\n 57438: \"lowervolume\",\n 57439: \"raisevolume\",\n 57440: \"mutevolume\",\n // Modifier-only keys\n 57441: \"leftshift\",\n 57442: \"leftcontrol\",\n 57443: \"leftalt\",\n 57444: \"leftsuper\",\n 57445: \"lefthyper\",\n 57446: \"leftmeta\",\n 57447: \"rightshift\",\n 57448: \"rightcontrol\",\n 57449: \"rightalt\",\n 57450: \"rightsuper\",\n 57451: \"righthyper\",\n 57452: \"rightmeta\",\n 57453: \"isoLevel3Shift\",\n 57454: \"isoLevel5Shift\",\n}\n\n/** Lookup a Kitty codepoint to a key name */\nfunction kittyCodepointToName(cp: number): string | undefined {\n return KITTY_CODEPOINT_MAP[cp]\n}\n\n/** Convert numeric Kitty event type (1/2/3) to string. */\nfunction numericToEventType(n: number): \"press\" | \"repeat\" | \"release\" | undefined {\n if (n === 1) return \"press\"\n if (n === 2) return \"repeat\"\n if (n === 3) return \"release\"\n return undefined\n}\n\n// ============================================================================\n// Key Parsing\n// ============================================================================\n\nexport interface ParsedKeypress {\n name: string\n ctrl: boolean\n meta: boolean\n shift: boolean\n option: boolean\n super: boolean\n hyper: boolean\n /** Kitty event type. Only set with Kitty flag 2 (report events). */\n eventType?: \"press\" | \"repeat\" | \"release\"\n /** The character when Shift is held. From Kitty shifted_codepoint. */\n shiftedKey?: string\n /** The key on a standard US layout (for non-Latin keyboards). From Kitty base_layout_key. */\n baseLayoutKey?: string\n /** CapsLock is active. Kitty modifier bit 6. */\n capsLock?: boolean\n /** NumLock is active. Kitty modifier bit 7. */\n numLock?: boolean\n /** Decoded text from Kitty REPORT_TEXT mode. */\n associatedText?: string\n sequence: string\n /** Raw input string, identical to sequence for most keys. */\n raw?: string\n code?: string\n /** Whether this key was parsed from the Kitty keyboard protocol. */\n isKittyProtocol?: boolean\n /**\n * Whether this key represents printable text input.\n * When false, the key is a control/function/modifier key that should not\n * produce text input (e.g., arrows, function keys, capslock, media keys).\n * Only set by the kitty protocol parser.\n */\n isPrintable?: boolean\n /**\n * Text associated with the key.\n * For printable kitty keys, defaults to the character from the codepoint.\n * When REPORT_TEXT flag is active, contains the decoded text-as-codepoints.\n */\n text?: string\n}\n\n/**\n * Parse a raw input sequence into a structured keypress object.\n * Accepts string or Buffer (Buffer support for stdin compatibility).\n */\nexport function parseKeypress(s: string | Buffer): ParsedKeypress {\n let input: string\n\n if (typeof Buffer !== \"undefined\" && Buffer.isBuffer(s)) {\n if (s[0] !== undefined && s[0]! > 127 && s[1] === undefined) {\n const buf = Buffer.from(s)\n buf[0]! -= 128\n input = `\\x1b${buf.toString()}`\n } else {\n input = s.toString()\n }\n } else {\n input = typeof s === \"string\" ? (s ?? \"\") : String(s)\n }\n\n const key: ParsedKeypress = {\n name: \"\",\n ctrl: false,\n meta: false,\n shift: false,\n option: false,\n super: false,\n hyper: false,\n sequence: input,\n }\n\n if (input === \"\\r\") {\n key.name = \"return\"\n } else if (input === \"\\n\") {\n // In legacy terminal mode, Enter sends \\r. The only way to get \\n is\n // Ctrl+Enter (or Ctrl+J, same byte). Treat it as ctrl+return so\n // TextArea's submitKey=\"ctrl+enter\" works without Kitty protocol.\n key.name = \"return\"\n key.ctrl = true\n } else if (input === \"\\t\") {\n key.name = \"tab\"\n } else if (input === \"\\b\" || input === \"\\x1b\\b\") {\n key.name = \"backspace\"\n key.meta = input.charAt(0) === \"\\x1b\"\n } else if (input === \"\\x7f\" || input === \"\\x1b\\x7f\") {\n // Modern terminals send \\x7f for Backspace key (not \\x08).\n // The actual Delete key sends \\x1b[3~ (handled by CODE_TO_KEY).\n key.name = \"backspace\"\n key.meta = input.charAt(0) === \"\\x1b\"\n } else if (input === \"\\x1b\\r\") {\n // Meta + Return (Alt+Enter / Option+Return on macOS)\n key.name = \"return\"\n key.meta = true\n } else if (input === \"\\x1b\" || input === \"\\x1b\\x1b\") {\n key.name = \"escape\"\n key.meta = input.length === 2\n } else if (input === \" \" || input === \"\\x1b \") {\n key.name = \"space\"\n key.meta = input.length === 2\n } else if (input.length === 1 && input <= \"\\x1a\") {\n // ctrl+letter\n key.name = String.fromCharCode(input.charCodeAt(0) + \"a\".charCodeAt(0) - 1)\n key.ctrl = true\n } else if (input === \"\\x1f\") {\n // Ctrl+/ sends 0x1F (Unit Separator) in terminals\n key.name = \"/\"\n key.ctrl = true\n } else if (input.length === 1 && input >= \"0\" && input <= \"9\") {\n key.name = \"number\"\n } else if (input.length === 1 && input >= \"a\" && input <= \"z\") {\n key.name = input\n } else if (input.length === 1 && input >= \"A\" && input <= \"Z\") {\n key.name = input.toLowerCase()\n key.shift = true\n } else {\n // Try Kitty keyboard protocol first (CSI codepoint ; modifiers u)\n // Must be checked before FN_KEY_RE because 'u' matches [a-zA-Z]\n const kittyParts = KITTY_RE.exec(input)\n // Kitty-enhanced special keys: CSI number ; modifiers : eventType {letter|~}\n const kittySpecialParts = !kittyParts && KITTY_SPECIAL_RE.exec(input)\n // xterm modifyOtherKeys format: CSI 27 ; modifier ; keycode ~\n // Sent by Ghostty, xterm, and others for modified keys like Ctrl+Enter\n const modifyOtherKeysParts = !kittyParts && !kittySpecialParts && MODIFY_OTHER_KEYS_RE.exec(input)\n\n if (kittySpecialParts) {\n // Kitty-enhanced special key: CSI number ; modifiers : eventType {letter|~}\n const number = Number(kittySpecialParts[1])\n const modifier = Math.max(0, Number(kittySpecialParts[2]) - 1)\n const eventType = Number(kittySpecialParts[3])\n const terminator = kittySpecialParts[4]!\n\n const name = terminator === \"~\" ? KITTY_SPECIAL_NUMBER_KEYS[number] : KITTY_SPECIAL_LETTER_KEYS[terminator]\n\n key.isKittyProtocol = true\n key.isPrintable = false\n key.raw = input\n key.name = name ?? \"\"\n\n key.shift = !!(modifier & 1)\n key.option = !!(modifier & 2) // alt\n key.ctrl = !!(modifier & 4)\n key.super = !!(modifier & 8)\n key.hyper = !!(modifier & 16)\n key.meta = !!(modifier & 32)\n key.capsLock = !!(modifier & 64)\n key.numLock = !!(modifier & 128)\n\n const eventTypeStr = numericToEventType(eventType)\n if (eventTypeStr) {\n key.eventType = eventTypeStr\n }\n } else if (kittyParts || modifyOtherKeysParts) {\n let codepoint: number\n let modifier: number\n if (kittyParts) {\n codepoint = Number(kittyParts[1])\n modifier = Math.max(0, Number(kittyParts[4] || 1) - 1)\n } else {\n const mokParts = modifyOtherKeysParts as RegExpExecArray\n modifier = Math.max(0, Number(mokParts[1]) - 1)\n codepoint = Number(mokParts[2])\n }\n\n // Mark as kitty protocol\n if (kittyParts) {\n key.isKittyProtocol = true\n key.raw = input\n\n // Handle invalid codepoints (above U+10FFFF or surrogates)\n if (!isValidCodepoint(codepoint)) {\n key.name = \"\"\n key.isPrintable = false\n return key\n }\n }\n\n key.shift = !!(modifier & 1)\n key.option = !!(modifier & 2) // alt (in kitty protocol, bit 2 = alt/option)\n key.ctrl = !!(modifier & 4)\n key.super = !!(modifier & 8) // super (Cmd on macOS)\n key.hyper = !!(modifier & 16) // hyper\n key.meta = !!(modifier & 32) // meta (kitty distinguishes meta from alt)\n key.capsLock = !!(modifier & 64)\n key.numLock = !!(modifier & 128)\n\n // Event type from Kitty protocol (group 5): 1=press, 2=repeat, 3=release\n if (kittyParts?.[5]) {\n const et = numericToEventType(Number(kittyParts[5]))\n if (et) key.eventType = et\n }\n\n // Shifted codepoint (group 2)\n if (kittyParts?.[2]) {\n key.shiftedKey = String.fromCodePoint(Number(kittyParts[2]))\n }\n\n // Base layout key (group 3)\n if (kittyParts?.[3]) {\n key.baseLayoutKey = String.fromCodePoint(Number(kittyParts[3]))\n }\n\n // Text-as-codepoints (group 6) — requires REPORT_TEXT flag\n let textFromProtocol: string | undefined\n if (kittyParts?.[6]) {\n textFromProtocol = kittyParts[6]\n .split(\":\")\n .map((cp) => safeFromCodePoint(Number(cp)))\n .join(\"\")\n key.associatedText = textFromProtocol\n key.text = textFromProtocol\n }\n\n // Map codepoint to key name and determine printability\n if (codepoint === 32) {\n key.name = \"space\"\n key.isPrintable = true\n } else if (codepoint === 13) {\n key.name = \"return\"\n key.isPrintable = true\n } else {\n const mapped = kittyCodepointToName(codepoint)\n if (mapped) {\n key.name = mapped\n key.isPrintable = false\n } else if (codepoint >= 1 && codepoint <= 26) {\n // Ctrl+letter comes as codepoint 1-26\n key.name = String.fromCodePoint(codepoint + 96) // 'a' is 97\n key.isPrintable = false\n } else if (codepoint >= 32 && codepoint <= 126) {\n // Printable ASCII\n key.name = String.fromCharCode(codepoint).toLowerCase()\n if (codepoint >= 65 && codepoint <= 90) {\n key.shift = true\n key.name = String.fromCharCode(codepoint + 32)\n }\n key.isPrintable = true\n } else if (isValidCodepoint(codepoint)) {\n key.name = safeFromCodePoint(codepoint)\n key.isPrintable = true\n } else {\n key.name = \"\"\n key.isPrintable = false\n }\n }\n\n // Default text to the character from the codepoint when not explicitly\n // provided by the protocol, so keys like space and return produce their\n // expected text input (' ' and '\\r' respectively).\n // With REPORT_ALL_KEYS, Shift+letter sends lowercase codepoint + shift modifier.\n // Produce uppercase text to match what the user typed.\n if (kittyParts && key.isPrintable && !textFromProtocol) {\n if (key.shift && codepoint >= 97 && codepoint <= 122) {\n key.text = String.fromCharCode(codepoint - 32) // 'a'→'A'\n } else {\n key.text = safeFromCodePoint(codepoint)\n }\n }\n } else if (KITTY_RE.test(input)) {\n // Matched kitty pattern but was rejected (e.g., invalid codepoint in the\n // parseKittyKeypress path above returned early). Return safe empty keypress.\n key.isKittyProtocol = true\n key.isPrintable = false\n key.raw = input\n return key\n } else {\n let parts = META_KEY_CODE_RE.exec(input)\n if (parts) {\n key.meta = true\n key.shift = /^[A-Z]$/.test(parts[1] ?? \"\")\n } else {\n parts = FN_KEY_RE.exec(input)\n if (parts) {\n const segs = input.split(\"\")\n if (segs[0] === \"\\u001b\" && segs[1] === \"\\u001b\") {\n key.option = true\n }\n\n // Reassemble key code\n const code = [parts[1], parts[2], parts[4], parts[6]].filter(Boolean).join(\"\")\n const modifier = (Number(parts[3] || parts[5] || 1) - 1) as number\n\n key.ctrl = !!(modifier & 4)\n key.meta = !!(modifier & 2) // alt\n key.super = !!(modifier & 8) // super (Cmd on macOS)\n key.hyper = !!(modifier & 16) // hyper\n key.shift = !!(modifier & 1)\n key.capsLock = !!(modifier & 64)\n key.numLock = !!(modifier & 128)\n key.code = code\n key.name = CODE_TO_KEY[code] ?? \"\"\n key.shift = SHIFT_CODES.has(code) || key.shift\n key.ctrl = CTRL_CODES.has(code) || key.ctrl\n }\n }\n }\n }\n\n return key\n}\n\n/**\n * Parse raw terminal input into a Key object and cleaned input string.\n *\n * @param rawInput Raw terminal input (string or Buffer)\n * @returns Tuple of [cleanedInput, Key]\n */\nexport function parseKey(rawInput: string | Buffer): [string, Key] {\n const keypress = parseKeypress(rawInput)\n\n const key: Key = {\n upArrow: keypress.name === \"up\",\n downArrow: keypress.name === \"down\",\n leftArrow: keypress.name === \"left\",\n rightArrow: keypress.name === \"right\",\n pageDown: keypress.name === \"pagedown\",\n pageUp: keypress.name === \"pageup\",\n home: keypress.name === \"home\",\n end: keypress.name === \"end\",\n return: keypress.name === \"return\",\n escape: keypress.name === \"escape\",\n ctrl: keypress.ctrl,\n shift: keypress.shift,\n tab: keypress.name === \"tab\",\n backspace: keypress.name === \"backspace\",\n delete: keypress.name === \"delete\",\n meta: keypress.name !== \"escape\" && (keypress.meta || keypress.option),\n super: keypress.super,\n hyper: keypress.hyper,\n capsLock: keypress.capsLock ?? false,\n numLock: keypress.numLock ?? false,\n eventType: keypress.eventType,\n }\n\n let input: string\n\n if (keypress.isKittyProtocol) {\n // Kitty protocol: use text field for printable keys, key name for ctrl+letter\n if (keypress.isPrintable) {\n input = keypress.text ?? keypress.name\n } else if (keypress.ctrl && keypress.name.length === 1) {\n // Ctrl+letter via codepoint 1-26: provide the letter name so handlers\n // (e.g., exitOnCtrlC checking input === 'c' && key.ctrl) still work.\n input = keypress.name\n } else {\n input = \"\"\n }\n } else {\n input = keypress.ctrl ? keypress.name : keypress.sequence\n\n if (NON_ALPHANUMERIC_KEYS.includes(keypress.name)) {\n input = \"\"\n }\n\n // Strip meta prefix if remaining\n if (input.startsWith(\"\\u001b\")) {\n input = input.slice(1)\n }\n\n // Filter out escape sequence fragments that leak through\n // e.g., \"[2~\" from Insert key, \"[A\" from arrows when not fully parsed\n // Single \"[\" and \"]\" are allowed — they're valid key bindings\n if ((input.startsWith(\"[\") && input.length > 1) || (input.startsWith(\"O\") && input.length > 1)) {\n // For Kitty-encoded keys (Super/Hyper modifiers), preserve the key name\n // since the raw sequence was CSI codepoint;modifiers u\n if (keypress.super || keypress.hyper) {\n input = keypress.name\n } else {\n input = \"\"\n }\n }\n }\n\n // Detect shift for uppercase letters\n if (input.length === 1 && typeof input[0] === \"string\" && /[A-Z]/.test(input[0])) {\n key.shift = true\n }\n\n return [input, key]\n}\n\n/**\n * Create an empty Key object (all fields false).\n */\nexport function emptyKey(): Key {\n return {\n upArrow: false,\n downArrow: false,\n leftArrow: false,\n rightArrow: false,\n pageDown: false,\n pageUp: false,\n home: false,\n end: false,\n return: false,\n escape: false,\n ctrl: false,\n shift: false,\n tab: false,\n backspace: false,\n delete: false,\n meta: false,\n super: false,\n hyper: false,\n capsLock: false,\n numLock: false,\n }\n}\n\n// ============================================================================\n// Key Utility Functions\n// ============================================================================\n\n/**\n * Convert a Key object to a named key string.\n *\n * Returns the Playwright-compatible name for special keys (ArrowUp, Enter, etc.)\n * or \"\" if no special key is pressed.\n */\nexport function keyToName(key: Key): string {\n if (key.upArrow) return \"ArrowUp\"\n if (key.downArrow) return \"ArrowDown\"\n if (key.leftArrow) return \"ArrowLeft\"\n if (key.rightArrow) return \"ArrowRight\"\n if (key.return) return \"Enter\"\n if (key.escape) return \"Escape\"\n if (key.backspace) return \"Backspace\"\n if (key.delete) return \"Delete\"\n if (key.tab) return \"Tab\"\n if (key.pageUp) return \"PageUp\"\n if (key.pageDown) return \"PageDown\"\n if (key.home) return \"Home\"\n if (key.end) return \"End\"\n return \"\"\n}\n\n/**\n * Extract modifier flags from a Key object.\n * `alt` is always false (terminals cannot distinguish alt from meta).\n */\nexport function keyToModifiers(key: Key): {\n ctrl: boolean\n meta: boolean\n shift: boolean\n alt: boolean\n super: boolean\n hyper: boolean\n} {\n return {\n ctrl: !!key.ctrl,\n meta: !!key.meta,\n shift: !!key.shift,\n alt: false,\n super: !!key.super,\n hyper: !!key.hyper,\n }\n}\n\n/**\n * Parse a hotkey string into base key and modifiers.\n *\n * Supports Playwright-style (\"Control+c\", \"Shift+ArrowUp\") and\n * lowercase aliases (\"ctrl+c\", \"shift+tab\", \"cmd+a\").\n *\n * @example\n * ```tsx\n * parseHotkey('j') // { key: 'j', ctrl: false, meta: false, shift: false, alt: false }\n * parseHotkey('Control+c') // { key: 'c', ctrl: true, ... }\n * parseHotkey('Shift+ArrowUp') // { key: 'ArrowUp', shift: true, ... }\n * parseHotkey('⌘j') // { key: 'j', super: true, ... } (macOS symbol prefix)\n * parseHotkey('⌃⇧a') // { key: 'a', ctrl: true, shift: true, ... }\n * ```\n */\nexport function parseHotkey(keyStr: string): ParsedHotkey {\n // Support macOS symbol prefix format: ⌘J, ⌃⇧J, ✦⌘J\n let remaining = keyStr\n const symbolMods = new Set<string>()\n for (const char of remaining) {\n if (MODIFIER_SYMBOLS.has(char)) {\n symbolMods.add(char)\n } else {\n break\n }\n }\n\n if (symbolMods.size > 0) {\n remaining = remaining.slice(symbolMods.size)\n if (remaining.startsWith(\"+\")) remaining = remaining.slice(1)\n }\n\n const parts = remaining.split(\"+\")\n const key = parts.pop() || keyStr\n const modifiers = new Set([...parts.map((p) => p.toLowerCase()), ...symbolMods])\n\n return {\n key,\n ctrl: modifiers.has(\"control\") || modifiers.has(\"ctrl\") || modifiers.has(\"⌃\"),\n meta:\n modifiers.has(\"meta\") ||\n modifiers.has(\"alt\") ||\n modifiers.has(\"opt\") ||\n modifiers.has(\"option\") ||\n modifiers.has(\"⌥\"),\n shift: modifiers.has(\"shift\") || modifiers.has(\"⇧\"),\n alt: false, // alt and meta are indistinguishable in terminals; use meta\n super: modifiers.has(\"super\") || modifiers.has(\"cmd\") || modifiers.has(\"command\") || modifiers.has(\"⌘\"),\n hyper: modifiers.has(\"hyper\") || modifiers.has(\"✦\"),\n }\n}\n\n/**\n * Match a parsed hotkey against a Key object and input string.\n *\n * @param hotkey Parsed hotkey to match\n * @param key Key object from input event\n * @param input Optional input string (for matching character keys)\n * @returns true if the hotkey matches the key event\n */\nexport function matchHotkey(hotkey: ParsedHotkey, key: Key, input?: string): boolean {\n // Check modifiers\n if (!!hotkey.ctrl !== !!key.ctrl) return false\n if (!!hotkey.meta !== !!key.meta) return false\n if (!!hotkey.super !== !!key.super) return false\n if (!!hotkey.hyper !== !!key.hyper) return false\n if (!!hotkey.alt !== false) return false // terminals can't distinguish alt from meta\n\n // For single uppercase letters (A-Z), shift is implicit\n const isUppercaseLetter = hotkey.key.length === 1 && hotkey.key >= \"A\" && hotkey.key <= \"Z\" && !hotkey.shift\n if (!isUppercaseLetter && !!hotkey.shift !== !!key.shift) return false\n\n // Check key name against Key boolean fields\n const name = keyToName(key)\n if (name && name === hotkey.key) return true\n\n // Check against input string\n if (input !== undefined && input === hotkey.key) return true\n\n return false\n}\n\n// ============================================================================\n// Kitty Protocol Output\n// ============================================================================\n\n/** Reverse map: key name → Kitty codepoint */\nconst NAME_TO_KITTY_CODEPOINT: Record<string, number> = {}\nfor (const [cp, name] of Object.entries(KITTY_CODEPOINT_MAP)) {\n NAME_TO_KITTY_CODEPOINT[name] = Number(cp)\n}\n\n/**\n * Implicit modifier bits for modifier-only keys.\n * When a modifier key is pressed by itself, real terminals include the\n * corresponding modifier bit in the CSI u sequence. E.g., pressing Cmd alone\n * sends `\\x1b[57444;9u` (codepoint 57444, modifier 9 = super(8) + 1).\n * This map ensures keyToKittyAnsi produces the same encoding.\n */\nconst MODIFIER_KEY_IMPLICIT_BITS: Record<string, number> = {\n leftshift: 1,\n rightshift: 1,\n leftcontrol: 4,\n rightcontrol: 4,\n leftalt: 2,\n rightalt: 2,\n leftsuper: 8,\n rightsuper: 8,\n lefthyper: 16,\n righthyper: 16,\n leftmeta: 32,\n rightmeta: 32,\n}\n\n/** Playwright-style key name → CSI u codepoint for keys using CSI u format */\nconst PLAYWRIGHT_TO_KITTY_CSI_U: Record<string, number> = {\n Enter: 13,\n Escape: 27,\n Backspace: 127,\n Tab: 9,\n Space: 32,\n}\n\n/** Playwright-style key name → Kitty enhanced special key suffix (letter-terminated) */\nconst PLAYWRIGHT_TO_KITTY_SPECIAL_LETTER: Record<string, string> = {\n ArrowUp: \"A\",\n ArrowDown: \"B\",\n ArrowRight: \"C\",\n ArrowLeft: \"D\",\n Home: \"H\",\n End: \"F\",\n F1: \"P\",\n F2: \"Q\",\n F3: \"R\",\n F4: \"S\",\n}\n\n/** Playwright-style key name → Kitty enhanced special key number (tilde-terminated) */\nconst PLAYWRIGHT_TO_KITTY_SPECIAL_TILDE: Record<string, number> = {\n Insert: 2,\n Delete: 3,\n PageUp: 5,\n PageDown: 6,\n F5: 15,\n F6: 17,\n F7: 18,\n F8: 19,\n F9: 20,\n F10: 21,\n F11: 23,\n F12: 24,\n}\n\n/**\n * Convert a Playwright-style key string to a Kitty keyboard protocol ANSI sequence.\n *\n * Uses the appropriate Kitty format for each key type:\n * - Regular keys: CSI codepoint ; modifiers u\n * - Arrow/nav keys: CSI 1 ; modifiers letter (enhanced special key format)\n * - Tilde keys: CSI number ; modifiers ~ (enhanced special key format)\n *\n * @example\n * ```tsx\n * keyToKittyAnsi('a') // '\\x1b[97u' (no modifiers → bare)\n * keyToKittyAnsi('Enter') // '\\x1b[13u'\n * keyToKittyAnsi('Control+c') // '\\x1b[99;5u' (ctrl = 4, modifier = 5)\n * keyToKittyAnsi('Shift+Enter') // '\\x1b[13;2u' (shift = 1, modifier = 2)\n * keyToKittyAnsi('ArrowUp') // '\\x1b[1;1A' (enhanced special key)\n * ```\n */\nexport function keyToKittyAnsi(key: string): string {\n const parts = key.split(\"+\")\n const mainKey = parts.pop()!\n const modifiers = parts.map(normalizeModifier)\n\n // Calculate modifier bitfield\n let mod = 0\n if (modifiers.includes(\"Shift\")) mod |= 1\n if (modifiers.includes(\"Alt\") || modifiers.includes(\"Meta\")) mod |= 2\n if (modifiers.includes(\"Control\")) mod |= 4\n if (modifiers.includes(\"Super\")) mod |= 8\n if (modifiers.includes(\"Hyper\")) mod |= 16\n\n // Check for letter-terminated special keys (arrow keys, home, end, F1-F4)\n const specialLetter = PLAYWRIGHT_TO_KITTY_SPECIAL_LETTER[mainKey]\n if (specialLetter) {\n return `\\x1b[1;${mod + 1}${specialLetter}`\n }\n\n // Check for tilde-terminated special keys (insert, delete, pageup, F5-F12)\n const specialNumber = PLAYWRIGHT_TO_KITTY_SPECIAL_TILDE[mainKey]\n if (specialNumber !== undefined) {\n return `\\x1b[${specialNumber};${mod + 1}~`\n }\n\n // Check CSI u format keys\n const csiUCodepoint = PLAYWRIGHT_TO_KITTY_CSI_U[mainKey]\n if (csiUCodepoint !== undefined) {\n if (mod > 0) {\n return `\\x1b[${csiUCodepoint};${mod + 1}u`\n }\n return `\\x1b[${csiUCodepoint}u`\n }\n\n // Single character — use Unicode codepoint in CSI u format\n if (mainKey.length === 1) {\n const codepoint = mainKey.charCodeAt(0)\n if (mod > 0) {\n return `\\x1b[${codepoint};${mod + 1}u`\n }\n return `\\x1b[${codepoint}u`\n }\n\n // Try lowercase as direct kitty name (e.g., \"return\", \"escape\")\n const cp = NAME_TO_KITTY_CODEPOINT[mainKey.toLowerCase()]\n if (cp !== undefined) {\n // Modifier-only keys: include the implicit modifier bit.\n // When the key IS a modifier (leftsuper, leftshift, etc.), real terminals\n // include the corresponding modifier bit in the sequence. E.g., pressing\n // Cmd sends codepoint 57444 with super bit (8), so modifier = 9.\n const implicitMod = MODIFIER_KEY_IMPLICIT_BITS[mainKey.toLowerCase()]\n if (implicitMod !== undefined) {\n mod |= implicitMod\n }\n if (mod > 0) {\n return `\\x1b[${cp};${mod + 1}u`\n }\n return `\\x1b[${cp}u`\n }\n\n // Fallback: return as-is (not a kitty key)\n return keyToAnsi(key)\n}\n\n// ============================================================================\n// Raw Input Splitting\n// ============================================================================\n\n/** Grapheme segmenter for splitting non-escape text into visual characters */\nconst graphemeSegmenter = new Intl.Segmenter(\"en\", { granularity: \"grapheme\" })\n\n/**\n * Split raw terminal input into individual keypresses.\n *\n * When stdin.read() returns multiple characters buffered together (e.g., rapid\n * typing, paste, or auto-repeat during heavy renders), this tokenizer splits\n * them into individual keypresses so each can be parsed and handled separately.\n *\n * Uses grapheme segmentation for non-escape text, so emoji with variation\n * selectors (❤️), ZWJ sequences (👨‍👩‍👧‍👦), and combining marks stay intact.\n *\n * Handles:\n * - CSI sequences: ESC [ ... (arrow keys, function keys, Kitty protocol)\n * - SS3 sequences: ESC O + letter\n * - Meta sequences: ESC + single char\n * - Double ESC\n * - Grapheme clusters (emoji, combining marks, CJK)\n */\nexport function* splitRawInput(data: string): Generator<string> {\n // Single character fast path (most common case in real terminal I/O)\n if (data.length <= 1) {\n if (data.length === 1) yield data\n return\n }\n\n let i = 0\n let textStart = -1 // start of accumulated non-escape text\n\n while (i < data.length) {\n if (data.charCodeAt(i) === 0x1b) {\n // Flush accumulated text before this escape sequence\n if (textStart >= 0) {\n yield* splitNonEscapeText(data.slice(textStart, i))\n textStart = -1\n }\n\n // ESC — start of escape sequence\n if (i + 1 >= data.length) {\n // Bare ESC at end of chunk\n yield \"\\x1b\"\n i++\n continue\n }\n\n const next = data.charCodeAt(i + 1)\n\n if (next === 0x5b) {\n // CSI sequence: ESC [ params final-byte\n // Final byte is in range 0x40-0x7E (@A-Z[\\]^_`a-z{|}~)\n let j = i + 2\n while (j < data.length) {\n const c = data.charCodeAt(j)\n if (c >= 0x40 && c <= 0x7e) {\n j++ // include the final byte\n break\n }\n j++\n }\n yield data.slice(i, j)\n i = j\n } else if (next === 0x4f) {\n // SS3 sequence: ESC O + one letter\n const end = Math.min(i + 3, data.length)\n yield data.slice(i, end)\n i = end\n } else if (next === 0x1b) {\n // Double ESC: meta + escape, OR meta + CSI/SS3 sequence\n // Check if a control sequence follows the double ESC\n if (i + 2 < data.length) {\n const third = data.charCodeAt(i + 2)\n if (third === 0x5b) {\n // Meta + CSI: ESC ESC [ params final-byte (e.g., meta+arrow)\n let j = i + 3\n while (j < data.length) {\n const c = data.charCodeAt(j)\n if (c >= 0x40 && c <= 0x7e) {\n j++ // include the final byte\n break\n }\n j++\n }\n yield data.slice(i, j)\n i = j\n } else if (third === 0x4f) {\n // Meta + SS3: ESC ESC O letter\n const end = Math.min(i + 4, data.length)\n yield data.slice(i, end)\n i = end\n } else {\n // Plain double ESC (meta+escape)\n yield \"\\x1b\\x1b\"\n i += 2\n }\n } else {\n // Double ESC at end of chunk\n yield \"\\x1b\\x1b\"\n i += 2\n }\n } else {\n // Meta + single char (Alt+key)\n yield data.slice(i, i + 2)\n i += 2\n }\n } else {\n // Non-escape: accumulate into text run\n if (textStart < 0) textStart = i\n i++\n }\n }\n\n // Flush final text run\n if (textStart >= 0) {\n yield* splitNonEscapeText(data.slice(textStart))\n }\n}\n\n/**\n * Split non-escape text, keeping most characters together as one chunk.\n *\n * Only delete (0x7F) and backspace (0x08) are split out individually because\n * they can arrive as rapid repeats in a single stdin chunk. Other control\n * characters like \\r and \\t may legitimately appear inside pasted text and\n * should NOT be split out (matching Ink's input-parser behavior).\n */\nfunction* splitNonEscapeText(text: string): Generator<string> {\n let segmentStart = 0\n for (let i = 0; i < text.length; i++) {\n const ch = text.charCodeAt(i)\n if (ch === 0x7f || ch === 0x08) {\n // Flush text before this delete/backspace\n if (i > segmentStart) {\n yield text.slice(segmentStart, i)\n }\n yield text[i]!\n segmentStart = i + 1\n }\n }\n // Flush remaining text\n if (segmentStart < text.length) {\n yield text.slice(segmentStart)\n }\n}\n\n/** Split a non-escape text run into grapheme clusters */\nfunction* splitGraphemes(text: string): Generator<string> {\n for (const { segment } of graphemeSegmenter.segment(text)) {\n yield segment\n }\n}\n",
7
+ "/**\n * Key parsing for silvery-loop runtime.\n *\n * Re-exports from canonical source in ../keys.ts.\n */\n\nexport type { Key, InputHandler, ParsedKeypress } from \"@silvery/ag/keys\"\nexport { parseKey, emptyKey, parseKeypress, splitRawInput } from \"@silvery/ag/keys\"\n",
8
+ "/**\n * SGR mouse event parsing (mode 1006).\n *\n * SGR format: CSI < button;x;y M (press) or CSI < button;x;y m (release)\n *\n * Button encoding:\n * - Bits 0-1: 0=left, 1=middle, 2=right, 3=release (X10 only, not SGR)\n * - Bit 2 (+4): Shift held\n * - Bit 3 (+8): Meta/Alt held\n * - Bit 4 (+16): Ctrl held\n * - Bit 5 (+32): Motion event (mouse moved while button held)\n * - Bits 6-7: 64=wheel-up, 65=wheel-down, 66=wheel-left, 67=wheel-right\n */\n\n/**\n * Parsed mouse event from SGR mouse protocol (mode 1006).\n */\nexport interface ParsedMouse {\n /** Mouse button: 0=left, 1=middle, 2=right */\n button: number\n /** Column (0-indexed) */\n x: number\n /** Row (0-indexed) */\n y: number\n /** Event action */\n action: \"down\" | \"up\" | \"move\" | \"wheel\"\n /** Wheel delta: -1 for up, +1 for down */\n delta?: number\n /** Shift was held */\n shift: boolean\n /** Alt/Meta was held */\n meta: boolean\n /** Ctrl was held */\n ctrl: boolean\n}\n\nconst SGR_MOUSE_RE = /^\\x1b\\[<(\\d+);(\\d+);(\\d+)([Mm])$/\n\n/**\n * Parse an SGR mouse sequence.\n *\n * @returns ParsedMouse or null if not a valid mouse sequence\n */\nexport function parseMouseSequence(input: string): ParsedMouse | null {\n const m = SGR_MOUSE_RE.exec(input)\n if (!m) return null\n\n const raw = parseInt(m[1]!)\n const x = parseInt(m[2]!) - 1 // 1-indexed → 0-indexed\n const y = parseInt(m[3]!) - 1\n const terminator = m[4]!\n\n const shift = !!(raw & 4)\n const meta = !!(raw & 8)\n const ctrl = !!(raw & 16)\n const motion = !!(raw & 32)\n const isWheel = !!(raw & 64)\n\n if (isWheel) {\n const wheelButton = raw & 3 // 0=up, 1=down, 2=left, 3=right\n return {\n button: 0,\n x,\n y,\n action: \"wheel\",\n delta: wheelButton === 0 ? -1 : 1,\n shift,\n meta,\n ctrl,\n }\n }\n\n const button = raw & 3\n const action = motion ? \"move\" : terminator === \"M\" ? \"down\" : \"up\"\n return { button, x, y, action, shift, meta, ctrl }\n}\n\nconst SGR_MOUSE_TEST_RE = /^\\x1b\\[<\\d+;\\d+;\\d+[Mm]$/\n\n/** Check if a raw input string is a mouse sequence */\nexport function isMouseSequence(input: string): boolean {\n return SGR_MOUSE_TEST_RE.test(input)\n}\n",
9
+ "/**\n * Bracketed Paste Mode\n *\n * Enables bracketed paste so the terminal wraps pasted text with markers.\n * This lets the app distinguish pasted text from typed input and receive\n * it as a single event rather than individual keystrokes.\n *\n * Protocol: DEC private mode 2004\n * - Enable: CSI ? 2004 h\n * - Disable: CSI ? 2004 l\n * - Paste start marker: CSI 200 ~\n * - Paste end marker: CSI 201 ~\n *\n * Supported by: Ghostty, Kitty, WezTerm, iTerm2, Alacritty, xterm, tmux, foot\n */\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Escape sequence that marks the beginning of pasted text */\nexport const PASTE_START = \"\\x1b[200~\"\n\n/** Escape sequence that marks the end of pasted text */\nexport const PASTE_END = \"\\x1b[201~\"\n\n// ============================================================================\n// Protocol Control\n// ============================================================================\n\n/**\n * Enable bracketed paste mode.\n * Writes CSI ? 2004 h to the output stream.\n */\nexport function enableBracketedPaste(stdout: NodeJS.WriteStream): void {\n stdout.write(\"\\x1b[?2004h\")\n}\n\n/**\n * Disable bracketed paste mode.\n * Writes CSI ? 2004 l to the output stream.\n */\nexport function disableBracketedPaste(stdout: NodeJS.WriteStream): void {\n stdout.write(\"\\x1b[?2004l\")\n}\n\n// ============================================================================\n// Parsing\n// ============================================================================\n\n/** Result of parsing a bracketed paste sequence */\nexport interface BracketedPasteResult {\n type: \"paste\"\n content: string\n}\n\n/**\n * Detect and extract bracketed paste content from raw terminal input.\n *\n * Returns the paste content if the input contains a complete bracketed paste\n * sequence (PASTE_START ... PASTE_END), or null if no paste markers are found.\n */\nexport function parseBracketedPaste(input: string): BracketedPasteResult | null {\n const startIdx = input.indexOf(PASTE_START)\n if (startIdx === -1) return null\n\n const contentStart = startIdx + PASTE_START.length\n const endIdx = input.indexOf(PASTE_END, contentStart)\n if (endIdx === -1) return null\n\n return {\n type: \"paste\",\n content: input.slice(contentStart, endIdx),\n }\n}\n",
10
+ "/**\n * Focus Reporting (CSI ?1004h)\n *\n * Enables/disables terminal focus-in/focus-out event reporting.\n * When enabled, the terminal sends CSI I on focus-in and CSI O on focus-out.\n *\n * Protocol:\n * - Enable: CSI ? 1004 h\n * - Disable: CSI ? 1004 l\n * - Focus In: CSI I (\\x1b[I)\n * - Focus Out: CSI O (\\x1b[O)\n *\n * Supported by: xterm (v282+), Ghostty, Kitty, WezTerm, iTerm2, foot, VTE\n */\n\nconst CSI = \"\\x1b[\"\n\n/**\n * Enable terminal focus reporting.\n * After enabling, the terminal will send CSI I / CSI O sequences\n * when the terminal window gains or loses focus.\n */\nexport function enableFocusReporting(write: (data: string) => void): void {\n write(`${CSI}?1004h`)\n}\n\n/**\n * Disable terminal focus reporting.\n */\nexport function disableFocusReporting(write: (data: string) => void): void {\n write(`${CSI}?1004l`)\n}\n\n/**\n * Parse a focus event from terminal input.\n *\n * @param input Raw terminal input string\n * @returns Parsed focus event, or null if not a focus sequence\n */\nexport function parseFocusEvent(input: string): { type: \"focus-in\" | \"focus-out\" } | null {\n if (input.includes(`${CSI}I`)) {\n return { type: \"focus-in\" }\n }\n if (input.includes(`${CSI}O`)) {\n return { type: \"focus-out\" }\n }\n return null\n}\n",
11
+ "/**\n * Terminal provider - wraps stdin/stdout as a Provider.\n *\n * This makes the terminal \"just another provider\" - no special handling needed.\n *\n * @example\n * ```typescript\n * const term = createTermProvider(process.stdin, process.stdout);\n *\n * // State\n * console.log(term.getState()); // { cols: 80, rows: 24 }\n *\n * // Events\n * for await (const event of term.events()) {\n * if (event.type === 'key') console.log('Key:', event.data.input);\n * if (event.type === 'resize') console.log('Resize:', event.data);\n * }\n *\n * // Cleanup\n * term[Symbol.dispose]();\n * ```\n */\n\nimport { type Key, parseKey } from \"./keys\"\nimport { isMouseSequence, parseMouseSequence, type ParsedMouse } from \"../mouse\"\nimport { parseBracketedPaste, enableBracketedPaste, disableBracketedPaste } from \"../bracketed-paste\"\nimport { enableFocusReporting, disableFocusReporting, parseFocusEvent } from \"../focus-reporting\"\nimport type { Dims, Provider, ProviderEvent } from \"./types\"\n\n// ============================================================================\n// Input Splitting\n// ============================================================================\n\n/**\n * Result of splitting raw input — includes parsed sequences and any\n * trailing incomplete CSI sequence that needs cross-chunk buffering.\n */\ninterface SplitResult {\n /** Fully parsed key/mouse sequences */\n sequences: string[]\n /** Incomplete CSI sequence at end of chunk (needs next chunk to complete) */\n incomplete: string | null\n}\n\n/**\n * Split a raw stdin chunk into individual key sequences.\n *\n * When the OS buffers key repeat events, stdin delivers multiple keystrokes\n * in a single read (e.g., \"jjjjj\" for held 'j'). parseKey expects one\n * keystroke at a time, so we must split first.\n *\n * When a CSI sequence (ESC [ ...) ends at the chunk boundary without a\n * terminator, it is returned as `incomplete` so the caller can buffer it\n * and prepend to the next chunk. This handles SGR mouse sequences that\n * split across stdin data events (e.g., '\\x1b[<0;58;8' + 'M').\n *\n * Strategy:\n * - ESC followed by [ or O starts a multi-byte sequence — consume until terminator\n * - ESC alone or ESC + single char is a 2-byte meta sequence\n * - Everything else is a single byte\n */\nfunction splitRawInput(raw: string): SplitResult {\n const sequences: string[] = []\n let i = 0\n while (i < raw.length) {\n if (raw[i] === \"\\x1b\") {\n // Escape sequence\n if (i + 1 >= raw.length) {\n // Bare ESC at end\n sequences.push(\"\\x1b\")\n i++\n } else if (raw[i + 1] === \"[\") {\n // CSI sequence: ESC [ ... <letter or ~>\n let j = i + 2\n while (j < raw.length && !isCSITerminator(raw[j]!)) j++\n if (j < raw.length) {\n j++ // include terminator\n sequences.push(raw.slice(i, j))\n i = j\n } else {\n // Incomplete CSI — hit end of chunk without finding terminator.\n // Return it as incomplete so caller can buffer for next chunk.\n return { sequences, incomplete: raw.slice(i) }\n }\n } else if (raw[i + 1] === \"O\") {\n // SS3 sequence: ESC O <letter>\n const end = Math.min(i + 3, raw.length)\n sequences.push(raw.slice(i, end))\n i = end\n } else if (raw[i + 1] === \"\\x1b\") {\n // Double ESC: meta + escape, OR meta + CSI/SS3 sequence\n if (i + 2 < raw.length && raw[i + 2] === \"[\") {\n // Meta + CSI: ESC ESC [ params terminator (e.g., meta+arrow)\n let j = i + 3\n while (j < raw.length && !isCSITerminator(raw[j]!)) j++\n if (j < raw.length) {\n j++ // include terminator\n sequences.push(raw.slice(i, j))\n i = j\n } else {\n return { sequences, incomplete: raw.slice(i) }\n }\n } else if (i + 2 < raw.length && raw[i + 2] === \"O\") {\n // Meta + SS3: ESC ESC O letter\n const end = Math.min(i + 4, raw.length)\n sequences.push(raw.slice(i, end))\n i = end\n } else {\n // Plain double ESC (meta+escape)\n sequences.push(\"\\x1b\\x1b\")\n i += 2\n }\n } else {\n // Meta key: ESC + char\n sequences.push(raw.slice(i, i + 2))\n i += 2\n }\n } else {\n // Single byte (printable char, ctrl code, etc.)\n sequences.push(raw[i]!)\n i++\n }\n }\n return { sequences, incomplete: null }\n}\n\n/** CSI sequences end with a letter (A-Z, a-z) or ~ */\nfunction isCSITerminator(ch: string): boolean {\n return (ch >= \"A\" && ch <= \"Z\") || (ch >= \"a\" && ch <= \"z\") || ch === \"~\"\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Terminal state.\n */\nexport interface TermState {\n cols: number\n rows: number\n}\n\n/**\n * Terminal events.\n */\nexport interface TermEvents {\n key: { input: string; key: Key }\n mouse: ParsedMouse\n paste: { text: string }\n resize: Dims\n focus: { focused: boolean }\n [key: string]: unknown\n}\n\n/**\n * Terminal provider type.\n */\nexport type TermProvider = Provider<TermState, TermEvents>\n\n/**\n * Options for createTermProvider.\n */\nexport interface TermProviderOptions {\n /** Initial columns (default: from stdout or 80) */\n cols?: number\n /** Initial rows (default: from stdout or 24) */\n rows?: number\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Create a terminal provider from stdin/stdout.\n *\n * The provider:\n * - Exposes terminal dimensions as state\n * - Yields keyboard and resize events\n * - Cleans up stdin/stdout listeners on dispose\n */\nexport function createTermProvider(\n stdin: NodeJS.ReadStream,\n stdout: NodeJS.WriteStream,\n options: TermProviderOptions = {},\n): TermProvider {\n const { cols = stdout.columns || 80, rows = stdout.rows || 24 } = options\n\n // Current state\n let state: TermState = { cols, rows }\n\n // Subscribers\n const listeners = new Set<(state: TermState) => void>()\n\n // Disposed flag\n let disposed = false\n\n // Abort controller for cleanup\n const controller = new AbortController()\n const signal = controller.signal\n\n // Shared stdin cleanup — set by events(), callable from dispose as safety net\n let stdinCleanup: (() => void) | null = null\n\n // Resize handler\n const onResize = () => {\n state = {\n cols: stdout.columns || 80,\n rows: stdout.rows || 24,\n }\n listeners.forEach((l) => l(state))\n }\n\n // Increase max listeners to avoid warnings in apps with many subscribers\n // (e.g., ScrollbackList items each using useTerm for reactive state)\n if (typeof stdout.setMaxListeners === \"function\") {\n const current = stdout.getMaxListeners?.() ?? 10\n if (current < 50) stdout.setMaxListeners(50)\n }\n\n // Subscribe to resize\n stdout.on(\"resize\", onResize)\n\n return {\n getState(): TermState {\n return state\n },\n\n subscribe(listener: (state: TermState) => void): () => void {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n\n async *events(): AsyncGenerator<ProviderEvent<TermEvents>, void, undefined> {\n if (disposed) return\n\n // Set up stdin for raw mode if TTY\n if (stdin.isTTY) {\n stdin.setRawMode(true)\n stdin.resume()\n stdin.setEncoding(\"utf8\")\n }\n\n // Queued events\n const queue: ProviderEvent<TermEvents>[] = []\n let eventResolve: (() => void) | null = null\n\n // Single-key handler: parses one key sequence and enqueues an event.\n // Focus, mouse sequences are detected and parsed separately.\n const onKey = (raw: string) => {\n // Focus events: CSI I (focus-in) / CSI O (focus-out)\n const focusEvent = parseFocusEvent(raw)\n if (focusEvent) {\n queue.push({ type: \"focus\", data: { focused: focusEvent.type === \"focus-in\" } })\n return\n }\n if (isMouseSequence(raw)) {\n const parsed = parseMouseSequence(raw)\n if (parsed) {\n queue.push({ type: \"mouse\", data: parsed })\n return\n }\n }\n const [input, key] = parseKey(raw)\n queue.push({ type: \"key\", data: { input, key } })\n }\n\n // Cross-chunk buffer for incomplete CSI sequences.\n // When an SGR mouse sequence (or other CSI) splits across two stdin\n // data events, we buffer the incomplete prefix and prepend it to the\n // next chunk so the sequence can be reassembled.\n let incompleteCSI: string | null = null\n\n // stdin handler: splits multi-char chunks into individual keystrokes.\n // When the OS buffers key repeat events, stdin delivers \"jjjjj\" as a\n // single read — splitRawInput breaks it into individual keys for onKey.\n const onChunk = (chunk: string) => {\n // Prepend any buffered incomplete CSI from the previous chunk\n if (incompleteCSI !== null) {\n chunk = incompleteCSI + chunk\n incompleteCSI = null\n }\n\n // Check for bracketed paste before splitting into individual keys.\n // Paste content is delivered as a single event, not individual keystrokes.\n const pasteResult = parseBracketedPaste(chunk)\n if (pasteResult) {\n queue.push({ type: \"paste\", data: { text: pasteResult.content } })\n if (eventResolve) {\n const resolve = eventResolve\n eventResolve = null\n resolve()\n }\n return\n }\n\n const { sequences, incomplete } = splitRawInput(chunk)\n for (const raw of sequences) onKey(raw)\n incompleteCSI = incomplete\n if (eventResolve) {\n const resolve = eventResolve\n eventResolve = null\n resolve()\n }\n }\n\n // Resize handler for events\n const onResizeEvent = () => {\n const event: ProviderEvent<TermEvents> = {\n type: \"resize\",\n data: {\n cols: stdout.columns || 80,\n rows: stdout.rows || 24,\n },\n }\n queue.push(event)\n if (eventResolve) {\n const resolve = eventResolve\n eventResolve = null\n resolve()\n }\n }\n\n // Enable bracketed paste for TTY input.\n // Note: focus reporting is NOT enabled here — it's controlled by the\n // focusReporting option in create-app.tsx and run.tsx. Unconditionally\n // enabling it causes CSI I/O sequences to leak to screen in inline mode.\n if (stdin.isTTY) {\n enableBracketedPaste(stdout)\n }\n\n // Subscribe — track the cleanup function for use by both finally and dispose\n stdin.on(\"data\", onChunk)\n stdout.on(\"resize\", onResizeEvent)\n stdinCleanup = () => {\n if (stdin.isTTY) {\n disableBracketedPaste(stdout)\n }\n stdin.off(\"data\", onChunk)\n stdout.off(\"resize\", onResizeEvent)\n if (stdin.isTTY) {\n stdin.setRawMode(false)\n }\n // Always pause stdin — on(\"data\") unconditionally sets readableFlowing=true,\n // so we must unconditionally pause to release the event loop reference.\n stdin.pause()\n }\n\n try {\n while (!disposed && !signal.aborted) {\n // Wait for event\n if (queue.length === 0) {\n await new Promise<void>((resolve) => {\n eventResolve = resolve\n signal.addEventListener(\"abort\", () => resolve(), { once: true })\n })\n }\n\n // Check if aborted while waiting\n if (disposed || signal.aborted) break\n\n // Yield queued events\n while (queue.length > 0) {\n yield queue.shift()!\n }\n }\n } finally {\n if (stdinCleanup) {\n const fn = stdinCleanup\n stdinCleanup = null\n fn()\n }\n }\n },\n\n [Symbol.dispose](): void {\n if (disposed) return\n disposed = true\n\n // Abort pending waits\n controller.abort()\n\n // Remove resize listener\n stdout.off(\"resize\", onResize)\n\n // Clear listeners\n listeners.clear()\n\n // Safety net: clean up stdin in case events() generator's finally\n // hasn't run yet (e.g., async .return() propagation is delayed)\n if (stdinCleanup) {\n const fn = stdinCleanup\n stdinCleanup = null\n fn()\n }\n },\n }\n}\n",
12
+ "/**\n * Term interface and createTerm() factory.\n *\n * Term is the central abstraction for terminal interaction:\n * - Detection: hasCursor(), hasInput(), hasColor(), hasUnicode()\n * - Dimensions: cols, rows\n * - I/O: stdout, stdin, write(), writeLine()\n * - Provider: getState(), subscribe(), events() — typed key/mouse/resize\n * - Styling: Chainable styles via Proxy (term.bold.red('text'))\n * - Lifecycle: Disposable pattern via Symbol.dispose\n *\n * @example\n * ```ts\n * // Styling\n * const term = createTerm()\n * console.log(term.bold.red('error'))\n *\n * // Full terminal app\n * using term = createTerm()\n * await run(<App />, term)\n * ```\n */\n\nimport { Chalk, type ChalkInstance } from \"chalk\"\nimport type {\n ColorLevel,\n CreateTermOptions,\n TermEmulator,\n TermEmulatorBackend,\n TermScreen,\n TerminalCaps,\n} from \"./types\"\nimport { defaultCaps, detectColor, detectCursor, detectInput, detectTerminalCaps, detectUnicode } from \"./detection\"\nimport type { ProviderEvent } from \"../runtime/types\"\nimport { createTermProvider, type TermState, type TermEvents } from \"../runtime/term-provider\"\nimport { splitRawInput, parseKey } from \"@silvery/ag/keys\"\nimport { isMouseSequence, parseMouseSequence } from \"../mouse\"\nimport { parseFocusEvent } from \"../focus-reporting\"\nimport { parseBracketedPaste } from \"../bracketed-paste\"\n\n// Re-export Provider-related types for convenience\nexport type { TermState, TermEvents } from \"../runtime/term-provider\"\n\n// =============================================================================\n// ANSI Utilities\n// =============================================================================\n\n/**\n * ANSI escape code pattern for stripping.\n */\nconst ANSI_REGEX =\n /\\x1b\\[[0-9;:]*m|\\x9b[0-9;:]*m|\\x1b\\]8;;[^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)|\\x9d8;;[^\\x07\\x1b\\x9c]*(?:\\x07|\\x1b\\\\|\\x9c)/g\n\n/**\n * Strip all ANSI escape codes from a string.\n */\nfunction stripAnsi(text: string): string {\n return text.replace(ANSI_REGEX, \"\")\n}\n\n// =============================================================================\n// Style Chain Types\n// =============================================================================\n\n/**\n * All chalk style method names that can be chained.\n */\ntype ChalkStyleName =\n // Modifiers\n | \"reset\"\n | \"bold\"\n | \"dim\"\n | \"italic\"\n | \"underline\"\n | \"overline\"\n | \"inverse\"\n | \"hidden\"\n | \"strikethrough\"\n | \"visible\"\n // Foreground colors\n | \"black\"\n | \"red\"\n | \"green\"\n | \"yellow\"\n | \"blue\"\n | \"magenta\"\n | \"cyan\"\n | \"white\"\n | \"gray\"\n | \"grey\"\n | \"blackBright\"\n | \"redBright\"\n | \"greenBright\"\n | \"yellowBright\"\n | \"blueBright\"\n | \"magentaBright\"\n | \"cyanBright\"\n | \"whiteBright\"\n // Background colors\n | \"bgBlack\"\n | \"bgRed\"\n | \"bgGreen\"\n | \"bgYellow\"\n | \"bgBlue\"\n | \"bgMagenta\"\n | \"bgCyan\"\n | \"bgWhite\"\n | \"bgGray\"\n | \"bgGrey\"\n | \"bgBlackBright\"\n | \"bgRedBright\"\n | \"bgGreenBright\"\n | \"bgYellowBright\"\n | \"bgBlueBright\"\n | \"bgMagentaBright\"\n | \"bgCyanBright\"\n | \"bgWhiteBright\"\n\n/**\n * StyleChain provides chainable styling methods.\n * Each property returns a new chain, and the chain is callable.\n */\nexport type StyleChain = {\n /**\n * Apply styles to text.\n */\n (text: string): string\n (template: TemplateStringsArray, ...values: unknown[]): string\n\n /**\n * RGB foreground color.\n */\n rgb(r: number, g: number, b: number): StyleChain\n\n /**\n * Hex foreground color.\n */\n hex(color: string): StyleChain\n\n /**\n * 256-color foreground.\n */\n ansi256(code: number): StyleChain\n\n /**\n * RGB background color.\n */\n bgRgb(r: number, g: number, b: number): StyleChain\n\n /**\n * Hex background color.\n */\n bgHex(color: string): StyleChain\n\n /**\n * 256-color background.\n */\n bgAnsi256(code: number): StyleChain\n} & {\n /**\n * Chainable style properties.\n */\n readonly [K in ChalkStyleName]: StyleChain\n}\n\n// =============================================================================\n// Term Interface\n// =============================================================================\n\n/**\n * Term — the central abstraction for terminal interaction.\n *\n * Term is both a styling helper (chainable ANSI via Proxy) and a\n * Provider (state + typed events). Pass it to `run()` or `createApp()`.\n *\n * Provides:\n * - Capability detection (cached on creation)\n * - Dimensions (live from stream)\n * - I/O (stdout, stdin, write, writeLine)\n * - Provider (getState, subscribe, events — key/mouse/resize)\n * - Styling (chainable via Proxy)\n * - Disposable lifecycle\n *\n * @example\n * ```ts\n * using term = createTerm()\n * await run(<App />, term)\n * ```\n */\nexport interface Term extends Disposable, StyleChain {\n // -------------------------------------------------------------------------\n // Detection Methods\n // -------------------------------------------------------------------------\n\n /**\n * Check if terminal supports cursor control (repositioning).\n * Returns false for dumb terminals and piped output.\n */\n hasCursor(): boolean\n\n /**\n * Check if terminal can read raw keystrokes.\n * Requires stdin to be a TTY with raw mode support.\n */\n hasInput(): boolean\n\n /**\n * Check color level supported by terminal.\n * Returns null if no color support.\n */\n hasColor(): ColorLevel | null\n\n /**\n * Check if terminal can render unicode symbols.\n */\n hasUnicode(): boolean\n\n /**\n * Terminal capabilities profile.\n * Detected when stdin is a TTY, undefined otherwise.\n * Override via createTerm({ caps: { ... } }).\n */\n readonly caps: TerminalCaps | undefined\n\n // -------------------------------------------------------------------------\n // Dimensions\n // -------------------------------------------------------------------------\n\n /**\n * Terminal width in columns.\n * Undefined if not a TTY or dimensions unavailable.\n */\n readonly cols: number | undefined\n\n /**\n * Terminal height in rows.\n * Undefined if not a TTY or dimensions unavailable.\n */\n readonly rows: number | undefined\n\n // -------------------------------------------------------------------------\n // Streams\n // -------------------------------------------------------------------------\n\n /**\n * Output stream (defaults to process.stdout).\n */\n readonly stdout: NodeJS.WriteStream\n\n /**\n * Input stream (defaults to process.stdin).\n */\n readonly stdin: NodeJS.ReadStream\n\n // -------------------------------------------------------------------------\n // I/O Methods\n // -------------------------------------------------------------------------\n\n /**\n * Write string to stdout.\n */\n write(str: string): void\n\n /**\n * Write string followed by newline to stdout.\n */\n writeLine(str: string): void\n\n // -------------------------------------------------------------------------\n // Provider (state + events)\n // -------------------------------------------------------------------------\n\n /**\n * Get current terminal state (dimensions).\n * Always returns defined values (falls back to 80x24).\n */\n getState(): TermState\n\n /**\n * Subscribe to terminal state changes (resize).\n * Returns unsubscribe function.\n */\n subscribe(listener: (state: TermState) => void): () => void\n\n /**\n * Event stream — yields typed key, mouse, and resize events.\n * Enables raw mode on stdin when iterated. Cleans up on return.\n */\n events(): AsyncIterable<ProviderEvent<TermEvents>>\n\n // -------------------------------------------------------------------------\n // Utilities\n // -------------------------------------------------------------------------\n\n /**\n * Strip ANSI escape codes from string.\n */\n stripAnsi(str: string): string\n\n // -------------------------------------------------------------------------\n // Terminal Emulator (present when created with a termless backend)\n // -------------------------------------------------------------------------\n\n /**\n * Visible screen region. Only available when created with a terminal backend.\n * Provides getText(), getLines(), containsText() for assertions.\n */\n readonly screen?: TermScreen\n\n /**\n * Scrollback region. Only available when created with a terminal backend.\n * Provides getText(), getLines(), containsText() for assertions.\n */\n readonly scrollback?: TermScreen\n\n /**\n * Resize the terminal emulator. Only available when created with a terminal backend.\n * Resizes the underlying emulator and triggers a re-render in the app.\n */\n resize?(cols: number, rows: number): void\n}\n\n// =============================================================================\n// createTerm Factory\n// =============================================================================\n\n/**\n * Create a Term instance.\n *\n * Factory overloads:\n * - `createTerm()` — Node.js terminal (auto-detect from process.stdin/stdout)\n * - `createTerm({ stdout, stdin, ... })` — Node.js with custom streams/overrides\n * - `createTerm({ cols, rows })` — Headless for testing (no I/O, fixed dims)\n * - `createTerm(backend, { cols, rows })` — Terminal emulator backend (termless) for testing\n * - `createTerm(emulator)` — Pre-created termless Terminal\n *\n * Detection results are cached at creation time for consistency.\n *\n * @example\n * ```ts\n * // Full terminal app\n * using term = createTerm()\n * await run(<App />, term)\n *\n * // Headless for testing\n * const term = createTerm({ cols: 80, rows: 24 })\n *\n * // Terminal emulator (termless) for full ANSI testing\n * using term = createTerm(createXtermBackend(), { cols: 80, rows: 24 })\n * await run(<App />, term)\n * expect(term.screen).toContainText(\"Hello\")\n *\n * // Custom streams\n * const term = createTerm({ stdout: customStream })\n * ```\n */\nexport function createTerm(options?: CreateTermOptions): Term\nexport function createTerm(dims: { cols: number; rows: number }): Term\nexport function createTerm(backend: TermEmulatorBackend, dims: { cols: number; rows: number }): Term\nexport function createTerm(emulator: TermEmulator): Term\nexport function createTerm(\n first?: CreateTermOptions | { cols: number; rows: number } | TermEmulator | TermEmulatorBackend,\n second?: { cols: number; rows: number },\n): Term {\n // Two-arg: createTerm(backend, { cols, rows }) — raw backend + dims\n if (second && first && isTermBackend(first)) {\n // Lazy require — @termless/core is an optional dependency, only needed\n // for emulator backends. Using a variable prevents static analysis from\n // trying to resolve it at bundle/parse time.\n const mod = \"@termless/core\"\n const { createTerminal } = require(mod) as {\n createTerminal: (opts: { backend: TermEmulatorBackend; cols: number; rows: number }) => TermEmulator\n }\n const emulator = createTerminal({ backend: first as TermEmulatorBackend, ...second })\n return createBackendTerm(emulator)\n }\n // Detect terminal emulator (termless Terminal): has feed + screen\n if (first && isTermEmulator(first)) {\n return createBackendTerm(first as TermEmulator)\n }\n // Detect headless dims: has cols + rows but no stdout/stdin/color/caps\n if (first && isHeadlessDims(first)) {\n return createHeadlessTerm(first as { cols: number; rows: number })\n }\n return createNodeTerm((first as CreateTermOptions) ?? {})\n}\n\n/** Detect terminal emulator (termless Terminal): has feed() + screen */\nfunction isTermEmulator(obj: unknown): obj is TermEmulator {\n if (typeof obj !== \"object\" || obj === null) return false\n const o = obj as Record<string, unknown>\n return typeof o.feed === \"function\" && typeof o.screen === \"object\" && o.screen !== null\n}\n\n/** Detect terminal emulator backend (termless TerminalBackend): has init() + name */\nfunction isTermBackend(obj: unknown): obj is TermEmulatorBackend {\n if (typeof obj !== \"object\" || obj === null) return false\n const o = obj as Record<string, unknown>\n return typeof o.init === \"function\" && typeof o.name === \"string\" && typeof o.destroy === \"function\"\n}\n\n/** Detect headless dims: has cols and rows numbers, no stdout */\nfunction isHeadlessDims(obj: unknown): boolean {\n if (typeof obj !== \"object\" || obj === null) return false\n const o = obj as Record<string, unknown>\n return typeof o.cols === \"number\" && typeof o.rows === \"number\" && !(\"stdout\" in o) && !(\"stdin\" in o)\n}\n\n/**\n * Create a Node.js terminal with full Provider capabilities.\n */\nfunction createNodeTerm(options: CreateTermOptions): Term {\n const stdout = options.stdout ?? process.stdout\n const stdin = options.stdin ?? process.stdin\n\n // Cache detection results\n const cachedCursor = options.cursor ?? detectCursor(stdout)\n const cachedInput = detectInput(stdin)\n const cachedColor = options.color !== undefined ? options.color : detectColor(stdout)\n const cachedUnicode = options.unicode ?? detectUnicode()\n\n // Detect terminal capabilities (only when interactive)\n const detectedCaps = options.caps\n ? { ...defaultCaps(), ...options.caps }\n : stdin.isTTY\n ? detectTerminalCaps()\n : undefined\n\n // Create chalk instance with appropriate color level\n const chalkLevel = cachedColor === null ? 0 : cachedColor === \"basic\" ? 1 : cachedColor === \"256\" ? 2 : 3\n const chalkInstance = new Chalk({ level: chalkLevel })\n\n // Lazy Provider — only created when getState/subscribe/events is called.\n // This avoids adding a resize listener for styling-only usage.\n let provider: ReturnType<typeof createTermProvider> | null = null\n const getProvider = () => {\n if (!provider) {\n provider = createTermProvider(stdin, stdout, {\n cols: stdout.columns || 80,\n rows: stdout.rows || 24,\n })\n }\n return provider\n }\n\n // Base term object with methods\n const termBase = {\n // Detection methods\n hasCursor: () => cachedCursor,\n hasInput: () => cachedInput,\n hasColor: () => cachedColor,\n hasUnicode: () => cachedUnicode,\n\n // Terminal capabilities\n caps: detectedCaps,\n\n // Streams\n stdout,\n stdin,\n\n // I/O methods\n write: (str: string) => {\n stdout.write(str)\n },\n writeLine: (str: string) => {\n stdout.write(str + \"\\n\")\n },\n\n // Provider methods (lazy — Provider created on first access)\n getState: (): TermState => getProvider().getState(),\n subscribe: (listener: (state: TermState) => void): (() => void) => getProvider().subscribe(listener),\n events: (): AsyncIterable<ProviderEvent<TermEvents>> => getProvider().events(),\n\n // Utilities\n stripAnsi,\n\n // Disposable — also disposes the Provider if created\n [Symbol.dispose]: () => {\n if (provider) provider[Symbol.dispose]()\n },\n }\n\n // Create proxy that wraps chalk for styling\n const term = createStyleProxy(chalkInstance, termBase)\n\n // Add dynamic dimension getters\n Object.defineProperty(term, \"cols\", {\n get: () => (stdout.isTTY ? stdout.columns : undefined),\n enumerable: true,\n })\n\n Object.defineProperty(term, \"rows\", {\n get: () => (stdout.isTTY ? stdout.rows : undefined),\n enumerable: true,\n })\n\n return term as Term\n}\n\n/**\n * Create a headless terminal for testing — no I/O, fixed dimensions.\n */\nfunction createHeadlessTerm(dims: { cols: number; rows: number }): Term {\n const state: TermState = { cols: dims.cols, rows: dims.rows }\n let disposed = false\n const controller = new AbortController()\n\n const chalkInstance = new Chalk({ level: 0 })\n\n const termBase = {\n hasCursor: () => false,\n hasInput: () => false,\n hasColor: () => null as ColorLevel | null,\n hasUnicode: () => false,\n caps: undefined as TerminalCaps | undefined,\n stdout: process.stdout,\n stdin: process.stdin,\n write: () => {},\n writeLine: () => {},\n getState: (): TermState => state,\n subscribe: (): (() => void) => () => {},\n async *events(): AsyncIterable<ProviderEvent<TermEvents>> {\n if (disposed) return\n await new Promise<void>((resolve) => {\n controller.signal.addEventListener(\"abort\", () => resolve(), { once: true })\n })\n },\n stripAnsi,\n [Symbol.dispose]: () => {\n if (disposed) return\n disposed = true\n controller.abort()\n },\n }\n\n const term = createStyleProxy(chalkInstance, termBase)\n\n Object.defineProperty(term, \"cols\", { get: () => dims.cols, enumerable: true })\n Object.defineProperty(term, \"rows\", { get: () => dims.rows, enumerable: true })\n\n return term as Term\n}\n\n/**\n * Create a terminal backed by a termless emulator — real ANSI processing, screen/scrollback.\n */\nfunction createBackendTerm(emulator: TermEmulator): Term {\n let disposed = false\n const controller = new AbortController()\n\n const chalkInstance = new Chalk({ level: 3 }) // Emulators support truecolor\n\n // Subscriber support for resize notifications\n const listeners = new Set<(state: TermState) => void>()\n\n // Event queue for resize events (consumed by events() async generator)\n const eventQueue: ProviderEvent<TermEvents>[] = []\n let eventResolve: (() => void) | null = null\n\n const termBase = {\n hasCursor: () => true,\n hasInput: () => true, // sendInput() makes this term capable of receiving input\n hasColor: () => \"truecolor\" as ColorLevel | null,\n hasUnicode: () => true,\n caps: undefined as TerminalCaps | undefined,\n stdout: process.stdout,\n stdin: process.stdin,\n write: (str: string) => emulator.feed(str),\n writeLine: (str: string) => emulator.feed(str + \"\\n\"),\n getState: (): TermState => ({ cols: emulator.cols, rows: emulator.rows }),\n subscribe: (listener: (state: TermState) => void): (() => void) => {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n async *events(): AsyncIterable<ProviderEvent<TermEvents>> {\n if (disposed) return\n while (!disposed && !controller.signal.aborted) {\n if (eventQueue.length === 0) {\n await new Promise<void>((resolve) => {\n eventResolve = resolve\n controller.signal.addEventListener(\"abort\", () => resolve(), { once: true })\n })\n }\n if (disposed || controller.signal.aborted) break\n while (eventQueue.length > 0) {\n yield eventQueue.shift()!\n }\n }\n },\n /** Resize the emulator and notify listeners/events */\n resize: (cols: number, rows: number) => {\n emulator.resize(cols, rows)\n const state: TermState = { cols, rows }\n listeners.forEach((l) => l(state))\n eventQueue.push({ type: \"resize\", data: { cols, rows } })\n if (eventResolve) {\n const resolve = eventResolve\n eventResolve = null\n resolve()\n }\n },\n /** Inject raw terminal input as if the user typed it.\n * Parsed and pushed into the event queue, flowing through the full\n * createApp/run() event pipeline (termProvider → processEventBatch). */\n sendInput: (data: string) => {\n // Check for bracketed paste first\n const pasteResult = parseBracketedPaste(data)\n if (pasteResult) {\n eventQueue.push({ type: \"paste\", data: { text: pasteResult.content } })\n } else {\n for (const raw of splitRawInput(data)) {\n // Focus events: CSI I (focus-in) / CSI O (focus-out)\n const focusEvent = parseFocusEvent(raw)\n if (focusEvent) {\n eventQueue.push({ type: \"focus\", data: { focused: focusEvent.type === \"focus-in\" } })\n continue\n }\n // Mouse events\n if (isMouseSequence(raw)) {\n const parsed = parseMouseSequence(raw)\n if (parsed) {\n eventQueue.push({ type: \"mouse\", data: parsed })\n }\n continue\n }\n // Key events\n const [input, key] = parseKey(raw)\n eventQueue.push({ type: \"key\", data: { input, key } })\n }\n }\n // Wake the events() generator\n if (eventResolve) {\n const resolve = eventResolve\n eventResolve = null\n resolve()\n }\n },\n stripAnsi,\n // Store emulator for run() to detect and auto-wire writable\n _emulator: emulator,\n [Symbol.dispose]: () => {\n if (disposed) return\n disposed = true\n controller.abort()\n listeners.clear()\n emulator.close().catch(() => {})\n },\n }\n\n // Add getters on termBase — Proxy intercepts all property access through termBase first,\n // so Object.defineProperty on the Proxy result won't work for these.\n Object.defineProperty(termBase, \"cols\", { get: () => emulator.cols, enumerable: true })\n Object.defineProperty(termBase, \"rows\", { get: () => emulator.rows, enumerable: true })\n Object.defineProperty(termBase, \"screen\", { get: () => emulator.screen, enumerable: true })\n Object.defineProperty(termBase, \"scrollback\", {\n get: () => emulator.scrollback,\n enumerable: true,\n })\n\n const term = createStyleProxy(chalkInstance, termBase)\n\n return term as Term\n}\n\n// =============================================================================\n// Style Proxy Implementation\n// =============================================================================\n\n/**\n * Create a proxy that combines term methods with chalk styling.\n *\n * The proxy makes the term object:\n * - Callable: term('text') applies current styles\n * - Chainable: term.bold.red('text') chains styles\n */\nfunction createStyleProxy(chalkInstance: ChalkInstance, termBase: object): Term {\n return createChainProxy(chalkInstance, termBase)\n}\n\n/**\n * Create a chainable proxy that wraps a chalk instance.\n */\nfunction createChainProxy(currentChalk: ChalkInstance, termBase: object): Term {\n const handler: ProxyHandler<ChalkInstance> = {\n // Make the proxy callable\n apply(_target, _thisArg, args) {\n // Handle both regular calls and template literals\n if (args.length === 1 && typeof args[0] === \"string\") {\n return currentChalk(args[0])\n }\n // Template literal call\n if (args.length > 0 && Array.isArray(args[0]) && \"raw\" in args[0]) {\n return currentChalk(args[0] as TemplateStringsArray, ...args.slice(1))\n }\n return currentChalk(String(args[0] ?? \"\"))\n },\n\n // Handle property access for chaining\n get(target, prop, receiver) {\n // Check termBase first for term-specific methods/properties\n if (prop in termBase) {\n const value = (termBase as Record<string | symbol, unknown>)[prop]\n // Return methods bound to termBase, or values directly\n if (typeof value === \"function\") {\n return value\n }\n return value\n }\n\n // Handle symbol properties\n if (typeof prop === \"symbol\") {\n if (prop === Symbol.dispose) {\n return (termBase as Record<symbol, unknown>)[Symbol.dispose]\n }\n return Reflect.get(target, prop, receiver)\n }\n\n // Handle chalk methods that take arguments and return a new chain\n if (prop === \"rgb\" || prop === \"bgRgb\") {\n return (r: number, g: number, b: number) => {\n const newChalk = currentChalk[prop](r, g, b) as ChalkInstance\n return createChainProxy(newChalk, termBase)\n }\n }\n\n if (prop === \"hex\" || prop === \"bgHex\") {\n return (color: string) => {\n const newChalk = currentChalk[prop](color) as ChalkInstance\n return createChainProxy(newChalk, termBase)\n }\n }\n\n if (prop === \"ansi256\" || prop === \"bgAnsi256\") {\n return (code: number) => {\n const newChalk = currentChalk[prop](code) as ChalkInstance\n return createChainProxy(newChalk, termBase)\n }\n }\n\n // Handle style properties (bold, red, etc.) - return new chain\n const chalkProp = currentChalk[prop as keyof ChalkInstance]\n if (chalkProp !== undefined) {\n // If it's a chalk chain property, wrap it in a new proxy\n if (typeof chalkProp === \"function\" || typeof chalkProp === \"object\") {\n return createChainProxy(chalkProp as ChalkInstance, termBase)\n }\n return chalkProp\n }\n\n return undefined\n },\n\n // Report that we have term properties\n has(_target, prop) {\n if (prop in termBase) return true\n if (typeof prop === \"string\" && prop in currentChalk) return true\n return false\n },\n }\n\n // Use a function as the proxy target so it's callable\n const proxyTarget = Object.assign(function () {}, currentChalk)\n return new Proxy(proxyTarget, handler) as unknown as Term\n}\n",
13
+ "/**\n * ANSI string utilities.\n *\n * This module can be imported separately via `@silvery/ansi/utils`\n * for projects that only need ANSI stripping without chalk.\n */\n\nimport stringWidth from \"string-width\"\n\n// =============================================================================\n// ANSI Regex Pattern\n// =============================================================================\n\n/**\n * ANSI escape code pattern for stripping.\n *\n * Matches:\n * - ESC CSI SGR sequences: \\x1b[31m, \\x1b[4:3m, \\x1b[38:2::255:100:0m\n * - C1 CSI SGR sequences: \\x9b31m, \\x9b4:3m\n * - ESC OSC 8 hyperlinks (BEL-terminated): \\x1b]8;;<url>\\x07\n * - ESC OSC 8 hyperlinks (ST-terminated): \\x1b]8;;<url>\\x1b\\\\\n * - C1 OSC 8 hyperlinks (BEL-terminated): \\x9d8;;<url>\\x07\n * - C1 OSC 8 hyperlinks (ST-terminated): \\x9d8;;<url>\\x1b\\\\\n * - C1 OSC 8 hyperlinks (C1 ST-terminated): \\x9d8;;<url>\\x9c\n */\nexport const ANSI_REGEX =\n /\\x1b\\[[0-9;:]*m|\\x9b[0-9;:]*m|\\x1b\\]8;;[^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)|\\x9d8;;[^\\x07\\x1b\\x9c]*(?:\\x07|\\x1b\\\\|\\x9c)/g\n\n// =============================================================================\n// String Utilities\n// =============================================================================\n\n/**\n * Strip all ANSI escape codes from a string.\n *\n * @param text - String potentially containing ANSI codes\n * @returns Clean string with all ANSI codes removed\n *\n * @example\n * ```ts\n * stripAnsi('\\x1b[31mred\\x1b[0m') // 'red'\n * stripAnsi('\\x1b[4:3mwavy\\x1b[4:0m') // 'wavy'\n * ```\n */\nexport function stripAnsi(text: string): string {\n return text.replace(ANSI_REGEX, \"\")\n}\n\n/**\n * Get the display width of a string, excluding ANSI escape codes.\n * Correctly handles CJK characters, emoji, and other wide characters.\n *\n * @param text - String potentially containing ANSI codes\n * @returns Number of terminal columns the text will occupy\n *\n * @example\n * ```ts\n * displayLength('\\x1b[31mhello\\x1b[0m') // 5\n * displayLength('hello') // 5\n * displayLength('한글') // 4 (2 chars × 2 cells each)\n * ```\n */\nexport function displayLength(text: string): number {\n return stringWidth(stripAnsi(text))\n}\n",
14
+ "/**\n * Extended underline style functions.\n *\n * Provides curly, dotted, dashed, and double underline styles\n * with graceful fallback to standard underline on unsupported terminals.\n */\n\nimport chalk from \"chalk\"\nimport {\n UNDERLINE_CODES,\n UNDERLINE_COLOR_RESET,\n UNDERLINE_STANDARD,\n UNDERLINE_RESET_STANDARD,\n buildUnderlineColorCode,\n} from \"./constants\"\nimport { detectExtendedUnderline } from \"./detection\"\nimport type { UnderlineStyle, RGB } from \"./types\"\n\n// =============================================================================\n// Extended Underline Functions\n// =============================================================================\n\n/**\n * Apply an extended underline style to text.\n * Falls back to regular underline on unsupported terminals.\n *\n * @param text - Text to underline\n * @param style - Underline style (default: \"single\")\n * @returns Styled text with ANSI codes\n */\nexport function underline(text: string, style: UnderlineStyle = \"single\"): string {\n if (!detectExtendedUnderline() || style === \"single\") {\n return chalk.underline(text)\n }\n\n return `${UNDERLINE_CODES[style]}${text}${UNDERLINE_CODES.reset}`\n}\n\n/**\n * Apply curly/wavy underline to text.\n * Commonly used for spell check errors in IDEs.\n * Falls back to regular underline on unsupported terminals.\n *\n * @param text - Text to underline\n * @returns Styled text with curly underline\n *\n * @example\n * ```ts\n * import { curlyUnderline, chalk } from '@silvery/ansi';\n *\n * console.log(curlyUnderline('misspelled'));\n * console.log(chalk.red(curlyUnderline('error')));\n * ```\n */\nexport function curlyUnderline(text: string): string {\n return underline(text, \"curly\")\n}\n\n/**\n * Apply dotted underline to text.\n * Falls back to regular underline on unsupported terminals.\n *\n * @param text - Text to underline\n * @returns Styled text with dotted underline\n */\nexport function dottedUnderline(text: string): string {\n return underline(text, \"dotted\")\n}\n\n/**\n * Apply dashed underline to text.\n * Falls back to regular underline on unsupported terminals.\n *\n * @param text - Text to underline\n * @returns Styled text with dashed underline\n */\nexport function dashedUnderline(text: string): string {\n return underline(text, \"dashed\")\n}\n\n/**\n * Apply double underline to text.\n * Falls back to regular underline on unsupported terminals.\n *\n * @param text - Text to underline\n * @returns Styled text with double underline\n */\nexport function doubleUnderline(text: string): string {\n return underline(text, \"double\")\n}\n\n// =============================================================================\n// Underline Color Functions\n// =============================================================================\n\n/**\n * Set underline color independently of text color.\n * On unsupported terminals, the color is ignored but underline still applies.\n *\n * @param r - Red component (0-255)\n * @param g - Green component (0-255)\n * @param b - Blue component (0-255)\n * @param text - Text to style\n * @returns Styled text with colored underline\n *\n * @example\n * ```ts\n * import { underlineColor, chalk } from '@silvery/ansi';\n *\n * // Red underline (text color unchanged)\n * console.log(underlineColor(255, 0, 0, 'warning'));\n *\n * // Red underline with blue text\n * console.log(chalk.blue(underlineColor(255, 0, 0, 'blue text, red underline')));\n * ```\n */\nexport function underlineColor(r: number, g: number, b: number, text: string): string {\n if (!detectExtendedUnderline()) {\n // Fallback: just apply regular underline, ignore color\n return chalk.underline(text)\n }\n\n const colorCode = buildUnderlineColorCode(r, g, b)\n return `${UNDERLINE_STANDARD}${colorCode}${text}${UNDERLINE_COLOR_RESET}${UNDERLINE_RESET_STANDARD}`\n}\n\n/**\n * Combine underline style with underline color.\n *\n * @param style - Underline style ('curly', 'dotted', 'dashed', 'double', 'single')\n * @param rgb - Color as [r, g, b] tuple (0-255 each)\n * @param text - Text to style\n * @returns Styled text with colored underline in specified style\n *\n * @example\n * ```ts\n * import { styledUnderline, chalk } from '@silvery/ansi';\n *\n * // Red curly underline (spell-check style)\n * console.log(styledUnderline('curly', [255, 0, 0], 'misspelled'));\n *\n * // Orange dashed underline with yellow text\n * console.log(chalk.yellow(styledUnderline('dashed', [255, 165, 0], 'warning')));\n * ```\n */\nexport function styledUnderline(style: UnderlineStyle, rgb: RGB, text: string): string {\n if (!detectExtendedUnderline()) {\n return chalk.underline(text)\n }\n\n const [r, g, b] = rgb\n const styleCode = UNDERLINE_CODES[style]\n const colorCode = buildUnderlineColorCode(r, g, b)\n\n return `${styleCode}${colorCode}${text}${UNDERLINE_CODES.reset}${UNDERLINE_COLOR_RESET}`\n}\n",
15
+ "/**\n * Terminal hyperlink functions using OSC 8.\n *\n * OSC 8 hyperlinks create clickable links in supporting terminals.\n * Unsupported terminals will display just the text.\n *\n * @see https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda\n */\n\nimport { buildHyperlink } from \"./constants\"\n\n// =============================================================================\n// Hyperlink Functions\n// =============================================================================\n\n/**\n * Create a clickable hyperlink in supporting terminals.\n * Falls back to showing just the text (no URL) on unsupported terminals.\n *\n * @param text - Display text for the link\n * @param url - Target URL (http://, https://, file://, etc.)\n * @returns Text wrapped in OSC 8 hyperlink escape codes\n *\n * @example\n * ```ts\n * hyperlink('Click here', 'https://example.com')\n * hyperlink('Open file', 'file:///path/to/file.txt')\n * ```\n *\n * @note Most modern terminals support OSC 8 hyperlinks:\n * - Ghostty, Kitty, WezTerm, iTerm2, Terminal.app, GNOME Terminal\n * - VS Code integrated terminal\n */\nexport function hyperlink(text: string, url: string): string {\n // Most modern terminals support OSC 8, so we emit it unconditionally.\n // Unsupported terminals will just show the text.\n return buildHyperlink(text, url)\n}\n",
16
+ "/**\n * ANSI primitives — Term factory, styling, detection, underlines, hyperlinks.\n *\n * Merged from the former `@silvery/ansi` package into `@silvery/ag-term`.\n *\n * ```ts\n * import { createTerm, patchConsole } from '@silvery/ag-term'\n *\n * using term = createTerm()\n * term.red('error')\n * term.bold.green('success')\n *\n * using patched = patchConsole(console)\n * patched.subscribe(() => console.log('new entry'))\n * ```\n *\n * @module\n */\n\n// =============================================================================\n// Term API (NewWay)\n// =============================================================================\n\nexport { createTerm } from \"./term\"\nexport type { Term, StyleChain, TermState, TermEvents } from \"./term\"\n\nimport { createTerm as _createTerm } from \"./term\"\nimport type { Term } from \"./term\"\n\n/**\n * Default term instance for convenience, lazily initialized on first access.\n * This avoids running terminal detection (including spawnSync) at import time.\n * Use this for simple scripts. For apps, prefer createTerm() with `using`.\n *\n * @example\n * ```ts\n * import { term } from '@silvery/ag-term'\n *\n * console.log(term.green('success'))\n * if (term.hasColor()) { ... }\n * ```\n */\nlet _lazyTerm: Term | undefined\nexport const term: Term = new Proxy({} as Term, {\n get(_target, prop, receiver) {\n if (!_lazyTerm) _lazyTerm = _createTerm()\n return Reflect.get(_lazyTerm, prop, receiver)\n },\n apply(_target, thisArg, args) {\n if (!_lazyTerm) _lazyTerm = _createTerm()\n return Reflect.apply(_lazyTerm as unknown as (...args: unknown[]) => unknown, thisArg, args)\n },\n has(_target, prop) {\n if (!_lazyTerm) _lazyTerm = _createTerm()\n return Reflect.has(_lazyTerm, prop)\n },\n})\n\nexport { patchConsole } from \"./patch-console\"\nexport type { PatchedConsole, PatchConsoleOptions, ConsoleStats } from \"./patch-console\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type {\n UnderlineStyle,\n RGB,\n ColorLevel,\n Color,\n AnsiColorName,\n StyleOptions,\n ConsoleMethod,\n ConsoleEntry,\n CreateTermOptions,\n TermEmulator,\n TermEmulatorBackend,\n TermScreen,\n} from \"./types\"\n\n// =============================================================================\n// Detection Functions\n// =============================================================================\n\nexport {\n detectCursor,\n detectInput,\n detectColor,\n detectUnicode,\n detectExtendedUnderline,\n detectTerminalCaps,\n defaultCaps,\n} from \"./detection\"\nexport type { TerminalCaps } from \"./detection\"\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\nexport { ANSI_REGEX, stripAnsi, displayLength } from \"./utils\"\n\n// =============================================================================\n// Underline Functions\n// =============================================================================\n\nexport {\n underline,\n curlyUnderline,\n dottedUnderline,\n dashedUnderline,\n doubleUnderline,\n underlineColor,\n styledUnderline,\n} from \"./underline\"\n\n// =============================================================================\n// Hyperlink Functions\n// =============================================================================\n\nexport { hyperlink } from \"./hyperlink\"\n\n// =============================================================================\n// ANSI Terminal Control Helpers\n// =============================================================================\n\nexport {\n enterAltScreen,\n leaveAltScreen,\n clearScreen,\n clearLine,\n cursorTo,\n cursorHome,\n cursorHide,\n cursorShow,\n cursorStyle,\n setTitle,\n enableMouse,\n disableMouse,\n enableBracketedPaste,\n disableBracketedPaste,\n enableSyncUpdate,\n disableSyncUpdate,\n setScrollRegion,\n resetScrollRegion,\n scrollUp,\n scrollDown,\n enableKittyKeyboard,\n disableKittyKeyboard,\n} from \"./ansi\"\n\n// =============================================================================\n// Background Override — Compose styled text inside Box with backgroundColor\n// =============================================================================\n\n/**\n * SGR code recognized by silvery to signal intentional bg override.\n * When text is wrapped with this, silvery won't warn/throw about chalk bg + silvery bg conflicts.\n * Exported for silvery to detect this marker in text content.\n */\nexport const BG_OVERRIDE_CODE = 9999\n\n/**\n * Compose styled text with an explicit background inside a Box that has its own\n * `backgroundColor`. This is the correct way to layer chalk/term background\n * colors on top of an silvery Box background.\n *\n * Without `bgOverride`, silvery throws (by default) when it detects both an ANSI\n * background in the text content AND a `backgroundColor` prop on an ancestor\n * Box, because the two conflict and produce visual artifacts (the ANSI bg\n * only covers the text, leaving gaps at line edges).\n *\n * `bgOverride` wraps the text with a private SGR marker that tells silvery\n * \"this background is intentional — don't throw.\" Use it when you need\n * pixel-precise background control within a styled container.\n *\n * @param text - Text containing ANSI background codes (e.g. from `chalk.bgBlack()`)\n * @returns The same text prefixed with a marker SGR code\n *\n * @example\n * ```tsx\n * import { bgOverride } from '@silvery/chalk'\n *\n * // Without bgOverride — silvery throws:\n * <Box backgroundColor=\"cyan\">\n * <Text>{chalk.bgBlack('text')}</Text> // Error!\n * </Box>\n *\n * // With bgOverride — explicitly allowed:\n * <Box backgroundColor=\"cyan\">\n * <Text>{bgOverride(chalk.bgBlack('text'))}</Text> // OK\n * </Box>\n *\n * // Also works with term styling:\n * <Box backgroundColor=\"$surface-bg\">\n * <Text>{bgOverride(term.bgRgb(30, 30, 30)('highlighted'))}</Text>\n * </Box>\n * ```\n */\nexport function bgOverride(text: string): string {\n return `\\x1b[${BG_OVERRIDE_CODE}m${text}`\n}\n",
17
+ "/**\n * SGR (Select Graphic Rendition) color code helpers.\n *\n * Shared by buffer.ts (styleToAnsiCodes) and output-phase.ts (styleTransition).\n * Emits the shortest possible SGR code string for a given color.\n */\n\n/**\n * Emit the shortest SGR code string for a foreground color.\n * - Basic 0-7: 4-bit code (30+N)\n * - Extended 8-255: 256-color (38;5;N)\n * - RGB: true color (38;2;R;G;B)\n */\nexport function fgColorCode(color: number | { r: number; g: number; b: number }): string {\n if (typeof color === \"number\") {\n if (color >= 0 && color <= 7) return `${30 + color}`\n return `38;5;${color}`\n }\n return `38;2;${color.r};${color.g};${color.b}`\n}\n\n/**\n * Emit the shortest SGR code string for a background color.\n * - Basic 0-7: 4-bit code (40+N)\n * - Extended 8-255: 256-color (48;5;N)\n * - RGB: true color (48;2;R;G;B)\n */\nexport function bgColorCode(color: number | { r: number; g: number; b: number }): string {\n if (typeof color === \"number\") {\n if (color >= 0 && color <= 7) return `${40 + color}`\n return `48;5;${color}`\n }\n return `48;2;${color.r};${color.g};${color.b}`\n}\n",
18
+ "/**\n * Terminal buffer implementation for Silvery.\n *\n * Uses packed Uint32Array for efficient cell metadata storage,\n * with separate string array for character storage (needed for\n * multi-byte Unicode graphemes and combining characters).\n */\n\nimport { fgColorCode, bgColorCode } from \"./ansi/sgr-codes\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Underline style variants (SGR 4:x codes).\n * - false: no underline\n * - 'single': standard underline (SGR 4 or 4:1)\n * - 'double': double underline (SGR 4:2)\n * - 'curly': curly/wavy underline (SGR 4:3)\n * - 'dotted': dotted underline (SGR 4:4)\n * - 'dashed': dashed underline (SGR 4:5)\n */\nexport type UnderlineStyle = false | \"single\" | \"double\" | \"curly\" | \"dotted\" | \"dashed\"\n\n/**\n * Text attributes that can be applied to a cell.\n */\nexport interface CellAttrs {\n bold?: boolean\n dim?: boolean\n italic?: boolean\n /** Simple underline flag (for backwards compatibility) */\n underline?: boolean\n /**\n * Underline style: 'single' | 'double' | 'curly' | 'dotted' | 'dashed'.\n * When set, takes precedence over the underline boolean.\n */\n underlineStyle?: UnderlineStyle\n blink?: boolean\n inverse?: boolean\n hidden?: boolean\n strikethrough?: boolean\n}\n\n/**\n * Color representation.\n * - number: 256-color index (0-255)\n * - RGB object: true color\n * - null: default/inherit\n * - DEFAULT_BG: terminal's default background (SGR 49), opaque but uses terminal's own bg color\n */\nexport type Color = number | { r: number; g: number; b: number } | null\n\n/**\n * Sentinel color representing the terminal's default background (SGR 49).\n * Unlike `null` (transparent/inherit), this actively fills cells with the\n * terminal's configured background, making the element opaque.\n */\nexport const DEFAULT_BG: Color = Object.freeze({ r: -1, g: -1, b: -1 })\n\n/** Check if a color is the default bg sentinel. */\nexport function isDefaultBg(color: Color): boolean {\n return color !== null && typeof color === \"object\" && color.r === -1\n}\n\n/**\n * A single cell in the terminal buffer.\n */\nexport interface Cell {\n /** The character/grapheme in this cell */\n char: string\n /** Foreground color */\n fg: Color\n /** Background color */\n bg: Color\n /**\n * Underline color (independent of fg).\n * Uses SGR 58. If null, underline uses fg color.\n */\n underlineColor: Color\n /** Text attributes */\n attrs: CellAttrs\n /** True if this is a wide character (CJK, emoji, etc.) */\n wide: boolean\n /** True if this is the continuation cell after a wide character */\n continuation: boolean\n /**\n * OSC 8 hyperlink URL.\n * When set, the cell is part of a clickable hyperlink in supporting terminals.\n */\n hyperlink?: string\n}\n\n/**\n * Style information for a cell (excludes char and position flags).\n */\nexport interface Style {\n fg: Color\n bg: Color\n /**\n * Underline color (independent of fg).\n * Uses SGR 58. If null, underline uses fg color.\n */\n underlineColor?: Color\n attrs: CellAttrs\n /**\n * OSC 8 hyperlink URL.\n * When set, the cell is part of a clickable hyperlink in supporting terminals.\n */\n hyperlink?: string\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n// Bit packing layout for cell metadata in Uint32Array:\n// [0-7]: foreground color index (8 bits)\n// [8-15]: background color index (8 bits)\n// [16-23]: attributes (8 bits): bold, dim, italic, blink, inverse, hidden, strikethrough + 1 spare\n// [24-26]: underline style (3 bits): 0=none, 1=single, 2=double, 3=curly, 4=dotted, 5=dashed\n// [27-31]: flags (5 bits): wide, continuation, true_color_fg, true_color_bg + 1 spare\n\n// Attribute bit positions (within bits 16-23)\nconst ATTR_BOLD = 1 << 16\nconst ATTR_DIM = 1 << 17\nconst ATTR_ITALIC = 1 << 18\nconst ATTR_BLINK = 1 << 19\nconst ATTR_INVERSE = 1 << 20\nconst ATTR_HIDDEN = 1 << 21\nconst ATTR_STRIKETHROUGH = 1 << 22\n// bit 23 spare\n\n// Underline style (3 bits in positions 24-26)\n// 0 = no underline, 1 = single, 2 = double, 3 = curly, 4 = dotted, 5 = dashed\nconst UNDERLINE_STYLE_SHIFT = 24\nconst UNDERLINE_STYLE_MASK = 0x7 << UNDERLINE_STYLE_SHIFT // 3 bits\n\n// Flag bit positions (in bits 27-31)\nconst WIDE_FLAG = 1 << 27\nconst CONTINUATION_FLAG = 1 << 28\nconst TRUE_COLOR_FG_FLAG = 1 << 29\nconst TRUE_COLOR_BG_FLAG = 1 << 30\n// bit 31 spare\n\n/**\n * Packed attribute bits that make a space character visually meaningful.\n * Inverse makes spaces visible (block of color), underline draws a line under spaces,\n * strikethrough draws a line through spaces. Other attrs (bold, dim, italic) don't\n * visually affect space characters.\n */\nexport const VISIBLE_SPACE_ATTR_MASK = ATTR_INVERSE | ATTR_STRIKETHROUGH | UNDERLINE_STYLE_MASK\n\n// Default empty cell\nconst EMPTY_CELL: Cell = {\n char: \" \",\n fg: null,\n bg: null,\n underlineColor: null,\n attrs: {},\n wide: false,\n continuation: false,\n hyperlink: undefined,\n}\n\n/** Frozen empty attrs object, shared across zero-allocation reads for OOB cells */\nconst EMPTY_ATTRS: CellAttrs = Object.freeze({})\n\n// ============================================================================\n// Packing/Unpacking Helpers\n// ============================================================================\n\n/**\n * Map UnderlineStyle to numeric value for bit packing.\n */\nfunction underlineStyleToNumber(style: UnderlineStyle | undefined): number {\n switch (style) {\n case false:\n return 0\n case \"single\":\n return 1\n case \"double\":\n return 2\n case \"curly\":\n return 3\n case \"dotted\":\n return 4\n case \"dashed\":\n return 5\n default:\n return 0 // undefined or unknown = no underline\n }\n}\n\n/**\n * Map numeric value back to UnderlineStyle.\n */\nfunction numberToUnderlineStyle(n: number): UnderlineStyle | undefined {\n switch (n) {\n case 0:\n return undefined // No underline\n case 1:\n return \"single\"\n case 2:\n return \"double\"\n case 3:\n return \"curly\"\n case 4:\n return \"dotted\"\n case 5:\n return \"dashed\"\n default:\n return undefined\n }\n}\n\n/**\n * Convert CellAttrs to bits for packing (used internally by packCell).\n * Note: This packs into the full 32-bit word, not just the attrs byte.\n */\nexport function attrsToNumber(attrs: CellAttrs): number {\n let n = 0\n if (attrs.bold) n |= ATTR_BOLD\n if (attrs.dim) n |= ATTR_DIM\n if (attrs.italic) n |= ATTR_ITALIC\n if (attrs.blink) n |= ATTR_BLINK\n if (attrs.inverse) n |= ATTR_INVERSE\n if (attrs.hidden) n |= ATTR_HIDDEN\n if (attrs.strikethrough) n |= ATTR_STRIKETHROUGH\n\n // Pack underline style (3 bits)\n // If underlineStyle is set, use it. Otherwise, check underline boolean.\n const ulStyle = attrs.underlineStyle ?? (attrs.underline ? \"single\" : undefined)\n n |= underlineStyleToNumber(ulStyle) << UNDERLINE_STYLE_SHIFT\n\n return n\n}\n\n/**\n * Convert a number back to CellAttrs.\n */\nexport function numberToAttrs(n: number): CellAttrs {\n const attrs: CellAttrs = {}\n if (n & ATTR_BOLD) attrs.bold = true\n if (n & ATTR_DIM) attrs.dim = true\n if (n & ATTR_ITALIC) attrs.italic = true\n if (n & ATTR_BLINK) attrs.blink = true\n if (n & ATTR_INVERSE) attrs.inverse = true\n if (n & ATTR_HIDDEN) attrs.hidden = true\n if (n & ATTR_STRIKETHROUGH) attrs.strikethrough = true\n\n // Unpack underline style\n const ulStyleNum = (n & UNDERLINE_STYLE_MASK) >> UNDERLINE_STYLE_SHIFT\n const ulStyle = numberToUnderlineStyle(ulStyleNum)\n if (ulStyle) {\n attrs.underlineStyle = ulStyle\n attrs.underline = true\n }\n\n return attrs\n}\n\n/**\n * Convert a color to an index value for packing.\n * Returns 0 for null (default), or (index + 1) for 256-color.\n * This +1 offset allows distinguishing null from black (color index 0).\n * True color is handled separately via flags and auxiliary storage.\n */\nfunction colorToIndex(color: Color): number {\n if (color === null) return 0\n if (typeof color === \"number\") return (color & 0xff) + 1 // +1 to distinguish from null\n // True color - return 0, handle via flag\n return 0\n}\n\n/**\n * Check if a color is true color (RGB).\n */\nfunction isTrueColor(color: Color): color is { r: number; g: number; b: number } {\n return color !== null && typeof color === \"object\"\n}\n\n/**\n * Pack cell metadata into a 32-bit number.\n */\nexport function packCell(cell: Cell): number {\n let packed = 0\n\n // Foreground color index (bits 0-7)\n packed |= colorToIndex(cell.fg) & 0xff\n\n // Background color index (bits 8-15)\n packed |= (colorToIndex(cell.bg) & 0xff) << 8\n\n // Attributes (bits 16-22) and underline style (bits 24-26)\n // attrsToNumber returns bits already in their final positions\n packed |= attrsToNumber(cell.attrs)\n\n // Flags (bits 27-30)\n if (cell.wide) packed |= WIDE_FLAG\n if (cell.continuation) packed |= CONTINUATION_FLAG\n if (isTrueColor(cell.fg)) packed |= TRUE_COLOR_FG_FLAG\n if (isTrueColor(cell.bg)) packed |= TRUE_COLOR_BG_FLAG\n\n return packed\n}\n\n/**\n * Unpack foreground color index from packed value.\n */\nfunction unpackFgIndex(packed: number): number {\n return packed & 0xff\n}\n\n/**\n * Unpack background color index from packed value.\n */\nfunction unpackBgIndex(packed: number): number {\n return (packed >> 8) & 0xff\n}\n\n/**\n * Unpack attributes from packed value.\n * Extracts both the boolean attrs (bits 16-22) and underline style (bits 24-26).\n */\nfunction unpackAttrs(packed: number): CellAttrs {\n // numberToAttrs expects the full packed value with attrs in bits 16-22\n // and underline style in bits 24-26\n return numberToAttrs(packed)\n}\n\n/**\n * Check if wide flag is set.\n */\nfunction unpackWide(packed: number): boolean {\n return (packed & WIDE_FLAG) !== 0\n}\n\n/**\n * Check if continuation flag is set.\n */\nfunction unpackContinuation(packed: number): boolean {\n return (packed & CONTINUATION_FLAG) !== 0\n}\n\n/**\n * Check if true color foreground flag is set.\n */\nfunction unpackTrueColorFg(packed: number): boolean {\n return (packed & TRUE_COLOR_FG_FLAG) !== 0\n}\n\n/**\n * Check if true color background flag is set.\n */\nfunction unpackTrueColorBg(packed: number): boolean {\n return (packed & TRUE_COLOR_BG_FLAG) !== 0\n}\n\n// ============================================================================\n// TerminalBuffer Class\n// ============================================================================\n\n/**\n * Efficient terminal cell buffer.\n *\n * Uses packed Uint32Array for cell metadata and separate string array\n * for characters. This allows efficient diffing while supporting\n * full Unicode grapheme clusters.\n */\nexport class TerminalBuffer {\n /** Packed cell metadata */\n private cells: Uint32Array\n /** Character storage (one per cell, may be multi-byte grapheme) */\n private chars: string[]\n /** True color foreground storage (only for cells with true color fg) */\n private fgColors: Map<number, { r: number; g: number; b: number }>\n /** True color background storage (only for cells with true color bg) */\n private bgColors: Map<number, { r: number; g: number; b: number }>\n /** Underline color storage (independent of fg, for SGR 58) */\n private underlineColors: Map<number, Color>\n /** OSC 8 hyperlink URL storage (only for cells that are part of a hyperlink) */\n private hyperlinks: Map<number, string>\n /**\n * Per-row dirty tracking for diff optimization.\n * When set, diffBuffers() can skip clean rows entirely.\n * 0 = clean (unchanged since last resetDirtyRows), 1 = dirty (modified).\n */\n private _dirtyRows: Uint8Array\n /** Bounding box: first dirty row (inclusive). -1 when no rows are dirty. */\n private _minDirtyRow: number\n /** Bounding box: last dirty row (inclusive). -1 when no rows are dirty. */\n private _maxDirtyRow: number\n\n readonly width: number\n readonly height: number\n\n constructor(width: number, height: number) {\n this.width = width\n this.height = height\n const size = width * height\n this.cells = new Uint32Array(size)\n this.chars = new Array<string>(size).fill(\" \")\n this.fgColors = new Map()\n this.bgColors = new Map()\n this.underlineColors = new Map()\n this.hyperlinks = new Map()\n // All rows start dirty (fresh buffer needs full diff on first comparison)\n this._dirtyRows = new Uint8Array(height).fill(1)\n this._minDirtyRow = 0\n this._maxDirtyRow = height - 1\n }\n\n /**\n * Get the index for a cell position.\n */\n private index(x: number, y: number): number {\n return y * this.width + x\n }\n\n /**\n * Check if coordinates are within bounds.\n */\n inBounds(x: number, y: number): boolean {\n return x >= 0 && x < this.width && y >= 0 && y < this.height\n }\n\n /**\n * Get a cell at the given position.\n */\n getCell(x: number, y: number): Cell {\n if (!this.inBounds(x, y)) {\n return { ...EMPTY_CELL }\n }\n\n const idx = this.index(x, y)\n const packed = this.cells[idx]\n const char = this.chars[idx]\n\n // Determine foreground color\n // Color indices are stored with +1 offset (0=null, 1=black, 2=red, etc.)\n let fg: Color = null\n if (unpackTrueColorFg(packed!)) {\n fg = this.fgColors.get(idx) ?? null\n } else {\n const fgIndex = unpackFgIndex(packed!)\n fg = fgIndex > 0 ? fgIndex - 1 : null // -1 to restore actual color index\n }\n\n // Determine background color\n let bg: Color = null\n if (unpackTrueColorBg(packed!)) {\n bg = this.bgColors.get(idx) ?? null\n } else {\n const bgIndex = unpackBgIndex(packed!)\n bg = bgIndex > 0 ? bgIndex - 1 : null // -1 to restore actual color index\n }\n\n const hyperlink = this.hyperlinks.get(idx)\n return {\n char: char!,\n fg,\n bg,\n underlineColor: this.underlineColors.get(idx) ?? null,\n attrs: unpackAttrs(packed!),\n wide: unpackWide(packed!),\n continuation: unpackContinuation(packed!),\n ...(hyperlink !== undefined ? { hyperlink } : {}),\n }\n }\n\n // --------------------------------------------------------------------------\n // Zero-allocation cell accessors for hot paths\n // --------------------------------------------------------------------------\n\n /**\n * Get just the character at a cell position (no object allocation).\n * Returns \" \" for out-of-bounds positions.\n */\n getCellChar(x: number, y: number): string {\n if (!this.inBounds(x, y)) return \" \"\n return this.chars[this.index(x, y)]!\n }\n\n /**\n * Get just the background color at a cell position (no object allocation).\n * Returns null for out-of-bounds positions.\n */\n getCellBg(x: number, y: number): Color {\n if (!this.inBounds(x, y)) return null\n const idx = this.index(x, y)\n const packed = this.cells[idx]!\n if (unpackTrueColorBg(packed)) {\n return this.bgColors.get(idx) ?? null\n }\n const bgIndex = unpackBgIndex(packed)\n return bgIndex > 0 ? bgIndex - 1 : null\n }\n\n /**\n * Get just the foreground color at a cell position (no object allocation).\n * Returns null for out-of-bounds positions.\n */\n getCellFg(x: number, y: number): Color {\n if (!this.inBounds(x, y)) return null\n const idx = this.index(x, y)\n const packed = this.cells[idx]!\n if (unpackTrueColorFg(packed)) {\n return this.fgColors.get(idx) ?? null\n }\n const fgIndex = unpackFgIndex(packed)\n return fgIndex > 0 ? fgIndex - 1 : null\n }\n\n /**\n * Get the raw packed metadata at a cell position (no unpackAttrs allocation).\n * Returns 0 for out-of-bounds positions. The packed value contains color\n * indices, attr bits, underline style, and flags in a single Uint32.\n */\n getCellAttrs(x: number, y: number): number {\n if (!this.inBounds(x, y)) return 0\n return this.cells[this.index(x, y)]!\n }\n\n /**\n * Check if a cell is a wide character (no object allocation).\n * Returns false for out-of-bounds positions.\n */\n isCellWide(x: number, y: number): boolean {\n if (!this.inBounds(x, y)) return false\n return unpackWide(this.cells[this.index(x, y)]!)\n }\n\n /**\n * Check if a cell is a continuation of a wide character (no object allocation).\n * Returns false for out-of-bounds positions.\n */\n isCellContinuation(x: number, y: number): boolean {\n if (!this.inBounds(x, y)) return false\n return unpackContinuation(this.cells[this.index(x, y)]!)\n }\n\n /**\n * Read cell data into a caller-provided Cell object (zero-allocation).\n * For hot loops that need the full Cell, reuse a single object:\n *\n * const cell = createMutableCell()\n * for (...) { buffer.readCellInto(x, y, cell) }\n *\n * Returns the same `out` object for chaining convenience.\n */\n readCellInto(x: number, y: number, out: Cell): Cell {\n if (!this.inBounds(x, y)) {\n out.char = \" \"\n out.fg = null\n out.bg = null\n out.underlineColor = null\n out.attrs = EMPTY_ATTRS\n out.wide = false\n out.continuation = false\n out.hyperlink = undefined\n return out\n }\n\n const idx = this.index(x, y)\n const packed = this.cells[idx]!\n\n out.char = this.chars[idx]!\n\n // Foreground color\n if (unpackTrueColorFg(packed)) {\n out.fg = this.fgColors.get(idx) ?? null\n } else {\n const fgIndex = unpackFgIndex(packed)\n out.fg = fgIndex > 0 ? fgIndex - 1 : null\n }\n\n // Background color\n if (unpackTrueColorBg(packed)) {\n out.bg = this.bgColors.get(idx) ?? null\n } else {\n const bgIndex = unpackBgIndex(packed)\n out.bg = bgIndex > 0 ? bgIndex - 1 : null\n }\n\n out.underlineColor = this.underlineColors.get(idx) ?? null\n\n // Unpack attrs inline to avoid allocating a new CellAttrs object.\n // We reuse the existing out.attrs object when possible.\n const attrs = out.attrs === EMPTY_ATTRS ? ((out.attrs = {}), out.attrs) : out.attrs\n attrs.bold = (packed & ATTR_BOLD) !== 0 ? true : undefined\n attrs.dim = (packed & ATTR_DIM) !== 0 ? true : undefined\n attrs.italic = (packed & ATTR_ITALIC) !== 0 ? true : undefined\n attrs.blink = (packed & ATTR_BLINK) !== 0 ? true : undefined\n attrs.inverse = (packed & ATTR_INVERSE) !== 0 ? true : undefined\n attrs.hidden = (packed & ATTR_HIDDEN) !== 0 ? true : undefined\n attrs.strikethrough = (packed & ATTR_STRIKETHROUGH) !== 0 ? true : undefined\n\n const ulStyleNum = (packed & UNDERLINE_STYLE_MASK) >> UNDERLINE_STYLE_SHIFT\n const ulStyle = numberToUnderlineStyle(ulStyleNum)\n if (ulStyle) {\n attrs.underlineStyle = ulStyle\n attrs.underline = true\n } else {\n attrs.underlineStyle = undefined\n attrs.underline = undefined\n }\n\n out.wide = (packed & WIDE_FLAG) !== 0\n out.continuation = (packed & CONTINUATION_FLAG) !== 0\n out.hyperlink = this.hyperlinks.get(idx)\n\n return out\n }\n\n /**\n * Set a cell at the given position.\n *\n * Optimized: resolves defaults and packs metadata inline to avoid\n * allocating an intermediate Cell object.\n */\n setCell(x: number, y: number, cell: Partial<Cell>): void {\n if (!this.inBounds(x, y)) {\n return\n }\n\n // Write trap for SILVERY_STRICT mismatch diagnosis\n const trap = (globalThis as any).__silvery_write_trap\n if (trap && x === trap.x && y === trap.y) {\n const char = cell.char ?? \" \"\n const stack = new Error().stack?.split(\"\\n\").slice(1, 6).join(\"\\n\") ?? \"\"\n trap.log.push(\n ` char=\"${char}\" fg=${cell.fg ?? \"null\"} bg=${cell.bg ?? \"null\"} dim=${cell.attrs?.dim} ul=${cell.attrs?.underline}\\n${stack}`,\n )\n }\n\n this._dirtyRows[y] = 1\n if (this._minDirtyRow === -1 || y < this._minDirtyRow) this._minDirtyRow = y\n if (y > this._maxDirtyRow) this._maxDirtyRow = y\n\n const idx = this.index(x, y)\n\n // Resolve properties with defaults (no intermediate object)\n const char = cell.char ?? \" \"\n const fg = cell.fg ?? null\n const bg = cell.bg ?? null\n const underlineColor = cell.underlineColor ?? null\n const attrs = cell.attrs ?? EMPTY_ATTRS\n const wide = cell.wide ?? false\n const continuation = cell.continuation ?? false\n\n // Store character\n this.chars[idx] = char\n\n // Handle true color storage\n if (isTrueColor(fg)) {\n this.fgColors.set(idx, fg)\n } else {\n this.fgColors.delete(idx)\n }\n\n if (isTrueColor(bg)) {\n this.bgColors.set(idx, bg)\n } else {\n this.bgColors.delete(idx)\n }\n\n // Handle underline color storage\n if (underlineColor !== null) {\n this.underlineColors.set(idx, underlineColor)\n } else {\n this.underlineColors.delete(idx)\n }\n\n // Handle hyperlink storage\n const hyperlink = cell.hyperlink\n if (hyperlink !== undefined && hyperlink !== \"\") {\n this.hyperlinks.set(idx, hyperlink)\n } else {\n this.hyperlinks.delete(idx)\n }\n\n // Pack metadata inline (avoids packCell's fullCell parameter overhead)\n let packed = 0\n packed |= colorToIndex(fg) & 0xff\n packed |= (colorToIndex(bg) & 0xff) << 8\n packed |= attrsToNumber(attrs)\n if (wide) packed |= WIDE_FLAG\n if (continuation) packed |= CONTINUATION_FLAG\n if (isTrueColor(fg)) packed |= TRUE_COLOR_FG_FLAG\n if (isTrueColor(bg)) packed |= TRUE_COLOR_BG_FLAG\n this.cells[idx] = packed\n }\n\n /**\n * Fill a region with a cell.\n *\n * Optimized: packs cell metadata once and assigns directly to arrays,\n * avoiding O(width*height) intermediate object allocations from setCell().\n */\n fill(x: number, y: number, width: number, height: number, cell: Partial<Cell>): void {\n const endX = Math.min(x + width, this.width)\n const endY = Math.min(y + height, this.height)\n const startX = Math.max(0, x)\n const startY = Math.max(0, y)\n\n if (startX >= endX || startY >= endY) return\n\n // Resolve cell properties once (instead of per-cell in setCell)\n const char = cell.char ?? \" \"\n const fg = cell.fg ?? null\n const bg = cell.bg ?? null\n const underlineColor = cell.underlineColor ?? null\n const attrs = cell.attrs ?? {}\n const wide = cell.wide ?? false\n const continuation = cell.continuation ?? false\n\n // Pack metadata once for the entire fill region\n const fullCell: Cell = {\n char,\n fg,\n bg,\n underlineColor,\n attrs,\n wide,\n continuation,\n }\n const packed = packCell(fullCell)\n\n // Determine true color values once\n const hasTrueColorFg = isTrueColor(fg)\n const hasTrueColorBg = isTrueColor(bg)\n const trueColorFg = hasTrueColorFg ? (fg as { r: number; g: number; b: number }) : null\n const trueColorBg = hasTrueColorBg ? (bg as { r: number; g: number; b: number }) : null\n const hasUnderlineColor = underlineColor !== null\n const hyperlink = cell.hyperlink\n const hasHyperlink = hyperlink !== undefined && hyperlink !== \"\"\n\n // Mark affected rows dirty + update bounding box\n for (let cy = startY; cy < endY; cy++) {\n this._dirtyRows[cy] = 1\n }\n if (startY < endY) {\n if (this._minDirtyRow === -1 || startY < this._minDirtyRow) this._minDirtyRow = startY\n if (endY - 1 > this._maxDirtyRow) this._maxDirtyRow = endY - 1\n }\n\n // Determine which Map operations are actually needed.\n // Skip delete() calls when the map is already empty (common case: no true colors).\n const needFgDelete = !hasTrueColorFg && this.fgColors.size > 0\n const needBgDelete = !hasTrueColorBg && this.bgColors.size > 0\n const needUlDelete = !hasUnderlineColor && this.underlineColors.size > 0\n const needHlDelete = !hasHyperlink && this.hyperlinks.size > 0\n\n for (let cy = startY; cy < endY; cy++) {\n const rowBase = cy * this.width\n for (let cx = startX; cx < endX; cx++) {\n const idx = rowBase + cx\n\n // Direct array assignment (no setCell overhead)\n this.cells[idx] = packed\n this.chars[idx] = char\n\n // Handle true color maps — skip delete when map is empty\n if (hasTrueColorFg) {\n this.fgColors.set(idx, trueColorFg!)\n } else if (needFgDelete) {\n this.fgColors.delete(idx)\n }\n\n if (hasTrueColorBg) {\n this.bgColors.set(idx, trueColorBg!)\n } else if (needBgDelete) {\n this.bgColors.delete(idx)\n }\n\n if (hasUnderlineColor) {\n this.underlineColors.set(idx, underlineColor)\n } else if (needUlDelete) {\n this.underlineColors.delete(idx)\n }\n\n if (hasHyperlink) {\n this.hyperlinks.set(idx, hyperlink!)\n } else if (needHlDelete) {\n this.hyperlinks.delete(idx)\n }\n }\n }\n }\n\n /**\n * Clear the buffer (fill with empty cells).\n */\n clear(): void {\n this.cells.fill(0)\n this.chars.fill(\" \")\n this.fgColors.clear()\n this.bgColors.clear()\n this.underlineColors.clear()\n this.hyperlinks.clear()\n this._dirtyRows.fill(1)\n this._minDirtyRow = 0\n this._maxDirtyRow = this.height - 1\n }\n\n /**\n * Copy a region from another buffer.\n */\n copyFrom(\n source: TerminalBuffer,\n srcX: number,\n srcY: number,\n destX: number,\n destY: number,\n width: number,\n height: number,\n ): void {\n const cell = createMutableCell()\n for (let dy = 0; dy < height; dy++) {\n const dstY = destY + dy\n if (dstY >= 0 && dstY < this.height) {\n this._dirtyRows[dstY] = 1\n if (this._minDirtyRow === -1 || dstY < this._minDirtyRow) this._minDirtyRow = dstY\n if (dstY > this._maxDirtyRow) this._maxDirtyRow = dstY\n }\n for (let dx = 0; dx < width; dx++) {\n const sx = srcX + dx\n const sy = srcY + dy\n const dX = destX + dx\n\n if (source.inBounds(sx, sy) && this.inBounds(dX, dstY)) {\n source.readCellInto(sx, sy, cell)\n this.setCell(dX, dstY, cell)\n }\n }\n }\n }\n\n /**\n * Shift content within a rectangular region vertically by `delta` rows.\n * Positive delta = shift content UP (scroll down), negative = shift DOWN (scroll up).\n * Exposed rows (at the bottom for positive delta, top for negative) are filled\n * with the given background cell.\n *\n * Uses Uint32Array.copyWithin for the packed cells (native memcpy) and\n * Array splice for the character array.\n */\n scrollRegion(\n x: number,\n y: number,\n regionWidth: number,\n regionHeight: number,\n delta: number,\n clearCell: Partial<Cell> = {},\n ): void {\n if (delta === 0 || regionHeight <= 0 || regionWidth <= 0) return\n\n const startX = Math.max(0, x)\n const endX = Math.min(x + regionWidth, this.width)\n const startY = Math.max(0, y)\n const endY = Math.min(y + regionHeight, this.height)\n const clampedWidth = endX - startX\n const clampedHeight = endY - startY\n\n if (clampedWidth <= 0 || clampedHeight <= 0) return\n\n // Mark all rows in the scroll region dirty + update bounding box\n for (let r = startY; r < endY; r++) {\n this._dirtyRows[r] = 1\n }\n if (this._minDirtyRow === -1 || startY < this._minDirtyRow) this._minDirtyRow = startY\n if (endY - 1 > this._maxDirtyRow) this._maxDirtyRow = endY - 1\n\n if (Math.abs(delta) >= clampedHeight) {\n // Scroll amount exceeds region — just clear everything\n this.fill(startX, startY, clampedWidth, clampedHeight, {\n char: clearCell.char ?? \" \",\n bg: clearCell.bg ?? null,\n })\n return\n }\n\n const absDelta = Math.abs(delta)\n const w = this.width\n\n if (delta > 0) {\n // Shift content UP: copy rows [startY + delta .. endY) to [startY .. endY - delta)\n for (let row = startY; row < endY - absDelta; row++) {\n const dstBase = row * w\n const srcBase = (row + absDelta) * w\n // Copy cells and chars for the region columns\n this.cells.copyWithin(dstBase + startX, srcBase + startX, srcBase + endX)\n for (let cx = startX; cx < endX; cx++) {\n this.chars[dstBase + cx] = this.chars[srcBase + cx]!\n // Move true color maps\n const srcIdx = srcBase + cx\n const dstIdx = dstBase + cx\n const fgc = this.fgColors.get(srcIdx)\n if (fgc) {\n this.fgColors.set(dstIdx, fgc)\n this.fgColors.delete(srcIdx)\n } else {\n this.fgColors.delete(dstIdx)\n }\n const bgc = this.bgColors.get(srcIdx)\n if (bgc) {\n this.bgColors.set(dstIdx, bgc)\n this.bgColors.delete(srcIdx)\n } else {\n this.bgColors.delete(dstIdx)\n }\n const ulc = this.underlineColors.get(srcIdx)\n if (ulc) {\n this.underlineColors.set(dstIdx, ulc)\n this.underlineColors.delete(srcIdx)\n } else {\n this.underlineColors.delete(dstIdx)\n }\n const hl = this.hyperlinks.get(srcIdx)\n if (hl) {\n this.hyperlinks.set(dstIdx, hl)\n this.hyperlinks.delete(srcIdx)\n } else {\n this.hyperlinks.delete(dstIdx)\n }\n }\n }\n // Clear exposed rows at bottom\n this.fill(startX, endY - absDelta, clampedWidth, absDelta, {\n char: clearCell.char ?? \" \",\n bg: clearCell.bg ?? null,\n })\n } else {\n // Shift content DOWN: copy rows [startY .. endY - absDelta) to [startY + absDelta .. endY)\n for (let row = endY - 1; row >= startY + absDelta; row--) {\n const dstBase = row * w\n const srcBase = (row - absDelta) * w\n this.cells.copyWithin(dstBase + startX, srcBase + startX, srcBase + endX)\n for (let cx = startX; cx < endX; cx++) {\n this.chars[dstBase + cx] = this.chars[srcBase + cx]!\n const srcIdx = srcBase + cx\n const dstIdx = dstBase + cx\n const fgc = this.fgColors.get(srcIdx)\n if (fgc) {\n this.fgColors.set(dstIdx, fgc)\n this.fgColors.delete(srcIdx)\n } else {\n this.fgColors.delete(dstIdx)\n }\n const bgc = this.bgColors.get(srcIdx)\n if (bgc) {\n this.bgColors.set(dstIdx, bgc)\n this.bgColors.delete(srcIdx)\n } else {\n this.bgColors.delete(dstIdx)\n }\n const ulc = this.underlineColors.get(srcIdx)\n if (ulc) {\n this.underlineColors.set(dstIdx, ulc)\n this.underlineColors.delete(srcIdx)\n } else {\n this.underlineColors.delete(dstIdx)\n }\n const hl = this.hyperlinks.get(srcIdx)\n if (hl) {\n this.hyperlinks.set(dstIdx, hl)\n this.hyperlinks.delete(srcIdx)\n } else {\n this.hyperlinks.delete(dstIdx)\n }\n }\n }\n // Clear exposed rows at top\n this.fill(startX, startY, clampedWidth, absDelta, {\n char: clearCell.char ?? \" \",\n bg: clearCell.bg ?? null,\n })\n }\n }\n\n /**\n * Clone this buffer.\n */\n clone(): TerminalBuffer {\n const copy = new TerminalBuffer(this.width, this.height)\n copy.cells.set(this.cells)\n copy.chars = [...this.chars]\n copy.fgColors = new Map(this.fgColors)\n copy.bgColors = new Map(this.bgColors)\n copy.underlineColors = new Map(this.underlineColors)\n copy.hyperlinks = new Map(this.hyperlinks)\n // Clone starts with all rows CLEAN. The content phase will mark only\n // the rows it modifies as dirty. diffBuffers() then skips clean rows,\n // which are guaranteed identical to the prev buffer (since this is a clone).\n copy._dirtyRows.fill(0)\n copy._minDirtyRow = -1\n copy._maxDirtyRow = -1\n return copy\n }\n\n /**\n * Check if a row has been modified since the last resetDirtyRows() call.\n * Used by diffBuffers() to skip unchanged rows.\n */\n isRowDirty(y: number): boolean {\n if (y < 0 || y >= this.height) return false\n return this._dirtyRows[y] !== 0\n }\n\n /** First dirty row (inclusive), or -1 if no rows are dirty. */\n get minDirtyRow(): number {\n return this._minDirtyRow\n }\n\n /** Last dirty row (inclusive), or -1 if no rows are dirty. */\n get maxDirtyRow(): number {\n return this._maxDirtyRow\n }\n\n /**\n * Reset all dirty row flags to clean.\n * Call after diffing to prepare for the next frame's modifications.\n */\n resetDirtyRows(): void {\n this._dirtyRows.fill(0)\n this._minDirtyRow = -1\n this._maxDirtyRow = -1\n }\n\n /**\n * Mark all rows as dirty.\n * Used when the buffer's dirty rows may not cover all changes relative\n * to a different prev buffer (e.g., after multiple doRender calls where\n * the runtime's prevBuffer skipped intermediate buffers).\n */\n markAllRowsDirty(): void {\n this._dirtyRows.fill(1)\n this._minDirtyRow = 0\n this._maxDirtyRow = this.height - 1\n }\n\n /**\n * Check if two cells at given positions are equal.\n * Used for diffing.\n */\n cellEquals(x: number, y: number, other: TerminalBuffer): boolean {\n if (!this.inBounds(x, y) || !other.inBounds(x, y)) {\n return false\n }\n\n const idx = this.index(x, y)\n const otherIdx = other.index(x, y)\n\n // Quick check: packed metadata must match\n if (this.cells[idx] !== other.cells[otherIdx]) {\n return false\n }\n\n // Character must match\n if (this.chars[idx] !== other.chars[otherIdx]) {\n return false\n }\n\n // If true color flags are set, check the color values\n const packed = this.cells[idx]!\n if (unpackTrueColorFg(packed)) {\n const a = this.fgColors.get(idx)\n const b = other.fgColors.get(otherIdx)\n if (!colorEquals(a, b)) return false\n }\n if (unpackTrueColorBg(packed!)) {\n const a = this.bgColors.get(idx)\n const b = other.bgColors.get(otherIdx)\n if (!colorEquals(a, b)) return false\n }\n\n // Check underline colors\n const ulA = this.underlineColors.get(idx) ?? null\n const ulB = other.underlineColors.get(otherIdx) ?? null\n if (!colorEquals(ulA, ulB)) return false\n\n // Check hyperlinks\n const hlA = this.hyperlinks.get(idx)\n const hlB = other.hyperlinks.get(otherIdx)\n if (hlA !== hlB) return false\n\n return true\n }\n\n /**\n * Fast check: are all packed metadata values identical for a row?\n * This is a bulk pre-check before per-cell comparison. If metadata differs,\n * we still need per-cell diffing. If metadata matches, we only need to\n * check chars, true color maps, underline colors, and hyperlinks.\n * Returns true if all packed 32-bit values in the row are identical.\n */\n rowMetadataEquals(y: number, other: TerminalBuffer): boolean {\n if (y < 0 || y >= this.height || y >= other.height) return false\n const start = y * this.width\n const otherStart = y * other.width\n const w = Math.min(this.width, other.width)\n for (let i = 0; i < w; i++) {\n if (this.cells[start + i] !== other.cells[otherStart + i]) return false\n }\n return true\n }\n\n /**\n * Fast check: are all characters identical for a row?\n * Companion to rowMetadataEquals for a two-phase row comparison.\n */\n rowCharsEquals(y: number, other: TerminalBuffer): boolean {\n if (y < 0 || y >= this.height || y >= other.height) return false\n const start = y * this.width\n const otherStart = y * other.width\n const w = Math.min(this.width, other.width)\n for (let i = 0; i < w; i++) {\n if (this.chars[start + i] !== other.chars[otherStart + i]) return false\n }\n return true\n }\n\n /**\n * Check Map-based extras for a row: true color fg/bg, underline colors, hyperlinks.\n * Must be called AFTER rowMetadataEquals confirms packed metadata matches.\n * Only checks cells that have true color flags set (the Maps are only populated\n * for those cells). Also checks underline colors and hyperlinks for all cells.\n */\n rowExtrasEquals(y: number, other: TerminalBuffer): boolean {\n if (y < 0 || y >= this.height || y >= other.height) return false\n const start = y * this.width\n const w = Math.min(this.width, other.width)\n const otherStart = y * other.width\n for (let i = 0; i < w; i++) {\n const idx = start + i\n const otherIdx = otherStart + i\n const packed = this.cells[idx]!\n\n // Check true color fg values\n if ((packed & TRUE_COLOR_FG_FLAG) !== 0) {\n const a = this.fgColors.get(idx)\n const b = other.fgColors.get(otherIdx)\n if (!colorEquals(a, b)) return false\n }\n\n // Check true color bg values\n if ((packed & TRUE_COLOR_BG_FLAG) !== 0) {\n const a = this.bgColors.get(idx)\n const b = other.bgColors.get(otherIdx)\n if (!colorEquals(a, b)) return false\n }\n\n // Check underline colors\n const ulA = this.underlineColors.get(idx) ?? null\n const ulB = other.underlineColors.get(otherIdx) ?? null\n if (!colorEquals(ulA, ulB)) return false\n\n // Check hyperlinks\n const hlA = this.hyperlinks.get(idx)\n const hlB = other.hyperlinks.get(otherIdx)\n if (hlA !== hlB) return false\n }\n return true\n }\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Compare two colors for equality.\n */\nexport function colorEquals(a: Color | undefined, b: Color | undefined): boolean {\n if (a === b) return true\n if (a === null || a === undefined) return b === null || b === undefined\n if (b === null || b === undefined) return false\n if (typeof a === \"number\") return a === b\n if (typeof b === \"number\") return false\n return a.r === b.r && a.g === b.g && a.b === b.b\n}\n\n/**\n * Compare two cells for equality.\n */\nexport function cellEquals(a: Cell, b: Cell): boolean {\n return (\n a.char === b.char &&\n colorEquals(a.fg, b.fg) &&\n colorEquals(a.bg, b.bg) &&\n colorEquals(a.underlineColor, b.underlineColor) &&\n a.wide === b.wide &&\n a.continuation === b.continuation &&\n attrsEquals(a.attrs, b.attrs) &&\n (a.hyperlink ?? undefined) === (b.hyperlink ?? undefined)\n )\n}\n\n/**\n * Compare two CellAttrs for equality.\n */\nexport function attrsEquals(a: CellAttrs, b: CellAttrs): boolean {\n return (\n Boolean(a.bold) === Boolean(b.bold) &&\n Boolean(a.dim) === Boolean(b.dim) &&\n Boolean(a.italic) === Boolean(b.italic) &&\n Boolean(a.underline) === Boolean(b.underline) &&\n (a.underlineStyle ?? false) === (b.underlineStyle ?? false) &&\n Boolean(a.blink) === Boolean(b.blink) &&\n Boolean(a.inverse) === Boolean(b.inverse) &&\n Boolean(a.hidden) === Boolean(b.hidden) &&\n Boolean(a.strikethrough) === Boolean(b.strikethrough)\n )\n}\n\n/**\n * Compare two styles for equality.\n */\nexport function styleEquals(a: Style | null, b: Style | null): boolean {\n if (a === b) return true\n if (!a || !b) return false\n return (\n colorEquals(a.fg, b.fg) &&\n colorEquals(a.bg, b.bg) &&\n colorEquals(a.underlineColor, b.underlineColor) &&\n attrsEquals(a.attrs, b.attrs) &&\n (a.hyperlink ?? undefined) === (b.hyperlink ?? undefined)\n )\n}\n\n/**\n * Create a mutable Cell object for use with readCellInto().\n * The returned object is reusable -- readCellInto() overwrites all fields.\n */\nexport function createMutableCell(): Cell {\n return {\n char: \" \",\n fg: null,\n bg: null,\n underlineColor: null,\n attrs: {},\n wide: false,\n continuation: false,\n hyperlink: undefined,\n }\n}\n\n/**\n * Create a buffer initialized with a specific character.\n */\nexport function createBuffer(width: number, height: number, char = \" \"): TerminalBuffer {\n const buffer = new TerminalBuffer(width, height)\n if (char !== \" \") {\n buffer.fill(0, 0, width, height, { char })\n }\n return buffer\n}\n\n// ============================================================================\n// Buffer Conversion Utilities\n// ============================================================================\n\n/**\n * Convert a terminal buffer to plain text (no ANSI codes).\n * Useful for snapshot testing and text-based assertions.\n *\n * @param buffer The buffer to convert\n * @param options.trimTrailingWhitespace Remove trailing spaces from each line (default: true)\n * @param options.trimEmptyLines Remove trailing empty lines (default: true)\n * @returns Plain text representation of the buffer\n */\nexport function bufferToText(\n buffer: TerminalBuffer,\n options: {\n trimTrailingWhitespace?: boolean\n trimEmptyLines?: boolean\n } = {},\n): string {\n const { trimTrailingWhitespace = true, trimEmptyLines = true } = options\n\n const lines: string[] = []\n\n for (let y = 0; y < buffer.height; y++) {\n let line = \"\"\n // Track string offset for each column (needed because continuation cells\n // are skipped, making string length != column count for wide chars)\n let strOffset = 0\n let contentEdgeStrOffset = 0\n const contentEdge = trimTrailingWhitespace ? getContentEdge(buffer, y) : 0\n for (let x = 0; x < buffer.width; x++) {\n // Use zero-allocation accessors instead of getCell()\n if (buffer.isCellContinuation(x, y)) continue\n line += buffer.getCellChar(x, y)\n strOffset++\n // Track the string offset corresponding to the content edge column\n if (x < contentEdge) {\n contentEdgeStrOffset = strOffset\n }\n }\n if (trimTrailingWhitespace) {\n // Smart trim: use content edge to preserve styled trailing spaces\n // while removing unstyled buffer padding.\n const trimmed = line.trimEnd()\n line = trimmed.length >= contentEdgeStrOffset ? trimmed : line.substring(0, contentEdgeStrOffset)\n }\n lines.push(line)\n }\n\n let result = lines.join(\"\\n\")\n if (trimEmptyLines) {\n // Remove trailing empty lines without stripping spaces from last content line\n while (lines.length > 0 && lines[lines.length - 1]!.length === 0) {\n lines.pop()\n }\n result = lines.join(\"\\n\")\n }\n return result\n}\n\n/**\n * Find the rightmost column with non-default cell content on a row.\n * A default cell has packed metadata === 0 (no fg, bg, attrs) AND char === ' '.\n * Returns the column count (1-indexed), so the content edge for trimming.\n */\nfunction getContentEdge(buffer: TerminalBuffer, y: number): number {\n // Mask out structural flags (wide, continuation) that don't indicate actual content.\n // True-color flags DO indicate styled content (they mean fg/bg is set in Maps).\n const FLAG_MASK = ~(WIDE_FLAG | CONTINUATION_FLAG)\n for (let x = buffer.width - 1; x >= 0; x--) {\n // Skip continuation cells (trailing half of wide chars) — the main cell covers them\n if (buffer.isCellContinuation(x, y)) continue\n // Check if cell has any actual styling (fg, bg, text attrs) after masking structural flags\n const attrs = buffer.getCellAttrs(x, y) & FLAG_MASK\n if (attrs !== 0) return x + 1\n // Check if cell has a non-space character\n if (buffer.getCellChar(x, y) !== \" \") return x + 1\n }\n return 0\n}\n\n/**\n * Convert a terminal buffer to styled ANSI text.\n * Unlike bufferToAnsi, this doesn't include cursor control sequences,\n * making it suitable for displaying in terminals or saving to files.\n *\n * @param buffer The buffer to convert\n * @param options.trimTrailingWhitespace Remove trailing spaces from each line (default: true)\n * @param options.trimEmptyLines Remove trailing empty lines (default: true)\n * @returns ANSI-styled text (no cursor control)\n */\nexport function bufferToStyledText(\n buffer: TerminalBuffer,\n options: {\n trimTrailingWhitespace?: boolean\n trimEmptyLines?: boolean\n } = {},\n): string {\n const { trimTrailingWhitespace = true, trimEmptyLines = true } = options\n\n const lines: string[] = []\n let currentStyle: Style | null = null\n let currentHyperlink: string | undefined\n\n for (let y = 0; y < buffer.height; y++) {\n let line = \"\"\n\n for (let x = 0; x < buffer.width; x++) {\n // getCell allocates a fresh object each call, which is fine here since\n // bufferToStyledText is a utility function, not a hot render path.\n const cell = buffer.getCell(x, y)\n // Skip continuation cells (part of wide character)\n if (cell.continuation) continue\n\n // Check if hyperlink changed (OSC 8 is separate from SGR)\n const cellHyperlink = cell.hyperlink\n if (cellHyperlink !== currentHyperlink) {\n if (currentHyperlink) {\n // Close previous hyperlink using the same format as the open\n line += emitHyperlinkClose(currentHyperlink)\n }\n if (cellHyperlink) {\n line += emitHyperlinkOpen(cellHyperlink)\n }\n currentHyperlink = cellHyperlink\n }\n\n // Check if style changed — emit minimal transition (chalk-compatible)\n const cellStyle: Style = {\n fg: cell.fg,\n bg: cell.bg,\n underlineColor: cell.underlineColor,\n attrs: cell.attrs,\n }\n if (!styleEquals(currentStyle, cellStyle)) {\n line += styleTransitionCodes(currentStyle, cellStyle)\n currentStyle = cellStyle\n }\n\n line += cell.char\n }\n\n // Close any open hyperlink at end of line\n if (currentHyperlink) {\n line += emitHyperlinkClose(currentHyperlink)\n currentHyperlink = undefined\n }\n\n // Reset style at end of line using per-attribute resets (chalk-compatible)\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n line += styleResetCodes(currentStyle)\n currentStyle = null\n }\n\n if (trimTrailingWhitespace) {\n // Need to be careful not to strip ANSI codes\n // Only trim actual whitespace at the end\n line = trimTrailingWhitespacePreservingAnsi(line)\n }\n lines.push(line)\n }\n\n // Final per-attribute reset (chalk-compatible)\n let result = lines.join(\"\\n\")\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n result += styleResetCodes(currentStyle)\n }\n\n if (trimEmptyLines) {\n // Remove empty lines at the end (but preserve ANSI resets)\n result = result.replace(/\\n+$/, \"\")\n }\n\n return result\n}\n\n// ============================================================================\n// Hyperlink Format Helpers\n// ============================================================================\n\n/**\n * Decode hyperlink format metadata from URL prefix.\n * parseAnsiText encodes the original OSC format (C1 vs ESC, BEL vs ST)\n * as a prefix: \\x01<tag>\\x02<url>\n *\n * Tags:\n * c1b = C1 OSC (\\x9d) + BEL (\\x07) terminator\n * c1s = C1 OSC (\\x9d) + ST (\\x1b\\\\) terminator\n * e7b = ESC OSC (\\x1b]) + BEL (\\x07) terminator\n * (no prefix) = ESC OSC + ST (default)\n */\nfunction decodeHyperlinkFormat(encoded: string): {\n url: string\n oscIntro: string\n oscClose: string\n closeIntro: string\n closeTerminator: string\n} {\n if (encoded.charCodeAt(0) === 1) {\n const sepIdx = encoded.indexOf(\"\\x02\")\n if (sepIdx > 0) {\n const tag = encoded.slice(1, sepIdx)\n const url = encoded.slice(sepIdx + 1)\n if (tag === \"c1b\") {\n return {\n url,\n oscIntro: \"\\x9d\",\n oscClose: \"\\x9d\",\n closeIntro: \"\\x9d\",\n closeTerminator: \"\\x07\",\n }\n }\n if (tag === \"c1s\") {\n return {\n url,\n oscIntro: \"\\x9d\",\n oscClose: \"\\x9d\",\n closeIntro: \"\\x9d\",\n closeTerminator: \"\\x1b\\\\\",\n }\n }\n if (tag === \"e7b\") {\n return {\n url,\n oscIntro: \"\\x1b]\",\n oscClose: \"\\x1b]\",\n closeIntro: \"\\x1b]\",\n closeTerminator: \"\\x07\",\n }\n }\n }\n }\n // Default: ESC OSC + ST\n return {\n url: encoded,\n oscIntro: \"\\x1b]\",\n oscClose: \"\\x1b]\",\n closeIntro: \"\\x1b]\",\n closeTerminator: \"\\x1b\\\\\",\n }\n}\n\n/** Emit OSC 8 hyperlink open sequence, respecting format metadata in URL. */\nfunction emitHyperlinkOpen(encoded: string): string {\n const fmt = decodeHyperlinkFormat(encoded)\n return `${fmt.oscIntro}8;;${fmt.url}${fmt.closeTerminator}`\n}\n\n/** Emit OSC 8 hyperlink close sequence, respecting format metadata in URL. */\nfunction emitHyperlinkClose(encoded: string): string {\n const fmt = decodeHyperlinkFormat(encoded)\n return `${fmt.closeIntro}8;;${fmt.closeTerminator}`\n}\n\n// ============================================================================\n// xterm-256 Color Palette\n// ============================================================================\n\n/** Standard xterm-256 color palette as hex strings. */\nconst XTERM_256_PALETTE: string[] = (() => {\n const palette: string[] = new Array(256)\n\n // Colors 0-7: standard colors\n const standard = [\"#000000\", \"#cd0000\", \"#00cd00\", \"#cdcd00\", \"#0000ee\", \"#cd00cd\", \"#00cdcd\", \"#e5e5e5\"]\n // Colors 8-15: bright colors\n const bright = [\"#7f7f7f\", \"#ff0000\", \"#00ff00\", \"#ffff00\", \"#5c5cff\", \"#ff00ff\", \"#00ffff\", \"#ffffff\"]\n for (let i = 0; i < 8; i++) {\n palette[i] = standard[i]!\n palette[i + 8] = bright[i]!\n }\n\n // Colors 16-231: 6x6x6 RGB cube\n const cubeValues = [0, 95, 135, 175, 215, 255]\n for (let i = 0; i < 216; i++) {\n const r = cubeValues[Math.floor(i / 36)]!\n const g = cubeValues[Math.floor((i % 36) / 6)]!\n const b = cubeValues[i % 6]!\n palette[16 + i] =\n \"#\" + r.toString(16).padStart(2, \"0\") + g.toString(16).padStart(2, \"0\") + b.toString(16).padStart(2, \"0\")\n }\n\n // Colors 232-255: grayscale ramp\n for (let i = 0; i < 24; i++) {\n const v = 8 + i * 10\n const hex = v.toString(16).padStart(2, \"0\")\n palette[232 + i] = \"#\" + hex + hex + hex\n }\n\n return palette\n})()\n\n/**\n * Convert a Color value to a CSS color string.\n * Returns null for default/inherit colors.\n */\nfunction colorToCSS(color: Color): string | null {\n if (color === null) return null\n if (typeof color === \"number\") {\n return XTERM_256_PALETTE[color] ?? null\n }\n // DEFAULT_BG sentinel → no CSS color (use inherited/default)\n if (color.r === -1) return null\n return `rgb(${color.r},${color.g},${color.b})`\n}\n\n// ============================================================================\n// Buffer to HTML Conversion\n// ============================================================================\n\n/**\n * Convert a terminal buffer to a full HTML document.\n * Suitable for rendering as a screenshot via headless browser.\n *\n * @param buffer The buffer to convert\n * @param options.fontFamily CSS font-family (default: 'JetBrains Mono, Menlo, monospace')\n * @param options.fontSize CSS font-size in px (default: 14)\n * @param options.theme Color scheme (default: 'dark')\n * @returns Complete HTML document string\n */\nexport function bufferToHTML(\n buffer: TerminalBuffer,\n options: {\n fontFamily?: string\n fontSize?: number\n theme?: \"dark\" | \"light\"\n } = {},\n): string {\n const { fontFamily = \"JetBrains Mono, Menlo, monospace\", fontSize = 14, theme = \"dark\" } = options\n\n const defaultFg = theme === \"dark\" ? \"#d4d4d4\" : \"#1e1e1e\"\n const defaultBg = theme === \"dark\" ? \"#1e1e1e\" : \"#ffffff\"\n\n const htmlLines: string[] = []\n\n for (let y = 0; y < buffer.height; y++) {\n let lineHTML = \"\"\n let currentStyle: Style | null = null\n let spanOpen = false\n let linkOpen = false\n let currentHyperlink: string | undefined\n\n for (let x = 0; x < buffer.width; x++) {\n const cell = buffer.getCell(x, y)\n if (cell.continuation) continue\n\n // Handle hyperlink transitions\n const cellHyperlink = cell.hyperlink\n if (cellHyperlink !== currentHyperlink) {\n if (linkOpen) {\n if (spanOpen) {\n lineHTML += \"</span>\"\n spanOpen = false\n }\n lineHTML += \"</a>\"\n linkOpen = false\n }\n if (cellHyperlink) {\n lineHTML += `<a href=\"${escapeHTML(cellHyperlink)}\">`\n linkOpen = true\n }\n currentHyperlink = cellHyperlink\n }\n\n const cellStyle: Style = {\n fg: cell.fg,\n bg: cell.bg,\n underlineColor: cell.underlineColor,\n attrs: cell.attrs,\n }\n\n if (!styleEquals(currentStyle, cellStyle)) {\n if (spanOpen) {\n lineHTML += \"</span>\"\n spanOpen = false\n }\n const css = styleToCSSProperties(cellStyle, defaultFg, defaultBg)\n if (css) {\n lineHTML += `<span style=\"${css}\">`\n spanOpen = true\n }\n currentStyle = cellStyle\n }\n\n lineHTML += escapeHTML(cell.char)\n }\n\n if (spanOpen) {\n lineHTML += \"</span>\"\n }\n if (linkOpen) {\n lineHTML += \"</a>\"\n currentHyperlink = undefined\n }\n\n htmlLines.push(`<div>${lineHTML}</div>`)\n }\n\n return `<!DOCTYPE html>\n<html>\n<head><meta charset=\"utf-8\"></head>\n<body style=\"margin:0;padding:0;background:${defaultBg};color:${defaultFg};font-family:${fontFamily};font-size:${fontSize}px;line-height:1.2;white-space:pre\">\n${htmlLines.join(\"\\n\")}\n</body>\n</html>`\n}\n\n/**\n * Convert a Style to CSS inline style properties.\n * Returns null if the style is entirely default.\n */\nfunction styleToCSSProperties(style: Style, defaultFg: string, defaultBg: string): string | null {\n const parts: string[] = []\n\n // Handle inverse: swap fg/bg\n let fgColor: string | null\n let bgColor: string | null\n if (style.attrs.inverse) {\n fgColor = colorToCSS(style.bg) ?? defaultBg\n bgColor = colorToCSS(style.fg) ?? defaultFg\n } else {\n fgColor = colorToCSS(style.fg)\n bgColor = colorToCSS(style.bg)\n }\n\n if (fgColor) parts.push(`color:${fgColor}`)\n if (bgColor) parts.push(`background:${bgColor}`)\n if (style.attrs.bold) parts.push(\"font-weight:bold\")\n if (style.attrs.dim) parts.push(\"opacity:0.5\")\n if (style.attrs.italic) parts.push(\"font-style:italic\")\n if (style.attrs.hidden) parts.push(\"visibility:hidden\")\n\n // Text decoration: underline and/or strikethrough\n const decorations: string[] = []\n const underlineStyle = style.attrs.underlineStyle\n if (typeof underlineStyle === \"string\") {\n const cssStyleMap: Record<string, string> = {\n single: \"solid\",\n double: \"double\",\n curly: \"wavy\",\n dotted: \"dotted\",\n dashed: \"dashed\",\n }\n decorations.push(\"underline\")\n const cssStyle = cssStyleMap[underlineStyle]\n if (cssStyle) parts.push(`text-decoration-style:${cssStyle}`)\n const ulColor = colorToCSS(style.underlineColor ?? null)\n if (ulColor) parts.push(`text-decoration-color:${ulColor}`)\n } else if (style.attrs.underline) {\n decorations.push(\"underline\")\n }\n if (style.attrs.strikethrough) decorations.push(\"line-through\")\n if (decorations.length > 0) parts.push(`text-decoration:${decorations.join(\" \")}`)\n\n return parts.length > 0 ? parts.join(\";\") : null\n}\n\n/** Escape special HTML characters. */\nfunction escapeHTML(str: string): string {\n if (str === \" \" || str.length === 0) return str\n return str.replaceAll(\"&\", \"&amp;\").replaceAll(\"<\", \"&lt;\").replaceAll(\">\", \"&gt;\").replaceAll('\"', \"&quot;\")\n}\n\n/**\n * Check if any text attributes are active.\n */\nexport function hasActiveAttrs(attrs: CellAttrs): boolean {\n return !!(\n attrs.bold ||\n attrs.dim ||\n attrs.italic ||\n attrs.underline ||\n attrs.underlineStyle ||\n attrs.blink ||\n attrs.inverse ||\n attrs.hidden ||\n attrs.strikethrough\n )\n}\n\n/**\n * Convert style to ANSI escape sequence.\n *\n * Uses SGR 7 for inverse so terminals correctly swap fg/bg\n * (including default terminal colors that have no explicit ANSI code).\n */\n// =============================================================================\n// Color code helpers (imported from ansi/sgr-codes.ts)\n// =============================================================================\n\n/**\n * Convert style to ANSI escape sequence (chalk-compatible format).\n *\n * Emits only non-default attributes with no reset prefix. Called when there\n * is no previous style context (first cell), so the terminal is already in\n * reset state. Each attribute gets its own \\x1b[Xm sequence.\n */\nfunction styleToAnsiCodes(style: Style): string {\n const fg = style.fg\n const bg = style.bg\n\n let result = \"\"\n\n // Foreground color\n if (fg !== null) {\n result += `\\x1b[${fgColorCode(fg)}m`\n }\n\n // Background color (DEFAULT_BG sentinel = terminal default, skip)\n if (bg !== null && !isDefaultBg(bg)) {\n result += `\\x1b[${bgColorCode(bg)}m`\n }\n\n // Attributes\n if (style.attrs.bold) result += \"\\x1b[1m\"\n if (style.attrs.dim) result += \"\\x1b[2m\"\n if (style.attrs.italic) result += \"\\x1b[3m\"\n\n // Underline: use SGR 4:x if style specified, otherwise simple SGR 4\n const underlineStyle = style.attrs.underlineStyle\n if (typeof underlineStyle === \"string\") {\n const styleMap: Record<string, number> = {\n single: 1,\n double: 2,\n curly: 3,\n dotted: 4,\n dashed: 5,\n }\n const subparam = styleMap[underlineStyle]\n if (subparam !== undefined && subparam !== 0) {\n result += `\\x1b[4:${subparam}m`\n }\n } else if (style.attrs.underline) {\n result += \"\\x1b[4m\" // Simple underline\n }\n\n // Use SGR 7 for inverse — lets the terminal correctly swap fg/bg\n if (style.attrs.inverse) result += \"\\x1b[7m\"\n if (style.attrs.strikethrough) result += \"\\x1b[9m\"\n\n // Underline color (SGR 58)\n if (style.underlineColor !== null && style.underlineColor !== undefined) {\n if (typeof style.underlineColor === \"number\") {\n result += `\\x1b[58;5;${style.underlineColor}m`\n } else {\n result += `\\x1b[58;2;${style.underlineColor.r};${style.underlineColor.g};${style.underlineColor.b}m`\n }\n }\n\n return result\n}\n\n/**\n * Compute the minimal SGR transition between two styles (chalk-compatible).\n *\n * When oldStyle is null (first cell or after reset), falls through to\n * full generation via styleToAnsiCodes. Otherwise, diffs attribute\n * by attribute and emits only changed SGR codes as individual \\x1b[Xm sequences.\n */\nfunction styleTransitionCodes(oldStyle: Style | null, newStyle: Style): string {\n // First cell or after reset — full generation\n if (!oldStyle) return styleToAnsiCodes(newStyle)\n\n // Same style — nothing to emit\n if (styleEquals(oldStyle, newStyle)) return \"\"\n\n let result = \"\"\n const oa = oldStyle.attrs\n const na = newStyle.attrs\n\n // Bold and dim share SGR 22 as their off-code\n const boldChanged = Boolean(oa.bold) !== Boolean(na.bold)\n const dimChanged = Boolean(oa.dim) !== Boolean(na.dim)\n if (boldChanged || dimChanged) {\n const boldOff = boldChanged && !na.bold\n const dimOff = dimChanged && !na.dim\n if (boldOff || dimOff) {\n result += \"\\x1b[22m\"\n if (na.bold) result += \"\\x1b[1m\"\n if (na.dim) result += \"\\x1b[2m\"\n } else {\n if (boldChanged && na.bold) result += \"\\x1b[1m\"\n if (dimChanged && na.dim) result += \"\\x1b[2m\"\n }\n }\n if (Boolean(oa.italic) !== Boolean(na.italic)) {\n result += na.italic ? \"\\x1b[3m\" : \"\\x1b[23m\"\n }\n\n // Underline\n const oldUl = Boolean(oa.underline)\n const newUl = Boolean(na.underline)\n const oldUlStyle = oa.underlineStyle ?? false\n const newUlStyle = na.underlineStyle ?? false\n if (oldUl !== newUl || oldUlStyle !== newUlStyle) {\n if (typeof na.underlineStyle === \"string\") {\n const styleMap: Record<string, number> = {\n single: 1,\n double: 2,\n curly: 3,\n dotted: 4,\n dashed: 5,\n }\n const sub = styleMap[na.underlineStyle]\n if (sub !== undefined && sub !== 0) {\n result += `\\x1b[4:${sub}m`\n } else if (newUl) {\n result += \"\\x1b[4m\"\n } else {\n result += \"\\x1b[24m\"\n }\n } else if (newUl) {\n result += \"\\x1b[4m\"\n } else {\n result += \"\\x1b[24m\"\n }\n }\n\n if (Boolean(oa.inverse) !== Boolean(na.inverse)) {\n result += na.inverse ? \"\\x1b[7m\" : \"\\x1b[27m\"\n }\n if (Boolean(oa.strikethrough) !== Boolean(na.strikethrough)) {\n result += na.strikethrough ? \"\\x1b[9m\" : \"\\x1b[29m\"\n }\n\n // Foreground color\n if (!colorEquals(oldStyle.fg, newStyle.fg)) {\n if (newStyle.fg === null) {\n result += \"\\x1b[39m\"\n } else {\n result += `\\x1b[${fgColorCode(newStyle.fg)}m`\n }\n }\n\n // Background color\n if (!colorEquals(oldStyle.bg, newStyle.bg)) {\n if (newStyle.bg === null) {\n result += \"\\x1b[49m\"\n } else {\n result += `\\x1b[${bgColorCode(newStyle.bg)}m`\n }\n }\n\n // Underline color (SGR 58/59)\n if (!colorEquals(oldStyle.underlineColor, newStyle.underlineColor)) {\n if (newStyle.underlineColor === null || newStyle.underlineColor === undefined) {\n result += \"\\x1b[59m\"\n } else if (typeof newStyle.underlineColor === \"number\") {\n result += `\\x1b[58;5;${newStyle.underlineColor}m`\n } else {\n result += `\\x1b[58;2;${newStyle.underlineColor.r};${newStyle.underlineColor.g};${newStyle.underlineColor.b}m`\n }\n }\n\n return result\n}\n\n/**\n * Emit per-attribute reset codes for all active attributes in a style.\n * Returns empty string if no attributes are active.\n * Uses individual \\x1b[Xm sequences to match chalk's format.\n */\nfunction styleResetCodes(style: Style): string {\n let result = \"\"\n // Attributes (order: underline, bold/dim, italic, strikethrough, inverse — matches chalk close order)\n if (style.attrs.underline || style.attrs.underlineStyle) result += \"\\x1b[24m\"\n if (style.attrs.bold || style.attrs.dim) result += \"\\x1b[22m\"\n if (style.attrs.italic) result += \"\\x1b[23m\"\n if (style.attrs.strikethrough) result += \"\\x1b[29m\"\n if (style.attrs.inverse) result += \"\\x1b[27m\"\n // Colors\n if (style.bg !== null && !isDefaultBg(style.bg)) result += \"\\x1b[49m\"\n if (style.fg !== null) result += \"\\x1b[39m\"\n // Underline color\n if (style.underlineColor !== null && style.underlineColor !== undefined) result += \"\\x1b[59m\"\n return result\n}\n\n/**\n * Trim trailing whitespace from a string while preserving ANSI codes.\n */\nfunction trimTrailingWhitespacePreservingAnsi(str: string): string {\n // Find the last non-whitespace character or ANSI escape\n let lastContentIndex = -1\n let i = 0\n\n while (i < str.length) {\n if (str[i] === \"\\x1b\") {\n // Check for OSC sequence (ESC ] ... ST or BEL)\n if (str[i + 1] === \"]\") {\n // Find the terminator: ST (\\x1b\\\\) or BEL (\\x07)\n let end = -1\n for (let j = i + 2; j < str.length; j++) {\n if (str[j] === \"\\x07\") {\n end = j\n break\n }\n if (str[j] === \"\\x1b\" && str[j + 1] === \"\\\\\") {\n end = j + 1\n break\n }\n }\n if (end !== -1) {\n lastContentIndex = end\n i = end + 1\n continue\n }\n }\n // Found SGR escape - skip the entire sequence\n const end = str.indexOf(\"m\", i)\n if (end !== -1) {\n lastContentIndex = end\n i = end + 1\n continue\n }\n }\n if (str[i] !== \" \" && str[i] !== \"\\t\") {\n lastContentIndex = i\n }\n i++\n }\n\n return str.slice(0, lastContentIndex + 1)\n}\n",
19
+ "/**\n * Text Sizing Protocol (OSC 66) -- Kitty v0.40+\n *\n * Lets the app specify how many cells a character should occupy.\n * This solves the measurement/rendering mismatch for Private Use Area (PUA)\n * characters (nerdfont icons, powerline symbols) that `string-width` reports\n * as 1-cell but terminals render as 2-cell.\n *\n * When OSC 66 is used with w=2, both the app's layout engine and the terminal\n * agree on the character width, eliminating truncation and misalignment.\n *\n * Protocol format:\n * ESC ] 66 ; w=<width> ; <text> BEL\n *\n * @see https://sw.kovidgoyal.net/kitty/text-sizing-protocol/\n */\n\nconst OSC = \"\\x1b]\"\nconst ST = \"\\x07\" // BEL terminator (more compatible than ESC \\)\n\n/**\n * Wrap text in an OSC 66 sequence that tells the terminal to render it\n * in exactly `width` cells.\n */\nexport function textSized(text: string, width: number): string {\n return `${OSC}66;w=${width};${text}${ST}`\n}\n\n/**\n * Check if a code point is in the Private Use Area (PUA).\n * Covers BMP PUA (U+E000-U+F8FF) and Supplementary PUA-A/B.\n */\nexport function isPrivateUseArea(cp: number): boolean {\n return (\n (cp >= 0xe000 && cp <= 0xf8ff) || // BMP PUA\n (cp >= 0xf0000 && cp <= 0xffffd) || // Supplementary PUA-A\n (cp >= 0x100000 && cp <= 0x10fffd) // Supplementary PUA-B\n )\n}\n\n/**\n * Check if text sizing is likely supported based on environment variables.\n * This is a fast synchronous check -- use detectTextSizingSupport() for\n * definitive detection via cursor position reports.\n */\nexport function isTextSizingLikelySupported(): boolean {\n const termProgram = process.env.TERM_PROGRAM?.toLowerCase() ?? \"\"\n const termVersion = process.env.TERM_PROGRAM_VERSION ?? \"\"\n\n // Kitty v0.40+ supports OSC 66\n if (termProgram === \"kitty\") {\n const parts = termVersion.split(\".\")\n const major = Number(parts[0]) || 0\n const minor = Number(parts[1]) || 0\n if (major > 0 || (major === 0 && minor >= 40)) return true\n }\n\n // Ghostty parses OSC 66 but does NOT render it (as of v1.3.0, March 2026).\n // Wrapping text in OSC 66 causes Ghostty to swallow the content silently.\n // Re-enable when Ghostty ships actual text sizing GUI support.\n // if (termProgram === \"ghostty\") return true\n\n return false\n}\n\n/** Result of text sizing probe */\nexport interface TextSizingProbeResult {\n supported: boolean\n widthOnly: boolean\n}\n\n/**\n * Cache of probe results by terminal fingerprint.\n * Persists across app instances in the same process so the probe\n * only runs once per terminal type.\n */\nconst probeCache = new Map<string, TextSizingProbeResult>()\n\n/**\n * Get a terminal fingerprint for cache keying.\n * Combines TERM_PROGRAM + TERM_PROGRAM_VERSION to uniquely identify\n * the terminal type. Different versions may add/remove OSC 66 support.\n */\nexport function getTerminalFingerprint(): string {\n const program = process.env.TERM_PROGRAM ?? \"unknown\"\n const version = process.env.TERM_PROGRAM_VERSION ?? \"unknown\"\n return `${program}@${version}`\n}\n\n/**\n * Get a cached probe result for the current terminal, if available.\n */\nexport function getCachedProbeResult(): TextSizingProbeResult | undefined {\n return probeCache.get(getTerminalFingerprint())\n}\n\n/**\n * Store a probe result in the cache for the current terminal.\n */\nexport function setCachedProbeResult(result: TextSizingProbeResult): void {\n probeCache.set(getTerminalFingerprint(), result)\n}\n\n/**\n * Clear the probe cache. Useful for testing.\n */\nexport function clearProbeCache(): void {\n probeCache.clear()\n}\n\n/**\n * Detect terminal support for the text sizing protocol.\n * Uses cursor position reports (CPR) to check if OSC 66 advances the cursor\n * by the specified width.\n *\n * Results are cached by terminal fingerprint so the probe only runs once\n * per terminal type per process.\n *\n * @returns Object with `supported` and `widthOnly` flags:\n * - supported=true, widthOnly=false: full support (scale + width)\n * - supported=true, widthOnly=true: width mode only\n * - supported=false: no support\n */\nexport async function detectTextSizingSupport(\n write: (data: string) => void,\n read: () => Promise<string>,\n timeout = 1000,\n): Promise<TextSizingProbeResult> {\n // Check cache first\n const cached = getCachedProbeResult()\n if (cached !== undefined) return cached\n\n // Detection sequence:\n // 1. CR to column 0\n // 2. OSC 66 w=2 with a space character\n // 3. Request CPR (cursor position report)\n // If cursor is at column 3 (1-indexed), w=2 worked\n const testSequence = \"\\r\" + textSized(\" \", 2) + \"\\x1b[6n\" + \"\\r\\x1b[K\"\n write(testSequence)\n\n try {\n const response = await Promise.race([\n read(),\n new Promise<string>((_resolve, reject) => setTimeout(() => reject(new Error(\"timeout\")), timeout)),\n ])\n\n // Parse CPR response: ESC [ row ; col R\n const match = response.match(/\\x1b\\[(\\d+);(\\d+)R/)\n if (match) {\n const col = Number(match[2])\n // Column 3 means the space occupied 2 cells (col is 1-indexed, started at 1)\n if (col === 3) {\n const result: TextSizingProbeResult = { supported: true, widthOnly: false }\n setCachedProbeResult(result)\n return result\n }\n }\n\n const result: TextSizingProbeResult = { supported: false, widthOnly: false }\n setCachedProbeResult(result)\n return result\n } catch {\n const result: TextSizingProbeResult = { supported: false, widthOnly: false }\n setCachedProbeResult(result)\n return result\n }\n}\n",
20
+ "/**\n * Unicode handling for Silvery.\n *\n * Uses Intl.Segmenter for proper grapheme cluster segmentation and\n * string-width for accurate terminal width calculation.\n *\n * Key concepts:\n * - Grapheme: A user-perceived character (may be multiple code points)\n * - Display width: How many terminal columns a character occupies (0, 1, or 2)\n * - Wide characters: CJK ideographs, emoji, etc. that take 2 columns\n * - Combining characters: Diacritics, emoji modifiers that take 0 columns\n */\n\nimport { BG_OVERRIDE_CODE } from \"./ansi/index\"\nimport sliceAnsi from \"slice-ansi\"\nimport stringWidth from \"string-width\"\nimport { type Cell, type Style, type TerminalBuffer, type UnderlineStyle, createMutableCell } from \"./buffer\"\nimport { isPrivateUseArea } from \"./text-sizing\"\n\n// Re-export for consumers of silvery\nexport { BG_OVERRIDE_CODE }\n\n// ============================================================================\n// Grapheme Segmentation\n// ============================================================================\n\n// Singleton Intl.Segmenter instance (stateless, reusable)\nconst segmenter = new Intl.Segmenter(undefined, { granularity: \"grapheme\" })\n\n// ============================================================================\n// Performance: LRU Cache for displayWidth\n// ============================================================================\n\n/**\n * Simple LRU cache for displayWidth results.\n * String width calculation is expensive (~8us for ASCII text),\n * but the same strings are often measured repeatedly.\n */\nclass DisplayWidthCache {\n private cache = new Map<string, number>()\n private maxSize: number\n\n constructor(maxSize = 1000) {\n this.maxSize = maxSize\n }\n\n get(text: string): number | undefined {\n const cached = this.cache.get(text)\n if (cached !== undefined) {\n // Move to end (most recently used)\n this.cache.delete(text)\n this.cache.set(text, cached)\n }\n return cached\n }\n\n set(text: string, width: number): void {\n // Evict oldest if at capacity\n if (this.cache.size >= this.maxSize) {\n const firstKey = this.cache.keys().next().value\n if (firstKey !== undefined) {\n this.cache.delete(firstKey)\n }\n }\n this.cache.set(text, width)\n }\n\n clear(): void {\n this.cache.clear()\n }\n}\n\n// Cache size: 10K entries should be enough for most TUI apps\n// Each entry is a string key + number value, ~100 bytes, so 10K = ~1MB\nconst displayWidthCache = new DisplayWidthCache(10000)\n\n// ============================================================================\n// Text Sizing Protocol (OSC 66) State\n// ============================================================================\n\n/**\n * Default text-presentation emoji width: true (modern terminal assumption).\n * Modern terminals (Ghostty, iTerm, Kitty) render text-presentation emoji\n * as 2-wide. Terminal.app renders them as 1-wide.\n */\nconst DEFAULT_TEXT_EMOJI_WIDE = true\n\n/**\n * Default text sizing mode: false.\n * When enabled via a Measurer, PUA characters are treated as 2-wide.\n */\nconst DEFAULT_TEXT_SIZING_ENABLED = false\n\n// ============================================================================\n// Scoped Measurer (pipeline execution context)\n// ============================================================================\n\n/**\n * Active measurer for the current pipeline execution.\n * Set by runWithMeasurer() during executeRender, restored after.\n * When null, module-level functions use the lazy default measurer.\n */\nlet _scopedMeasurer: WidthMeasurer | null = null\n\n/**\n * Run a function with a specific measurer as the active scope.\n * Module-level convenience functions (graphemeWidth, displayWidth, etc.)\n * will use this measurer instead of the lazy default for the duration.\n */\nexport function runWithMeasurer<T>(measurer: WidthMeasurer, fn: () => T): T {\n const prev = _scopedMeasurer\n _scopedMeasurer = measurer\n try {\n return fn()\n } finally {\n _scopedMeasurer = prev\n }\n}\n\n/**\n * @deprecated Use createWidthMeasurer() with { textEmojiWide } instead.\n * Kept for backward compatibility but is a no-op.\n */\nexport function setTextEmojiWide(_wide: boolean): void {\n // No-op: use createWidthMeasurer() with { textEmojiWide } instead\n}\n\n/**\n * @deprecated Use createWidthMeasurer() with { textSizingEnabled } instead.\n * Kept for backward compatibility but is a no-op.\n */\nexport function setTextSizingEnabled(_enabled: boolean): void {\n // No-op: use createWidthMeasurer() with { textSizingEnabled } instead\n}\n\n/**\n * Check if text sizing mode is currently enabled.\n * Returns the default (false) since globals have been removed.\n * Use measurer.textSizingEnabled for scoped queries.\n */\nexport function isTextSizingEnabled(): boolean {\n if (_scopedMeasurer) return _scopedMeasurer.textSizingEnabled\n return DEFAULT_TEXT_SIZING_ENABLED\n}\n\n// ============================================================================\n// Width Measurer (per-term instance, no globals)\n// ============================================================================\n\n/**\n * Width measurement functions scoped to specific terminal capabilities.\n * Created by createWidthMeasurer() from TerminalCaps.\n */\nexport interface Measurer {\n readonly textEmojiWide: boolean\n readonly textSizingEnabled: boolean\n displayWidth(text: string): number\n displayWidthAnsi(text: string): number\n graphemeWidth(grapheme: string): number\n wrapText(text: string, width: number, trim?: boolean, hard?: boolean): string[]\n sliceByWidth(text: string, maxWidth: number): string\n sliceByWidthFromEnd(text: string, maxWidth: number): string\n}\n\n/** Backward-compatible alias for Measurer. */\nexport type WidthMeasurer = Measurer\n\n/**\n * Strip OSC 8 hyperlink sequences before passing to slice-ansi.\n * slice-ansi doesn't understand OSC sequences and corrupts them.\n */\nconst OSC8_RE = /\\x1b\\]8;;[^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)/g\nfunction stripOsc8ForSlice(text: string): string {\n return text.replace(OSC8_RE, \"\")\n}\n\n/**\n * Create a width measurer scoped to terminal capabilities.\n * Each measurer has its own caches (no shared global state).\n */\nexport function createWidthMeasurer(caps: { textEmojiWide?: boolean; textSizingEnabled?: boolean } = {}): Measurer {\n const textEmojiWide = caps.textEmojiWide ?? true\n const textSizingEnabled = caps.textSizingEnabled ?? false\n const cache = new DisplayWidthCache(10000)\n\n function measuredGraphemeWidth(grapheme: string): number {\n const width = stringWidth(grapheme)\n if (width !== 1) return width\n if (textEmojiWide && isTextPresentationEmoji(grapheme)) return 2\n if (textSizingEnabled) {\n const cp = grapheme.codePointAt(0)\n if (cp !== undefined && isPrivateUseArea(cp)) return 2\n }\n return width\n }\n\n function measuredDisplayWidth(text: string): number {\n const cached = cache.get(text)\n if (cached !== undefined) return cached\n\n let width: number\n const needsSlowPath = MAY_CONTAIN_TEXT_EMOJI.test(text) || (textSizingEnabled && MAY_CONTAIN_PUA.test(text))\n if (!needsSlowPath) {\n width = stringWidth(text)\n } else {\n const stripped = stripAnsi(text)\n width = 0\n for (const grapheme of splitGraphemes(stripped)) {\n width += measuredGraphemeWidth(grapheme)\n }\n }\n cache.set(text, width)\n return width\n }\n\n function measuredDisplayWidthAnsi(text: string): number {\n return measuredDisplayWidth(stripAnsi(text))\n }\n\n function measuredSliceByWidth(text: string, maxWidth: number): string {\n if (hasAnsi(text)) {\n return sliceAnsi(stripOsc8ForSlice(text), 0, maxWidth)\n }\n let width = 0\n let result = \"\"\n const graphemes = splitGraphemes(text)\n for (const grapheme of graphemes) {\n const gWidth = measuredGraphemeWidth(grapheme)\n if (width + gWidth > maxWidth) break\n result += grapheme\n width += gWidth\n }\n return result\n }\n\n function measuredSliceByWidthFromEnd(text: string, maxWidth: number): string {\n const totalWidth = measuredDisplayWidthAnsi(text)\n if (totalWidth <= maxWidth) return text\n if (hasAnsi(text)) {\n const cleaned = stripOsc8ForSlice(text)\n const cleanedWidth = measuredDisplayWidthAnsi(cleaned)\n const startIndex = cleanedWidth - maxWidth\n return sliceAnsi(cleaned, startIndex)\n }\n const graphemes = splitGraphemes(text)\n let width = 0\n let startIdx = graphemes.length\n for (let i = graphemes.length - 1; i >= 0; i--) {\n const gWidth = measuredGraphemeWidth(graphemes[i]!)\n if (width + gWidth > maxWidth) break\n width += gWidth\n startIdx = i\n }\n return graphemes.slice(startIdx).join(\"\")\n }\n\n function measuredWrapText(text: string, width: number, trim?: boolean, hard?: boolean): string[] {\n return wrapTextWithMeasurer(text, width, measurer, trim ?? false, hard ?? false)\n }\n\n const measurer: Measurer = {\n textEmojiWide,\n textSizingEnabled,\n displayWidth: measuredDisplayWidth,\n displayWidthAnsi: measuredDisplayWidthAnsi,\n graphemeWidth: measuredGraphemeWidth,\n wrapText: measuredWrapText,\n sliceByWidth: measuredSliceByWidth,\n sliceByWidthFromEnd: measuredSliceByWidthFromEnd,\n }\n\n return measurer\n}\n\n/** Alias for createWidthMeasurer. */\nexport const createMeasurer = createWidthMeasurer\n\n// ============================================================================\n// Default Measurer (lazy singleton for module-level convenience functions)\n// ============================================================================\n\nlet _defaultMeasurer: Measurer | undefined\n\n/** Get the default measurer (lazy init, uses default caps). */\nfunction getDefaultMeasurer(): Measurer {\n if (!_defaultMeasurer) {\n _defaultMeasurer = createWidthMeasurer()\n }\n return _defaultMeasurer\n}\n\n/**\n * @deprecated Use createWidthMeasurer() and pass the measurer explicitly.\n * Kept as a no-op for backward compatibility.\n */\nexport function withMeasurer<T>(_measurer: WidthMeasurer, fn: () => T): T {\n return fn()\n}\n\n/**\n * Split a string into grapheme clusters.\n * Each grapheme is a user-perceived character that may consist of\n * multiple Unicode code points.\n *\n * Examples:\n * - \"cafe\\u0301\" (café with combining accent) -> [\"c\", \"a\", \"f\", \"e\\u0301\"]\n * - \"👨‍👩‍👧\" (family emoji) -> [\"👨‍👩‍👧\"]\n * - \"한국어\" -> [\"한\", \"국\", \"어\"]\n */\nexport function splitGraphemes(text: string): string[] {\n return [...segmenter.segment(text)].map((s) => s.segment)\n}\n\n/**\n * Count the number of graphemes in a string.\n */\nexport function graphemeCount(text: string): number {\n let count = 0\n for (const _ of segmenter.segment(text)) count++\n return count\n}\n\n// ============================================================================\n// Emoji Width Correction\n// ============================================================================\n\n/**\n * Regex for Extended_Pictographic characters that have default text presentation.\n * These characters are reported as width 1 by string-width (per Unicode EAW),\n * but most modern terminals render them as 2 columns wide using emoji glyphs.\n *\n * Specifically: Extended_Pictographic AND NOT Emoji_Presentation.\n * Examples: ⚠ (U+26A0), ☑ (U+2611), ✈ (U+2708), ❤ (U+2764)\n * Counter-examples: 📁 (U+1F4C1) has Emoji_Presentation so string-width is correct.\n *\n * Uses the RGI_Emoji regex with VS16 to detect characters that support\n * emoji presentation -- if char+VS16 is RGI emoji, the terminal likely\n * renders the bare char as 2-wide.\n */\nconst TEXT_PRESENTATION_EMOJI_REGEX = /^\\p{Extended_Pictographic}$/u\nconst EMOJI_PRESENTATION_REGEX = /^\\p{Emoji_Presentation}$/u\nconst RGI_EMOJI_REGEX = /^\\p{RGI_Emoji}$/v\n\n/**\n * Cache for isTextPresentationEmoji results.\n * Maps first code point to boolean.\n */\nconst textPresentationEmojiCache = new Map<number, boolean>()\n\n/**\n * Check if a grapheme is a text-presentation emoji that terminals render wide.\n *\n * Returns true for characters that are Extended_Pictographic, do NOT have\n * the Emoji_Presentation property, but become RGI emoji when followed by\n * VS16 (U+FE0F). These characters are rendered as 2 columns in most\n * modern terminals despite string-width reporting width 1.\n */\nexport function isTextPresentationEmoji(grapheme: string): boolean {\n const cp = grapheme.codePointAt(0)\n if (cp === undefined) return false\n\n // Check cache\n const cached = textPresentationEmojiCache.get(cp)\n if (cached !== undefined) return cached\n\n // Multi-codepoint graphemes (with VS16, ZWJ, etc.) are already handled\n // correctly by string-width. Only check single-codepoint graphemes.\n const singleChar = String.fromCodePoint(cp)\n if (singleChar.length !== grapheme.length) {\n textPresentationEmojiCache.set(cp, false)\n return false\n }\n\n // Must be Extended_Pictographic but NOT Emoji_Presentation\n const isExtPict = TEXT_PRESENTATION_EMOJI_REGEX.test(grapheme)\n const isEmojiPres = EMOJI_PRESENTATION_REGEX.test(grapheme)\n if (!isExtPict || isEmojiPres) {\n textPresentationEmojiCache.set(cp, false)\n return false\n }\n\n // Check if adding VS16 makes it an RGI emoji sequence\n const withVs16 = grapheme + \"\\uFE0F\"\n const result = RGI_EMOJI_REGEX.test(withVs16)\n textPresentationEmojiCache.set(cp, result)\n return result\n}\n\n// ============================================================================\n// Private Use Area (PUA) — Nerdfont / Powerline Icons\n// ============================================================================\n\n/**\n * Append VS16 (U+FE0F) to emoji characters that have default text presentation.\n *\n * Use this to normalize icon characters for consistent terminal rendering.\n * Characters that already have emoji presentation or VS16 are returned unchanged.\n *\n * @example\n * ```ts\n * ensureEmojiPresentation('⚠') // '⚠\\uFE0F' (⚠️)\n * ensureEmojiPresentation('☑') // '☑\\uFE0F' (☑️)\n * ensureEmojiPresentation('☐') // '☐' (unchanged, not an emoji)\n * ensureEmojiPresentation('📁') // '📁' (unchanged, already emoji presentation)\n * ```\n */\nexport function ensureEmojiPresentation(char: string): string {\n if (char.includes(\"\\uFE0F\")) return char // Already has VS16\n if (isTextPresentationEmoji(char)) return char + \"\\uFE0F\"\n return char\n}\n\n// ============================================================================\n// Display Width Calculation\n// ============================================================================\n\n/**\n * Regex to detect strings that MAY contain text-presentation emoji.\n * Used as a fast pre-check before the more expensive grapheme-based calculation.\n * Covers the Unicode blocks where Extended_Pictographic characters live:\n * - Miscellaneous Technical (U+2300-U+23FF)\n * - Miscellaneous Symbols (U+2600-U+26FF)\n * - Dingbats (U+2700-U+27BF)\n * - Miscellaneous Symbols and Arrows (U+2B00-U+2BFF)\n * - Other scattered ranges\n */\nconst MAY_CONTAIN_TEXT_EMOJI =\n /[\\u203C\\u2049\\u2122\\u2139\\u2194-\\u2199\\u21A9\\u21AA\\u2328\\u23CF\\u23ED-\\u23EF\\u23F1\\u23F2\\u23F8-\\u23FA\\u25AA\\u25AB\\u25B6\\u25C0\\u25FB-\\u25FE\\u2600-\\u2604\\u260E\\u2611\\u2614\\u2615\\u2618\\u261D\\u2620\\u2622\\u2623\\u2626\\u262A\\u262E\\u262F\\u2638-\\u263A\\u2640\\u2642\\u2648-\\u2653\\u265F\\u2660\\u2663\\u2665\\u2666\\u2668\\u267B\\u267E\\u267F\\u2692-\\u2697\\u2699\\u269B\\u269C\\u26A0\\u26A1\\u26A7\\u26AA\\u26AB\\u26B0\\u26B1\\u26BD\\u26BE\\u26C4\\u26C5\\u26C8\\u26CE\\u26CF\\u26D1\\u26D3\\u26D4\\u26E9\\u26EA\\u26F0-\\u26F5\\u26F7-\\u26FA\\u26FD\\u2702\\u2705\\u2708-\\u270D\\u270F\\u2712\\u2714\\u2716\\u271D\\u2721\\u2728\\u2733\\u2734\\u2744\\u2747\\u274C\\u274E\\u2753-\\u2755\\u2757\\u2763\\u2764\\u2795-\\u2797\\u27A1\\u27B0\\u27BF\\u2934\\u2935\\u2B05-\\u2B07\\u2B1B\\u2B1C\\u2B50\\u2B55\\u3030\\u303D\\u3297\\u3299]/\n\n/**\n * Fast pre-check regex for BMP Private Use Area characters (U+E000-U+F8FF).\n * Used to gate the slow grapheme-by-grapheme path when text sizing is enabled.\n */\nconst MAY_CONTAIN_PUA = /[\\uE000-\\uF8FF]/\n\n/**\n * Get the display width of a string (number of terminal columns).\n * Uses string-width which handles:\n * - Wide characters (CJK) -> 2 columns\n * - Regular ASCII -> 1 column\n * - Zero-width characters (combining, ZWJ) -> 0 columns\n * - Emoji -> varies (1 or 2)\n * - ANSI escape sequences -> 0 columns (stripped)\n *\n * Corrects string-width for text-presentation emoji characters\n * (e.g., ⚠ U+26A0) that terminals render as 2 columns wide.\n *\n * Results are cached for performance.\n */\nexport function displayWidth(text: string): number {\n if (_scopedMeasurer) return _scopedMeasurer.displayWidth(text)\n // Check cache first\n const cached = displayWidthCache.get(text)\n if (cached !== undefined) {\n return cached\n }\n\n let width: number\n // Fast path: if text cannot contain text-presentation emoji, use string-width directly.\n // Default measurer does not enable text sizing, so PUA check uses the constant default.\n const needsSlowPath = MAY_CONTAIN_TEXT_EMOJI.test(text) || (DEFAULT_TEXT_SIZING_ENABLED && MAY_CONTAIN_PUA.test(text))\n if (!needsSlowPath) {\n width = stringWidth(text)\n } else {\n // Slow path: strip ANSI codes first (they'd inflate the grapheme count),\n // then split into graphemes and sum corrected widths\n const stripped = stripAnsi(text)\n width = 0\n for (const grapheme of splitGraphemes(stripped)) {\n width += graphemeWidth(grapheme)\n }\n }\n\n displayWidthCache.set(text, width)\n return width\n}\n\n/**\n * Get the display width of a single grapheme.\n *\n * Overrides string-width for characters that are Extended_Pictographic with\n * default text presentation. These characters (e.g., ⚠ U+26A0, ☑ U+2611)\n * are reported as width 1 by string-width (per Unicode EAW tables), but most\n * modern terminals render them as 2 columns wide using emoji glyphs.\n *\n * The mismatch causes text after these characters to be placed at the wrong\n * column, leading to truncation or overlap.\n */\nexport function graphemeWidth(grapheme: string): number {\n if (_scopedMeasurer) return _scopedMeasurer.graphemeWidth(grapheme)\n const width = stringWidth(grapheme)\n // If string-width already says 2 (or 0), trust it\n if (width !== 1) return width\n // Check if this is a text-presentation emoji that terminals render wide.\n // Uses DEFAULT_TEXT_EMOJI_WIDE (true) — assumes modern terminal.\n if (DEFAULT_TEXT_EMOJI_WIDE && isTextPresentationEmoji(grapheme)) return 2\n // Default module-level function does not enable text sizing.\n // Scoped measurers handle PUA via their own graphemeWidth.\n if (DEFAULT_TEXT_SIZING_ENABLED) {\n const cp = grapheme.codePointAt(0)\n if (cp !== undefined && isPrivateUseArea(cp)) return 2\n }\n return width\n}\n\n/**\n * Check if a grapheme is a wide character (takes 2 columns).\n */\nexport function isWideGrapheme(grapheme: string): boolean {\n return graphemeWidth(grapheme) === 2\n}\n\n/**\n * Check if a grapheme is zero-width (combining character, ZWJ, etc.).\n */\nexport function isZeroWidthGrapheme(grapheme: string): boolean {\n return stringWidth(grapheme) === 0\n}\n\n// ============================================================================\n// Text Manipulation\n// ============================================================================\n\n/**\n * Truncate a string to fit within a given display width.\n * Handles wide characters and ANSI escape sequences (including OSC 8 hyperlinks) correctly.\n *\n * @param text - The text to truncate (may contain ANSI escape sequences)\n * @param maxWidth - Maximum display width\n * @param ellipsis - Ellipsis to append if truncated (default: \"...\")\n * @returns Truncated string\n */\nexport function truncateText(\n text: string,\n maxWidth: number,\n ellipsis = \"\\u2026\", // Unicode ellipsis (single character)\n): string {\n const textWidth = displayWidth(text)\n\n // No truncation needed\n if (textWidth <= maxWidth) {\n return text\n }\n\n const ellipsisWidth = displayWidth(ellipsis)\n const targetWidth = maxWidth - ellipsisWidth\n\n if (targetWidth <= 0) {\n // Not enough space for even the ellipsis\n return maxWidth > 0 ? ellipsis.slice(0, maxWidth) : \"\"\n }\n\n // Use ANSI-aware grapheme splitting when text contains escape sequences\n // (including OSC 8 hyperlinks) to avoid counting escape bytes as visible width.\n const graphemes = hasAnsi(text) ? splitGraphemesAnsiAware(text) : splitGraphemes(text)\n let result = \"\"\n let currentWidth = 0\n\n for (const grapheme of graphemes) {\n const gWidth = graphemeWidth(grapheme)\n if (currentWidth + gWidth > targetWidth) {\n break\n }\n result += grapheme\n currentWidth += gWidth\n }\n\n return result + ellipsis\n}\n\n/**\n * Pad a string to a given display width.\n *\n * @param text - The text to pad\n * @param width - Target display width\n * @param align - Alignment: 'left', 'right', or 'center'\n * @param padChar - Character to use for padding (default: space)\n * @returns Padded string\n */\nexport function padText(\n text: string,\n width: number,\n align: \"left\" | \"right\" | \"center\" = \"left\",\n padChar = \" \",\n): string {\n const textWidth = displayWidth(text)\n const padWidth = width - textWidth\n\n if (padWidth <= 0) {\n return text\n }\n\n const padCharWidth = displayWidth(padChar)\n if (padCharWidth === 0) {\n // Can't pad with zero-width characters\n return text\n }\n\n // Calculate number of pad characters needed\n const padCount = Math.floor(padWidth / padCharWidth)\n\n switch (align) {\n case \"left\":\n return text + padChar.repeat(padCount)\n case \"right\":\n return padChar.repeat(padCount) + text\n case \"center\": {\n const leftPad = Math.floor(padCount / 2)\n const rightPad = padCount - leftPad\n return padChar.repeat(leftPad) + text + padChar.repeat(rightPad)\n }\n }\n}\n\n/**\n * Constrain text to width and height limits.\n * Combines wrapping and truncation to fit text in a box.\n *\n * @param text - Text to constrain (may contain ANSI codes)\n * @param width - Maximum display width per line\n * @param maxLines - Maximum number of lines\n * @param pad - If true, pad lines to full width\n * @param ellipsis - Custom ellipsis character (default: \"…\")\n * @returns Object with lines array and truncated flag\n */\nexport function constrainText(\n text: string,\n width: number,\n maxLines: number,\n pad = false,\n ellipsis = \"…\",\n): { lines: string[]; truncated: boolean } {\n const allLines = wrapText(text, width)\n const truncated = allLines.length > maxLines\n let lines = allLines.slice(0, maxLines)\n\n if (truncated && lines.length > 0) {\n const lastIdx = lines.length - 1\n const lastLine = lines[lastIdx]\n if (lastLine) {\n const ellipsisLen = displayWidth(ellipsis)\n const lastLineLen = displayWidth(lastLine)\n if (lastLineLen + ellipsisLen <= width) {\n lines[lastIdx] = lastLine + ellipsis\n } else {\n lines[lastIdx] = truncateText(lastLine, width, ellipsis)\n }\n }\n }\n\n if (pad) {\n lines = lines.map((line) => padText(line, width))\n }\n\n return { lines, truncated }\n}\n\n/**\n * Check if a grapheme is a word boundary character (space, hyphen, etc.)\n */\nfunction isWordBoundary(grapheme: string): boolean {\n // Common word boundary characters\n return grapheme === \" \" || grapheme === \"-\" || grapheme === \"\\t\"\n}\n\n/**\n * Look ahead from a space to check if the next word is a single-character\n * operator (like +, =, *, /, etc.) followed by another space. Breaking before\n * such operators looks bad — e.g. \"$12k\\n+ $400\" — so we suppress the break\n * point to keep the operator with its left operand.\n *\n * Accepts an explicit graphemeWidth function so it works with both the\n * module-level default and per-measurer instances.\n */\nfunction isBreakBeforeOperatorWith(graphemes: string[], spaceIndex: number, gWidthFn: (g: string) => number): boolean {\n // Look for pattern: [current space] [operator] [space]\n // spaceIndex is the index of the current space in the graphemes array\n let j = spaceIndex + 1\n // Skip any zero-width characters (ANSI escapes)\n while (j < graphemes.length && gWidthFn(graphemes[j]!) === 0) j++\n if (j >= graphemes.length) return false\n const nextChar = graphemes[j]!\n // Must be a single visible character that is not alphanumeric or space\n if (gWidthFn(nextChar) !== 1) return false\n if (/^[a-zA-Z0-9\\s]$/.test(nextChar)) return false\n // Check that it's followed by a space (it's an infix operator, not a prefix)\n let k = j + 1\n while (k < graphemes.length && gWidthFn(graphemes[k]!) === 0) k++\n if (k >= graphemes.length) return false\n return graphemes[k] === \" \"\n}\n\n/**\n * Check if a grapheme can break anywhere (CJK characters).\n * CJK text doesn't use spaces between words, so any character boundary is valid.\n */\nfunction canBreakAnywhere(grapheme: string): boolean {\n return isCJK(grapheme)\n}\n\n// ANSI CSI pattern: ESC [ (params) (letter)\nconst ANSI_CSI_RE = /^\\x1b\\[[0-9;:?]*[A-Za-z]/\n// ANSI OSC pattern: ESC ] ... (BEL or ST)\nconst ANSI_OSC_RE = /^\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)/\n// Single-char escape: ESC followed by one letter\nconst ANSI_SINGLE_RE = /^\\x1b[DME78(B]/\n\n/**\n * Split text into graphemes, keeping ANSI escape sequences as single zero-width tokens.\n * Without this, `splitGraphemes` would split `\\x1b[38;5;1m` into individual characters\n * like `[`, `3`, `8`, `;`, etc., each consuming display width.\n */\nfunction splitGraphemesAnsiAware(text: string): string[] {\n if (!hasAnsi(text)) {\n return splitGraphemes(text)\n }\n\n const result: string[] = []\n let pos = 0\n\n while (pos < text.length) {\n if (text[pos] === \"\\x1b\") {\n // Try to match an ANSI sequence starting at pos\n const remaining = text.slice(pos)\n const csi = remaining.match(ANSI_CSI_RE)\n if (csi) {\n result.push(csi[0])\n pos += csi[0].length\n continue\n }\n const osc = remaining.match(ANSI_OSC_RE)\n if (osc) {\n result.push(osc[0])\n pos += osc[0].length\n continue\n }\n const single = remaining.match(ANSI_SINGLE_RE)\n if (single) {\n result.push(single[0])\n pos += single[0].length\n continue\n }\n }\n\n // Find the next ESC or end of string\n const nextEsc = text.indexOf(\"\\x1b\", pos + 1)\n const chunk = nextEsc === -1 ? text.slice(pos) : text.slice(pos, nextEsc)\n\n // Split this non-ANSI chunk into graphemes\n for (const g of splitGraphemes(chunk)) {\n result.push(g)\n }\n pos += chunk.length\n }\n\n return result\n}\n\n/**\n * Wrap text to fit within a given width.\n *\n * Implements word-boundary wrapping:\n * 1. Breaks at word boundaries (spaces, hyphens) when possible\n * 2. Falls back to character wrap only when necessary (very long words)\n * 3. Handles CJK text properly (can break anywhere since CJK has no word spaces)\n * 4. Preserves intentional line breaks\n *\n * @param text - The text to wrap (may contain ANSI escape sequences)\n * @param width - Maximum display width per line\n * @param preserveNewlines - Whether to preserve existing newlines\n * @param trim - Trim trailing spaces on broken lines and skip leading spaces on continuation lines (useful for rendering)\n * @returns Array of wrapped lines\n */\nexport function wrapText(text: string, width: number, preserveNewlines = true, trim = false): string[] {\n return wrapTextWithMeasurer(text, width, _scopedMeasurer ?? undefined, trim, false, preserveNewlines)\n}\n\n/**\n * Internal: wrap text using an explicit measurer for grapheme width calculations.\n * When measurer is undefined, falls back to the module-level graphemeWidth.\n */\nfunction wrapTextWithMeasurer(\n text: string,\n width: number,\n measurer: Measurer | undefined,\n trim = false,\n _hard = false,\n preserveNewlines = true,\n): string[] {\n if (width <= 0) {\n return []\n }\n\n const gWidthFn = measurer ? measurer.graphemeWidth.bind(measurer) : graphemeWidth\n\n const lines: string[] = []\n\n // Split by newlines first if preserving\n const inputLines = preserveNewlines ? text.split(\"\\n\") : [text.replace(/\\n/g, \" \")]\n\n for (const line of inputLines) {\n // Handle empty lines\n if (line === \"\") {\n lines.push(\"\")\n continue\n }\n\n // If the line contains ANSI escape sequences, split them out so they\n // don't consume display width. We interleave visible graphemes with\n // zero-width ANSI \"tokens\" that are appended to currentLine untouched.\n const graphemes = splitGraphemesAnsiAware(line)\n let currentLine = \"\"\n let currentWidth = 0\n let isFirstLineOfParagraph = true\n\n // Track the last valid break point\n let lastBreakIndex = -1 // Index in currentLine (character position)\n let lastBreakWidth = 0 // Width at break point\n let lastBreakGraphemeIndex = -1 // Index in graphemes array\n\n for (let i = 0; i < graphemes.length; i++) {\n const grapheme = graphemes[i]!\n const gWidth = gWidthFn(grapheme)\n\n // Handle zero-width characters\n if (gWidth === 0) {\n currentLine += grapheme\n continue\n }\n\n // In trim mode, skip leading spaces on continuation lines\n if (trim && !isFirstLineOfParagraph && currentWidth === 0 && isWordBoundary(grapheme) && grapheme !== \"-\") {\n continue\n }\n\n // Check if this grapheme is a break point\n // Break AFTER spaces/hyphens, or BEFORE CJK characters\n if (isWordBoundary(grapheme)) {\n // Include the boundary character, then mark as break point\n if (currentWidth + gWidth <= width) {\n currentLine += grapheme\n currentWidth += gWidth\n // Suppress break point if the next word is a lone operator (e.g. \"+\", \"=\")\n // to avoid orphaning operators at the start of the next line.\n if (grapheme !== \" \" || !isBreakBeforeOperatorWith(graphemes, i, gWidthFn)) {\n lastBreakIndex = currentLine.length\n lastBreakWidth = currentWidth\n lastBreakGraphemeIndex = i + 1\n }\n continue\n }\n // Space/hyphen doesn't fit — break here (before the boundary char).\n // The current line is complete; the boundary char is consumed as the break.\n if (currentLine) {\n let lineToAdd = currentLine\n if (trim) lineToAdd = lineToAdd.trimEnd()\n lines.push(lineToAdd)\n isFirstLineOfParagraph = false\n }\n currentLine = \"\"\n currentWidth = 0\n lastBreakIndex = -1\n lastBreakWidth = 0\n lastBreakGraphemeIndex = -1\n continue\n } else if (canBreakAnywhere(grapheme)) {\n // CJK: can break before this character\n lastBreakIndex = currentLine.length\n lastBreakWidth = currentWidth\n lastBreakGraphemeIndex = i\n }\n\n // Would this grapheme overflow?\n if (currentWidth + gWidth > width) {\n if (lastBreakIndex > 0) {\n // We have a valid break point - use it\n let lineToAdd = currentLine.slice(0, lastBreakIndex)\n if (trim) lineToAdd = lineToAdd.trimEnd()\n lines.push(lineToAdd)\n isFirstLineOfParagraph = false\n\n // Reset and continue from break point\n currentLine = currentLine.slice(lastBreakIndex)\n currentWidth = currentWidth - lastBreakWidth\n\n // Rewind to process graphemes after the break\n i = lastBreakGraphemeIndex - 1\n currentLine = \"\"\n currentWidth = 0\n lastBreakIndex = -1\n lastBreakWidth = 0\n lastBreakGraphemeIndex = -1\n } else {\n // No break point found - must do character wrap\n if (currentLine) {\n if (trim) currentLine = currentLine.trimEnd()\n lines.push(currentLine)\n isFirstLineOfParagraph = false\n }\n currentLine = grapheme\n currentWidth = gWidth\n lastBreakIndex = -1\n lastBreakWidth = 0\n lastBreakGraphemeIndex = -1\n }\n } else {\n currentLine += grapheme\n currentWidth += gWidth\n }\n }\n\n // Push remaining content\n if (currentLine) {\n lines.push(currentLine)\n }\n }\n\n // Fix OSC 8 hyperlink state across wrapped lines — each line must be self-contained\n if (text.includes(\"\\x1b]8;;\")) {\n fixOsc8AcrossWrappedLines(lines)\n }\n\n return lines\n}\n\n/**\n * Post-process wrapped lines to ensure OSC 8 hyperlink sequences are self-contained per line.\n * When wrapping splits text containing OSC 8 sequences across lines, the open/close sequences\n * may end up on different lines. This closes unclosed hyperlinks at line ends and re-opens\n * them on continuation lines. Mutates the array in place.\n */\nfunction fixOsc8AcrossWrappedLines(lines: string[]): void {\n let activeHref: string | null = null\n\n for (let i = 0; i < lines.length; i++) {\n let line = lines[i]!\n\n // Re-open hyperlink from previous line\n if (activeHref !== null) {\n line = `\\x1b]8;;${activeHref}\\x1b\\\\` + line\n }\n\n // Scan line for OSC 8 sequences to determine end-of-line state\n let lineHref: string | null = activeHref\n const osc8Matches = line.matchAll(/\\x1b\\]8;;([^\\x07\\x1b]*)(?:\\x07|\\x1b\\\\)/g)\n for (const m of osc8Matches) {\n lineHref = m[1] === \"\" ? null : m[1]!\n }\n\n // Close unclosed hyperlink at line end\n if (lineHref !== null) {\n line += \"\\x1b]8;;\\x1b\\\\\"\n }\n\n lines[i] = line\n activeHref = lineHref\n }\n}\n\n/**\n * Slice text by display width (from start).\n * Returns the first `maxWidth` columns of text.\n * Uses the default measurer for width calculations.\n * Handles both ANSI-styled and plain text.\n *\n * @param text - The text to slice\n * @param maxWidth - Maximum display width to keep from the start\n * @returns Sliced string from the start\n */\nexport function sliceByWidth(text: string, maxWidth: number): string {\n return (_scopedMeasurer ?? getDefaultMeasurer()).sliceByWidth(text, maxWidth)\n}\n\n/**\n * Slice a string by display width range.\n * Like string.slice() but works with display columns.\n *\n * @param text - The text to slice\n * @param start - Start display column (inclusive)\n * @param end - End display column (exclusive)\n * @returns Sliced string\n */\nexport function sliceByWidthRange(text: string, start: number, end?: number): string {\n const graphemes = splitGraphemes(text)\n let result = \"\"\n let currentCol = 0\n const endCol = end ?? Number.POSITIVE_INFINITY\n\n for (const grapheme of graphemes) {\n const gWidth = graphemeWidth(grapheme)\n\n // Haven't reached start yet\n if (currentCol + gWidth <= start) {\n currentCol += gWidth\n continue\n }\n\n // Past the end\n if (currentCol >= endCol) {\n break\n }\n\n // This grapheme is at least partially in range\n result += grapheme\n currentCol += gWidth\n }\n\n return result\n}\n\n/**\n * Slice text by display width from the end.\n * Returns the last `maxWidth` columns of text.\n * Uses the default measurer for width calculations.\n *\n * @param text - The text to slice\n * @param maxWidth - Maximum display width to keep from the end\n * @returns Sliced string from the end\n */\nexport function sliceByWidthFromEnd(text: string, maxWidth: number): string {\n return (_scopedMeasurer ?? getDefaultMeasurer()).sliceByWidthFromEnd(text, maxWidth)\n}\n\n// ============================================================================\n// Buffer Writing\n// ============================================================================\n\n/**\n * Write styled text to a terminal buffer.\n *\n * Handles:\n * - Multi-byte graphemes (emoji, combining characters)\n * - Wide characters (CJK) that take 2 cells\n * - Zero-width characters (appended to previous cell)\n *\n * @param buffer - The buffer to write to\n * @param x - Starting column\n * @param y - Row\n * @param text - Text to write\n * @param style - Style to apply\n * @returns The ending column (x + display_width)\n */\nexport function writeTextToBuffer(\n buffer: TerminalBuffer,\n x: number,\n y: number,\n text: string,\n style: Style = { fg: null, bg: null, attrs: {} },\n): number {\n const graphemes = splitGraphemes(text)\n let col = x\n let combineCell: Cell | null = null\n\n for (const grapheme of graphemes) {\n const width = graphemeWidth(grapheme)\n\n if (width === 0) {\n // Zero-width character: combine with previous cell.\n // Use readCellInto to avoid allocating a fresh Cell on each combine.\n if (col > 0 && buffer.inBounds(col - 1, y)) {\n // Lazy-init reusable cell (zero-width combining is uncommon)\n combineCell ??= createMutableCell()\n buffer.readCellInto(col - 1, y, combineCell)\n combineCell.char = combineCell.char + grapheme\n buffer.setCell(col - 1, y, combineCell)\n }\n } else if (width === 1) {\n // Normal single-width character\n if (buffer.inBounds(col, y)) {\n buffer.setCell(col, y, {\n char: grapheme,\n fg: style.fg,\n bg: style.bg,\n attrs: style.attrs,\n wide: false,\n continuation: false,\n })\n }\n col++\n } else if (width === 2) {\n // Wide character: takes 2 cells\n // For text-presentation emoji, add VS16 so terminals render at 2 columns\n const outputChar = ensureEmojiPresentation(grapheme)\n if (buffer.inBounds(col, y)) {\n buffer.setCell(col, y, {\n char: outputChar,\n fg: style.fg,\n bg: style.bg,\n attrs: style.attrs,\n wide: true,\n continuation: false,\n })\n }\n if (buffer.inBounds(col + 1, y)) {\n buffer.setCell(col + 1, y, {\n char: \"\",\n fg: style.fg,\n bg: style.bg,\n attrs: style.attrs,\n wide: false,\n continuation: true,\n })\n }\n col += 2\n }\n\n // Stop if we've gone past the buffer edge\n if (col >= buffer.width) {\n break\n }\n }\n\n return col\n}\n\n/**\n * Write styled text to a buffer with automatic truncation.\n *\n * @param buffer - The buffer to write to\n * @param x - Starting column\n * @param y - Row\n * @param text - Text to write\n * @param maxWidth - Maximum width (truncate if exceeded)\n * @param style - Style to apply\n * @param ellipsis - Ellipsis for truncated text\n */\nexport function writeTextTruncated(\n buffer: TerminalBuffer,\n x: number,\n y: number,\n text: string,\n maxWidth: number,\n style: Style = { fg: null, bg: null, attrs: {} },\n ellipsis = \"\\u2026\",\n): void {\n const textWidth = displayWidth(text)\n\n if (textWidth <= maxWidth) {\n writeTextToBuffer(buffer, x, y, text, style)\n } else {\n const truncated = truncateText(text, maxWidth, ellipsis)\n writeTextToBuffer(buffer, x, y, truncated, style)\n }\n}\n\n/**\n * Write multiple lines of styled text to a buffer.\n *\n * @param buffer - The buffer to write to\n * @param x - Starting column\n * @param y - Starting row\n * @param lines - Lines to write\n * @param style - Style to apply\n */\nexport function writeLinesToBuffer(\n buffer: TerminalBuffer,\n x: number,\n y: number,\n lines: string[],\n style: Style = { fg: null, bg: null, attrs: {} },\n): void {\n for (let i = 0; i < lines.length; i++) {\n if (y + i >= buffer.height) break\n writeTextToBuffer(buffer, x, y + i, lines[i]!, style)\n }\n}\n\n// ============================================================================\n// ANSI-Aware Operations\n// ============================================================================\n\n/**\n * Strip all ANSI escape codes from a string.\n *\n * Handles:\n * - CSI sequences (cursor movement, colors, SGR, etc.)\n * - OSC sequences (window titles, hyperlinks)\n * - Single-character escape sequences\n * - Character set selection\n */\nexport function stripAnsi(text: string): string {\n return text\n .replace(/\\x1b\\[[0-9;:?]*[A-Za-z]/g, \"\") // ESC CSI sequences (including SGR with colons)\n .replace(/\\x9b[0-9;:?]*[A-Za-z]/g, \"\") // C1 CSI sequences\n .replace(/\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)/g, \"\") // ESC OSC sequences\n .replace(/\\x9d[^\\x07\\x1b\\x9c]*(?:\\x07|\\x1b\\\\|\\x9c)/g, \"\") // C1 OSC sequences\n .replace(/\\x1b[DME78]/g, \"\") // Single-char sequences\n .replace(/\\x1b\\(B/g, \"\") // Character set selection\n}\n\n/**\n * Get display width of text with ANSI sequences.\n * ANSI sequences don't contribute to display width.\n */\nexport function displayWidthAnsi(text: string): number {\n return displayWidth(stripAnsi(text))\n}\n\n/**\n * Truncate text that may contain ANSI sequences.\n * Preserves ANSI codes while truncating visible characters.\n *\n * Note: This is a simplified implementation that strips ANSI before\n * truncation. For proper ANSI-aware truncation, consider using\n * slice-ansi or similar library.\n */\nexport function truncateAnsi(text: string, maxWidth: number, ellipsis = \"\\u2026\"): string {\n // Simple approach: if text has ANSI, strip and truncate\n // A more sophisticated approach would preserve styles\n const stripped = stripAnsi(text)\n return truncateText(stripped, maxWidth, ellipsis)\n}\n\n// ============================================================================\n// ANSI Parsing\n// ============================================================================\n\n// BG_OVERRIDE_CODE is imported from ansi and re-exported at top of file\n\n/** Styled text segment with associated ANSI colors/attributes */\nexport interface StyledSegment {\n text: string\n fg?: number | null // SGR color code (30-37, 90-97, or 38;5;N / 38;2;r;g;b)\n bg?: number | null // SGR color code (40-47, 100-107, or 48;5;N / 48;2;r;g;b)\n /**\n * Underline color (SGR 58).\n * Same format as fg/bg: packed RGB with 0x1000000 marker, or 256-color index.\n */\n underlineColor?: number | null\n bold?: boolean\n dim?: boolean\n italic?: boolean\n underline?: boolean\n /**\n * Underline style variant (SGR 4:x).\n * Uses UnderlineStyle from buffer.ts.\n */\n underlineStyle?: UnderlineStyle\n inverse?: boolean\n bgOverride?: boolean // Set when BG_OVERRIDE_CODE (9999) is present\n /**\n * OSC 8 hyperlink URL.\n * Set when the segment is inside an OSC 8 hyperlink sequence.\n */\n hyperlink?: string\n /**\n * True when the foreground color was specified using colon-separated SGR\n * (e.g., `38:2::255:100:0m` instead of `38;2;255;100;0m`).\n * Used to preserve the original format in round-trip output.\n */\n colonFg?: boolean\n /**\n * True when the background color was specified using colon-separated SGR.\n */\n colonBg?: boolean\n}\n\n/**\n * Map SGR 4:x subparameter to underline style.\n * 0=none, 1=single, 2=double, 3=curly, 4=dotted, 5=dashed\n */\nfunction parseUnderlineStyle(subparam: number): UnderlineStyle {\n switch (subparam) {\n case 0:\n return false\n case 1:\n return \"single\"\n case 2:\n return \"double\"\n case 3:\n return \"curly\"\n case 4:\n return \"dotted\"\n case 5:\n return \"dashed\"\n default:\n return \"single\" // Unknown, default to single\n }\n}\n\n/**\n * Parse text with ANSI escape sequences into styled segments.\n * Handles basic SGR (Select Graphic Rendition) codes including:\n * - Standard colors (30-37, 40-47, 90-97, 100-107)\n * - Extended colors (38;5;N, 48;5;N for 256-color, 38;2;r;g;b, 48;2;r;g;b for RGB)\n * - Underline styles (4:x where x = 0-5)\n * - Underline color (58;5;N for 256-color, 58;2;r;g;b for RGB)\n */\nexport function parseAnsiText(text: string): StyledSegment[] {\n const segments: StyledSegment[] = []\n\n // Step 1: Strip non-SGR CSI sequences (cursor movement, erase, etc.) that would\n // otherwise leak through as literal text. SGR sequences end in 'm'; all\n // other CSI sequences (ending in A-L, H, J, K, S, T, etc.) are stripped.\n // Handles both ESC-based CSI (\\x1b[) and C1 CSI (\\x9b).\n // This must happen BEFORE OSC 8 processing so hyperlink position tracking\n // is based on the cleaned text (no position drift from stripped sequences).\n const sanitizedText = text.replace(/\\x1b\\[[0-9;:]*[A-LN-Za-ln-z@`]/g, \"\").replace(/\\x9b[0-9;:]*[A-LN-Za-ln-z@`]/g, \"\")\n\n // Step 2: Strip OSC 8 hyperlink sequences and build a position-to-URL map.\n // OSC 8 format: \\x1b]8;;URL(\\x1b\\\\ | \\x07) for open, \\x1b]8;;(\\x1b\\\\ | \\x07) for close.\n // Also handles C1 OSC (\\x9d) form: \\x9d8;;URL(\\x1b\\\\ | \\x07 | \\x9c)\n // We strip these from the text before SGR parsing and track which character\n // positions map to which hyperlink URL.\n //\n // Hyperlink format metadata is encoded in the URL using control char prefixes:\n // \\x01c1b\\x02 = C1 OSC intro + BEL terminator\n // \\x01c1s\\x02 = C1 OSC intro + ST terminator\n // \\x01e7b\\x02 = ESC OSC intro + BEL terminator\n // (no prefix) = ESC OSC intro + ST terminator (default)\n // bufferToStyledText reads these prefixes to emit the original format.\n const oscPattern = /(?:\\x1b\\]|\\x9d)8;;([^\\x07\\x1b\\x9c]*)(?:\\x07|\\x1b\\\\|\\x9c)/g\n let currentHyperlink: string | undefined\n // Map from character index in cleaned text to hyperlink URL\n const hyperlinkRanges: Array<{ start: number; end: number; url: string }> = []\n let rangeStart = -1\n let cleaned = \"\"\n let oscMatch: RegExpExecArray | null\n let oscLastIndex = 0\n\n while ((oscMatch = oscPattern.exec(sanitizedText)) !== null) {\n // Append text between last OSC and this one (preserving SGR codes)\n cleaned += sanitizedText.slice(oscLastIndex, oscMatch.index)\n const url = oscMatch[1]!\n\n // Detect format: C1 vs ESC intro, BEL vs ST terminator\n const matchStr = oscMatch[0]\n const isC1 = matchStr.charCodeAt(0) === 0x9d\n const lastChar = matchStr.charCodeAt(matchStr.length - 1)\n const isBel = lastChar === 0x07\n const isSt9c = lastChar === 0x9c\n\n if (url === \"\") {\n // Close hyperlink — format prefix matches the open sequence\n if (currentHyperlink && rangeStart >= 0) {\n hyperlinkRanges.push({ start: rangeStart, end: cleaned.length, url: currentHyperlink })\n }\n currentHyperlink = undefined\n rangeStart = -1\n } else {\n // Open hyperlink — encode format metadata in URL prefix\n if (currentHyperlink && rangeStart >= 0) {\n // Close previous unclosed hyperlink\n hyperlinkRanges.push({ start: rangeStart, end: cleaned.length, url: currentHyperlink })\n }\n // Encode hyperlink format as a prefix on the URL:\n // \\x01c1b\\x02 = C1 intro + BEL terminator\n // \\x01c1s\\x02 = C1 intro + ST terminator (ESC \\ or C1 ST \\x9c)\n // \\x01e7b\\x02 = ESC intro + BEL terminator\n // no prefix = ESC intro + ST terminator (default)\n let encodedUrl = url\n if (isC1 && (isBel || isSt9c)) {\n encodedUrl = `\\x01c1b\\x02${url}`\n } else if (isC1) {\n encodedUrl = `\\x01c1s\\x02${url}`\n } else if (isBel) {\n encodedUrl = `\\x01e7b\\x02${url}`\n }\n // else: ESC + ST = default, no prefix needed\n currentHyperlink = encodedUrl\n rangeStart = cleaned.length\n }\n\n oscLastIndex = oscMatch.index + oscMatch[0].length\n }\n // Append remaining text after last OSC\n cleaned += sanitizedText.slice(oscLastIndex)\n // Close any still-open hyperlink\n if (currentHyperlink && rangeStart >= 0) {\n hyperlinkRanges.push({ start: rangeStart, end: cleaned.length, url: currentHyperlink })\n }\n\n // If no OSC 8 sequences found, use sanitized text for efficiency\n const processText = hyperlinkRanges.length > 0 ? cleaned : sanitizedText\n\n // Extended pattern: matches SGR with semicolons AND colons (for 4:x, 58:2::r:g:b)\n // Handles both ESC-based CSI (\\x1b[) and C1 CSI (\\x9b)\n const ansiPattern = /(?:\\x1b\\[|\\x9b)([0-9;:]*)m/g\n\n let currentStyle: Omit<StyledSegment, \"text\"> = {}\n let lastIndex = 0\n let match: RegExpExecArray | null\n\n // Helper to find hyperlink URL for a position in the cleaned text.\n // Positions in cleaned text map directly to hyperlinkRanges since OSC 8\n // sequences were stripped but SGR sequences remain at the same indices.\n function getHyperlinkAt(pos: number): string | undefined {\n for (const range of hyperlinkRanges) {\n if (pos >= range.start && pos < range.end) return range.url\n }\n return undefined\n }\n\n while ((match = ansiPattern.exec(processText)) !== null) {\n // Add text before this escape sequence\n if (match.index > lastIndex) {\n const content = processText.slice(lastIndex, match.index)\n if (content.length > 0) {\n if (hyperlinkRanges.length > 0) {\n // Split content into runs by hyperlink URL.\n // lastIndex is the position of content[0] in processText/cleaned.\n let segStart = 0\n for (let ci = 0; ci < content.length; ci++) {\n const hl = getHyperlinkAt(lastIndex + ci)\n const prevHl = ci > 0 ? getHyperlinkAt(lastIndex + ci - 1) : undefined\n if (ci > 0 && hl !== prevHl) {\n const sub = content.slice(segStart, ci)\n if (sub.length > 0) {\n const seg: StyledSegment = { text: sub, ...currentStyle }\n if (prevHl) seg.hyperlink = prevHl\n segments.push(seg)\n }\n segStart = ci\n }\n }\n // Push remaining\n const sub = content.slice(segStart)\n if (sub.length > 0) {\n const hl = getHyperlinkAt(lastIndex + segStart)\n const seg: StyledSegment = { text: sub, ...currentStyle }\n if (hl) seg.hyperlink = hl\n segments.push(seg)\n }\n } else {\n segments.push({ text: content, ...currentStyle })\n }\n }\n }\n\n // Parse SGR codes - split by semicolon first, then handle colon subparams\n const rawParams = match[1]!\n\n // Handle colon-separated sequences (like 4:3 for curly underline, 58:2::r:g:b)\n // Split by semicolon first to get top-level params\n const params = rawParams.split(\";\")\n\n for (let i = 0; i < params.length; i++) {\n const param = params[i]!\n\n // Check if this param has colon subparameters (e.g., \"4:3\", \"58:2::255:0:0\")\n if (param.includes(\":\")) {\n const subparts = param.split(\":\").map((s) => (s === \"\" ? 0 : Number(s)))\n const mainCode = subparts[0]!\n\n if (mainCode === 4) {\n // SGR 4:x - underline style\n const styleCode = subparts[1] ?? 1\n currentStyle.underlineStyle = parseUnderlineStyle(styleCode)\n currentStyle.underline = currentStyle.underlineStyle !== false\n } else if (mainCode === 58) {\n // SGR 58 - underline color\n // Format: 58:5:N (256-color) or 58:2::r:g:b (RGB, note double colon)\n if (subparts[1] === 5 && subparts[2] !== undefined) {\n currentStyle.underlineColor = subparts[2]\n } else if (subparts[1] === 2) {\n // RGB: 58:2::r:g:b (indices 3,4,5 after the empty slot)\n // or 58:2:r:g:b (indices 2,3,4)\n // Handle both formats by looking for valid RGB values\n const r = subparts[3] ?? subparts[2] ?? 0\n const g = subparts[4] ?? subparts[3] ?? 0\n const b = subparts[5] ?? subparts[4] ?? 0\n currentStyle.underlineColor = 0x1000000 | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff)\n }\n } else if (mainCode === 38) {\n // SGR 38:2::r:g:b or 38:5:N format (colon-separated)\n if (subparts[1] === 5 && subparts[2] !== undefined) {\n currentStyle.fg = subparts[2]\n currentStyle.colonFg = true\n } else if (subparts[1] === 2) {\n const r = subparts[3] ?? subparts[2] ?? 0\n const g = subparts[4] ?? subparts[3] ?? 0\n const b = subparts[5] ?? subparts[4] ?? 0\n currentStyle.fg = 0x1000000 | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff)\n currentStyle.colonFg = true\n }\n } else if (mainCode === 48) {\n // SGR 48:2::r:g:b or 48:5:N format (colon-separated)\n if (subparts[1] === 5 && subparts[2] !== undefined) {\n currentStyle.bg = subparts[2]\n currentStyle.colonBg = true\n } else if (subparts[1] === 2) {\n const r = subparts[3] ?? subparts[2] ?? 0\n const g = subparts[4] ?? subparts[3] ?? 0\n const b = subparts[5] ?? subparts[4] ?? 0\n currentStyle.bg = 0x1000000 | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff)\n currentStyle.colonBg = true\n }\n }\n continue\n }\n\n // Standard semicolon-separated params\n const code = Number(param)\n switch (code) {\n case 0:\n // Reset\n currentStyle = {}\n break\n case 1:\n currentStyle.bold = true\n break\n case 2:\n currentStyle.dim = true\n break\n case 3:\n currentStyle.italic = true\n break\n case 4:\n // Plain SGR 4 - simple underline (no subparam)\n currentStyle.underline = true\n currentStyle.underlineStyle = \"single\"\n break\n case 7:\n currentStyle.inverse = true\n break\n case 22:\n currentStyle.bold = false\n currentStyle.dim = false\n break\n case 23:\n currentStyle.italic = false\n break\n case 24:\n // SGR 24 - underline off\n currentStyle.underline = false\n currentStyle.underlineStyle = false\n break\n case 27:\n currentStyle.inverse = false\n break\n case 30:\n case 31:\n case 32:\n case 33:\n case 34:\n case 35:\n case 36:\n case 37:\n currentStyle.fg = code\n break\n case 38: {\n // Extended color: 38;5;N (256 color) or 38;2;r;g;b (true color)\n // Semicolon-separated — clear colonFg flag\n const nextParams = params.slice(i + 1).map(Number)\n currentStyle.colonFg = undefined\n if (nextParams[0] === 5 && nextParams[1] !== undefined) {\n currentStyle.fg = nextParams[1]\n i += 2\n } else if (nextParams[0] === 2 && nextParams[3] !== undefined) {\n // True color - store as RGB values packed\n currentStyle.fg =\n 0x1000000 | ((nextParams[1]! & 0xff) << 16) | ((nextParams[2]! & 0xff) << 8) | (nextParams[3]! & 0xff)\n i += 4\n }\n break\n }\n case 39:\n currentStyle.fg = null // Default foreground\n break\n case 40:\n case 41:\n case 42:\n case 43:\n case 44:\n case 45:\n case 46:\n case 47:\n currentStyle.bg = code\n break\n case 48: {\n // Extended color: 48;5;N (256 color) or 48;2;r;g;b (true color)\n // Semicolon-separated — clear colonBg flag\n const nextParams = params.slice(i + 1).map(Number)\n currentStyle.colonBg = undefined\n if (nextParams[0] === 5 && nextParams[1] !== undefined) {\n currentStyle.bg = nextParams[1]\n i += 2\n } else if (nextParams[0] === 2 && nextParams[3] !== undefined) {\n // True color - store as RGB values packed\n currentStyle.bg =\n 0x1000000 | ((nextParams[1]! & 0xff) << 16) | ((nextParams[2]! & 0xff) << 8) | (nextParams[3]! & 0xff)\n i += 4\n }\n break\n }\n case 49:\n currentStyle.bg = null // Default background\n break\n case 58: {\n // Underline color: 58;5;N (256 color) or 58;2;r;g;b (true color)\n const nextParams = params.slice(i + 1).map(Number)\n if (nextParams[0] === 5 && nextParams[1] !== undefined) {\n currentStyle.underlineColor = nextParams[1]\n i += 2\n } else if (nextParams[0] === 2 && nextParams[3] !== undefined) {\n // True color - store as RGB values packed\n currentStyle.underlineColor =\n 0x1000000 | ((nextParams[1]! & 0xff) << 16) | ((nextParams[2]! & 0xff) << 8) | (nextParams[3]! & 0xff)\n i += 4\n }\n break\n }\n case 59:\n currentStyle.underlineColor = null // Default underline color\n break\n case 90:\n case 91:\n case 92:\n case 93:\n case 94:\n case 95:\n case 96:\n case 97:\n currentStyle.fg = code // Bright foreground colors\n break\n case 100:\n case 101:\n case 102:\n case 103:\n case 104:\n case 105:\n case 106:\n case 107:\n currentStyle.bg = code // Bright background colors\n break\n case BG_OVERRIDE_CODE:\n // Private code: signals intentional bg override, skip conflict detection\n currentStyle.bgOverride = true\n break\n }\n }\n\n lastIndex = match.index + match[0].length\n }\n\n // Add remaining text\n if (lastIndex < processText.length) {\n const content = processText.slice(lastIndex)\n if (content.length > 0) {\n if (hyperlinkRanges.length > 0) {\n // Split remaining content by hyperlink URL\n let segStart = 0\n for (let ci = 0; ci < content.length; ci++) {\n const hl = getHyperlinkAt(lastIndex + ci)\n const prevHl = ci > 0 ? getHyperlinkAt(lastIndex + ci - 1) : undefined\n if (ci > 0 && hl !== prevHl) {\n const sub = content.slice(segStart, ci)\n if (sub.length > 0) {\n const seg: StyledSegment = { text: sub, ...currentStyle }\n if (prevHl) seg.hyperlink = prevHl\n segments.push(seg)\n }\n segStart = ci\n }\n }\n const sub = content.slice(segStart)\n if (sub.length > 0) {\n const hl = getHyperlinkAt(lastIndex + segStart)\n const seg: StyledSegment = { text: sub, ...currentStyle }\n if (hl) seg.hyperlink = hl\n segments.push(seg)\n }\n } else {\n segments.push({ text: content, ...currentStyle })\n }\n }\n }\n\n return segments\n}\n\nconst ANSI_TEST_REGEX = /\\x1b(?:\\[[0-9;:]*[A-Za-z]|\\])|\\x9b[\\x30-\\x3f]*[\\x40-\\x7e]|\\x9d/\n\n/**\n * Check if text contains ANSI escape sequences (SGR or OSC).\n * Detects both ESC-based (7-bit) and C1 (8-bit) forms:\n * - ESC [ params final (CSI)\n * - ESC ] (OSC)\n * - U+009B params final (C1 CSI)\n * - U+009D (C1 OSC)\n */\nexport function hasAnsi(text: string): boolean {\n // Use a non-global regex for testing to avoid lastIndex issues\n return ANSI_TEST_REGEX.test(text)\n}\n\n// ============================================================================\n// Measurement Utilities\n// ============================================================================\n\n/**\n * Measure the dimensions of multi-line text.\n *\n * @param text - Text to measure (may contain newlines)\n * @returns { width, height } in display columns and rows\n */\nexport function measureText(text: string): { width: number; height: number } {\n const lines = text.split(\"\\n\")\n let maxWidth = 0\n\n for (const line of lines) {\n const lineWidth = displayWidth(line)\n if (lineWidth > maxWidth) {\n maxWidth = lineWidth\n }\n }\n\n return {\n width: maxWidth,\n height: lines.length,\n }\n}\n\n/**\n * Check if a string contains any wide characters.\n */\nexport function hasWideCharacters(text: string): boolean {\n const graphemes = splitGraphemes(text)\n return graphemes.some(isWideGrapheme)\n}\n\n/**\n * Check if a string contains any combining/zero-width characters.\n */\nexport function hasZeroWidthCharacters(text: string): boolean {\n const graphemes = splitGraphemes(text)\n return graphemes.some(isZeroWidthGrapheme)\n}\n\n/**\n * Normalize string for consistent handling.\n * Applies Unicode NFC normalization.\n */\nexport function normalizeText(text: string): string {\n return text.normalize(\"NFC\")\n}\n\n// ============================================================================\n// Character Detection\n// ============================================================================\n\n/**\n * Common character ranges for quick checks.\n */\nconst CHAR_RANGES = {\n // Basic Latin (ASCII)\n isBasicLatin: (cp: number) => cp >= 0x0020 && cp <= 0x007f,\n\n // CJK Unified Ideographs\n isCJK: (cp: number) =>\n (cp >= 0x4e00 && cp <= 0x9fff) || // CJK Unified Ideographs\n (cp >= 0x3400 && cp <= 0x4dbf) || // CJK Unified Ideographs Extension A\n (cp >= 0x20000 && cp <= 0x2a6df) || // CJK Unified Ideographs Extension B\n (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs\n (cp >= 0x2f800 && cp <= 0x2fa1f), // CJK Compatibility Ideographs Supplement\n\n // Japanese Hiragana/Katakana\n isJapaneseKana: (cp: number) =>\n (cp >= 0x3040 && cp <= 0x309f) || // Hiragana\n (cp >= 0x30a0 && cp <= 0x30ff), // Katakana\n\n // Korean Hangul\n isHangul: (cp: number) =>\n (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables\n (cp >= 0x1100 && cp <= 0x11ff), // Hangul Jamo\n\n // Emoji ranges (simplified)\n isEmoji: (cp: number) =>\n (cp >= 0x1f600 && cp <= 0x1f64f) || // Emoticons\n (cp >= 0x1f300 && cp <= 0x1f5ff) || // Misc Symbols and Pictographs\n (cp >= 0x1f680 && cp <= 0x1f6ff) || // Transport and Map\n (cp >= 0x1f700 && cp <= 0x1f77f) || // Alchemical Symbols\n (cp >= 0x1f900 && cp <= 0x1f9ff) || // Supplemental Symbols and Pictographs\n (cp >= 0x2600 && cp <= 0x26ff) || // Misc symbols\n (cp >= 0x2700 && cp <= 0x27bf), // Dingbats\n} as const\n\n/**\n * Get the first code point of a string.\n */\nexport function getFirstCodePoint(str: string): number {\n const cp = str.codePointAt(0)\n return cp ?? 0\n}\n\n/**\n * Check if a grapheme is likely an emoji.\n * Note: This is a heuristic, not comprehensive.\n */\nexport function isLikelyEmoji(grapheme: string): boolean {\n const cp = getFirstCodePoint(grapheme)\n return CHAR_RANGES.isEmoji(cp) || grapheme.includes(\"\\u200d\") // Contains ZWJ\n}\n\n/**\n * Check if a grapheme is a CJK character.\n */\nexport function isCJK(grapheme: string): boolean {\n const cp = getFirstCodePoint(grapheme)\n return CHAR_RANGES.isCJK(cp) || CHAR_RANGES.isJapaneseKana(cp) || CHAR_RANGES.isHangul(cp)\n}\n",
21
+ "/**\n * Silvery React Contexts\n *\n * Provides contexts for:\n * - TermContext: Access to Term instance (for styling/detection)\n * - NodeContext: Access to the current SilveryNode (for useContentRect)\n * - RuntimeContext: Unified input/app controls (replaces Events/Input/Stdin/App contexts)\n * - StdoutContext: Access to stdout\n * - StderrContext: Access to stderr\n */\n\nimport type { Term } from \"@silvery/ag-term/ansi\"\nimport { createContext } from \"react\"\nimport type { FocusManager } from \"@silvery/ag/focus-manager\"\nimport type { Key } from \"@silvery/ag/keys\"\nimport type { AgNode } from \"@silvery/ag/types\"\n\n// ============================================================================\n// Term Context\n// ============================================================================\n\n/**\n * Context that provides access to the Term instance.\n * Used by useTerm() hook to access terminal capabilities and styling.\n */\nexport const TermContext = createContext<Term | null>(null)\n\n// ============================================================================\n// Node Context\n// ============================================================================\n\n/**\n * Context that provides access to the current SilveryNode.\n * Used by useContentRect() to subscribe to layout changes.\n *\n * Each Box component wraps its children in a NodeContext.Provider\n * with its corresponding SilveryNode.\n */\nexport const NodeContext = createContext<AgNode | null>(null)\n\n// ============================================================================\n// Stdio Context\n// ============================================================================\n\nexport interface StdoutContextValue {\n /** Standard output stream */\n stdout: NodeJS.WriteStream\n /** Write to stdout */\n write: (data: string) => void\n /**\n * Notify the scheduler that lines were written to stdout externally.\n * Used by useScrollback to report lines written between renders so that\n * inline mode cursor positioning accounts for the displacement.\n */\n notifyScrollback?: (lines: number) => void\n /**\n * Reset inline cursor state in the output phase.\n * Used by useScrollback on resize to clear cursor tracking before\n * re-emitting frozen items at the new width.\n */\n resetInlineCursor?: () => void\n /**\n * Get inline cursor row relative to render region start. -1 if unknown.\n * Used by useScrollback to position frozen items at the render region start.\n */\n getInlineCursorRow?: () => number\n /**\n * Promote frozen content to scrollback via the output phase.\n * Instead of writing directly to stdout (which causes flicker),\n * this passes the content to the output phase which writes frozen content\n * + live content in a single target.write() — no blanking, no cursor desync.\n */\n promoteScrollback?: (frozenContent: string, frozenLineCount: number) => void\n}\n\n/**\n * Context for stdout access.\n * Used by useStdout() hook.\n */\nexport const StdoutContext = createContext<StdoutContextValue | null>(null)\n\nexport interface StderrContextValue {\n /** Standard error stream */\n stderr: NodeJS.WriteStream\n /** Write to stderr */\n write: (data: string) => void\n}\n\n/**\n * Context for stderr access.\n * Used by useStderr() hook.\n */\nexport const StderrContext = createContext<StderrContextValue | null>(null)\n\n// ============================================================================\n// Runtime Context (typed bidirectional event bus — TEA)\n// ============================================================================\n\n/**\n * Base events every runtime provides.\n * Apps extend this to add custom events (e.g., BoardEvents adds \"op\").\n */\nexport interface BaseRuntimeEvents {\n /** Keyboard input: [parsedInput, keyMetadata] */\n input: [input: string, key: Key]\n /** Bracketed paste: [pastedText] */\n paste: [text: string]\n /** Terminal window focus change: [isFocused] */\n focus: [focused: boolean]\n}\n\n/**\n * Extract handler function type from an event map entry.\n */\ntype EventHandler<Args extends unknown[]> = (...args: Args) => void\n\n/**\n * Typed bidirectional event bus + app lifecycle controls.\n *\n * Replaces EventsContext, InputContext, StdinContext, and AppContext with\n * a single typed interface. Components never see stdin or raw mode.\n *\n * Generic parameter E extends BaseRuntimeEvents — all runtimes provide\n * at least \"input\" and \"paste\" events. Apps can extend with custom events:\n *\n * ```tsx\n * interface BoardEvents extends BaseRuntimeEvents {\n * op: [BoardOp]\n * }\n * const rt = useRuntime<BoardEvents>()\n * rt?.on(\"input\", handler) // runtime → view\n * rt?.emit(\"op\", { type: \"cursor_down\" }) // view → runtime\n * ```\n *\n * Present in interactive mode (run/render/createApp/test renderer).\n * Absent (null) in static mode (renderStatic).\n */\nexport interface RuntimeContextValue<E extends BaseRuntimeEvents = BaseRuntimeEvents> {\n /** Subscribe to a typed event. Returns cleanup function. */\n on<K extends string & keyof E>(event: K, handler: EventHandler<E[K] extends unknown[] ? E[K] : never>): () => void\n /** Emit a typed event (view → runtime). */\n emit<K extends string & keyof E>(event: K, ...args: E[K] extends unknown[] ? E[K] : never): void\n /** Exit the application with optional error. */\n exit: (error?: Error) => void\n /** Pause rendering output (for screen switching). */\n pause?: () => void\n /** Resume rendering after pause. */\n resume?: () => void\n}\n\n/**\n * Context that provides the typed runtime event bus.\n *\n * When non-null: interactive mode — useInput works, components can subscribe\n * to events via rt.on() and emit via rt.emit().\n *\n * When null: static mode — useInput throws (by design), use useRuntime()\n * for components that need to work in both modes.\n */\nexport const RuntimeContext = createContext<RuntimeContextValue | null>(null)\n\n// ============================================================================\n// Focus Manager Context (tree-based focus system)\n// ============================================================================\n\n/**\n * Context for the tree-based focus manager.\n * Provides the FocusManager instance to useFocusable(), useFocusWithin(), and useFocusManager() hooks.\n */\nexport const FocusManagerContext = createContext<FocusManager | null>(null)\n",
22
+ "var YGEnums={},ALIGN_AUTO=YGEnums.ALIGN_AUTO=0,ALIGN_FLEX_START=YGEnums.ALIGN_FLEX_START=1,ALIGN_CENTER=YGEnums.ALIGN_CENTER=2,ALIGN_FLEX_END=YGEnums.ALIGN_FLEX_END=3,ALIGN_STRETCH=YGEnums.ALIGN_STRETCH=4,ALIGN_BASELINE=YGEnums.ALIGN_BASELINE=5,ALIGN_SPACE_BETWEEN=YGEnums.ALIGN_SPACE_BETWEEN=6,ALIGN_SPACE_AROUND=YGEnums.ALIGN_SPACE_AROUND=7,DIMENSION_WIDTH=YGEnums.DIMENSION_WIDTH=0,DIMENSION_HEIGHT=YGEnums.DIMENSION_HEIGHT=1,DIRECTION_INHERIT=YGEnums.DIRECTION_INHERIT=0,DIRECTION_LTR=YGEnums.DIRECTION_LTR=1,DIRECTION_RTL=YGEnums.DIRECTION_RTL=2,DISPLAY_FLEX=YGEnums.DISPLAY_FLEX=0,DISPLAY_NONE=YGEnums.DISPLAY_NONE=1,EDGE_LEFT=YGEnums.EDGE_LEFT=0,EDGE_TOP=YGEnums.EDGE_TOP=1,EDGE_RIGHT=YGEnums.EDGE_RIGHT=2,EDGE_BOTTOM=YGEnums.EDGE_BOTTOM=3,EDGE_START=YGEnums.EDGE_START=4,EDGE_END=YGEnums.EDGE_END=5,EDGE_HORIZONTAL=YGEnums.EDGE_HORIZONTAL=6,EDGE_VERTICAL=YGEnums.EDGE_VERTICAL=7,EDGE_ALL=YGEnums.EDGE_ALL=8,EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS=YGEnums.EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS=0,EXPERIMENTAL_FEATURE_ABSOLUTE_PERCENTAGE_AGAINST_PADDING_EDGE=YGEnums.EXPERIMENTAL_FEATURE_ABSOLUTE_PERCENTAGE_AGAINST_PADDING_EDGE=1,EXPERIMENTAL_FEATURE_FIX_ABSOLUTE_TRAILING_COLUMN_MARGIN=YGEnums.EXPERIMENTAL_FEATURE_FIX_ABSOLUTE_TRAILING_COLUMN_MARGIN=2,FLEX_DIRECTION_COLUMN=YGEnums.FLEX_DIRECTION_COLUMN=0,FLEX_DIRECTION_COLUMN_REVERSE=YGEnums.FLEX_DIRECTION_COLUMN_REVERSE=1,FLEX_DIRECTION_ROW=YGEnums.FLEX_DIRECTION_ROW=2,FLEX_DIRECTION_ROW_REVERSE=YGEnums.FLEX_DIRECTION_ROW_REVERSE=3,GUTTER_COLUMN=YGEnums.GUTTER_COLUMN=0,GUTTER_ROW=YGEnums.GUTTER_ROW=1,GUTTER_ALL=YGEnums.GUTTER_ALL=2,JUSTIFY_FLEX_START=YGEnums.JUSTIFY_FLEX_START=0,JUSTIFY_CENTER=YGEnums.JUSTIFY_CENTER=1,JUSTIFY_FLEX_END=YGEnums.JUSTIFY_FLEX_END=2,JUSTIFY_SPACE_BETWEEN=YGEnums.JUSTIFY_SPACE_BETWEEN=3,JUSTIFY_SPACE_AROUND=YGEnums.JUSTIFY_SPACE_AROUND=4,JUSTIFY_SPACE_EVENLY=YGEnums.JUSTIFY_SPACE_EVENLY=5,LOG_LEVEL_ERROR=YGEnums.LOG_LEVEL_ERROR=0,LOG_LEVEL_WARN=YGEnums.LOG_LEVEL_WARN=1,LOG_LEVEL_INFO=YGEnums.LOG_LEVEL_INFO=2,LOG_LEVEL_DEBUG=YGEnums.LOG_LEVEL_DEBUG=3,LOG_LEVEL_VERBOSE=YGEnums.LOG_LEVEL_VERBOSE=4,LOG_LEVEL_FATAL=YGEnums.LOG_LEVEL_FATAL=5,MEASURE_MODE_UNDEFINED=YGEnums.MEASURE_MODE_UNDEFINED=0,MEASURE_MODE_EXACTLY=YGEnums.MEASURE_MODE_EXACTLY=1,MEASURE_MODE_AT_MOST=YGEnums.MEASURE_MODE_AT_MOST=2,NODE_TYPE_DEFAULT=YGEnums.NODE_TYPE_DEFAULT=0,NODE_TYPE_TEXT=YGEnums.NODE_TYPE_TEXT=1,OVERFLOW_VISIBLE=YGEnums.OVERFLOW_VISIBLE=0,OVERFLOW_HIDDEN=YGEnums.OVERFLOW_HIDDEN=1,OVERFLOW_SCROLL=YGEnums.OVERFLOW_SCROLL=2,POSITION_TYPE_STATIC=YGEnums.POSITION_TYPE_STATIC=0,POSITION_TYPE_RELATIVE=YGEnums.POSITION_TYPE_RELATIVE=1,POSITION_TYPE_ABSOLUTE=YGEnums.POSITION_TYPE_ABSOLUTE=2,PRINT_OPTIONS_LAYOUT=YGEnums.PRINT_OPTIONS_LAYOUT=1,PRINT_OPTIONS_STYLE=YGEnums.PRINT_OPTIONS_STYLE=2,PRINT_OPTIONS_CHILDREN=YGEnums.PRINT_OPTIONS_CHILDREN=4,UNIT_UNDEFINED=YGEnums.UNIT_UNDEFINED=0,UNIT_POINT=YGEnums.UNIT_POINT=1,UNIT_PERCENT=YGEnums.UNIT_PERCENT=2,UNIT_AUTO=YGEnums.UNIT_AUTO=3,WRAP_NO_WRAP=YGEnums.WRAP_NO_WRAP=0,WRAP_WRAP=YGEnums.WRAP_WRAP=1,WRAP_WRAP_REVERSE=YGEnums.WRAP_WRAP_REVERSE=2;let CONSTANTS=YGEnums;var wrapAsm=E=>{function _(E,_,T){let N=E[_];E[_]=function(...E){return T.call(this,N,...E)}}for(let T of[\"setPosition\",\"setMargin\",\"setFlexBasis\",\"setWidth\",\"setHeight\",\"setMinWidth\",\"setMinHeight\",\"setMaxWidth\",\"setMaxHeight\",\"setPadding\"]){let N={[YGEnums.UNIT_POINT]:E.Node.prototype[T],[YGEnums.UNIT_PERCENT]:E.Node.prototype[`${T}Percent`],[YGEnums.UNIT_AUTO]:E.Node.prototype[`${T}Auto`]};_(E.Node.prototype,T,function(E,..._){let I,L;let O=_.pop();if(\"auto\"===O)I=YGEnums.UNIT_AUTO,L=void 0;else if(\"object\"==typeof O)I=O.unit,L=O.valueOf();else if(I=\"string\"==typeof O&&O.endsWith(\"%\")?YGEnums.UNIT_PERCENT:YGEnums.UNIT_POINT,L=parseFloat(O),!Number.isNaN(O)&&Number.isNaN(L))throw Error(`Invalid value ${O} for ${T}`);if(!N[I])throw Error(`Failed to execute \"${T}\": Unsupported unit '${O}'`);return void 0!==L?N[I].call(this,..._,L):N[I].call(this,..._)})}function T(_){return E.MeasureCallback.implement({measure:(...E)=>{let{width:T,height:N}=_(...E);return{width:T??NaN,height:N??NaN}}})}function N(_){return E.DirtiedCallback.implement({dirtied:_})}return _(E.Node.prototype,\"setMeasureFunc\",function(E,_){return _?E.call(this,T(_)):this.unsetMeasureFunc()}),_(E.Node.prototype,\"setDirtiedFunc\",function(E,_){E.call(this,N(_))}),_(E.Config.prototype,\"free\",function(){E.Config.destroy(this)}),_(E.Node,\"create\",(_,T)=>T?E.Node.createWithConfig(T):E.Node.createDefault()),_(E.Node.prototype,\"free\",function(){E.Node.destroy(this)}),_(E.Node.prototype,\"freeRecursive\",function(){for(let E=0,_=this.getChildCount();E<_;++E)this.getChild(0).freeRecursive();this.free()}),_(E.Node.prototype,\"calculateLayout\",function(E,_=NaN,T=NaN,N=YGEnums.DIRECTION_LTR){return E.call(this,_,T,N)}),{Config:E.Config,Node:E.Node,...YGEnums}};export{OVERFLOW_HIDDEN as $,ALIGN_AUTO as A,FLEX_DIRECTION_ROW as B,FLEX_DIRECTION_ROW_REVERSE as C,DIMENSION_WIDTH as D,EDGE_LEFT as E,FLEX_DIRECTION_COLUMN as F,GUTTER_COLUMN as G,GUTTER_ROW as H,GUTTER_ALL as I,JUSTIFY_FLEX_START as J,JUSTIFY_CENTER as K,JUSTIFY_FLEX_END as L,JUSTIFY_SPACE_BETWEEN as M,JUSTIFY_SPACE_AROUND as N,JUSTIFY_SPACE_EVENLY as O,LOG_LEVEL_ERROR as P,LOG_LEVEL_WARN as Q,LOG_LEVEL_INFO as R,LOG_LEVEL_DEBUG as S,LOG_LEVEL_VERBOSE as T,LOG_LEVEL_FATAL as U,MEASURE_MODE_UNDEFINED as V,MEASURE_MODE_EXACTLY as W,MEASURE_MODE_AT_MOST as X,NODE_TYPE_DEFAULT as Y,NODE_TYPE_TEXT as Z,OVERFLOW_VISIBLE as _,ALIGN_FLEX_START as a,OVERFLOW_SCROLL as a0,POSITION_TYPE_STATIC as a1,POSITION_TYPE_RELATIVE as a2,POSITION_TYPE_ABSOLUTE as a3,PRINT_OPTIONS_LAYOUT as a4,PRINT_OPTIONS_STYLE as a5,PRINT_OPTIONS_CHILDREN as a6,UNIT_UNDEFINED as a7,UNIT_POINT as a8,UNIT_PERCENT as a9,UNIT_AUTO as aa,WRAP_NO_WRAP as ab,WRAP_WRAP as ac,WRAP_WRAP_REVERSE as ad,ALIGN_CENTER as b,ALIGN_FLEX_END as c,ALIGN_STRETCH as d,ALIGN_BASELINE as e,ALIGN_SPACE_BETWEEN as f,ALIGN_SPACE_AROUND as g,DIMENSION_HEIGHT as h,DIRECTION_INHERIT as i,DIRECTION_LTR as j,DIRECTION_RTL as k,DISPLAY_FLEX as l,DISPLAY_NONE as m,EDGE_TOP as n,EDGE_RIGHT as o,EDGE_BOTTOM as p,EDGE_START as q,EDGE_END as r,EDGE_HORIZONTAL as s,EDGE_VERTICAL as t,EDGE_ALL as u,EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS as v,wrapAsm as w,EXPERIMENTAL_FEATURE_ABSOLUTE_PERCENTAGE_AGAINST_PADDING_EDGE as x,EXPERIMENTAL_FEATURE_FIX_ABSOLUTE_TRAILING_COLUMN_MARGIN as y,FLEX_DIRECTION_COLUMN_REVERSE as z};\n",
23
+ "import{w as n}from\"./wrapAsm-f766f97f.js\";export{A as ALIGN_AUTO,e as ALIGN_BASELINE,b as ALIGN_CENTER,c as ALIGN_FLEX_END,a as ALIGN_FLEX_START,g as ALIGN_SPACE_AROUND,f as ALIGN_SPACE_BETWEEN,d as ALIGN_STRETCH,h as DIMENSION_HEIGHT,D as DIMENSION_WIDTH,i as DIRECTION_INHERIT,j as DIRECTION_LTR,k as DIRECTION_RTL,l as DISPLAY_FLEX,m as DISPLAY_NONE,u as EDGE_ALL,p as EDGE_BOTTOM,r as EDGE_END,s as EDGE_HORIZONTAL,E as EDGE_LEFT,o as EDGE_RIGHT,q as EDGE_START,n as EDGE_TOP,t as EDGE_VERTICAL,x as EXPERIMENTAL_FEATURE_ABSOLUTE_PERCENTAGE_AGAINST_PADDING_EDGE,y as EXPERIMENTAL_FEATURE_FIX_ABSOLUTE_TRAILING_COLUMN_MARGIN,v as EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS,F as FLEX_DIRECTION_COLUMN,z as FLEX_DIRECTION_COLUMN_REVERSE,B as FLEX_DIRECTION_ROW,C as FLEX_DIRECTION_ROW_REVERSE,I as GUTTER_ALL,G as GUTTER_COLUMN,H as GUTTER_ROW,K as JUSTIFY_CENTER,L as JUSTIFY_FLEX_END,J as JUSTIFY_FLEX_START,N as JUSTIFY_SPACE_AROUND,M as JUSTIFY_SPACE_BETWEEN,O as JUSTIFY_SPACE_EVENLY,S as LOG_LEVEL_DEBUG,P as LOG_LEVEL_ERROR,U as LOG_LEVEL_FATAL,R as LOG_LEVEL_INFO,T as LOG_LEVEL_VERBOSE,Q as LOG_LEVEL_WARN,X as MEASURE_MODE_AT_MOST,W as MEASURE_MODE_EXACTLY,V as MEASURE_MODE_UNDEFINED,Y as NODE_TYPE_DEFAULT,Z as NODE_TYPE_TEXT,$ as OVERFLOW_HIDDEN,a0 as OVERFLOW_SCROLL,_ as OVERFLOW_VISIBLE,a3 as POSITION_TYPE_ABSOLUTE,a2 as POSITION_TYPE_RELATIVE,a1 as POSITION_TYPE_STATIC,a6 as PRINT_OPTIONS_CHILDREN,a4 as PRINT_OPTIONS_LAYOUT,a5 as PRINT_OPTIONS_STYLE,aa as UNIT_AUTO,a9 as UNIT_PERCENT,a8 as UNIT_POINT,a7 as UNIT_UNDEFINED,ab as WRAP_NO_WRAP,ac as WRAP_WRAP,ad as WRAP_WRAP_REVERSE}from\"./wrapAsm-f766f97f.js\";var yoga=(()=>{var n=\"undefined\"!=typeof document&&document.currentScript?document.currentScript.src:void 0;return function(t={}){u||(u=void 0!==t?t:{}),u.ready=new Promise(function(n,t){c=n,f=t});var r,e,a=Object.assign({},u),i=\"\";\"undefined\"!=typeof document&&document.currentScript&&(i=document.currentScript.src),n&&(i=n),i=0!==i.indexOf(\"blob:\")?i.substr(0,i.replace(/[?#].*/,\"\").lastIndexOf(\"/\")+1):\"\";var o=console.log.bind(console),s=console.warn.bind(console);Object.assign(u,a),a=null,\"object\"!=typeof WebAssembly&&w(\"no native wasm support detected\");var u,c,f,l,h=!1;function p(n,t,r){r=t+r;for(var e=\"\";!(t>=r);){var a=n[t++];if(!a)break;if(128&a){var i=63&n[t++];if(192==(224&a))e+=String.fromCharCode((31&a)<<6|i);else{var o=63&n[t++];65536>(a=224==(240&a)?(15&a)<<12|i<<6|o:(7&a)<<18|i<<12|o<<6|63&n[t++])?e+=String.fromCharCode(a):(a-=65536,e+=String.fromCharCode(55296|a>>10,56320|1023&a))}}else e+=String.fromCharCode(a)}return e}function v(){var n=l.buffer;u.HEAP8=d=new Int8Array(n),u.HEAP16=m=new Int16Array(n),u.HEAP32=g=new Int32Array(n),u.HEAPU8=y=new Uint8Array(n),u.HEAPU16=E=new Uint16Array(n),u.HEAPU32=_=new Uint32Array(n),u.HEAPF32=T=new Float32Array(n),u.HEAPF64=L=new Float64Array(n)}var d,y,m,E,g,_,T,L,A,O=[],P=[],b=[],N=0,I=null;function w(n){throw s(n=\"Aborted(\"+n+\")\"),h=!0,f(n=new WebAssembly.RuntimeError(n+\". Build with -sASSERTIONS for more info.\")),n}function S(){return r.startsWith(\"data:application/octet-stream;base64,\")}function R(){try{throw\"both async and sync fetching of the wasm failed\"}catch(n){w(n)}}function C(n){for(;0<n.length;)n.shift()(u)}function W(n){if(void 0===n)return\"_unknown\";var t=(n=n.replace(/[^a-zA-Z0-9_]/g,\"$\")).charCodeAt(0);return 48<=t&&57>=t?\"_\"+n:n}function U(n,t){return n=W(n),function(){return t.apply(this,arguments)}}r=\"yoga.wasm\",S()||(r=i+r);var M=[{},{value:void 0},{value:null},{value:!0},{value:!1}],F=[];function D(n){var t=Error,r=U(n,function(t){this.name=n,this.message=t,void 0!==(t=Error(t).stack)&&(this.stack=this.toString()+\"\\n\"+t.replace(/^Error(:[^\\n]*)?\\n/,\"\"))});return r.prototype=Object.create(t.prototype),r.prototype.constructor=r,r.prototype.toString=function(){return void 0===this.message?this.name:this.name+\": \"+this.message},r}var k=void 0;function V(n){throw new k(n)}var j=n=>(n||V(\"Cannot use deleted val. handle = \"+n),M[n].value),G=n=>{switch(n){case void 0:return 1;case null:return 2;case!0:return 3;case!1:return 4;default:var t=F.length?F.pop():M.length;return M[t]={fa:1,value:n},t}},Y=void 0,X=void 0;function B(n){for(var t=\"\";y[n];)t+=X[y[n++]];return t}var H=[];function x(){for(;H.length;){var n=H.pop();n.L.Z=!1,n.delete()}}var z=void 0,$={};function Z(n,t){for(void 0===t&&V(\"ptr should not be undefined\");n.P;)t=n.aa(t),n=n.P;return t}var J={};function q(n){var t=B(n=nz(n));return nZ(n),t}function K(n,t){var r=J[n];return void 0===r&&V(t+\" has unknown type \"+q(n)),r}function Q(){}var nn=!1;function nt(n){--n.count.value,0===n.count.value&&(n.S?n.T.V(n.S):n.O.M.V(n.N))}var nr={},ne=void 0;function na(n){throw new ne(n)}function ni(n,t){return t.O&&t.N||na(\"makeClassHandle requires ptr and ptrType\"),!!t.T!=!!t.S&&na(\"Both smartPtrType and smartPtr must be specified\"),t.count={value:1},no(Object.create(n,{L:{value:t}}))}function no(n){return\"undefined\"==typeof FinalizationRegistry?(no=n=>n,n):(nn=new FinalizationRegistry(n=>{nt(n.L)}),no=n=>{var t=n.L;return t.S&&nn.register(n,{L:t},n),n},Q=n=>{nn.unregister(n)},no(n))}var ns={};function nu(n){for(;n.length;){var t=n.pop();n.pop()(t)}}function nc(n){return this.fromWireType(g[n>>2])}var nf={},nl={};function nh(n,t,r){function e(t){(t=r(t)).length!==n.length&&na(\"Mismatched type converter count\");for(var e=0;e<n.length;++e)nv(n[e],t[e])}n.forEach(function(n){nl[n]=t});var a=Array(t.length),i=[],o=0;t.forEach((n,t)=>{J.hasOwnProperty(n)?a[t]=J[n]:(i.push(n),nf.hasOwnProperty(n)||(nf[n]=[]),nf[n].push(()=>{a[t]=J[n],++o===i.length&&e(a)}))}),0===i.length&&e(a)}function np(n){switch(n){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw TypeError(\"Unknown type size: \"+n)}}function nv(n,t,r={}){if(!(\"argPackAdvance\"in t))throw TypeError(\"registerType registeredInstance requires argPackAdvance\");var e=t.name;if(n||V('type \"'+e+'\" must have a positive integer typeid pointer'),J.hasOwnProperty(n)){if(r.ta)return;V(\"Cannot register type '\"+e+\"' twice\")}J[n]=t,delete nl[n],nf.hasOwnProperty(n)&&(t=nf[n],delete nf[n],t.forEach(n=>n()))}function nd(n){V(n.L.O.M.name+\" instance already deleted\")}function ny(){}function nm(n,t,r){if(void 0===n[t].R){var e=n[t];n[t]=function(){return n[t].R.hasOwnProperty(arguments.length)||V(\"Function '\"+r+\"' called with an invalid number of arguments (\"+arguments.length+\") - expects one of (\"+n[t].R+\")!\"),n[t].R[arguments.length].apply(this,arguments)},n[t].R=[],n[t].R[e.Y]=e}}function nE(n,t,r,e,a,i,o,s){this.name=n,this.constructor=t,this.W=r,this.V=e,this.P=a,this.oa=i,this.aa=o,this.ma=s,this.ia=[]}function ng(n,t,r){for(;t!==r;)t.aa||V(\"Expected null or instance of \"+r.name+\", got an instance of \"+t.name),n=t.aa(n),t=t.P;return n}function n_(n,t){return null===t?(this.da&&V(\"null is not a valid \"+this.name),0):(t.L||V('Cannot pass \"'+nC(t)+'\" as a '+this.name),t.L.N||V(\"Cannot pass deleted object as a pointer of type \"+this.name),ng(t.L.N,t.L.O.M,this.M))}function nT(n,t){if(null===t){if(this.da&&V(\"null is not a valid \"+this.name),this.ca){var r=this.ea();return null!==n&&n.push(this.V,r),r}return 0}if(t.L||V('Cannot pass \"'+nC(t)+'\" as a '+this.name),t.L.N||V(\"Cannot pass deleted object as a pointer of type \"+this.name),!this.ba&&t.L.O.ba&&V(\"Cannot convert argument of type \"+(t.L.T?t.L.T.name:t.L.O.name)+\" to parameter type \"+this.name),r=ng(t.L.N,t.L.O.M,this.M),this.ca)switch(void 0===t.L.S&&V(\"Passing raw pointer to smart pointer is illegal\"),this.Aa){case 0:t.L.T===this?r=t.L.S:V(\"Cannot convert argument of type \"+(t.L.T?t.L.T.name:t.L.O.name)+\" to parameter type \"+this.name);break;case 1:r=t.L.S;break;case 2:if(t.L.T===this)r=t.L.S;else{var e=t.clone();r=this.wa(r,G(function(){e.delete()})),null!==n&&n.push(this.V,r)}break;default:V(\"Unsupporting sharing policy\")}return r}function nL(n,t){return null===t?(this.da&&V(\"null is not a valid \"+this.name),0):(t.L||V('Cannot pass \"'+nC(t)+'\" as a '+this.name),t.L.N||V(\"Cannot pass deleted object as a pointer of type \"+this.name),t.L.O.ba&&V(\"Cannot convert argument of type \"+t.L.O.name+\" to parameter type \"+this.name),ng(t.L.N,t.L.O.M,this.M))}function nA(n,t,r,e){this.name=n,this.M=t,this.da=r,this.ba=e,this.ca=!1,this.V=this.wa=this.ea=this.ja=this.Aa=this.va=void 0,void 0!==t.P?this.toWireType=nT:(this.toWireType=e?n_:nL,this.U=null)}var nO=[];function nP(n){var t=nO[n];return t||(n>=nO.length&&(nO.length=n+1),nO[n]=t=A.get(n)),t}function nb(n,t){var r,e,a=(n=B(n)).includes(\"j\")?(r=n,e=[],function(){if(e.length=0,Object.assign(e,arguments),r.includes(\"j\")){var n=u[\"dynCall_\"+r];n=e&&e.length?n.apply(null,[t].concat(e)):n.call(null,t)}else n=nP(t).apply(null,e);return n}):nP(t);return\"function\"!=typeof a&&V(\"unknown function pointer with signature \"+n+\": \"+t),a}var nN=void 0;function nI(n,t){var r=[],e={};throw t.forEach(function n(t){e[t]||J[t]||(nl[t]?nl[t].forEach(n):(r.push(t),e[t]=!0))}),new nN(n+\": \"+r.map(q).join([\", \"]))}function nw(n,t,r,e,a){var i=t.length;2>i&&V(\"argTypes array size mismatch! Must at least get return value and 'this' types!\");var o=null!==t[1]&&null!==r,s=!1;for(r=1;r<t.length;++r)if(null!==t[r]&&void 0===t[r].U){s=!0;break}var u=\"void\"!==t[0].name,c=i-2,f=Array(c),l=[],h=[];return function(){if(arguments.length!==c&&V(\"function \"+n+\" called with \"+arguments.length+\" arguments, expected \"+c+\" args!\"),h.length=0,l.length=o?2:1,l[0]=a,o){var r=t[1].toWireType(h,this);l[1]=r}for(var i=0;i<c;++i)f[i]=t[i+2].toWireType(h,arguments[i]),l.push(f[i]);if(i=e.apply(null,l),s)nu(h);else for(var p=o?1:2;p<t.length;p++){var v=1===p?r:f[p-2];null!==t[p].U&&t[p].U(v)}return u?t[0].fromWireType(i):void 0}}function nS(n,t){for(var r=[],e=0;e<n;e++)r.push(_[t+4*e>>2]);return r}function nR(n){4<n&&0==--M[n].fa&&(M[n]=void 0,F.push(n))}function nC(n){if(null===n)return\"null\";var t=typeof n;return\"object\"===t||\"array\"===t||\"function\"===t?n.toString():\"\"+n}function nW(n,t){for(var r=\"\",e=0;!(e>=t/2);++e){var a=m[n+2*e>>1];if(0==a)break;r+=String.fromCharCode(a)}return r}function nU(n,t,r){if(void 0===r&&(r=2147483647),2>r)return 0;r-=2;var e=t;r=r<2*n.length?r/2:n.length;for(var a=0;a<r;++a)m[t>>1]=n.charCodeAt(a),t+=2;return m[t>>1]=0,t-e}function nM(n){return 2*n.length}function nF(n,t){for(var r=0,e=\"\";!(r>=t/4);){var a=g[n+4*r>>2];if(0==a)break;++r,65536<=a?(a-=65536,e+=String.fromCharCode(55296|a>>10,56320|1023&a)):e+=String.fromCharCode(a)}return e}function nD(n,t,r){if(void 0===r&&(r=2147483647),4>r)return 0;var e=t;r=e+r-4;for(var a=0;a<n.length;++a){var i=n.charCodeAt(a);if(55296<=i&&57343>=i&&(i=65536+((1023&i)<<10)|1023&n.charCodeAt(++a)),g[t>>2]=i,(t+=4)+4>r)break}return g[t>>2]=0,t-e}function nk(n){for(var t=0,r=0;r<n.length;++r){var e=n.charCodeAt(r);55296<=e&&57343>=e&&++r,t+=4}return t}var nV={};function nj(n){var t=nV[n];return void 0===t?B(n):t}var nG=[],nY=[],nX=[null,[],[]];k=u.BindingError=D(\"BindingError\"),u.count_emval_handles=function(){for(var n=0,t=5;t<M.length;++t)void 0!==M[t]&&++n;return n},u.get_first_emval=function(){for(var n=5;n<M.length;++n)if(void 0!==M[n])return M[n];return null},Y=u.PureVirtualError=D(\"PureVirtualError\");for(var nB=Array(256),nH=0;256>nH;++nH)nB[nH]=String.fromCharCode(nH);X=nB,u.getInheritedInstanceCount=function(){return Object.keys($).length},u.getLiveInheritedInstances=function(){var n,t=[];for(n in $)$.hasOwnProperty(n)&&t.push($[n]);return t},u.flushPendingDeletes=x,u.setDelayFunction=function(n){z=n,H.length&&z&&z(x)},ne=u.InternalError=D(\"InternalError\"),ny.prototype.isAliasOf=function(n){if(!(this instanceof ny&&n instanceof ny))return!1;var t=this.L.O.M,r=this.L.N,e=n.L.O.M;for(n=n.L.N;t.P;)r=t.aa(r),t=t.P;for(;e.P;)n=e.aa(n),e=e.P;return t===e&&r===n},ny.prototype.clone=function(){if(this.L.N||nd(this),this.L.$)return this.L.count.value+=1,this;var n=no,t=Object,r=t.create,e=Object.getPrototypeOf(this),a=this.L;return n=n(r.call(t,e,{L:{value:{count:a.count,Z:a.Z,$:a.$,N:a.N,O:a.O,S:a.S,T:a.T}}})),n.L.count.value+=1,n.L.Z=!1,n},ny.prototype.delete=function(){this.L.N||nd(this),this.L.Z&&!this.L.$&&V(\"Object already scheduled for deletion\"),Q(this),nt(this.L),this.L.$||(this.L.S=void 0,this.L.N=void 0)},ny.prototype.isDeleted=function(){return!this.L.N},ny.prototype.deleteLater=function(){return this.L.N||nd(this),this.L.Z&&!this.L.$&&V(\"Object already scheduled for deletion\"),H.push(this),1===H.length&&z&&z(x),this.L.Z=!0,this},nA.prototype.pa=function(n){return this.ja&&(n=this.ja(n)),n},nA.prototype.ga=function(n){this.V&&this.V(n)},nA.prototype.argPackAdvance=8,nA.prototype.readValueFromPointer=nc,nA.prototype.deleteObject=function(n){null!==n&&n.delete()},nA.prototype.fromWireType=function(n){function t(){return this.ca?ni(this.M.W,{O:this.va,N:e,T:this,S:n}):ni(this.M.W,{O:this,N:n})}var r,e=this.pa(n);if(!e)return this.ga(n),null;var a=$[Z(this.M,e)];if(void 0!==a)return 0===a.L.count.value?(a.L.N=e,a.L.S=n,a.clone()):(a=a.clone(),this.ga(n),a);if(!(a=nr[a=this.M.oa(e)]))return t.call(this);a=this.ba?a.ka:a.pointerType;var i=function n(t,r,e){return r===e?t:void 0===e.P?null:null===(t=n(t,r,e.P))?null:e.ma(t)}(e,this.M,a.M);return null===i?t.call(this):this.ca?ni(a.M.W,{O:a,N:i,T:this,S:n}):ni(a.M.W,{O:a,N:i})},nN=u.UnboundTypeError=D(\"UnboundTypeError\");var nx={q:function(n,t,r){n=B(n),t=K(t,\"wrapper\"),r=j(r);var e=[].slice,a=t.M,i=a.W,o=a.P.W,s=a.P.constructor;for(var u in n=U(n,function(){a.P.ia.forEach((function(n){if(this[n]===o[n])throw new Y(\"Pure virtual function \"+n+\" must be implemented in JavaScript\")}).bind(this)),Object.defineProperty(this,\"__parent\",{value:i}),this.__construct.apply(this,e.call(arguments))}),i.__construct=function(){this===i&&V(\"Pass correct 'this' to __construct\");var n=s.implement.apply(void 0,[this].concat(e.call(arguments)));Q(n);var t=n.L;n.notifyOnDestruction(),t.$=!0,Object.defineProperties(this,{L:{value:t}}),no(this),n=Z(a,n=t.N),$.hasOwnProperty(n)?V(\"Tried to register registered instance: \"+n):$[n]=this},i.__destruct=function(){this===i&&V(\"Pass correct 'this' to __destruct\"),Q(this);var n=this.L.N;n=Z(a,n),$.hasOwnProperty(n)?delete $[n]:V(\"Tried to unregister unregistered instance: \"+n)},n.prototype=Object.create(i),r)n.prototype[u]=r[u];return G(n)},l:function(n){var t=ns[n];delete ns[n];var r=t.ea,e=t.V,a=t.ha;nh([n],a.map(n=>n.sa).concat(a.map(n=>n.ya)),n=>{var i={};return a.forEach((t,r)=>{var e=n[r],o=t.qa,s=t.ra,u=n[r+a.length],c=t.xa,f=t.za;i[t.na]={read:n=>e.fromWireType(o(s,n)),write:(n,t)=>{var r=[];c(f,n,u.toWireType(r,t)),nu(r)}}}),[{name:t.name,fromWireType:function(n){var t,r={};for(t in i)r[t]=i[t].read(n);return e(n),r},toWireType:function(n,t){for(var a in i)if(!(a in t))throw TypeError('Missing field: \"'+a+'\"');var o=r();for(a in i)i[a].write(o,t[a]);return null!==n&&n.push(e,o),o},argPackAdvance:8,readValueFromPointer:nc,U:e}]})},v:function(){},B:function(n,t,r,e,a){var i=np(r);nv(n,{name:t=B(t),fromWireType:function(n){return!!n},toWireType:function(n,t){return t?e:a},argPackAdvance:8,readValueFromPointer:function(n){if(1===r)var e=d;else if(2===r)e=m;else if(4===r)e=g;else throw TypeError(\"Unknown boolean type size: \"+t);return this.fromWireType(e[n>>i])},U:null})},h:function(n,t,r,e,a,i,o,s,c,f,l,h,p){l=B(l),i=nb(a,i),s&&(s=nb(o,s)),f&&(f=nb(c,f)),p=nb(h,p);var v,d=W(l);v=function(){nI(\"Cannot construct \"+l+\" due to unbound types\",[e])},u.hasOwnProperty(d)?(V(\"Cannot register public name '\"+d+\"' twice\"),nm(u,d,d),u.hasOwnProperty(void 0)&&V(\"Cannot register multiple overloads of a function with the same number of arguments (undefined)!\"),u[d].R[void 0]=v):u[d]=v,nh([n,t,r],e?[e]:[],function(t){if(t=t[0],e)var r,a=t.M,o=a.W;else o=ny.prototype;t=U(d,function(){if(Object.getPrototypeOf(this)!==c)throw new k(\"Use 'new' to construct \"+l);if(void 0===h.X)throw new k(l+\" has no accessible constructor\");var n=h.X[arguments.length];if(void 0===n)throw new k(\"Tried to invoke ctor of \"+l+\" with invalid number of parameters (\"+arguments.length+\") - expected (\"+Object.keys(h.X).toString()+\") parameters instead!\");return n.apply(this,arguments)});var c=Object.create(o,{constructor:{value:t}});t.prototype=c;var h=new nE(l,t,c,p,a,i,s,f);a=new nA(l,h,!0,!1),o=new nA(l+\"*\",h,!1,!1);var v=new nA(l+\" const*\",h,!1,!0);return nr[n]={pointerType:o,ka:v},r=t,u.hasOwnProperty(d)||na(\"Replacing nonexistant public symbol\"),u[d]=r,u[d].Y=void 0,[a,o,v]})},d:function(n,t,r,e,a,i,o){var s=nS(r,e);t=B(t),i=nb(a,i),nh([],[n],function(n){function e(){nI(\"Cannot call \"+a+\" due to unbound types\",s)}var a=(n=n[0]).name+\".\"+t;t.startsWith(\"@@\")&&(t=Symbol[t.substring(2)]);var u=n.M.constructor;return void 0===u[t]?(e.Y=r-1,u[t]=e):(nm(u,t,a),u[t].R[r-1]=e),nh([],s,function(n){return n=nw(a,[n[0],null].concat(n.slice(1)),null,i,o),void 0===u[t].R?(n.Y=r-1,u[t]=n):u[t].R[r-1]=n,[]}),[]})},p:function(n,t,r,e,a,i){0<t||w();var o=nS(t,r);a=nb(e,a),nh([],[n],function(n){var r=\"constructor \"+(n=n[0]).name;if(void 0===n.M.X&&(n.M.X=[]),void 0!==n.M.X[t-1])throw new k(\"Cannot register multiple constructors with identical number of parameters (\"+(t-1)+\") for class '\"+n.name+\"'! Overload resolution is currently only performed using the parameter count, not actual type info!\");return n.M.X[t-1]=()=>{nI(\"Cannot construct \"+n.name+\" due to unbound types\",o)},nh([],o,function(e){return e.splice(1,0,null),n.M.X[t-1]=nw(r,e,null,a,i),[]}),[]})},a:function(n,t,r,e,a,i,o,s){var u=nS(r,e);t=B(t),i=nb(a,i),nh([],[n],function(n){function e(){nI(\"Cannot call \"+a+\" due to unbound types\",u)}var a=(n=n[0]).name+\".\"+t;t.startsWith(\"@@\")&&(t=Symbol[t.substring(2)]),s&&n.M.ia.push(t);var c=n.M.W,f=c[t];return void 0===f||void 0===f.R&&f.className!==n.name&&f.Y===r-2?(e.Y=r-2,e.className=n.name,c[t]=e):(nm(c,t,a),c[t].R[r-2]=e),nh([],u,function(e){return e=nw(a,e,n,i,o),void 0===c[t].R?(e.Y=r-2,c[t]=e):c[t].R[r-2]=e,[]}),[]})},A:function(n,t){nv(n,{name:t=B(t),fromWireType:function(n){var t=j(n);return nR(n),t},toWireType:function(n,t){return G(t)},argPackAdvance:8,readValueFromPointer:nc,U:null})},n:function(n,t,r){r=np(r),nv(n,{name:t=B(t),fromWireType:function(n){return n},toWireType:function(n,t){return t},argPackAdvance:8,readValueFromPointer:function(n,t){switch(t){case 2:return function(n){return this.fromWireType(T[n>>2])};case 3:return function(n){return this.fromWireType(L[n>>3])};default:throw TypeError(\"Unknown float type: \"+n)}}(t,r),U:null})},e:function(n,t,r,e,a){t=B(t),-1===a&&(a=4294967295),a=np(r);var i=n=>n;if(0===e){var o=32-8*r;i=n=>n<<o>>>o}r=t.includes(\"unsigned\")?function(n,t){return t>>>0}:function(n,t){return t},nv(n,{name:t,fromWireType:i,toWireType:r,argPackAdvance:8,readValueFromPointer:function(n,t,r){switch(t){case 0:return r?function(n){return d[n]}:function(n){return y[n]};case 1:return r?function(n){return m[n>>1]}:function(n){return E[n>>1]};case 2:return r?function(n){return g[n>>2]}:function(n){return _[n>>2]};default:throw TypeError(\"Unknown integer type: \"+n)}}(t,a,0!==e),U:null})},b:function(n,t,r){function e(n){n>>=2;var t=_;return new a(t.buffer,t[n+1],t[n])}var a=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array][t];nv(n,{name:r=B(r),fromWireType:e,argPackAdvance:8,readValueFromPointer:e},{ta:!0})},o:function(n,t){var r=\"std::string\"===(t=B(t));nv(n,{name:t,fromWireType:function(n){var t=_[n>>2],e=n+4;if(r)for(var a=e,i=0;i<=t;++i){var o=e+i;if(i==t||0==y[o]){if(a=a?p(y,a,o-a):\"\",void 0===s)var s=a;else s+=\"\\x00\"+a;a=o+1}}else{for(i=0,s=Array(t);i<t;++i)s[i]=String.fromCharCode(y[e+i]);s=s.join(\"\")}return nZ(n),s},toWireType:function(n,t){t instanceof ArrayBuffer&&(t=new Uint8Array(t));var e,a=\"string\"==typeof t;if(a||t instanceof Uint8Array||t instanceof Uint8ClampedArray||t instanceof Int8Array||V(\"Cannot pass non-string to std::string\"),r&&a){var i=0;for(e=0;e<t.length;++e){var o=t.charCodeAt(e);127>=o?i++:2047>=o?i+=2:55296<=o&&57343>=o?(i+=4,++e):i+=3}e=i}else e=t.length;if(o=(i=n$(4+e+1))+4,_[i>>2]=e,r&&a){if(a=o,o=e+1,e=y,0<o){o=a+o-1;for(var s=0;s<t.length;++s){var u=t.charCodeAt(s);if(55296<=u&&57343>=u&&(u=65536+((1023&u)<<10)|1023&t.charCodeAt(++s)),127>=u){if(a>=o)break;e[a++]=u}else{if(2047>=u){if(a+1>=o)break;e[a++]=192|u>>6}else{if(65535>=u){if(a+2>=o)break;e[a++]=224|u>>12}else{if(a+3>=o)break;e[a++]=240|u>>18,e[a++]=128|u>>12&63}e[a++]=128|u>>6&63}e[a++]=128|63&u}}e[a]=0}}else if(a)for(a=0;a<e;++a)255<(s=t.charCodeAt(a))&&(nZ(o),V(\"String has UTF-16 code units that do not fit in 8 bits\")),y[o+a]=s;else for(a=0;a<e;++a)y[o+a]=t[a];return null!==n&&n.push(nZ,i),i},argPackAdvance:8,readValueFromPointer:nc,U:function(n){nZ(n)}})},k:function(n,t,r){if(r=B(r),2===t)var e=nW,a=nU,i=nM,o=()=>E,s=1;else 4===t&&(e=nF,a=nD,i=nk,o=()=>_,s=2);nv(n,{name:r,fromWireType:function(n){for(var r,a=_[n>>2],i=o(),u=n+4,c=0;c<=a;++c){var f=n+4+c*t;(c==a||0==i[f>>s])&&(u=e(u,f-u),void 0===r?r=u:r+=\"\\x00\"+u,u=f+t)}return nZ(n),r},toWireType:function(n,e){\"string\"!=typeof e&&V(\"Cannot pass non-string to C++ string type \"+r);var o=i(e),u=n$(4+o+t);return _[u>>2]=o>>s,a(e,u+4,o+t),null!==n&&n.push(nZ,u),u},argPackAdvance:8,readValueFromPointer:nc,U:function(n){nZ(n)}})},m:function(n,t,r,e,a,i){ns[n]={name:B(t),ea:nb(r,e),V:nb(a,i),ha:[]}},c:function(n,t,r,e,a,i,o,s,u,c){ns[n].ha.push({na:B(t),sa:r,qa:nb(e,a),ra:i,ya:o,xa:nb(s,u),za:c})},C:function(n,t){nv(n,{ua:!0,name:t=B(t),argPackAdvance:0,fromWireType:function(){},toWireType:function(){}})},t:function(n,t,r,e,a){n=nG[n],t=j(t),r=nj(r);var i=[];return _[e>>2]=G(i),n(t,r,i,a)},j:function(n,t,r,e){n=nG[n],n(t=j(t),r=nj(r),null,e)},f:nR,g:function(n,t){var r,e,a=function(n,t){for(var r=Array(n),e=0;e<n;++e)r[e]=K(_[t+4*e>>2],\"parameter \"+e);return r}(n,t),i=a[0],o=nY[t=i.name+\"_$\"+a.slice(1).map(function(n){return n.name}).join(\"_\")+\"$\"];if(void 0!==o)return o;var s=Array(n-1);return r=(t,r,e,o)=>{for(var u=0,c=0;c<n-1;++c)s[c]=a[c+1].readValueFromPointer(o+u),u+=a[c+1].argPackAdvance;for(c=0,t=t[r].apply(t,s);c<n-1;++c)a[c+1].la&&a[c+1].la(s[c]);if(!i.ua)return i.toWireType(e,t)},e=nG.length,nG.push(r),o=e,nY[t]=o},r:function(n){4<n&&(M[n].fa+=1)},s:function(n){nu(j(n)),nR(n)},i:function(){w(\"\")},x:function(n,t,r){y.copyWithin(n,t,t+r)},w:function(n){var t=y.length;if(2147483648<(n>>>=0))return!1;for(var r=1;4>=r;r*=2){var e=t*(1+.2/r);e=Math.min(e,n+100663296);var a=Math,i=a.min;e=Math.max(n,e),e+=(65536-e%65536)%65536;n:{var o=l.buffer;try{l.grow(i.call(a,2147483648,e)-o.byteLength+65535>>>16),v();var s=1;break n}catch(n){}s=void 0}if(s)return!0}return!1},z:function(){return 52},u:function(){return 70},y:function(n,t,r,e){for(var a=0,i=0;i<r;i++){var u=_[t>>2],c=_[t+4>>2];t+=8;for(var f=0;f<c;f++){var l=y[u+f],h=nX[n];0===l||10===l?((1===n?o:s)(p(h,0)),h.length=0):h.push(l)}a+=c}return _[e>>2]=a,0}};!function(){function n(n){u.asm=n.exports,l=u.asm.D,v(),A=u.asm.I,P.unshift(u.asm.E),0==--N&&I&&(n=I,I=null,n())}function t(t){n(t.instance)}function e(n){return(\"function\"==typeof fetch?fetch(r,{credentials:\"same-origin\"}).then(function(n){if(!n.ok)throw\"failed to load wasm binary file at '\"+r+\"'\";return n.arrayBuffer()}).catch(function(){return R()}):Promise.resolve().then(function(){return R()})).then(function(n){return WebAssembly.instantiate(n,a)}).then(function(n){return n}).then(n,function(n){s(\"failed to asynchronously prepare wasm: \"+n),w(n)})}var a={a:nx};if(N++,u.instantiateWasm)try{return u.instantiateWasm(a,n)}catch(n){s(\"Module.instantiateWasm callback failed with error: \"+n),f(n)}(\"function\"!=typeof WebAssembly.instantiateStreaming||S()||\"function\"!=typeof fetch?e(t):fetch(r,{credentials:\"same-origin\"}).then(function(n){return WebAssembly.instantiateStreaming(n,a).then(t,function(n){return s(\"wasm streaming compile failed: \"+n),s(\"falling back to ArrayBuffer instantiation\"),e(t)})})).catch(f)}();var nz=u.___getTypeName=function(){return(nz=u.___getTypeName=u.asm.F).apply(null,arguments)};function n$(){return(n$=u.asm.H).apply(null,arguments)}function nZ(){return(nZ=u.asm.J).apply(null,arguments)}function nJ(){0<N||(C(O),0<N||e||(e=!0,u.calledRun=!0,h||(C(P),c(u),C(b))))}return u.__embind_initialize_bindings=function(){return(u.__embind_initialize_bindings=u.asm.G).apply(null,arguments)},u.dynCall_jiji=function(){return(u.dynCall_jiji=u.asm.K).apply(null,arguments)},I=function n(){e||nJ(),e||(I=n)},nJ(),t.ready}})();async function initYoga(t){let r=await yoga({instantiateWasm(n,r){WebAssembly.instantiate(t,n).then(n=>{n instanceof WebAssembly.Instance?r(n):r(n.instance)})}});return n(r)}async function initStreaming(t){let r=await yoga({instantiateWasm(n,r){WebAssembly.instantiateStreaming(t,n).then(({instance:n})=>{r(n)})}});return n(r)}export{initYoga as default,initStreaming};\n",
24
+ "import{readFile as E}from\"node:fs/promises\";import{createRequire as _}from\"node:module\";import a from\"./index.js\";export{A as ALIGN_AUTO,e as ALIGN_BASELINE,b as ALIGN_CENTER,c as ALIGN_FLEX_END,a as ALIGN_FLEX_START,g as ALIGN_SPACE_AROUND,f as ALIGN_SPACE_BETWEEN,d as ALIGN_STRETCH,h as DIMENSION_HEIGHT,D as DIMENSION_WIDTH,i as DIRECTION_INHERIT,j as DIRECTION_LTR,k as DIRECTION_RTL,l as DISPLAY_FLEX,m as DISPLAY_NONE,u as EDGE_ALL,p as EDGE_BOTTOM,r as EDGE_END,s as EDGE_HORIZONTAL,E as EDGE_LEFT,o as EDGE_RIGHT,q as EDGE_START,n as EDGE_TOP,t as EDGE_VERTICAL,x as EXPERIMENTAL_FEATURE_ABSOLUTE_PERCENTAGE_AGAINST_PADDING_EDGE,y as EXPERIMENTAL_FEATURE_FIX_ABSOLUTE_TRAILING_COLUMN_MARGIN,v as EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS,F as FLEX_DIRECTION_COLUMN,z as FLEX_DIRECTION_COLUMN_REVERSE,B as FLEX_DIRECTION_ROW,C as FLEX_DIRECTION_ROW_REVERSE,I as GUTTER_ALL,G as GUTTER_COLUMN,H as GUTTER_ROW,K as JUSTIFY_CENTER,L as JUSTIFY_FLEX_END,J as JUSTIFY_FLEX_START,N as JUSTIFY_SPACE_AROUND,M as JUSTIFY_SPACE_BETWEEN,O as JUSTIFY_SPACE_EVENLY,S as LOG_LEVEL_DEBUG,P as LOG_LEVEL_ERROR,U as LOG_LEVEL_FATAL,R as LOG_LEVEL_INFO,T as LOG_LEVEL_VERBOSE,Q as LOG_LEVEL_WARN,X as MEASURE_MODE_AT_MOST,W as MEASURE_MODE_EXACTLY,V as MEASURE_MODE_UNDEFINED,Y as NODE_TYPE_DEFAULT,Z as NODE_TYPE_TEXT,$ as OVERFLOW_HIDDEN,a0 as OVERFLOW_SCROLL,_ as OVERFLOW_VISIBLE,a3 as POSITION_TYPE_ABSOLUTE,a2 as POSITION_TYPE_RELATIVE,a1 as POSITION_TYPE_STATIC,a6 as PRINT_OPTIONS_CHILDREN,a4 as PRINT_OPTIONS_LAYOUT,a5 as PRINT_OPTIONS_STYLE,aa as UNIT_AUTO,a9 as UNIT_PERCENT,a8 as UNIT_POINT,a7 as UNIT_UNDEFINED,ab as WRAP_NO_WRAP,ac as WRAP_WRAP,ad as WRAP_WRAP_REVERSE}from\"./wrapAsm-f766f97f.js\";let Yoga=await a(await E(_(import.meta.url).resolve(\"./yoga.wasm\")));export{Yoga as default};\n",
25
+ "/**\n * Yoga Layout Engine Adapter\n *\n * Wraps yoga-wasm-web to implement the LayoutEngine interface.\n */\n\nimport type {\n Align,\n Direction,\n Display,\n Edge,\n FlexDirection,\n Gutter,\n Justify,\n Overflow,\n PositionType,\n Wrap,\n Yoga,\n Node as YogaNode,\n} from \"yoga-wasm-web\"\nimport type {\n AlignValue,\n DirectionValue,\n DisplayValue,\n EdgeValue,\n FlexDirectionValue,\n GutterValue,\n JustifyValue,\n LayoutConstants,\n LayoutEngine,\n LayoutNode,\n MeasureFunc,\n MeasureMode,\n MeasureModeValue,\n OverflowValue,\n PositionTypeValue,\n WrapValue,\n} from \"../layout-engine\"\n\n// ============================================================================\n// Yoga Node Adapter\n// ============================================================================\n\n/**\n * Wraps a Yoga node to implement LayoutNode interface.\n */\nclass YogaNodeAdapter implements LayoutNode {\n private node: YogaNode\n private yoga: Yoga\n private hasMeasureFunc = false\n\n constructor(node: YogaNode, yoga: Yoga) {\n this.node = node\n this.yoga = yoga\n }\n\n /** Get the underlying Yoga node (for tree operations) */\n getYogaNode(): YogaNode {\n return this.node\n }\n\n // Tree operations\n insertChild(child: LayoutNode, index: number): void {\n const yogaChild = (child as YogaNodeAdapter).getYogaNode()\n this.node.insertChild(yogaChild, index)\n }\n\n removeChild(child: LayoutNode): void {\n const yogaChild = (child as YogaNodeAdapter).getYogaNode()\n this.node.removeChild(yogaChild)\n }\n\n free(): void {\n this.node.free()\n }\n\n // Measure function\n setMeasureFunc(measureFunc: MeasureFunc): void {\n this.hasMeasureFunc = true\n this.node.setMeasureFunc((width, widthMode, height, heightMode) => {\n const widthModeStr = this.measureModeToString(widthMode)\n const heightModeStr = this.measureModeToString(heightMode)\n return measureFunc(width, widthModeStr, height, heightModeStr)\n })\n }\n\n // Dirty tracking - forces layout recalculation\n // Yoga only allows markDirty() on leaf nodes with measure functions\n markDirty(): void {\n if (this.hasMeasureFunc) {\n this.node.markDirty()\n }\n }\n\n private measureModeToString(mode: number): MeasureMode {\n if (mode === this.yoga.MEASURE_MODE_EXACTLY) return \"exactly\"\n if (mode === this.yoga.MEASURE_MODE_AT_MOST) return \"at-most\"\n return \"undefined\"\n }\n\n // Dimension setters\n setWidth(value: number): void {\n this.node.setWidth(value)\n }\n setWidthPercent(value: number): void {\n this.node.setWidthPercent(value)\n }\n setWidthAuto(): void {\n this.node.setWidthAuto()\n }\n setHeight(value: number): void {\n this.node.setHeight(value)\n }\n setHeightPercent(value: number): void {\n this.node.setHeightPercent(value)\n }\n setHeightAuto(): void {\n this.node.setHeightAuto()\n }\n setMinWidth(value: number): void {\n this.node.setMinWidth(value)\n }\n setMinWidthPercent(value: number): void {\n this.node.setMinWidthPercent(value)\n }\n setMinHeight(value: number): void {\n this.node.setMinHeight(value)\n }\n setMinHeightPercent(value: number): void {\n this.node.setMinHeightPercent(value)\n }\n setMaxWidth(value: number): void {\n this.node.setMaxWidth(value)\n }\n setMaxWidthPercent(value: number): void {\n this.node.setMaxWidthPercent(value)\n }\n setMaxHeight(value: number): void {\n this.node.setMaxHeight(value)\n }\n setMaxHeightPercent(value: number): void {\n this.node.setMaxHeightPercent(value)\n }\n\n // Flex properties\n setFlexGrow(value: number): void {\n this.node.setFlexGrow(value)\n }\n setFlexShrink(value: number): void {\n this.node.setFlexShrink(value)\n }\n setFlexBasis(value: number): void {\n this.node.setFlexBasis(value)\n }\n setFlexBasisPercent(value: number): void {\n this.node.setFlexBasisPercent(value)\n }\n setFlexBasisAuto(): void {\n this.node.setFlexBasisAuto()\n }\n setFlexDirection(direction: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded FlexDirection type\n this.node.setFlexDirection(direction as FlexDirection)\n }\n setFlexWrap(wrap: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded Wrap type\n this.node.setFlexWrap(wrap as Wrap)\n }\n\n // Alignment\n setAlignItems(align: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded Align type\n this.node.setAlignItems(align as Align)\n }\n setAlignSelf(align: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded Align type\n this.node.setAlignSelf(align as Align)\n }\n setAlignContent(align: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded Align type\n this.node.setAlignContent(align as Align)\n }\n setJustifyContent(justify: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded Justify type\n this.node.setJustifyContent(justify as Justify)\n }\n\n // Spacing\n setPadding(edge: number, value: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded Edge type\n this.node.setPadding(edge as Edge, value)\n }\n setMargin(edge: number, value: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded Edge type\n this.node.setMargin(edge as Edge, value)\n }\n setBorder(edge: number, value: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded Edge type\n this.node.setBorder(edge as Edge, value)\n }\n setGap(gutter: number, value: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded Gutter type\n this.node.setGap(gutter as Gutter, value)\n }\n\n // Display & Position\n setDisplay(display: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded Display type\n this.node.setDisplay(display as Display)\n }\n setPositionType(positionType: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded PositionType type\n this.node.setPositionType(positionType as PositionType)\n }\n setPosition(edge: number, value: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded Edge type\n this.node.setPosition(edge as Edge, value)\n }\n setPositionPercent(edge: number, value: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded Edge type\n this.node.setPositionPercent(edge as Edge, value)\n }\n setOverflow(overflow: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded Overflow type\n this.node.setOverflow(overflow as Overflow)\n }\n\n // Aspect Ratio\n setAspectRatio(value: number): void {\n this.node.setAspectRatio(value)\n }\n\n // Layout calculation\n calculateLayout(width: number, height: number, direction?: number): void {\n // LayoutEngine uses plain numbers; Yoga uses branded Direction type\n this.node.calculateLayout(width, height, (direction ?? this.yoga.DIRECTION_LTR) as Direction)\n }\n\n // Layout results\n getComputedLeft(): number {\n return this.node.getComputedLeft()\n }\n getComputedTop(): number {\n return this.node.getComputedTop()\n }\n getComputedWidth(): number {\n return this.node.getComputedWidth()\n }\n getComputedHeight(): number {\n return this.node.getComputedHeight()\n }\n}\n\n// ============================================================================\n// Yoga Layout Engine\n// ============================================================================\n\n/**\n * Layout engine implementation using Yoga (WASM).\n */\nexport class YogaLayoutEngine implements LayoutEngine {\n private yoga: Yoga\n private _constants: LayoutConstants\n\n constructor(yoga: Yoga) {\n this.yoga = yoga\n // Cast Yoga's branded types to our LayoutEngine branded types at the adapter boundary\n this._constants = {\n // Flex Direction\n FLEX_DIRECTION_COLUMN: yoga.FLEX_DIRECTION_COLUMN as unknown as FlexDirectionValue,\n FLEX_DIRECTION_COLUMN_REVERSE: yoga.FLEX_DIRECTION_COLUMN_REVERSE as unknown as FlexDirectionValue,\n FLEX_DIRECTION_ROW: yoga.FLEX_DIRECTION_ROW as unknown as FlexDirectionValue,\n FLEX_DIRECTION_ROW_REVERSE: yoga.FLEX_DIRECTION_ROW_REVERSE as unknown as FlexDirectionValue,\n\n // Wrap\n WRAP_NO_WRAP: yoga.WRAP_NO_WRAP as unknown as WrapValue,\n WRAP_WRAP: yoga.WRAP_WRAP as unknown as WrapValue,\n WRAP_WRAP_REVERSE: yoga.WRAP_WRAP_REVERSE as unknown as WrapValue,\n\n // Align\n ALIGN_AUTO: yoga.ALIGN_AUTO as unknown as AlignValue,\n ALIGN_FLEX_START: yoga.ALIGN_FLEX_START as unknown as AlignValue,\n ALIGN_CENTER: yoga.ALIGN_CENTER as unknown as AlignValue,\n ALIGN_FLEX_END: yoga.ALIGN_FLEX_END as unknown as AlignValue,\n ALIGN_STRETCH: yoga.ALIGN_STRETCH as unknown as AlignValue,\n ALIGN_BASELINE: yoga.ALIGN_BASELINE as unknown as AlignValue,\n ALIGN_SPACE_BETWEEN: yoga.ALIGN_SPACE_BETWEEN as unknown as AlignValue,\n ALIGN_SPACE_AROUND: yoga.ALIGN_SPACE_AROUND as unknown as AlignValue,\n ALIGN_SPACE_EVENLY: (yoga as any).ALIGN_SPACE_EVENLY as unknown as AlignValue,\n\n // Justify\n JUSTIFY_FLEX_START: yoga.JUSTIFY_FLEX_START as unknown as JustifyValue,\n JUSTIFY_CENTER: yoga.JUSTIFY_CENTER as unknown as JustifyValue,\n JUSTIFY_FLEX_END: yoga.JUSTIFY_FLEX_END as unknown as JustifyValue,\n JUSTIFY_SPACE_BETWEEN: yoga.JUSTIFY_SPACE_BETWEEN as unknown as JustifyValue,\n JUSTIFY_SPACE_AROUND: yoga.JUSTIFY_SPACE_AROUND as unknown as JustifyValue,\n JUSTIFY_SPACE_EVENLY: yoga.JUSTIFY_SPACE_EVENLY as unknown as JustifyValue,\n\n // Edge\n EDGE_LEFT: yoga.EDGE_LEFT as unknown as EdgeValue,\n EDGE_TOP: yoga.EDGE_TOP as unknown as EdgeValue,\n EDGE_RIGHT: yoga.EDGE_RIGHT as unknown as EdgeValue,\n EDGE_BOTTOM: yoga.EDGE_BOTTOM as unknown as EdgeValue,\n EDGE_HORIZONTAL: yoga.EDGE_HORIZONTAL as unknown as EdgeValue,\n EDGE_VERTICAL: yoga.EDGE_VERTICAL as unknown as EdgeValue,\n EDGE_ALL: yoga.EDGE_ALL as unknown as EdgeValue,\n\n // Gutter\n GUTTER_COLUMN: yoga.GUTTER_COLUMN as unknown as GutterValue,\n GUTTER_ROW: yoga.GUTTER_ROW as unknown as GutterValue,\n GUTTER_ALL: yoga.GUTTER_ALL as unknown as GutterValue,\n\n // Display\n DISPLAY_FLEX: yoga.DISPLAY_FLEX as unknown as DisplayValue,\n DISPLAY_NONE: yoga.DISPLAY_NONE as unknown as DisplayValue,\n\n // Position Type\n POSITION_TYPE_STATIC: yoga.POSITION_TYPE_STATIC as unknown as PositionTypeValue,\n POSITION_TYPE_RELATIVE: yoga.POSITION_TYPE_RELATIVE as unknown as PositionTypeValue,\n POSITION_TYPE_ABSOLUTE: yoga.POSITION_TYPE_ABSOLUTE as unknown as PositionTypeValue,\n\n // Overflow\n OVERFLOW_VISIBLE: yoga.OVERFLOW_VISIBLE as unknown as OverflowValue,\n OVERFLOW_HIDDEN: yoga.OVERFLOW_HIDDEN as unknown as OverflowValue,\n OVERFLOW_SCROLL: yoga.OVERFLOW_SCROLL as unknown as OverflowValue,\n\n // Direction\n DIRECTION_LTR: yoga.DIRECTION_LTR as unknown as DirectionValue,\n\n // Measure Mode\n MEASURE_MODE_UNDEFINED: yoga.MEASURE_MODE_UNDEFINED as unknown as MeasureModeValue,\n MEASURE_MODE_EXACTLY: yoga.MEASURE_MODE_EXACTLY as unknown as MeasureModeValue,\n MEASURE_MODE_AT_MOST: yoga.MEASURE_MODE_AT_MOST as unknown as MeasureModeValue,\n }\n }\n\n createNode(): LayoutNode {\n return new YogaNodeAdapter(this.yoga.Node.create(), this.yoga)\n }\n\n get constants(): LayoutConstants {\n return this._constants\n }\n\n get name(): string {\n return \"yoga\"\n }\n}\n\n// ============================================================================\n// Initialization Helper\n// ============================================================================\n\n/**\n * Create a Yoga layout engine from an initialized Yoga instance.\n */\nexport function createYogaEngine(yoga: Yoga): YogaLayoutEngine {\n return new YogaLayoutEngine(yoga)\n}\n\n/**\n * Initialize Yoga and create a layout engine.\n * Uses yoga-wasm-web/auto which automatically selects the right implementation.\n */\nexport async function initYogaEngine(): Promise<YogaLayoutEngine> {\n const { default: yoga } = (await import(\"yoga-wasm-web/auto\")) as {\n default: Yoga\n }\n return new YogaLayoutEngine(yoga)\n}\n",
26
+ "/**\n * Flexily Layout Engine Adapter\n *\n * Wraps Flexily to implement the LayoutEngine interface.\n * Uses the default zero-allocation algorithm from flexily.\n */\n\nimport {\n ALIGN_AUTO,\n ALIGN_BASELINE,\n ALIGN_CENTER,\n ALIGN_FLEX_END,\n ALIGN_FLEX_START,\n ALIGN_SPACE_AROUND,\n ALIGN_SPACE_BETWEEN,\n ALIGN_SPACE_EVENLY,\n ALIGN_STRETCH,\n DIRECTION_LTR,\n DISPLAY_FLEX,\n DISPLAY_NONE,\n EDGE_ALL,\n EDGE_BOTTOM,\n EDGE_HORIZONTAL,\n EDGE_LEFT,\n EDGE_RIGHT,\n EDGE_TOP,\n EDGE_VERTICAL,\n // Constants\n FLEX_DIRECTION_COLUMN,\n FLEX_DIRECTION_COLUMN_REVERSE,\n FLEX_DIRECTION_ROW,\n FLEX_DIRECTION_ROW_REVERSE,\n Node as FlexilyNode,\n GUTTER_ALL,\n GUTTER_COLUMN,\n GUTTER_ROW,\n JUSTIFY_CENTER,\n JUSTIFY_FLEX_END,\n JUSTIFY_FLEX_START,\n JUSTIFY_SPACE_AROUND,\n JUSTIFY_SPACE_BETWEEN,\n JUSTIFY_SPACE_EVENLY,\n MEASURE_MODE_AT_MOST,\n MEASURE_MODE_EXACTLY,\n MEASURE_MODE_UNDEFINED,\n OVERFLOW_HIDDEN,\n OVERFLOW_SCROLL,\n OVERFLOW_VISIBLE,\n POSITION_TYPE_ABSOLUTE,\n POSITION_TYPE_RELATIVE,\n POSITION_TYPE_STATIC,\n WRAP_NO_WRAP,\n WRAP_WRAP,\n WRAP_WRAP_REVERSE,\n} from \"flexily\"\n\nimport type {\n AlignValue,\n DirectionValue,\n DisplayValue,\n EdgeValue,\n FlexDirectionValue,\n GutterValue,\n JustifyValue,\n LayoutConstants,\n LayoutEngine,\n LayoutNode,\n MeasureFunc,\n MeasureMode,\n MeasureModeValue,\n OverflowValue,\n PositionTypeValue,\n WrapValue,\n} from \"../layout-engine\"\n\n// ============================================================================\n// Flexily Zero Node Adapter\n// ============================================================================\n\n/**\n * Wraps a Flexily zero-alloc node to implement LayoutNode interface.\n * Since Flexily already has a Yoga-compatible API, this is mostly delegation.\n */\nclass FlexilyZeroNodeAdapter implements LayoutNode {\n private node: FlexilyNode\n\n constructor(node: FlexilyNode) {\n this.node = node\n }\n\n /** Get the underlying Flexily node (for tree operations) */\n getFlexilyNode(): FlexilyNode {\n return this.node\n }\n\n // Tree operations\n insertChild(child: LayoutNode, index: number): void {\n const flexilyChild = (child as FlexilyZeroNodeAdapter).getFlexilyNode()\n this.node.insertChild(flexilyChild, index)\n }\n\n removeChild(child: LayoutNode): void {\n const flexilyChild = (child as FlexilyZeroNodeAdapter).getFlexilyNode()\n this.node.removeChild(flexilyChild)\n }\n\n free(): void {\n this.node.free()\n }\n\n // Measure function\n setMeasureFunc(measureFunc: MeasureFunc): void {\n this.node.setMeasureFunc((width, widthMode, height, heightMode) => {\n const widthModeStr = this.measureModeToString(widthMode)\n const heightModeStr = this.measureModeToString(heightMode)\n return measureFunc(width, widthModeStr, height, heightModeStr)\n })\n }\n\n // Dirty tracking - forces layout recalculation\n markDirty(): void {\n this.node.markDirty()\n }\n\n private measureModeToString(mode: number): MeasureMode {\n if (mode === MEASURE_MODE_EXACTLY) return \"exactly\"\n if (mode === MEASURE_MODE_AT_MOST) return \"at-most\"\n return \"undefined\"\n }\n\n // Dimension setters\n setWidth(value: number): void {\n this.node.setWidth(value)\n }\n setWidthPercent(value: number): void {\n this.node.setWidthPercent(value)\n }\n setWidthAuto(): void {\n this.node.setWidthAuto()\n }\n setHeight(value: number): void {\n this.node.setHeight(value)\n }\n setHeightPercent(value: number): void {\n this.node.setHeightPercent(value)\n }\n setHeightAuto(): void {\n this.node.setHeightAuto()\n }\n setMinWidth(value: number): void {\n this.node.setMinWidth(value)\n }\n setMinWidthPercent(value: number): void {\n this.node.setMinWidthPercent(value)\n }\n setMinHeight(value: number): void {\n this.node.setMinHeight(value)\n }\n setMinHeightPercent(value: number): void {\n this.node.setMinHeightPercent(value)\n }\n setMaxWidth(value: number): void {\n this.node.setMaxWidth(value)\n }\n setMaxWidthPercent(value: number): void {\n this.node.setMaxWidthPercent(value)\n }\n setMaxHeight(value: number): void {\n this.node.setMaxHeight(value)\n }\n setMaxHeightPercent(value: number): void {\n this.node.setMaxHeightPercent(value)\n }\n\n // Flex properties\n setFlexGrow(value: number): void {\n this.node.setFlexGrow(value)\n }\n setFlexShrink(value: number): void {\n this.node.setFlexShrink(value)\n }\n setFlexBasis(value: number): void {\n this.node.setFlexBasis(value)\n }\n setFlexBasisPercent(value: number): void {\n this.node.setFlexBasisPercent(value)\n }\n setFlexBasisAuto(): void {\n this.node.setFlexBasisAuto()\n }\n setFlexDirection(direction: number): void {\n this.node.setFlexDirection(direction)\n }\n setFlexWrap(wrap: number): void {\n this.node.setFlexWrap(wrap)\n }\n\n // Alignment\n setAlignItems(align: number): void {\n this.node.setAlignItems(align)\n }\n setAlignSelf(align: number): void {\n this.node.setAlignSelf(align)\n }\n setAlignContent(align: number): void {\n this.node.setAlignContent(align)\n }\n setJustifyContent(justify: number): void {\n this.node.setJustifyContent(justify)\n }\n\n // Spacing\n setPadding(edge: number, value: number): void {\n this.node.setPadding(edge, value)\n }\n setMargin(edge: number, value: number): void {\n this.node.setMargin(edge, value)\n }\n setBorder(edge: number, value: number): void {\n this.node.setBorder(edge, value)\n }\n setGap(gutter: number, value: number): void {\n this.node.setGap(gutter, value)\n }\n\n // Display & Position\n setDisplay(display: number): void {\n this.node.setDisplay(display)\n }\n setPositionType(positionType: number): void {\n this.node.setPositionType(positionType)\n }\n setPosition(edge: number, value: number): void {\n this.node.setPosition(edge, value)\n }\n setPositionPercent(edge: number, value: number): void {\n this.node.setPositionPercent(edge, value)\n }\n setOverflow(overflow: number): void {\n this.node.setOverflow(overflow)\n }\n\n // Aspect Ratio\n setAspectRatio(value: number): void {\n this.node.setAspectRatio(value)\n }\n\n // Layout calculation\n calculateLayout(width: number, height: number, direction?: number): void {\n this.node.calculateLayout(width, height, direction ?? DIRECTION_LTR)\n }\n\n // Layout results\n getComputedLeft(): number {\n return this.node.getComputedLeft()\n }\n getComputedTop(): number {\n return this.node.getComputedTop()\n }\n getComputedWidth(): number {\n return this.node.getComputedWidth()\n }\n getComputedHeight(): number {\n return this.node.getComputedHeight()\n }\n}\n\n// ============================================================================\n// Flexily Zero Layout Engine\n// ============================================================================\n\n/**\n * Layout engine implementation using Flexily zero-allocation variant.\n * Optimized for high-frequency layout with reduced GC pressure.\n */\nexport class FlexilyZeroLayoutEngine implements LayoutEngine {\n private _constants: LayoutConstants = {\n // Flex Direction (cast from Flexily's plain numbers to branded types)\n FLEX_DIRECTION_COLUMN: FLEX_DIRECTION_COLUMN as FlexDirectionValue,\n FLEX_DIRECTION_COLUMN_REVERSE: FLEX_DIRECTION_COLUMN_REVERSE as FlexDirectionValue,\n FLEX_DIRECTION_ROW: FLEX_DIRECTION_ROW as FlexDirectionValue,\n FLEX_DIRECTION_ROW_REVERSE: FLEX_DIRECTION_ROW_REVERSE as FlexDirectionValue,\n\n // Wrap\n WRAP_NO_WRAP: WRAP_NO_WRAP as WrapValue,\n WRAP_WRAP: WRAP_WRAP as WrapValue,\n WRAP_WRAP_REVERSE: WRAP_WRAP_REVERSE as WrapValue,\n\n // Align\n ALIGN_AUTO: ALIGN_AUTO as AlignValue,\n ALIGN_FLEX_START: ALIGN_FLEX_START as AlignValue,\n ALIGN_CENTER: ALIGN_CENTER as AlignValue,\n ALIGN_FLEX_END: ALIGN_FLEX_END as AlignValue,\n ALIGN_STRETCH: ALIGN_STRETCH as AlignValue,\n ALIGN_BASELINE: ALIGN_BASELINE as AlignValue,\n ALIGN_SPACE_BETWEEN: ALIGN_SPACE_BETWEEN as AlignValue,\n ALIGN_SPACE_AROUND: ALIGN_SPACE_AROUND as AlignValue,\n ALIGN_SPACE_EVENLY: ALIGN_SPACE_EVENLY as AlignValue,\n\n // Justify\n JUSTIFY_FLEX_START: JUSTIFY_FLEX_START as JustifyValue,\n JUSTIFY_CENTER: JUSTIFY_CENTER as JustifyValue,\n JUSTIFY_FLEX_END: JUSTIFY_FLEX_END as JustifyValue,\n JUSTIFY_SPACE_BETWEEN: JUSTIFY_SPACE_BETWEEN as JustifyValue,\n JUSTIFY_SPACE_AROUND: JUSTIFY_SPACE_AROUND as JustifyValue,\n JUSTIFY_SPACE_EVENLY: JUSTIFY_SPACE_EVENLY as JustifyValue,\n\n // Edge\n EDGE_LEFT: EDGE_LEFT as EdgeValue,\n EDGE_TOP: EDGE_TOP as EdgeValue,\n EDGE_RIGHT: EDGE_RIGHT as EdgeValue,\n EDGE_BOTTOM: EDGE_BOTTOM as EdgeValue,\n EDGE_HORIZONTAL: EDGE_HORIZONTAL as EdgeValue,\n EDGE_VERTICAL: EDGE_VERTICAL as EdgeValue,\n EDGE_ALL: EDGE_ALL as EdgeValue,\n\n // Gutter\n GUTTER_COLUMN: GUTTER_COLUMN as GutterValue,\n GUTTER_ROW: GUTTER_ROW as GutterValue,\n GUTTER_ALL: GUTTER_ALL as GutterValue,\n\n // Display\n DISPLAY_FLEX: DISPLAY_FLEX as DisplayValue,\n DISPLAY_NONE: DISPLAY_NONE as DisplayValue,\n\n // Position Type\n POSITION_TYPE_STATIC: POSITION_TYPE_STATIC as PositionTypeValue,\n POSITION_TYPE_RELATIVE: POSITION_TYPE_RELATIVE as PositionTypeValue,\n POSITION_TYPE_ABSOLUTE: POSITION_TYPE_ABSOLUTE as PositionTypeValue,\n\n // Overflow\n OVERFLOW_VISIBLE: OVERFLOW_VISIBLE as OverflowValue,\n OVERFLOW_HIDDEN: OVERFLOW_HIDDEN as OverflowValue,\n OVERFLOW_SCROLL: OVERFLOW_SCROLL as OverflowValue,\n\n // Direction\n DIRECTION_LTR: DIRECTION_LTR as DirectionValue,\n\n // Measure Mode\n MEASURE_MODE_UNDEFINED: MEASURE_MODE_UNDEFINED as MeasureModeValue,\n MEASURE_MODE_EXACTLY: MEASURE_MODE_EXACTLY as MeasureModeValue,\n MEASURE_MODE_AT_MOST: MEASURE_MODE_AT_MOST as MeasureModeValue,\n }\n\n createNode(): LayoutNode {\n return new FlexilyZeroNodeAdapter(FlexilyNode.create())\n }\n\n get constants(): LayoutConstants {\n return this._constants\n }\n\n get name(): string {\n return \"flexily-zero\"\n }\n}\n\n// ============================================================================\n// Initialization Helper\n// ============================================================================\n\n/**\n * Create a Flexily zero-allocation layout engine.\n * Unlike Yoga, Flexily doesn't require async initialization.\n */\nexport function createFlexilyZeroEngine(): FlexilyZeroLayoutEngine {\n return new FlexilyZeroLayoutEngine()\n}\n",
27
+ "/**\n * Layout Engine Abstraction\n *\n * Provides a pluggable interface for layout engines (Yoga, Flexily, etc.)\n * This allows silvery to use different layout backends without code changes.\n */\n\n// ============================================================================\n// Measure Function Types\n// ============================================================================\n\n/**\n * Measure mode determines how the width/height constraint should be interpreted.\n */\nexport type MeasureMode = \"undefined\" | \"exactly\" | \"at-most\"\n\n/**\n * Measure function callback for intrinsic sizing.\n * Called when a node needs to determine its size based on content.\n */\nexport type MeasureFunc = (\n width: number,\n widthMode: MeasureMode,\n height: number,\n heightMode: MeasureMode,\n) => { width: number; height: number }\n\n// ============================================================================\n// Layout Node Interface\n// ============================================================================\n\n/**\n * Abstract layout node interface.\n * Represents a single node in the layout tree.\n */\nexport interface LayoutNode {\n // Tree operations\n insertChild(child: LayoutNode, index: number): void\n removeChild(child: LayoutNode): void\n free(): void\n\n // Measure function\n setMeasureFunc(measureFunc: MeasureFunc): void\n\n // Dirty tracking\n markDirty(): void\n\n // Dimension setters\n setWidth(value: number): void\n setWidthPercent(value: number): void\n setWidthAuto(): void\n setHeight(value: number): void\n setHeightPercent(value: number): void\n setHeightAuto(): void\n setMinWidth(value: number): void\n setMinWidthPercent(value: number): void\n setMinHeight(value: number): void\n setMinHeightPercent(value: number): void\n setMaxWidth(value: number): void\n setMaxWidthPercent(value: number): void\n setMaxHeight(value: number): void\n setMaxHeightPercent(value: number): void\n\n // Flex properties\n setFlexGrow(value: number): void\n setFlexShrink(value: number): void\n setFlexBasis(value: number): void\n setFlexBasisPercent(value: number): void\n setFlexBasisAuto(): void\n setFlexDirection(direction: number): void\n setFlexWrap(wrap: number): void\n\n // Alignment\n setAlignItems(align: number): void\n setAlignSelf(align: number): void\n setAlignContent(align: number): void\n setJustifyContent(justify: number): void\n\n // Spacing\n setPadding(edge: number, value: number): void\n setMargin(edge: number, value: number): void\n setBorder(edge: number, value: number): void\n setGap(gutter: number, value: number): void\n\n // Display & Position\n setDisplay(display: number): void\n setPositionType(positionType: number): void\n setPosition(edge: number, value: number): void\n setPositionPercent(edge: number, value: number): void\n setOverflow(overflow: number): void\n\n // Aspect Ratio\n setAspectRatio(value: number): void\n\n // Layout calculation\n calculateLayout(width: number, height: number, direction?: number): void\n\n // Layout results\n getComputedLeft(): number\n getComputedTop(): number\n getComputedWidth(): number\n getComputedHeight(): number\n}\n\n// ============================================================================\n// Branded Types for Type Safety\n// ============================================================================\n\n/**\n * Branded types prevent accidentally mixing up layout constant categories.\n * E.g., you can't pass an AlignValue where a FlexDirectionValue is expected.\n */\nexport type FlexDirectionValue = number & { readonly __brand: \"FlexDirection\" }\nexport type WrapValue = number & { readonly __brand: \"Wrap\" }\nexport type AlignValue = number & { readonly __brand: \"Align\" }\nexport type JustifyValue = number & { readonly __brand: \"Justify\" }\nexport type EdgeValue = number & { readonly __brand: \"Edge\" }\nexport type GutterValue = number & { readonly __brand: \"Gutter\" }\nexport type DisplayValue = number & { readonly __brand: \"Display\" }\nexport type PositionTypeValue = number & { readonly __brand: \"PositionType\" }\nexport type OverflowValue = number & { readonly __brand: \"Overflow\" }\nexport type DirectionValue = number & { readonly __brand: \"Direction\" }\nexport type MeasureModeValue = number & { readonly __brand: \"MeasureMode\" }\n\n// ============================================================================\n// Layout Constants Interface\n// ============================================================================\n\n/**\n * Constants for layout configuration.\n * These are the same across Yoga and Flexily.\n * Uses branded types for compile-time safety.\n */\nexport interface LayoutConstants {\n // Flex Direction\n FLEX_DIRECTION_COLUMN: FlexDirectionValue\n FLEX_DIRECTION_COLUMN_REVERSE: FlexDirectionValue\n FLEX_DIRECTION_ROW: FlexDirectionValue\n FLEX_DIRECTION_ROW_REVERSE: FlexDirectionValue\n\n // Wrap\n WRAP_NO_WRAP: WrapValue\n WRAP_WRAP: WrapValue\n WRAP_WRAP_REVERSE: WrapValue\n\n // Align\n ALIGN_AUTO: AlignValue\n ALIGN_FLEX_START: AlignValue\n ALIGN_CENTER: AlignValue\n ALIGN_FLEX_END: AlignValue\n ALIGN_STRETCH: AlignValue\n ALIGN_BASELINE: AlignValue\n ALIGN_SPACE_BETWEEN: AlignValue\n ALIGN_SPACE_AROUND: AlignValue\n ALIGN_SPACE_EVENLY: AlignValue\n\n // Justify\n JUSTIFY_FLEX_START: JustifyValue\n JUSTIFY_CENTER: JustifyValue\n JUSTIFY_FLEX_END: JustifyValue\n JUSTIFY_SPACE_BETWEEN: JustifyValue\n JUSTIFY_SPACE_AROUND: JustifyValue\n JUSTIFY_SPACE_EVENLY: JustifyValue\n\n // Edge\n EDGE_LEFT: EdgeValue\n EDGE_TOP: EdgeValue\n EDGE_RIGHT: EdgeValue\n EDGE_BOTTOM: EdgeValue\n EDGE_HORIZONTAL: EdgeValue\n EDGE_VERTICAL: EdgeValue\n EDGE_ALL: EdgeValue\n\n // Gutter\n GUTTER_COLUMN: GutterValue\n GUTTER_ROW: GutterValue\n GUTTER_ALL: GutterValue\n\n // Display\n DISPLAY_FLEX: DisplayValue\n DISPLAY_NONE: DisplayValue\n\n // Position Type\n POSITION_TYPE_STATIC: PositionTypeValue\n POSITION_TYPE_RELATIVE: PositionTypeValue\n POSITION_TYPE_ABSOLUTE: PositionTypeValue\n\n // Overflow\n OVERFLOW_VISIBLE: OverflowValue\n OVERFLOW_HIDDEN: OverflowValue\n OVERFLOW_SCROLL: OverflowValue\n\n // Direction\n DIRECTION_LTR: DirectionValue\n\n // Measure Mode\n MEASURE_MODE_UNDEFINED: MeasureModeValue\n MEASURE_MODE_EXACTLY: MeasureModeValue\n MEASURE_MODE_AT_MOST: MeasureModeValue\n}\n\n// ============================================================================\n// Layout Engine Interface\n// ============================================================================\n\n/**\n * Abstract layout engine interface.\n * Implementations can wrap Yoga, Flexily, or other layout engines.\n */\nexport interface LayoutEngine {\n /** Create a new layout node */\n createNode(): LayoutNode\n\n /** Layout constants for this engine */\n readonly constants: LayoutConstants\n\n /** Engine name for debugging */\n readonly name: string\n}\n\n// ============================================================================\n// Global Layout Engine Management\n// ============================================================================\n\nlet layoutEngine: LayoutEngine | null = null\n\n/**\n * Set the global layout engine instance.\n * Must be called before rendering.\n */\nexport function setLayoutEngine(engine: LayoutEngine): void {\n layoutEngine = engine\n}\n\n/**\n * Get the global layout engine instance.\n * Throws if not initialized.\n */\nexport function getLayoutEngine(): LayoutEngine {\n if (!layoutEngine) {\n throw new Error(\"Layout engine not initialized. Call setLayoutEngine() or initYoga()/initFlexily() first.\")\n }\n return layoutEngine\n}\n\n/**\n * Check if a layout engine is initialized.\n */\nexport function isLayoutEngineInitialized(): boolean {\n return layoutEngine !== null\n}\n\n/**\n * Get the layout constants from the current engine.\n * Convenience function for accessing constants.\n */\nexport function getConstants(): LayoutConstants {\n return getLayoutEngine().constants\n}\n\n// ============================================================================\n// Default Engine Initialization\n// ============================================================================\n\n/**\n * Layout engine type for configuration.\n *\n * - 'flexily': Zero-allocation Flexily (default, optimized for high-frequency layout)\n * - 'flexily-classic': Classic Flexily algorithm (for debugging/compatibility)\n * - 'yoga': Facebook's WASM-based flexbox (most mature)\n */\nexport type LayoutEngineType = \"flexily\" | \"yoga\"\n\n/**\n * Initialize the layout engine if not already set.\n *\n * @param engineType - 'flexily', 'flexily-classic', or 'yoga'. If not provided, checks\n * SILVERY_ENGINE env var, then defaults to 'flexily'.\n */\nexport async function ensureDefaultLayoutEngine(engineType?: LayoutEngineType): Promise<void> {\n if (isLayoutEngineInitialized()) {\n return\n }\n\n // Resolve engine type: option → env → 'flexily'\n const resolved = engineType ?? (process.env.SILVERY_ENGINE?.toLowerCase() as LayoutEngineType) ?? \"flexily\"\n\n if (resolved === \"yoga\") {\n const { initYogaEngine } = await import(\"./adapters/yoga-adapter.js\")\n setLayoutEngine(await initYogaEngine())\n } else {\n // 'flexily' (default) uses zero-allocation engine\n const { createFlexilyZeroEngine } = await import(\"./adapters/flexily-zero-adapter.js\")\n setLayoutEngine(createFlexilyZeroEngine())\n }\n}\n",
28
+ "/**\n * Shared text content collection primitives.\n *\n * Multiple pipeline phases need to collect plain text from node trees:\n * - Measure phase: to compute intrinsic size for fit-content nodes\n * - Render phase: to compute DOM-level truncation budget before ANSI serialization\n * - Reconciler: to feed text to the Yoga measure function\n *\n * All share the same traversal logic: walk children, apply internal_transform,\n * concatenate text. The only difference is whether hidden nodes are skipped\n * (the reconciler skips them because Suspense hides the primary tree;\n * the render phases don't because hidden nodes are already laid out with 0 size).\n *\n * The ANSI-styled variants (collectTextContent in render-text.ts) and the\n * styled-segment variants (collectStyledSegments in content-phase-adapter.ts)\n * have fundamentally different output shapes and child processing logic,\n * so they remain separate.\n */\n\nimport type { AgNode } from \"@silvery/ag/types\"\n\n/**\n * Collect plain text from a node tree, applying internal_transform.\n *\n * This is the base traversal used by:\n * - measure-phase.ts (fit-content measurement)\n * - render-text.ts (DOM-level truncation budget)\n *\n * Does NOT filter hidden or display:none nodes — those are handled by\n * the layout engine (display:none gets 0x0 size) or by other phases.\n */\nexport function collectPlainText(node: AgNode): string {\n if (node.textContent !== undefined) return node.textContent\n let result = \"\"\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i]!\n let childText = collectPlainText(child)\n if (childText.length > 0 && (child.props as any).internal_transform) {\n childText = (child.props as any).internal_transform(childText, i)\n }\n result += childText\n }\n return result\n}\n\n/**\n * Collect plain text from a node tree, skipping hidden children.\n *\n * Used by the reconciler's Yoga measure function where hidden nodes\n * (from Suspense) must not contribute to measured size.\n *\n * Identical to collectPlainText except for the hidden check.\n */\nexport function collectPlainTextSkipHidden(node: AgNode): string {\n if (node.textContent !== undefined) return node.textContent\n let result = \"\"\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i]!\n if (child.hidden) continue\n let childText = collectPlainTextSkipHidden(child)\n if (childText.length > 0 && (child.props as any).internal_transform) {\n childText = (child.props as any).internal_transform(childText, i)\n }\n result += childText\n }\n return result\n}\n",
29
+ "/**\n * Shared helper functions for silvery pipeline phases.\n */\n\nimport type { BoxProps } from \"@silvery/ag/types\"\n\n/**\n * Get padding values from props.\n */\nexport function getPadding(props: BoxProps): {\n top: number\n bottom: number\n left: number\n right: number\n} {\n return {\n top: props.paddingTop ?? props.paddingY ?? props.padding ?? 0,\n bottom: props.paddingBottom ?? props.paddingY ?? props.padding ?? 0,\n left: props.paddingLeft ?? props.paddingX ?? props.padding ?? 0,\n right: props.paddingRight ?? props.paddingX ?? props.padding ?? 0,\n }\n}\n\n/**\n * Get border size (1 or 0 for each side).\n */\nexport function getBorderSize(props: BoxProps): {\n top: number\n bottom: number\n left: number\n right: number\n} {\n if (!props.borderStyle) {\n return { top: 0, bottom: 0, left: 0, right: 0 }\n }\n return {\n top: props.borderTop !== false ? 1 : 0,\n bottom: props.borderBottom !== false ? 1 : 0,\n left: props.borderLeft !== false ? 1 : 0,\n right: props.borderRight !== false ? 1 : 0,\n }\n}\n",
30
+ "/**\n * Phase 1: Measure Phase\n *\n * Handle fit-content nodes by measuring their intrinsic content size.\n */\n\nimport type { BoxProps, AgNode, TextProps } from \"@silvery/ag/types\"\nimport { displayWidthAnsi, wrapText } from \"../unicode\"\nimport { collectPlainText as collectTextContent } from \"./collect-text\"\nimport { getBorderSize, getPadding } from \"./helpers\"\nimport type { PipelineContext } from \"./types\"\n\n/**\n * Handle fit-content nodes by measuring their intrinsic content size.\n *\n * Traverses the tree and for any node with width=\"fit-content\" or\n * height=\"fit-content\", measures the content and sets the Yoga constraint.\n */\nexport function measurePhase(root: AgNode, ctx?: PipelineContext): void {\n traverseTree(root, (node) => {\n // Skip nodes without Yoga (raw text nodes)\n if (!node.layoutNode) return\n\n const props = node.props as BoxProps\n\n if (props.width === \"fit-content\" || props.height === \"fit-content\") {\n // When height=\"fit-content\" but width is fixed, pass the available width\n // so text nodes can wrap and compute correct intrinsic height.\n let availableWidth: number | undefined\n if (props.height === \"fit-content\" && props.width !== \"fit-content\" && typeof props.width === \"number\") {\n // Subtract padding and border from the fixed width to get content area width\n const padding = getPadding(props)\n availableWidth = props.width - padding.left - padding.right\n if (props.borderStyle) {\n const border = getBorderSize(props)\n availableWidth -= border.left + border.right\n }\n if (availableWidth < 1) availableWidth = 1\n }\n\n const intrinsicSize = measureIntrinsicSize(node, ctx, availableWidth)\n\n if (props.width === \"fit-content\") {\n node.layoutNode.setWidth(intrinsicSize.width)\n }\n if (props.height === \"fit-content\") {\n node.layoutNode.setHeight(intrinsicSize.height)\n }\n }\n })\n}\n\n/**\n * Measure the intrinsic size of a node's content.\n *\n * For text nodes: measures the text width and line count.\n * For box nodes: recursively measures children based on flex direction.\n *\n * @param availableWidth - When set, text nodes wrap at this width for height calculation.\n * Used when a container has fixed width + fit-content height.\n */\nfunction measureIntrinsicSize(\n node: AgNode,\n ctx?: PipelineContext,\n availableWidth?: number,\n): {\n width: number\n height: number\n} {\n const props = node.props as BoxProps\n\n // display=\"none\" nodes have 0x0 intrinsic size\n if (props.display === \"none\") {\n return { width: 0, height: 0 }\n }\n\n if (node.type === \"silvery-text\") {\n const textProps = props as TextProps\n const text = collectTextContent(node)\n\n // Apply internal_transform if present (used by Transform component).\n // The transform is applied per-line, which can change the width.\n const transform = textProps.internal_transform\n let lines: string[]\n\n if (availableWidth !== undefined && availableWidth > 0 && isWrapEnabled(textProps.wrap)) {\n // Wrap text at available width to compute correct height\n lines = ctx ? ctx.measurer.wrapText(text, availableWidth, true, true) : wrapText(text, availableWidth, true, true)\n } else {\n lines = text.split(\"\\n\")\n }\n\n if (transform) {\n lines = lines.map((line, index) => transform(line, index))\n }\n\n const width = Math.max(...lines.map((line) => getTextWidth(line, ctx)))\n return {\n width,\n height: lines.length,\n }\n }\n\n // For boxes, measure based on flex direction\n const isRow = props.flexDirection === \"row\" || props.flexDirection === \"row-reverse\"\n\n let width = 0\n let height = 0\n\n let childCount = 0\n for (const child of node.children) {\n const childSize = measureIntrinsicSize(child, ctx, availableWidth)\n childCount++\n\n if (isRow) {\n width += childSize.width\n height = Math.max(height, childSize.height)\n } else {\n width = Math.max(width, childSize.width)\n height += childSize.height\n }\n }\n\n // Add gap between children\n const gap = (props.gap as number) ?? 0\n if (gap > 0 && childCount > 1) {\n const totalGap = gap * (childCount - 1)\n if (isRow) {\n width += totalGap\n } else {\n height += totalGap\n }\n }\n\n // Add padding\n const padding = getPadding(props)\n width += padding.left + padding.right\n height += padding.top + padding.bottom\n\n // Add border\n if (props.borderStyle) {\n const border = getBorderSize(props)\n width += border.left + border.right\n height += border.top + border.bottom\n }\n\n return { width, height }\n}\n\n/**\n * Check if text wrapping is enabled for a text node.\n */\nfunction isWrapEnabled(wrap: TextProps[\"wrap\"]): boolean {\n return wrap === \"wrap\" || wrap === true || wrap === undefined\n}\n\n/**\n * Traverse tree in depth-first order.\n */\nfunction traverseTree(node: AgNode, callback: (node: AgNode) => void): void {\n callback(node)\n for (const child of node.children) {\n traverseTree(child, callback)\n }\n}\n\n/**\n * Get text display width (accounting for wide characters and ANSI codes).\n * Uses ANSI-aware width calculation to handle styled text.\n */\nfunction getTextWidth(text: string, ctx?: PipelineContext): number {\n if (ctx) return ctx.measurer.displayWidthAnsi(text)\n return displayWidthAnsi(text)\n}\n\n// collectTextContent is imported from ./collect-text as collectPlainText.\n// Previously duplicated here; now shared across measure-phase, render-text,\n// and the reconciler's measure function.\n",
31
+ "/**\n * Profiling counters for measure function performance analysis (dev only).\n *\n * Shared between @silvery/ag-react/reconciler/nodes (where measure happens)\n * and @silvery/ag-term/pipeline/layout-phase (where stats are logged).\n *\n * Lives in @silvery/ag-term to keep the @silvery/ag-term barrel React-free.\n */\n\nexport const measureStats = {\n calls: 0,\n cacheHits: 0,\n textCollects: 0,\n displayWidthCalls: 0,\n reset() {\n this.calls = 0\n this.cacheHits = 0\n this.textCollects = 0\n this.displayWidthCalls = 0\n },\n}\n",
32
+ "/**\n * Silvery Types\n *\n * Core types for the Silvery renderer architecture.\n */\n\nimport type { FocusEventProps } from \"./focus-events\"\nimport type { LayoutNode } from \"@silvery/ag-term/layout-engine\"\nimport type { MouseEventProps } from \"@silvery/ag-term/mouse-events\"\n\n// ============================================================================\n// Layout Types\n// ============================================================================\n\n/**\n * A rectangle with position and size.\n * All values are in terminal columns/rows (integers).\n */\nexport interface Rect {\n /** X position (0-indexed terminal column) */\n x: number\n /** Y position (0-indexed terminal row) */\n y: number\n /** Width in terminal columns */\n width: number\n /** Height in terminal rows */\n height: number\n}\n\n/**\n * Check if two rects are equal (same position and size).\n */\nexport function rectEqual(a: Rect | null, b: Rect | null): boolean {\n if (a === b) return true\n if (!a || !b) return false\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height\n}\n\n// ============================================================================\n// Node Types\n// ============================================================================\n\n/**\n * Silvery node types - the primitive elements in the render tree.\n */\nexport type AgNodeType = \"silvery-root\" | \"silvery-box\" | \"silvery-text\"\n\n/**\n * Flexbox properties that can be applied to Box nodes.\n */\nexport interface FlexboxProps {\n // Size\n width?: number | string\n height?: number | string\n minWidth?: number | string\n minHeight?: number | string\n maxWidth?: number | string\n maxHeight?: number | string\n\n // Flex\n flexGrow?: number\n flexShrink?: number\n flexBasis?: number | string\n flexDirection?: \"row\" | \"column\" | \"row-reverse\" | \"column-reverse\"\n flexWrap?: \"nowrap\" | \"wrap\" | \"wrap-reverse\"\n\n // Alignment\n alignItems?: \"flex-start\" | \"flex-end\" | \"center\" | \"stretch\" | \"baseline\"\n alignSelf?: \"auto\" | \"flex-start\" | \"flex-end\" | \"center\" | \"stretch\" | \"baseline\"\n alignContent?: \"flex-start\" | \"flex-end\" | \"center\" | \"stretch\" | \"space-between\" | \"space-around\" | \"space-evenly\"\n justifyContent?: \"flex-start\" | \"flex-end\" | \"center\" | \"space-between\" | \"space-around\" | \"space-evenly\"\n\n // Spacing\n padding?: number\n paddingTop?: number\n paddingBottom?: number\n paddingLeft?: number\n paddingRight?: number\n paddingX?: number\n paddingY?: number\n margin?: number\n marginTop?: number\n marginBottom?: number\n marginLeft?: number\n marginRight?: number\n marginX?: number\n marginY?: number\n gap?: number\n columnGap?: number\n rowGap?: number\n\n // Position\n position?: \"relative\" | \"absolute\" | \"sticky\" | \"static\"\n\n // Position offsets (used with position='absolute' or position='relative')\n top?: number | string\n left?: number | string\n bottom?: number | string\n right?: number | string\n\n // Sticky offsets (only used when position='sticky')\n // The element will \"stick\" when it reaches this offset from the container edge\n stickyTop?: number\n stickyBottom?: number\n\n // Aspect ratio\n aspectRatio?: number\n\n // Display\n display?: \"flex\" | \"none\"\n\n // Overflow\n overflow?: \"visible\" | \"hidden\" | \"scroll\"\n overflowX?: \"visible\" | \"hidden\"\n overflowY?: \"visible\" | \"hidden\"\n\n // Scroll control (only used when overflow='scroll')\n /** Child index to ensure visible (edge-based: only scrolls if off-screen) */\n scrollTo?: number\n /** Explicit scroll offset in rows (used when scrollTo is undefined for frozen scroll state) */\n scrollOffset?: number\n}\n\n/**\n * Props for testing and identification.\n * These props are stored in the node for DOM query access.\n */\nexport interface TestProps {\n /** Element ID for DOM queries and visual debugging */\n id?: string\n /** Test ID for querying nodes (like Playwright's data-testid) */\n testID?: string\n /** Allow arbitrary data-* attributes for testing */\n [key: `data-${string}`]: unknown\n}\n\n/**\n * Underline style variants (SGR 4:x codes).\n * - false: no underline\n * - 'single': standard underline (SGR 4 or 4:1)\n * - 'double': double underline (SGR 4:2)\n * - 'curly': curly/wavy underline (SGR 4:3)\n * - 'dotted': dotted underline (SGR 4:4)\n * - 'dashed': dashed underline (SGR 4:5)\n */\nexport type UnderlineStyle = false | \"single\" | \"double\" | \"curly\" | \"dotted\" | \"dashed\"\n\n/**\n * Style properties for text rendering.\n */\nexport interface StyleProps {\n color?: string\n backgroundColor?: string\n bold?: boolean\n dim?: boolean\n /** Alias for dim (Ink compatibility) */\n dimColor?: boolean\n italic?: boolean\n /** Enable underline. Use underlineStyle for style variants. */\n underline?: boolean\n /**\n * Underline style variant: 'single' | 'double' | 'curly' | 'dotted' | 'dashed'.\n * Setting this implies underline=true. Takes precedence over underline prop.\n */\n underlineStyle?: UnderlineStyle\n /**\n * Underline color (independent of text color).\n * Uses SGR 58 (underline color). Falls back to text color if not specified.\n */\n underlineColor?: string\n strikethrough?: boolean\n inverse?: boolean\n}\n\n/**\n * Props for Box component.\n */\nexport interface BoxProps extends FlexboxProps, StyleProps, TestProps, MouseEventProps, FocusEventProps {\n borderStyle?: \"single\" | \"double\" | \"round\" | \"bold\" | \"singleDouble\" | \"doubleSingle\" | \"classic\"\n borderColor?: string\n borderTop?: boolean\n borderBottom?: boolean\n borderLeft?: boolean\n borderRight?: boolean\n\n /**\n * Outline style — renders border characters at the box edges without affecting layout.\n *\n * Unlike `borderStyle` which adds border dimensions to the layout (making the content\n * area smaller), `outlineStyle` draws border characters that OVERLAP the content area.\n * The layout engine sees no border at all — outline is purely visual.\n *\n * Use cases: selection indicators, hover highlights, focus rings — anything that\n * should visually frame a box without shifting content.\n */\n outlineStyle?: \"single\" | \"double\" | \"round\" | \"bold\" | \"singleDouble\" | \"doubleSingle\" | \"classic\"\n /** Foreground color for the outline */\n outlineColor?: string\n /** Apply dim styling to the outline */\n outlineDimColor?: boolean\n /** Show top outline edge (default: true) */\n outlineTop?: boolean\n /** Show bottom outline edge (default: true) */\n outlineBottom?: boolean\n /** Show left outline edge (default: true) */\n outlineLeft?: boolean\n /** Show right outline edge (default: true) */\n outlineRight?: boolean\n\n /**\n * Override theme for this subtree — $token colors resolve against this theme.\n * Pushed onto the context theme stack during content phase tree walk.\n */\n theme?: import(\"@silvery/theme\").Theme\n\n /** CSS pointer-events equivalent. \"none\" makes this node and its subtree invisible to hit testing. */\n pointerEvents?: \"auto\" | \"none\"\n\n onLayout?: (layout: Rect) => void\n\n /**\n * Show scroll overflow indicators (▲N / ▼N) for scrollable containers.\n *\n * For bordered containers, indicators appear on the border.\n * For borderless containers, indicators overlay the content at top-right/bottom-right.\n *\n * Only applies when overflow='scroll'.\n */\n overflowIndicator?: boolean\n}\n\n/**\n * Props for Text component.\n */\nexport interface TextProps extends StyleProps, TestProps, MouseEventProps {\n children?: React.ReactNode\n wrap?: \"wrap\" | \"truncate\" | \"truncate-start\" | \"truncate-middle\" | \"truncate-end\" | \"clip\" | boolean\n /** Internal transform function applied to each rendered line. Used by Transform component. */\n internal_transform?: (line: string, index: number) => string\n}\n\n/**\n * The core Silvery node - represents an element in the render tree.\n *\n * Each node has:\n * - A Yoga node for layout calculation\n * - Computed layout after Yoga runs\n * - Subscribers that get notified when layout changes\n * - Dirty flags for incremental updates\n */\nexport interface AgNode {\n /** Node type */\n type: AgNodeType\n\n /** Props passed to this node */\n props: BoxProps | TextProps | Record<string, unknown>\n\n /** Child nodes */\n children: AgNode[]\n\n /** Parent node (null for root) */\n parent: AgNode | null\n\n /** The layout node for layout calculation (null for raw text nodes) */\n layoutNode: LayoutNode | null\n\n /** Computed layout from previous render (for change detection) */\n prevLayout: Rect | null\n\n /**\n * Content-relative position (like CSS offsetTop/offsetLeft).\n * Position within the scrollable content, ignoring scroll offsets.\n * Set after layout phase.\n */\n contentRect: Rect | null\n\n /**\n * Screen-relative position (like CSS getBoundingClientRect).\n * Actual position on the terminal screen, accounting for scroll offsets.\n * Set after screen rect phase.\n *\n * Note: For sticky children, this reflects the node's layout position\n * adjusted for scroll offsets, NOT the actual render position. Use\n * `renderRect` for the actual pixel position on screen.\n */\n screenRect: Rect | null\n\n /** Previous screen rect (for change detection in notifyLayoutSubscribers) */\n prevScreenRect: Rect | null\n\n /**\n * Actual render position on the terminal screen.\n * For non-sticky nodes, this equals `screenRect`.\n * For sticky nodes (position=\"sticky\"), this accounts for sticky render\n * offsets — the position where pixels are actually painted.\n *\n * Use this for hit testing, cursor positioning, and any feature that\n * needs to know where a node visually appears on screen.\n * Set after screen rect phase.\n */\n renderRect: Rect | null\n\n /** Previous render rect (for change detection) */\n prevRenderRect: Rect | null\n\n /** True if layout changed THIS frame (position or size).\n * Set by propagateLayout in layout phase. Cleared by content phase.\n * This is the authoritative signal for \"did layout change?\" — unlike\n * !rectEqual(prevLayout, contentRect) which becomes stale when layout\n * phase skips (no dirty nodes). */\n layoutChangedThisFrame: boolean\n\n /** True if layout-affecting props changed and Yoga needs recalculation.\n * Set by reconciler on prop changes. Cleared after layout phase. */\n layoutDirty: boolean\n\n /** True if content changed but layout didn't (e.g., text content update).\n * Set by reconciler. Cleared by content phase after rendering.\n * NOTE: measure phase may clear this for its text-collection cache —\n * stylePropsDirty acts as the surviving witness for style changes. */\n contentDirty: boolean\n\n /** True if visual props changed (color, backgroundColor, borderStyle, etc.).\n * Set by reconciler alongside contentDirty. Survives measure phase clearing\n * of contentDirty, ensuring content phase still detects style changes.\n * Cleared by content phase after rendering. */\n stylePropsDirty: boolean\n\n /** True if backgroundColor specifically changed (added, modified, or removed).\n * Set by reconciler when backgroundColor prop changes. Used by content phase\n * to avoid cascading re-renders for border-only paint changes (borderColor\n * doesn't affect the content area). Cleared by content phase. */\n bgDirty: boolean\n\n /** True if this node or any descendant has dirty content/layout.\n * Propagated upward by reconciler when any descendant is dirtied.\n * When only subtreeDirty (no other flags), the node's OWN rendering is\n * skipped — only descendants are traversed. Cleared by content phase. */\n subtreeDirty: boolean\n\n /** True if direct children were added, removed, or reordered.\n * Set by reconciler on child list changes. Triggers own repaint\n * (gap regions may need clearing) and forces child re-render.\n * Cleared by content phase. */\n childrenDirty: boolean\n\n /** Callbacks subscribed to layout changes (used by useContentRect) */\n layoutSubscribers: Set<() => void>\n\n /** Text content for text nodes */\n textContent?: string\n\n /** True if this is a raw text node (created by createTextInstance) */\n isRawText?: boolean\n\n /** True if this node is hidden (for Suspense support) */\n hidden?: boolean\n\n /** Sticky children with computed render positions (for non-scroll containers).\n * When a parent has sticky children but is NOT a scroll container, this array\n * holds the computed render offsets. Same shape as scrollState.stickyChildren. */\n stickyChildren?: Array<{\n /** Index of the sticky child */\n index: number\n /** Computed Y offset to render at (relative to parent content area) */\n renderOffset: number\n /** Original natural Y position (relative to parent content area) */\n naturalTop: number\n /** Height of the sticky element */\n height: number\n }>\n\n /** Inline rects for virtual text nodes (no layout node). Computed during text rendering.\n * Array for wrapped text (one rect per line fragment). Enables hit testing on nested Text. */\n inlineRects?: Array<{ x: number; y: number; width: number; height: number }> | null\n\n /** Scroll state for overflow='scroll' containers */\n scrollState?: {\n /** Current scroll offset (in terminal rows) */\n offset: number\n /** Previous scroll offset from last render (for incremental rendering) */\n prevOffset: number\n /** Total content height (all children) */\n contentHeight: number\n /** Visible height (container height minus borders/padding) */\n viewportHeight: number\n /** Index of first visible child */\n firstVisibleChild: number\n /** Index of last visible child */\n lastVisibleChild: number\n /** Previous first visible child from last render (for incremental rendering) */\n prevFirstVisibleChild: number\n /** Previous last visible child from last render (for incremental rendering) */\n prevLastVisibleChild: number\n /** Count of items hidden above viewport */\n hiddenAbove: number\n /** Count of items hidden below viewport */\n hiddenBelow: number\n /** Sticky children with their computed render positions */\n stickyChildren?: Array<{\n /** Index of the sticky child */\n index: number\n /** Computed Y offset to render at (relative to viewport, not content) */\n renderOffset: number\n /** Original natural Y position (before sticky adjustment) */\n naturalTop: number\n /** Height of the sticky element */\n height: number\n }>\n }\n}\n\n// ============================================================================\n// Terminal Buffer Types\n// ============================================================================\n\n/**\n * Text attributes that can be applied to a cell.\n */\nexport interface CellAttrs {\n bold?: boolean\n dim?: boolean\n italic?: boolean\n /** Simple underline flag (for backwards compatibility) */\n underline?: boolean\n /**\n * Underline style: 'single' | 'double' | 'curly' | 'dotted' | 'dashed'.\n * When set, takes precedence over the underline boolean.\n */\n underlineStyle?: UnderlineStyle\n strikethrough?: boolean\n inverse?: boolean\n}\n\n/**\n * A single cell in the terminal buffer.\n */\nexport interface Cell {\n /** The character (grapheme cluster) in this cell */\n char: string\n /** Foreground color (ANSI code or RGB) */\n fg: string | null\n /** Background color (ANSI code or RGB) */\n bg: string | null\n /** Text attributes */\n attrs: CellAttrs\n /** True if this is a wide character (CJK) that takes 2 cells */\n wide: boolean\n /** True if this cell is the continuation of a wide character */\n continuation: boolean\n}\n\n/**\n * Interface for the terminal buffer.\n */\nexport interface TerminalBuffer {\n readonly width: number\n readonly height: number\n getCell(x: number, y: number): Cell\n setCell(x: number, y: number, cell: Cell): void\n clear(): void\n}\n\n// ============================================================================\n// Event Types\n// ============================================================================\n\n/**\n * Keyboard event with key information and modifiers.\n */\nexport interface KeyEvent {\n type: \"key\"\n /** The key pressed (character or key name like 'ArrowUp') */\n key: string\n /** Ctrl modifier was held */\n ctrl?: boolean\n /** Meta/Alt modifier was held */\n meta?: boolean\n /** Shift modifier was held */\n shift?: boolean\n /** Alt/Option modifier was held */\n alt?: boolean\n /** Super/Cmd modifier was held. Requires Kitty protocol. */\n super?: boolean\n /** Hyper modifier was held. Requires Kitty protocol. */\n hyper?: boolean\n /** Kitty event type. Requires Kitty flag 2. */\n eventType?: \"press\" | \"repeat\" | \"release\"\n /** CapsLock is active. Kitty modifier bit 6. */\n capsLock?: boolean\n /** NumLock is active. Kitty modifier bit 7. */\n numLock?: boolean\n}\n\n/**\n * Mouse event with position and button information.\n */\nexport interface MouseEvent {\n type: \"mouse\"\n /** X position in terminal columns (0-indexed) */\n x: number\n /** Y position in terminal rows (0-indexed) */\n y: number\n /** Mouse button (0=left, 1=middle, 2=right) */\n button: number\n /** Event action */\n action: \"down\" | \"up\" | \"move\" | \"wheel\"\n /** Wheel delta for scroll events */\n delta?: number\n}\n\n/**\n * Terminal resize event.\n */\nexport interface ResizeEvent {\n type: \"resize\"\n /** New width in columns */\n width: number\n /** New height in rows */\n height: number\n}\n\n/**\n * Terminal focus event.\n */\nexport interface FocusEvent {\n type: \"focus\"\n}\n\n/**\n * Terminal blur event.\n */\nexport interface BlurEvent {\n type: \"blur\"\n}\n\n/**\n * Signal event (SIGINT, SIGTERM, etc.).\n */\nexport interface SignalEvent {\n type: \"signal\"\n /** Signal name (e.g., 'SIGINT', 'SIGTERM') */\n signal: string\n}\n\n/**\n * Custom event for extensibility.\n */\nexport interface CustomEvent {\n type: \"custom\"\n /** Event name */\n name: string\n /** Event data */\n data: unknown\n}\n\n/**\n * Union of all event types.\n *\n * Events drive the render loop in interactive mode. When events are present,\n * the render loop runs until exit() is called. When events are absent,\n * the render completes when the UI is stable.\n */\nexport type Event = KeyEvent | MouseEvent | ResizeEvent | FocusEvent | BlurEvent | SignalEvent | CustomEvent\n\n/**\n * Event source that can be subscribed to and unsubscribed from.\n */\nexport interface EventSource {\n /** Subscribe to events, returns unsubscribe function */\n subscribe(handler: (event: Event) => void): () => void\n /** Convert to async iterable */\n [Symbol.asyncIterator](): AsyncIterator<Event>\n}\n\n// ============================================================================\n// TermDef - Minimal Render Configuration\n// ============================================================================\n\n// ColorLevel is re-exported from ansi in index.ts\n// Import here for use in TermDef\nimport type { ColorLevel } from \"@silvery/ag-term/ansi\"\n\n/**\n * Minimal surface for configuring render().\n *\n * TermDef provides a simple way to configure rendering without requiring\n * a full Term instance. It's useful for:\n * - Static rendering (just width/height, no events)\n * - Testing (mock dimensions and events)\n * - Quick scripts (auto-detect everything from stdin/stdout)\n *\n * The presence of `events` (or `stdin` which auto-creates events)\n * determines the render mode:\n * - No events → static mode (render until stable)\n * - Has events → interactive mode (render until exit() called)\n *\n * @example\n * ```tsx\n * // Static render with custom width\n * const output = await render(<App />, { width: 100 })\n *\n * // Interactive with stdin/stdout\n * await render(<App />, { stdin: process.stdin, stdout: process.stdout })\n *\n * // Custom events\n * await render(<App />, { events: myEventSource })\n * ```\n */\nexport interface TermDef {\n // -------------------------------------------------------------------------\n // Output Configuration\n // -------------------------------------------------------------------------\n\n /** Output stream (used for dimensions if not specified) */\n stdout?: NodeJS.WriteStream\n\n /** Width in columns (default: stdout?.columns ?? 80) */\n width?: number\n\n /** Height in rows (default: stdout?.rows ?? 24) */\n height?: number\n\n /** Color support (true=detect, false=none, or specific level) */\n colors?: boolean | ColorLevel | null\n\n // -------------------------------------------------------------------------\n // Input Configuration\n // -------------------------------------------------------------------------\n\n /**\n * Event source for interactive mode.\n *\n * When present, render runs until exit() is called.\n * When absent, render completes when UI is stable.\n */\n events?: AsyncIterable<Event> | EventSource\n\n /**\n * Standard input stream.\n *\n * When provided (and events is not), automatically creates input events\n * from stdin, enabling interactive mode.\n */\n stdin?: NodeJS.ReadStream\n}\n\n// ============================================================================\n// Render Context Types\n// ============================================================================\n\n/**\n * Options passed to the render function.\n */\nexport interface RenderOptions {\n stdout?: NodeJS.WriteStream\n stdin?: NodeJS.ReadStream\n exitOnCtrlC?: boolean\n debug?: boolean\n}\n\n/**\n * The render instance returned by render().\n */\nexport interface RenderInstance {\n /** Re-render with new element */\n rerender: (element: React.ReactNode) => void\n /** Unmount and clean up */\n unmount: () => void\n /** Wait for render to complete */\n waitUntilExit: () => Promise<void>\n /** Clear terminal output */\n clear: () => void\n}\n",
33
+ "/**\n * Phase 2: Layout Phase\n *\n * Run Yoga layout calculation and propagate dimensions to all nodes.\n */\n\nimport { createLogger } from \"loggily\"\nimport { measureStats } from \"./measure-stats\"\nimport { type BoxProps, type AgNode, type Rect, rectEqual } from \"@silvery/ag/types\"\nimport { getBorderSize, getPadding } from \"./helpers\"\n\nconst log = createLogger(\"silvery:layout\")\n\n/**\n * Run Yoga layout calculation and propagate dimensions to all nodes.\n *\n * @param root The root SilveryNode\n * @param width Terminal width in columns\n * @param height Terminal height in rows\n */\nexport function layoutPhase(root: AgNode, width: number, height: number): void {\n // Check if dimensions changed from previous layout\n const prevLayout = root.contentRect\n const dimensionsChanged = prevLayout && (prevLayout.width !== width || prevLayout.height !== height)\n\n // Only recalculate if something changed (dirty nodes or dimensions)\n if (!dimensionsChanged && !hasLayoutDirtyNodes(root)) {\n return\n }\n\n // Run layout calculation (root always has a layoutNode)\n if (root.layoutNode) {\n const nodeCount = countNodes(root)\n measureStats.reset()\n const t0 = Date.now()\n root.layoutNode.calculateLayout(width, height)\n const elapsed = Date.now() - t0\n log.debug?.(\n `calculateLayout: ${elapsed}ms (${nodeCount} nodes) measure: calls=${measureStats.calls} hits=${measureStats.cacheHits} collects=${measureStats.textCollects} displayWidth=${measureStats.displayWidthCalls}`,\n )\n }\n\n // Propagate computed dimensions to all nodes\n propagateLayout(root, 0, 0)\n\n // NOTE: Subscribers are NOT notified here anymore.\n // They are notified in executeRender AFTER screenRectPhase completes,\n // so useScreenRectCallback can read the correct screen positions.\n}\n\n/**\n * Count total nodes in tree.\n */\nfunction countNodes(node: AgNode): number {\n let count = 1\n for (const child of node.children) {\n count += countNodes(child)\n }\n return count\n}\n\n/**\n * Check if any node in the tree has layoutDirty flag set.\n */\nfunction hasLayoutDirtyNodes(node: AgNode, path = \"root\"): boolean {\n if (node.layoutDirty) {\n const props = node.props as BoxProps\n log.debug?.(`dirty node found: ${path} (id=${props.id ?? \"?\"}, type=${node.type})`)\n return true\n }\n for (let i = 0; i < node.children.length; i++) {\n if (hasLayoutDirtyNodes(node.children[i]!, `${path}[${i}]`)) return true\n }\n return false\n}\n\n/**\n * Propagate computed layout from Yoga nodes to SilveryNodes.\n * Sets contentRect (content-relative position) on each node.\n *\n * @param node The node to process\n * @param parentX Absolute X position of parent\n * @param parentY Absolute Y position of parent\n */\nfunction propagateLayout(node: AgNode, parentX: number, parentY: number): void {\n // Save previous layout for change detection\n node.prevLayout = node.contentRect\n\n // Virtual/raw text nodes (no layoutNode) inherit parent's position\n if (!node.layoutNode) {\n const rect: Rect = {\n x: parentX,\n y: parentY,\n width: 0,\n height: 0,\n }\n node.contentRect = rect\n node.layoutDirty = false\n // Still recurse to children (virtual text nodes can have raw text children)\n for (const child of node.children) {\n propagateLayout(child, parentX, parentY)\n }\n return\n }\n\n // Compute absolute position from Yoga (content-relative)\n const rect: Rect = {\n x: parentX + node.layoutNode.getComputedLeft(),\n y: parentY + node.layoutNode.getComputedTop(),\n width: node.layoutNode.getComputedWidth(),\n height: node.layoutNode.getComputedHeight(),\n }\n node.contentRect = rect\n\n // Clear layout dirty flag\n node.layoutDirty = false\n\n // Set authoritative \"layout changed this frame\" flag.\n // Unlike !rectEqual(prevLayout, contentRect) which becomes stale when\n // layout phase skips on subsequent frames, this flag is explicitly set\n // each time propagateLayout runs and cleared by the content phase.\n node.layoutChangedThisFrame = !!(node.prevLayout && !rectEqual(node.prevLayout, node.contentRect))\n\n // STRICT invariant: if layoutChangedThisFrame is true, prevLayout must differ from contentRect.\n // This validates that the flag is consistent with the actual rect comparison. A violation\n // would mean the flag is set spuriously, causing unnecessary re-renders and cascade propagation.\n if (process.env.SILVERY_STRICT && node.layoutChangedThisFrame) {\n if (rectEqual(node.prevLayout, node.contentRect)) {\n const props = node.props as BoxProps\n throw new Error(\n `[SILVERY_STRICT] layoutChangedThisFrame=true but prevLayout equals contentRect ` +\n `(node: ${props.id ?? node.type}, rect: ${JSON.stringify(node.contentRect)})`,\n )\n }\n }\n\n // When layout changes, mark ancestors subtreeDirty so contentPhase doesn't\n // fast-path skip them. Without this, a deeply nested node whose dimensions\n // change (e.g., width 3→4) would never be re-rendered because all ancestors\n // appear clean — their own layout didn't change, just a descendant's did.\n if (node.layoutChangedThisFrame) {\n let ancestor = node.parent\n while (ancestor && !ancestor.subtreeDirty) {\n ancestor.subtreeDirty = true\n ancestor = ancestor.parent\n }\n }\n\n // Recurse to children\n for (const child of node.children) {\n propagateLayout(child, rect.x, rect.y)\n }\n}\n\n/**\n * Notify all layout subscribers of dimension changes.\n *\n * Called from executeRender AFTER screenRectPhase completes,\n * so useScreenRectCallback can read correct screen positions.\n *\n * Notifies when EITHER contentRect, screenRect, or renderRect changed.\n * screenRect can change from scroll offset changes even when\n * contentRect stays the same — subscribers (like useScreenRectCallback)\n * need notification in both cases. renderRect can change from sticky\n * offset changes even when screenRect stays the same.\n */\nexport function notifyLayoutSubscribers(node: AgNode): void {\n // Notify if content rect, screen rect, or render rect changed\n const contentChanged = !rectEqual(node.prevLayout, node.contentRect)\n const screenChanged = !rectEqual(node.prevScreenRect, node.screenRect)\n const renderChanged = !rectEqual(node.prevRenderRect, node.renderRect)\n if (contentChanged || screenChanged || renderChanged) {\n for (const subscriber of node.layoutSubscribers) {\n subscriber()\n }\n }\n\n // Recurse to children\n for (const child of node.children) {\n notifyLayoutSubscribers(child)\n }\n}\n\n// Re-export from types\nexport { rectEqual } from \"@silvery/ag/types\"\n\n// ============================================================================\n// Phase 2.5: Scroll Phase (for overflow='scroll' containers)\n// ============================================================================\n\n/**\n * Options for scrollPhase.\n */\nexport interface ScrollPhaseOptions {\n /**\n * Skip state updates (for fresh render comparisons).\n * When true, calculates scroll positions but doesn't mutate node.scrollState.\n * Default: false\n */\n skipStateUpdates?: boolean\n}\n\n/**\n * Calculate scroll state for all overflow='scroll' containers.\n *\n * This phase runs after layout to determine which children are visible\n * within each scrollable container.\n */\nexport function scrollPhase(root: AgNode, options: ScrollPhaseOptions = {}): void {\n const { skipStateUpdates = false } = options\n traverseTree(root, (node) => {\n const props = node.props as BoxProps\n if (props.overflow !== \"scroll\") return\n\n // Calculate scroll state for this container\n calculateScrollState(node, props, skipStateUpdates)\n })\n}\n\n/**\n * Calculate scroll state for a single scrollable container.\n */\nfunction calculateScrollState(node: AgNode, props: BoxProps, skipStateUpdates: boolean): void {\n const layout = node.contentRect\n if (!layout || !node.layoutNode) return\n\n // Calculate viewport (container minus borders/padding)\n const border = props.borderStyle ? getBorderSize(props) : { top: 0, bottom: 0, left: 0, right: 0 }\n const padding = getPadding(props)\n\n const rawViewportHeight = layout.height - border.top - border.bottom - padding.top - padding.bottom\n\n // Calculate total content height and child positions\n let contentHeight = 0\n const childPositions: {\n child: AgNode\n top: number\n bottom: number\n index: number\n isSticky: boolean\n stickyTop?: number\n stickyBottom?: number\n }[] = []\n\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i]!\n if (!child.layoutNode || !child.contentRect) continue\n\n const childTop = child.contentRect.y - layout.y - border.top - padding.top\n const childBottom = childTop + child.contentRect.height\n const childProps = child.props as BoxProps\n\n childPositions.push({\n child: child!,\n top: childTop,\n bottom: childBottom,\n index: i,\n isSticky: childProps.position === \"sticky\",\n stickyTop: childProps.stickyTop,\n stickyBottom: childProps.stickyBottom,\n })\n\n contentHeight = Math.max(contentHeight, childBottom)\n }\n\n const viewportHeight = rawViewportHeight\n\n // Reserve 1 row at the bottom for the overflow indicator when:\n // 1. Container uses borderless overflow indicators (overflowIndicator prop)\n // 2. Content exceeds viewport (there will be hidden items below or above)\n // This ensures the indicator doesn't overlay the last visible child's content.\n const showBorderlessIndicator = props.overflowIndicator === true && !props.borderStyle\n const hasOverflow = contentHeight > rawViewportHeight\n const indicatorReserve = showBorderlessIndicator && hasOverflow ? 1 : 0\n\n // Calculate scroll offset based on scrollTo prop\n // Use \"ensure visible\" scrolling: only scroll when target would be off-screen\n // Preserve previous offset when target is already visible\n //\n // Priority:\n // 1. If scrollTo is defined: use edge-based scrolling to ensure child is visible\n // 2. If scrollOffset is defined: use explicit offset (for frozen scroll state)\n // 3. Otherwise: use previous offset or default to 0\n const prevOffset = node.scrollState?.offset\n const explicitOffset = props.scrollOffset\n let scrollOffset = explicitOffset ?? prevOffset ?? 0\n const scrollTo = props.scrollTo\n\n if (scrollTo !== undefined && scrollTo >= 0 && scrollTo < childPositions.length) {\n // Find the target child\n const target = childPositions.find((c) => c.index === scrollTo)\n if (target) {\n // Calculate current visible range, accounting for indicator reserve.\n // The effective visible height is reduced by indicatorReserve so the\n // scrollTo target is fully visible ABOVE the overflow indicator row.\n const effectiveHeight = viewportHeight - indicatorReserve\n const visibleTop = scrollOffset\n const visibleBottom = scrollOffset + effectiveHeight\n\n // Only scroll if target is outside visible range\n if (target.top < visibleTop) {\n // Target is above viewport - scroll up to show it at top\n scrollOffset = target.top\n } else if (target.bottom > visibleBottom) {\n // Target is below viewport - scroll down to show it at bottom\n scrollOffset = target.bottom - effectiveHeight\n }\n // Otherwise, keep current scroll position (target is visible)\n\n // Clamp to valid range\n scrollOffset = Math.max(0, scrollOffset)\n scrollOffset = Math.min(scrollOffset, Math.max(0, contentHeight - viewportHeight))\n }\n }\n\n // Determine visible children.\n // When the overflow indicator reserves a row (indicatorReserve=1), reduce the\n // visible bottom by 1 so the indicator has its own row after the last visible child.\n const visibleTop = scrollOffset\n const visibleBottom = scrollOffset + viewportHeight - indicatorReserve\n\n let firstVisible = -1\n let lastVisible = -1\n let hiddenAbove = 0\n let hiddenBelow = 0\n\n for (const cp of childPositions) {\n // Sticky children are always considered \"visible\" for rendering purposes\n if (cp.isSticky) {\n if (firstVisible === -1) firstVisible = cp.index\n lastVisible = Math.max(lastVisible, cp.index)\n continue\n }\n\n // Skip zero-height children from hidden counts — they have no visual\n // presence and would produce spurious overflow indicators (e.g., a\n // zero-height child at position 0 has top=0, bottom=0, and 0 <= 0\n // would incorrectly count it as \"hidden above\").\n if (cp.top === cp.bottom) {\n continue\n }\n\n if (cp.bottom <= visibleTop) {\n hiddenAbove++\n } else if (cp.top >= visibleBottom) {\n hiddenBelow++\n } else if (cp.top < visibleTop) {\n // Child is partially visible at top — render it (clipped by scroll\n // container's clip bounds) so partial content is visible instead of blank space\n if (firstVisible === -1) firstVisible = cp.index\n lastVisible = Math.max(lastVisible, cp.index)\n } else if (cp.bottom > visibleBottom) {\n // Child is partially visible at bottom — render it (clipped by scroll\n // container's clip bounds) so partial content is visible instead of blank space.\n // When indicatorReserve is active, this child extends past the reserved row,\n // but we still render it — the overflow indicator renders AFTER children and\n // overlays the appropriate row.\n if (firstVisible === -1) firstVisible = cp.index\n lastVisible = cp.index\n // When indicator reserve is active, count partially visible bottom children\n // in hiddenBelow so the indicator shows the correct count.\n if (indicatorReserve > 0) {\n hiddenBelow++\n }\n } else {\n // This child is fully visible within the viewport\n if (firstVisible === -1) firstVisible = cp.index\n lastVisible = cp.index\n }\n }\n\n // Calculate sticky children render positions\n const stickyChildren: NonNullable<AgNode[\"scrollState\"]>[\"stickyChildren\"] = []\n\n for (const cp of childPositions) {\n if (!cp.isSticky) continue\n\n const childHeight = cp.bottom - cp.top\n const stickyTop = cp.stickyTop ?? 0\n const stickyBottom = cp.stickyBottom\n\n // Natural position: where it would be without sticking (relative to viewport)\n const naturalRenderY = cp.top - scrollOffset\n\n let renderOffset: number\n\n if (stickyBottom !== undefined) {\n // Sticky to bottom: element pins to bottom edge when scrolled past\n const bottomPinPosition = viewportHeight - stickyBottom - childHeight\n // Use natural position if it's below the pin point, otherwise pin\n renderOffset = Math.min(naturalRenderY, bottomPinPosition)\n } else if (naturalRenderY >= stickyTop) {\n // Child hasn't reached stick point: use natural position\n renderOffset = naturalRenderY\n } else if (childHeight > viewportHeight) {\n // Oversized sticky-top child scrolled past stick point: progressively\n // scroll the child so its bottom aligns with viewport bottom when\n // scrolled far enough. Clamp between bottom-align and stick point.\n renderOffset = Math.max(viewportHeight - childHeight, naturalRenderY)\n } else {\n // Normal sticky-top child scrolled past stick point: pin at stickyTop\n renderOffset = stickyTop\n }\n\n // Clamp to viewport bounds — only when element is actually sticking.\n // Elements at their natural position below the viewport must NOT be\n // pulled up into view by clamping (that would overwrite other children's\n // pixels, corrupting incremental rendering's buffer shift).\n const isSticking = renderOffset !== naturalRenderY\n if (isSticking) {\n if (childHeight > viewportHeight) {\n renderOffset = Math.max(viewportHeight - childHeight, renderOffset)\n } else {\n renderOffset = Math.max(0, Math.min(renderOffset, viewportHeight - childHeight))\n }\n }\n\n // Skip off-screen sticky children — they're not visible and shouldn't\n // be rendered (would corrupt other children's pixels in the buffer).\n if (renderOffset + childHeight <= 0 || renderOffset >= viewportHeight) continue\n\n stickyChildren.push({\n index: cp.index,\n renderOffset,\n naturalTop: cp.top,\n height: childHeight,\n })\n }\n\n // Skip state updates for fresh render comparisons (SILVERY_STRICT)\n if (skipStateUpdates) return\n\n // Track previous visible range for incremental rendering\n const prevFirstVisible = node.scrollState?.firstVisibleChild ?? firstVisible\n const prevLastVisible = node.scrollState?.lastVisibleChild ?? lastVisible\n\n // Mark node dirty if scroll offset or visible range changed (for incremental rendering)\n // Without this, contentPhase would skip the container and children would\n // remain at their old pixel positions in the cloned buffer\n if (scrollOffset !== prevOffset || firstVisible !== prevFirstVisible || lastVisible !== prevLastVisible) {\n node.subtreeDirty = true\n }\n\n // Store scroll state (preserve previous offset and visible range for incremental rendering)\n node.scrollState = {\n offset: scrollOffset,\n prevOffset: prevOffset ?? scrollOffset,\n contentHeight,\n viewportHeight,\n firstVisibleChild: firstVisible,\n lastVisibleChild: lastVisible,\n prevFirstVisibleChild: prevFirstVisible,\n prevLastVisibleChild: prevLastVisible,\n hiddenAbove,\n hiddenBelow,\n stickyChildren: stickyChildren.length > 0 ? stickyChildren : undefined,\n }\n}\n\n// ============================================================================\n// Phase 2.55: Sticky Phase (for non-scroll containers with sticky children)\n// ============================================================================\n\n/**\n * Compute sticky offsets for non-scroll containers that have sticky children.\n *\n * Scroll containers handle their own sticky logic in calculateScrollState().\n * This phase handles the remaining case: parents that are NOT overflow=\"scroll\"\n * but still contain position=\"sticky\" children with stickyBottom.\n *\n * For non-scroll containers, sticky means: pin the child to the parent's bottom\n * edge when content is shorter than the parent. When content fills the parent,\n * the child stays at its natural position.\n */\nexport function stickyPhase(root: AgNode): void {\n traverseTree(root, (node) => {\n const props = node.props as BoxProps\n // Skip scroll containers — they handle sticky in scrollPhase\n if (props.overflow === \"scroll\") return\n\n // Check if any children are sticky with stickyBottom\n let hasStickyChildren = false\n for (const child of node.children) {\n const childProps = child.props as BoxProps\n if (childProps.position === \"sticky\" && childProps.stickyBottom !== undefined) {\n hasStickyChildren = true\n break\n }\n }\n\n if (!hasStickyChildren) {\n // Clear stale data if previously had sticky children\n if (node.stickyChildren !== undefined) {\n node.stickyChildren = undefined\n node.subtreeDirty = true\n }\n return\n }\n\n const layout = node.contentRect\n if (!layout || !node.layoutNode) return\n\n const border = props.borderStyle ? getBorderSize(props) : { top: 0, bottom: 0, left: 0, right: 0 }\n const padding = getPadding(props)\n const parentContentHeight = layout.height - border.top - border.bottom - padding.top - padding.bottom\n\n const newStickyChildren: NonNullable<AgNode[\"stickyChildren\"]> = []\n\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i]!\n const childProps = child.props as BoxProps\n if (childProps.position !== \"sticky\") continue\n if (childProps.stickyBottom === undefined) continue\n\n if (!child.contentRect) continue\n\n // Natural position relative to parent content area\n const naturalY = child.contentRect.y - layout.y - border.top - padding.top\n const childHeight = child.contentRect.height\n const stickyBottom = childProps.stickyBottom\n\n // Pin position: where the child would be if pinned to parent bottom\n const bottomPin = parentContentHeight - stickyBottom - childHeight\n // Child pins to bottom when content is short (naturalY < bottomPin)\n // Stays at natural position when content fills parent (naturalY >= bottomPin)\n const renderOffset = Math.max(naturalY, bottomPin)\n\n newStickyChildren.push({\n index: i,\n renderOffset,\n naturalTop: naturalY,\n height: childHeight,\n })\n }\n\n // Compare with previous value to detect changes\n const prev = node.stickyChildren\n const next = newStickyChildren.length > 0 ? newStickyChildren : undefined\n\n const changed = !stickyChildrenEqual(prev, next)\n node.stickyChildren = next\n\n if (changed) {\n node.subtreeDirty = true\n }\n })\n}\n\n/**\n * Compare two stickyChildren arrays for equality.\n */\nfunction stickyChildrenEqual(a: AgNode[\"stickyChildren\"], b: AgNode[\"stickyChildren\"]): boolean {\n if (a === b) return true\n if (!a || !b) return false\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n const ai = a[i]!\n const bi = b[i]!\n if (\n ai.index !== bi.index ||\n ai.renderOffset !== bi.renderOffset ||\n ai.naturalTop !== bi.naturalTop ||\n ai.height !== bi.height\n ) {\n return false\n }\n }\n return true\n}\n\n/**\n * Traverse tree in depth-first order.\n */\nfunction traverseTree(node: AgNode, callback: (node: AgNode) => void): void {\n callback(node)\n for (const child of node.children) {\n traverseTree(child, callback)\n }\n}\n\n// ============================================================================\n// Phase 2.6: Screen Rect Phase\n// ============================================================================\n\n/**\n * Calculate screen-relative positions for all nodes.\n *\n * This phase runs after scroll phase to compute where each node actually\n * appears on the terminal screen, accounting for all ancestor scroll offsets.\n *\n * Also computes `renderRect` which accounts for sticky render offsets.\n * For non-sticky nodes, renderRect === screenRect. For sticky nodes,\n * renderRect reflects the actual pixel position where the node is painted.\n *\n * Screen position = content position - sum of ancestor scroll offsets\n */\nexport function screenRectPhase(root: AgNode): void {\n propagateScreenRect(root, 0)\n}\n\n/**\n * Propagate screen-relative positions through the tree.\n *\n * @param node The node to process\n * @param ancestorScrollOffset Sum of all ancestor scroll offsets\n */\nfunction propagateScreenRect(node: AgNode, ancestorScrollOffset: number): void {\n // Save previous rects for change detection in notifyLayoutSubscribers\n node.prevScreenRect = node.screenRect\n node.prevRenderRect = node.renderRect\n\n const content = node.contentRect\n if (!content) {\n node.screenRect = null\n node.renderRect = null\n for (const child of node.children) {\n propagateScreenRect(child, ancestorScrollOffset)\n }\n return\n }\n\n // Compute screen position by subtracting ancestor scroll offsets\n node.screenRect = {\n x: content.x,\n y: content.y - ancestorScrollOffset,\n width: content.width,\n height: content.height,\n }\n\n // Default: renderRect equals screenRect (overridden below for sticky nodes)\n node.renderRect = node.screenRect\n\n // If this node is a scroll container, add its offset for children\n const scrollOffset = node.scrollState?.offset ?? 0\n const childScrollOffset = ancestorScrollOffset + scrollOffset\n\n // Compute renderRect for sticky children.\n // Sticky nodes render at a computed offset instead of their layout position.\n // The offset data lives on the parent (this node) in either scrollState.stickyChildren\n // (for scroll containers) or node.stickyChildren (for non-scroll parents).\n computeStickyRenderRects(node)\n\n // Recurse to children\n for (const child of node.children) {\n propagateScreenRect(child, childScrollOffset)\n }\n}\n\n/**\n * Compute renderRect for sticky children of a node.\n *\n * For sticky children, the actual render position differs from the layout\n * position (screenRect). The renderOffset from the scroll/sticky phase\n * determines where pixels are actually painted. This function sets\n * renderRect on those children to reflect the true screen position.\n *\n * @param parent The parent node whose sticky children need renderRect computation\n */\nfunction computeStickyRenderRects(parent: AgNode): void {\n // Determine which sticky children list to use\n const stickyList = parent.scrollState?.stickyChildren ?? parent.stickyChildren\n if (!stickyList || stickyList.length === 0) return\n\n // Calculate the parent's content area origin on screen (inside border/padding)\n const parentScreenRect = parent.screenRect\n if (!parentScreenRect) return\n\n const props = parent.props as BoxProps\n const border = props.borderStyle ? getBorderSize(props) : { top: 0, bottom: 0, left: 0, right: 0 }\n const padding = getPadding(props)\n const contentOriginY = parentScreenRect.y + border.top + padding.top\n\n for (const sticky of stickyList) {\n const child = parent.children[sticky.index]\n if (!child?.screenRect) continue\n\n // renderRect has the same x, width, height as screenRect,\n // but Y is adjusted to the sticky render position\n child.renderRect = {\n x: child.screenRect.x,\n y: contentOriginY + sticky.renderOffset,\n width: child.screenRect.width,\n height: child.screenRect.height,\n }\n }\n}\n",
34
+ "/**\n * Color manipulation utilities.\n *\n * Operates in OKLCH for perceptual uniformity — hue rotations look right,\n * lightness changes feel linear, chroma is preserved.\n *\n * Currently uses simple RGB blending as a foundation. Full OKLCH conversion\n * will be added when needed for advanced derivation (shade generation,\n * palette generation from minimal input).\n */\n\n// ============================================================================\n// Hex ↔ RGB Parsing\n// ============================================================================\n\n/** Parse a hex color string to [r, g, b] (0-255). Returns null for non-hex. */\nexport function hexToRgb(hex: string): [number, number, number] | null {\n const match = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex)\n if (!match) return null\n return [parseInt(match[1]!, 16), parseInt(match[2]!, 16), parseInt(match[3]!, 16)]\n}\n\n/** Convert [r, g, b] (0-255) to hex string. */\nexport function rgbToHex(r: number, g: number, b: number): string {\n const clamp = (n: number) => Math.max(0, Math.min(255, Math.round(n)))\n return `#${clamp(r).toString(16).padStart(2, \"0\")}${clamp(g).toString(16).padStart(2, \"0\")}${clamp(b).toString(16).padStart(2, \"0\")}`.toUpperCase()\n}\n\n// ============================================================================\n// Color Manipulation\n// ============================================================================\n\n/**\n * Blend two hex colors. t=0 returns a, t=1 returns b.\n * For non-hex inputs (ANSI names), returns `a` unchanged.\n */\nexport function blend(a: string, b: string, t: number): string {\n const rgbA = hexToRgb(a)\n const rgbB = hexToRgb(b)\n if (!rgbA || !rgbB) return a\n\n return rgbToHex(\n rgbA[0] + (rgbB[0] - rgbA[0]) * t,\n rgbA[1] + (rgbB[1] - rgbA[1]) * t,\n rgbA[2] + (rgbB[2] - rgbA[2]) * t,\n )\n}\n\n/**\n * Brighten a hex color. amount=0.1 adds 10% lightness toward white.\n * For non-hex inputs (ANSI names), returns the color unchanged.\n */\nexport function brighten(color: string, amount: number): string {\n return blend(color, \"#FFFFFF\", amount)\n}\n\n/**\n * Darken a hex color. amount=0.1 adds 10% darkness toward black.\n * For non-hex inputs (ANSI names), returns the color unchanged.\n */\nexport function darken(color: string, amount: number): string {\n return blend(color, \"#000000\", amount)\n}\n\n/**\n * Pick black or white text for readability on the given background.\n * Uses relative luminance (WCAG formula).\n */\nexport function contrastFg(bg: string): \"#000000\" | \"#FFFFFF\" {\n const rgb = hexToRgb(bg)\n if (!rgb) return \"#FFFFFF\" // default to white for non-hex\n\n // Relative luminance per WCAG 2.0\n const [r, g, b] = rgb.map((c) => {\n const s = c / 255\n return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4)\n })\n const luminance = 0.2126 * r! + 0.7152 * g! + 0.0722 * b!\n return luminance > 0.179 ? \"#000000\" : \"#FFFFFF\"\n}\n\n// ============================================================================\n// HSL Utilities\n// ============================================================================\n\nexport type HSL = [number, number, number] // [h: 0-360, s: 0-1, l: 0-1]\n\nexport function rgbToHsl(r: number, g: number, b: number): HSL {\n r /= 255\n g /= 255\n b /= 255\n const max = Math.max(r, g, b),\n min = Math.min(r, g, b)\n const l = (max + min) / 2\n if (max === min) return [0, 0, l]\n const d = max - min\n const s = l > 0.5 ? d / (2 - max - min) : d / (max + min)\n let h = 0\n if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6\n else if (max === g) h = ((b - r) / d + 2) / 6\n else h = ((r - g) / d + 4) / 6\n return [h * 360, s, l]\n}\n\nexport function hslToHex(h: number, s: number, l: number): string {\n h = ((h % 360) + 360) % 360\n const a = s * Math.min(l, 1 - l)\n const f = (n: number) => {\n const k = (n + h / 30) % 12\n return l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1)\n }\n return rgbToHex(f(0) * 255, f(8) * 255, f(4) * 255)\n}\n\nexport function hexToHsl(hex: string): HSL | null {\n const rgb = hexToRgb(hex)\n if (!rgb) return null\n return rgbToHsl(rgb[0], rgb[1], rgb[2])\n}\n\n/**\n * Desaturate a hex color by reducing saturation.\n * amount=0.4 reduces saturation by 40%.\n * For non-hex inputs, returns the color unchanged.\n */\nexport function desaturate(color: string, amount: number): string {\n const hsl = hexToHsl(color)\n if (!hsl) return color\n const [h, s, l] = hsl\n return hslToHex(h, s * (1 - amount), l)\n}\n\n/**\n * Get the complementary color (180° hue rotation).\n * For non-hex inputs, returns the color unchanged.\n */\nexport function complement(color: string): string {\n const hsl = hexToHsl(color)\n if (!hsl) return color\n const [h, s, l] = hsl\n return hslToHex(h + 180, s, l)\n}\n",
35
+ "/**\n * WCAG 2.1 contrast checking and enforcement.\n *\n * - checkContrast(): measure the ratio between two colors\n * - ensureContrast(): adjust a color until it meets a target ratio\n *\n * Uses the relative luminance formula from WCAG 2.1.\n */\n\nimport { hexToRgb, hexToHsl, hslToHex, contrastFg } from \"./color\"\n\n/** Result of a contrast check between two colors. */\nexport interface ContrastResult {\n /** The contrast ratio (1:1 to 21:1), expressed as a single number (e.g. 4.5). */\n ratio: number\n /** Whether the ratio meets WCAG AA for normal text (>= 4.5:1). */\n aa: boolean\n /** Whether the ratio meets WCAG AAA for normal text (>= 7:1). */\n aaa: boolean\n}\n\n/**\n * Compute relative luminance of an sRGB color channel value (0-255).\n * Per WCAG 2.1: linearize, then weight by standard coefficients.\n */\nfunction channelLuminance(c: number): number {\n const s = c / 255\n return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4)\n}\n\n/**\n * Compute relative luminance of a hex color.\n * Returns a value between 0 (darkest) and 1 (lightest).\n */\nfunction relativeLuminance(hex: string): number | null {\n const rgb = hexToRgb(hex)\n if (!rgb) return null\n return 0.2126 * channelLuminance(rgb[0]) + 0.7152 * channelLuminance(rgb[1]) + 0.0722 * channelLuminance(rgb[2])\n}\n\n/**\n * Check contrast ratio between foreground and background colors.\n *\n * Uses the WCAG 2.1 relative luminance formula to compute the contrast\n * ratio and check AA (>= 4.5:1) and AAA (>= 7:1) compliance for normal text.\n *\n * @param fg - Foreground hex color (e.g. \"#FFFFFF\")\n * @param bg - Background hex color (e.g. \"#000000\")\n * @returns Contrast ratio and AA/AAA pass/fail, or null if colors are not valid hex\n *\n * @example\n * ```typescript\n * const result = checkContrast(\"#FFFFFF\", \"#000000\")\n * // { ratio: 21, aa: true, aaa: true }\n *\n * const poor = checkContrast(\"#777777\", \"#888888\")\n * // { ratio: ~1.3, aa: false, aaa: false }\n * ```\n */\nexport function checkContrast(fg: string, bg: string): ContrastResult | null {\n const fgLum = relativeLuminance(fg)\n const bgLum = relativeLuminance(bg)\n if (fgLum === null || bgLum === null) return null\n\n const lighter = Math.max(fgLum, bgLum)\n const darker = Math.min(fgLum, bgLum)\n const ratio = (lighter + 0.05) / (darker + 0.05)\n\n return {\n ratio, // exact — callers round for display if needed\n aa: ratio >= 4.5,\n aaa: ratio >= 7,\n }\n}\n\n/**\n * Adjust a color's lightness until it meets a minimum contrast ratio\n * against a reference color. Preserves hue and saturation — only\n * lightness is shifted, and only as much as needed.\n *\n * Returns the original color unchanged if it already meets the target.\n *\n * @param color - The color to adjust (hex)\n * @param against - The reference background color (hex)\n * @param minRatio - Minimum contrast ratio to achieve (e.g. 4.5 for AA)\n * @returns Adjusted hex color meeting the target, or original if already OK\n *\n * For impossible targets (e.g. 21:1 against mid-gray), returns the\n * best achievable color (near-black or near-white in the same hue).\n *\n * @example\n * ```typescript\n * // Yellow on white — too low contrast, gets darkened\n * ensureContrast(\"#FFAB91\", \"#FFFFFF\", 4.5) // → \"#B35600\" (darker orange)\n *\n * // Blue on dark bg — already fine, returned unchanged\n * ensureContrast(\"#5C9FFF\", \"#1A1A2E\", 4.5) // → \"#5C9FFF\"\n * ```\n */\nexport function ensureContrast(color: string, against: string, minRatio: number): string {\n const current = checkContrast(color, against)\n if (!current) return color // non-hex input — return unchanged\n if (current.ratio >= minRatio) return color\n\n const hsl = hexToHsl(color)\n if (!hsl) return color\n const [h, s] = hsl\n\n // Light bg → darken (decrease L), dark bg → lighten (increase L)\n const lightBg = contrastFg(against) === \"#000000\"\n\n // Binary search for the minimum lightness shift that achieves the target.\n // lo/hi bracket the L range to search within.\n let lo: number, hi: number\n if (lightBg) {\n lo = 0 // maximum darkening\n hi = hsl[2] // current lightness\n } else {\n lo = hsl[2] // current lightness\n hi = 1 // maximum lightening\n }\n\n for (let i = 0; i < 20; i++) {\n const mid = (lo + hi) / 2\n const candidate = hslToHex(h, s, mid)\n const r = checkContrast(candidate, against)\n if (!r) break\n if (lightBg) {\n // Lower L = more contrast. Find highest L that still passes.\n if (r.ratio >= minRatio) lo = mid\n else hi = mid\n } else {\n // Higher L = more contrast. Find lowest L that still passes.\n if (r.ratio >= minRatio) hi = mid\n else lo = mid\n }\n }\n\n return hslToHex(h, s, lightBg ? lo : hi)\n}\n",
36
+ "/**\n * Theme derivation — transforms a ColorPalette into a Theme.\n *\n * All inputs ultimately flow through deriveTheme():\n * ColorPalette (22) → deriveTheme() → Theme (33)\n *\n * Supports two modes:\n * - truecolor (default): rich derivation with blends, contrast pairing\n * - ansi16: direct aliases into the 22 palette colors (no blending)\n *\n * ## Contrast-aware derivation\n *\n * Fixed blend factors (e.g. \"40% toward background\") produce wildly different\n * contrast ratios across themes because palettes have different fg/bg luminance\n * ranges. A 40% blend that gives 5:1 on Nord gives 2.2:1 on Tokyo Night Day.\n *\n * Instead of fixed blend factors, derived tokens use contrast targets:\n *\n * | Token | Target | Rationale |\n * |-------------------|--------|-------------------------------------|\n * | fg / surfaces | 4.5:1 | Body text on all surface backgrounds|\n * | muted / bg | 4.5:1 | Secondary text, WCAG AA |\n * | disabled-fg / bg | 3.0:1 | Intentionally dim but visible |\n * | border / bg | 1.5:1 | Faint structural divider |\n * | inputborder / bg | 3.0:1 | WCAG 1.4.11 non-text minimum |\n * | accent as text | 4.5:1 | Colored text on root background |\n * | selection pair | 4.5:1 | Selected text readable |\n * | cursor pair | 4.5:1 | Text under cursor readable |\n *\n * The blend-first-then-ensure pattern preserves the palette's aesthetic:\n * the initial blend sets the color's character, ensureContrast only\n * adjusts lightness (preserving hue/saturation) if the ratio falls short.\n */\n\nimport { blend, contrastFg, desaturate, complement } from \"./color\"\nimport { checkContrast, ensureContrast } from \"./contrast\"\nimport type { ColorPalette, Theme } from \"./types\"\n\n/** A single contrast adjustment made during theme derivation. */\nexport interface ThemeAdjustment {\n /** Token name (e.g. \"primary\", \"muted\", \"fg\") */\n token: string\n /** Original color before adjustment */\n from: string\n /** Adjusted color */\n to: string\n /** Background color used for contrast check */\n against: string\n /** Target contrast ratio */\n target: number\n /** Contrast ratio before and after adjustment */\n ratioBefore: number\n ratioAfter: number\n}\n\n/**\n * Derive a complete Theme from a ColorPalette.\n *\n * The palette provides 22 terminal colors. This function maps them to\n * 33 semantic tokens + a 16-color content palette.\n *\n * @param palette - The 22-color terminal palette\n * @param mode - \"truecolor\" (default) for rich derivation, \"ansi16\" for direct aliases\n * @param adjustments - Optional array to collect contrast adjustments made during derivation\n */\nexport function deriveTheme(\n palette: ColorPalette,\n mode: \"ansi16\" | \"truecolor\" = \"truecolor\",\n adjustments?: ThemeAdjustment[],\n): Theme {\n if (mode === \"ansi16\") return deriveAnsi16Theme(palette)\n return deriveTruecolorTheme(palette, adjustments)\n}\n\n// ── Contrast targets ────────────────────────────────────────────────\n// These are minimums — most themes exceed them without adjustment.\n// ensureContrast() is a no-op when the target is already met.\n\n/** WCAG AA for normal text — secondary/muted text, accent-as-text */\nconst AA = 4.5\n/** Reduced contrast for intentionally dim UI — disabled text */\nconst DIM = 3.0\n/** Faint structural element — borders, dividers */\nconst FAINT = 1.5\n/** WCAG 1.4.11 non-text minimum — interactive control boundaries */\nconst CONTROL = 3.0\n\nfunction deriveTruecolorTheme(p: ColorPalette, adjustments?: ThemeAdjustment[]): Theme {\n const dark = p.dark ?? true\n const bg = p.background\n\n /** ensureContrast with optional adjustment tracking */\n function ensure(token: string, color: string, against: string, target: number): string {\n const result = ensureContrast(color, against, target)\n if (adjustments && result !== color) {\n const before = checkContrast(color, against)\n const after = checkContrast(result, against)\n adjustments.push({\n token,\n from: color,\n to: result,\n against,\n target,\n ratioBefore: before?.ratio ?? 0,\n ratioAfter: after?.ratio ?? 0,\n })\n }\n return result\n }\n\n // ── Body text — ensure readability on all surfaces ──────────────\n // Surface backgrounds blend bg toward fg by 4-8%. popoverbg (8%) is the\n // hardest case — if fg meets AA there, it meets AA on all surfaces.\n const surfacebg = blend(bg, p.foreground, 0.05)\n const popoverbg = blend(bg, p.foreground, 0.08)\n const fg = ensure(\"fg\", p.foreground, popoverbg, AA)\n\n // ── Accent colors — ensure readability as text on root bg ────────\n // Use explicit primary seed if provided, else infer from ANSI slots.\n const primary = ensure(\"primary\", p.primary ?? (dark ? p.yellow : p.blue), bg, AA)\n const accent = ensure(\"accent\", complement(primary), bg, AA)\n const secondary = ensure(\"secondary\", blend(primary, accent, 0.35), bg, AA)\n const error = ensure(\"error\", p.red, bg, AA)\n const warning = ensure(\"warning\", p.yellow, bg, AA)\n const success = ensure(\"success\", p.green, bg, AA)\n const info = ensure(\"info\", blend(fg, accent, 0.5), bg, AA)\n const link = ensure(\"link\", dark ? p.brightBlue : p.blue, bg, AA)\n\n // ── Blended tokens — blend first, then ensure contrast ───────────\n // Muted targets mutedbg (the harder case) so it passes on both bg and mutedbg.\n const mutedbg = blend(bg, p.foreground, 0.04)\n const muted = ensure(\"muted\", blend(fg, bg, 0.4), mutedbg, AA)\n const disabledfg = ensure(\"disabledfg\", blend(fg, bg, 0.5), bg, DIM)\n const border = ensure(\"border\", blend(bg, p.foreground, 0.15), bg, FAINT)\n const inputborder = ensure(\"inputborder\", blend(bg, p.foreground, 0.25), bg, CONTROL)\n\n // ── Selection & cursor — ensure pairs are readable ─────────────\n const selection = ensure(\"selection\", p.selectionForeground, p.selectionBackground, AA)\n const cursor = ensure(\"cursor\", p.cursorText, p.cursorColor, AA)\n\n return {\n name: p.name ?? (dark ? \"derived-dark\" : \"derived-light\"),\n\n // ── Root pair ─────────────────────────────────────────────────\n bg,\n fg,\n\n // ── Surface pairs (base = text, *bg = background) ──────────\n muted,\n mutedbg,\n surface: fg,\n surfacebg,\n popover: fg,\n popoverbg,\n inverse: contrastFg(blend(fg, bg, 0.1)),\n inversebg: blend(fg, bg, 0.1),\n cursor,\n cursorbg: p.cursorColor,\n selection,\n selectionbg: p.selectionBackground,\n\n // ── Accent pairs (base = area bg, *fg = text on area) ──────\n primary,\n primaryfg: contrastFg(primary),\n secondary,\n secondaryfg: contrastFg(secondary),\n accent,\n accentfg: contrastFg(accent),\n error,\n errorfg: contrastFg(error),\n warning,\n warningfg: contrastFg(warning),\n success,\n successfg: contrastFg(success),\n info,\n infofg: contrastFg(info),\n\n // ── Standalone ───────────────────────────────────────────────\n border,\n inputborder,\n focusborder: link,\n link,\n disabledfg,\n\n // ── 16 palette passthrough ───────────────────────────────────\n palette: [\n p.black,\n p.red,\n p.green,\n p.yellow,\n p.blue,\n p.magenta,\n p.cyan,\n p.white,\n p.brightBlack,\n p.brightRed,\n p.brightGreen,\n p.brightYellow,\n p.brightBlue,\n p.brightMagenta,\n p.brightCyan,\n p.brightWhite,\n ],\n }\n}\n\nfunction deriveAnsi16Theme(p: ColorPalette): Theme {\n const dark = p.dark ?? true\n const primaryColor = dark ? p.yellow : p.blue\n\n return {\n name: p.name ?? (dark ? \"derived-ansi16-dark\" : \"derived-ansi16-light\"),\n\n // ── Root pair ─────────────────────────────────────────────────\n bg: p.background,\n fg: p.foreground,\n\n // ── Surface pairs (base = text, *bg = background) ──────────\n muted: p.white,\n mutedbg: p.black,\n surface: p.foreground,\n surfacebg: p.black,\n popover: p.foreground,\n popoverbg: p.black,\n inverse: p.black,\n inversebg: p.brightWhite,\n cursor: p.cursorText,\n cursorbg: p.cursorColor,\n selection: p.selectionForeground,\n selectionbg: p.selectionBackground,\n\n // ── Accent pairs (base = area bg, *fg = text on area) ──────\n primary: primaryColor,\n primaryfg: p.black,\n secondary: p.magenta,\n secondaryfg: p.black,\n accent: p.cyan,\n accentfg: p.black,\n error: dark ? p.brightRed : p.red,\n errorfg: p.black,\n warning: p.yellow,\n warningfg: p.black,\n success: dark ? p.brightGreen : p.green,\n successfg: p.black,\n info: p.cyan,\n infofg: p.black,\n\n // ── Standalone ───────────────────────────────────────────────\n border: p.brightBlack,\n inputborder: p.brightBlack,\n focusborder: dark ? p.brightBlue : p.blue,\n link: dark ? p.brightBlue : p.blue,\n disabledfg: p.brightBlack,\n\n // ── 16 palette passthrough ───────────────────────────────────\n palette: [\n p.black,\n p.red,\n p.green,\n p.yellow,\n p.blue,\n p.magenta,\n p.cyan,\n p.white,\n p.brightBlack,\n p.brightRed,\n p.brightGreen,\n p.brightYellow,\n p.brightBlue,\n p.brightMagenta,\n p.brightCyan,\n p.brightWhite,\n ],\n }\n}\n",
37
+ "/**\n * Catppuccin palettes — the most popular 4-flavor theme system.\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Catppuccin Mocha — the classic dark variant. */\nexport const catppuccinMocha: ColorPalette = {\n name: \"catppuccin-mocha\",\n dark: true,\n black: \"#11111B\",\n red: \"#F38BA8\",\n green: \"#A6E3A1\",\n yellow: \"#F9E2AF\",\n blue: \"#89B4FA\",\n magenta: \"#CBA6F7\",\n cyan: \"#94E2D5\",\n white: \"#A6ADC8\",\n brightBlack: \"#313244\",\n brightRed: \"#FAB387\",\n brightGreen: brighten(\"#A6E3A1\", 0.15),\n brightYellow: brighten(\"#F9E2AF\", 0.15),\n brightBlue: brighten(\"#89B4FA\", 0.15),\n brightMagenta: \"#F5C2E7\",\n brightCyan: brighten(\"#94E2D5\", 0.15),\n brightWhite: \"#CDD6F4\",\n foreground: \"#CDD6F4\",\n background: \"#1E1E2E\",\n cursorColor: \"#CDD6F4\",\n cursorText: \"#1E1E2E\",\n selectionBackground: \"#6C7086\",\n selectionForeground: \"#CDD6F4\",\n}\n\n/** Catppuccin Frappe — muted dark variant. */\nexport const catppuccinFrappe: ColorPalette = {\n name: \"catppuccin-frappe\",\n dark: true,\n black: \"#232634\",\n red: \"#E78284\",\n green: \"#A6D189\",\n yellow: \"#E5C890\",\n blue: \"#8CAAEE\",\n magenta: \"#CA9EE6\",\n cyan: \"#81C8BE\",\n white: \"#A5ADCE\",\n brightBlack: \"#414559\",\n brightRed: \"#EF9F76\",\n brightGreen: brighten(\"#A6D189\", 0.15),\n brightYellow: brighten(\"#E5C890\", 0.15),\n brightBlue: brighten(\"#8CAAEE\", 0.15),\n brightMagenta: \"#F4B8E4\",\n brightCyan: brighten(\"#81C8BE\", 0.15),\n brightWhite: \"#C6D0F5\",\n foreground: \"#C6D0F5\",\n background: \"#303446\",\n cursorColor: \"#C6D0F5\",\n cursorText: \"#303446\",\n selectionBackground: \"#737994\",\n selectionForeground: \"#C6D0F5\",\n}\n\n/** Catppuccin Macchiato — warm dark variant. */\nexport const catppuccinMacchiato: ColorPalette = {\n name: \"catppuccin-macchiato\",\n dark: true,\n black: \"#181926\",\n red: \"#ED8796\",\n green: \"#A6DA95\",\n yellow: \"#EED49F\",\n blue: \"#8AADF4\",\n magenta: \"#C6A0F6\",\n cyan: \"#8BD5CA\",\n white: \"#A5ADCB\",\n brightBlack: \"#363A4F\",\n brightRed: \"#F5A97F\",\n brightGreen: brighten(\"#A6DA95\", 0.15),\n brightYellow: brighten(\"#EED49F\", 0.15),\n brightBlue: brighten(\"#8AADF4\", 0.15),\n brightMagenta: \"#F5BDE6\",\n brightCyan: brighten(\"#8BD5CA\", 0.15),\n brightWhite: \"#CAD3F5\",\n foreground: \"#CAD3F5\",\n background: \"#24273A\",\n cursorColor: \"#CAD3F5\",\n cursorText: \"#24273A\",\n selectionBackground: \"#6E738D\",\n selectionForeground: \"#CAD3F5\",\n}\n\n/** Catppuccin Latte — the light variant. */\nexport const catppuccinLatte: ColorPalette = {\n name: \"catppuccin-latte\",\n dark: false,\n black: \"#DCE0E8\",\n red: \"#D20F39\",\n green: \"#40A02B\",\n yellow: \"#DF8E1D\",\n blue: \"#1E66F5\",\n magenta: \"#8839EF\",\n cyan: \"#179299\",\n white: \"#6C6F85\",\n brightBlack: \"#CCD0DA\",\n brightRed: \"#FE640B\",\n brightGreen: brighten(\"#40A02B\", 0.15),\n brightYellow: brighten(\"#DF8E1D\", 0.15),\n brightBlue: brighten(\"#1E66F5\", 0.15),\n brightMagenta: \"#EA76CB\",\n brightCyan: brighten(\"#179299\", 0.15),\n brightWhite: \"#4C4F69\",\n foreground: \"#4C4F69\",\n background: \"#EFF1F5\",\n cursorColor: \"#4C4F69\",\n cursorText: \"#EFF1F5\",\n selectionBackground: \"#9CA0B0\",\n selectionForeground: \"#4C4F69\",\n}\n",
38
+ "/**\n * Nord palette — Arctic, north-bluish clean.\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Nord — the classic dark arctic theme. */\nexport const nord: ColorPalette = {\n name: \"nord\",\n dark: true,\n black: \"#2E3440\",\n red: \"#BF616A\",\n green: \"#A3BE8C\",\n yellow: \"#EBCB8B\",\n blue: \"#5E81AC\",\n magenta: \"#B48EAD\",\n cyan: \"#8FBCBB\",\n white: \"#D8DEE9\",\n brightBlack: \"#3B4252\",\n brightRed: \"#D08770\",\n brightGreen: brighten(\"#A3BE8C\", 0.15),\n brightYellow: brighten(\"#EBCB8B\", 0.15),\n brightBlue: brighten(\"#5E81AC\", 0.15),\n brightMagenta: \"#B48EAD\",\n brightCyan: brighten(\"#8FBCBB\", 0.15),\n brightWhite: \"#ECEFF4\",\n foreground: \"#ECEFF4\",\n background: \"#2E3440\",\n cursorColor: \"#ECEFF4\",\n cursorText: \"#2E3440\",\n selectionBackground: \"#4C566A\",\n selectionForeground: \"#ECEFF4\",\n}\n",
39
+ "/**\n * Dracula palette — dark theme with vibrant accents.\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Dracula — vibrant dark theme. */\nexport const dracula: ColorPalette = {\n name: \"dracula\",\n dark: true,\n black: \"#21222C\",\n red: \"#FF5555\",\n green: \"#50FA7B\",\n yellow: \"#F1FA8C\",\n blue: \"#BD93F9\",\n magenta: \"#BD93F9\",\n cyan: \"#8BE9FD\",\n white: \"#6272A4\",\n brightBlack: \"#44475A\",\n brightRed: \"#FFB86C\",\n brightGreen: brighten(\"#50FA7B\", 0.15),\n brightYellow: brighten(\"#F1FA8C\", 0.15),\n brightBlue: brighten(\"#BD93F9\", 0.15),\n brightMagenta: \"#FF79C6\",\n brightCyan: brighten(\"#8BE9FD\", 0.15),\n brightWhite: \"#F8F8F2\",\n foreground: \"#F8F8F2\",\n background: \"#282A36\",\n cursorColor: \"#F8F8F2\",\n cursorText: \"#282A36\",\n selectionBackground: \"#6272A4\",\n selectionForeground: \"#F8F8F2\",\n}\n",
40
+ "/**\n * Solarized palettes — precision colors for machines and people.\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Solarized Dark — Ethan Schoonover's classic dark variant. */\nexport const solarizedDark: ColorPalette = {\n name: \"solarized-dark\",\n dark: true,\n black: \"#002B36\",\n red: \"#DC322F\",\n green: \"#859900\",\n yellow: \"#B58900\",\n blue: \"#268BD2\",\n magenta: \"#6C71C4\",\n cyan: \"#2AA198\",\n white: \"#839496\",\n brightBlack: \"#586E75\",\n brightRed: \"#CB4B16\",\n brightGreen: brighten(\"#859900\", 0.15),\n brightYellow: brighten(\"#B58900\", 0.15),\n brightBlue: brighten(\"#268BD2\", 0.15),\n brightMagenta: \"#D33682\",\n brightCyan: brighten(\"#2AA198\", 0.15),\n brightWhite: \"#FDF6E3\",\n foreground: \"#FDF6E3\",\n background: \"#073642\",\n cursorColor: \"#FDF6E3\",\n cursorText: \"#073642\",\n selectionBackground: \"#657B83\",\n selectionForeground: \"#FDF6E3\",\n}\n\n/** Solarized Light — Ethan Schoonover's classic light variant. */\nexport const solarizedLight: ColorPalette = {\n name: \"solarized-light\",\n dark: false,\n black: \"#FDF6E3\",\n red: \"#DC322F\",\n green: \"#859900\",\n yellow: \"#B58900\",\n blue: \"#268BD2\",\n magenta: \"#6C71C4\",\n cyan: \"#2AA198\",\n white: \"#657B83\",\n brightBlack: \"#DDD6C1\",\n brightRed: \"#CB4B16\",\n brightGreen: brighten(\"#859900\", 0.15),\n brightYellow: brighten(\"#B58900\", 0.15),\n brightBlue: brighten(\"#268BD2\", 0.15),\n brightMagenta: \"#D33682\",\n brightCyan: brighten(\"#2AA198\", 0.15),\n brightWhite: \"#073642\",\n foreground: \"#073642\",\n background: \"#EEE8D5\",\n cursorColor: \"#073642\",\n cursorText: \"#EEE8D5\",\n selectionBackground: \"#93A1A1\",\n selectionForeground: \"#073642\",\n}\n",
41
+ "/**\n * Tokyo Night palette — a clean dark theme inspired by Tokyo city lights.\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Tokyo Night — the default dark variant. */\nexport const tokyoNight: ColorPalette = {\n name: \"tokyo-night\",\n dark: true,\n black: \"#1A1B26\",\n red: \"#F7768E\",\n green: \"#9ECE6A\",\n yellow: \"#E0AF68\",\n blue: \"#7AA2F7\",\n magenta: \"#BB9AF7\",\n cyan: \"#73DACA\",\n white: \"#A9B1D6\",\n brightBlack: \"#292E42\",\n brightRed: \"#FF9E64\",\n brightGreen: brighten(\"#9ECE6A\", 0.15),\n brightYellow: brighten(\"#E0AF68\", 0.15),\n brightBlue: brighten(\"#7AA2F7\", 0.15),\n brightMagenta: \"#FF007C\",\n brightCyan: brighten(\"#73DACA\", 0.15),\n brightWhite: \"#C0CAF5\",\n foreground: \"#C0CAF5\",\n background: \"#24283B\",\n cursorColor: \"#C0CAF5\",\n cursorText: \"#24283B\",\n selectionBackground: \"#545C7E\",\n selectionForeground: \"#C0CAF5\",\n}\n\n/** Tokyo Night Storm — slightly lighter background. */\nexport const tokyoNightStorm: ColorPalette = {\n name: \"tokyo-night-storm\",\n dark: true,\n black: \"#1F2335\",\n red: \"#F7768E\",\n green: \"#9ECE6A\",\n yellow: \"#E0AF68\",\n blue: \"#7AA2F7\",\n magenta: \"#BB9AF7\",\n cyan: \"#73DACA\",\n white: \"#A9B1D6\",\n brightBlack: \"#292E42\",\n brightRed: \"#FF9E64\",\n brightGreen: brighten(\"#9ECE6A\", 0.15),\n brightYellow: brighten(\"#E0AF68\", 0.15),\n brightBlue: brighten(\"#7AA2F7\", 0.15),\n brightMagenta: \"#FF007C\",\n brightCyan: brighten(\"#73DACA\", 0.15),\n brightWhite: \"#C0CAF5\",\n foreground: \"#C0CAF5\",\n background: \"#24283B\",\n cursorColor: \"#C0CAF5\",\n cursorText: \"#24283B\",\n selectionBackground: \"#545C7E\",\n selectionForeground: \"#C0CAF5\",\n}\n\n/** Tokyo Night Day — the light variant. */\nexport const tokyoNightDay: ColorPalette = {\n name: \"tokyo-night-day\",\n dark: false,\n black: \"#E1E2E7\",\n red: \"#F52A65\",\n green: \"#587539\",\n yellow: \"#8C6C3E\",\n blue: \"#2E7DE9\",\n magenta: \"#9854F1\",\n cyan: \"#118C74\",\n white: \"#6172B0\",\n brightBlack: \"#C4C5CB\",\n brightRed: \"#B15C00\",\n brightGreen: brighten(\"#587539\", 0.15),\n brightYellow: brighten(\"#8C6C3E\", 0.15),\n brightBlue: brighten(\"#2E7DE9\", 0.15),\n brightMagenta: \"#F52A65\",\n brightCyan: brighten(\"#118C74\", 0.15),\n brightWhite: \"#3760BF\",\n foreground: \"#3760BF\",\n background: \"#D5D6DB\",\n cursorColor: \"#3760BF\",\n cursorText: \"#D5D6DB\",\n selectionBackground: \"#9699A3\",\n selectionForeground: \"#3760BF\",\n}\n",
42
+ "/**\n * One Dark palette — Atom's iconic dark theme.\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** One Dark — the classic Atom editor theme. */\nexport const oneDark: ColorPalette = {\n name: \"one-dark\",\n dark: true,\n black: \"#21252B\",\n red: \"#E06C75\",\n green: \"#98C379\",\n yellow: \"#E5C07B\",\n blue: \"#61AFEF\",\n magenta: \"#C678DD\",\n cyan: \"#56B6C2\",\n white: \"#ABB2BF\",\n brightBlack: \"#2C313A\",\n brightRed: \"#D19A66\",\n brightGreen: brighten(\"#98C379\", 0.15),\n brightYellow: brighten(\"#E5C07B\", 0.15),\n brightBlue: brighten(\"#61AFEF\", 0.15),\n brightMagenta: \"#E06C75\",\n brightCyan: brighten(\"#56B6C2\", 0.15),\n brightWhite: \"#ABB2BF\",\n foreground: \"#ABB2BF\",\n background: \"#282C34\",\n cursorColor: \"#ABB2BF\",\n cursorText: \"#282C34\",\n selectionBackground: \"#5C6370\",\n selectionForeground: \"#ABB2BF\",\n}\n",
43
+ "/**\n * Gruvbox palettes — retro groove color scheme.\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Gruvbox Dark — warm retro dark theme. */\nexport const gruvboxDark: ColorPalette = {\n name: \"gruvbox-dark\",\n dark: true,\n black: \"#1D2021\",\n red: \"#FB4934\",\n green: \"#B8BB26\",\n yellow: \"#FABD2F\",\n blue: \"#83A598\",\n magenta: \"#D3869B\",\n cyan: \"#8EC07C\",\n white: \"#BDAE93\",\n brightBlack: \"#3C3836\",\n brightRed: \"#FE8019\",\n brightGreen: brighten(\"#B8BB26\", 0.15),\n brightYellow: brighten(\"#FABD2F\", 0.15),\n brightBlue: brighten(\"#83A598\", 0.15),\n brightMagenta: \"#D3869B\",\n brightCyan: brighten(\"#8EC07C\", 0.15),\n brightWhite: \"#EBDBB2\",\n foreground: \"#EBDBB2\",\n background: \"#282828\",\n cursorColor: \"#EBDBB2\",\n cursorText: \"#282828\",\n selectionBackground: \"#665C54\",\n selectionForeground: \"#EBDBB2\",\n}\n\n/** Gruvbox Light — warm retro light theme. */\nexport const gruvboxLight: ColorPalette = {\n name: \"gruvbox-light\",\n dark: false,\n black: \"#F9F5D7\",\n red: \"#CC241D\",\n green: \"#98971A\",\n yellow: \"#D79921\",\n blue: \"#458588\",\n magenta: \"#B16286\",\n cyan: \"#689D6A\",\n white: \"#665C54\",\n brightBlack: \"#EBDBB2\",\n brightRed: \"#D65D0E\",\n brightGreen: brighten(\"#98971A\", 0.15),\n brightYellow: brighten(\"#D79921\", 0.15),\n brightBlue: brighten(\"#458588\", 0.15),\n brightMagenta: \"#B16286\",\n brightCyan: brighten(\"#689D6A\", 0.15),\n brightWhite: \"#3C3836\",\n foreground: \"#3C3836\",\n background: \"#FBF1C7\",\n cursorColor: \"#3C3836\",\n cursorText: \"#FBF1C7\",\n selectionBackground: \"#A89984\",\n selectionForeground: \"#3C3836\",\n}\n",
44
+ "/**\n * Rosé Pine palettes — all natural pine, faux fur and a bit of soho vibes.\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Rosé Pine — the main dark variant. */\nexport const rosePine: ColorPalette = {\n name: \"rose-pine\",\n dark: true,\n black: \"#191724\",\n red: \"#EB6F92\",\n green: \"#31748F\",\n yellow: \"#F6C177\",\n blue: \"#3E8FB0\",\n magenta: \"#C4A7E7\",\n cyan: \"#9CCFD8\",\n white: \"#908CAA\",\n brightBlack: \"#26233A\",\n brightRed: \"#EA9A97\",\n brightGreen: brighten(\"#31748F\", 0.15),\n brightYellow: brighten(\"#F6C177\", 0.15),\n brightBlue: brighten(\"#3E8FB0\", 0.15),\n brightMagenta: \"#EBBCBA\",\n brightCyan: brighten(\"#9CCFD8\", 0.15),\n brightWhite: \"#E0DEF4\",\n foreground: \"#E0DEF4\",\n background: \"#1F1D2E\",\n cursorColor: \"#E0DEF4\",\n cursorText: \"#1F1D2E\",\n selectionBackground: \"#6E6A86\",\n selectionForeground: \"#E0DEF4\",\n}\n\n/** Rosé Pine Moon — slightly lighter dark variant. */\nexport const rosePineMoon: ColorPalette = {\n name: \"rose-pine-moon\",\n dark: true,\n black: \"#232136\",\n red: \"#EB6F92\",\n green: \"#3E8FB0\",\n yellow: \"#F6C177\",\n blue: \"#3E8FB0\",\n magenta: \"#C4A7E7\",\n cyan: \"#9CCFD8\",\n white: \"#908CAA\",\n brightBlack: \"#393552\",\n brightRed: \"#EA9A97\",\n brightGreen: brighten(\"#3E8FB0\", 0.15),\n brightYellow: brighten(\"#F6C177\", 0.15),\n brightBlue: brighten(\"#3E8FB0\", 0.15),\n brightMagenta: \"#EA9A97\",\n brightCyan: brighten(\"#9CCFD8\", 0.15),\n brightWhite: \"#E0DEF4\",\n foreground: \"#E0DEF4\",\n background: \"#2A273F\",\n cursorColor: \"#E0DEF4\",\n cursorText: \"#2A273F\",\n selectionBackground: \"#6E6A86\",\n selectionForeground: \"#E0DEF4\",\n}\n\n/** Rosé Pine Dawn — the light variant. */\nexport const rosePineDawn: ColorPalette = {\n name: \"rose-pine-dawn\",\n dark: false,\n black: \"#FAF4ED\",\n red: \"#B4637A\",\n green: \"#286983\",\n yellow: \"#EA9D34\",\n blue: \"#286983\",\n magenta: \"#907AA9\",\n cyan: \"#56949F\",\n white: \"#797593\",\n brightBlack: \"#F2E9E1\",\n brightRed: \"#D7827E\",\n brightGreen: brighten(\"#286983\", 0.15),\n brightYellow: brighten(\"#EA9D34\", 0.15),\n brightBlue: brighten(\"#286983\", 0.15),\n brightMagenta: \"#D7827E\",\n brightCyan: brighten(\"#56949F\", 0.15),\n brightWhite: \"#575279\",\n foreground: \"#575279\",\n background: \"#FFFAF3\",\n cursorColor: \"#575279\",\n cursorText: \"#FFFAF3\",\n selectionBackground: \"#9893A5\",\n selectionForeground: \"#575279\",\n}\n",
45
+ "/**\n * Kanagawa palettes — inspired by Katsushika Hokusai's famous paintings.\n * Source: https://github.com/rebelot/kanagawa.nvim (palette.lua)\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Kanagawa Wave — the default dark variant, inspired by \"The Great Wave off Kanagawa\". */\nexport const kanagawaWave: ColorPalette = {\n name: \"kanagawa-wave\",\n dark: true,\n black: \"#16161D\", // sumiInk0\n red: \"#C34043\", // autumnRed\n green: \"#98BB6C\", // springGreen\n yellow: \"#E6C384\", // carpYellow\n blue: \"#7E9CD8\", // crystalBlue\n magenta: \"#957FB8\", // oniViolet\n cyan: \"#6A9589\", // waveAqua1\n white: \"#727169\", // fujiGray\n brightBlack: \"#2A2A37\", // sumiInk4\n brightRed: \"#FFA066\", // surimiOrange\n brightGreen: brighten(\"#98BB6C\", 0.15),\n brightYellow: brighten(\"#E6C384\", 0.15),\n brightBlue: brighten(\"#7E9CD8\", 0.15),\n brightMagenta: \"#D27E99\", // sakuraPink\n brightCyan: brighten(\"#6A9589\", 0.15),\n brightWhite: \"#DCD7BA\", // fujiWhite\n foreground: \"#DCD7BA\", // fujiWhite\n background: \"#1F1F28\", // sumiInk3\n cursorColor: \"#DCD7BA\", // fujiWhite\n cursorText: \"#1F1F28\", // sumiInk3\n selectionBackground: \"#54546D\", // sumiInk6\n selectionForeground: \"#DCD7BA\", // fujiWhite\n}\n\n/** Kanagawa Dragon — a muted, earthy dark variant. */\nexport const kanagawaDragon: ColorPalette = {\n name: \"kanagawa-dragon\",\n dark: true,\n black: \"#0d0c0c\", // dragonBlack0\n red: \"#c4746e\", // dragonRed\n green: \"#87a987\", // dragonGreen\n yellow: \"#c4b28a\", // dragonYellow\n blue: \"#8ba4b0\", // dragonBlue2\n magenta: \"#8992a7\", // dragonViolet\n cyan: \"#8ea4a2\", // dragonAqua\n white: \"#737c73\", // dragonAsh\n brightBlack: \"#282727\", // dragonBlack4\n brightRed: \"#b6927b\", // dragonOrange\n brightGreen: brighten(\"#87a987\", 0.15),\n brightYellow: brighten(\"#c4b28a\", 0.15),\n brightBlue: brighten(\"#8ba4b0\", 0.15),\n brightMagenta: \"#a292a3\", // dragonPink\n brightCyan: brighten(\"#8ea4a2\", 0.15),\n brightWhite: \"#c5c9c5\", // dragonWhite\n foreground: \"#c5c9c5\", // dragonWhite\n background: \"#181616\", // dragonBlack3\n cursorColor: \"#c5c9c5\", // dragonWhite\n cursorText: \"#181616\", // dragonBlack3\n selectionBackground: \"#625e5a\", // dragonBlack6\n selectionForeground: \"#c5c9c5\", // dragonWhite\n}\n\n/** Kanagawa Lotus — the light variant, inspired by lotus flowers. */\nexport const kanagawaLotus: ColorPalette = {\n name: \"kanagawa-lotus\",\n dark: false,\n black: \"#e5ddb0\", // lotusWhite2\n red: \"#c84053\", // lotusRed\n green: \"#6f894e\", // lotusGreen\n yellow: \"#de9800\", // lotusYellow3\n blue: \"#4d699b\", // lotusBlue4\n magenta: \"#624c83\", // lotusViolet4\n cyan: \"#597b75\", // lotusAqua\n white: \"#716e61\", // lotusGray2\n brightBlack: \"#dcd5ac\", // lotusWhite1\n brightRed: \"#cc6d00\", // lotusOrange\n brightGreen: brighten(\"#6f894e\", 0.15),\n brightYellow: brighten(\"#de9800\", 0.15),\n brightBlue: brighten(\"#4d699b\", 0.15),\n brightMagenta: \"#b35b79\", // lotusPink\n brightCyan: brighten(\"#597b75\", 0.15),\n brightWhite: \"#545464\", // lotusInk1\n foreground: \"#545464\", // lotusInk1\n background: \"#f2ecbc\", // lotusWhite3\n cursorColor: \"#545464\", // lotusInk1\n cursorText: \"#f2ecbc\", // lotusWhite3\n selectionBackground: \"#8a8980\", // lotusGray3\n selectionForeground: \"#545464\", // lotusInk1\n}\n",
46
+ "/**\n * Everforest palettes — a green-toned comfortable color scheme.\n * Source: https://github.com/sainnhe/everforest (medium background variants)\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Everforest Dark — warm green-based dark theme (medium background). */\nexport const everforestDark: ColorPalette = {\n name: \"everforest-dark\",\n dark: true,\n black: \"#232a2e\", // bg_dim\n red: \"#e67e80\",\n green: \"#a7c080\",\n yellow: \"#dbbc7f\",\n blue: \"#7fbbb3\",\n magenta: \"#d699b6\",\n cyan: \"#83c092\", // aqua\n white: \"#859289\", // grey1\n brightBlack: \"#343f44\", // bg1\n brightRed: \"#e69875\",\n brightGreen: brighten(\"#a7c080\", 0.15),\n brightYellow: brighten(\"#dbbc7f\", 0.15),\n brightBlue: brighten(\"#7fbbb3\", 0.15),\n brightMagenta: \"#e67e80\", // everforest has no distinct pink; reuse red\n brightCyan: brighten(\"#83c092\", 0.15),\n brightWhite: \"#d3c6aa\", // fg\n foreground: \"#d3c6aa\", // fg\n background: \"#2d353b\", // bg0\n cursorColor: \"#d3c6aa\", // fg\n cursorText: \"#2d353b\", // bg0\n selectionBackground: \"#4f585e\", // bg4\n selectionForeground: \"#d3c6aa\", // fg\n}\n\n/** Everforest Light — warm green-based light theme (medium background). */\nexport const everforestLight: ColorPalette = {\n name: \"everforest-light\",\n dark: false,\n black: \"#efebd4\", // bg_dim\n red: \"#f85552\",\n green: \"#8da101\",\n yellow: \"#dfa000\",\n blue: \"#3a94c5\",\n magenta: \"#df69ba\",\n cyan: \"#35a77c\", // aqua\n white: \"#939f91\", // grey1\n brightBlack: \"#f4f0d9\", // bg1\n brightRed: \"#f57d26\",\n brightGreen: brighten(\"#8da101\", 0.15),\n brightYellow: brighten(\"#dfa000\", 0.15),\n brightBlue: brighten(\"#3a94c5\", 0.15),\n brightMagenta: \"#f85552\", // everforest has no distinct pink; reuse red\n brightCyan: brighten(\"#35a77c\", 0.15),\n brightWhite: \"#5c6a72\", // fg\n foreground: \"#5c6a72\", // fg\n background: \"#fdf6e3\", // bg0\n cursorColor: \"#5c6a72\", // fg\n cursorText: \"#fdf6e3\", // bg0\n selectionBackground: \"#e0dcc7\", // bg4\n selectionForeground: \"#5c6a72\", // fg\n}\n",
47
+ "/**\n * Monokai palettes — the iconic syntax highlighting theme.\n * Classic source: Sublime Text default (Wimer Hazenberg, 2006)\n * Pro source: https://monokai.pro/ (default \"Pro\" filter)\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Monokai Classic — the original Sublime Text Monokai colors. */\nexport const monokai: ColorPalette = {\n name: \"monokai\",\n dark: true,\n black: \"#1a1a1a\", // darker than bg\n red: \"#F92672\", // keywords/pink-red\n green: \"#A6E22E\", // functions\n yellow: \"#E6DB74\", // strings\n blue: \"#66D9EF\", // classic Monokai has no distinct blue; cyan doubles\n magenta: \"#AE81FF\", // numbers/constants\n cyan: \"#66D9EF\", // types/builtins (cyan)\n white: \"#a59f85\", // muted text (between comments and fg)\n brightBlack: \"#3e3d32\", // line highlight\n brightRed: \"#FD971F\", // parameters/constants\n brightGreen: brighten(\"#A6E22E\", 0.15),\n brightYellow: brighten(\"#E6DB74\", 0.15),\n brightBlue: brighten(\"#66D9EF\", 0.15),\n brightMagenta: \"#F92672\", // classic Monokai pink == red\n brightCyan: brighten(\"#66D9EF\", 0.15),\n brightWhite: \"#F8F8F2\", // foreground\n foreground: \"#F8F8F2\", // foreground\n background: \"#272822\", // classic Monokai background\n cursorColor: \"#F8F8F2\", // foreground\n cursorText: \"#272822\", // classic Monokai background\n selectionBackground: \"#75715E\", // comments\n selectionForeground: \"#F8F8F2\", // foreground\n}\n\n/** Monokai Pro — the modern, refined Monokai with balanced colors. */\nexport const monokaiPro: ColorPalette = {\n name: \"monokai-pro\",\n dark: true,\n black: \"#221f22\", // darker than base\n red: \"#ff6188\", // keywords\n green: \"#a9dc76\", // functions\n yellow: \"#ffd866\", // strings/types\n blue: \"#78dce8\", // Pro shares cyan as blue\n magenta: \"#ab9df2\", // decorative/numbers\n cyan: \"#78dce8\", // cyan — support/builtins\n white: \"#939293\", // secondary text\n brightBlack: \"#403e41\", // raised surfaces\n brightRed: \"#fc9867\", // constants/parameters\n brightGreen: brighten(\"#a9dc76\", 0.15),\n brightYellow: brighten(\"#ffd866\", 0.15),\n brightBlue: brighten(\"#78dce8\", 0.15),\n brightMagenta: \"#ff6188\", // Pro pink == red\n brightCyan: brighten(\"#78dce8\", 0.15),\n brightWhite: \"#fcfcfa\", // foreground\n foreground: \"#fcfcfa\", // foreground\n background: \"#2d2a2e\", // editor background\n cursorColor: \"#fcfcfa\", // foreground\n cursorText: \"#2d2a2e\", // editor background\n selectionBackground: \"#727072\", // borders/muted chrome\n selectionForeground: \"#fcfcfa\", // foreground\n}\n",
48
+ "/**\n * Snazzy palette — elegant dark theme with vivid colors.\n * Source: https://github.com/sindresorhus/hyper-snazzy\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Snazzy — clean dark theme by Sindre Sorhus. */\nexport const snazzy: ColorPalette = {\n name: \"snazzy\",\n dark: true,\n black: \"#222430\", // border color (deepest)\n red: \"#ff5c57\",\n green: \"#5af78e\",\n yellow: \"#f3f99d\",\n blue: \"#57c7ff\",\n magenta: \"#b267e6\", // interpolated purple (between magenta and blue)\n cyan: \"#9aedfe\", // cyan\n white: \"#97979b\", // cursor/secondary text\n brightBlack: \"#34353e\", // slightly raised (interpolated)\n brightRed: \"#ff9f43\", // snazzy bright orange (warm interpolation)\n brightGreen: brighten(\"#5af78e\", 0.15),\n brightYellow: brighten(\"#f3f99d\", 0.15),\n brightBlue: brighten(\"#57c7ff\", 0.15),\n brightMagenta: \"#ff6ac1\", // magenta\n brightCyan: brighten(\"#9aedfe\", 0.15),\n brightWhite: \"#eff0eb\", // foreground\n foreground: \"#eff0eb\", // foreground\n background: \"#282a36\", // background\n cursorColor: \"#eff0eb\", // foreground\n cursorText: \"#282a36\", // background\n selectionBackground: \"#686868\", // light black / muted chrome\n selectionForeground: \"#eff0eb\", // foreground\n}\n",
49
+ "/**\n * Material Theme palettes — material design-inspired editor theme.\n * Source: https://github.com/kaicataldo/material.vim (material.vim)\n * Reference: https://material-theme.com/docs/reference/color-palette/\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Material Darker — the deep dark Material variant. */\nexport const materialDark: ColorPalette = {\n name: \"material-dark\",\n dark: true,\n black: \"#171717\", // line_highlight (deepest)\n red: \"#ff5370\",\n green: \"#c3e88d\",\n yellow: \"#ffcb6b\",\n blue: \"#82aaff\",\n magenta: \"#c792ea\",\n cyan: \"#89ddff\", // cyan\n white: \"#545454\", // comments\n brightBlack: \"#2c2c2c\", // selection\n brightRed: \"#f78c6c\",\n brightGreen: brighten(\"#c3e88d\", 0.15),\n brightYellow: brighten(\"#ffcb6b\", 0.15),\n brightBlue: brighten(\"#82aaff\", 0.15),\n brightMagenta: \"#f07178\",\n brightCyan: brighten(\"#89ddff\", 0.15),\n brightWhite: \"#eeffff\", // foreground\n foreground: \"#eeffff\", // foreground\n background: \"#212121\", // background\n cursorColor: \"#eeffff\", // foreground\n cursorText: \"#212121\", // background\n selectionBackground: \"#424242\", // guides/line_numbers\n selectionForeground: \"#eeffff\", // foreground\n}\n\n/** Material Lighter — the light Material variant. */\nexport const materialLight: ColorPalette = {\n name: \"material-light\",\n dark: false,\n black: \"#ecf0f1\", // line_highlight\n red: \"#e53935\",\n green: \"#91b859\",\n yellow: \"#ffb62c\",\n blue: \"#6182b8\",\n magenta: \"#7c4dff\",\n cyan: \"#39adb5\", // cyan\n white: \"#90a4ae\", // comments/fg\n brightBlack: \"#ebf4f3\", // selection\n brightRed: \"#f76d47\",\n brightGreen: brighten(\"#91b859\", 0.15),\n brightYellow: brighten(\"#ffb62c\", 0.15),\n brightBlue: brighten(\"#6182b8\", 0.15),\n brightMagenta: \"#ff5370\",\n brightCyan: brighten(\"#39adb5\", 0.15),\n brightWhite: \"#546E7A\", // foreground (darker than comments for light theme)\n foreground: \"#546E7A\", // foreground (darker than comments for light theme)\n background: \"#fafafa\", // background\n cursorColor: \"#546E7A\", // foreground\n cursorText: \"#fafafa\", // background\n selectionBackground: \"#cfd8dc\", // line_numbers\n selectionForeground: \"#546E7A\", // foreground\n}\n",
50
+ "/**\n * Palenight palette — Material Palenight variant.\n * Source: https://github.com/kaicataldo/material.vim (palenight style)\n * Reference: https://github.com/JonathanSpeek/palenight-iterm2\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Palenight — the soft, purple-tinted Material dark variant. */\nexport const palenight: ColorPalette = {\n name: \"palenight\",\n dark: true,\n black: \"#1c1f2b\", // line_highlight (deepest)\n red: \"#f07178\",\n green: \"#c3e88d\",\n yellow: \"#ffcb6b\",\n blue: \"#82aaff\",\n magenta: \"#c792ea\",\n cyan: \"#89ddff\", // cyan\n white: \"#676e95\", // comments\n brightBlack: \"#343b51\", // selection\n brightRed: \"#f78c6c\",\n brightGreen: brighten(\"#c3e88d\", 0.15),\n brightYellow: brighten(\"#ffcb6b\", 0.15),\n brightBlue: brighten(\"#82aaff\", 0.15),\n brightMagenta: \"#ff5370\",\n brightCyan: brighten(\"#89ddff\", 0.15),\n brightWhite: \"#a6accd\", // foreground\n foreground: \"#a6accd\", // foreground\n background: \"#292d3e\", // background\n cursorColor: \"#a6accd\", // foreground\n cursorText: \"#292d3e\", // background\n selectionBackground: \"#4e5579\", // guides/invisibles\n selectionForeground: \"#a6accd\", // foreground\n}\n",
51
+ "/**\n * Ayu palettes — warm, modern theme in three variants.\n * Source: https://github.com/ayu-theme/ayu-colors\n * Reference: https://github.com/Shatur/neovim-ayu\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Ayu Dark — deep dark variant with warm accents. */\nexport const ayuDark: ColorPalette = {\n name: \"ayu-dark\",\n dark: true,\n black: \"#05070A\",\n red: \"#D95757\",\n green: \"#AAD94C\",\n yellow: \"#E6B450\",\n blue: \"#59C2FF\",\n magenta: \"#D2A6FF\",\n cyan: \"#95E6CB\",\n white: \"#636A72\",\n brightBlack: \"#11151C\",\n brightRed: \"#F29668\",\n brightGreen: brighten(\"#AAD94C\", 0.15),\n brightYellow: brighten(\"#E6B450\", 0.15),\n brightBlue: brighten(\"#59C2FF\", 0.15),\n brightMagenta: \"#F07178\",\n brightCyan: brighten(\"#95E6CB\", 0.15),\n brightWhite: \"#BFBDB6\",\n foreground: \"#BFBDB6\",\n background: \"#0B0E14\",\n cursorColor: \"#BFBDB6\",\n cursorText: \"#0B0E14\",\n selectionBackground: \"#565B66\",\n selectionForeground: \"#BFBDB6\",\n}\n\n/** Ayu Mirage — balanced dark variant with softer contrast. */\nexport const ayuMirage: ColorPalette = {\n name: \"ayu-mirage\",\n dark: true,\n black: \"#101521\",\n red: \"#FF6666\",\n green: \"#D5FF80\",\n yellow: \"#FFCC66\",\n blue: \"#73D0FF\",\n magenta: \"#DFBFFF\",\n cyan: \"#95E6CB\",\n white: \"#6C7A8B\",\n brightBlack: \"#171B24\",\n brightRed: \"#F29E74\",\n brightGreen: brighten(\"#D5FF80\", 0.15),\n brightYellow: brighten(\"#FFCC66\", 0.15),\n brightBlue: brighten(\"#73D0FF\", 0.15),\n brightMagenta: \"#F28779\",\n brightCyan: brighten(\"#95E6CB\", 0.15),\n brightWhite: \"#CCCAC2\",\n foreground: \"#CCCAC2\",\n background: \"#1F2430\",\n cursorColor: \"#CCCAC2\",\n cursorText: \"#1F2430\",\n selectionBackground: \"#707A8C\",\n selectionForeground: \"#CCCAC2\",\n}\n\n/** Ayu Light — clean light variant. */\nexport const ayuLight: ColorPalette = {\n name: \"ayu-light\",\n dark: false,\n black: \"#E7EAED\",\n red: \"#E65050\",\n green: \"#86B300\",\n yellow: \"#FFAA33\",\n blue: \"#399EE6\",\n magenta: \"#A37ACC\",\n cyan: \"#4CBF99\",\n white: \"#ABADB1\",\n brightBlack: \"#F3F4F5\",\n brightRed: \"#ED9366\",\n brightGreen: brighten(\"#86B300\", 0.15),\n brightYellow: brighten(\"#FFAA33\", 0.15),\n brightBlue: brighten(\"#399EE6\", 0.15),\n brightMagenta: \"#F07171\",\n brightCyan: brighten(\"#4CBF99\", 0.15),\n brightWhite: \"#5C6166\",\n foreground: \"#5C6166\",\n background: \"#F8F9FA\",\n cursorColor: \"#5C6166\",\n cursorText: \"#F8F9FA\",\n selectionBackground: \"#8A9199\",\n selectionForeground: \"#5C6166\",\n}\n",
52
+ "/**\n * Nightfox palettes — rich, vibrant themes for Neovim.\n * Source: https://github.com/EdenEast/nightfox.nvim\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Nightfox — dark blue-toned variant. */\nexport const nightfox: ColorPalette = {\n name: \"nightfox\",\n dark: true,\n black: \"#131A24\",\n red: \"#C94F6D\",\n green: \"#81B29A\",\n yellow: \"#DBC074\",\n blue: \"#719CD6\",\n magenta: \"#9D79D6\",\n cyan: \"#63CDCF\",\n white: \"#71839B\",\n brightBlack: \"#212E3F\",\n brightRed: \"#F4A261\",\n brightGreen: brighten(\"#81B29A\", 0.15),\n brightYellow: brighten(\"#DBC074\", 0.15),\n brightBlue: brighten(\"#719CD6\", 0.15),\n brightMagenta: \"#D67AD2\",\n brightCyan: brighten(\"#63CDCF\", 0.15),\n brightWhite: \"#CDCECF\",\n foreground: \"#CDCECF\",\n background: \"#192330\",\n cursorColor: \"#CDCECF\",\n cursorText: \"#192330\",\n selectionBackground: \"#39506D\",\n selectionForeground: \"#CDCECF\",\n}\n\n/** Dawnfox — warm light variant inspired by Rose Pine Dawn. */\nexport const dawnfox: ColorPalette = {\n name: \"dawnfox\",\n dark: false,\n black: \"#EBE5DF\",\n red: \"#B4637A\",\n green: \"#618774\",\n yellow: \"#EA9D34\",\n blue: \"#286983\",\n magenta: \"#907AA9\",\n cyan: \"#56949F\",\n white: \"#A8A3B3\",\n brightBlack: \"#EBE0DF\",\n brightRed: \"#D7827E\",\n brightGreen: brighten(\"#618774\", 0.15),\n brightYellow: brighten(\"#EA9D34\", 0.15),\n brightBlue: brighten(\"#286983\", 0.15),\n brightMagenta: \"#D685AF\",\n brightCyan: brighten(\"#56949F\", 0.15),\n brightWhite: \"#575279\",\n foreground: \"#575279\",\n background: \"#FAF4ED\",\n cursorColor: \"#575279\",\n cursorText: \"#FAF4ED\",\n selectionBackground: \"#BDBFC9\",\n selectionForeground: \"#575279\",\n}\n",
53
+ "/**\n * Horizon palette — beautifully warm theme.\n * Source: https://github.com/jolaleye/horizon-theme-vscode\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Horizon — warm dark variant with vivid accents. */\nexport const horizon: ColorPalette = {\n name: \"horizon\",\n dark: true,\n black: \"#16161C\",\n red: \"#E95678\",\n green: \"#29D398\",\n yellow: \"#FAC29A\",\n blue: \"#26BBD9\",\n magenta: \"#B877DB\",\n cyan: \"#59E1E3\",\n white: \"#6C6F93\",\n brightBlack: \"#232530\",\n brightRed: \"#FAB795\",\n brightGreen: brighten(\"#29D398\", 0.15),\n brightYellow: brighten(\"#FAC29A\", 0.15),\n brightBlue: brighten(\"#26BBD9\", 0.15),\n brightMagenta: \"#EE64AC\",\n brightCyan: brighten(\"#59E1E3\", 0.15),\n brightWhite: \"#D5D8DA\",\n foreground: \"#D5D8DA\",\n background: \"#1C1E26\",\n cursorColor: \"#D5D8DA\",\n cursorText: \"#1C1E26\",\n selectionBackground: \"#2E303E\",\n selectionForeground: \"#D5D8DA\",\n}\n",
54
+ "/**\n * Moonfly palette — dark charcoal theme with pastel accents.\n * Source: https://github.com/bluz71/vim-moonfly-colors\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Moonfly — dark charcoal theme. */\nexport const moonfly: ColorPalette = {\n name: \"moonfly\",\n dark: true,\n black: \"#121212\",\n red: \"#FF5D5D\",\n green: \"#8CC85F\",\n yellow: \"#E3C78A\",\n blue: \"#80A0FF\",\n magenta: \"#AE81FF\",\n cyan: \"#79DAC8\",\n white: \"#808080\",\n brightBlack: \"#1C1C1C\",\n brightRed: \"#DE935F\",\n brightGreen: brighten(\"#8CC85F\", 0.15),\n brightYellow: brighten(\"#E3C78A\", 0.15),\n brightBlue: brighten(\"#80A0FF\", 0.15),\n brightMagenta: \"#FF5189\",\n brightCyan: brighten(\"#79DAC8\", 0.15),\n brightWhite: \"#C6C6C6\",\n foreground: \"#C6C6C6\",\n background: \"#080808\",\n cursorColor: \"#C6C6C6\",\n cursorText: \"#080808\",\n selectionBackground: \"#323437\",\n selectionForeground: \"#C6C6C6\",\n}\n",
55
+ "/**\n * Nightfly palette — dark midnight-blue theme with neon accents.\n * Source: https://github.com/bluz71/vim-nightfly-colors\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Nightfly — midnight-blue dark theme. */\nexport const nightfly: ColorPalette = {\n name: \"nightfly\",\n dark: true,\n black: \"#081E2F\",\n red: \"#FC514E\",\n green: \"#A1CD5E\",\n yellow: \"#E3D18A\",\n blue: \"#82AAFF\",\n magenta: \"#C792EA\",\n cyan: \"#7FDBCA\",\n white: \"#7C8F8F\",\n brightBlack: \"#0E293F\",\n brightRed: \"#F78C6C\",\n brightGreen: brighten(\"#A1CD5E\", 0.15),\n brightYellow: brighten(\"#E3D18A\", 0.15),\n brightBlue: brighten(\"#82AAFF\", 0.15),\n brightMagenta: \"#FF5874\",\n brightCyan: brighten(\"#7FDBCA\", 0.15),\n brightWhite: \"#C3CCDC\",\n foreground: \"#C3CCDC\",\n background: \"#011627\",\n cursorColor: \"#C3CCDC\",\n cursorText: \"#011627\",\n selectionBackground: \"#2C3043\",\n selectionForeground: \"#C3CCDC\",\n}\n",
56
+ "/**\n * Oxocarbon palettes — IBM Carbon Design-inspired theme.\n * Source: https://github.com/nyoom-engineering/oxocarbon.nvim\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Oxocarbon Dark — IBM Carbon-inspired dark variant. */\nexport const oxocarbonDark: ColorPalette = {\n name: \"oxocarbon-dark\",\n dark: true,\n black: \"#131313\",\n red: \"#EE5396\",\n green: \"#42BE65\",\n yellow: \"#82CFFF\",\n blue: \"#78A9FF\",\n magenta: \"#BE95FF\",\n cyan: \"#08BDBA\",\n white: \"#5C5C5C\",\n brightBlack: \"#2A2A2A\",\n brightRed: \"#FF7EB6\",\n brightGreen: brighten(\"#42BE65\", 0.15),\n brightYellow: brighten(\"#82CFFF\", 0.15),\n brightBlue: brighten(\"#78A9FF\", 0.15),\n brightMagenta: \"#FF7EB6\",\n brightCyan: brighten(\"#08BDBA\", 0.15),\n brightWhite: \"#F3F3F3\",\n foreground: \"#F3F3F3\",\n background: \"#161616\",\n cursorColor: \"#F3F3F3\",\n cursorText: \"#161616\",\n selectionBackground: \"#404040\",\n selectionForeground: \"#F3F3F3\",\n}\n\n/** Oxocarbon Light — IBM Carbon-inspired light variant. */\nexport const oxocarbonLight: ColorPalette = {\n name: \"oxocarbon-light\",\n dark: false,\n black: \"#F3F3F3\",\n red: \"#EE5396\",\n green: \"#42BE65\",\n yellow: \"#FFAB91\",\n blue: \"#0F62FE\",\n magenta: \"#BE95FF\",\n cyan: \"#08BDBA\",\n white: \"#90A4AE\",\n brightBlack: \"#D5D5D5\",\n brightRed: \"#FF6F00\",\n brightGreen: brighten(\"#42BE65\", 0.15),\n brightYellow: brighten(\"#FFAB91\", 0.15),\n brightBlue: brighten(\"#0F62FE\", 0.15),\n brightMagenta: \"#FF7EB6\",\n brightCyan: brighten(\"#08BDBA\", 0.15),\n brightWhite: \"#37474F\",\n foreground: \"#37474F\",\n background: \"#FFFFFF\",\n cursorColor: \"#37474F\",\n cursorText: \"#FFFFFF\",\n selectionBackground: \"#525252\",\n selectionForeground: \"#37474F\",\n}\n",
57
+ "/**\n * Sonokai palette — high-contrast vivid theme (Monokai Pro-inspired).\n * Source: https://github.com/sainnhe/sonokai (default style)\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Sonokai — vivid dark theme with Monokai-inspired accents. */\nexport const sonokai: ColorPalette = {\n name: \"sonokai\",\n dark: true,\n black: \"#181819\",\n red: \"#FC5D7C\",\n green: \"#9ED072\",\n yellow: \"#E7C664\",\n blue: \"#76CCE0\",\n magenta: \"#B39DF3\",\n cyan: \"#76CCE0\",\n white: \"#7F8490\",\n brightBlack: \"#33353F\",\n brightRed: \"#F39660\",\n brightGreen: brighten(\"#9ED072\", 0.15),\n brightYellow: brighten(\"#E7C664\", 0.15),\n brightBlue: brighten(\"#76CCE0\", 0.15),\n brightMagenta: \"#FC5D7C\",\n brightCyan: brighten(\"#76CCE0\", 0.15),\n brightWhite: \"#E2E2E3\",\n foreground: \"#E2E2E3\",\n background: \"#2C2E34\",\n cursorColor: \"#E2E2E3\",\n cursorText: \"#2C2E34\",\n selectionBackground: \"#414550\",\n selectionForeground: \"#E2E2E3\",\n}\n",
58
+ "/**\n * Edge palettes — clean, modern theme by sainnhe.\n * Source: https://github.com/sainnhe/edge (default style)\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Edge Dark — clean dark variant with balanced accents. */\nexport const edgeDark: ColorPalette = {\n name: \"edge-dark\",\n dark: true,\n black: \"#202023\",\n red: \"#EC7279\",\n green: \"#A0C980\",\n yellow: \"#DEB974\",\n blue: \"#6CB6EB\",\n magenta: \"#D38AEA\",\n cyan: \"#5DBBC1\",\n white: \"#758094\",\n brightBlack: \"#33353F\",\n brightRed: \"#DEB974\",\n brightGreen: brighten(\"#A0C980\", 0.15),\n brightYellow: brighten(\"#DEB974\", 0.15),\n brightBlue: brighten(\"#6CB6EB\", 0.15),\n brightMagenta: \"#EC7279\",\n brightCyan: brighten(\"#5DBBC1\", 0.15),\n brightWhite: \"#C5CDD9\",\n foreground: \"#C5CDD9\",\n background: \"#2C2E34\",\n cursorColor: \"#C5CDD9\",\n cursorText: \"#2C2E34\",\n selectionBackground: \"#414550\",\n selectionForeground: \"#C5CDD9\",\n}\n\n/** Edge Light — clean, readable light variant. */\nexport const edgeLight: ColorPalette = {\n name: \"edge-light\",\n dark: false,\n black: \"#DDE2E7\",\n red: \"#D05858\",\n green: \"#608E32\",\n yellow: \"#BE7E05\",\n blue: \"#5079BE\",\n magenta: \"#B05CCC\",\n cyan: \"#3A8B84\",\n white: \"#8790A0\",\n brightBlack: \"#EEF1F4\",\n brightRed: \"#BE7E05\",\n brightGreen: brighten(\"#608E32\", 0.15),\n brightYellow: brighten(\"#BE7E05\", 0.15),\n brightBlue: brighten(\"#5079BE\", 0.15),\n brightMagenta: \"#D05858\",\n brightCyan: brighten(\"#3A8B84\", 0.15),\n brightWhite: \"#4B505B\",\n foreground: \"#4B505B\",\n background: \"#FAFAFA\",\n cursorColor: \"#4B505B\",\n cursorText: \"#FAFAFA\",\n selectionBackground: \"#DDE2E7\",\n selectionForeground: \"#4B505B\",\n}\n",
59
+ "/**\n * Modus palettes — GNU Emacs themes conforming to WCAG AAA contrast.\n * Source: https://github.com/protesilaos/modus-themes\n */\n\nimport type { ColorPalette } from \"../types\"\nimport { brighten } from \"../color\"\n\n/** Modus Vivendi — elegant dark theme with maximum legibility. */\nexport const modusVivendi: ColorPalette = {\n name: \"modus-vivendi\",\n dark: true,\n black: \"#000000\",\n red: \"#FF5F59\",\n green: \"#44BC44\",\n yellow: \"#D0BC00\",\n blue: \"#2FAFFF\",\n magenta: \"#B6A0FF\",\n cyan: \"#00D3D0\",\n white: \"#989898\",\n brightBlack: \"#1E1E1E\",\n brightRed: \"#FEC43F\",\n brightGreen: brighten(\"#44BC44\", 0.15),\n brightYellow: brighten(\"#D0BC00\", 0.15),\n brightBlue: brighten(\"#2FAFFF\", 0.15),\n brightMagenta: \"#FEACD0\",\n brightCyan: brighten(\"#00D3D0\", 0.15),\n brightWhite: \"#FFFFFF\",\n foreground: \"#FFFFFF\",\n background: \"#000000\",\n cursorColor: \"#FFFFFF\",\n cursorText: \"#000000\",\n selectionBackground: \"#535353\",\n selectionForeground: \"#FFFFFF\",\n}\n\n/** Modus Operandi — elegant light theme with maximum legibility. */\nexport const modusOperandi: ColorPalette = {\n name: \"modus-operandi\",\n dark: false,\n black: \"#E0E0E0\",\n red: \"#A60000\",\n green: \"#006800\",\n yellow: \"#6F5500\",\n blue: \"#0031A9\",\n magenta: \"#531AB6\",\n cyan: \"#005E8B\",\n white: \"#595959\",\n brightBlack: \"#F2F2F2\",\n brightRed: \"#884900\",\n brightGreen: brighten(\"#006800\", 0.15),\n brightYellow: brighten(\"#6F5500\", 0.15),\n brightBlue: brighten(\"#0031A9\", 0.15),\n brightMagenta: \"#721045\",\n brightCyan: brighten(\"#005E8B\", 0.15),\n brightWhite: \"#000000\",\n foreground: \"#000000\",\n background: \"#FFFFFF\",\n cursorColor: \"#000000\",\n cursorText: \"#FFFFFF\",\n selectionBackground: \"#9F9F9F\",\n selectionForeground: \"#000000\",\n}\n",
60
+ "/**\n * Built-in themes and palette registry.\n *\n * Exports:\n * - Pre-derived Theme objects (ansi16Dark, ansi16Light, defaultDark, defaultLight)\n * - ColorPalette definitions from popular theme systems (45 palettes)\n * - Registry functions (getThemeByName, getPaletteByName)\n */\n\nimport { deriveTheme } from \"../derive\"\nimport type { Theme, ColorPalette } from \"../types\"\n\n// ── Re-export all palette definitions ──────────────────────────────\nexport { catppuccinMocha, catppuccinFrappe, catppuccinMacchiato, catppuccinLatte } from \"./catppuccin\"\nexport { nord } from \"./nord\"\nexport { dracula } from \"./dracula\"\nexport { solarizedDark, solarizedLight } from \"./solarized\"\nexport { tokyoNight, tokyoNightStorm, tokyoNightDay } from \"./tokyo-night\"\nexport { oneDark } from \"./one-dark\"\nexport { gruvboxDark, gruvboxLight } from \"./gruvbox\"\nexport { rosePine, rosePineMoon, rosePineDawn } from \"./rose-pine\"\nexport { kanagawaWave, kanagawaDragon, kanagawaLotus } from \"./kanagawa\"\nexport { everforestDark, everforestLight } from \"./everforest\"\nexport { monokai, monokaiPro } from \"./monokai\"\nexport { snazzy } from \"./snazzy\"\nexport { materialDark, materialLight } from \"./material\"\nexport { palenight } from \"./palenight\"\nexport { ayuDark, ayuMirage, ayuLight } from \"./ayu\"\nexport { nightfox, dawnfox } from \"./nightfox\"\nexport { horizon } from \"./horizon\"\nexport { moonfly } from \"./moonfly\"\nexport { nightfly } from \"./nightfly\"\nexport { oxocarbonDark, oxocarbonLight } from \"./oxocarbon\"\nexport { sonokai } from \"./sonokai\"\nexport { edgeDark, edgeLight } from \"./edge\"\nexport { modusVivendi, modusOperandi } from \"./modus\"\n\n// ── Import for registry ────────────────────────────────────────────\nimport { catppuccinMocha, catppuccinFrappe, catppuccinMacchiato, catppuccinLatte } from \"./catppuccin\"\nimport { nord } from \"./nord\"\nimport { dracula } from \"./dracula\"\nimport { solarizedDark, solarizedLight } from \"./solarized\"\nimport { tokyoNight, tokyoNightStorm, tokyoNightDay } from \"./tokyo-night\"\nimport { oneDark } from \"./one-dark\"\nimport { gruvboxDark, gruvboxLight } from \"./gruvbox\"\nimport { rosePine, rosePineMoon, rosePineDawn } from \"./rose-pine\"\nimport { kanagawaWave, kanagawaDragon, kanagawaLotus } from \"./kanagawa\"\nimport { everforestDark, everforestLight } from \"./everforest\"\nimport { monokai, monokaiPro } from \"./monokai\"\nimport { snazzy } from \"./snazzy\"\nimport { materialDark, materialLight } from \"./material\"\nimport { palenight } from \"./palenight\"\nimport { ayuDark, ayuMirage, ayuLight } from \"./ayu\"\nimport { nightfox, dawnfox } from \"./nightfox\"\nimport { horizon } from \"./horizon\"\nimport { moonfly } from \"./moonfly\"\nimport { nightfly } from \"./nightfly\"\nimport { oxocarbonDark, oxocarbonLight } from \"./oxocarbon\"\nimport { sonokai } from \"./sonokai\"\nimport { edgeDark, edgeLight } from \"./edge\"\nimport { modusVivendi, modusOperandi } from \"./modus\"\n\n// ============================================================================\n// ANSI 16 Themes (no palette required — hardcoded for any terminal)\n// ============================================================================\n\n/** Dark ANSI 16 theme — works on any terminal. Primary = yellow. */\nexport const ansi16DarkTheme: Theme = {\n name: \"dark-ansi16\",\n bg: \"\",\n fg: \"whiteBright\",\n muted: \"white\",\n mutedbg: \"black\",\n surface: \"whiteBright\",\n surfacebg: \"black\",\n popover: \"whiteBright\",\n popoverbg: \"black\",\n inverse: \"black\",\n inversebg: \"whiteBright\",\n cursor: \"black\",\n cursorbg: \"yellow\",\n selection: \"black\",\n selectionbg: \"yellow\",\n primary: \"yellow\",\n primaryfg: \"black\",\n secondary: \"white\",\n secondaryfg: \"black\",\n accent: \"blueBright\",\n accentfg: \"black\",\n error: \"redBright\",\n errorfg: \"black\",\n warning: \"yellow\",\n warningfg: \"black\",\n success: \"greenBright\",\n successfg: \"black\",\n info: \"cyan\",\n infofg: \"black\",\n border: \"gray\",\n inputborder: \"gray\",\n focusborder: \"blueBright\",\n link: \"blueBright\",\n disabledfg: \"gray\",\n palette: [\n \"black\",\n \"red\",\n \"green\",\n \"yellow\",\n \"blue\",\n \"magenta\",\n \"cyan\",\n \"white\",\n \"blackBright\",\n \"redBright\",\n \"greenBright\",\n \"yellowBright\",\n \"blueBright\",\n \"magentaBright\",\n \"cyanBright\",\n \"whiteBright\",\n ],\n}\n\n/** Light ANSI 16 theme — works on any terminal. Primary = blue. */\nexport const ansi16LightTheme: Theme = {\n name: \"light-ansi16\",\n bg: \"\",\n fg: \"black\",\n muted: \"blackBright\",\n mutedbg: \"white\",\n surface: \"black\",\n surfacebg: \"white\",\n popover: \"black\",\n popoverbg: \"white\",\n inverse: \"whiteBright\",\n inversebg: \"black\",\n cursor: \"black\",\n cursorbg: \"blue\",\n selection: \"black\",\n selectionbg: \"cyan\",\n primary: \"blue\",\n primaryfg: \"black\",\n secondary: \"blue\",\n secondaryfg: \"black\",\n accent: \"cyan\",\n accentfg: \"black\",\n error: \"red\",\n errorfg: \"black\",\n warning: \"yellow\",\n warningfg: \"black\",\n success: \"green\",\n successfg: \"black\",\n info: \"cyan\",\n infofg: \"black\",\n border: \"gray\",\n inputborder: \"gray\",\n focusborder: \"blue\",\n link: \"blueBright\",\n disabledfg: \"gray\",\n palette: [\n \"black\",\n \"red\",\n \"green\",\n \"yellow\",\n \"blue\",\n \"magenta\",\n \"cyan\",\n \"white\",\n \"blackBright\",\n \"redBright\",\n \"greenBright\",\n \"yellowBright\",\n \"blueBright\",\n \"magentaBright\",\n \"cyanBright\",\n \"whiteBright\",\n ],\n}\n\n// ============================================================================\n// Default Truecolor Themes (derived from Nord palette)\n// ============================================================================\n\n/** Dark truecolor theme — derived from Nord. */\nexport const defaultDarkTheme: Theme = deriveTheme(nord)\n\n/** Light truecolor theme — derived from Catppuccin Latte. */\nexport const defaultLightTheme: Theme = deriveTheme(catppuccinLatte)\n\n// ============================================================================\n// Registry\n// ============================================================================\n\n/** All built-in ColorPalette definitions (45 palettes). */\nexport const builtinPalettes: Record<string, ColorPalette> = {\n // Catppuccin\n \"catppuccin-mocha\": catppuccinMocha,\n \"catppuccin-frappe\": catppuccinFrappe,\n \"catppuccin-macchiato\": catppuccinMacchiato,\n \"catppuccin-latte\": catppuccinLatte,\n // Nord\n nord: nord,\n // Dracula\n dracula: dracula,\n // Solarized\n \"solarized-dark\": solarizedDark,\n \"solarized-light\": solarizedLight,\n // Tokyo Night\n \"tokyo-night\": tokyoNight,\n \"tokyo-night-storm\": tokyoNightStorm,\n \"tokyo-night-day\": tokyoNightDay,\n // One Dark\n \"one-dark\": oneDark,\n // Gruvbox\n \"gruvbox-dark\": gruvboxDark,\n \"gruvbox-light\": gruvboxLight,\n // Rose Pine\n \"rose-pine\": rosePine,\n \"rose-pine-moon\": rosePineMoon,\n \"rose-pine-dawn\": rosePineDawn,\n // Kanagawa\n \"kanagawa-wave\": kanagawaWave,\n \"kanagawa-dragon\": kanagawaDragon,\n \"kanagawa-lotus\": kanagawaLotus,\n // Everforest\n \"everforest-dark\": everforestDark,\n \"everforest-light\": everforestLight,\n // Monokai\n monokai: monokai,\n \"monokai-pro\": monokaiPro,\n // Snazzy\n snazzy: snazzy,\n // Material\n \"material-dark\": materialDark,\n \"material-light\": materialLight,\n // Palenight\n palenight: palenight,\n // Ayu\n \"ayu-dark\": ayuDark,\n \"ayu-mirage\": ayuMirage,\n \"ayu-light\": ayuLight,\n // Nightfox\n nightfox: nightfox,\n dawnfox: dawnfox,\n // Horizon\n horizon: horizon,\n // Moonfly\n moonfly: moonfly,\n // Nightfly\n nightfly: nightfly,\n // Oxocarbon\n \"oxocarbon-dark\": oxocarbonDark,\n \"oxocarbon-light\": oxocarbonLight,\n // Sonokai\n sonokai: sonokai,\n // Edge\n \"edge-dark\": edgeDark,\n \"edge-light\": edgeLight,\n // Modus\n \"modus-vivendi\": modusVivendi,\n \"modus-operandi\": modusOperandi,\n}\n\n/** All built-in themes, indexed by name (includes backward-compat aliases). */\nexport const builtinThemes: Record<string, Theme> = {\n // ANSI 16\n \"dark-ansi16\": ansi16DarkTheme,\n \"light-ansi16\": ansi16LightTheme,\n // Truecolor defaults\n \"dark-truecolor\": defaultDarkTheme,\n \"light-truecolor\": defaultLightTheme,\n // Old names as aliases\n dark: defaultDarkTheme,\n light: defaultLightTheme,\n \"ansi16-dark\": ansi16DarkTheme,\n \"ansi16-light\": ansi16LightTheme,\n}\n\n/** Resolve a theme by name. Defaults to dark-ansi16. */\nexport function getThemeByName(name?: string): Theme {\n if (!name) return ansi16DarkTheme\n // Check pre-built themes first\n const builtin = builtinThemes[name]\n if (builtin) return builtin\n // Check palettes (derive on first access)\n const palette = builtinPalettes[name]\n if (palette) return deriveTheme(palette)\n return ansi16DarkTheme\n}\n\n/** Resolve a palette by name. Returns undefined if not found. */\nexport function getPaletteByName(name: string): ColorPalette | undefined {\n return builtinPalettes[name]\n}\n",
61
+ "/**\n * Active theme state — module-level for pipeline access.\n *\n * This module has side effects (global mutable state).\n * Marked in package.json sideEffects for tree-shaking.\n *\n * Usage is optional — standalone users pass Theme objects explicitly\n * to resolveThemeColor(token, theme). The global state exists for\n * silvery's render pipeline where React context isn't accessible.\n */\n\nimport type { Theme } from \"./types\"\nimport { ansi16DarkTheme } from \"./palettes/index\"\n\n// ============================================================================\n// Active Theme\n// ============================================================================\n\n/**\n * The currently active theme, set by ThemeProvider during render.\n * Used by parseColor() to resolve $token strings without React context access.\n */\nlet _activeTheme: Theme = ansi16DarkTheme\n\n/** Set the active theme (called by ThemeProvider). */\nexport function setActiveTheme(theme: Theme): void {\n _activeTheme = theme\n}\n\n/** Get the active theme (called by parseColor in render-helpers). */\nexport function getActiveTheme(): Theme {\n return _contextStack.length > 0 ? _contextStack[_contextStack.length - 1]! : _activeTheme\n}\n\n// ============================================================================\n// Context Theme Stack (per-subtree overrides during content phase)\n// ============================================================================\n\n/**\n * Stack of per-subtree theme overrides, pushed/popped during content phase\n * tree walk. When a Box has a `theme` prop, its theme is pushed before\n * rendering children and popped after. getActiveTheme() checks this stack\n * first, falling back to _activeTheme.\n *\n * This enables CSS custom property-like cascading: the nearest ancestor\n * Box with a theme prop determines $token resolution for its subtree.\n */\nconst _contextStack: Theme[] = []\n\n/** Push a context theme (called by content phase for Box nodes with theme prop). */\nexport function pushContextTheme(theme: Theme): void {\n _contextStack.push(theme)\n}\n\n/** Pop a context theme (called by content phase after processing Box subtree). */\nexport function popContextTheme(): void {\n _contextStack.pop()\n}\n",
62
+ "/**\n * Token resolution — resolves `$token` strings against a Theme.\n *\n * All theme property names are lowercase, no hyphens (e.g. `surfacebg`).\n * Hyphens in token strings are stripped for lookup: `$surface-bg` → `surfacebg`.\n * This allows both `$surfacebg` and `$surface-bg` to resolve identically.\n */\n\nimport type { Theme } from \"./types\"\n\n/** Color-typed keys of Theme (excludes metadata and palette). */\ntype ThemeColorKey = Exclude<keyof Theme, \"name\" | \"palette\">\n\n/**\n * Resolve a color value — if it starts with `$`, look up the token in the theme.\n *\n * Supports:\n * - Named tokens: `$primary`, `$fg`, `$border`, `$muted`, etc.\n * - Compound tokens: `$surfacebg`, `$mutedbg`, `$disabledfg`, etc.\n * - Hyphenated tokens: `$surface-bg` → strips hyphens → `surfacebg`\n * - Palette colors: `$color0` through `$color15`\n *\n * Returns `undefined` for `undefined` input. Non-`$` strings pass through unchanged.\n * Unknown tokens pass through as-is.\n */\nexport function resolveThemeColor(color: string | undefined, theme: Theme): string | undefined {\n if (!color) return undefined\n if (!color.startsWith(\"$\")) return color\n\n const token = color.slice(1)\n\n // Palette colors: $color0 through $color15\n if (token.startsWith(\"color\")) {\n const idx = parseInt(token.slice(5), 10)\n if (idx >= 0 && idx < 16 && theme.palette && idx < theme.palette.length) {\n return theme.palette[idx]\n }\n }\n\n // Strip hyphens for lookup (supports both $surfacebg and $surface-bg)\n const key = token.replace(/-/g, \"\") as ThemeColorKey\n const val = theme[key]\n return typeof val === \"string\" ? val : color\n}\n",
63
+ "/**\n * Render Helpers - Pure utility functions for content rendering.\n *\n * Contains:\n * - Color parsing (parseColor)\n * - Border character definitions (getBorderChars)\n * - Style extraction (getTextStyle)\n * - Text width utilities (getTextWidth)\n *\n * Re-exports layout helpers from helpers.ts:\n * - getPadding, getBorderSize\n */\n\nimport { DEFAULT_BG, type Color, type Style, type UnderlineStyle } from \"../buffer\"\nimport { getActiveTheme } from \"@silvery/theme/state\"\nimport { resolveThemeColor } from \"@silvery/theme/resolve\"\nimport type { BoxProps, TextProps } from \"@silvery/ag/types\"\nimport { displayWidthAnsi } from \"../unicode\"\nimport type { BorderChars, PipelineContext } from \"./types\"\n\n// Re-export shared layout helpers\nexport { getBorderSize, getPadding } from \"./helpers\"\n\n// ============================================================================\n// Color Parsing\n// ============================================================================\n\n// Named colors map to 256-color indices (hoisted to module scope to avoid per-call allocation)\nconst namedColors: Record<string, number> = {\n black: 0,\n red: 1,\n green: 2,\n yellow: 3,\n blue: 4,\n magenta: 5,\n cyan: 6,\n white: 7,\n gray: 8,\n grey: 8,\n blackBright: 8,\n redBright: 9,\n greenBright: 10,\n yellowBright: 11,\n blueBright: 12,\n magentaBright: 13,\n cyanBright: 14,\n whiteBright: 15,\n}\n\n/**\n * Blend two RGB colors in sRGB space.\n * Formula: result = c1 * (1 - t) + c2 * t, where t is 0..1.\n * Returns an RGB object with each channel clamped to 0-255.\n */\nfunction blendColors(\n c1: { r: number; g: number; b: number },\n c2: { r: number; g: number; b: number },\n t: number,\n): { r: number; g: number; b: number } {\n return {\n r: Math.round(c1.r * (1 - t) + c2.r * t),\n g: Math.round(c1.g * (1 - t) + c2.g * t),\n b: Math.round(c1.b * (1 - t) + c2.b * t),\n }\n}\n\n/**\n * Parse color string to Color type.\n * Supports: mix(c1,c2,amount), $token (theme), named colors, hex (#rgb, #rrggbb), rgb(r,g,b)\n */\nexport function parseColor(color: string): Color {\n // Inherit: no color — parent's color flows through (like CSS color: inherit)\n if (color === \"inherit\") return null\n\n // Special token: terminal's default background (SGR 49)\n if (color === \"$default\") return DEFAULT_BG\n\n // Mix: blend two colors — mix(color1, color2, amount)\n // Amount can be a percentage (e.g. 50%) or a decimal (e.g. 0.5).\n // Both colors are recursively resolved via parseColor (supports theme tokens, hex, named, etc.).\n // Only blends when both colors resolve to RGB objects; returns null if either is null or an ANSI index.\n if (color.startsWith(\"mix(\") && color.endsWith(\")\")) {\n const inner = color.slice(4, -1)\n // Split on commas, but respect nested parentheses (e.g. rgb(r,g,b) as an argument)\n const args: string[] = []\n let depth = 0\n let start = 0\n for (let i = 0; i < inner.length; i++) {\n if (inner[i] === \"(\") depth++\n else if (inner[i] === \")\") depth--\n else if (inner[i] === \",\" && depth === 0) {\n args.push(inner.slice(start, i).trim())\n start = i + 1\n }\n }\n args.push(inner.slice(start).trim())\n\n if (args.length === 3) {\n const c1 = parseColor(args[0]!)\n const c2 = parseColor(args[1]!)\n const amountStr = args[2]!\n\n // Parse amount: percentage (e.g. \"50%\") or decimal (e.g. \"0.5\")\n let t: number\n if (amountStr.endsWith(\"%\")) {\n t = Number.parseFloat(amountStr.slice(0, -1)) / 100\n } else {\n t = Number.parseFloat(amountStr)\n }\n\n // Only blend RGB objects; ANSI indices (number) and null cannot be blended\n if (c1 !== null && c2 !== null && typeof c1 === \"object\" && typeof c2 === \"object\" && !Number.isNaN(t)) {\n return blendColors(c1, c2, Math.max(0, Math.min(1, t)))\n }\n return null\n }\n }\n\n // Future: slash notation for background opacity (e.g. \"$link/10\") is not yet supported.\n // It would require richer return types to carry opacity alongside the base color.\n\n // Resolve $token colors against the active theme\n if (color.startsWith(\"$\")) {\n const resolved = resolveThemeColor(color, getActiveTheme())\n if (resolved && resolved !== color) return parseColor(resolved)\n return null\n }\n\n if (color in namedColors) {\n return namedColors[color as keyof typeof namedColors]!\n }\n\n // Hex color\n if (color.startsWith(\"#\")) {\n const hex = color.slice(1)\n if (hex.length === 3) {\n const r = Number.parseInt(hex[0]! + hex[0]!, 16)\n const g = Number.parseInt(hex[1]! + hex[1]!, 16)\n const b = Number.parseInt(hex[2]! + hex[2]!, 16)\n return { r, g, b }\n }\n if (hex.length === 6) {\n const r = Number.parseInt(hex.slice(0, 2), 16)\n const g = Number.parseInt(hex.slice(2, 4), 16)\n const b = Number.parseInt(hex.slice(4, 6), 16)\n return { r, g, b }\n }\n }\n\n // rgb(r,g,b)\n const rgbMatch = color.match(/^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i)\n if (rgbMatch) {\n return {\n r: Number.parseInt(rgbMatch[1]!, 10),\n g: Number.parseInt(rgbMatch[2]!, 10),\n b: Number.parseInt(rgbMatch[3]!, 10),\n }\n }\n\n // ansi256(N) — 256-color palette index (0-255)\n const ansi256Match = color.match(/^ansi256\\s*\\(\\s*(\\d+)\\s*\\)$/i)\n if (ansi256Match) {\n return Number.parseInt(ansi256Match[1]!, 10)\n }\n\n return null\n}\n\n// ============================================================================\n// Border Characters\n// ============================================================================\n\n/**\n * Border character sets by style. Hoisted to module scope to avoid\n * re-allocating 7 objects on every call.\n */\nconst borders: Record<NonNullable<BoxProps[\"borderStyle\"]>, BorderChars> = {\n single: {\n topLeft: \"\\u250c\",\n topRight: \"\\u2510\",\n bottomLeft: \"\\u2514\",\n bottomRight: \"\\u2518\",\n horizontal: \"\\u2500\",\n vertical: \"\\u2502\",\n },\n double: {\n topLeft: \"\\u2554\",\n topRight: \"\\u2557\",\n bottomLeft: \"\\u255a\",\n bottomRight: \"\\u255d\",\n horizontal: \"\\u2550\",\n vertical: \"\\u2551\",\n },\n round: {\n topLeft: \"\\u256d\",\n topRight: \"\\u256e\",\n bottomLeft: \"\\u2570\",\n bottomRight: \"\\u256f\",\n horizontal: \"\\u2500\",\n vertical: \"\\u2502\",\n },\n bold: {\n topLeft: \"\\u250f\",\n topRight: \"\\u2513\",\n bottomLeft: \"\\u2517\",\n bottomRight: \"\\u251b\",\n horizontal: \"\\u2501\",\n vertical: \"\\u2503\",\n },\n singleDouble: {\n topLeft: \"\\u2553\",\n topRight: \"\\u2556\",\n bottomLeft: \"\\u2559\",\n bottomRight: \"\\u255c\",\n horizontal: \"\\u2500\",\n vertical: \"\\u2551\",\n },\n doubleSingle: {\n topLeft: \"\\u2552\",\n topRight: \"\\u2555\",\n bottomLeft: \"\\u2558\",\n bottomRight: \"\\u255b\",\n horizontal: \"\\u2550\",\n vertical: \"\\u2502\",\n },\n classic: {\n topLeft: \"+\",\n topRight: \"+\",\n bottomLeft: \"+\",\n bottomRight: \"+\",\n horizontal: \"-\",\n vertical: \"|\",\n },\n}\n\n/**\n * Get border characters for a style.\n */\nexport function getBorderChars(style: BoxProps[\"borderStyle\"]): BorderChars {\n if (style && typeof style === \"object\") {\n // Custom border object (Ink compat): map Ink's top/bottom/left/right to\n // silvery's horizontal/vertical format. Supports distinct chars per side.\n const obj = style as Record<string, string>\n const topHorizontal = obj.top ?? obj.horizontal ?? \"-\"\n const leftVertical = obj.left ?? obj.vertical ?? \"|\"\n return {\n topLeft: obj.topLeft ?? \"+\",\n topRight: obj.topRight ?? \"+\",\n bottomLeft: obj.bottomLeft ?? \"+\",\n bottomRight: obj.bottomRight ?? \"+\",\n horizontal: topHorizontal,\n vertical: leftVertical,\n bottomHorizontal: obj.bottom && obj.bottom !== topHorizontal ? obj.bottom : undefined,\n rightVertical: obj.right && obj.right !== leftVertical ? obj.right : undefined,\n }\n }\n return borders[style ?? \"single\"]\n}\n\n// ============================================================================\n// Style Extraction\n// ============================================================================\n\n/**\n * Get text style from props.\n */\nexport function getTextStyle(props: TextProps): Style {\n // Determine underline style: underlineStyle takes precedence over underline boolean\n let underlineStyle: UnderlineStyle | undefined\n if (props.underlineStyle !== undefined) {\n underlineStyle = props.underlineStyle\n } else if (props.underline) {\n underlineStyle = \"single\"\n }\n\n return {\n fg: props.color ? parseColor(props.color) : null,\n bg: props.backgroundColor ? parseColor(props.backgroundColor) : null,\n underlineColor: props.underlineColor ? parseColor(props.underlineColor) : null,\n attrs: {\n bold: props.bold,\n dim: props.dim || props.dimColor, // dimColor is Ink compatibility alias\n italic: props.italic,\n underline: props.underline || !!underlineStyle,\n underlineStyle,\n strikethrough: props.strikethrough,\n inverse: props.inverse,\n },\n }\n}\n\n// ============================================================================\n// Text Width Utilities\n// ============================================================================\n\n/**\n * Get text display width (accounting for wide characters and ANSI codes).\n * Uses ANSI-aware width calculation to handle styled text.\n *\n * When a PipelineContext is provided, uses the context's measurer for\n * terminal-capability-aware width calculation. Falls back to the module-level\n * displayWidthAnsi (which reads the scoped measurer or default).\n */\nexport function getTextWidth(text: string, ctx?: PipelineContext): number {\n if (ctx) return ctx.measurer.displayWidthAnsi(text)\n return displayWidthAnsi(text)\n}\n",
64
+ "/**\n * Text Rendering - Functions for rendering text content to the buffer.\n *\n * Contains:\n * - ANSI text line rendering (renderAnsiTextLine)\n * - Plain text line rendering (renderTextLine)\n * - Text formatting (formatTextLines)\n * - Text truncation (truncateText)\n * - Text content collection (collectTextContent)\n */\n\nimport {\n type CellAttrs,\n type Color,\n type Style,\n type TerminalBuffer,\n type UnderlineStyle,\n createMutableCell,\n} from \"../buffer\"\nimport type { AgNode, TextProps } from \"@silvery/ag/types\"\nimport {\n type StyledSegment,\n ensureEmojiPresentation,\n graphemeWidth,\n hasAnsi,\n parseAnsiText,\n sliceByWidth,\n sliceByWidthFromEnd,\n splitGraphemes,\n wrapText,\n} from \"../unicode\"\nimport { collectPlainText } from \"./collect-text\"\nimport { getTextStyle, getTextWidth, parseColor } from \"./render-helpers\"\nimport type { BgConflictMode, NodeRenderState, PipelineContext } from \"./types\"\n\n// ============================================================================\n// Background Conflict Detection\n// ============================================================================\n\n/** Cached bg conflict mode. Read from env once at module load. */\nlet bgConflictMode: BgConflictMode = (() => {\n const env = process.env.SILVERY_BG_CONFLICT?.toLowerCase()\n if (env === \"ignore\" || env === \"warn\" || env === \"throw\") return env\n return \"throw\" // default - fail fast on programming errors\n})()\n\n/**\n * Get the current background conflict detection mode.\n */\nfunction getBgConflictMode(): BgConflictMode {\n return bgConflictMode\n}\n\n/**\n * Set the background conflict detection mode. For tests.\n */\nexport function setBgConflictMode(mode: BgConflictMode): void {\n bgConflictMode = mode\n}\n\n// Track warned conflicts to avoid spam (only used in 'warn' mode)\nconst warnedBgConflicts = new Set<string>()\n\n/**\n * Clear the background conflict warning cache.\n * Call this at the start of each render cycle to:\n * - Prevent memory leaks in long-running apps\n * - Allow warnings to repeat after user fixes issues\n */\nexport function clearBgConflictWarnings(): void {\n warnedBgConflicts.clear()\n}\n\n// ============================================================================\n// Text Content Collection\n// ============================================================================\n\n/**\n * Style context for nested Text elements.\n * Tracks cumulative styles through the tree to enable proper push/pop behavior.\n */\ninterface StyleContext {\n color?: string\n backgroundColor?: string\n bold?: boolean\n dim?: boolean\n italic?: boolean\n underline?: boolean\n inverse?: boolean\n strikethrough?: boolean\n}\n\n/**\n * Build ANSI escape sequence for a style context.\n *\n * Note: backgroundColor is intentionally NOT embedded as ANSI codes.\n * Background color is handled at the buffer level (via BgSegment tracking)\n * to prevent bg bleed across wrapped text lines. See km-silvery.bg-bleed.\n */\nfunction styleToAnsi(style: StyleContext): string {\n const codes: number[] = []\n\n // Foreground color - use parseColor directly instead of roundtripping through getTextStyle\n if (style.color) {\n const color = parseColor(style.color)\n if (color !== null) {\n if (typeof color === \"number\") {\n codes.push(38, 5, color)\n } else {\n codes.push(38, 2, color.r, color.g, color.b)\n }\n }\n }\n\n // backgroundColor is NOT embedded here - it is tracked separately via\n // BgSegment and applied at the buffer level in renderText(). This prevents\n // bg color from bleeding across wrapped lines. See collectTextWithBg().\n\n // Attributes\n if (style.bold) codes.push(1)\n if (style.dim) codes.push(2)\n if (style.italic) codes.push(3)\n if (style.underline) codes.push(4)\n if (style.inverse) codes.push(7)\n if (style.strikethrough) codes.push(9)\n\n if (codes.length === 0) {\n return \"\"\n }\n\n return `\\x1b[${codes.join(\";\")}m`\n}\n\n/**\n * Merge child props into parent context.\n * Child values override parent values when specified.\n */\nfunction mergeStyleContext(parent: StyleContext, childProps: TextProps): StyleContext {\n return {\n color: childProps.color ?? parent.color,\n backgroundColor: childProps.backgroundColor ?? parent.backgroundColor,\n bold: childProps.bold ?? parent.bold,\n dim: childProps.dim ?? childProps.dimColor ?? parent.dim,\n italic: childProps.italic ?? parent.italic,\n underline: childProps.underline ?? parent.underline,\n inverse: childProps.inverse ?? parent.inverse,\n strikethrough: childProps.strikethrough ?? parent.strikethrough,\n }\n}\n\n/**\n * Apply text styles as ANSI escape codes with proper push/pop behavior.\n * After the child text, restores the parent context's styles.\n *\n * @param text - The text content to wrap\n * @param childStyle - The merged style for this child (child overrides parent)\n * @param parentStyle - The parent's style context to restore after\n */\nfunction applyTextStyleAnsi(text: string, childStyle: StyleContext, parentStyle: StyleContext): string {\n if (!text) {\n return text\n }\n\n const childAnsi = styleToAnsi(childStyle)\n const parentAnsi = styleToAnsi(parentStyle)\n\n // If child has no style changes, just return text\n if (!childAnsi) {\n return text\n }\n\n // Apply child style, then reset and re-apply parent style\n // We use \\x1b[0m to reset, then re-apply parent styles\n return `${childAnsi}${text}\\x1b[0m${parentAnsi}`\n}\n\n/**\n * Recursively collect text content from a node and its children.\n * Handles both raw text nodes (textContent set directly) and\n * Text component wrappers (text in children).\n *\n * For nested Text nodes with style props (color, bold, etc.),\n * applies ANSI codes so the styles are preserved when rendered.\n * Uses a style stack to properly restore parent styles after nested elements.\n *\n * @param node - The node to collect text from\n * @param parentContext - The inherited style context from parent (used for restoration)\n */\nexport function collectTextContent(node: AgNode, parentContext: StyleContext = {}): string {\n // If this node has direct text content, return it\n if (node.textContent !== undefined) {\n return node.textContent\n }\n\n // Otherwise, collect from children\n // Matching Ink's squashTextNodes: apply internal_transform to the full text\n // of each child node (not per-line), using the child index as the index argument.\n let result = \"\"\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i]!\n // If child is a Text node (virtual/nested) with style props, apply ANSI codes\n if (child.type === \"silvery-text\" && child.props && !child.layoutNode) {\n const childProps = child.props as TextProps\n // Merge child props with parent context to get effective child style\n const childContext = mergeStyleContext(parentContext, childProps)\n // Recursively collect with child's context\n let childContent = collectTextContent(child, childContext)\n // Apply internal_transform from virtual text nodes (nested Transform components).\n // Matches Ink's squashTextNodes: transform is applied to the full concatenated\n // text of the child, with index = child position in parent's children array.\n const childTransform = (childProps as any).internal_transform\n if (childTransform && childContent.length > 0) {\n childContent = childTransform(childContent, i)\n }\n // Apply styles with proper push/pop (child style, then restore parent)\n result += applyTextStyleAnsi(childContent, childContext, parentContext)\n } else {\n // Not a styled Text node, just collect recursively\n result += collectTextContent(child, parentContext)\n }\n }\n return result\n}\n\n// ============================================================================\n// Background Segment Tracking\n// ============================================================================\n\n/**\n * A background color segment in collected text.\n * Tracks which character range has which background color,\n * independent of ANSI codes. Used to apply bg at the buffer level\n * after text wrapping, preventing bg bleed across wrapped lines.\n */\ninterface BgSegment {\n /** Start character offset in the collected text (inclusive) */\n start: number\n /** End character offset in the collected text (exclusive) */\n end: number\n /** Background color to apply */\n bg: Color\n}\n\n/**\n * A span mapping a virtual text child node to its character range.\n * Used to compute inlineRects for hit testing on nested Text.\n */\ninterface ChildSpan {\n /** The virtual text node */\n node: AgNode\n /** Start display-width offset in the collected text (inclusive) */\n start: number\n /** End display-width offset in the collected text (exclusive) */\n end: number\n}\n\n/**\n * Result of collecting text with background segments.\n */\ninterface TextWithBg {\n /** The collected text string (with ANSI codes for fg/attrs, but NOT bg) */\n text: string\n /** Background color segments from nested Text elements */\n bgSegments: BgSegment[]\n /** Spans mapping virtual text children to display-width ranges */\n childSpans: ChildSpan[]\n /** Plain text character count (excluding ANSI codes). Used for DOM-level budget tracking. */\n plainLen: number\n}\n\n// collectPlainText is imported from ./collect-text.\n// Previously duplicated here; now shared across measure-phase, render-text,\n// and the reconciler's measure function.\n\n/**\n * Collect text content and background color segments from a node tree.\n *\n * Like collectTextContent, but also tracks backgroundColor from nested Text\n * elements as separate BgSegment entries. Background is NOT embedded as ANSI\n * codes, preventing bg bleed when text wraps across lines.\n *\n * @param node - The node to collect text from\n * @param parentContext - The inherited style context from parent\n * @param offset - Current character offset in the collected text (for bg tracking)\n * @param maxDisplayWidth - Maximum display width (columns) to collect. When set,\n * stops collecting once this many display columns of content have been gathered.\n * This truncates at the DOM level BEFORE ANSI serialization, so escape sequences\n * (OSC 8, etc.) are never generated for content that won't be displayed.\n * Uses getTextWidth (ANSI-aware) so pre-styled leaf text is handled correctly.\n */\nfunction collectTextWithBg(\n node: AgNode,\n parentContext: StyleContext = {},\n offset = 0,\n maxDisplayWidth?: number,\n ctx?: PipelineContext,\n): TextWithBg {\n // If this node has direct text content, return it with no bg segments\n if (node.textContent !== undefined) {\n let text = node.textContent\n // DOM-level truncation: trim leaf text to display width budget\n if (maxDisplayWidth !== undefined) {\n const textW = getTextWidth(text, ctx)\n if (textW > maxDisplayWidth) {\n const sliceFn = ctx ? ctx.measurer.sliceByWidth : sliceByWidth\n text = sliceFn(text, maxDisplayWidth)\n }\n }\n // plainLen tracks display width for budget and BgSegment offset tracking.\n // Both use display-width coordinates consistently: collectTextWithBg uses\n // getTextWidth for offsets, mapLinesToCharOffsets returns display-width,\n // and applyBgSegmentsToLine compares via display-width (col - x).\n const plainLen = getTextWidth(text, ctx)\n return { text, bgSegments: [], childSpans: [], plainLen }\n }\n\n let result = \"\"\n const bgSegments: BgSegment[] = []\n const childSpans: ChildSpan[] = []\n let currentOffset = offset\n let displayWidthCollected = 0\n\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i]!\n // Stop collecting if budget exhausted\n if (maxDisplayWidth !== undefined && displayWidthCollected >= maxDisplayWidth) break\n\n // Compute remaining budget for this child\n const childBudget = maxDisplayWidth !== undefined ? maxDisplayWidth - displayWidthCollected : undefined\n\n if (child.type === \"silvery-text\" && child.props && !child.layoutNode) {\n const childProps = child.props as TextProps\n const childContext = mergeStyleContext(parentContext, childProps)\n\n // Recursively collect with child's context and budget\n const childResult = collectTextWithBg(child, childContext, currentOffset, childBudget, ctx)\n\n // Apply internal_transform from virtual text nodes (nested Transform components).\n // Matches Ink's squashTextNodes: transform is applied to the full concatenated\n // text of the child, with index = child position in parent's children array.\n const childTransform = (childProps as any).internal_transform\n if (childTransform && childResult.text.length > 0) {\n childResult.text = childTransform(childResult.text, i)\n }\n\n // Apply ANSI styles for fg/attrs (but NOT bg) with push/pop\n const styledText = applyTextStyleAnsi(childResult.text, childContext, parentContext)\n result += styledText\n\n // Track bg segment if this child (or its ancestors) has backgroundColor.\n // When backgroundColor is \"\" (empty string), create a null-bg segment to\n // explicitly clear inherited background (e.g., from a parent Box).\n if (childContext.backgroundColor) {\n const bg = parseColor(childContext.backgroundColor)\n if (bg !== null) {\n if (childResult.plainLen > 0) {\n bgSegments.push({\n start: currentOffset,\n end: currentOffset + childResult.plainLen,\n bg,\n })\n }\n }\n } else if (childProps.backgroundColor === \"\" && childResult.plainLen > 0) {\n // Explicit backgroundColor=\"\" clears inherited bg (from parent Text\n // or ancestor Box's inheritedBg). Push a null-bg segment so\n // applyBgSegmentsToLine overrides inheritedBg to null for this range.\n bgSegments.push({\n start: currentOffset,\n end: currentOffset + childResult.plainLen,\n bg: null,\n })\n }\n\n // Track child span for inlineRects computation\n if (childResult.plainLen > 0) {\n childSpans.push({\n node: child,\n start: currentOffset,\n end: currentOffset + childResult.plainLen,\n })\n }\n\n // Include child's nested bg segments and child spans\n bgSegments.push(...childResult.bgSegments)\n childSpans.push(...childResult.childSpans)\n\n // Track using plainLen (display width) — not text.length which includes ANSI codes\n currentOffset += childResult.plainLen\n displayWidthCollected += childResult.plainLen\n } else {\n // Not a styled Text node, just collect recursively\n const childResult = collectTextWithBg(child, parentContext, currentOffset, childBudget, ctx)\n result += childResult.text\n bgSegments.push(...childResult.bgSegments)\n childSpans.push(...childResult.childSpans)\n currentOffset += childResult.plainLen\n displayWidthCollected += childResult.plainLen\n }\n }\n\n return { text: result, bgSegments, childSpans, plainLen: displayWidthCollected }\n}\n\n/**\n * Apply background segments to buffer cells for a single rendered line.\n *\n * Maps character offsets from the original collected text to screen positions,\n * accounting for text wrapping. Each bg segment fills only the cells that\n * correspond to actual text characters, not trailing whitespace.\n *\n * @param buffer - The terminal buffer to write to\n * @param x - Screen x position of the line start\n * @param y - Screen y position of the line\n * @param lineText - The rendered line text (may contain ANSI codes)\n * @param lineCharStart - Character offset in original text where this line starts\n * @param lineCharEnd - Character offset in original text where this line ends\n * @param bgSegments - Background color segments to apply\n */\nfunction applyBgSegmentsToLine(\n buffer: TerminalBuffer,\n x: number,\n y: number,\n lineText: string,\n lineCharStart: number,\n lineCharEnd: number,\n bgSegments: BgSegment[],\n ctx?: PipelineContext,\n): void {\n if (bgSegments.length === 0) return\n if (y < 0 || y >= buffer.height) return\n\n // Reusable cell for readCellInto to avoid per-character allocation\n const bgCell = createMutableCell()\n const gWidthFn = ctx ? ctx.measurer.graphemeWidth : graphemeWidth\n\n // For each bg segment that overlaps this line's character range,\n // calculate the screen columns and fill the bg\n for (const seg of bgSegments) {\n // Check overlap between segment [seg.start, seg.end) and line [lineCharStart, lineCharEnd)\n const overlapStart = Math.max(seg.start, lineCharStart)\n const overlapEnd = Math.min(seg.end, lineCharEnd)\n if (overlapStart >= overlapEnd) continue\n\n // Convert display-width offsets to column positions within the line.\n // BgSegment offsets and lineCharStart/lineCharEnd are all in display-width\n // coordinates, so relStart/relEnd are display-width offsets within the line.\n const relStart = overlapStart - lineCharStart\n const relEnd = overlapEnd - lineCharStart\n\n // Walk through the line's visible characters to find screen columns.\n // Use display-width offset (col - x) to match BgSegment coordinate system.\n let col = x\n const graphemes = splitGraphemes(hasAnsi(lineText) ? stripAnsiForBg(lineText) : lineText)\n\n for (const grapheme of graphemes) {\n const gWidth = gWidthFn(grapheme)\n if (gWidth === 0) continue\n\n const displayOffset = col - x\n if (displayOffset >= relStart && displayOffset < relEnd) {\n // This character is within the bg segment -- set bg on its cells.\n // Use readCellInto to avoid allocating a new Cell per iteration.\n buffer.readCellInto(col, y, bgCell)\n bgCell.bg = seg.bg\n buffer.setCell(col, y, bgCell)\n if (gWidth === 2 && col + 1 < buffer.width) {\n buffer.readCellInto(col + 1, y, bgCell)\n bgCell.bg = seg.bg\n buffer.setCell(col + 1, y, bgCell)\n }\n }\n\n col += gWidth\n if (col - x >= relEnd) break\n }\n }\n}\n\n/**\n * Strip ANSI escape codes from text for character counting.\n */\nfunction stripAnsiForBg(text: string): string {\n return text\n .replace(/\\x1b\\[[0-9;:?]*[A-Za-z]/g, \"\")\n .replace(/\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)/g, \"\")\n .replace(/\\x1b[DME78]/g, \"\")\n .replace(/\\x1b\\(B/g, \"\")\n}\n\n/**\n * Map formatted lines back to character offsets in the original text.\n *\n * After wrapping/truncation, each output line corresponds to a range\n * of characters in the original text. This function computes those ranges\n * by searching for each line's content in the normalized text.\n *\n * Handles characters consumed by word wrapping (spaces at break points,\n * newlines) and characters added by truncation (ellipsis).\n *\n * Returns display-width offsets (not UTF-16 code units) to match BgSegment\n * coordinate system. BgSegments use display-width via getTextWidth/plainLen.\n *\n * @param originalText - The original collected text (with ANSI, before wrapping)\n * @param formattedLines - The wrapped/truncated output lines\n * @param ctx - Pipeline context for width measurement\n * @returns Array of { start, end } display-width offsets for each formatted line\n */\nfunction mapLinesToCharOffsets(\n originalText: string,\n formattedLines: string[],\n ctx?: PipelineContext,\n): Array<{ start: number; end: number }> {\n // Strip ANSI from the original to get the plain text character sequence\n const plainOriginal = hasAnsi(originalText) ? stripAnsiForBg(originalText) : originalText\n // Normalize tabs to match formatTextLines behavior\n const normalized = plainOriginal.replace(/\\t/g, \" \")\n\n const result: Array<{ start: number; end: number }> = []\n let charOffset = 0 // UTF-16 offset for string matching (findLineStart)\n let displayOffset = 0 // Display-width offset for BgSegment matching\n\n for (const line of formattedLines) {\n const plainLine = hasAnsi(line) ? stripAnsiForBg(line) : line\n\n // Find where this line starts in the normalized text (UTF-16 matching).\n const lineStart = findLineStart(normalized, plainLine, charOffset)\n\n // Convert skipped characters (between previous line end and this line start)\n // to display width. These are whitespace/newlines consumed by wrapping.\n if (lineStart > charOffset) {\n const skipped = normalized.slice(charOffset, lineStart)\n displayOffset += getTextWidth(skipped, ctx)\n }\n\n // Line content display width\n const lineDisplayWidth = getTextWidth(plainLine, ctx)\n result.push({ start: displayOffset, end: displayOffset + lineDisplayWidth })\n\n // Advance both offset trackers\n const lineLen = Math.min(plainLine.length, normalized.length - lineStart)\n charOffset = lineStart + lineLen\n displayOffset += lineDisplayWidth\n }\n\n return result\n}\n\n/**\n * Find where a formatted line starts in the normalized original text.\n *\n * Scans forward from the given offset, matching the line content\n * character by character. Skips newlines and whitespace that were\n * consumed by wrapping between lines.\n */\nfunction findLineStart(normalized: string, plainLine: string, fromOffset: number): number {\n if (plainLine.length === 0) {\n // Empty line -- skip to next newline\n let pos = fromOffset\n while (pos < normalized.length && normalized[pos] === \"\\n\") {\n pos++\n }\n return pos\n }\n\n // Try exact match at current offset first (fast path for first line\n // and for lines that follow explicit newlines without space trimming)\n if (normalized.startsWith(plainLine, fromOffset)) {\n return fromOffset\n }\n\n // For truncated lines, extract prefix before ellipsis for matching.\n // startsWith fails when the line has \"…\" that doesn't exist in the original.\n const ELLIPSIS = \"\\u2026\"\n const ellipsisIdx = plainLine.indexOf(ELLIPSIS)\n const truncatedPrefix = ellipsisIdx > 0 ? plainLine.slice(0, ellipsisIdx) : null\n\n if (truncatedPrefix && normalized.startsWith(truncatedPrefix, fromOffset)) {\n return fromOffset\n }\n\n // Scan forward, skipping newlines and spaces consumed by wrapping\n let pos = fromOffset\n while (pos < normalized.length) {\n const ch = normalized[pos]!\n if (ch === \"\\n\" || ch === \" \") {\n pos++\n continue\n }\n // Found a non-whitespace character -- check if line starts here\n if (normalized.startsWith(plainLine, pos)) {\n return pos\n }\n // Check truncated prefix match (e.g. \"abcde…\" -> match \"abcde\")\n if (truncatedPrefix && normalized.startsWith(truncatedPrefix, pos)) {\n return pos\n }\n pos++\n }\n\n // Fallback: return current position\n return fromOffset\n}\n\n// ============================================================================\n// Text Formatting\n// ============================================================================\n\n/**\n * Format text into lines based on wrap mode.\n *\n * @param trim - When true, trims trailing spaces on broken lines and skips leading\n * spaces on continuation lines. When false (e.g., text has backgroundColor),\n * preserves trailing spaces so background color covers them. Defaults to true.\n */\nexport function formatTextLines(\n text: string,\n width: number,\n wrap: TextProps[\"wrap\"],\n ctx?: PipelineContext,\n trim = true,\n): string[] {\n // Guard against width <= 0 to prevent infinite loops\n // This can happen with display=\"none\" nodes (0x0 dimensions)\n if (width <= 0) {\n return []\n }\n\n // Convert tabs to spaces (tabs have 0 display width in string-width library)\n const normalizedText = text.replace(/\\t/g, \" \")\n const lines = normalizedText.split(\"\\n\")\n\n // Hard clip: truncate without ellipsis (used by Fill component)\n if (wrap === \"clip\") {\n const sliceFn = ctx ? ctx.measurer.sliceByWidth : sliceByWidth\n return lines.map((line) => {\n if (getTextWidth(line, ctx) <= width) return line\n return sliceFn(line, width)\n })\n }\n\n // No wrapping, just truncate at end\n if (wrap === false || wrap === \"truncate-end\" || wrap === \"truncate\") {\n return lines.map((line) => truncateText(line, width, \"end\", ctx))\n }\n\n if (wrap === \"truncate-start\") {\n return lines.map((line) => truncateText(line, width, \"start\", ctx))\n }\n\n if (wrap === \"truncate-middle\") {\n return lines.map((line) => truncateText(line, width, \"middle\", ctx))\n }\n\n // wrap === true or wrap === 'wrap' - word-aware wrapping\n // Uses wrapText from unicode.ts with trim for rendering\n // (when trim=true, trims trailing spaces on broken lines, skips leading spaces\n // on continuation lines; when trim=false, preserves spaces for bg-colored text)\n if (ctx) return ctx.measurer.wrapText(normalizedText, width, true, trim)\n return wrapText(normalizedText, width, true, trim)\n}\n\n/**\n * Truncate text to fit within width.\n */\nexport function truncateText(\n text: string,\n width: number,\n mode: \"start\" | \"middle\" | \"end\",\n ctx?: PipelineContext,\n): string {\n const textWidth = getTextWidth(text, ctx)\n if (textWidth <= width) return text\n\n const ellipsis = \"\\u2026\" // ...\n const availableWidth = width - 1 // Reserve space for ellipsis\n\n if (availableWidth <= 0) {\n return width > 0 ? ellipsis : \"\"\n }\n\n const sliceFn = ctx ? ctx.measurer.sliceByWidth : sliceByWidth\n const sliceEndFn = ctx ? ctx.measurer.sliceByWidthFromEnd : sliceByWidthFromEnd\n\n if (mode === \"end\") {\n return sliceFn(text, availableWidth) + ellipsis\n }\n\n if (mode === \"start\") {\n return ellipsis + sliceEndFn(text, availableWidth)\n }\n\n // middle\n const halfWidth = Math.floor(availableWidth / 2)\n const startPart = sliceFn(text, halfWidth)\n const endPart = sliceEndFn(text, availableWidth - halfWidth)\n return startPart + ellipsis + endPart\n}\n\n// ============================================================================\n// Text Line Rendering\n// ============================================================================\n\n/**\n * Render a single line of text to the buffer.\n *\n * @param maxCol - Right edge of the text node's layout area. Wide characters\n * whose continuation cell would exceed this boundary are replaced with a\n * space, matching terminal behavior for wide chars at the screen edge.\n * Without this, continuation cells overflow into adjacent containers and\n * become stale during incremental rendering (the owning container's dirty\n * tracking doesn't cover cells outside its layout bounds).\n */\nexport function renderTextLine(\n buffer: TerminalBuffer,\n x: number,\n y: number,\n text: string,\n baseStyle: Style,\n maxCol?: number,\n inheritedBg?: Color,\n ctx?: PipelineContext,\n): void {\n // Check if text contains ANSI escape sequences\n if (hasAnsi(text)) {\n renderAnsiTextLine(buffer, x, y, text, baseStyle, maxCol, inheritedBg, ctx)\n return\n }\n\n renderGraphemes(buffer, splitGraphemes(text), x, y, baseStyle, maxCol, inheritedBg, ctx)\n}\n\n/**\n * Like renderTextLine but returns the column position after the last rendered character.\n * Used by renderText to know where to clear remaining cells.\n */\nfunction renderTextLineReturn(\n buffer: TerminalBuffer,\n x: number,\n y: number,\n text: string,\n baseStyle: Style,\n maxCol?: number,\n inheritedBg?: Color,\n ctx?: PipelineContext,\n): number {\n if (hasAnsi(text)) {\n return renderAnsiTextLineReturn(buffer, x, y, text, baseStyle, maxCol, inheritedBg, ctx)\n }\n return renderGraphemes(buffer, splitGraphemes(text), x, y, baseStyle, maxCol, inheritedBg, ctx)\n}\n\n/**\n * Render graphemes to buffer cells with proper Unicode handling.\n * Shared by renderTextLine (plain text) and renderAnsiTextLine (per-segment).\n *\n * @param maxCol - Right edge of the text node's layout area (exclusive).\n * Wide characters whose continuation cell would reach or exceed this\n * boundary are replaced with a space character. This matches terminal\n * behavior for wide chars at the right edge of a container and prevents\n * continuation cells from overflowing into adjacent containers, where\n * they become stale during incremental rendering.\n *\n * Returns the column position after the last rendered grapheme.\n */\nfunction renderGraphemes(\n buffer: TerminalBuffer,\n graphemes: string[],\n startCol: number,\n y: number,\n style: Style,\n maxCol?: number,\n inheritedBg?: Color,\n ctx?: PipelineContext,\n): number {\n let col = startCol\n // Effective right boundary: text node's layout edge or buffer edge\n const rightEdge = maxCol !== undefined ? Math.min(maxCol, buffer.width) : buffer.width\n const gWidthFn = ctx ? ctx.measurer.graphemeWidth : graphemeWidth\n\n for (const grapheme of graphemes) {\n if (col >= rightEdge) break\n\n const width = gWidthFn(grapheme)\n if (width === 0) continue\n\n // Determine background color for this cell.\n // Priority: 1) Text's own bg, 2) inherited bg from ancestor Box, 3) buffer read (legacy fallback).\n // Using inherited bg instead of getCellBg decouples text rendering from buffer state,\n // which is critical for incremental rendering: the cloned buffer may have stale bg\n // at positions outside the parent's bg-filled region (e.g., overflow text).\n const existingBg = style.bg !== null ? style.bg : inheritedBg !== undefined ? inheritedBg : buffer.getCellBg(col, y)\n\n // Wide character at the boundary: the continuation cell would overflow\n // into an adjacent container. Replace with a space to match terminal\n // behavior (real terminals leave the last column blank for wide chars\n // that don't fit). Without this, the continuation cell extends outside\n // the text node's layout bounds and becomes stale during incremental\n // rendering — the owning container's dirty flag tracking doesn't cover\n // cells outside its layout area.\n if (width === 2 && col + 1 >= rightEdge) {\n buffer.setCell(col, y, {\n char: \" \",\n fg: style.fg,\n bg: existingBg,\n underlineColor: style.underlineColor ?? null,\n attrs: style.attrs,\n wide: false,\n continuation: false,\n hyperlink: style.hyperlink,\n })\n col += 1\n continue\n }\n\n // For text-presentation emoji, add VS16 so terminals render at 2 columns\n const outputChar = width === 2 ? ensureEmojiPresentation(grapheme) : grapheme\n\n buffer.setCell(col, y, {\n char: outputChar,\n fg: style.fg,\n bg: existingBg,\n underlineColor: style.underlineColor ?? null,\n attrs: style.attrs,\n wide: width === 2,\n continuation: false,\n hyperlink: style.hyperlink,\n })\n\n if (width === 2 && col + 1 < buffer.width) {\n const existingBg2 =\n style.bg !== null ? style.bg : inheritedBg !== undefined ? inheritedBg : buffer.getCellBg(col + 1, y)\n buffer.setCell(col + 1, y, {\n char: \"\",\n fg: style.fg,\n bg: existingBg2,\n underlineColor: style.underlineColor ?? null,\n attrs: style.attrs,\n wide: false,\n continuation: true,\n hyperlink: style.hyperlink,\n })\n col += 2\n } else {\n col += width\n }\n }\n\n return col\n}\n\n/**\n * Render text line with ANSI escape sequences.\n * Parses ANSI codes and applies styles to individual segments.\n */\nexport function renderAnsiTextLine(\n buffer: TerminalBuffer,\n x: number,\n y: number,\n text: string,\n baseStyle: Style,\n maxCol?: number,\n inheritedBg?: Color,\n ctx?: PipelineContext,\n): void {\n renderAnsiTextLineReturn(buffer, x, y, text, baseStyle, maxCol, inheritedBg, ctx)\n}\n\n/**\n * Like renderAnsiTextLine but returns the column position after the last rendered character.\n */\nfunction renderAnsiTextLineReturn(\n buffer: TerminalBuffer,\n x: number,\n y: number,\n text: string,\n baseStyle: Style,\n maxCol?: number,\n inheritedBg?: Color,\n ctx?: PipelineContext,\n): number {\n const segments = parseAnsiText(text)\n let col = x\n\n for (const segment of segments) {\n // Merge segment style with base style\n const style = mergeAnsiStyle(baseStyle, segment)\n\n // Detect background conflict: chalk.bg* overwrites existing silvery background\n // Check both: 1) Text's own backgroundColor, 2) Parent Box's bg already in buffer\n // Skip if segment has bgOverride flag (explicit opt-out via ansi.bgOverride)\n const effectiveBgConflictMode = ctx?.bgConflictMode ?? getBgConflictMode()\n if (\n effectiveBgConflictMode !== \"ignore\" &&\n !segment.bgOverride &&\n segment.bg !== undefined &&\n segment.bg !== null\n ) {\n // Check if there's an existing background (from Text prop or parent Box fill)\n const existingBufBg = col < buffer.width ? buffer.getCellBg(col, y) : null\n const hasExistingBg = baseStyle.bg !== null || existingBufBg !== null\n\n if (hasExistingBg) {\n const preview = segment.text.slice(0, 30)\n const msg = `[silvery] Background conflict: chalk.bg* on text that already has silvery background. Chalk bg will override only text characters, causing visual gaps in padding. Use ansi.bgOverride() to suppress if intentional. Text: \"${preview}${segment.text.length > 30 ? \"...\" : \"\"}\"`\n\n if (effectiveBgConflictMode === \"throw\") {\n throw new Error(msg)\n }\n // 'warn' mode - deduplicate\n const effectiveWarnedBgConflicts = ctx?.warnedBgConflicts ?? warnedBgConflicts\n const key = `${JSON.stringify(existingBufBg)}-${segment.bg}-${preview}`\n if (!effectiveWarnedBgConflicts.has(key)) {\n effectiveWarnedBgConflicts.add(key)\n console.warn(msg)\n }\n }\n }\n\n col = renderGraphemes(buffer, splitGraphemes(segment.text), col, y, style, maxCol, inheritedBg, ctx)\n }\n return col\n}\n\n// ============================================================================\n// Style Merging (Category-Based)\n// ============================================================================\n\n/**\n * Options for category-based style merging.\n */\nexport interface MergeStylesOptions {\n /**\n * Preserve decoration attributes through layers (OR merge).\n * Affects: underline, underlineStyle, underlineColor, strikethrough\n * Default: true\n */\n preserveDecorations?: boolean\n /**\n * Preserve emphasis attributes through layers (OR merge).\n * Affects: bold, dim, italic\n * Default: true\n */\n preserveEmphasis?: boolean\n}\n\n/**\n * Merge two styles using category-based semantics.\n *\n * Categories and their merge behavior:\n * - Container (bg): overlay replaces base\n * - Text (fg): overlay replaces base\n * - Decorations (underline*, strikethrough): OR merge if preserveDecorations=true\n * - Emphasis (bold, dim, italic): OR merge if preserveEmphasis=true\n * - Transform (inverse, hidden, blink): overlay only, not inherited\n *\n * @param base - The base style (from parent/container)\n * @param overlay - The overlay style (from child/content)\n * @param options - Merge behavior options\n */\nexport function mergeStyles(base: Style, overlay: Partial<Style>, options: MergeStylesOptions = {}): Style {\n const { preserveDecorations = true, preserveEmphasis = true } = options\n\n const baseAttrs = base.attrs ?? {}\n const overlayAttrs = overlay.attrs ?? {}\n\n // Merge attributes by category\n const attrs: CellAttrs = {}\n\n // Decorations: OR if preserving, otherwise overlay takes precedence\n if (preserveDecorations) {\n // Underline: OR the boolean, but style from overlay wins if specified\n const hasBaseUnderline = baseAttrs.underline || baseAttrs.underlineStyle\n const hasOverlayUnderline = overlayAttrs.underline || overlayAttrs.underlineStyle\n if (hasBaseUnderline || hasOverlayUnderline) {\n attrs.underline = true\n // Style: overlay wins if specified, else base\n attrs.underlineStyle = overlayAttrs.underlineStyle ?? baseAttrs.underlineStyle ?? \"single\"\n }\n attrs.strikethrough = overlayAttrs.strikethrough || baseAttrs.strikethrough\n } else {\n attrs.underline = overlayAttrs.underline ?? baseAttrs.underline\n attrs.underlineStyle = overlayAttrs.underlineStyle ?? baseAttrs.underlineStyle\n attrs.strikethrough = overlayAttrs.strikethrough ?? baseAttrs.strikethrough\n }\n\n // Emphasis: OR if preserving\n if (preserveEmphasis) {\n attrs.bold = overlayAttrs.bold || baseAttrs.bold\n attrs.dim = overlayAttrs.dim || baseAttrs.dim\n attrs.italic = overlayAttrs.italic || baseAttrs.italic\n } else {\n attrs.bold = overlayAttrs.bold ?? baseAttrs.bold\n attrs.dim = overlayAttrs.dim ?? baseAttrs.dim\n attrs.italic = overlayAttrs.italic ?? baseAttrs.italic\n }\n\n // Transform: overlay only, not inherited from base\n attrs.inverse = overlayAttrs.inverse\n attrs.hidden = overlayAttrs.hidden\n attrs.blink = overlayAttrs.blink\n\n return {\n // Container/Text: overlay wins if specified\n fg: overlay.fg ?? base.fg,\n bg: overlay.bg ?? base.bg,\n // Underline color: always use overlay ?? base (part of decoration preservation)\n underlineColor: overlay.underlineColor ?? base.underlineColor,\n attrs,\n }\n}\n\n// ============================================================================\n// ANSI Style Helpers\n// ============================================================================\n\n/**\n * Merge ANSI segment style with base style.\n * Uses category-based merging to preserve decorations and emphasis.\n */\nfunction mergeAnsiStyle(base: Style, segment: StyledSegment, options: MergeStylesOptions = {}): Style {\n const { preserveDecorations = true, preserveEmphasis = true } = options\n\n // Convert ANSI SGR codes to overlay style\n let fg: Color = base.fg\n let bg: Color = base.bg\n let underlineColor: Color = base.underlineColor ?? null\n\n if (segment.fg !== undefined && segment.fg !== null) {\n fg = ansiColorToColor(segment.fg)\n }\n if (segment.bg !== undefined && segment.bg !== null) {\n bg = ansiColorToColor(segment.bg)\n }\n if (segment.underlineColor !== undefined && segment.underlineColor !== null) {\n underlineColor = ansiColorToColor(segment.underlineColor)\n }\n\n // Build overlay attrs from segment\n const overlayAttrs: CellAttrs = {}\n if (segment.bold !== undefined) overlayAttrs.bold = segment.bold\n if (segment.dim !== undefined) overlayAttrs.dim = segment.dim\n if (segment.italic !== undefined) overlayAttrs.italic = segment.italic\n if (segment.underline !== undefined) {\n overlayAttrs.underline = segment.underline\n }\n if (segment.underlineStyle !== undefined) {\n overlayAttrs.underlineStyle = segment.underlineStyle as UnderlineStyle\n }\n if (segment.inverse !== undefined) overlayAttrs.inverse = segment.inverse\n\n // Use mergeStyles for consistent category-based merging\n const merged = mergeStyles(\n base,\n { fg, bg, underlineColor, attrs: overlayAttrs },\n { preserveDecorations, preserveEmphasis },\n )\n\n // Pass through OSC 8 hyperlink from segment (not an SGR attribute)\n if (segment.hyperlink) {\n merged.hyperlink = segment.hyperlink\n }\n\n return merged\n}\n\n/**\n * Convert ANSI SGR color code to our Color type.\n * Color is: number (256-color index) | { r, g, b } (true color) | null\n */\nfunction ansiColorToColor(code: number): Color {\n // True color (packed RGB with 0x1000000 marker from parseAnsiText)\n if (code >= 0x1000000) {\n const r = (code >> 16) & 0xff\n const g = (code >> 8) & 0xff\n const b = code & 0xff\n return { r, g, b }\n }\n\n // 256 color palette index (0-255)\n if (code < 30 || (code >= 38 && code < 40) || (code >= 48 && code < 90)) {\n // Direct palette index (0-255) — return as-is\n return code\n }\n\n // Standard foreground colors (30-37) map to palette 0-7\n if (code >= 30 && code <= 37) {\n return code - 30\n }\n\n // Standard background colors (40-47) map to palette 0-7\n if (code >= 40 && code <= 47) {\n return code - 40\n }\n\n // Bright foreground colors (90-97) map to palette 8-15\n if (code >= 90 && code <= 97) {\n return code - 90 + 8\n }\n\n // Bright background colors (100-107) map to palette 8-15\n if (code >= 100 && code <= 107) {\n return code - 100 + 8\n }\n\n return null\n}\n\n// ============================================================================\n// Render Text Node (Main Entry Point)\n// ============================================================================\n\n/**\n * Render a Text node.\n *\n * Background colors from nested Text elements are handled at the buffer level\n * (not via ANSI codes) to prevent bg bleed across wrapped text lines.\n * See km-silvery.bg-bleed for details.\n */\nexport function renderText(\n node: AgNode,\n buffer: TerminalBuffer,\n layout: { x: number; y: number; width: number; height: number },\n props: TextProps,\n nodeState: NodeRenderState,\n inheritedBg?: Color,\n inheritedFg?: Color,\n ctx?: PipelineContext,\n): void {\n const { scrollOffset, clipBounds } = nodeState\n const { x, width, height } = layout\n let { y } = layout\n\n // Apply scroll offset\n y -= scrollOffset\n\n // Explicit backgroundColor=\"\" on a Text node means \"no background\" — force\n // null bg to override both inherited bg from ancestor Boxes and any bg\n // already in the buffer cells (set by Box's renderBox fill). The sentinel\n // value `null` is used instead of `undefined` so renderGraphemes uses it\n // directly instead of falling back to buffer.getCellBg().\n if (props.backgroundColor === \"\") {\n inheritedBg = null\n }\n\n // Clip to bounds if specified\n if (clipBounds) {\n if (y + height <= clipBounds.top || y >= clipBounds.bottom) {\n return // Completely outside vertical clip bounds\n }\n if (clipBounds.left !== undefined && clipBounds.right !== undefined) {\n if (x + width <= clipBounds.left || x >= clipBounds.right) {\n return // Completely outside horizontal clip bounds\n }\n }\n }\n\n // Compute DOM-level display width budget for truncate-end modes.\n // This limits how much text collectTextWithBg gathers BEFORE ANSI serialization,\n // making OSC 8 hyperlinks and other escape sequences safe by construction.\n // Only applies to end-truncation (truncate, truncate-end, false) where we keep\n // text from the start. Start/middle truncation keep text from the end or both\n // ends, so they fall back to ANSI-level truncation in formatTextLines.\n // Budget is width + 1 display columns per line to ensure formatTextLines sees\n // text wider than the container and adds the ellipsis character.\n let maxDisplayWidth: number | undefined\n const isTruncateEnd =\n props.wrap === false || props.wrap === \"truncate-end\" || props.wrap === \"truncate\" || props.wrap === \"clip\"\n if (isTruncateEnd && width > 0) {\n const plainText = collectPlainText(node)\n const lineCount = (plainText.match(/\\n/g)?.length ?? 0) + 1\n // Each line needs width+1 columns to trigger ellipsis. Multiply by line count.\n maxDisplayWidth = (width + 1) * lineCount\n }\n\n // Collect text content and background segments from this node and all children.\n // Background color from nested Text elements is tracked as BgSegments\n // (not embedded as ANSI codes) to survive text wrapping correctly.\n const { text, bgSegments, childSpans } = collectTextWithBg(node, {}, 0, maxDisplayWidth, ctx)\n\n // Get style for this Text node.\n // Inherit foreground from nearest ancestor Box with color prop (CSS semantics).\n const style = getTextStyle(props)\n if (style.fg === null && inheritedFg !== undefined) {\n style.fg = inheritedFg\n }\n\n // Handle wrapping/truncation\n // When text has background color (from own prop, nested children, or inherited\n // from parent Box), preserve trailing spaces on wrapped lines so the background\n // color covers them. Ink preserves these spaces for the same reason.\n const hasBg = style.bg !== null || bgSegments.length > 0 || (inheritedBg !== undefined && inheritedBg !== null)\n let lines = formatTextLines(text, width, props.wrap, ctx, !hasBg)\n\n // Apply internal_transform if present (used by Transform component).\n // Transform is applied per-line after formatting, matching ink's behavior.\n // The transform may change the length of each line (e.g., adding brackets\n // or line numbers). We track whether a transform is active so the rendering\n // loop can expand maxCol to accommodate the transformed content.\n const internalTransform = props.internal_transform\n if (internalTransform) {\n lines = lines.map((line, index) => internalTransform(line, index))\n }\n\n // Map formatted lines back to display-width offsets for bg segment and inlineRect application\n const needLineOffsets = bgSegments.length > 0 || childSpans.length > 0\n const lineOffsets = needLineOffsets ? mapLinesToCharOffsets(text, lines, ctx) : []\n\n // Render each line\n for (let lineIdx = 0; lineIdx < lines.length && lineIdx < height; lineIdx++) {\n const lineY = y + lineIdx\n // Skip lines outside clip bounds\n if (clipBounds && (lineY < clipBounds.top || lineY >= clipBounds.bottom)) {\n continue\n }\n const line = lines[lineIdx]!\n\n // Pass maxCol to prevent wide characters from overflowing into adjacent\n // containers. Without this, continuation cells outside the text node's\n // layout bounds become stale during incremental rendering.\n // Clip right edge to horizontal clip bounds (overflow:hidden containers).\n // When internal_transform is active, expand maxCol to buffer width so the\n // transformed text (which may be wider than the original layout) is not clipped.\n const layoutRight = internalTransform ? buffer.width : x + width\n const maxCol =\n clipBounds && \"right\" in clipBounds && clipBounds.right !== undefined\n ? Math.min(layoutRight, clipBounds.right)\n : layoutRight\n const endCol = renderTextLineReturn(buffer, x, lineY, line, style, maxCol, inheritedBg, ctx)\n\n // Clear remaining cells after text to end of layout width (clipped).\n // When text content shrinks (e.g., breadcrumb changes from long to short path),\n // the parent Box may skip its bg fill (skipBgFill=true when only subtreeDirty).\n // Without explicit clearing here, stale chars from the previous longer text\n // survive in the cloned buffer. This is safe: we only clear within our own\n // layout area, writing spaces with the correct inherited background.\n if (endCol < maxCol) {\n const clearBg = inheritedBg ?? null\n for (let cx = endCol; cx < maxCol && cx < buffer.width; cx++) {\n buffer.setCell(cx, lineY, {\n char: \" \",\n fg: style.fg,\n bg: clearBg,\n underlineColor: null,\n attrs: {\n bold: false,\n dim: false,\n italic: false,\n underline: false,\n inverse: false,\n strikethrough: false,\n blink: false,\n hidden: false,\n },\n wide: false,\n continuation: false,\n })\n }\n }\n\n // Apply background segments from nested Text elements to the buffer.\n // This happens after renderTextLine so the bg is applied to cells\n // that already have the correct character/fg/attrs written.\n if (bgSegments.length > 0 && lineIdx < lineOffsets.length) {\n const { start, end } = lineOffsets[lineIdx]!\n applyBgSegmentsToLine(buffer, x, lineY, line, start, end, bgSegments, ctx)\n }\n }\n\n // Compute inlineRects for virtual text children.\n // Maps each child's display-width span to screen-space rectangles,\n // accounting for text wrapping (one rect per line fragment).\n if (childSpans.length > 0 && lineOffsets.length > 0) {\n computeInlineRects(childSpans, lineOffsets, x, y, lines.length, height)\n }\n}\n\n/**\n * Compute inlineRects for virtual text children based on their display-width spans\n * and the formatted line offsets. For wrapped text, a child may span multiple lines,\n * producing one rect per line fragment.\n *\n * @param childSpans - Virtual text children with their display-width ranges\n * @param lineOffsets - Display-width offset ranges for each formatted line\n * @param parentX - Screen X of the parent Text node\n * @param parentY - Screen Y of the parent Text node (after scroll offset)\n * @param lineCount - Number of formatted lines\n * @param maxHeight - Maximum height (layout height) of the parent Text node\n */\nfunction computeInlineRects(\n childSpans: ChildSpan[],\n lineOffsets: Array<{ start: number; end: number }>,\n parentX: number,\n parentY: number,\n lineCount: number,\n maxHeight: number,\n): void {\n for (const span of childSpans) {\n const rects: Array<{ x: number; y: number; width: number; height: number }> = []\n\n for (let lineIdx = 0; lineIdx < lineCount && lineIdx < maxHeight; lineIdx++) {\n const lineOffset = lineOffsets[lineIdx]\n if (!lineOffset) continue\n\n // Check overlap between span [span.start, span.end) and line [lineOffset.start, lineOffset.end)\n const overlapStart = Math.max(span.start, lineOffset.start)\n const overlapEnd = Math.min(span.end, lineOffset.end)\n if (overlapStart >= overlapEnd) continue\n\n // Convert to screen coordinates\n const rectX = parentX + (overlapStart - lineOffset.start)\n const rectY = parentY + lineIdx\n const rectWidth = overlapEnd - overlapStart\n\n rects.push({ x: rectX, y: rectY, width: rectWidth, height: 1 })\n }\n\n span.node.inlineRects = rects.length > 0 ? rects : null\n }\n}\n",
65
+ "/**\n * Box Rendering - Functions for rendering box elements to the buffer.\n *\n * Contains:\n * - Box rendering (renderBox)\n * - Border rendering (renderBorder)\n * - Scroll indicators (renderScrollIndicators)\n */\n\nimport type { Color, Style, TerminalBuffer } from \"../buffer\"\nimport type { BoxProps, AgNode, Rect } from \"@silvery/ag/types\"\nimport type { Theme } from \"@silvery/theme/types\"\nimport { getPadding } from \"./helpers\"\nimport { getBorderChars, getBorderSize, parseColor } from \"./render-helpers\"\nimport { renderTextLine } from \"./render-text\"\nimport type { NodeRenderState, PipelineContext } from \"./types\"\n\n/**\n * Get the effective background color string for a Box.\n * Returns explicit backgroundColor if set, otherwise theme.bg if theme is set.\n * Used by both renderBox (paint fill) and content-phase (cascade logic).\n */\nexport function getEffectiveBg(props: BoxProps): string | undefined {\n if (props.backgroundColor) return props.backgroundColor as string\n if (props.theme) return (props.theme as Theme).bg\n return undefined\n}\n\n// ============================================================================\n// Box Rendering\n// ============================================================================\n\n/**\n * Render a Box node.\n */\nexport function renderBox(\n _node: AgNode,\n buffer: TerminalBuffer,\n layout: Rect,\n props: BoxProps,\n nodeState: NodeRenderState,\n skipBgFill = false,\n inheritedBg?: Color | null,\n): void {\n const { scrollOffset, clipBounds } = nodeState\n const { x, width, height } = layout\n // Apply scroll offset to y position\n const y = layout.y - scrollOffset\n\n // Skip if completely outside clip bounds\n if (clipBounds) {\n if (y + height <= clipBounds.top || y >= clipBounds.bottom) return\n if (clipBounds.left !== undefined && clipBounds.right !== undefined) {\n if (x + width <= clipBounds.left || x >= clipBounds.right) return\n }\n }\n\n // Fill background if set (explicit backgroundColor or theme.bg).\n // In incremental mode, skipBgFill=true when the box itself hasn't changed\n // (only subtreeDirty). The cloned buffer already has the correct bg fill,\n // and re-filling would destroy child pixels that won't be repainted.\n const effectiveBgStr = getEffectiveBg(props)\n if (effectiveBgStr && !skipBgFill) {\n const bg = parseColor(effectiveBgStr)\n // Clip background fill to bounds\n if (clipBounds) {\n const clippedY = Math.max(y, clipBounds.top)\n const clippedHeight = Math.min(y + height, clipBounds.bottom) - clippedY\n let clippedX = x\n let clippedWidth = width\n if (clipBounds.left !== undefined && clipBounds.right !== undefined) {\n clippedX = Math.max(x, clipBounds.left)\n clippedWidth = Math.min(x + width, clipBounds.right) - clippedX\n }\n if (clippedHeight > 0 && clippedWidth > 0) {\n buffer.fill(clippedX, clippedY, clippedWidth, clippedHeight, { bg })\n }\n } else {\n buffer.fill(x, y, width, height, { bg })\n }\n }\n\n // Render border if set\n if (props.borderStyle) {\n renderBorder(buffer, x, y, width, height, props, clipBounds, inheritedBg)\n }\n}\n\n// ============================================================================\n// Border Rendering\n// ============================================================================\n\n/**\n * Render a border around a box.\n */\nexport function renderBorder(\n buffer: TerminalBuffer,\n x: number,\n y: number,\n width: number,\n height: number,\n props: BoxProps,\n clipBounds?: { top: number; bottom: number; left?: number; right?: number },\n inheritedBg?: Color | null,\n): void {\n const chars = getBorderChars(props.borderStyle ?? \"single\")\n const color = props.borderColor ? parseColor(props.borderColor) : null\n // Preserve the box's background color on border cells. Falls back to\n // inherited bg from the nearest ancestor with backgroundColor, ensuring\n // border cells don't punch transparent holes through parent backgrounds.\n const bg = props.backgroundColor ? parseColor(props.backgroundColor) : (inheritedBg ?? null)\n\n const showTop = props.borderTop !== false\n const showBottom = props.borderBottom !== false\n const showLeft = props.borderLeft !== false\n const showRight = props.borderRight !== false\n\n // Helper to check if a row is visible within clip bounds\n const isRowVisible = (row: number): boolean => {\n if (!clipBounds) return row >= 0 && row < buffer.height\n return row >= clipBounds.top && row < clipBounds.bottom && row < buffer.height\n }\n\n // Helper to check if a column is visible within clip bounds\n const isColVisible = (col: number): boolean => {\n if (clipBounds?.left === undefined || clipBounds.right === undefined) return col >= 0 && col < buffer.width\n return col >= clipBounds.left && col < clipBounds.right && col < buffer.width\n }\n\n // Top border\n if (showTop && isRowVisible(y)) {\n if (showLeft && isColVisible(x)) buffer.setCell(x, y, { char: chars.topLeft, fg: color, bg })\n const hStart = showLeft ? x + 1 : x\n const hEnd = showRight ? x + width - 1 : x + width\n for (let col = hStart; col < hEnd && col < buffer.width; col++) {\n if (isColVisible(col)) buffer.setCell(col, y, { char: chars.horizontal, fg: color, bg })\n }\n if (showRight && x + width - 1 < buffer.width && isColVisible(x + width - 1)) {\n buffer.setCell(x + width - 1, y, { char: chars.topRight, fg: color, bg })\n }\n }\n\n // Side borders — extend range when top/bottom borders are hidden\n const rightVertical = chars.rightVertical ?? chars.vertical\n const sideStart = showTop ? y + 1 : y\n const sideEnd = showBottom ? y + height - 1 : y + height\n for (let row = sideStart; row < sideEnd; row++) {\n if (!isRowVisible(row)) continue\n if (showLeft && isColVisible(x)) buffer.setCell(x, row, { char: chars.vertical, fg: color, bg })\n if (showRight && x + width - 1 < buffer.width && isColVisible(x + width - 1)) {\n buffer.setCell(x + width - 1, row, { char: rightVertical, fg: color, bg })\n }\n }\n\n // Bottom border\n const bottomHorizontal = chars.bottomHorizontal ?? chars.horizontal\n const bottomY = y + height - 1\n if (showBottom && isRowVisible(bottomY)) {\n if (showLeft && isColVisible(x)) {\n buffer.setCell(x, bottomY, { char: chars.bottomLeft, fg: color, bg })\n }\n const bStart = showLeft ? x + 1 : x\n const bEnd = showRight ? x + width - 1 : x + width\n for (let col = bStart; col < bEnd && col < buffer.width; col++) {\n if (isColVisible(col)) buffer.setCell(col, bottomY, { char: bottomHorizontal, fg: color, bg })\n }\n if (showRight && x + width - 1 < buffer.width && isColVisible(x + width - 1)) {\n buffer.setCell(x + width - 1, bottomY, {\n char: chars.bottomRight,\n fg: color,\n bg,\n })\n }\n }\n}\n\n// ============================================================================\n// Outline Rendering\n// ============================================================================\n\n/**\n * Render an outline around a box.\n *\n * Unlike borders, outlines do NOT affect layout dimensions. They draw border\n * characters that OVERLAP the content area at the node's screen rect edges.\n * This is the CSS `outline` equivalent for terminal UI.\n */\nexport function renderOutline(\n buffer: TerminalBuffer,\n x: number,\n y: number,\n width: number,\n height: number,\n props: BoxProps,\n clipBounds?: { top: number; bottom: number; left?: number; right?: number },\n inheritedBg?: Color | null,\n): void {\n const chars = getBorderChars(props.outlineStyle ?? \"single\")\n const color = props.outlineColor ? parseColor(props.outlineColor) : null\n const bg = props.backgroundColor ? parseColor(props.backgroundColor) : (inheritedBg ?? null)\n const attrs = props.outlineDimColor ? { dim: true } : {}\n\n // Helper to check if a row is visible within clip bounds\n const isRowVisible = (row: number): boolean => {\n if (!clipBounds) return row >= 0 && row < buffer.height\n return row >= clipBounds.top && row < clipBounds.bottom && row < buffer.height\n }\n\n // Helper to check if a column is visible within clip bounds\n const isColVisible = (col: number): boolean => {\n if (clipBounds?.left === undefined || clipBounds.right === undefined) return col >= 0 && col < buffer.width\n return col >= clipBounds.left && col < clipBounds.right && col < buffer.width\n }\n\n const showTop = props.outlineTop !== false\n const showBottom = props.outlineBottom !== false\n const showLeft = props.outlineLeft !== false\n const showRight = props.outlineRight !== false\n\n // Top border\n if (showTop && isRowVisible(y)) {\n if (showLeft && isColVisible(x)) buffer.setCell(x, y, { char: chars.topLeft, fg: color, bg, attrs })\n for (let col = x + 1; col < x + width - 1 && col < buffer.width; col++) {\n if (isColVisible(col)) buffer.setCell(col, y, { char: chars.horizontal, fg: color, bg, attrs })\n }\n if (showRight && x + width - 1 < buffer.width && isColVisible(x + width - 1)) {\n buffer.setCell(x + width - 1, y, { char: chars.topRight, fg: color, bg, attrs })\n }\n }\n\n // Side borders — extend range when top/bottom are hidden\n const outlineRightVertical = chars.rightVertical ?? chars.vertical\n const sideStart = showTop ? y + 1 : y\n const sideEnd = showBottom ? y + height - 1 : y + height\n for (let row = sideStart; row < sideEnd; row++) {\n if (!isRowVisible(row)) continue\n if (showLeft && isColVisible(x)) buffer.setCell(x, row, { char: chars.vertical, fg: color, bg, attrs })\n if (showRight && x + width - 1 < buffer.width && isColVisible(x + width - 1)) {\n buffer.setCell(x + width - 1, row, { char: outlineRightVertical, fg: color, bg, attrs })\n }\n }\n\n // Bottom border\n const outlineBottomHorizontal = chars.bottomHorizontal ?? chars.horizontal\n const bottomY = y + height - 1\n if (showBottom && isRowVisible(bottomY)) {\n if (showLeft && isColVisible(x)) {\n buffer.setCell(x, bottomY, { char: chars.bottomLeft, fg: color, bg, attrs })\n }\n for (let col = x + 1; col < x + width - 1 && col < buffer.width; col++) {\n if (isColVisible(col)) buffer.setCell(col, bottomY, { char: outlineBottomHorizontal, fg: color, bg, attrs })\n }\n if (showRight && x + width - 1 < buffer.width && isColVisible(x + width - 1)) {\n buffer.setCell(x + width - 1, bottomY, {\n char: chars.bottomRight,\n fg: color,\n bg,\n attrs,\n })\n }\n }\n}\n\n// ============================================================================\n// Scroll Indicators\n// ============================================================================\n\n/**\n * Render scroll indicators showing hidden items above/below viewport.\n *\n * Two rendering modes:\n * 1. Bordered containers: Indicators appear on the border (e.g., \"───▲42───\")\n * 2. Borderless containers with overflowIndicator: Indicators appear directly\n * after the last visible child (not at the viewport bottom)\n *\n * Uses ▲N for items hidden above, ▼N for items hidden below.\n */\nexport function renderScrollIndicators(\n _node: AgNode,\n buffer: TerminalBuffer,\n layout: Rect,\n props: BoxProps,\n ss: NonNullable<AgNode[\"scrollState\"]>,\n ctx?: PipelineContext,\n): void {\n const border = props.borderStyle ? getBorderSize(props) : { top: 0, bottom: 0, left: 0, right: 0 }\n\n // Inverse bar style: white text on dark background\n const indicatorStyle: Style = {\n fg: 15, // Bright white\n bg: 8, // Dark gray\n attrs: {},\n }\n\n // Determine if we should show indicators for borderless containers\n const showBorderless = props.overflowIndicator === true\n\n // Top indicator\n if (ss.hiddenAbove > 0) {\n const indicator = `\\u25b2${ss.hiddenAbove}`\n\n if (border.top > 0) {\n // Bordered: render centered inverse bar on top border line\n const contentWidth = layout.width - border.left - border.right\n const bar = padCenter(indicator, contentWidth)\n const x = layout.x + border.left\n const y = layout.y\n const maxCol = x + contentWidth\n renderTextLine(buffer, x, y, bar, indicatorStyle, maxCol, undefined, ctx)\n } else if (showBorderless) {\n // Borderless: render centered inverse bar on first content row\n const padding = getPadding(props)\n const contentWidth = layout.width - padding.left - padding.right\n const bar = padCenter(indicator, contentWidth)\n const x = layout.x + padding.left\n const y = layout.y + padding.top\n const maxCol = x + contentWidth\n renderTextLine(buffer, x, y, bar, indicatorStyle, maxCol, undefined, ctx)\n }\n }\n\n // Bottom indicator\n if (ss.hiddenBelow > 0) {\n const indicator = `\\u25bc${ss.hiddenBelow}`\n\n if (border.bottom > 0) {\n // Bordered: render centered inverse bar on bottom border line\n const contentWidth = layout.width - border.left - border.right\n const bar = padCenter(indicator, contentWidth)\n const x = layout.x + border.left\n const y = layout.y + layout.height - 1\n const maxCol = x + contentWidth\n renderTextLine(buffer, x, y, bar, indicatorStyle, maxCol, undefined, ctx)\n } else if (showBorderless) {\n // Borderless: render indicator flush to viewport bottom\n const padding = getPadding(props)\n const contentWidth = layout.width - padding.left - padding.right\n const bar = padCenter(indicator, contentWidth)\n const x = layout.x + padding.left\n const y = layout.y + layout.height - padding.bottom - 1\n const maxCol = x + contentWidth\n renderTextLine(buffer, x, y, bar, indicatorStyle, maxCol, undefined, ctx)\n }\n }\n}\n\n/** Center text within a fixed width, padding with spaces on both sides.\n * Truncates from the right if text exceeds available width. */\nfunction padCenter(text: string, width: number): string {\n if (width <= 0) return \"\"\n if (text.length > width) return text.slice(0, width)\n if (text.length === width) return text\n const leftPad = Math.floor((width - text.length) / 2)\n const rightPad = width - text.length - leftPad\n return \" \".repeat(leftPad) + text + \" \".repeat(rightPad)\n}\n",
66
+ "/**\n * Cascade Predicates — Pure boolean logic extracted from renderNodeToBuffer.\n *\n * These 6 computed values (plus 1 intermediate: textPaintDirty) control the\n * entire incremental rendering cascade. Extracted here for exhaustive testing.\n *\n * The actual rendering code in content-phase.ts computes some inputs inline\n * (absoluteChildMutated, descendantOverflowChanged require node tree access),\n * but the boolean algebra is identical.\n *\n * TRUTH TABLE (key invariants):\n *\n * ┌─────────────────────────────────────────────────────────────────────────────┐\n * │ canSkipEntireSubtree │\n * │ = hasPrevBuffer && !contentDirty && !stylePropsDirty && !layoutChanged │\n * │ && !subtreeDirty && !childrenDirty && !childPositionChanged │\n * │ && !ancestorLayoutChanged │\n * │ True only when hasPrevBuffer=true AND all 7 dirty flags are false. │\n * │ When true, the node is skipped entirely (clone has correct pixels). │\n * │ NOTE: content-phase.ts also checks !scrollOffsetChanged (node-level │\n * │ defensive check for scroll containers — not modeled here). │\n * ├─────────────────────────────────────────────────────────────────────────────┤\n * │ textPaintDirty (intermediate) │\n * │ = isTextNode && stylePropsDirty │\n * │ For TEXT nodes, stylePropsDirty IS a content area change (no borders). │\n * ├─────────────────────────────────────────────────────────────────────────────┤\n * │ contentAreaAffected │\n * │ = contentDirty || layoutChanged || childPositionChanged │\n * │ || childrenDirty || bgDirty || textPaintDirty │\n * │ || absoluteChildMutated || descendantOverflowChanged │\n * │ True when anything changed that affects the node's content area. │\n * │ Excludes border-only paint changes for BOX nodes. │\n * ├─────────────────────────────────────────────────────────────────────────────┤\n * │ bgRefillNeeded │\n * │ = hasPrevBuffer && !contentAreaAffected && subtreeDirty && hasBgColor │\n * │ Descendant changed inside a bg-bearing Box. Forces bg refill. │\n * │ Mutually exclusive with contentAreaAffected (gated on !contentAreaAffected).│\n * ├─────────────────────────────────────────────────────────────────────────────┤\n * │ contentRegionCleared │\n * │ = (hasPrevBuffer || ancestorCleared) && contentAreaAffected │\n * │ && !hasBgColor │\n * │ Clear region with inherited bg when content changed but no own bg fill. │\n * │ False when hasPrevBuffer=false AND ancestorCleared=false (fresh buffer). │\n * ├─────────────────────────────────────────────────────────────────────────────┤\n * │ skipBgFill │\n * │ = hasPrevBuffer && !ancestorCleared && !contentAreaAffected │\n * │ && !bgRefillNeeded │\n * │ Clone already has correct bg. Skip redundant fill. │\n * ├─────────────────────────────────────────────────────────────────────────────┤\n * │ childrenNeedFreshRender │\n * │ = (hasPrevBuffer || ancestorCleared) && (contentAreaAffected │\n * │ || bgRefillNeeded) │\n * │ Children must re-render (childHasPrev=false). │\n * │ False when hasPrevBuffer=false AND ancestorCleared=false (fresh buffer). │\n * └─────────────────────────────────────────────────────────────────────────────┘\n *\n * KEY INVARIANTS:\n * 1. contentAreaAffected && bgRefillNeeded can never both be true\n * (bgRefillNeeded is gated on !contentAreaAffected)\n * 2. contentRegionCleared && skipBgFill can never both be true\n * (contentRegionCleared requires contentAreaAffected; skipBgFill requires !contentAreaAffected)\n * 3. When !hasPrevBuffer && !ancestorCleared: contentRegionCleared=false, childrenNeedFreshRender=false\n * (both gated on hasPrevBuffer || ancestorCleared)\n * 4. canSkipEntireSubtree requires hasPrevBuffer=true\n */\n\n/** Inputs to the cascade predicates (all boolean flags from renderNodeToBuffer) */\nexport interface CascadeInputs {\n hasPrevBuffer: boolean\n contentDirty: boolean\n stylePropsDirty: boolean\n layoutChanged: boolean\n subtreeDirty: boolean\n childrenDirty: boolean\n childPositionChanged: boolean\n ancestorLayoutChanged: boolean\n ancestorCleared: boolean\n bgDirty: boolean\n isTextNode: boolean\n hasBgColor: boolean\n absoluteChildMutated: boolean\n descendantOverflowChanged: boolean\n}\n\n/** Outputs of the cascade predicates */\nexport interface CascadeOutputs {\n canSkipEntireSubtree: boolean\n contentAreaAffected: boolean\n bgRefillNeeded: boolean\n contentRegionCleared: boolean\n skipBgFill: boolean\n childrenNeedFreshRender: boolean\n}\n\n/**\n * Compute all cascade predicate values from boolean inputs.\n *\n * This is a pure function — no side effects, no node dependencies.\n * The formulas exactly match those in content-phase.ts renderNodeToBuffer.\n */\nexport function computeCascade(inputs: CascadeInputs): CascadeOutputs {\n const {\n hasPrevBuffer,\n contentDirty,\n stylePropsDirty,\n layoutChanged,\n subtreeDirty,\n childrenDirty,\n childPositionChanged,\n ancestorLayoutChanged,\n ancestorCleared,\n bgDirty,\n isTextNode,\n hasBgColor,\n absoluteChildMutated,\n descendantOverflowChanged,\n } = inputs\n\n // FAST PATH: Skip unchanged subtrees when we have a valid previous buffer.\n const canSkipEntireSubtree =\n hasPrevBuffer &&\n !contentDirty &&\n !stylePropsDirty &&\n !layoutChanged &&\n !subtreeDirty &&\n !childrenDirty &&\n !childPositionChanged &&\n !ancestorLayoutChanged\n\n // Intermediate: for TEXT nodes, stylePropsDirty IS a content area change (no borders).\n const textPaintDirty = isTextNode && stylePropsDirty\n\n // Did this node's CONTENT AREA change?\n const contentAreaAffected =\n contentDirty ||\n layoutChanged ||\n childPositionChanged ||\n childrenDirty ||\n bgDirty ||\n textPaintDirty ||\n absoluteChildMutated ||\n descendantOverflowChanged\n\n // Descendant changed inside a bg-bearing Box (forces bg refill).\n const bgRefillNeeded = hasPrevBuffer && !contentAreaAffected && subtreeDirty && hasBgColor\n\n // Clear region with inherited bg when content changed but no own bg fill.\n const contentRegionCleared = (hasPrevBuffer || ancestorCleared) && contentAreaAffected && !hasBgColor\n\n // Skip bg fill when clone already has correct bg at this position.\n const skipBgFill = hasPrevBuffer && !ancestorCleared && !contentAreaAffected && !bgRefillNeeded\n\n // Children must re-render (content area modified OR bg needs refresh).\n const childrenNeedFreshRender = (hasPrevBuffer || ancestorCleared) && (contentAreaAffected || bgRefillNeeded)\n\n return {\n canSkipEntireSubtree,\n contentAreaAffected,\n bgRefillNeeded,\n contentRegionCleared,\n skipBgFill,\n childrenNeedFreshRender,\n }\n}\n",
67
+ "/**\n * Phase 3: Content Phase\n *\n * Render all nodes to a terminal buffer.\n *\n * This module orchestrates the rendering process by traversing the node tree\n * and delegating to specialized rendering functions for boxes and text.\n *\n * Layout (top-down):\n * contentPhase → renderNodeToBuffer → buildCascadeInputs + computeCascade\n * → traceRenderDecision (diagnostics)\n * → executeRegionClearing\n * → renderOwnContent\n * → renderScrollContainerChildren / renderNormalChildren\n * Helpers: clearDirtyFlags, hasChildPositionChanged, computeChildClipBounds\n * Region clearing: findInheritedBg, clearNodeRegion, clearExcessArea, clippedFill\n */\n\nimport { createLogger } from \"loggily\"\nimport type { Color } from \"../buffer\"\nimport { TerminalBuffer } from \"../buffer\"\nimport type { BoxProps, AgNode, TextProps } from \"@silvery/ag/types\"\nimport { getBorderSize, getPadding } from \"./helpers\"\nimport { renderBox, renderOutline, renderScrollIndicators, getEffectiveBg } from \"./render-box\"\nimport { parseColor } from \"./render-helpers\"\nimport { clearBgConflictWarnings, renderText, setBgConflictMode } from \"./render-text\"\nimport { pushContextTheme, popContextTheme } from \"@silvery/theme/state\"\nimport type { Theme } from \"@silvery/theme/types\"\nimport { computeCascade } from \"./cascade-predicates\"\nimport type { CascadeOutputs } from \"./cascade-predicates\"\nimport type { ClipBounds, ContentPhaseStats, NodeRenderState, NodeTraceEntry, PipelineContext } from \"./types\"\n\nconst contentLog = createLogger(\"silvery:content\")\nconst traceLog = createLogger(\"silvery:content:trace\")\nconst cellLog = createLogger(\"silvery:content:cell\")\n\n/**\n * Render all nodes to a terminal buffer.\n *\n * @param root The root SilveryNode\n * @param prevBuffer Previous buffer for incremental rendering (optional)\n * @returns A TerminalBuffer with the rendered content\n */\nexport function contentPhase(root: AgNode, prevBuffer?: TerminalBuffer | null, ctx?: PipelineContext): TerminalBuffer {\n const layout = root.contentRect\n if (!layout) {\n throw new Error(\"contentPhase called before layout phase\")\n }\n\n // Resolve instrumentation from ctx (if provided) or module-level globals\n const instrumentEnabled = ctx?.instrumentEnabled ?? _instrumentEnabled\n const stats = ctx?.stats ?? _contentPhaseStats\n const nodeTrace = ctx?.nodeTrace ?? _nodeTrace\n const nodeTraceEnabled = ctx?.nodeTraceEnabled ?? _nodeTraceEnabled\n\n // Clone prevBuffer if same dimensions, else create fresh\n const hasPrevBuffer = prevBuffer && prevBuffer.width === layout.width && prevBuffer.height === layout.height\n\n if (instrumentEnabled) {\n _contentPhaseCallCount++\n stats._prevBufferNull = prevBuffer == null ? 1 : 0\n stats._prevBufferDimMismatch = prevBuffer && !hasPrevBuffer ? 1 : 0\n stats._hasPrevBuffer = hasPrevBuffer ? 1 : 0\n stats._layoutW = layout.width\n stats._layoutH = layout.height\n stats._prevW = prevBuffer?.width ?? 0\n stats._prevH = prevBuffer?.height ?? 0\n stats._callCount = _contentPhaseCallCount\n }\n\n const t0 = instrumentEnabled ? performance.now() : 0\n const buffer = hasPrevBuffer ? prevBuffer.clone() : new TerminalBuffer(layout.width, layout.height)\n const tClone = instrumentEnabled ? performance.now() - t0 : 0\n\n const t1 = instrumentEnabled ? performance.now() : 0\n renderNodeToBuffer(\n root,\n buffer,\n {\n scrollOffset: 0,\n clipBounds: undefined,\n hasPrevBuffer: !!hasPrevBuffer,\n ancestorCleared: false,\n bufferIsCloned: !!hasPrevBuffer,\n ancestorLayoutChanged: false,\n },\n ctx,\n )\n const tRender = instrumentEnabled ? performance.now() - t1 : 0\n\n if (instrumentEnabled) {\n // Expose sub-phase timing for profiling\n const snap = {\n clone: tClone,\n render: tRender,\n ...structuredClone(stats),\n }\n // Retain globalThis for programmatic consumers (STRICT diagnostics, perf profiling)\n ;(globalThis as any).__silvery_content_detail = snap\n const arr = ((globalThis as any).__silvery_content_all ??= [] as (typeof snap)[])\n arr.push(snap)\n // Route human-readable output through loggily\n contentLog.debug?.(\n `frame ${snap._callCount}: ${snap.nodesRendered}/${snap.nodesVisited} rendered, ${snap.nodesSkipped} skipped (${tClone.toFixed(1)}ms clone, ${tRender.toFixed(1)}ms render)`,\n )\n for (const key of Object.keys(stats) as (keyof ContentPhaseStats)[]) {\n ;(stats as any)[key] = 0\n }\n stats.cascadeMinDepth = 999\n stats.cascadeNodes = \"\"\n stats.scrollClearReason = \"\"\n stats.normalRepaintReason = \"\"\n }\n\n // Export node trace for SILVERY_STRICT diagnosis\n if (nodeTraceEnabled && nodeTrace.length > 0) {\n // Retain globalThis for programmatic consumers (STRICT diagnostics)\n const traceArr = ((globalThis as any).__silvery_node_trace ??= [] as NodeTraceEntry[][])\n traceArr.push([...nodeTrace])\n // Route human-readable output through loggily\n traceLog.debug?.(`${nodeTrace.length} nodes traced`)\n nodeTrace.length = 0\n }\n\n // Sync prevLayout after content phase to prevent staleness on subsequent frames.\n // Without this, prevLayout stays at the old value from propagateLayout, causing\n // hasChildPositionChanged and clearExcessArea to use stale coordinates.\n syncPrevLayout(root)\n\n return buffer\n}\n\n/**\n * Sync prevLayout to contentRect for all nodes in the tree.\n *\n * Called at the end of each contentPhase pass. This prevents:\n * 1. The O(N) staleness bug where prevLayout drifts from contentRect\n * causing !rectEqual to always be true on subsequent frames.\n * 2. Stale old-bounds references in clearExcessArea on doRender iteration 2+.\n * 3. Asymmetry between incremental and fresh renders — doFreshRender's layout\n * phase syncs prevLayout before content, so without this, the real render\n * has null/stale prevLayout while fresh has synced prevLayout, causing\n * different cascade behavior (layoutChanged true vs false).\n */\nfunction syncPrevLayout(root: AgNode): void {\n const stack: AgNode[] = [root]\n while (stack.length > 0) {\n const node = stack.pop()!\n node.prevLayout = node.contentRect\n const children = node.children\n for (let i = children.length - 1; i >= 0; i--) {\n stack.push(children[i]!)\n }\n }\n}\n\n/** Instrumentation enabled when SILVERY_STRICT or SILVERY_INSTRUMENT is set */\nconst _instrumentEnabled =\n typeof process !== \"undefined\" && !!(process.env?.SILVERY_STRICT || process.env?.SILVERY_INSTRUMENT)\n\n/** Mutable stats counters — reset after each contentPhase call */\nconst _contentPhaseStats: ContentPhaseStats = {\n nodesVisited: 0,\n nodesRendered: 0,\n nodesSkipped: 0,\n textNodes: 0,\n boxNodes: 0,\n clearOps: 0,\n noPrevBuffer: 0,\n flagContentDirty: 0,\n flagStylePropsDirty: 0,\n flagLayoutChanged: 0,\n flagSubtreeDirty: 0,\n flagChildrenDirty: 0,\n flagChildPositionChanged: 0,\n flagAncestorLayoutChanged: 0,\n scrollContainerCount: 0,\n scrollViewportCleared: 0,\n scrollClearReason: \"\",\n normalChildrenRepaint: 0,\n normalRepaintReason: \"\",\n cascadeMinDepth: 999,\n cascadeNodes: \"\",\n _prevBufferNull: 0,\n _prevBufferDimMismatch: 0,\n _hasPrevBuffer: 0,\n _layoutW: 0,\n _layoutH: 0,\n _prevW: 0,\n _prevH: 0,\n _callCount: 0,\n}\n\nlet _contentPhaseCallCount = 0\n\n/** Module-level node trace (fallback when ctx.nodeTrace is not provided) */\nconst _nodeTrace: NodeTraceEntry[] = []\nconst _nodeTraceEnabled = typeof process !== \"undefined\" && !!process.env?.SILVERY_STRICT\n\n/** DIAG: compute node depth in tree */\nfunction _getNodeDepth(node: AgNode): number {\n let depth = 0\n let n: AgNode | null = node.parent\n while (n) {\n depth++\n n = n.parent\n }\n return depth\n}\n\n// Re-export for consumers who need to clear bg conflict warnings\nexport { clearBgConflictWarnings, setBgConflictMode }\n\n// ============================================================================\n// Core Rendering\n// ============================================================================\n\n/**\n * Render a single node to the buffer.\n */\nfunction renderNodeToBuffer(\n node: AgNode,\n buffer: TerminalBuffer,\n nodeState: NodeRenderState,\n ctx?: PipelineContext,\n): void {\n const {\n scrollOffset,\n clipBounds,\n hasPrevBuffer,\n ancestorCleared,\n bufferIsCloned,\n ancestorLayoutChanged = false,\n } = nodeState\n // Resolve instrumentation from ctx or module globals\n const instrumentEnabled = ctx?.instrumentEnabled ?? _instrumentEnabled\n const stats = ctx?.stats ?? _contentPhaseStats\n const nodeTrace = ctx?.nodeTrace ?? _nodeTrace\n const nodeTraceEnabled = ctx?.nodeTraceEnabled ?? _nodeTraceEnabled\n if (instrumentEnabled) stats.nodesVisited++\n const layout = node.contentRect\n if (!layout) return\n\n // Skip nodes without Yoga (raw text and virtual text nodes)\n // Their content is rendered by their parent silvery-text via collectTextContent()\n if (!node.layoutNode) {\n // Clear dirty flags so markSubtreeDirty() can propagate future updates.\n // Without this, virtual text children keep stale subtreeDirty=true from\n // creation, causing markSubtreeDirty to stop early and never reach the\n // layout ancestor — producing 0-byte diffs on text content changes.\n clearVirtualTextFlags(node)\n return\n }\n\n // Skip hidden nodes (Suspense support)\n // When a Suspense boundary shows a fallback, the hidden subtree is not rendered\n if (node.hidden) {\n clearDirtyFlags(node)\n return\n }\n\n const props = node.props as BoxProps & TextProps\n\n // Skip display=\"none\" nodes - they have 0x0 dimensions and shouldn't render\n // Also skip their children since the entire subtree is hidden\n if (props.display === \"none\") {\n clearDirtyFlags(node)\n return\n }\n\n // Skip nodes entirely off-screen (viewport clipping).\n // The scroll container's VirtualList already handles most culling, but this\n // catches any remaining nodes rendered below/above the visible area.\n //\n // IMPORTANT: Don't clear dirty flags on nodes that were never rendered.\n // Just skip them and leave their flags intact so they render correctly\n // when scrolled into view.\n //\n // Why this matters: clearDirtyFlags on off-screen nodes prevents them from\n // rendering when they later become visible:\n // 1. Node off-screen → clearDirtyFlags → all flags false\n // 2. Scroll brings node on-screen with hasPrevBuffer=true\n // 3. canSkipEntireSubtree = true (all flags clean) → node SKIPPED\n // 4. Buffer has stale/blank pixels → blank content visible\n //\n // By preserving dirty flags, the node forces rendering when it enters\n // the visible area. The subtreeDirty flag on ancestors is maintained\n // because we don't clear it — markSubtreeDirty() already set it during\n // reconciliation/layout, and not clearing here preserves that signal.\n const screenY = layout.y - scrollOffset\n if (screenY >= buffer.height || screenY + layout.height <= 0) {\n return\n }\n\n // FAST PATH: Skip entire subtree if unchanged and we have a previous buffer\n // The buffer was cloned from prevBuffer, so skipped nodes keep their rendered output\n //\n // layoutChanged: did this node's layout position/size change?\n // Uses layoutChangedThisFrame (set by propagateLayout in layout phase) instead of\n // the stale !rectEqual(prevLayout, contentRect). The rect comparison is asymmetric\n // between incremental and fresh renders: doFreshRender's layout phase syncs\n // prevLayout=contentRect before content, making layoutChanged=false, while the\n // real render may have prevLayout=null (new nodes), making layoutChanged=true.\n // This asymmetry causes contentAreaAffected→clearNodeRegion to fire in incremental\n // but not fresh, wiping sibling content. layoutChangedThisFrame is symmetric.\n const layoutChanged = node.layoutChangedThisFrame\n\n // Check if any child shifted position (sibling shift from size changes).\n // Gap space between children belongs to this container, so must re-render.\n const childPositionChanged = !!(hasPrevBuffer && !layoutChanged && hasChildPositionChanged(node))\n\n // Check if this node is a scroll container whose offset changed.\n // The scroll phase (layout-phase.ts) sets subtreeDirty on the scroll container\n // when offset changes, and the reconciler propagates subtreeDirty to ancestors\n // when scrollTo/scrollOffset props change. However, this defensive check\n // catches edge cases where scroll offset changes without proper dirty\n // propagation — e.g., layout feedback loops that alter scroll state between\n // render passes without a reconciler commit.\n const scrollOffsetChanged = !!(node.scrollState && node.scrollState.offset !== node.scrollState.prevOffset)\n\n // FAST PATH: Skip unchanged subtrees when we have a valid previous buffer.\n // The cloned buffer already has correct pixels for clean nodes.\n // SILVERY_STRICT=1 verifies this by comparing incremental vs fresh renders.\n //\n // ancestorLayoutChanged: an ancestor's layout position/size changed this frame.\n // Even if this node's own flags are clean, its pixels in the cloned buffer are\n // at coordinates relative to the old ancestor layout. The node must re-render\n // at its new absolute position. This is a safety net — normally the parent's\n // childrenNeedFreshRender cascade sets childHasPrev=false which prevents skipping,\n // but ancestorLayoutChanged catches cases where the cascade doesn't propagate\n // (e.g., ancestor with backgroundColor that breaks the ancestorCleared chain).\n const canSkipEntireSubtree =\n hasPrevBuffer &&\n !node.contentDirty &&\n !node.stylePropsDirty &&\n !layoutChanged &&\n !node.subtreeDirty &&\n !node.childrenDirty &&\n !childPositionChanged &&\n !ancestorLayoutChanged &&\n !scrollOffsetChanged\n\n // Node ID for tracing (only trace named nodes to keep compact)\n const _nodeId = instrumentEnabled ? ((props.id as string | undefined) ?? \"\") : \"\"\n const _traceThis = instrumentEnabled && nodeTraceEnabled && _nodeId\n\n // Cell debug: log nodes that cover the target cell.\n // Retained on globalThis for STRICT diagnostics (create-app.tsx reads the accumulated log).\n const _cellDbg = (globalThis as any).__silvery_cell_debug as { x: number; y: number; log: string[] } | undefined\n const _coversCellNow =\n _cellDbg &&\n layout.x <= _cellDbg.x &&\n layout.x + layout.width > _cellDbg.x &&\n screenY <= _cellDbg.y &&\n screenY + layout.height > _cellDbg.y\n const _coversCellPrev =\n _cellDbg &&\n node.prevLayout &&\n node.prevLayout.x <= _cellDbg.x &&\n node.prevLayout.x + node.prevLayout.width > _cellDbg.x &&\n node.prevLayout.y - scrollOffset <= _cellDbg.y &&\n node.prevLayout.y - scrollOffset + node.prevLayout.height > _cellDbg.y\n\n if (canSkipEntireSubtree) {\n if (_cellDbg && (_coversCellNow || _coversCellPrev)) {\n const id = (props.id as string) ?? node.type\n const depth = _getNodeDepth(node)\n const prev = node.prevLayout\n const msg =\n `SKIP ${id}@${depth} rect=${layout.x},${screenY} ${layout.width}x${layout.height}` +\n ` prev=${prev ? `${prev.x},${prev.y - scrollOffset} ${prev.width}x${prev.height}` : \"null\"}` +\n ` coversNow=${_coversCellNow} coversPrev=${_coversCellPrev}`\n _cellDbg.log.push(msg)\n cellLog.debug?.(msg)\n }\n if (instrumentEnabled) {\n stats.nodesSkipped++\n if (_traceThis) {\n nodeTrace.push({\n id: _nodeId,\n type: node.type,\n depth: _getNodeDepth(node),\n rect: `${layout.x},${layout.y} ${layout.width}x${layout.height}`,\n prevLayout: node.prevLayout\n ? `${node.prevLayout.x},${node.prevLayout.y} ${node.prevLayout.width}x${node.prevLayout.height}`\n : \"null\",\n hasPrev: hasPrevBuffer,\n ancestorCleared,\n flags: \"\",\n decision: \"SKIPPED\",\n layoutChanged,\n })\n }\n }\n clearDirtyFlags(node)\n return\n }\n if (instrumentEnabled) {\n stats.nodesRendered++\n if (!hasPrevBuffer) stats.noPrevBuffer++\n if (node.contentDirty) stats.flagContentDirty++\n if (node.stylePropsDirty) stats.flagStylePropsDirty++\n if (layoutChanged) stats.flagLayoutChanged++\n if (node.subtreeDirty) stats.flagSubtreeDirty++\n if (node.childrenDirty) stats.flagChildrenDirty++\n if (childPositionChanged) stats.flagChildPositionChanged++\n if (ancestorLayoutChanged) stats.flagAncestorLayoutChanged++\n }\n\n // Push per-subtree theme override (if this Box has a theme prop).\n // Placed after all early returns and fast-path skip — only active during\n // actual rendering. Popped at the end of this function after all child passes.\n const nodeTheme = (props as BoxProps).theme as Theme | undefined\n if (nodeTheme) pushContextTheme(nodeTheme)\n try {\n // Check if this is a scrollable container\n const isScrollContainer = props.overflow === \"scroll\" && node.scrollState\n\n // Build tree-dependent cascade inputs (child traversal), then compute cascade.\n // See cascade-predicates.ts for the truth table and invariants.\n const { absoluteChildMutated, descendantOverflowChanged } = buildCascadeInputs(node, hasPrevBuffer)\n const cascade = computeCascade({\n hasPrevBuffer,\n contentDirty: node.contentDirty,\n stylePropsDirty: node.stylePropsDirty,\n layoutChanged,\n subtreeDirty: node.subtreeDirty,\n childrenDirty: node.childrenDirty,\n childPositionChanged,\n ancestorLayoutChanged,\n ancestorCleared,\n bgDirty: node.bgDirty,\n isTextNode: node.type === \"silvery-text\",\n hasBgColor: !!getEffectiveBg(props),\n absoluteChildMutated,\n descendantOverflowChanged,\n })\n const { contentRegionCleared, skipBgFill, childrenNeedFreshRender } = cascade\n\n // DIAG: Per-node trace, cascade tracking, and cell debug\n if (instrumentEnabled || (_cellDbg && (_coversCellNow || _coversCellPrev))) {\n traceRenderDecision(\n node,\n props,\n layout,\n screenY,\n scrollOffset,\n hasPrevBuffer,\n ancestorCleared,\n layoutChanged,\n childPositionChanged,\n cascade,\n _nodeId,\n _traceThis,\n _cellDbg,\n _coversCellNow,\n _coversCellPrev,\n instrumentEnabled,\n stats,\n nodeTrace,\n )\n }\n\n // Clear stale regions in the cloned buffer before rendering content.\n executeRegionClearing(\n node,\n buffer,\n layout,\n scrollOffset,\n clipBounds,\n bufferIsCloned,\n layoutChanged,\n contentRegionCleared,\n descendantOverflowChanged,\n instrumentEnabled,\n stats,\n )\n\n // Determine if this node's own content (border, bg, text) needs repainting.\n // When hasPrevBuffer=true and only subtreeDirty is set (no own visual changes),\n // the cloned buffer already has correct own content. Skipping avoids a border\n // redraw that would overwrite child content rendered on top of the border area\n // (e.g., text overflow into border columns).\n const needsOwnRepaint =\n !hasPrevBuffer ||\n ancestorCleared ||\n ancestorLayoutChanged ||\n cascade.contentAreaAffected ||\n node.stylePropsDirty ||\n cascade.bgRefillNeeded\n\n // Render this node's own content (box bg/border or text).\n // Compute boxInheritedBg even when skipping own repaint — it's needed by\n // outline rendering (after children) and may be needed by child rendering.\n const boxInheritedBg =\n node.type === \"silvery-box\" && !getEffectiveBg(props) ? findInheritedBg(node).color : undefined\n if (needsOwnRepaint) {\n renderOwnContent(node, buffer, layout, props, nodeState, skipBgFill, instrumentEnabled, stats, ctx)\n }\n\n // Render children\n if (isScrollContainer) {\n renderScrollContainerChildren(node, buffer, props, nodeState, contentRegionCleared, childrenNeedFreshRender, ctx)\n\n // Render overflow indicators AFTER children so they survive viewport clear.\n // renderScrollContainerChildren may clear the viewport (Tier 2) which would\n // overwrite indicators drawn before children.\n renderScrollIndicators(node, buffer, layout, props, node.scrollState!, ctx)\n } else {\n renderNormalChildren(\n node,\n buffer,\n props,\n nodeState,\n childPositionChanged,\n contentRegionCleared,\n childrenNeedFreshRender,\n ctx,\n )\n }\n\n // Render outline AFTER children — outline overlaps content at edges\n if (node.type === \"silvery-box\" && props.outlineStyle) {\n const { x, width, height } = layout\n const y = layout.y - scrollOffset\n renderOutline(buffer, x, y, width, height, props, clipBounds, boxInheritedBg)\n }\n\n // Clear dirty flags (current node only — children clear their own when rendered)\n clearNodeDirtyFlags(node)\n } finally {\n // Pop per-subtree theme override (after ALL child passes including absolute/sticky)\n if (nodeTheme) popContextTheme()\n }\n}\n\n// ============================================================================\n// Cascade Input Helpers\n// ============================================================================\n\n/**\n * Build tree-dependent cascade inputs that require child traversal.\n *\n * These feed into computeCascade() alongside the node's own dirty flags.\n * Separated from renderNodeToBuffer to keep the main function focused on\n * the rendering flow rather than child traversal details.\n */\nfunction buildCascadeInputs(\n node: AgNode,\n hasPrevBuffer: boolean,\n): { absoluteChildMutated: boolean; descendantOverflowChanged: boolean } {\n if (!hasPrevBuffer || !node.subtreeDirty || node.children === undefined) {\n return { absoluteChildMutated: false, descendantOverflowChanged: false }\n }\n\n // absoluteChildMutated: an absolute child had structural changes (children\n // mount/unmount/reorder, layout change, child position shift). Forces parent\n // to clear stale overlay pixels in gap areas.\n const absoluteChildMutated = node.children.some((child) => {\n const cp = child.props as BoxProps\n return (\n cp.position === \"absolute\" &&\n (child.childrenDirty || child.layoutChangedThisFrame || hasChildPositionChanged(child))\n )\n })\n\n // descendantOverflowChanged: a descendant was overflowing THIS node's rect\n // in the previous frame and its layout changed. Must be detected recursively\n // at THIS level so borders are properly restored.\n const descendantOverflowChanged = hasDescendantOverflowChanged(node)\n\n return { absoluteChildMutated, descendantOverflowChanged }\n}\n\n// ============================================================================\n// Render Decision Tracing\n// ============================================================================\n\n/**\n * Log per-node trace, cascade tracking, and cell debug info.\n *\n * Gated on instrumentation or cell debug being active. Separated from\n * renderNodeToBuffer to keep the main function focused on rendering logic.\n */\nfunction traceRenderDecision(\n node: AgNode,\n props: BoxProps & TextProps,\n layout: NonNullable<AgNode[\"contentRect\"]>,\n screenY: number,\n scrollOffset: number,\n hasPrevBuffer: boolean,\n ancestorCleared: boolean,\n layoutChanged: boolean,\n childPositionChanged: boolean,\n cascade: CascadeOutputs,\n _nodeId: string,\n _traceThis: string | false | 0 | \"\",\n _cellDbg: { x: number; y: number; log: string[] } | undefined,\n _coversCellNow: boolean | undefined,\n _coversCellPrev: boolean | null | undefined,\n instrumentEnabled: boolean,\n stats: ContentPhaseStats,\n nodeTrace: NodeTraceEntry[],\n): void {\n const { contentAreaAffected, contentRegionCleared, skipBgFill, childrenNeedFreshRender } = cascade\n\n // Per-node trace and cascade tracking (gated on instrumentation)\n if (instrumentEnabled) {\n if (_traceThis) {\n const flagStr = [\n node.contentDirty && \"C\",\n node.stylePropsDirty && \"P\",\n node.bgDirty && \"B\",\n node.subtreeDirty && \"S\",\n node.childrenDirty && \"Ch\",\n childPositionChanged && \"CP\",\n ]\n .filter(Boolean)\n .join(\",\")\n const childrenNeedRepaint_ = node.childrenDirty || childPositionChanged || childrenNeedFreshRender\n const childHasPrev_ = childrenNeedRepaint_ ? false : hasPrevBuffer\n const childAncestorCleared_ = contentRegionCleared || (ancestorCleared && !getEffectiveBg(props))\n nodeTrace.push({\n id: _nodeId,\n type: node.type,\n depth: _getNodeDepth(node),\n rect: `${layout.x},${layout.y} ${layout.width}x${layout.height}`,\n prevLayout: node.prevLayout\n ? `${node.prevLayout.x},${node.prevLayout.y} ${node.prevLayout.width}x${node.prevLayout.height}`\n : \"null\",\n hasPrev: hasPrevBuffer,\n ancestorCleared,\n flags: flagStr,\n decision: \"RENDER\",\n layoutChanged,\n contentAreaAffected,\n contentRegionCleared,\n childrenNeedFreshRender,\n childHasPrev: childHasPrev_,\n childAncestorCleared: childAncestorCleared_,\n skipBgFill,\n bgColor: props.backgroundColor as string | undefined,\n })\n }\n if (childrenNeedFreshRender && node.children.length > 0) {\n const depth = _getNodeDepth(node)\n if (depth < stats.cascadeMinDepth) {\n stats.cascadeMinDepth = depth\n }\n const id = (node.props as Record<string, unknown>).id ?? node.type\n const flags = [\n node.contentDirty && \"C\",\n node.stylePropsDirty && \"P\",\n node.childrenDirty && \"Ch\",\n layoutChanged && \"L\",\n childPositionChanged && \"CP\",\n ]\n .filter(Boolean)\n .join(\"\")\n const entry = `${id}@${depth}[${flags}:${node.children.length}ch]`\n stats.cascadeNodes += (stats.cascadeNodes ? \" \" : \"\") + entry\n }\n }\n\n // Cell debug: log render decision for nodes covering target cell\n if (_cellDbg && (_coversCellNow || _coversCellPrev)) {\n const id = (props.id as string) ?? node.type\n const depth = _getNodeDepth(node)\n const prev = node.prevLayout\n const flags = [\n node.contentDirty && \"C\",\n node.stylePropsDirty && \"P\",\n layoutChanged && \"L\",\n node.subtreeDirty && \"S\",\n node.childrenDirty && \"Ch\",\n childPositionChanged && \"CP\",\n node.bgDirty && \"B\",\n ]\n .filter(Boolean)\n .join(\",\")\n const msg =\n `RENDER ${id}@${depth} rect=${layout.x},${screenY} ${layout.width}x${layout.height}` +\n ` prev=${prev ? `${prev.x},${prev.y - scrollOffset} ${prev.width}x${prev.height}` : \"null\"}` +\n ` flags=[${flags}] hasPrev=${hasPrevBuffer} ancClr=${ancestorCleared}` +\n ` caa=${contentAreaAffected} prc=${contentRegionCleared} prm=${childrenNeedFreshRender}` +\n ` coversNow=${_coversCellNow} coversPrev=${_coversCellPrev}` +\n ` bg=${props.backgroundColor ?? \"none\"}`\n _cellDbg.log.push(msg)\n cellLog.debug?.(msg)\n }\n}\n\n// ============================================================================\n// Region Clearing (executeRegionClearing)\n// ============================================================================\n\n/**\n * Handle all region clearing before rendering own content.\n *\n * Three clearing paths:\n * 1. contentRegionCleared: clear the node's region with inherited bg (no own bg)\n * 2. Excess area: clear stale pixels when a node shrank (even without contentRegionCleared)\n * 3. Descendant overflow: clear areas where descendants previously overflowed this node's rect\n *\n * All clearing runs BEFORE renderBox/renderText so borders drawn later are not overwritten.\n */\nfunction executeRegionClearing(\n node: AgNode,\n buffer: TerminalBuffer,\n layout: NonNullable<AgNode[\"contentRect\"]>,\n scrollOffset: number,\n clipBounds: ClipBounds | undefined,\n bufferIsCloned: boolean,\n layoutChanged: boolean,\n contentRegionCleared: boolean,\n descendantOverflowChanged: boolean,\n instrumentEnabled: boolean,\n stats: ContentPhaseStats,\n): void {\n if (contentRegionCleared) {\n if (instrumentEnabled) stats.clearOps++\n clearNodeRegion(node, buffer, layout, scrollOffset, clipBounds, layoutChanged)\n } else if (bufferIsCloned && layoutChanged && node.prevLayout) {\n // Even when contentRegionCleared is false, a shrinking node needs its excess\n // area cleared. Key scenario: absolute-positioned overlays (e.g., search dialog)\n // that shrink while normal-flow siblings are dirty -- forceRepaint sets\n // hasPrevBuffer=false + ancestorCleared=false, making contentRegionCleared=false,\n // but the cloned buffer still has stale pixels from the old larger layout.\n // Also applies to nodes WITH backgroundColor: renderBox fills only the NEW\n // (smaller) region, leaving stale pixels in the excess area.\n //\n // Gated on bufferIsCloned: on a fresh buffer (e.g., multi-pass resize where\n // dimensions changed between passes), there are no stale pixels to clear.\n // Without this guard, clearExcessArea writes inherited bg into cells that\n // doFreshRender leaves as default, causing STRICT mismatches.\n clearExcessArea(node, buffer, layout, scrollOffset, clipBounds, layoutChanged)\n }\n\n // Clear descendant overflow regions: areas where descendants' previous layouts\n // extended beyond THIS node's rect. clearNodeRegion covers the node's interior,\n // but overflow content is beyond it. This is separate from contentRegionCleared\n // because overflow is OUTSIDE the rect -- it needs clearing even for nodes with\n // backgroundColor (whose interior is handled by renderBox's bg fill).\n if (descendantOverflowChanged) {\n clearDescendantOverflowRegions(node, buffer, layout, scrollOffset, clipBounds)\n }\n}\n\n// ============================================================================\n// Own Content Rendering\n// ============================================================================\n\n/**\n * Render this node's own content (box background/border or text).\n *\n * For boxes: computes inherited bg for border rendering and calls renderBox.\n * For text: computes inherited bg/fg for text rendering and calls renderText.\n *\n * @returns The boxInheritedBg color (needed by outline rendering after children).\n */\nfunction renderOwnContent(\n node: AgNode,\n buffer: TerminalBuffer,\n layout: NonNullable<AgNode[\"contentRect\"]>,\n props: BoxProps & TextProps,\n nodeState: NodeRenderState,\n skipBgFill: boolean,\n instrumentEnabled: boolean,\n stats: ContentPhaseStats,\n ctx?: PipelineContext,\n): Color | undefined {\n // Compute inherited bg once for boxes -- used by border and outline rendering\n // to preserve parent backgrounds on border cells (prevents transparent holes).\n const boxInheritedBg = node.type === \"silvery-box\" && !getEffectiveBg(props) ? findInheritedBg(node).color : undefined\n\n if (node.type === \"silvery-box\") {\n if (instrumentEnabled) stats.boxNodes++\n renderBox(node, buffer, layout, props, nodeState, skipBgFill, boxInheritedBg)\n } else if (node.type === \"silvery-text\") {\n if (instrumentEnabled) stats.textNodes++\n // Pass inherited bg/fg from nearest ancestor with backgroundColor/color.\n // inheritedBg decouples text rendering from buffer state, which is critical\n // for incremental rendering: the cloned buffer may have stale bg at positions\n // outside the parent's bg-filled region (e.g., overflow text, moved nodes).\n // Foreground inheritance matches CSS semantics: Box color cascades to Text children.\n const textInheritedBg = findInheritedBg(node).color\n const textInheritedFg = findInheritedFg(node)\n renderText(node, buffer, layout, props, nodeState, textInheritedBg, textInheritedFg, ctx)\n }\n\n return boxInheritedBg\n}\n\n// ============================================================================\n// Scroll Tier Planner\n// ============================================================================\n\n/** Which tier strategy a scroll container uses for this frame. */\nexport type ScrollTier = \"shift\" | \"clear\" | \"subtree-only\"\n\n/** Inputs for the scroll tier decision (all from renderScrollContainerChildren). */\nexport interface ScrollPlanInputs {\n /** Scroll offset changed since last frame. */\n scrollOffsetChanged: boolean\n /** Visible child index range changed. */\n visibleRangeChanged: boolean\n /** Scroll container has sticky children. */\n hasStickyChildren: boolean\n /** Parent cascade: children need fresh render (contentAreaAffected || bgRefillNeeded). */\n childrenNeedFreshRender: boolean\n /** Node has restructured children (added/removed/reordered). */\n childrenDirty: boolean\n /** Buffer from previous frame is available (incremental mode). */\n hasPrevBuffer: boolean\n /** An ancestor cleared its region. */\n ancestorCleared: boolean\n /** This node's content region was cleared (no own bg). */\n contentRegionCleared: boolean\n /** The bg to use for viewport clears (own bg or inherited). */\n scrollBg: Color | null\n}\n\n/** Result of the scroll tier decision. */\nexport interface ScrollPlan {\n /** Which tier strategy to use. */\n tier: ScrollTier\n /** Background color for viewport clear/shift fill (null = no bg). */\n clearBg: Color | null\n /** Default hasPrevBuffer for children. */\n childHasPrev: boolean\n /** Default ancestorCleared for children. */\n childAncestorCleared: boolean\n /** Whether all first-pass items must re-render (Tier 3 with sticky children). */\n stickyForceRefresh: boolean\n /** Human-readable reasons for the tier decision (for instrumentation). */\n reasons: string[]\n}\n\n/**\n * Determine the scroll tier strategy for this frame.\n *\n * Pure function -- no side effects, no node access beyond the inputs.\n *\n * Three-tier strategy:\n * 1. **shift**: Only scroll offset changed, no sticky children. Buffer contents\n * shifted by scroll delta; only newly visible edges re-render.\n * 2. **clear**: Children restructured, visible range changed with scroll, or\n * parent region changed. Entire viewport cleared and all children re-render.\n * 3. **subtree-only**: Only some descendants changed. Children use hasPrevBuffer=true\n * and skip via fast-path if clean. With sticky children, forces all first-pass\n * items to re-render (stickyForceRefresh).\n */\nexport function planScrollRender(inputs: ScrollPlanInputs): ScrollPlan {\n const {\n scrollOffsetChanged,\n visibleRangeChanged,\n hasStickyChildren,\n childrenNeedFreshRender,\n childrenDirty,\n hasPrevBuffer,\n ancestorCleared,\n contentRegionCleared,\n scrollBg,\n } = inputs\n\n // Tier 1: Buffer shift -- scroll offset changed but nothing else.\n // Unsafe with sticky children (sticky second pass overwrites shifted pixels).\n const scrollOnly =\n hasPrevBuffer &&\n scrollOffsetChanged &&\n !childrenDirty &&\n !childrenNeedFreshRender &&\n !hasStickyChildren &&\n !visibleRangeChanged\n\n // Tier 2: Full viewport clear -- children restructured, scroll+sticky, or parent changed.\n const needsViewportClear =\n hasPrevBuffer &&\n !scrollOnly &&\n (scrollOffsetChanged || childrenDirty || childrenNeedFreshRender || visibleRangeChanged)\n\n // Tier 3 with sticky: force all first-pass items to re-render.\n // The cloned buffer has stale content from previous frames' sticky positions.\n const stickyForceRefresh = hasStickyChildren && hasPrevBuffer && !needsViewportClear\n\n // Build reasons for instrumentation\n const reasons: string[] = []\n if (scrollOnly) reasons.push(\"SHIFT\")\n if (needsViewportClear) {\n if (scrollOffsetChanged) reasons.push(\"scrollOffset\")\n if (childrenDirty) reasons.push(\"childrenDirty\")\n if (childrenNeedFreshRender) reasons.push(\"childrenNeedFreshRender\")\n if (visibleRangeChanged) reasons.push(\"visibleRangeChanged\")\n }\n if (stickyForceRefresh) reasons.push(\"stickyForceRefresh\")\n\n const tier: ScrollTier = scrollOnly ? \"shift\" : needsViewportClear ? \"clear\" : \"subtree-only\"\n\n const childHasPrev = needsViewportClear ? false : hasPrevBuffer\n const childAncestorCleared = needsViewportClear ? true : ancestorCleared || contentRegionCleared\n\n return {\n tier,\n clearBg: scrollOnly || needsViewportClear ? scrollBg : null,\n childHasPrev,\n childAncestorCleared,\n stickyForceRefresh,\n reasons,\n }\n}\n\n/**\n * Render children of a scroll container with proper clipping and offset.\n */\nfunction renderScrollContainerChildren(\n node: AgNode,\n buffer: TerminalBuffer,\n props: BoxProps,\n nodeState: NodeRenderState,\n contentRegionCleared = false,\n childrenNeedFreshRender = false,\n ctx?: PipelineContext,\n): void {\n const { clipBounds, hasPrevBuffer, ancestorCleared, bufferIsCloned, ancestorLayoutChanged } = nodeState\n // Resolve instrumentation from ctx or module globals\n const instrumentEnabled = ctx?.instrumentEnabled ?? _instrumentEnabled\n const stats = ctx?.stats ?? _contentPhaseStats\n const layout = node.contentRect\n const ss = node.scrollState\n if (!layout || !ss) return\n\n const border = props.borderStyle ? getBorderSize(props) : { top: 0, bottom: 0, left: 0, right: 0 }\n const padding = getPadding(props)\n // Scroll containers clip vertically (for scrolling) but NOT horizontally.\n // Horizontal clipping is only for overflow=\"hidden\" containers (e.g., HVL).\n const childClipBounds = computeChildClipBounds(\n layout,\n props,\n clipBounds,\n 0,\n /* horizontal */ false,\n /* vertical */ true,\n )\n\n // Determine if scroll offset changed since last render.\n const scrollOffsetChanged = ss.offset !== ss.prevOffset\n const hasStickyChildren = !!(ss.stickyChildren && ss.stickyChildren.length > 0)\n const visibleRangeChanged =\n ss.firstVisibleChild !== ss.prevFirstVisibleChild || ss.lastVisibleChild !== ss.prevLastVisibleChild\n\n // Compute viewport geometry (shared by all tiers)\n const clearY = childClipBounds.top\n const clearHeight = childClipBounds.bottom - childClipBounds.top\n const contentX = layout.x + border.left + padding.left\n const contentWidth = layout.width - border.left - border.right - padding.left - padding.right\n\n // Compute scroll bg eagerly -- planScrollRender needs it and it's cheap\n const scrollBg =\n scrollOffsetChanged || node.childrenDirty || childrenNeedFreshRender || visibleRangeChanged\n ? getEffectiveBg(props)\n ? parseColor(getEffectiveBg(props)!)\n : findInheritedBg(node).color\n : null\n\n // Plan the scroll tier strategy (pure decision, no side effects)\n const plan = planScrollRender({\n scrollOffsetChanged,\n visibleRangeChanged,\n hasStickyChildren,\n childrenNeedFreshRender,\n childrenDirty: node.childrenDirty,\n hasPrevBuffer,\n ancestorCleared,\n contentRegionCleared,\n scrollBg,\n })\n const { tier, stickyForceRefresh } = plan\n const defaultChildHasPrev = plan.childHasPrev\n const defaultChildAncestorCleared = plan.childAncestorCleared\n\n if (instrumentEnabled) {\n stats.scrollContainerCount++\n if (tier !== \"subtree-only\" || stickyForceRefresh) {\n stats.scrollViewportCleared++\n const reasons = [...plan.reasons]\n if (scrollOffsetChanged) reasons.push(`scrollOffset(${ss.prevOffset}->${ss.offset})`)\n reasons.push(\n `vp=${ss.viewportHeight} content=${ss.contentHeight} vis=${ss.firstVisibleChild}-${ss.lastVisibleChild}`,\n )\n stats.scrollClearReason = reasons.join(\"+\")\n }\n }\n\n // STRICT invariant: Tier 1 (buffer shift) must never be used with sticky children.\n if (process.env.SILVERY_STRICT && tier === \"shift\" && hasStickyChildren) {\n throw new Error(\n `[SILVERY_STRICT] Scroll Tier 1 (buffer shift) activated with sticky children ` +\n `(node: ${(props.id as string | undefined) ?? node.type}, ` +\n `stickyCount: ${ss.stickyChildren?.length ?? 0})`,\n )\n }\n\n // Apply the plan: buffer shift, viewport clear, or sticky force refresh\n const scrollDelta = ss.offset - (ss.prevOffset ?? ss.offset)\n if (tier === \"shift\" && clearHeight > 0) {\n // Clear scroll indicator rows before shifting to prevent stale indicator\n // pixels at edges (columns not covered by children).\n const showBorderless = props.overflowIndicator === true\n if (showBorderless && !border.top && !border.bottom) {\n const topIndicatorY = clearY\n const bottomIndicatorY = clearY + clearHeight - 1\n if (ss.prevOffset != null && ss.prevOffset > 0) {\n buffer.fill(contentX, topIndicatorY, contentWidth, 1, { char: \" \", bg: plan.clearBg })\n }\n buffer.fill(contentX, bottomIndicatorY, contentWidth, 1, { char: \" \", bg: plan.clearBg })\n }\n buffer.scrollRegion(contentX, clearY, contentWidth, clearHeight, scrollDelta, {\n char: \" \",\n bg: plan.clearBg,\n })\n }\n\n if (tier === \"clear\" && clearHeight > 0) {\n buffer.fill(contentX, clearY, contentWidth, clearHeight, {\n char: \" \",\n bg: plan.clearBg,\n })\n }\n\n // Tier 3 with sticky: clear viewport to null bg (matches fresh render state)\n // before re-rendering all items, so the sticky second pass works correctly.\n if (stickyForceRefresh && clearHeight > 0) {\n buffer.fill(contentX, clearY, contentWidth, clearHeight, { char: \" \", bg: null })\n }\n\n // Propagate ancestor layout change to scroll container children.\n const childAncestorLayoutChanged = node.layoutChangedThisFrame || !!ancestorLayoutChanged\n\n // For buffer shift: children that were fully visible in BOTH the previous\n // and current frames have correct pixels after the shift (childHasPrev=true).\n const prevVisTop = ss.prevOffset ?? ss.offset\n const prevVisBottom = prevVisTop + ss.viewportHeight\n\n // First pass: render non-sticky visible children with scroll offset\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i]\n if (!child) continue\n const childProps = child.props as BoxProps\n\n // Skip sticky children - they're rendered in second pass\n if (childProps.position === \"sticky\") {\n continue\n }\n\n // Skip children that are completely outside the visible range\n if (i < ss.firstVisibleChild || i > ss.lastVisibleChild) {\n continue\n }\n\n // Determine per-child hasPrev for buffer shift mode\n let thisChildHasPrev = defaultChildHasPrev\n let thisChildAncestorCleared = defaultChildAncestorCleared\n if (tier === \"shift\") {\n // Check if child was fully visible in the previous frame\n const childRect = child.contentRect\n if (childRect) {\n const childTop = childRect.y - layout.y - border.top - padding.top\n const childBottom = childTop + childRect.height\n const wasFullyVisible = childTop >= prevVisTop && childBottom <= prevVisBottom\n thisChildHasPrev = wasFullyVisible\n // Shifted children: their pixels are intact (not cleared)\n // Newly visible: exposed region was filled by scrollRegion\n thisChildAncestorCleared = wasFullyVisible ? ancestorCleared || contentRegionCleared : true\n }\n }\n\n // Force fresh rendering when sticky children exist (see stickyForceRefresh).\n if (stickyForceRefresh && thisChildHasPrev) {\n thisChildHasPrev = false\n thisChildAncestorCleared = false\n }\n\n // Render visible children with scroll offset applied.\n renderNodeToBuffer(\n child,\n buffer,\n {\n scrollOffset: ss.offset,\n clipBounds: childClipBounds,\n hasPrevBuffer: thisChildHasPrev,\n ancestorCleared: thisChildAncestorCleared,\n bufferIsCloned,\n ancestorLayoutChanged: childAncestorLayoutChanged,\n },\n ctx,\n )\n }\n\n // Second pass: render sticky children at their computed positions\n // Rendered last so they appear on top of other content\n if (ss.stickyChildren) {\n for (const sticky of ss.stickyChildren) {\n const child = node.children[sticky.index]\n if (!child?.contentRect) continue\n\n // Calculate the scroll offset that would place the child at its sticky position\n // stickyOffset = naturalTop - renderOffset\n // This makes the child render at renderOffset instead of its natural position\n const stickyScrollOffset = sticky.naturalTop - sticky.renderOffset\n\n // Sticky children always re-render (hasPrevBuffer=false) since their\n // effective scroll offset may change even when the container's doesn't.\n //\n // ancestorCleared=false matches fresh render semantics: on a fresh render,\n // the buffer at sticky positions has first-pass content (not \"cleared\").\n // Using ancestorCleared=true would cause transparent spacer Boxes to clear\n // their region (via layoutChanged=true from prevLayout=null → cascading\n // contentRegionCleared), wiping overlapping sticky headers rendered earlier\n // in this pass.\n //\n // Stale bg from previous frames is handled by the stickyForceRefresh\n // pre-clear above, which ensures correct bg is in the buffer before sticky\n // children render on top of first-pass content.\n renderNodeToBuffer(\n child,\n buffer,\n {\n scrollOffset: stickyScrollOffset,\n clipBounds: childClipBounds,\n hasPrevBuffer: false,\n ancestorCleared: false,\n bufferIsCloned,\n ancestorLayoutChanged: childAncestorLayoutChanged,\n },\n ctx,\n )\n }\n }\n}\n\n/**\n * Render children of a normal (non-scroll) container.\n */\nfunction renderNormalChildren(\n node: AgNode,\n buffer: TerminalBuffer,\n props: BoxProps,\n nodeState: NodeRenderState,\n childPositionChanged = false,\n contentRegionCleared = false,\n childrenNeedFreshRender = false,\n ctx?: PipelineContext,\n): void {\n const { scrollOffset, clipBounds, hasPrevBuffer, ancestorCleared, bufferIsCloned, ancestorLayoutChanged } = nodeState\n // Resolve instrumentation from ctx or module globals\n const instrumentEnabled = ctx?.instrumentEnabled ?? _instrumentEnabled\n const stats = ctx?.stats ?? _contentPhaseStats\n const layout = node.contentRect\n if (!layout) return\n\n // For overflow='hidden' containers, clip children to content area.\n // Supports per-axis clipping: overflowX/overflowY override the shorthand overflow prop.\n const clipX = (props.overflowX ?? props.overflow) === \"hidden\"\n const clipY = (props.overflowY ?? props.overflow) === \"hidden\"\n const effectiveClipBounds =\n clipX || clipY ? computeChildClipBounds(layout, props, clipBounds, scrollOffset, clipX, clipY) : clipBounds\n\n // Non-scroll sticky children support. When the layout phase computes\n // node.stickyChildren, we use the same two-pass pattern as scroll containers:\n // first pass renders non-sticky children, second pass renders sticky children\n // at their computed renderOffset positions.\n const hasStickyChildren = !!(node.stickyChildren && node.stickyChildren.length > 0)\n\n // When sticky children exist and hasPrevBuffer is true, force all first-pass\n // children to re-render. The cloned buffer may have stale pixels from previous\n // frames' sticky positions. This matches the stickyForceRefresh pattern from\n // scroll containers (Tier 3).\n const stickyForceRefresh = hasStickyChildren && hasPrevBuffer\n\n // Pre-clear the content area to bg=null when stickyForceRefresh is true.\n // Fresh renders start with a blank buffer (null bg everywhere). The cloned\n // buffer has stale content from old sticky positions that would leak through\n // on incremental renders. Clearing to null matches fresh render state before\n // any content renders.\n if (stickyForceRefresh) {\n const border = props.borderStyle ? getBorderSize(props) : { top: 0, bottom: 0, left: 0, right: 0 }\n const padding = getPadding(props)\n let clearX = layout.x + border.left + padding.left\n let clearY = layout.y - scrollOffset + border.top + padding.top\n let clearW = layout.width - border.left - border.right - padding.left - padding.right\n let clearH = layout.height - border.top - border.bottom - padding.top - padding.bottom\n // Clip to clipBounds (same discipline as scroll container clear)\n if (clipBounds) {\n const clipTop = clipBounds.top\n const clipBottom = clipBounds.bottom\n if (clearY < clipTop) {\n clearH -= clipTop - clearY\n clearY = clipTop\n }\n if (clearY + clearH > clipBottom) {\n clearH = clipBottom - clearY\n }\n if (clipBounds.left !== undefined && clearX < clipBounds.left) {\n clearW -= clipBounds.left - clearX\n clearX = clipBounds.left\n }\n if (clipBounds.right !== undefined && clearX + clearW > clipBounds.right) {\n clearW = clipBounds.right - clearX\n }\n }\n if (clearW > 0 && clearH > 0) {\n buffer.fill(clearX, clearY, clearW, clearH, { char: \" \", bg: null })\n }\n }\n\n // Force children to re-render when parent's region was modified on a clone,\n // children were restructured, or sibling positions shifted.\n const childrenNeedRepaint = node.childrenDirty || childPositionChanged || childrenNeedFreshRender\n if (instrumentEnabled && childrenNeedRepaint && hasPrevBuffer) {\n stats.normalChildrenRepaint++\n const reasons: string[] = []\n if (node.childrenDirty) reasons.push(\"childrenDirty\")\n if (childPositionChanged) reasons.push(\"childPositionChanged\")\n if (childrenNeedFreshRender) reasons.push(\"childrenNeedFreshRender\")\n stats.normalRepaintReason = reasons.join(\"+\")\n }\n let childHasPrev = childrenNeedRepaint ? false : hasPrevBuffer\n // childAncestorCleared: tells descendants that STALE pixels exist in the buffer.\n // Only contentRegionCleared (no bg fill → stale pixels remain) propagates this.\n // childrenNeedFreshRender WITHOUT contentRegionCleared means the parent filled its bg,\n // so children's positions have correct bg — NOT stale. Setting ancestorCleared\n // there would cause children to re-fill, overwriting border cells at boundaries.\n // When this node has backgroundColor, its renderBox fill covers any stale\n // pixels from ancestor clears — so children don't need ancestorCleared.\n let childAncestorCleared = contentRegionCleared || (ancestorCleared && !getEffectiveBg(props))\n\n // Propagate ancestor layout change to children: if this node or any ancestor\n // had layoutChangedThisFrame, children must not be skipped even if their own\n // flags are clean — their pixels in the cloned buffer are at wrong positions.\n const childAncestorLayoutChanged = node.layoutChangedThisFrame || !!ancestorLayoutChanged\n\n // Override child flags when sticky force refresh is active — all first-pass\n // children must re-render fresh (matching the scroll container pattern).\n if (stickyForceRefresh) {\n childHasPrev = false\n childAncestorCleared = false\n }\n\n // Multi-pass rendering to match CSS paint order:\n // 1. Normal-flow children (skip sticky and absolute)\n // 2. Sticky children at computed positions (on top of normal-flow)\n // 3. Absolute children on top of everything\n //\n // This ensures absolute children's pixels (bg fills, text) are never\n // overwritten by normal-flow siblings' clearNodeRegion/render.\n //\n // Pre-scan: detect if any non-absolute, non-sticky sibling is dirty. When\n // true, absolute children in the third pass must force-repaint because the\n // first pass may have overwritten their pixels in the cloned buffer.\n let hasAbsoluteChildren = false\n\n // First pass: render normal-flow children (skip sticky + absolute), track dirty state\n for (const child of node.children) {\n const childProps = child.props as BoxProps\n if (childProps.position === \"absolute\") {\n hasAbsoluteChildren = true\n continue // Skip — rendered in third pass\n }\n if (hasStickyChildren && childProps.position === \"sticky\") {\n continue // Skip — rendered in second pass\n }\n\n renderNodeToBuffer(\n child,\n buffer,\n {\n scrollOffset,\n clipBounds: effectiveClipBounds,\n hasPrevBuffer: childHasPrev,\n ancestorCleared: childAncestorCleared,\n bufferIsCloned,\n ancestorLayoutChanged: childAncestorLayoutChanged,\n },\n ctx,\n )\n }\n\n // Second pass: render sticky children at their computed positions.\n // Rendered after normal-flow so they appear on top of other content.\n if (node.stickyChildren) {\n for (const sticky of node.stickyChildren) {\n const child = node.children[sticky.index]\n if (!child?.contentRect) continue\n\n // Calculate the scroll offset that would place the child at its sticky position.\n // stickyScrollOffset = naturalTop - renderOffset\n // This makes the child render at renderOffset instead of its natural position.\n const stickyScrollOffset = sticky.naturalTop - sticky.renderOffset\n\n // Sticky children always re-render (hasPrevBuffer=false) since their\n // effective position may change between frames.\n //\n // ancestorCleared=false matches fresh render semantics: on a fresh render,\n // the buffer at sticky positions has first-pass content (not \"cleared\").\n // Using ancestorCleared=true would cause transparent spacer Boxes to clear\n // their region, wiping overlapping sticky headers rendered earlier in this pass.\n //\n // ancestorLayoutChanged propagated so descendants know to re-render.\n renderNodeToBuffer(\n child,\n buffer,\n {\n scrollOffset: stickyScrollOffset,\n clipBounds: effectiveClipBounds,\n hasPrevBuffer: false,\n ancestorCleared: false,\n bufferIsCloned,\n ancestorLayoutChanged: childAncestorLayoutChanged,\n },\n ctx,\n )\n }\n }\n\n // Third pass: render absolute children on top (CSS paint order)\n if (hasAbsoluteChildren) {\n for (const child of node.children) {\n const childProps = child.props as BoxProps\n if (childProps.position !== \"absolute\") continue\n\n // Both hasPrevBuffer and ancestorCleared must be false for absolute children\n // in the second pass. The buffer at the absolute child's position contains\n // first-pass content (normal-flow siblings), not \"previous frame\" content.\n // This is conceptually a fresh render at the absolute child's position:\n //\n // - hasPrevBuffer=false: prevents contentRegionCleared from firing.\n // Without this, a transparent overlay (no backgroundColor) that changes\n // (contentAreaAffected=true) would clear its entire region, wiping the\n // normal-flow content painted in the first pass. On a fresh render,\n // hasPrevBuffer=false prevents clearing, so this matches.\n //\n // - ancestorCleared=false: prevents transparent descendants from clearing\n // their regions, which would also wipe first-pass content.\n renderNodeToBuffer(\n child,\n buffer,\n {\n scrollOffset,\n clipBounds: effectiveClipBounds,\n hasPrevBuffer: false,\n ancestorCleared: false,\n bufferIsCloned,\n ancestorLayoutChanged: childAncestorLayoutChanged,\n },\n ctx,\n )\n }\n }\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Clear dirty flags on the current node only (no recursion).\n * Used after rendering a node to reset its flags.\n */\nfunction clearNodeDirtyFlags(node: AgNode): void {\n node.contentDirty = false\n node.stylePropsDirty = false\n node.bgDirty = false\n node.subtreeDirty = false\n node.childrenDirty = false\n node.layoutChangedThisFrame = false\n}\n\n/**\n * Clear dirty flags on a subtree that was skipped during incremental rendering.\n */\nfunction clearDirtyFlags(node: AgNode): void {\n clearNodeDirtyFlags(node)\n for (const child of node.children) {\n if (child.layoutNode) {\n clearDirtyFlags(child)\n } else {\n // Virtual text children also need flags cleared — they're rendered by\n // their parent's collectTextContent(), not by renderNodeToBuffer().\n clearVirtualTextFlags(child)\n }\n }\n}\n\n/**\n * Clear dirty flags on a virtual text node and its descendants.\n * Virtual text nodes (no layoutNode) are rendered by their parent layout\n * ancestor via collectTextContent(). Their dirty flags must be cleared\n * after the parent renders, otherwise stale subtreeDirty blocks\n * markSubtreeDirty() propagation on future updates.\n */\nfunction clearVirtualTextFlags(node: AgNode): void {\n clearNodeDirtyFlags(node)\n for (const child of node.children) {\n clearVirtualTextFlags(child)\n }\n}\n\n/**\n * Check if any child's position changed since last render (sibling shift).\n * Checked even when subtreeDirty=true because subtreeDirty only means\n * descendants are dirty, not that this container's gap regions need clearing.\n */\nfunction hasChildPositionChanged(node: AgNode): boolean {\n for (const child of node.children) {\n if (child.contentRect && child.prevLayout) {\n if (child.contentRect.x !== child.prevLayout.x || child.contentRect.y !== child.prevLayout.y) {\n return true\n }\n }\n }\n return false\n}\n\n/**\n * Check if any descendant was overflowing THIS node's rect and had its layout change.\n * Recursive: a grandchild overflowing a child AND this node is detected here.\n *\n * When a descendant overflows (prevLayout extends beyond this node's rect) and then\n * shrinks, clearExcessArea on the descendant clips to its immediate parent's content\n * area, leaving stale pixels in this node's border/padding area and beyond this node's\n * rect. By detecting at THIS level, the node clears its region (restoring borders)\n * and handles overflow beyond its rect via clearDescendantOverflowRegions.\n *\n * Only follows subtreeDirty paths for efficiency — layoutChangedThisFrame on a\n * descendant implies subtreeDirty on all its ancestors.\n */\nfunction hasDescendantOverflowChanged(node: AgNode): boolean {\n const rect = node.contentRect!\n return _checkDescendantOverflow(node.children, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height)\n}\n\nfunction _checkDescendantOverflow(\n children: readonly AgNode[],\n nodeLeft: number,\n nodeTop: number,\n nodeRight: number,\n nodeBottom: number,\n): boolean {\n for (const child of children) {\n // Check this child's previous layout against the ancestor's rect\n if (child.prevLayout && child.layoutChangedThisFrame) {\n const prev = child.prevLayout\n if (\n prev.x + prev.width > nodeRight ||\n prev.y + prev.height > nodeBottom ||\n prev.x < nodeLeft ||\n prev.y < nodeTop\n ) {\n return true\n }\n }\n // Recurse into subtree-dirty children to find deeper overflows\n if (child.subtreeDirty && child.children !== undefined) {\n if (_checkDescendantOverflow(child.children, nodeLeft, nodeTop, nodeRight, nodeBottom)) {\n return true\n }\n }\n }\n return false\n}\n\n/**\n * Compute clip bounds for a container's children by insetting for border+padding,\n * then intersecting with parent clip bounds.\n */\nfunction computeChildClipBounds(\n layout: NonNullable<AgNode[\"contentRect\"]>,\n props: BoxProps,\n parentClip: ClipBounds | undefined,\n scrollOffset = 0,\n /** Compute left/right clip bounds for horizontal overflow clipping. */\n horizontal = true,\n /** Compute top/bottom clip bounds for vertical overflow clipping.\n * Defaults to true — scroll containers pass vertical=true, horizontal=false. */\n vertical = true,\n): ClipBounds {\n const border = props.borderStyle ? getBorderSize(props) : { top: 0, bottom: 0, left: 0, right: 0 }\n const padding = getPadding(props)\n const adjustedY = layout.y - scrollOffset\n const nodeClip: ClipBounds = vertical\n ? {\n top: adjustedY + border.top + padding.top,\n bottom: adjustedY + layout.height - border.bottom - padding.bottom,\n }\n : { top: -Infinity, bottom: Infinity }\n if (horizontal) {\n nodeClip.left = layout.x + border.left + padding.left\n nodeClip.right = layout.x + layout.width - border.right - padding.right\n }\n if (!parentClip) return nodeClip\n const result: ClipBounds = {\n top: vertical ? Math.max(parentClip.top, nodeClip.top) : parentClip.top,\n bottom: vertical ? Math.min(parentClip.bottom, nodeClip.bottom) : parentClip.bottom,\n }\n if (horizontal && nodeClip.left !== undefined && nodeClip.right !== undefined) {\n result.left = Math.max(parentClip.left ?? 0, nodeClip.left)\n result.right = Math.min(parentClip.right ?? Infinity, nodeClip.right)\n } else if (parentClip.left !== undefined && parentClip.right !== undefined) {\n // Pass through parent's horizontal clip bounds without adding own\n result.left = parentClip.left\n result.right = parentClip.right\n }\n return result\n}\n\n// ============================================================================\n// Region Clearing\n// ============================================================================\n\n/**\n * Result of finding inherited background - includes both color and ancestor bounds.\n */\ninterface InheritedBgResult {\n color: Color\n /** The rect of the ancestor that has the background color (for clipping) */\n ancestorRect: { x: number; y: number; width: number; height: number } | null\n}\n\n/**\n * Find the nearest ancestor with a backgroundColor and return the parsed color\n * along with the ancestor's rect for proper clipping.\n *\n * When clearing excess area after a node shrinks, we need to clip to the colored\n * ancestor's bounds - not just the immediate parent. Otherwise the inherited\n * color can bleed into sibling areas that should have different backgrounds.\n */\nfunction findInheritedBg(node: AgNode): InheritedBgResult {\n let current = node.parent\n while (current) {\n const props = current.props as BoxProps\n if (props.backgroundColor) {\n return {\n color: parseColor(props.backgroundColor),\n ancestorRect: current.contentRect,\n }\n }\n // Box with theme prop: resolve $bg directly from the theme object\n // (avoids context stack dependency — see findInheritedFg comment)\n if (props.theme) {\n const theme = props.theme as Theme\n return {\n color: parseColor(theme.bg),\n ancestorRect: current.contentRect,\n }\n }\n current = current.parent\n }\n return { color: null, ancestorRect: null }\n}\n\n/**\n * Find the nearest ancestor Box with a `color` prop and return the parsed color.\n * Implements CSS-style foreground color inheritance: Text children without an\n * explicit `color` prop inherit from the nearest Box ancestor that sets one.\n *\n * Also resolves `$fg` from the nearest ancestor Box with a `theme` prop,\n * reading the hex value directly from the Theme object to avoid context stack\n * dependency (parseColor(\"$fg\") is stateful and depends on render-order\n * context stack position, but this function walks UP the tree).\n *\n * Returns null when no ancestor sets a color — text uses the terminal default.\n */\nfunction findInheritedFg(node: AgNode): Color {\n let current = node.parent\n while (current) {\n const props = current.props as BoxProps\n if (props.color) return parseColor(props.color)\n // Box with theme prop: resolve $fg directly from the theme object\n if (props.theme) {\n const theme = props.theme as Theme\n return parseColor(theme.fg)\n }\n current = current.parent\n }\n return null\n}\n\n/**\n * Clear overflow regions: areas where children's prevLayouts extended beyond\n * this node's rect. Called when childOverflowChanged detected stale overflow.\n *\n * clearNodeRegion handles the node's own rect. This function handles the\n * overflow area — pixels that a child rendered OUTSIDE the parent's rect\n * in a previous frame (via overflow:visible behavior). When the child shrinks,\n * those pixels become stale in the cloned buffer.\n *\n * Clears each child's overflow extent, clipped to buffer bounds.\n */\n/**\n * Clear areas where descendants' previous layouts overflowed beyond THIS node's rect.\n * Only clears OUTSIDE the node's rect — interior clearing is handled by clearNodeRegion\n * and renderBox. Recursive: follows subtreeDirty paths to find all overflowing descendants.\n */\nfunction clearDescendantOverflowRegions(\n node: AgNode,\n buffer: TerminalBuffer,\n layout: NonNullable<AgNode[\"contentRect\"]>,\n scrollOffset: number,\n clipBounds: ClipBounds | undefined,\n): void {\n const inherited = findInheritedBg(node)\n const clearBg = inherited.color\n const nodeRight = layout.x + layout.width\n const nodeBottom = layout.y - scrollOffset + layout.height\n const nodeLeft = layout.x\n const nodeTop = layout.y - scrollOffset\n\n _clearDescendantOverflow(\n node.children,\n buffer,\n nodeLeft,\n nodeTop,\n nodeRight,\n nodeBottom,\n scrollOffset,\n clipBounds,\n clearBg,\n )\n}\n\nfunction _clearDescendantOverflow(\n children: readonly AgNode[],\n buffer: TerminalBuffer,\n nodeLeft: number,\n nodeTop: number,\n nodeRight: number,\n nodeBottom: number,\n scrollOffset: number,\n clipBounds: ClipBounds | undefined,\n clearBg: Color,\n): void {\n for (const child of children) {\n if (child.prevLayout && child.layoutChangedThisFrame) {\n const prev = child.prevLayout\n const prevRight = prev.x + prev.width\n const prevBottom = prev.y - scrollOffset + prev.height\n const prevTop = prev.y - scrollOffset\n\n // Clear overflow to the right of the ancestor\n if (prevRight > nodeRight) {\n const overflowX = nodeRight\n const overflowWidth = Math.min(prevRight, buffer.width) - overflowX\n const overflowTop = Math.max(prevTop, clipBounds?.top ?? 0)\n const overflowBottom = Math.min(prevBottom, clipBounds?.bottom ?? buffer.height)\n if (overflowWidth > 0 && overflowBottom > overflowTop) {\n buffer.fill(overflowX, overflowTop, overflowWidth, overflowBottom - overflowTop, {\n char: \" \",\n bg: clearBg,\n })\n }\n }\n // Clear overflow below the ancestor\n if (prevBottom > nodeBottom) {\n const overflowTop = Math.max(nodeBottom, clipBounds?.top ?? 0)\n const overflowBottom = Math.min(prevBottom, clipBounds?.bottom ?? buffer.height)\n const overflowX = Math.max(prev.x, clipBounds?.left ?? 0)\n const overflowWidth = Math.min(prevRight, clipBounds?.right ?? buffer.width) - overflowX\n if (overflowWidth > 0 && overflowBottom > overflowTop) {\n buffer.fill(overflowX, overflowTop, overflowWidth, overflowBottom - overflowTop, {\n char: \" \",\n bg: clearBg,\n })\n }\n }\n // Clear overflow to the left of the ancestor\n if (prev.x < nodeLeft) {\n const overflowX = Math.max(prev.x, 0)\n const overflowWidth = Math.min(nodeLeft, buffer.width) - overflowX\n const overflowTop = Math.max(prevTop, clipBounds?.top ?? 0)\n const overflowBottom = Math.min(prevBottom, clipBounds?.bottom ?? buffer.height)\n if (overflowWidth > 0 && overflowBottom > overflowTop) {\n buffer.fill(overflowX, overflowTop, overflowWidth, overflowBottom - overflowTop, {\n char: \" \",\n bg: clearBg,\n })\n }\n }\n // Clear overflow above the ancestor\n if (prevTop < nodeTop) {\n const overflowTop = Math.max(prevTop, clipBounds?.top ?? 0)\n const overflowBottom = Math.min(nodeTop, clipBounds?.bottom ?? buffer.height)\n const overflowX = Math.max(prev.x, clipBounds?.left ?? 0)\n const overflowWidth = Math.min(prevRight, clipBounds?.right ?? buffer.width) - overflowX\n if (overflowWidth > 0 && overflowBottom > overflowTop) {\n buffer.fill(overflowX, overflowTop, overflowWidth, overflowBottom - overflowTop, {\n char: \" \",\n bg: clearBg,\n })\n }\n }\n }\n // Recurse into subtree-dirty children to find deeper overflows\n if (child.subtreeDirty && child.children !== undefined) {\n _clearDescendantOverflow(\n child.children,\n buffer,\n nodeLeft,\n nodeTop,\n nodeRight,\n nodeBottom,\n scrollOffset,\n clipBounds,\n clearBg,\n )\n }\n }\n}\n\n/**\n * Clear a node's region with inherited bg when it has no backgroundColor.\n * Also clears excess area when the node shrank (previous layout was larger).\n *\n * Clipping: clips to parent's contentRect (prevents overflow) and to the\n * colored ancestor's bounds (prevents bg color bleeding into siblings).\n */\nfunction clearNodeRegion(\n node: AgNode,\n buffer: TerminalBuffer,\n layout: NonNullable<AgNode[\"contentRect\"]>,\n scrollOffset: number,\n clipBounds: ClipBounds | undefined,\n layoutChanged: boolean,\n): void {\n const inherited = findInheritedBg(node)\n const clearBg = inherited.color\n const screenY = layout.y - scrollOffset\n\n // Clip to parent's contentRect to prevent oversized children from clearing\n // beyond their parent's bounds and bleeding inherited bg into sibling regions.\n const parentRect = node.parent?.contentRect\n const parentBottom = parentRect ? parentRect.y - scrollOffset + parentRect.height : undefined\n\n const clearY = clipBounds ? Math.max(screenY, clipBounds.top) : screenY\n let clearBottom = clipBounds ? Math.min(screenY + layout.height, clipBounds.bottom) : screenY + layout.height\n if (parentBottom !== undefined) {\n clearBottom = Math.min(clearBottom, parentBottom)\n }\n\n // Clip horizontally to clipBounds (overflow:hidden containers) and to the\n // colored ancestor's bounds (prevents inherited bg bleeding into siblings).\n let clearX = layout.x\n let clearWidth = layout.width\n if (clipBounds?.left !== undefined && clipBounds.right !== undefined) {\n if (clearX < clipBounds.left) {\n clearWidth -= clipBounds.left - clearX\n clearX = clipBounds.left\n }\n if (clearX + clearWidth > clipBounds.right) {\n clearWidth = Math.max(0, clipBounds.right - clearX)\n }\n }\n if (inherited.ancestorRect) {\n const ancestorRight = inherited.ancestorRect.x + inherited.ancestorRect.width\n const ancestorLeft = inherited.ancestorRect.x\n if (clearX < ancestorLeft) {\n clearWidth -= ancestorLeft - clearX\n clearX = ancestorLeft\n }\n if (clearX + clearWidth > ancestorRight) {\n clearWidth = Math.max(0, ancestorRight - clearX)\n }\n }\n\n const clearHeight = clearBottom - clearY\n if (clearHeight > 0 && clearWidth > 0) {\n // Cell debug: log clearNodeRegion coverage\n const _cellDbg2 = (globalThis as any).__silvery_cell_debug as { x: number; y: number; log: string[] } | undefined\n if (_cellDbg2) {\n const covers =\n clearX <= _cellDbg2.x &&\n clearX + clearWidth > _cellDbg2.x &&\n clearY <= _cellDbg2.y &&\n clearY + clearHeight > _cellDbg2.y\n if (covers) {\n const id = ((node.props as Record<string, unknown>).id as string) ?? node.type\n const msg = `CLEAR_REGION ${id} fill=${clearX},${clearY} ${clearWidth}x${clearHeight} bg=${String(clearBg)} COVERS TARGET`\n _cellDbg2.log.push(msg)\n cellLog.debug?.(msg)\n }\n }\n buffer.fill(clearX, clearY, clearWidth, clearHeight, {\n char: \" \",\n bg: clearBg,\n })\n }\n\n // Delegate excess area clearing to shared helper\n clearExcessArea(node, buffer, layout, scrollOffset, clipBounds, layoutChanged, inherited)\n}\n\n/**\n * Clear the excess area when a node shrinks (old bounds were larger than new).\n *\n * This is separated from clearNodeRegion because excess area clearing must happen\n * even when contentRegionCleared is false. Key scenario: absolute-positioned overlays\n * (e.g., search dialog) that shrink while normal-flow siblings are dirty. The\n * forceRepaint path sets hasPrevBuffer=false + ancestorCleared=false, making\n * contentRegionCleared=false — but the cloned buffer still has stale pixels from\n * the old larger layout that must be cleared.\n *\n * Clips to the COLORED ANCESTOR's content area (not immediate parent's full rect)\n * to prevent inherited color from bleeding into sibling areas with different bg.\n *\n * IMPORTANT: Uses content area (inside border/padding), not full contentRect.\n * Without this, excess clearing of a child that previously filled the parent's\n * content area will extend into the parent's border row, overwriting border chars.\n */\nfunction clearExcessArea(\n node: AgNode,\n buffer: TerminalBuffer,\n layout: NonNullable<AgNode[\"contentRect\"]>,\n scrollOffset: number,\n clipBounds: ClipBounds | undefined,\n layoutChanged: boolean,\n inherited?: InheritedBgResult,\n): void {\n if (!layoutChanged || !node.prevLayout) return\n const prev = node.prevLayout\n\n // Cell debug: log clearExcessArea decisions\n const _cellDbg3 = (globalThis as any).__silvery_cell_debug as { x: number; y: number; log: string[] } | undefined\n const _prevCoversCell3 =\n _cellDbg3 &&\n prev.x <= _cellDbg3.x &&\n prev.x + prev.width > _cellDbg3.x &&\n prev.y - scrollOffset <= _cellDbg3.y &&\n prev.y - scrollOffset + prev.height > _cellDbg3.y\n\n // Only clear if the node actually shrank in at least one dimension\n if (prev.width <= layout.width && prev.height <= layout.height) {\n if (_cellDbg3 && _prevCoversCell3) {\n const id = ((node.props as Record<string, unknown>).id as string) ?? node.type\n const msg =\n `EXCESS_SKIP_NO_SHRINK ${id} prev=${prev.x},${prev.y - scrollOffset} ${prev.width}x${prev.height}` +\n ` now=${layout.x},${layout.y - scrollOffset} ${layout.width}x${layout.height}`\n _cellDbg3.log.push(msg)\n cellLog.debug?.(msg)\n }\n return\n }\n\n // Skip excess clearing when the node MOVED (changed x or y position).\n // The right/bottom excess formulas use new-x + old-y coordinates, which\n // creates a phantom rectangle at wrong positions when the node moved.\n // Example: text at old=(30,7,23,1) → new=(22,8,14,2) computes excess at\n // (36,7) which overwrites a sibling's border character.\n //\n // When the node moved, the parent handles old-pixel cleanup:\n // - Parent's clearNodeRegion covers old pixels within parent's current rect\n // - Parent's clearExcessArea covers old pixels outside parent's rect\n if (prev.x !== layout.x || prev.y !== layout.y) {\n if (_cellDbg3 && _prevCoversCell3) {\n const id = ((node.props as Record<string, unknown>).id as string) ?? node.type\n const msg =\n `EXCESS_SKIP_MOVED ${id} prev=${prev.x},${prev.y - scrollOffset} ${prev.width}x${prev.height}` +\n ` now=${layout.x},${layout.y - scrollOffset} ${layout.width}x${layout.height}` +\n ` (dx=${layout.x - prev.x} dy=${layout.y - prev.y})`\n _cellDbg3.log.push(msg)\n cellLog.debug?.(msg)\n }\n return\n }\n\n if (!inherited) inherited = findInheritedBg(node)\n const clearBg = inherited.color\n const screenY = layout.y - scrollOffset\n const prevScreenY = prev.y - scrollOffset\n\n // Clip to prevent excess clearing from bleeding outside valid bounds.\n // Start with the colored ancestor's rect (prevents bg color bleed),\n // then further restrict to the immediate parent's content area (prevents\n // overwriting parent's border characters).\n const clipRect = inherited.ancestorRect ?? node.parent?.contentRect\n if (!clipRect) return\n\n const clipRectScreenY = clipRect.y - scrollOffset\n let clipRectBottom = clipRectScreenY + clipRect.height\n let clipRectRight = clipRect.x + clipRect.width\n\n // Always inset by the immediate parent's border/padding.\n // Without this, a child's excess clearing extends into the parent's\n // border row, overwriting border characters with spaces.\n // (The old code skipped inset when clip rect came from a colored ancestor,\n // assuming \"its bg fill covers its border area\" — but bg fill only covers\n // the inside, while renderBorder draws characters on the border row.)\n const parent = node.parent\n if (parent?.contentRect) {\n const parentProps = parent.props as BoxProps\n const border = getBorderSize(parentProps)\n const padding = getPadding(parentProps)\n const parentRight = parent.contentRect.x + parent.contentRect.width - border.right - padding.right\n const parentBottom =\n parent.contentRect.y - scrollOffset + parent.contentRect.height - border.bottom - padding.bottom\n clipRectRight = Math.min(clipRectRight, parentRight)\n clipRectBottom = Math.min(clipRectBottom, parentBottom)\n }\n\n // Clear right margin (old was wider than new)\n if (prev.width > layout.width) {\n const excessX = layout.x + layout.width\n let excessWidth = prev.width - layout.width\n // Clip horizontally to parent's content area (inside border/padding).\n // Without this, excess clearing of a child that previously filled a wider\n // layout extends into the parent's right border, overwriting border chars.\n if (excessX + excessWidth > clipRectRight) {\n excessWidth = Math.max(0, clipRectRight - excessX)\n }\n if (excessWidth > 0) {\n clippedFill(\n buffer,\n excessX,\n excessWidth,\n prevScreenY,\n prevScreenY + prev.height,\n clipBounds,\n clipRectBottom,\n clearBg,\n )\n }\n }\n\n // Clear bottom margin (old was taller than new)\n if (prev.height > layout.height) {\n let bottomWidth = prev.width\n // Clip horizontally to parent's content area\n if (layout.x + bottomWidth > clipRectRight) {\n bottomWidth = Math.max(0, clipRectRight - layout.x)\n }\n clippedFill(\n buffer,\n layout.x,\n bottomWidth,\n screenY + layout.height,\n prevScreenY + prev.height,\n clipBounds,\n clipRectBottom,\n clearBg,\n )\n }\n}\n\n/** Fill a rectangular region, clipping to clipBounds and an outer bottom limit. */\nfunction clippedFill(\n buffer: TerminalBuffer,\n x: number,\n width: number,\n top: number,\n bottom: number,\n clipBounds: ClipBounds | undefined,\n outerBottom: number,\n bg: Color,\n): void {\n const clippedTop = clipBounds ? Math.max(top, clipBounds.top) : top\n const clippedBottom = Math.min(clipBounds ? Math.min(bottom, clipBounds.bottom) : bottom, outerBottom)\n let clippedX = x\n let clippedWidth = width\n if (clipBounds?.left !== undefined && clipBounds.right !== undefined) {\n if (clippedX < clipBounds.left) {\n clippedWidth -= clipBounds.left - clippedX\n clippedX = clipBounds.left\n }\n if (clippedX + clippedWidth > clipBounds.right) {\n clippedWidth = Math.max(0, clipBounds.right - clippedX)\n }\n }\n const height = clippedBottom - clippedTop\n if (height > 0 && clippedWidth > 0) {\n buffer.fill(clippedX, clippedTop, clippedWidth, height, { char: \" \", bg })\n }\n}\n",
68
+ "/**\n * Phase 3: Content Phase (Adapter-aware) -- DIVERGENT RENDERER\n *\n * A simplified, adapter-agnostic content renderer that renders the full node\n * tree to a RenderBuffer every frame. Used by `executeRenderAdapter()` for\n * non-terminal targets (xterm.js web showcases, canvas, etc.) where the main\n * terminal-optimized content phase cannot be used.\n *\n * Relationship to content-phase.ts:\n * content-phase.ts is the primary renderer for terminal output. It has\n * incremental rendering (dirty flags, buffer cloning, fast-path skips),\n * bg inheritance via findInheritedBg(), ANSI-aware text rendering, theme\n * context propagation, region clearing, excess area cleanup, descendant\n * overflow detection, and extensive instrumentation/STRICT mode support.\n *\n * This file is a parallel implementation that re-implements the same tree\n * traversal and rendering logic but against the abstract RenderBuffer\n * interface (drawChar/drawText/fillRect) instead of TerminalBuffer directly.\n *\n * Why it exists:\n * The RenderAdapter abstraction (see render-adapter.ts) allows silvery to\n * target different backends -- terminal, xterm.js, canvas. The main\n * content-phase.ts is tightly coupled to TerminalBuffer (cell-level access,\n * getCellBg, scrollRegion, packed metadata). This adapter version works with\n * any RenderBuffer implementation, making it usable for web showcases and\n * future non-terminal targets.\n *\n * Known divergences from content-phase.ts:\n * - No incremental rendering: full re-render every frame (no dirty flag\n * evaluation, no buffer cloning, no fast-path skips)\n * - No bg inheritance via findInheritedBg() for text -- uses a simpler\n * ancestor walk (findAncestorBg) that doesn't handle all edge cases\n * - No theme context propagation (pushContextTheme/popContextTheme)\n * - No region clearing or excess area cleanup (not needed without\n * incremental rendering since the buffer starts fresh each frame)\n * - No instrumentation, STRICT mode, or diagnostic support\n * - No ANSI-aware text rendering (collectTextContent is plain string\n * concatenation, not the segment-based BgSegment approach)\n * - No absolute/sticky incremental rendering optimizations in renderNormalChildren\n * (three-pass paint order is implemented but without hasPrevBuffer/ancestorCleared cascading)\n *\n * Future direction:\n * The xterm-unification design (docs/design/xterm-unification.md) proposes\n * eliminating this file by making xterm.js use the main terminal pipeline\n * via createXtermProvider(). Since xterm.js is a terminal emulator that\n * accepts ANSI output, it can use the real content-phase.ts + output-phase.ts\n * and benefit from incremental rendering. Until then, this file must be\n * maintained in parallel -- any rendering feature added to content-phase.ts\n * may need a corresponding (simplified) implementation here.\n */\n\nimport { type RenderBuffer, type RenderStyle, getRenderAdapter, hasRenderAdapter } from \"../render-adapter\"\nimport type { BoxProps, AgNode, Rect, TextProps } from \"@silvery/ag/types\"\nimport { getBorderSize, getPadding } from \"./helpers\"\nimport { displayWidth } from \"../unicode\"\nimport { formatTextLines } from \"./render-text\"\n\n// ============================================================================\n// Main Entry Point\n// ============================================================================\n\n/**\n * Render all nodes to a RenderBuffer using the current adapter.\n *\n * @param root The root SilveryNode\n * @returns A RenderBuffer with the rendered content\n */\nexport function contentPhaseAdapter(root: AgNode): RenderBuffer {\n if (!hasRenderAdapter()) {\n throw new Error(\"contentPhaseAdapter called without a render adapter set\")\n }\n\n const layout = root.contentRect\n if (!layout) {\n throw new Error(\"contentPhaseAdapter called before layout phase\")\n }\n\n const adapter = getRenderAdapter()\n const buffer = adapter.createBuffer(layout.width, layout.height)\n\n renderNodeToBuffer(root, buffer)\n return buffer\n}\n\n// ============================================================================\n// Node Rendering\n// ============================================================================\n\n/** Clip bounds for vertical and optional horizontal clipping. */\ninterface ClipRect {\n top: number\n bottom: number\n left?: number\n right?: number\n}\n\nfunction renderNodeToBuffer(node: AgNode, buffer: RenderBuffer, scrollOffset = 0, clipBounds?: ClipRect): void {\n const layout = node.contentRect\n if (!layout) return\n\n // Skip nodes without layout (raw text and virtual text nodes)\n if (!node.layoutNode) return\n\n // Skip hidden nodes (Suspense support)\n if (node.hidden) return\n\n const props = node.props as BoxProps & TextProps\n\n // Skip display=\"none\" nodes\n if (props.display === \"none\") return\n\n // Check if this is a scrollable container\n const isScrollContainer = props.overflow === \"scroll\" && node.scrollState\n\n // Render based on node type\n if (node.type === \"silvery-box\") {\n renderBox(node, buffer, layout, props, clipBounds, scrollOffset)\n\n // Scroll indicators\n if (isScrollContainer && node.scrollState) {\n renderScrollIndicators(node, buffer, layout, props, node.scrollState)\n }\n } else if (node.type === \"silvery-text\") {\n renderText(node, buffer, layout, props, scrollOffset, clipBounds)\n }\n\n // Render children\n if (isScrollContainer && node.scrollState) {\n renderScrollContainerChildren(node, buffer, props, clipBounds)\n } else {\n renderNormalChildren(node, buffer, scrollOffset, props, clipBounds)\n }\n\n // Render outline AFTER children — outline overlaps content at edges\n if (node.type === \"silvery-box\" && props.outlineStyle) {\n const { x, width, height } = layout\n const outlineY = layout.y - scrollOffset\n renderOutlineAdapter(buffer, x, outlineY, width, height, props, clipBounds)\n }\n\n // Clear content dirty flag\n node.contentDirty = false\n}\n\n// ============================================================================\n// Box Rendering\n// ============================================================================\n\n/**\n * Render a Box node.\n */\nfunction renderBox(\n _node: AgNode,\n buffer: RenderBuffer,\n layout: Rect,\n props: BoxProps,\n clipBounds?: ClipRect,\n scrollOffset = 0,\n): void {\n const { x, width, height } = layout\n const y = layout.y - scrollOffset\n\n // Skip if completely outside clip bounds\n if (clipBounds) {\n if (y + height <= clipBounds.top || y >= clipBounds.bottom) return\n if (clipBounds.left !== undefined && clipBounds.right !== undefined) {\n if (x + width <= clipBounds.left || x >= clipBounds.right) return\n }\n }\n\n // Fill background if set\n if (props.backgroundColor) {\n const style: RenderStyle = { bg: props.backgroundColor }\n\n if (clipBounds) {\n const clippedY = Math.max(y, clipBounds.top)\n const clippedHeight = Math.min(y + height, clipBounds.bottom) - clippedY\n const clippedX = clipBounds.left !== undefined ? Math.max(x, clipBounds.left) : x\n const clippedWidth =\n clipBounds.right !== undefined ? Math.min(x + width, clipBounds.right) - clippedX : width - (clippedX - x)\n if (clippedHeight > 0 && clippedWidth > 0) {\n buffer.fillRect(clippedX, clippedY, clippedWidth, clippedHeight, style)\n }\n } else {\n buffer.fillRect(x, y, width, height, style)\n }\n }\n\n // Render border if set\n if (props.borderStyle) {\n renderBorder(buffer, x, y, width, height, props, clipBounds)\n }\n}\n\n/**\n * Render a border around a box.\n */\nfunction renderBorder(\n buffer: RenderBuffer,\n x: number,\n y: number,\n width: number,\n height: number,\n props: BoxProps,\n clipBounds?: ClipRect,\n): void {\n const adapter = getRenderAdapter()\n const chars = adapter.getBorderChars(props.borderStyle ?? \"single\")\n const style: RenderStyle = props.borderColor ? { fg: props.borderColor } : {}\n\n const showTop = props.borderTop !== false\n const showBottom = props.borderBottom !== false\n const showLeft = props.borderLeft !== false\n const showRight = props.borderRight !== false\n\n const isRowVisible = (row: number): boolean =>\n clipBounds ? row >= clipBounds.top && row < clipBounds.bottom && buffer.inBounds(0, row) : buffer.inBounds(0, row)\n\n // Top border\n if (showTop && isRowVisible(y)) {\n renderHorizontalBorder(\n buffer,\n x,\n y,\n width,\n showLeft,\n showRight,\n chars.topLeft,\n chars.topRight,\n chars.horizontal,\n style,\n clipBounds,\n )\n }\n\n // Side borders — extend range when top/bottom borders are hidden\n const rightVertical = chars.rightVertical ?? chars.vertical\n const sideStart = showTop ? y + 1 : y\n const sideEnd = showBottom ? y + height - 1 : y + height\n renderSideBorders(\n buffer,\n x,\n width,\n sideStart,\n sideEnd,\n showLeft,\n showRight,\n chars.vertical,\n rightVertical,\n style,\n isRowVisible,\n clipBounds,\n )\n\n // Bottom border\n const bottomHorizontal = chars.bottomHorizontal ?? chars.horizontal\n const bottomY = y + height - 1\n if (showBottom && isRowVisible(bottomY)) {\n renderHorizontalBorder(\n buffer,\n x,\n bottomY,\n width,\n showLeft,\n showRight,\n chars.bottomLeft,\n chars.bottomRight,\n bottomHorizontal,\n style,\n clipBounds,\n )\n }\n}\n\nfunction renderHorizontalBorder(\n buffer: RenderBuffer,\n x: number,\n row: number,\n width: number,\n showLeft: boolean,\n showRight: boolean,\n leftCorner: string,\n rightCorner: string,\n horizontal: string,\n style: RenderStyle,\n clipBounds?: ClipRect,\n): void {\n const clipLeft = clipBounds?.left ?? -Infinity\n const clipRight = clipBounds?.right ?? Infinity\n if (showLeft && x >= clipLeft && x < clipRight) buffer.drawChar(x, row, leftCorner, style)\n for (let col = x + 1; col < x + width - 1; col++) {\n if (col >= clipLeft && col < clipRight && buffer.inBounds(col, row)) {\n buffer.drawChar(col, row, horizontal, style)\n }\n }\n const rightCol = x + width - 1\n if (showRight && rightCol >= clipLeft && rightCol < clipRight && buffer.inBounds(rightCol, row)) {\n buffer.drawChar(rightCol, row, rightCorner, style)\n }\n}\n\nfunction renderSideBorders(\n buffer: RenderBuffer,\n x: number,\n width: number,\n startRow: number,\n endRow: number,\n showLeft: boolean,\n showRight: boolean,\n leftVertical: string,\n rightVertical: string,\n style: RenderStyle,\n isRowVisible: (row: number) => boolean,\n clipBounds?: ClipRect,\n): void {\n const clipLeft = clipBounds?.left ?? -Infinity\n const clipRight = clipBounds?.right ?? Infinity\n for (let row = startRow; row < endRow; row++) {\n if (!isRowVisible(row)) continue\n if (showLeft && x >= clipLeft && x < clipRight) buffer.drawChar(x, row, leftVertical, style)\n const rightCol = x + width - 1\n if (showRight && rightCol >= clipLeft && rightCol < clipRight && buffer.inBounds(rightCol, row)) {\n buffer.drawChar(rightCol, row, rightVertical, style)\n }\n }\n}\n\n// ============================================================================\n// Outline Rendering\n// ============================================================================\n\n/**\n * Render an outline around a box (adapter version).\n *\n * Unlike borders, outlines do NOT affect layout dimensions. They draw border\n * characters that OVERLAP the content area at the node's screen rect edges.\n */\nfunction renderOutlineAdapter(\n buffer: RenderBuffer,\n x: number,\n y: number,\n width: number,\n height: number,\n props: BoxProps,\n clipBounds?: ClipRect,\n): void {\n const adapter = getRenderAdapter()\n const chars = adapter.getBorderChars(props.outlineStyle ?? \"single\")\n const style: RenderStyle = {}\n if (props.outlineColor) style.fg = props.outlineColor\n if (props.outlineDimColor) style.attrs = { dim: true }\n\n const showTop = props.outlineTop !== false\n const showBottom = props.outlineBottom !== false\n const showLeft = props.outlineLeft !== false\n const showRight = props.outlineRight !== false\n\n const isRowVisible = (row: number): boolean =>\n clipBounds ? row >= clipBounds.top && row < clipBounds.bottom && buffer.inBounds(0, row) : buffer.inBounds(0, row)\n\n // Top border\n if (showTop && isRowVisible(y)) {\n renderHorizontalBorder(\n buffer,\n x,\n y,\n width,\n showLeft,\n showRight,\n chars.topLeft,\n chars.topRight,\n chars.horizontal,\n style,\n clipBounds,\n )\n }\n\n // Side borders — extend range when top/bottom are hidden\n const outRightVertical = chars.rightVertical ?? chars.vertical\n const sideStart = showTop ? y + 1 : y\n const sideEnd = showBottom ? y + height - 1 : y + height\n renderSideBorders(\n buffer,\n x,\n width,\n sideStart,\n sideEnd,\n showLeft,\n showRight,\n chars.vertical,\n outRightVertical,\n style,\n isRowVisible,\n clipBounds,\n )\n\n // Bottom border\n const outBottomHorizontal = chars.bottomHorizontal ?? chars.horizontal\n const bottomY = y + height - 1\n if (showBottom && isRowVisible(bottomY)) {\n renderHorizontalBorder(\n buffer,\n x,\n bottomY,\n width,\n showLeft,\n showRight,\n chars.bottomLeft,\n chars.bottomRight,\n outBottomHorizontal,\n style,\n clipBounds,\n )\n }\n}\n\n// ============================================================================\n// Text Rendering\n// ============================================================================\n\n/**\n * Walk the parent chain to find the nearest ancestor Box with backgroundColor.\n * Mirrors findInheritedBg() in content-phase.ts.\n */\nfunction findAncestorBg(node: AgNode): string | undefined {\n let current = node.parent\n while (current) {\n const bg = (current.props as BoxProps).backgroundColor\n if (bg) return bg\n current = current.parent\n }\n return undefined\n}\n\n/** A segment of text with its resolved style. */\ninterface StyledSegment {\n text: string\n style: RenderStyle\n}\n\n/** Style context for nested Text style inheritance (mirrors render-text.ts StyleContext). */\ninterface AdapterStyleContext {\n color?: string\n bold?: boolean\n dim?: boolean\n italic?: boolean\n underline?: boolean\n underlineStyle?: \"single\" | \"double\" | \"curly\" | \"dotted\" | \"dashed\"\n underlineColor?: string\n inverse?: boolean\n strikethrough?: boolean\n}\n\n/** Merge child TextProps into parent style context. Child values override parent. */\nfunction mergeAdapterStyleContext(parent: AdapterStyleContext, childProps: TextProps): AdapterStyleContext {\n return {\n color: childProps.color ?? parent.color,\n bold: childProps.bold ?? parent.bold,\n dim: childProps.dim ?? (childProps as any).dimColor ?? parent.dim,\n italic: childProps.italic ?? parent.italic,\n underline: childProps.underline ?? parent.underline,\n underlineStyle: (childProps.underlineStyle as AdapterStyleContext[\"underlineStyle\"]) ?? parent.underlineStyle,\n underlineColor: childProps.underlineColor ?? parent.underlineColor,\n inverse: childProps.inverse ?? parent.inverse,\n strikethrough: childProps.strikethrough ?? parent.strikethrough,\n }\n}\n\n/** Build a RenderStyle from a style context and inherited bg. */\nfunction contextToRenderStyle(ctx: AdapterStyleContext, bg?: string): RenderStyle {\n return {\n fg: ctx.color ?? undefined,\n bg: bg ?? undefined,\n attrs: {\n bold: ctx.bold,\n dim: ctx.dim,\n italic: ctx.italic,\n underline: ctx.underline,\n underlineStyle: ctx.underlineStyle,\n underlineColor: ctx.underlineColor,\n strikethrough: ctx.strikethrough,\n inverse: ctx.inverse,\n },\n }\n}\n\n/**\n * Collect styled text segments from a node tree.\n *\n * Walks the tree like render-text.ts collectTextContent but instead of embedding\n * ANSI codes, returns an array of { text, style } segments. The adapter renders\n * each segment with its own RenderStyle via drawText — no ANSI parsing needed.\n *\n * Handles: nested Text style push/pop, internal_transform, display=\"none\" skipping.\n */\nfunction collectStyledSegments(\n node: AgNode,\n parentContext: AdapterStyleContext,\n inheritedBg: string | undefined,\n segments: StyledSegment[],\n): void {\n // Raw text nodes — emit a segment with the current style\n if (node.textContent !== undefined) {\n if (node.textContent.length > 0) {\n segments.push({\n text: node.textContent,\n style: contextToRenderStyle(parentContext, inheritedBg),\n })\n }\n return\n }\n\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i]!\n const childProps = child.props as TextProps & BoxProps\n\n // Skip display=\"none\" children\n if (childProps?.display === \"none\") continue\n\n // Skip hidden children (Suspense)\n if (child.hidden) continue\n\n // Nested virtual Text node with style props\n if (child.type === \"silvery-text\" && child.props && !child.layoutNode) {\n const childContext = mergeAdapterStyleContext(parentContext, childProps)\n\n // Check for internal_transform\n const childTransform = (childProps as any).internal_transform as\n | ((text: string, index: number) => string)\n | undefined\n\n if (childTransform) {\n // Collect child's plain text first, apply transform, then emit as styled segment\n const plainText = collectPlainTextAdapter(child)\n if (plainText.length > 0) {\n const transformed = childTransform(plainText, i)\n if (transformed.length > 0) {\n segments.push({\n text: transformed,\n style: contextToRenderStyle(childContext, inheritedBg),\n })\n }\n }\n } else {\n // Recurse into children with merged style context\n collectStyledSegments(child, childContext, inheritedBg, segments)\n }\n } else {\n // Not a styled Text node — recurse with parent context\n collectStyledSegments(child, parentContext, inheritedBg, segments)\n }\n }\n}\n\n/**\n * Collect plain text from a node tree (no styles). Used for internal_transform\n * application which needs the full concatenated text before transformation.\n */\nfunction collectPlainTextAdapter(node: AgNode): string {\n if (node.textContent !== undefined) return node.textContent\n let result = \"\"\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i]!\n const childProps = child.props as TextProps & BoxProps\n if (childProps?.display === \"none\") continue\n if (child.hidden) continue\n let childText = collectPlainTextAdapter(child)\n if (childText.length > 0 && (child.props as any)?.internal_transform) {\n childText = (child.props as any).internal_transform(childText, i)\n }\n result += childText\n }\n return result\n}\n\n/**\n * Render a Text node.\n */\nfunction renderText(\n node: AgNode,\n buffer: RenderBuffer,\n layout: Rect,\n props: TextProps,\n scrollOffset = 0,\n clipBounds?: ClipRect,\n): void {\n const { x, width: layoutWidth } = layout\n const y = layout.y - scrollOffset\n\n // Build root style context from the Text node's own props\n const rootContext: AdapterStyleContext = {\n color: props.color ?? undefined,\n bold: props.bold,\n dim: props.dim,\n italic: props.italic,\n underline: props.underline,\n underlineStyle: props.underlineStyle as AdapterStyleContext[\"underlineStyle\"],\n underlineColor: props.underlineColor ?? undefined,\n inverse: props.inverse,\n strikethrough: props.strikethrough,\n }\n\n // Inherit bg from nearest ancestor Box with backgroundColor\n const inheritedBg = props.backgroundColor ?? findAncestorBg(node)\n\n // Collect styled segments from all children\n const segments: StyledSegment[] = []\n collectStyledSegments(node, rootContext, inheritedBg, segments)\n\n // Build flat text for formatTextLines (wrapping/truncation)\n const text = segments.map((s) => s.text).join(\"\")\n if (!text) return\n\n // Skip if outside vertical clip bounds\n if (clipBounds && (y < clipBounds.top || y >= clipBounds.bottom)) {\n return\n }\n\n // Determine the maximum column for text rendering.\n let maxCol = x + layoutWidth\n if (clipBounds?.right !== undefined) {\n maxCol = Math.min(maxCol, clipBounds.right)\n }\n\n // Determine the starting column (horizontal clip from left)\n let startCol = x\n if (clipBounds?.left !== undefined) {\n startCol = Math.max(startCol, clipBounds.left)\n }\n\n // Skip if entirely clipped horizontally\n if (startCol >= maxCol) return\n\n // Format text into lines (handles wrapping, truncation, newlines)\n const availableWidth = maxCol - x\n const lines = formatTextLines(text, availableWidth, props.wrap)\n\n // If all segments have the same style (common case), use fast path\n if (segments.length <= 1) {\n const style = segments.length === 1 ? segments[0]!.style : contextToRenderStyle(rootContext, inheritedBg)\n for (let i = 0; i < lines.length; i++) {\n const lineY = y + i\n if (clipBounds && (lineY < clipBounds.top || lineY >= clipBounds.bottom)) continue\n if (!buffer.inBounds(0, lineY)) continue\n const truncated = truncateToWidth(lines[i]!, availableWidth)\n if (truncated) {\n buffer.drawText(x, lineY, truncated, style)\n }\n }\n return\n }\n\n // Multi-segment path: render each line with per-character style lookup.\n // Build a character-to-segment index for the flat text.\n const segmentForChar: number[] = new Array(text.length)\n let charIdx = 0\n for (let s = 0; s < segments.length; s++) {\n const segText = segments[s]!.text\n for (let j = 0; j < segText.length; j++) {\n segmentForChar[charIdx++] = s\n }\n }\n\n // Track how far we've consumed in the flat text across lines\n let flatOffset = 0\n\n for (let i = 0; i < lines.length; i++) {\n const lineY = y + i\n if (clipBounds && (lineY < clipBounds.top || lineY >= clipBounds.bottom)) {\n // Still advance flatOffset past this line\n flatOffset = advanceFlatOffset(text, flatOffset, lines[i]!)\n continue\n }\n if (!buffer.inBounds(0, lineY)) {\n flatOffset = advanceFlatOffset(text, flatOffset, lines[i]!)\n continue\n }\n\n const line = lines[i]!\n const truncated = truncateToWidth(line, availableWidth)\n if (!truncated) {\n flatOffset = advanceFlatOffset(text, flatOffset, line)\n continue\n }\n\n // Render truncated line character by character with per-segment styles\n let col = x\n const lineStartOffset = flatOffset\n let lineCharIdx = 0\n for (const char of truncated) {\n if (col >= maxCol) break\n const srcIdx = lineStartOffset + lineCharIdx\n const segIdx = srcIdx < segmentForChar.length ? segmentForChar[srcIdx]! : 0\n const style = segments[segIdx]!.style\n const charWidth = displayWidth(char)\n if (col + charWidth <= maxCol) {\n buffer.drawChar(col, lineY, char, style)\n // Mark continuation cells for wide characters\n for (let w = 1; w < charWidth; w++) {\n if (buffer.inBounds(col + w, lineY)) {\n buffer.drawChar(col + w, lineY, \"\", style)\n }\n }\n }\n col += charWidth\n lineCharIdx += char.length\n }\n\n flatOffset = advanceFlatOffset(text, flatOffset, line)\n }\n}\n\n/**\n * Advance the flat text offset past a formatted line.\n * formatTextLines may split on whitespace or add ellipsis — we need to find\n * where this line's content came from in the original flat text.\n */\nfunction advanceFlatOffset(flatText: string, offset: number, line: string): number {\n // Skip leading whitespace that formatTextLines may have trimmed\n while (offset < flatText.length && (flatText[offset] === \" \" || flatText[offset] === \"\\n\")) {\n // Check if the line starts with this whitespace — if so, don't skip it\n if (line.length > 0 && line[0] === flatText[offset]) break\n offset++\n }\n // Advance past the line's characters in the flat text\n let lineIdx = 0\n while (lineIdx < line.length && offset < flatText.length) {\n // Handle ellipsis in truncated text — the ellipsis char isn't in the source\n if (line[lineIdx] === \"\\u2026\") {\n lineIdx++\n continue\n }\n if (line[lineIdx] === flatText[offset]) {\n lineIdx++\n offset++\n } else {\n // Mismatch — skip source char (may have been trimmed by wrapping)\n offset++\n }\n }\n return offset\n}\n\n/**\n * Truncate text to fit within a given display width.\n * Respects multi-column characters (CJK, emoji).\n */\nfunction truncateToWidth(text: string, maxWidth: number): string {\n if (maxWidth <= 0) return \"\"\n const textWidth = displayWidth(text)\n if (textWidth <= maxWidth) return text\n\n // Need to truncate — iterate character by character\n let width = 0\n let end = 0\n for (const char of text) {\n const charWidth = displayWidth(char)\n if (width + charWidth > maxWidth) break\n width += charWidth\n end += char.length\n }\n return text.slice(0, end)\n}\n\n/**\n * Collect text content from a node and its children (flat string, no styles).\n * Used by external callers that only need the plain text.\n */\nfunction collectTextContent(node: AgNode): string {\n // Raw text nodes have textContent set directly\n if (node.isRawText && node.textContent !== undefined) {\n return node.textContent\n }\n\n let result = \"\"\n for (const child of node.children) {\n const childProps = child.props as TextProps & BoxProps\n // Skip display=\"none\" children\n if (childProps?.display === \"none\") continue\n // Skip hidden children (Suspense)\n if (child.hidden) continue\n result += collectTextContent(child)\n }\n return result\n}\n\n// ============================================================================\n// Scroll Indicators\n// ============================================================================\n\ninterface ScrollState {\n offset: number\n contentHeight: number\n viewportHeight: number\n firstVisibleChild: number\n lastVisibleChild: number\n stickyChildren?: Array<{\n index: number\n naturalTop: number\n renderOffset: number\n }>\n}\n\n/**\n * Render scroll indicators for a scrollable container.\n */\nfunction renderScrollIndicators(\n _node: AgNode,\n buffer: RenderBuffer,\n layout: Rect,\n props: BoxProps,\n scrollState: ScrollState,\n): void {\n const { x, width, height } = layout\n const y = layout.y\n\n const border = props.borderStyle ? getBorderSize(props) : { top: 0, bottom: 0, right: 0 }\n const canScrollUp = scrollState.offset > 0\n const canScrollDown = scrollState.offset + scrollState.viewportHeight < scrollState.contentHeight\n\n const indicatorX = x + width - border.right - 1\n const style: RenderStyle = { fg: props.borderColor ?? \"#808080\" }\n\n // Up indicator\n if (canScrollUp) {\n const indicatorY = y + border.top\n if (buffer.inBounds(indicatorX, indicatorY)) {\n buffer.drawChar(indicatorX, indicatorY, \"▲\", style)\n }\n }\n\n // Down indicator\n if (canScrollDown) {\n const indicatorY = y + height - border.bottom - 1\n if (buffer.inBounds(indicatorX, indicatorY)) {\n buffer.drawChar(indicatorX, indicatorY, \"▼\", style)\n }\n }\n}\n\n// ============================================================================\n// Children Rendering\n// ============================================================================\n\n/**\n * Render children of a scroll container.\n */\nfunction renderScrollContainerChildren(\n node: AgNode,\n buffer: RenderBuffer,\n props: BoxProps,\n clipBounds?: ClipRect,\n): void {\n const layout = node.contentRect\n const ss = node.scrollState as ScrollState | undefined\n if (!layout || !ss) return\n\n const border = props.borderStyle ? getBorderSize(props) : { top: 0, bottom: 0, left: 0, right: 0 }\n const padding = getPadding(props)\n\n const nodeClip: ClipRect = {\n top: layout.y + border.top + padding.top,\n bottom: layout.y + layout.height - border.bottom - padding.bottom,\n left: layout.x + border.left + padding.left,\n right: layout.x + layout.width - border.right - padding.right,\n }\n\n const childClipBounds: ClipRect = clipBounds\n ? {\n top: Math.max(clipBounds.top, nodeClip.top),\n bottom: Math.min(clipBounds.bottom, nodeClip.bottom),\n left: Math.max(clipBounds.left ?? nodeClip.left!, nodeClip.left!),\n right: Math.min(clipBounds.right ?? nodeClip.right!, nodeClip.right!),\n }\n : nodeClip\n\n // Render visible children\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i]\n if (!child) continue\n const childProps = child.props as BoxProps\n\n if (childProps.position === \"sticky\") continue\n if (i < ss.firstVisibleChild || i > ss.lastVisibleChild) continue\n\n renderNodeToBuffer(child, buffer, ss.offset, childClipBounds)\n }\n\n // Render sticky children\n if (ss.stickyChildren) {\n for (const sticky of ss.stickyChildren) {\n const child = node.children[sticky.index]\n if (!child?.contentRect) continue\n\n const stickyScrollOffset = sticky.naturalTop - sticky.renderOffset\n renderNodeToBuffer(child, buffer, stickyScrollOffset, childClipBounds)\n }\n }\n}\n\n/**\n * Render children of a normal container.\n */\nfunction renderNormalChildren(\n node: AgNode,\n buffer: RenderBuffer,\n scrollOffset: number,\n props: BoxProps,\n clipBounds?: ClipRect,\n): void {\n const layout = node.contentRect\n if (!layout) return\n\n let effectiveClipBounds = clipBounds\n\n if (props.overflow === \"hidden\") {\n const border = props.borderStyle ? getBorderSize(props) : { top: 0, bottom: 0, left: 0, right: 0 }\n const padding = getPadding(props)\n\n // Adjust layout position by scrollOffset to get screen coordinates\n const adjustedY = layout.y - scrollOffset\n const nodeClip: ClipRect = {\n top: adjustedY + border.top + padding.top,\n bottom: adjustedY + layout.height - border.bottom - padding.bottom,\n left: layout.x + border.left + padding.left,\n right: layout.x + layout.width - border.right - padding.right,\n }\n\n effectiveClipBounds = clipBounds\n ? {\n top: Math.max(clipBounds.top, nodeClip.top),\n bottom: Math.min(clipBounds.bottom, nodeClip.bottom),\n left: Math.max(clipBounds.left ?? nodeClip.left!, nodeClip.left!),\n right: Math.min(clipBounds.right ?? nodeClip.right!, nodeClip.right!),\n }\n : nodeClip\n }\n\n const hasStickyChildren = !!(node.stickyChildren && node.stickyChildren.length > 0)\n\n // Multi-pass rendering to match CSS paint order (and content-phase.ts):\n // 1. Normal-flow children (skip sticky and absolute)\n // 2. Sticky children at computed positions\n // 3. Absolute children on top of everything\n\n // First pass: render normal-flow children (skip sticky + absolute)\n let hasAbsoluteChildren = false\n for (const child of node.children) {\n const childProps = child.props as BoxProps\n if (childProps.position === \"absolute\") {\n hasAbsoluteChildren = true\n continue // Skip — rendered in third pass\n }\n if (hasStickyChildren && childProps.position === \"sticky\") continue\n renderNodeToBuffer(child, buffer, scrollOffset, effectiveClipBounds)\n }\n\n // Second pass: render sticky children at their computed positions\n if (node.stickyChildren) {\n for (const sticky of node.stickyChildren) {\n const child = node.children[sticky.index]\n if (!child?.contentRect) continue\n const stickyScrollOffset = sticky.naturalTop - sticky.renderOffset\n renderNodeToBuffer(child, buffer, stickyScrollOffset, effectiveClipBounds)\n }\n }\n\n // Third pass: render absolute children on top (CSS paint order)\n if (hasAbsoluteChildren) {\n for (const child of node.children) {\n const childProps = child.props as BoxProps\n if (childProps.position !== \"absolute\") continue\n renderNodeToBuffer(child, buffer, scrollOffset, effectiveClipBounds)\n }\n }\n}\n",
69
+ "/**\n * Error types for silvery terminal rendering.\n *\n * Separated from scheduler.ts to allow React-free barrel imports.\n * Keep this file React-free — only plain types allowed.\n */\n\nimport type { ContentPhaseStats } from \"./pipeline/types\"\n\n/** Structured mismatch data attached to the error (mirrors MismatchDebugContext shape) */\nexport interface MismatchErrorData {\n /** Content-phase instrumentation snapshot (nodes visited/rendered/skipped, per-flag breakdown) */\n contentPhaseStats?: ContentPhaseStats\n /** Debug context for the mismatched cell (from debug-mismatch.ts) */\n mismatchContext?: unknown\n}\n\n/**\n * Error thrown when SILVERY_STRICT detects a mismatch.\n * This error should NOT be caught by general error handlers - it indicates\n * a bug in incremental rendering that needs to be fixed.\n *\n * When SILVERY_STRICT fires, the error automatically includes:\n * - Content-phase instrumentation (nodes visited/rendered/skipped, per-flag breakdown)\n * - Cell attribution (which node owns the mismatched cell, dirty flags, scroll context)\n */\nexport class IncrementalRenderMismatchError extends Error {\n /** Content-phase instrumentation snapshot */\n contentPhaseStats?: ContentPhaseStats\n /** Debug context for the mismatched cell */\n mismatchContext?: unknown\n\n constructor(message: string, data?: MismatchErrorData) {\n super(message)\n this.name = \"IncrementalRenderMismatchError\"\n this.contentPhaseStats = data?.contentPhaseStats\n this.mismatchContext = data?.mismatchContext\n }\n}\n",
70
+ "/**\n * Buffer diffing for the output phase.\n *\n * Compares two TerminalBuffers and returns a list of changed cells via a\n * pre-allocated pool (zero per-frame allocation). Handles dimension changes\n * (growth and shrink) and wide→narrow character transitions.\n */\n\nimport type { TerminalBuffer } from \"../buffer\"\nimport type { CellChange } from \"./types\"\n\n// ============================================================================\n// Pre-allocated diff pool\n// ============================================================================\n\n/**\n * Create a fresh CellChange with empty cell data.\n * Used to populate the pre-allocated pool.\n */\nfunction createEmptyCellChange(): CellChange {\n return {\n x: 0,\n y: 0,\n cell: {\n char: \" \",\n fg: null,\n bg: null,\n underlineColor: null,\n attrs: {},\n wide: false,\n continuation: false,\n hyperlink: undefined,\n },\n }\n}\n\n/** Pre-allocated pool of CellChange objects, reused across frames. */\nconst diffPool: CellChange[] = []\n\n/** Current pool capacity. */\nlet diffPoolCapacity = 0\n\n/**\n * Ensure the diff pool has at least `capacity` entries.\n * Grows the pool if needed; never shrinks.\n */\nfunction ensureDiffPoolCapacity(capacity: number): void {\n if (capacity <= diffPoolCapacity) return\n for (let i = diffPoolCapacity; i < capacity; i++) {\n diffPool.push(createEmptyCellChange())\n }\n diffPoolCapacity = capacity\n}\n\n/**\n * Write cell data from a buffer into a pre-allocated CellChange entry.\n * Uses readCellInto for zero-allocation reads.\n */\nfunction writeCellChange(change: CellChange, x: number, y: number, buffer: TerminalBuffer): void {\n change.x = x\n change.y = y\n buffer.readCellInto(x, y, change.cell)\n}\n\n/**\n * Write empty cell data into a pre-allocated CellChange entry.\n * Used for shrink regions where cells need to be cleared.\n */\nfunction writeEmptyCellChange(change: CellChange, x: number, y: number): void {\n change.x = x\n change.y = y\n const cell = change.cell\n cell.char = \" \"\n cell.fg = null\n cell.bg = null\n cell.underlineColor = null\n // Reset attrs fields\n const attrs = cell.attrs\n attrs.bold = undefined\n attrs.dim = undefined\n attrs.italic = undefined\n attrs.underline = undefined\n attrs.underlineStyle = undefined\n attrs.blink = undefined\n attrs.inverse = undefined\n attrs.hidden = undefined\n attrs.strikethrough = undefined\n cell.wide = false\n cell.continuation = false\n cell.hyperlink = undefined\n}\n\n/**\n * Diff result: pool reference + count (avoids per-frame array allocation).\n */\nexport interface DiffResult {\n pool: CellChange[]\n count: number\n}\n\n/** Reusable diff result object (avoids allocating a new one per frame). */\nconst diffResult: DiffResult = { pool: diffPool, count: 0 }\n\n/**\n * Diff two buffers and return changes via pre-allocated pool.\n *\n * Optimization: Uses a pre-allocated pool of CellChange objects to avoid\n * allocating new objects per changed cell. Uses readCellInto for\n * zero-allocation cell reads. The pool grows as needed but is reused\n * between frames. Returns a pool+count pair instead of slicing the array.\n */\nexport function diffBuffers(prev: TerminalBuffer, next: TerminalBuffer): DiffResult {\n // Ensure pool is large enough for worst case (all cells changed).\n // Wide→narrow transitions emit an extra change for the continuation cell,\n // so worst case is 1.5x (every other cell could be a wide→narrow transition).\n const cells = Math.max(prev.width, next.width) * Math.max(prev.height, next.height)\n const maxChanges = cells + (cells >> 1) // 1.5x\n ensureDiffPoolCapacity(maxChanges)\n\n let changeCount = 0\n\n // Dimension mismatch means we need to re-render everything visible\n const height = Math.min(prev.height, next.height)\n const width = Math.min(prev.width, next.width)\n\n // Use dirty row bounding box to narrow the scan range.\n // If no rows are dirty, minDirtyRow is -1 and the loop body is skipped.\n const startRow = next.minDirtyRow === -1 ? 0 : next.minDirtyRow\n const endRow = next.maxDirtyRow === -1 ? -1 : Math.min(next.maxDirtyRow, height - 1)\n\n for (let y = startRow; y <= endRow; y++) {\n // Skip individual clean rows within the bounding box\n if (!next.isRowDirty(y)) continue\n\n // Fast row-level pre-check: if all packed metadata, chars, AND Map-based\n // extras (true colors, underline colors, hyperlinks) match, skip per-cell\n // comparison entirely. This catches rows marked dirty by fill() or\n // scrollRegion() that didn't actually change content.\n // NOTE: rowExtrasEquals is essential — rowMetadataEquals only checks packed\n // flags (e.g., \"has true color fg\"), not the actual RGB values in the Maps.\n if (next.rowMetadataEquals(y, prev) && next.rowCharsEquals(y, prev) && next.rowExtrasEquals(y, prev)) continue\n\n for (let x = 0; x < width; x++) {\n // Use buffer's optimized cellEquals which compares packed metadata first\n if (!next.cellEquals(x, y, prev)) {\n writeCellChange(diffPool[changeCount]!, x, y, next)\n changeCount++\n\n // Wide char transition: when prev had a wide char and next doesn't,\n // we must also emit the continuation position (x+1) as a change.\n // The terminal's state at x+1 contains the second half of the wide\n // char, but the buffer may show x+1 as \"unchanged\" (both prev and\n // next are ' '). Without this explicit change, changesToAnsi skips\n // x+1 and the terminal retains the wide char remnant, causing\n // cursor drift.\n if (x + 1 < width && prev.isCellWide(x, y) && !next.isCellWide(x, y)) {\n writeCellChange(diffPool[changeCount]!, x + 1, y, next)\n changeCount++\n }\n }\n }\n }\n\n // Handle size growth: add all cells in new areas.\n // Width growth covers the right strip (x >= prev.width) for ALL rows.\n // Height growth covers the bottom strip (y >= prev.height) but only up to\n // prev.width to avoid double-counting the corner with width growth.\n const widthGrew = next.width > prev.width\n if (widthGrew) {\n for (let y = 0; y < next.height; y++) {\n for (let x = prev.width; x < next.width; x++) {\n writeCellChange(diffPool[changeCount]!, x, y, next)\n changeCount++\n }\n }\n }\n if (next.height > prev.height) {\n // When width also grew, only iterate x=0..prev.width (the rest was\n // already covered by width growth above). Otherwise iterate full width.\n const xEnd = widthGrew ? prev.width : next.width\n for (let y = prev.height; y < next.height; y++) {\n for (let x = 0; x < xEnd; x++) {\n writeCellChange(diffPool[changeCount]!, x, y, next)\n changeCount++\n }\n }\n }\n\n // Handle size shrink: clear cells in old-but-not-new areas.\n // Width shrink covers x >= next.width for the shared height.\n // Height shrink covers y >= next.height but only up to next.width when\n // width also shrank, to avoid double-counting the corner.\n const widthShrank = prev.width > next.width\n if (widthShrank) {\n for (let y = 0; y < height; y++) {\n for (let x = next.width; x < prev.width; x++) {\n writeEmptyCellChange(diffPool[changeCount]!, x, y)\n changeCount++\n }\n }\n }\n if (prev.height > next.height) {\n // When width also shrank, the corner (x >= next.width, y >= next.height)\n // was NOT covered by width shrink (which only iterates y < height =\n // min(prev.height, next.height) = next.height). So iterate full prev.width.\n for (let y = next.height; y < prev.height; y++) {\n for (let x = 0; x < prev.width; x++) {\n writeEmptyCellChange(diffPool[changeCount]!, x, y)\n changeCount++\n }\n }\n }\n\n if (changeCount > maxChanges) {\n throw new Error(\n `diffBuffers: changeCount ${changeCount} exceeds pool capacity ${maxChanges} ` +\n `(prev ${prev.width}x${prev.height}, next ${next.width}x${next.height})`,\n )\n }\n\n diffResult.pool = diffPool\n diffResult.count = changeCount\n return diffResult\n}\n",
71
+ "/**\n * Phase 4: Output Phase\n *\n * Diff two buffers and produce minimal ANSI output.\n *\n * Debug: Set SILVERY_DEBUG_OUTPUT=1 to log diff changes and ANSI sequences.\n */\n\nimport {\n type Style,\n type TerminalBuffer,\n type UnderlineStyle,\n VISIBLE_SPACE_ATTR_MASK,\n colorEquals,\n createMutableCell,\n hasActiveAttrs,\n isDefaultBg,\n styleEquals,\n} from \"../buffer\"\nimport { fgColorCode, bgColorCode } from \"../ansi/sgr-codes\"\nimport type { CursorState } from \"@silvery/ag-react/hooks/useCursor\"\nimport { IncrementalRenderMismatchError } from \"../errors\"\nimport { textSized } from \"../text-sizing\"\nimport { graphemeWidth, isTextSizingEnabled } from \"../unicode\"\nimport type { CellChange } from \"./types\"\n\nconst DEBUG_OUTPUT = !!process.env.SILVERY_DEBUG_OUTPUT\nconst FULL_RENDER = !!process.env.SILVERY_FULL_RENDER\nconst DEBUG_CAPTURE = !!process.env.SILVERY_DEBUG_CAPTURE\nconst CAPTURE_RAW = !!process.env.SILVERY_CAPTURE_RAW\nlet _debugFrameCount = 0\nlet _captureRawFrameCount = 0\n\n// ============================================================================\n// Terminal Capability Flags (suppress unsupported SGR codes)\n// ============================================================================\n\nimport type { TerminalCaps } from \"../terminal-caps\"\n\n/**\n * @deprecated Use createOutputPhase(caps) instead. This is a no-op.\n */\nexport function setOutputCaps(\n _caps: Partial<Pick<TerminalCaps, \"underlineStyles\" | \"underlineColor\" | \"colorLevel\">>,\n): void {\n // No-op: use createOutputPhase(caps) instead\n}\n\n// ============================================================================\n// Output Phase Factory (per-term instance, no globals)\n// ============================================================================\n\n/** Output-phase capabilities type. */\nexport type OutputCaps = Pick<TerminalCaps, \"underlineStyles\" | \"underlineColor\" | \"colorLevel\">\n\n// ============================================================================\n// Output Context (per-instance state, replaces module-level globals)\n// ============================================================================\n\n/**\n * Per-instance output context containing terminal capabilities, measurer,\n * caches, and per-frame viewport state. Threaded through internal functions\n * to eliminate module-level mutable state and prevent \"forgot to thread\n * parameter\" failures. Caches are per-context because SGR output depends on caps.\n *\n * Per-frame fields (mode, termRows) are set by the caller before each frame:\n * - createOutputPhase() closure sets them in scopedOutputPhase()\n * - bare outputPhase() sets them from its parameters\n */\nexport interface OutputContext {\n readonly caps: OutputCaps\n readonly measurer: OutputMeasurer | null\n readonly sgrCache: Map<string, string>\n readonly transitionCache: Map<string, string>\n /** Render mode for the current frame. Set per-frame by the output phase entry point. */\n mode: \"fullscreen\" | \"inline\"\n /** Terminal height in rows for the current frame. Caps output to prevent\n * scrollback corruption when content exceeds terminal height. Set per-frame. */\n termRows: number | undefined\n}\n\n/** Default context used by bare outputPhase() calls (full capability support, no measurer). */\nconst defaultContext: OutputContext = {\n caps: {\n underlineStyles: true,\n underlineColor: true,\n colorLevel: \"truecolor\",\n },\n measurer: null,\n sgrCache: new Map(),\n transitionCache: new Map(),\n mode: \"fullscreen\",\n termRows: undefined,\n}\n\n/** Output phase function signature. */\nexport interface OutputPhaseFn {\n (\n prev: TerminalBuffer | null,\n next: TerminalBuffer,\n mode?: \"fullscreen\" | \"inline\",\n scrollbackOffset?: number,\n termRows?: number,\n cursorPos?: CursorState | null,\n ): string\n /** Reset inline cursor state. Used by useScrollback to clear cursor tracking on resize. */\n resetInlineState?: () => void\n /** Get the current inline cursor row (relative to render region start). -1 if unknown. */\n getInlineCursorRow?: () => number\n /** Promote frozen content to scrollback. Called by useScrollback to queue\n * frozen content for the next render — the output phase writes frozen + live\n * content in a single target.write() to avoid flicker. */\n promoteScrollback?: (frozenContent: string, frozenLineCount: number) => void\n}\n\n// ============================================================================\n// Output Phase Measurer (module-local, avoids dual-module-loading issues)\n// ============================================================================\n// bun can load the same .ts file via symlink + real path as separate module\n// instances. This means `_scopedMeasurer` set by `runWithMeasurer()` in\n// pipeline/index.ts's instance of unicode.ts is invisible to output-phase.ts's\n// instance. We avoid this by closing over the measurer in createOutputPhase()\n// and setting a module-local variable that all output-phase functions read.\n\nexport interface OutputMeasurer {\n graphemeWidth(grapheme: string): number\n readonly textSizingEnabled: boolean\n}\n\n/** Get grapheme width using the output context measurer (falls back to unicode.ts import). */\nfunction outputGraphemeWidth(g: string, ctx: OutputContext): number {\n return ctx.measurer ? ctx.measurer.graphemeWidth(g) : graphemeWidth(g)\n}\n\n/** Check if text sizing is enabled using the output context measurer. */\nfunction outputTextSizingEnabled(ctx: OutputContext): boolean {\n return ctx.measurer ? ctx.measurer.textSizingEnabled : isTextSizingEnabled()\n}\n\n/**\n * Create a scoped output phase that uses specific terminal capabilities.\n *\n * @param caps - Terminal capabilities for SGR code generation\n * @param measurer - Width measurer for graphemeWidth/textSizingEnabled (avoids dual-module-loading issues)\n */\nexport function createOutputPhase(caps: Partial<OutputCaps>, measurer?: OutputMeasurer): OutputPhaseFn {\n // Instance-scoped context — caps, measurer, and caches are all per-instance.\n // mode and termRows are set per-frame in scopedOutputPhase() below.\n // No module-level globals are read or modified.\n const ctx: OutputContext = {\n caps: {\n underlineStyles: caps.underlineStyles ?? true,\n underlineColor: caps.underlineColor ?? true,\n colorLevel: caps.colorLevel ?? \"truecolor\",\n },\n measurer: measurer ?? null,\n sgrCache: new Map(),\n transitionCache: new Map(),\n mode: \"fullscreen\",\n termRows: undefined,\n }\n // Instance-scoped inline cursor state — persists across frames for incremental rendering.\n // Each createOutputPhase() call gets its own state, eliminating module-level globals.\n const inlineState = createInlineCursorState()\n\n // Instance-scoped accumulated ANSI state for SILVERY_STRICT_ACCUMULATE verification.\n // Tracks per-instance verification state rather than using module-level globals.\n const accState = {\n accumulatedAnsi: \"\",\n accumulateWidth: 0,\n accumulateHeight: 0,\n accumulateFrameCount: 0,\n }\n\n // Instance-scoped terminal verify state for SILVERY_STRICT_TERMINAL verification.\n const tvState = createTerminalVerifyState()\n\n // Pending scrollback promotion — queued by useScrollback, consumed by the next render.\n let pendingPromotion: { frozenContent: string; frozenLineCount: number } | null = null\n\n const fn: OutputPhaseFn = function scopedOutputPhase(\n prev: TerminalBuffer | null,\n next: TerminalBuffer,\n mode: \"fullscreen\" | \"inline\" = \"fullscreen\",\n scrollbackOffset = 0,\n termRows?: number,\n cursorPos?: CursorState | null,\n ): string {\n // Set per-frame viewport state on the context — internal functions read from ctx\n // instead of accepting mode/termRows as separate parameters.\n ctx.mode = mode\n ctx.termRows = termRows\n // Handle scrollback promotion: write frozen content + live content in one pass.\n if (pendingPromotion && mode === \"inline\") {\n const promo = pendingPromotion\n pendingPromotion = null\n return handleScrollbackPromotion(inlineState, promo.frozenContent, promo.frozenLineCount, next, cursorPos, ctx)\n }\n return outputPhase(prev, next, mode, scrollbackOffset, termRows, cursorPos, inlineState, ctx, accState, tvState)\n }\n\n fn.resetInlineState = () => {\n Object.assign(inlineState, createInlineCursorState())\n inlineState.forceFirstRender = true\n // Clear any queued promotion — the resize handler re-emits all frozen items\n // directly, so any pending promotion from the freeze effect is redundant.\n // Without this, freeze+resize in the same frame causes duplicate frozen content.\n pendingPromotion = null\n }\n\n fn.getInlineCursorRow = () => inlineState.prevCursorRow\n\n fn.promoteScrollback = (frozenContent: string, frozenLineCount: number) => {\n if (pendingPromotion) {\n pendingPromotion.frozenContent += frozenContent\n pendingPromotion.frozenLineCount += frozenLineCount\n } else {\n pendingPromotion = { frozenContent, frozenLineCount }\n }\n }\n\n return fn\n}\n\n/**\n * Handle scrollback promotion: write frozen content + live content in a single output string.\n *\n * Instead of useScrollback writing directly to stdout (which blanks the screen and\n * causes flicker), this function builds one output string that:\n * 1. Moves cursor to the render region start (no clearing)\n * 2. Writes frozen content (each line overwrites in-place via \\x1b[K])\n * 3. Writes live content via bufferToAnsi (also with per-line \\x1b[K])\n * 4. Erases any leftover lines from the previous frame\n * 5. Positions the hardware cursor\n *\n * Result: a single target.write() with no blanking — no flicker.\n */\nfunction handleScrollbackPromotion(\n state: InlineCursorState,\n frozenContent: string,\n frozenLineCount: number,\n next: TerminalBuffer,\n cursorPos: CursorState | null | undefined,\n ctx: OutputContext,\n): string {\n const { termRows } = ctx\n // 1. Move cursor to render region start\n let output = \"\"\n if (state.prevCursorRow > 0) {\n output += `\\x1b[${state.prevCursorRow}A`\n }\n output += \"\\r\" // column 0, NO \\x1b[J clear\n\n // 2. Write frozen content (overwrites old content in-place, OSC markers included)\n output += frozenContent\n\n // 3. Write live content via bufferToAnsi (each line has \\x1b[K — no blanking)\n const nextContentLines = findLastContentLine(next) + 1\n const maxOutputLines = termRows != null ? Math.min(nextContentLines, termRows) : nextContentLines\n output += bufferToAnsi(next, ctx, maxOutputLines)\n\n // Total lines on-screen: frozen + live. The terminal may scroll if this exceeds\n // termRows, naturally pushing frozen lines into scrollback. No padding needed —\n // we track ALL on-screen lines so the next render can overwrite them cleanly.\n const totalOnScreen = frozenLineCount + maxOutputLines\n\n // 4. Erase leftover lines at bottom (if content shrank)\n const oldTotalLines = state.prevOutputLines\n const nextLastLine = totalOnScreen - 1\n const terminalScroll = termRows != null ? Math.max(0, totalOnScreen - termRows) : 0\n const lastOccupied = Math.max(oldTotalLines - 1 - terminalScroll, 0)\n if (lastOccupied > nextLastLine) {\n for (let y = nextLastLine + 1; y <= lastOccupied; y++) {\n output += \"\\n\\r\\x1b[K\"\n }\n const up = lastOccupied - nextLastLine\n if (up > 0) output += `\\x1b[${up}A`\n }\n\n // 5. Cursor suffix (hardware cursor positioning)\n // Cursor is at the end of live content (row totalOnScreen - 1 relative to\n // render region start). inlineCursorSuffix moves it to the useCursor position\n // within the live content area.\n output += inlineCursorSuffix(cursorPos ?? null, next, ctx)\n\n // 6. Update tracking for subsequent incremental renders.\n // Track cursor position and output lines relative to the LIVE content only.\n // Frozen content has been written as raw ANSI above the live content and is\n // now \"owned\" by the terminal — it stays on screen until natural scrolling\n // pushes it into terminal scrollback. The next render only needs to cursor-up\n // to the start of the live content area, not past the frozen content.\n //\n // This is critical for real terminals where pre-existing content (shell prompt,\n // direnv output) sits above the app. If prevCursorRow included frozenLineCount,\n // the cursor-up would overshoot into the shell prompt area, clearing it.\n let startLine = 0\n if (termRows != null && nextContentLines > termRows) startLine = nextContentLines - termRows\n state.prevBuffer = next\n\n // Cursor row within the LIVE content area only (not including frozen lines).\n // inlineCursorSuffix already positioned the cursor within the live content.\n if (cursorPos?.visible) {\n const visibleRow = cursorPos.y - startLine\n state.prevCursorRow = visibleRow >= 0 && visibleRow < maxOutputLines ? visibleRow : maxOutputLines - 1\n } else {\n state.prevCursorRow = maxOutputLines - 1\n }\n state.prevOutputLines = maxOutputLines\n\n return output\n}\n\n// These use getters so they can be set after module load (e.g., in test files).\n// SILVERY_STRICT enables buffer + output checks (per-frame), including vt100 output verification.\n// SILVERY_STRICT_ACCUMULATE is separate — it replays ALL frames (O(N²)) and is opt-in only.\nfunction isStrictOutput(): boolean {\n return !!process.env.SILVERY_STRICT\n}\nfunction isStrictAccumulate(): boolean {\n return !!process.env.SILVERY_STRICT_ACCUMULATE\n}\n/** Parse SILVERY_STRICT_TERMINAL into a list of backends.\n *\n * Accepts `all` (= `vt100,xterm,ghostty`) or a comma-separated list of\n * backend names (e.g., `vt100,xterm`).\n *\n * The `vt100` backend uses the internal `replayAnsiWithStyles` parser (stateless).\n * `xterm` and `ghostty` use persistent terminal emulators (stateful).\n */\nfunction strictTerminalBackends(): Array<\"vt100\" | \"xterm\" | \"ghostty\"> {\n const val = (process.env.SILVERY_STRICT_TERMINAL ?? \"\").toLowerCase().trim()\n if (!val) return []\n if (val === \"all\") return [\"vt100\", \"xterm\", \"ghostty\"]\n // Comma-separated list\n const backends = val\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean)\n const valid = new Set([\"vt100\", \"xterm\", \"ghostty\"])\n for (const b of backends) {\n if (!valid.has(b)) {\n // eslint-disable-next-line no-console\n console.warn(`SILVERY_STRICT_TERMINAL: unknown backend '${b}', ignoring`)\n }\n }\n return backends.filter((b) => valid.has(b)) as Array<\"vt100\" | \"xterm\" | \"ghostty\">\n}\n\n/** Per-instance state for SILVERY_STRICT_ACCUMULATE verification. */\ninterface AccumulateState {\n accumulatedAnsi: string\n accumulateWidth: number\n accumulateHeight: number\n accumulateFrameCount: number\n}\n\n/** Default accumulate state used by bare outputPhase() calls. */\nconst defaultAccState: AccumulateState = {\n accumulatedAnsi: \"\",\n accumulateWidth: 0,\n accumulateHeight: 0,\n accumulateFrameCount: 0,\n}\n\n/** Per-instance state for SILVERY_STRICT_TERMINAL verification.\n * Holds persistent terminal(s) that accumulate incremental ANSI output\n * across frames, enabling comparison against a fresh render in an independent emulator. */\ninterface TerminalVerifyState {\n /** The persistent xterm.js terminal accumulating incremental output */\n terminal: import(\"@termless/core\").Terminal | null\n /** Optional persistent Ghostty terminal for cross-backend verification */\n ghosttyTerminal: import(\"@termless/core\").Terminal | null\n /** Width of the terminal */\n width: number\n /** Height of the terminal */\n height: number\n /** Frame count for diagnostics */\n frameCount: number\n /** Which emulator backends to verify (xterm, ghostty — vt100 handled separately) */\n backends: Array<\"xterm\" | \"ghostty\">\n /** Whether the vt100 (replayAnsiWithStyles) backend is enabled */\n hasVt100: boolean\n}\n\n/** Create fresh terminal verify state. */\nfunction createTerminalVerifyState(): TerminalVerifyState {\n const allBackends = strictTerminalBackends()\n return {\n terminal: null,\n ghosttyTerminal: null,\n width: 0,\n height: 0,\n frameCount: 0,\n backends: allBackends.filter((b) => b !== \"vt100\") as Array<\"xterm\" | \"ghostty\">,\n hasVt100: allBackends.includes(\"vt100\"),\n }\n}\n\n/** Default terminal verify state used by bare outputPhase() calls. */\nconst defaultTerminalVerifyState = createTerminalVerifyState()\n\n// ============================================================================\n// Inline Mode: Inter-frame Cursor Tracking (instance-scoped)\n// ============================================================================\n\n/**\n * Mutable state for inline mode inter-frame cursor tracking.\n * Captured in the createOutputPhase() closure — no module-level globals.\n */\ninterface InlineCursorState {\n /** Row within render region after last inline frame's cursor suffix. -1 = unknown. */\n prevCursorRow: number\n /** Total output lines rendered in last frame. Used to clear old content on resize. */\n prevOutputLines: number\n /** Previous frame's buffer — used for incremental rendering when runtime invalidates (resize). */\n prevBuffer: TerminalBuffer | null\n /** When true, the next inline render treats prev as null (first-render path).\n * Set by resetInlineState() after useScrollback clears and re-emits frozen items. */\n forceFirstRender: boolean\n}\n\n/** Create fresh inline cursor state (unknown position → first call falls back to full render). */\nfunction createInlineCursorState(): InlineCursorState {\n return {\n prevCursorRow: -1,\n prevOutputLines: 0,\n prevBuffer: null,\n forceFirstRender: false,\n }\n}\n\n/**\n * Update cursor tracking after an inline render frame.\n * Records where the terminal cursor ends up after inlineCursorSuffix().\n */\nfunction updateInlineCursorRow(\n state: InlineCursorState,\n cursorPos: CursorState | null | undefined,\n maxOutputLines: number,\n startLine: number,\n): void {\n if (cursorPos?.visible) {\n const visibleRow = cursorPos.y - startLine\n state.prevCursorRow = visibleRow >= 0 && visibleRow < maxOutputLines ? visibleRow : maxOutputLines - 1\n } else {\n // Cursor hidden: cursor stays at end of last content line\n state.prevCursorRow = maxOutputLines - 1\n }\n state.prevOutputLines = maxOutputLines\n}\n\n/**\n * Wrap a cell character in OSC 66 if text sizing is enabled and the cell is\n * wide. OSC 66 tells the terminal to render the character in exactly `width`\n * cells, matching the layout engine's measurement.\n *\n * Previously this only wrapped specific categories (PUA, text-presentation\n * emoji, flag emoji) — a whack-a-mole approach that missed new categories\n * as Unicode evolved. Now wraps ALL wide chars unconditionally: if the buffer\n * says width 2, the terminal is told width 2. CJK chars don't strictly need\n * it (terminals agree on their width), but the ~8-byte overhead per wide char\n * is negligible and eliminates any future width disagreement.\n */\nfunction wrapTextSizing(char: string, wide: boolean, ctx: OutputContext): string {\n if (!wide || !outputTextSizingEnabled(ctx)) return char\n return textSized(char, 2)\n}\n\n// ============================================================================\n// Style Interning + SGR Cache\n// ============================================================================\n\n// SGR caches are now per-OutputContext (see OutputContext interface).\n// This is correct because SGR output depends on caps (underlineStyles,\n// underlineColor), so caches populated under one caps configuration\n// would produce wrong results under a different one.\n\n/**\n * Serialize a Style into a cache key string.\n * Fast path: most styles are simple (256-color or null fg/bg, no true color).\n */\nfunction styleToKey(style: Style): string {\n const fg = style.fg\n const bg = style.bg\n const attrs = style.attrs\n\n // Fast path: common case of simple colors + few attrs\n let key = \"\"\n\n // fg\n if (fg === null) {\n key = \"n\"\n } else if (typeof fg === \"number\") {\n key = `${fg}`\n } else {\n key = `r${fg.r},${fg.g},${fg.b}`\n }\n\n key += \"|\"\n\n // bg\n if (bg === null) {\n key += \"n\"\n } else if (typeof bg === \"number\") {\n key += `${bg}`\n } else {\n key += `r${bg.r},${bg.g},${bg.b}`\n }\n\n // attrs packed as bitmask for speed\n let attrBits = 0\n if (attrs.bold) attrBits |= 1\n if (attrs.dim) attrBits |= 2\n if (attrs.italic) attrBits |= 4\n if (attrs.underline) attrBits |= 8\n if (attrs.inverse) attrBits |= 16\n if (attrs.strikethrough) attrBits |= 32\n if (attrs.blink) attrBits |= 64\n if (attrs.hidden) attrBits |= 128\n\n key += `|${attrBits}`\n\n // Underline style (rare)\n if (attrs.underlineStyle) {\n key += `|u${attrs.underlineStyle}`\n }\n\n // Underline color (rare)\n const ul = style.underlineColor\n if (ul !== null && ul !== undefined) {\n if (typeof ul === \"number\") {\n key += `|l${ul}`\n } else {\n key += `|lr${ul.r},${ul.g},${ul.b}`\n }\n }\n\n // Hyperlink URL (rare)\n if (style.hyperlink) {\n key += `|h${style.hyperlink}`\n }\n\n return key\n}\n\n/**\n * Get the SGR escape string for a style, using the intern cache.\n * Cache hit: O(1) Map lookup + key serialization.\n * Cache miss: builds the SGR string and caches it.\n */\nfunction cachedStyleToAnsi(style: Style, ctx: OutputContext): string {\n const key = styleToKey(style)\n let sgr = ctx.sgrCache.get(key)\n if (sgr !== undefined) return sgr\n sgr = styleToAnsi(style, ctx)\n ctx.sgrCache.set(key, sgr)\n if (ctx.sgrCache.size > 1000) ctx.sgrCache.clear()\n return sgr\n}\n\n/**\n * Compute the minimal SGR transition between two styles.\n *\n * When oldStyle is null (first cell or after reset), falls through to\n * full SGR generation via cachedStyleToAnsi. Otherwise, diffs attribute\n * by attribute and emits only changed SGR codes. Caches the result for\n * each (oldKey, newKey) pair.\n */\nfunction styleTransition(oldStyle: Style | null, newStyle: Style, ctx: OutputContext): string {\n // First cell or after reset — full generation\n if (!oldStyle) return cachedStyleToAnsi(newStyle, ctx)\n\n // Same style — nothing to emit\n if (styleEquals(oldStyle, newStyle)) return \"\"\n\n // Check transition cache\n const oldKey = styleToKey(oldStyle)\n const newKey = styleToKey(newStyle)\n const cacheKey = `${oldKey}\\x00${newKey}`\n const cached = ctx.transitionCache.get(cacheKey)\n if (cached !== undefined) return cached\n\n // Build minimal diff\n const codes: string[] = []\n\n // Check attributes that can only be \"turned off\" via reset or specific off-codes.\n // If an attribute was on and is now off, we need either the off-code or a full reset.\n const oa = oldStyle.attrs\n const na = newStyle.attrs\n\n // Bold and dim share SGR 22 as their off-code, so handle them together\n // to avoid emitting duplicate codes.\n const boldChanged = Boolean(oa.bold) !== Boolean(na.bold)\n const dimChanged = Boolean(oa.dim) !== Boolean(na.dim)\n if (boldChanged || dimChanged) {\n const boldOff = boldChanged && !na.bold\n const dimOff = dimChanged && !na.dim\n if (boldOff || dimOff) {\n // SGR 22 resets both bold and dim\n codes.push(\"22\")\n // Re-enable whichever should stay on\n if (na.bold) codes.push(\"1\")\n if (na.dim) codes.push(\"2\")\n } else {\n // Only turning attributes on\n if (boldChanged && na.bold) codes.push(\"1\")\n if (dimChanged && na.dim) codes.push(\"2\")\n }\n }\n if (Boolean(oa.italic) !== Boolean(na.italic)) {\n codes.push(na.italic ? \"3\" : \"23\")\n }\n\n // Underline: compare both underline flag and underlineStyle\n const oldUl = Boolean(oa.underline)\n const newUl = Boolean(na.underline)\n const oldUlStyle = oa.underlineStyle ?? false\n const newUlStyle = na.underlineStyle ?? false\n if (oldUl !== newUl || oldUlStyle !== newUlStyle) {\n if (!ctx.caps.underlineStyles) {\n // Terminal doesn't support SGR 4:x — fall back to simple SGR 4/24\n codes.push(newUl || na.underlineStyle ? \"4\" : \"24\")\n } else {\n const sgrSub = underlineStyleToSgr(na.underlineStyle)\n if (sgrSub !== null && sgrSub !== 0) {\n codes.push(`4:${sgrSub}`)\n } else if (newUl) {\n codes.push(\"4\")\n } else {\n codes.push(\"24\")\n }\n }\n }\n\n if (Boolean(oa.inverse) !== Boolean(na.inverse)) {\n codes.push(na.inverse ? \"7\" : \"27\")\n }\n if (Boolean(oa.hidden) !== Boolean(na.hidden)) {\n codes.push(na.hidden ? \"8\" : \"28\")\n }\n if (Boolean(oa.strikethrough) !== Boolean(na.strikethrough)) {\n codes.push(na.strikethrough ? \"9\" : \"29\")\n }\n if (Boolean(oa.blink) !== Boolean(na.blink)) {\n codes.push(na.blink ? \"5\" : \"25\")\n }\n\n // Foreground color\n if (!colorEquals(oldStyle.fg, newStyle.fg)) {\n if (newStyle.fg === null) {\n codes.push(\"39\")\n } else {\n codes.push(fgColorCode(newStyle.fg))\n }\n }\n\n // Background color\n if (!colorEquals(oldStyle.bg, newStyle.bg)) {\n if (newStyle.bg === null) {\n codes.push(\"49\")\n } else {\n codes.push(bgColorCode(newStyle.bg))\n }\n }\n\n // Underline color (SGR 58/59) — skip for terminals that don't support it\n if (ctx.caps.underlineColor && !colorEquals(oldStyle.underlineColor, newStyle.underlineColor)) {\n if (newStyle.underlineColor === null || newStyle.underlineColor === undefined) {\n // SGR 59 resets underline color\n codes.push(\"59\")\n } else if (typeof newStyle.underlineColor === \"number\") {\n codes.push(`58;5;${newStyle.underlineColor}`)\n } else {\n codes.push(`58;2;${newStyle.underlineColor.r};${newStyle.underlineColor.g};${newStyle.underlineColor.b}`)\n }\n }\n\n // Hyperlink (OSC 8) is handled separately in the render loops, not here.\n\n let result: string\n if (codes.length === 0) {\n // Styles differ but no SGR codes emitted (e.g., hyperlink-only change).\n // Fall back to full generation to be safe.\n result = cachedStyleToAnsi(newStyle, ctx)\n } else {\n result = `\\x1b[${codes.join(\";\")}m`\n }\n\n ctx.transitionCache.set(cacheKey, result)\n if (ctx.transitionCache.size > 1000) ctx.transitionCache.clear()\n return result\n}\n\n/**\n * Map underline style to SGR 4:x subparameter.\n */\nfunction underlineStyleToSgr(style: UnderlineStyle | undefined): number | null {\n switch (style) {\n case false:\n return 0 // SGR 4:0 = no underline\n case \"single\":\n return 1 // SGR 4:1 = single underline\n case \"double\":\n return 2 // SGR 4:2 = double underline\n case \"curly\":\n return 3 // SGR 4:3 = curly underline\n case \"dotted\":\n return 4 // SGR 4:4 = dotted underline\n case \"dashed\":\n return 5 // SGR 4:5 = dashed underline\n default:\n return null // Use simple SGR 4 or no underline\n }\n}\n\n/**\n * Diff two buffers and produce minimal ANSI output.\n *\n * @param prev Previous buffer (null on first render)\n * @param next Current buffer\n * @param mode Render mode: fullscreen or inline\n * @param scrollbackOffset Lines written to stdout between renders (inline mode)\n * @param termRows Terminal height in rows (inline mode) — caps output to prevent\n * scrollback corruption when content exceeds terminal height\n * @returns ANSI escape sequence string\n */\nexport function outputPhase(\n prev: TerminalBuffer | null,\n next: TerminalBuffer,\n mode: \"fullscreen\" | \"inline\" = \"fullscreen\",\n scrollbackOffset = 0,\n termRows?: number,\n cursorPos?: CursorState | null,\n _inlineState?: InlineCursorState,\n _ctx?: OutputContext,\n _accState?: AccumulateState,\n _tvState?: TerminalVerifyState,\n): string {\n // Bare outputPhase() calls use a fresh cursor state each time.\n // prevCursorRow = -1 means incremental rendering always falls back to full render.\n // Instance-scoped state (via createOutputPhase) enables incremental across frames.\n const inlineState = _inlineState ?? createInlineCursorState()\n const ctx = _ctx ?? defaultContext\n const accState = _accState ?? defaultAccState\n\n // Set per-frame viewport state on the context — internal functions read from ctx.\n // For bare outputPhase() calls (no createOutputPhase), this updates defaultContext\n // each call. For scoped calls, ctx.mode/termRows were already set by the closure.\n ctx.mode = mode\n ctx.termRows = termRows\n const tvState = _tvState ?? defaultTerminalVerifyState\n\n // After resetInlineState (e.g., useScrollback cleared and re-emitted frozen items),\n // treat the next render as a first render. The cursor is at a known position\n // (right after the re-emitted frozen items) and prev is stale.\n if (mode === \"inline\" && inlineState.forceFirstRender) {\n inlineState.forceFirstRender = false\n prev = null // may already be null (runtime.invalidate on resize), consume flag regardless\n }\n\n // First render: output entire buffer\n if (!prev) {\n // Inline mode resize optimization: if the runtime invalidated prevBuffer (resize)\n // but we have a stored buffer with matching dimensions, use incremental rendering\n // instead of clear+full render. This avoids wiping content when the buffer is unchanged\n // (e.g., content narrower than both old and new terminal widths).\n if (mode === \"inline\" && inlineState.prevBuffer && inlineState.prevCursorRow >= 0) {\n const stored = inlineState.prevBuffer\n if (stored.width === next.width && stored.height === next.height) {\n // Dimensions match — use incremental rendering (skip clear entirely)\n inlineState.prevBuffer = next\n return inlineIncrementalRender(inlineState, stored, next, scrollbackOffset, cursorPos, ctx, tvState)\n }\n }\n\n // Cap output to terminal height to prevent scroll desync.\n // In inline mode: prevents scrollback corruption (cursor-up clamped at row 0).\n // In fullscreen mode: prevents terminal scroll that desynchronizes prevBuffer\n // from actual terminal state, causing ghost pixels on subsequent incremental renders.\n const firstOutput = bufferToAnsi(next, ctx, termRows)\n // For inline first render, append cursor positioning and initialize tracking\n if (mode === \"inline\") {\n const firstContentLines = findLastContentLine(next) + 1\n const firstMaxOutput = termRows != null ? Math.min(firstContentLines, termRows) : firstContentLines\n let firstStartLine = 0\n if (termRows != null && firstContentLines > termRows) firstStartLine = firstContentLines - termRows\n\n // Resize: clear the entire visible screen and re-render.\n // Terminal reflow is unpredictable — lines wrap differently based on content,\n // unicode, wrap points. Rather than guessing cursor-up distances, overshoot:\n // ESC[nA is clamped at row 0 of the visible screen (can't touch scrollback),\n // so using termRows is safe. Frozen scrollback content above is preserved.\n let prefix = \"\"\n if (inlineState.prevCursorRow >= 0) {\n const clearDistance = termRows ?? Math.max(inlineState.prevCursorRow, inlineState.prevOutputLines - 1)\n if (clearDistance > 0) {\n prefix += `\\x1b[${clearDistance}A`\n }\n prefix += \"\\r\\x1b[J\" // column 0, clear from cursor to end of screen\n }\n\n inlineState.prevBuffer = next\n updateInlineCursorRow(inlineState, cursorPos, firstMaxOutput, firstStartLine)\n return prefix + firstOutput + inlineCursorSuffix(cursorPos ?? null, next, ctx)\n }\n if (isStrictAccumulate()) {\n accState.accumulatedAnsi = firstOutput\n accState.accumulateWidth = next.width\n accState.accumulateHeight = next.height\n accState.accumulateFrameCount = 0\n }\n if (tvState.backends.length > 0) {\n initTerminalVerifyState(tvState, next.width, next.height, firstOutput)\n }\n if (CAPTURE_RAW) {\n try {\n const fs = require(\"fs\")\n _captureRawFrameCount = 0\n // Write initial render with frame separator\n fs.writeFileSync(\"/tmp/silvery-raw.ansi\", firstOutput)\n fs.writeFileSync(\n \"/tmp/silvery-raw-frames.jsonl\",\n JSON.stringify({\n frame: 0,\n type: \"full\",\n bytes: firstOutput.length,\n width: next.width,\n height: next.height,\n }) + \"\\n\",\n )\n } catch {}\n }\n return firstOutput\n }\n\n // Inline mode: use incremental rendering when safe, fall back to full render.\n if (mode === \"inline\") {\n inlineState.prevBuffer = next\n return inlineIncrementalRender(inlineState, prev, next, scrollbackOffset, cursorPos, ctx, tvState)\n }\n\n // SILVERY_FULL_RENDER: bypass incremental diff, always render full buffer.\n // Use to diagnose garbled rendering — if FULL_RENDER fixes it, the bug\n // is in changesToAnsi (diff → ANSI serialization).\n if (FULL_RENDER) {\n return bufferToAnsi(next, ctx, termRows)\n }\n\n // Dimension change: fall back to full render.\n // When prev and next buffers have different dimensions (e.g., terminal resize\n // without prevBuffer invalidation, or test renderer dimension changes),\n // incremental diff produces CUP sequences targeting positions beyond the\n // current terminal bounds. Terminals clamp out-of-bounds CUP to the last\n // valid row/column, causing stale cells from the diff's shrink-region clears\n // to overwrite valid content at the terminal edge.\n if (prev.width !== next.width || prev.height !== next.height) {\n return bufferToAnsi(next, ctx, termRows)\n }\n\n // Fullscreen mode: diff and emit only changes\n const { pool, count: rawCount } = diffBuffers(prev, next)\n\n // Filter out changes beyond terminal height to prevent CUP targeting rows\n // past the terminal, which causes scrolling and prevBuffer desync.\n let count = rawCount\n if (termRows != null) {\n let writeIdx = 0\n for (let i = 0; i < rawCount; i++) {\n if (pool[i]!.y < termRows) {\n pool[writeIdx++] = pool[i]!\n }\n }\n count = writeIdx\n }\n\n if (DEBUG_OUTPUT) {\n // eslint-disable-next-line no-console\n console.error(\n `[SILVERY_DEBUG_OUTPUT] diffBuffers: ${count} changes${rawCount !== count ? ` (${rawCount - count} clamped beyond termRows)` : \"\"}`,\n )\n const debugLimit = Math.min(count, 10)\n for (let i = 0; i < debugLimit; i++) {\n const change = pool[i]!\n // eslint-disable-next-line no-console\n console.error(` (${change.x},${change.y}): \"${change.cell.char}\"`)\n }\n if (count > 10) {\n // eslint-disable-next-line no-console\n console.error(` ... and ${count - 10} more`)\n }\n }\n\n if (count === 0) {\n return \"\" // No changes\n }\n\n // Wide characters are handled atomically in changesToAnsi():\n // - Wide char main cells emit the character and advance cursor by 2\n // - Continuation cells are skipped (handled with their main cell)\n // - Orphaned continuation cells (main cell unchanged) trigger a\n // re-emit of the main cell from the buffer\n const incrOutput = changesToAnsi(pool, count, ctx, next).output\n\n // Log output sizes when debug or strict-accumulate is enabled\n if (DEBUG_OUTPUT || isStrictAccumulate()) {\n const bytes = Buffer.byteLength(incrOutput)\n try {\n const fs = require(\"fs\")\n fs.appendFileSync(\"/tmp/silvery-sizes.log\", `changesToAnsi: ${count} changes, ${bytes} bytes\\n`)\n } catch {}\n }\n\n // Debug capture: write both incremental and fresh ANSI to files for comparison.\n if (DEBUG_CAPTURE) {\n _debugFrameCount++\n try {\n const fs = require(\"fs\")\n const freshOutput = bufferToAnsi(next, ctx)\n const freshPrev = prev ? bufferToAnsi(prev, ctx) : \"\"\n // Replay incremental on top of fresh prev\n const w = Math.max(prev?.width ?? next.width, next.width)\n const h = Math.max(prev?.height ?? next.height, next.height)\n const screenIncr = replayAnsiWithStyles(w, h, freshPrev + incrOutput, ctx)\n const screenFresh = replayAnsiWithStyles(w, h, freshOutput, ctx)\n // Find first mismatch\n let mismatchInfo = \"\"\n for (let y = 0; y < h && !mismatchInfo; y++) {\n for (let x = 0; x < w && !mismatchInfo; x++) {\n const ic = screenIncr[y]?.[x]\n const fc = screenFresh[y]?.[x]\n if (ic && fc && (ic.char !== fc.char || !sgrColorEquals(ic.fg, fc.fg) || !sgrColorEquals(ic.bg, fc.bg))) {\n mismatchInfo = `MISMATCH at (${x},${y}): incr='${ic.char}' fresh='${fc.char}' incrFg=${formatColor(ic.fg)} freshFg=${formatColor(fc.fg)} incrBg=${formatColor(ic.bg)} freshBg=${formatColor(fc.bg)}`\n // Show row context\n const incrRow = screenIncr[y]!.map((c) => c.char).join(\"\")\n const freshRow = screenFresh[y]!.map((c) => c.char).join(\"\")\n mismatchInfo += `\\n incr row ${y}: ${incrRow.slice(Math.max(0, x - 20), x + 40)}\\n fresh row ${y}: ${freshRow.slice(Math.max(0, x - 20), x + 40)}`\n }\n }\n }\n const status = mismatchInfo || \"MATCH\"\n fs.appendFileSync(\"/tmp/silvery-capture.log\", `Frame ${_debugFrameCount}: ${count} changes, ${status}\\n`)\n if (mismatchInfo) {\n fs.writeFileSync(`/tmp/silvery-incr-${_debugFrameCount}.ansi`, freshPrev + incrOutput)\n fs.writeFileSync(`/tmp/silvery-fresh-${_debugFrameCount}.ansi`, freshOutput)\n fs.appendFileSync(\n \"/tmp/silvery-capture.log\",\n ` Saved ANSI files: /tmp/silvery-incr-${_debugFrameCount}.ansi and /tmp/silvery-fresh-${_debugFrameCount}.ansi\\n`,\n )\n }\n } catch (e) {\n try {\n require(\"fs\").appendFileSync(\"/tmp/silvery-capture.log\", `Frame ${_debugFrameCount}: ERROR ${e}\\n`)\n } catch {}\n }\n }\n\n // vt100 output verification: verify that the incremental ANSI output produces\n // the same visible terminal state as a fresh render. Uses the internal\n // replayAnsiWithStyles parser (stateless). Enabled by SILVERY_STRICT or\n // SILVERY_STRICT_TERMINAL containing \"vt100\".\n if (isStrictOutput() || tvState.hasVt100) {\n verifyOutputEquivalence(prev, next, incrOutput, ctx)\n }\n\n // SILVERY_STRICT_ACCUMULATE: verify that the accumulated output from ALL frames\n // produces the same terminal state as a fresh render of the current buffer.\n // Catches compounding errors that per-frame verification misses.\n if (isStrictAccumulate()) {\n accState.accumulatedAnsi += incrOutput\n accState.accumulateFrameCount++\n verifyAccumulatedOutput(next, ctx, accState)\n }\n\n // SILVERY_STRICT_TERMINAL (xterm/ghostty backends): verify via independent\n // terminal emulators that the cumulative incremental ANSI output produces the\n // same terminal state as a fresh full render. Unlike the vt100 backend (which\n // uses replayAnsiWithStyles — the same ANSI parser as the output generator),\n // these feed output through real terminal emulators, catching bugs where our\n // parser and generator agree but a real terminal disagrees (e.g., OSC 66, wide\n // char cursor drift, buffer overflow scrolling).\n if (tvState.backends.length > 0 && (tvState.terminal || tvState.ghosttyTerminal)) {\n tvState.frameCount++\n verifyTerminalEquivalence(tvState, incrOutput, next, ctx)\n }\n\n if (CAPTURE_RAW) {\n try {\n const fs = require(\"fs\")\n _captureRawFrameCount++\n // Append output to cumulative ANSI file\n fs.appendFileSync(\"/tmp/silvery-raw.ansi\", incrOutput)\n // Also save the fresh render of this frame for comparison\n const freshOutput = bufferToAnsi(next, ctx)\n fs.writeFileSync(`/tmp/silvery-raw-fresh-${_captureRawFrameCount}.ansi`, freshOutput)\n fs.appendFileSync(\n \"/tmp/silvery-raw-frames.jsonl\",\n JSON.stringify({\n frame: _captureRawFrameCount,\n type: \"incremental\",\n changes: count,\n bytes: incrOutput.length,\n width: next.width,\n height: next.height,\n }) + \"\\n\",\n )\n } catch {}\n }\n\n return incrOutput\n}\n\n/**\n * Check if a line has any non-space content or styling.\n * A row with only spaces but with background color or other styling\n * (bold, inverse, underline, etc.) is visually meaningful.\n */\nfunction lineHasContent(buffer: TerminalBuffer, y: number): boolean {\n for (let x = 0; x < buffer.width; x++) {\n const ch = buffer.getCellChar(x, y)\n if (ch !== \" \" && ch !== \"\") return true\n // Styled blank cells are visually meaningful:\n // - background color (colored spacer rows)\n // - inverse (visible block of fg color)\n // - underline (visible line under space)\n // - strikethrough (visible line through space)\n const bg = buffer.getCellBg(x, y)\n if (bg !== null) return true\n if (buffer.getCellAttrs(x, y) & VISIBLE_SPACE_ATTR_MASK) return true\n }\n return false\n}\n\n/**\n * Find the last line with content in the buffer.\n */\nfunction findLastContentLine(buffer: TerminalBuffer): number {\n for (let y = buffer.height - 1; y >= 0; y--) {\n if (lineHasContent(buffer, y)) {\n return y\n }\n }\n return 0 // At least render first line\n}\n\n/**\n * Compute the ANSI suffix that positions the real terminal cursor for inline mode.\n *\n * After inline rendering, the terminal cursor sits at the end of the last\n * content line. If a component used useCursor(), we move the cursor to\n * that position (relative to the rendered output). Otherwise we just show\n * the cursor at its current position.\n *\n * @param cursorPos The cursor state from useCursor() (or null if none)\n * @param buffer The rendered buffer\n * @param ctx Output context (termRows read from ctx.termRows)\n */\nfunction inlineCursorSuffix(\n cursorPos: CursorState | null | undefined,\n buffer: TerminalBuffer,\n ctx: OutputContext,\n): string {\n const { termRows } = ctx\n if (!cursorPos?.visible) {\n // No active cursor — hide it\n return \"\\x1b[?25l\"\n }\n\n // Determine the visible row range (same logic as bufferToAnsi for inline)\n const lastContentLine = findLastContentLine(buffer)\n const maxLine = lastContentLine\n let startLine = 0\n const maxOutputLines = termRows != null ? Math.min(lastContentLine + 1, termRows) : lastContentLine + 1\n if (termRows != null && maxLine >= termRows) {\n startLine = maxLine - termRows + 1\n }\n\n // Convert absolute buffer cursor position to visible row index\n const visibleRow = cursorPos.y - startLine\n if (visibleRow < 0 || visibleRow >= maxOutputLines) {\n // Cursor is outside the visible area (scrolled off) — hide it\n return \"\\x1b[?25l\"\n }\n\n // After rendering, the terminal cursor is at the end of the last output line.\n // The last output line is at visible row (maxOutputLines - 1).\n const currentRow = maxOutputLines - 1\n const rowDelta = currentRow - visibleRow\n\n let suffix = \"\"\n // Move up to the correct row\n if (rowDelta > 0) {\n suffix += `\\x1b[${rowDelta}A`\n }\n // Move to column 0, then right to the correct column\n suffix += \"\\r\"\n if (cursorPos.x > 0) {\n suffix += `\\x1b[${cursorPos.x}C`\n }\n // Show cursor\n suffix += \"\\x1b[?25h\"\n return suffix\n}\n\n/**\n * Incremental rendering for inline mode.\n *\n * When conditions are safe (no external writes, dimensions unchanged),\n * diffs prev/next buffers and emits only changed cells using relative\n * cursor positioning. Falls back to full render otherwise.\n *\n * This reduces output from ~5,848 bytes (full re-render at 50 items)\n * to ~50-100 bytes per keystroke, matching fullscreen efficiency.\n */\nfunction inlineIncrementalRender(\n state: InlineCursorState,\n prev: TerminalBuffer,\n next: TerminalBuffer,\n scrollbackOffset: number,\n cursorPos?: CursorState | null,\n ctx: OutputContext = defaultContext,\n tvState?: TerminalVerifyState,\n): string {\n const { termRows } = ctx\n // Guard: fall back to full render for complex cases\n if (scrollbackOffset > 0 || prev.width !== next.width || prev.height !== next.height || state.prevCursorRow < 0) {\n return inlineFullRender(state, prev, next, scrollbackOffset, cursorPos, ctx)\n }\n\n const nextContentLines = findLastContentLine(next) + 1\n const prevContentLines = findLastContentLine(prev) + 1\n\n // Compute visible ranges for both prev and next content\n const prevMaxOutputLines = termRows != null ? Math.min(prevContentLines, termRows) : prevContentLines\n const maxOutputLines = termRows != null ? Math.min(nextContentLines, termRows) : nextContentLines\n let prevStartLine = 0\n if (termRows != null && prevContentLines > termRows) {\n prevStartLine = prevContentLines - termRows\n }\n let startLine = 0\n if (termRows != null && nextContentLines > termRows) {\n startLine = nextContentLines - termRows\n }\n\n // When the visible window shifts (content exceeds termRows and startLine changes),\n // the entire visible region is different — fall back to full render.\n if (startLine !== prevStartLine) {\n return inlineFullRender(state, prev, next, scrollbackOffset, cursorPos, ctx)\n }\n\n // Diff buffers\n const { pool, count } = diffBuffers(prev, next)\n if (count === 0 && nextContentLines === prevContentLines) {\n // No buffer changes, but cursor position may have changed.\n // Emit cursor suffix to update the terminal cursor.\n const suffix = inlineCursorSuffix(cursorPos ?? null, next, ctx)\n updateInlineCursorRow(state, cursorPos, maxOutputLines, startLine)\n return suffix\n }\n\n // Move cursor from tracked row to row 0 of render region\n let output = \"\"\n if (state.prevCursorRow > 0) {\n output += `\\x1b[${state.prevCursorRow}A`\n }\n output += \"\\r\"\n output += \"\\x1b[?25l\" // hide cursor during update\n\n // Emit changes with relative positioning.\n // Use the larger of prev/next output lines so changesToAnsi processes all\n // visible cells including rows that shrank (need clearing) or grew (need writing).\n const effectiveOutputLines = Math.max(prevMaxOutputLines, maxOutputLines)\n const changes = changesToAnsi(pool, count, ctx, next, startLine, effectiveOutputLines)\n output += changes.output\n\n // After changesToAnsi, cursor is at changes.finalY (render-relative).\n // We need to position cursor at the effective bottom row, then handle\n // growth/shrinkage, and end at (maxOutputLines - 1) for inlineCursorSuffix.\n const finalY = changes.finalY\n const prevBottomRow = prevMaxOutputLines - 1\n const bottomRow = maxOutputLines - 1\n\n if (maxOutputLines > prevMaxOutputLines) {\n // Content grew: rows beyond the previous bottom don't exist on the terminal.\n // CUD (\\x1b[nB) is clamped at the terminal edge and won't create new lines.\n // Use \\r\\n for each new row to ensure the terminal extends naturally.\n //\n // changesToAnsi may have already moved past prevBottomRow using \\r\\n\n // (creating new terminal lines in the process). Only add \\r\\n for\n // rows beyond what changesToAnsi already reached.\n const fromRow = finalY >= 0 ? finalY : 0\n if (fromRow >= bottomRow) {\n // changesToAnsi already reached or passed the new bottom — nothing to do\n } else if (fromRow >= prevBottomRow) {\n // Cursor already past old bottom (changesToAnsi extended the terminal).\n // Only need \\r\\n for remaining rows.\n const remainingRows = bottomRow - fromRow\n for (let i = 0; i < remainingRows; i++) {\n output += \"\\r\\n\"\n }\n } else {\n // Cursor is still within the old content area.\n // First, move to the old bottom row using CUD (safe, rows exist on terminal)\n if (fromRow < prevBottomRow) {\n const dy = prevBottomRow - fromRow\n output += dy === 1 ? \"\\r\\n\" : `\\r\\x1b[${dy}B`\n }\n // Then extend to new bottom with \\r\\n for each new row\n const newRows = bottomRow - prevBottomRow\n for (let i = 0; i < newRows; i++) {\n output += \"\\r\\n\"\n }\n }\n } else if (maxOutputLines < prevMaxOutputLines) {\n // Content shrank: erase orphan lines below the new content.\n // changesToAnsi already wrote empty cells to clear the old content,\n // but we need \\x1b[K to clear any residual characters.\n // The cursor is at finalY after changesToAnsi — move to the new bottom first.\n const fromRow = finalY >= 0 ? finalY : 0\n if (fromRow < bottomRow) {\n const dy = bottomRow - fromRow\n output += dy === 1 ? \"\\r\\n\" : `\\r\\x1b[${dy}B`\n } else if (fromRow > bottomRow) {\n // Cursor is past the new bottom (was erasing orphan rows) — move back up\n output += `\\x1b[${fromRow - bottomRow}A`\n }\n // Now at new bottom row (bottomRow). Erase orphan lines below by moving\n // down one line at a time, erasing each. This leaves cursor at the last\n // orphan row (prevMaxOutputLines - 1).\n const orphanCount = prevMaxOutputLines - maxOutputLines\n for (let y = 0; y < orphanCount; y++) {\n output += \"\\n\\r\\x1b[K\"\n }\n // Move back up from last orphan row to new bottom row\n if (orphanCount > 0) output += `\\x1b[${orphanCount}A`\n } else {\n // Same height: move to bottom row if not already there\n if (finalY >= 0 && finalY < bottomRow) {\n const dy = bottomRow - finalY\n output += dy === 1 ? \"\\r\\n\" : `\\r\\x1b[${dy}B`\n }\n }\n\n output += inlineCursorSuffix(cursorPos ?? null, next, ctx)\n\n // STRICT verification for inline incremental renders.\n // Inline mode uses relative cursor positioning (CUU/CUD/CR) instead of\n // absolute CUP, so we can't pass the inline output directly to\n // verifyOutputEquivalence (which replays freshPrev + incrOutput — the cursor\n // position after freshPrev differs from the tracked prevCursorRow). Instead,\n // re-diff in fullscreen mode for vt100 verification. This verifies the buffer\n // diff logic produces correct ANSI output, even though it doesn't test the\n // inline cursor positioning specifically.\n if (isStrictOutput() || tvState?.hasVt100) {\n const savedMode = ctx.mode\n ctx.mode = \"fullscreen\"\n const fsIncrOutput = changesToAnsi(pool, count, ctx, next).output\n verifyOutputEquivalence(prev, next, fsIncrOutput, ctx)\n ctx.mode = savedMode\n }\n // TODO: verifyTerminalEquivalence (xterm/ghostty) is skipped for inline mode.\n // The persistent terminal emulators track cumulative state across frames, but\n // inline mode's cursor management (relative positioning, scrollback promotion,\n // cursor tracking via prevCursorRow) is incompatible with this model.\n // verifyAccumulatedOutput is also skipped — inline has a different accumulation\n // model (scrollback promotion, frozen content).\n\n // Update tracking\n updateInlineCursorRow(state, cursorPos, maxOutputLines, startLine)\n\n return output\n}\n\n/**\n * Full re-render for inline mode.\n *\n * Moves cursor to the start of the render region, writes the entire\n * buffer fresh, and erases any leftover lines from the previous render.\n *\n * When content exceeds terminal height, output is capped to termRows lines.\n * Lines beyond the terminal can't be managed (cursor-up is clamped at row 0),\n * so we truncate to prevent scrollback corruption.\n */\nfunction inlineFullRender(\n state: InlineCursorState,\n prev: TerminalBuffer,\n next: TerminalBuffer,\n scrollbackOffset: number,\n cursorPos?: CursorState | null,\n ctx: OutputContext = defaultContext,\n): string {\n const { termRows } = ctx\n const nextContentLines = findLastContentLine(next) + 1\n\n // Use tracked state from the previous frame for cursor position and output height.\n // state.prevCursorRow tracks where the terminal cursor actually is (row within render\n // region), and state.prevOutputLines tracks how many lines the previous frame rendered.\n // Re-deriving from the prev buffer can disagree (e.g., when cursor was positioned at a\n // visible row via useCursor, not at the bottom), causing the cursor-up to overshoot\n // and leaving orphan lines below the content (\"inline-bleed\").\n // Fall back to prev-buffer derivation only when cursor tracking is uninitialized.\n let prevOutputLines: number\n let cursorRowInRegion: number\n if (state.prevCursorRow >= 0) {\n prevOutputLines = state.prevOutputLines\n cursorRowInRegion = state.prevCursorRow\n } else {\n const prevContentLines = findLastContentLine(prev) + 1\n prevOutputLines = termRows != null ? Math.min(prevContentLines, termRows) : prevContentLines\n cursorRowInRegion = prevOutputLines - 1\n }\n\n // How far the cursor is below the start of the render region:\n // tracked cursor row + any lines written to stdout between renders.\n // Cap to termRows-1: terminal clamps cursor-up at row 0.\n const rawCursorOffset = cursorRowInRegion + scrollbackOffset\n const cursorOffset = termRows != null && !isStrictOutput() ? Math.min(rawCursorOffset, termRows - 1) : rawCursorOffset\n\n // Cap output at terminal height to prevent scrollback corruption.\n // Content taller than the terminal pushes lines into scrollback where\n // they can never be overwritten (cursor-up is clamped at terminal row 0).\n const maxOutputLines = termRows != null ? Math.min(nextContentLines, termRows) : nextContentLines\n\n // Quick check: if nothing changed and no scrollback displacement, skip\n if (scrollbackOffset === 0) {\n const { count } = diffBuffers(prev, next)\n if (count === 0) return \"\"\n }\n\n // Move cursor up to the start of the render region\n let prefix = \"\"\n if (cursorOffset > 0) {\n prefix = `\\x1b[${cursorOffset}A\\r`\n }\n // bufferToAnsi handles: hide cursor, render content lines with\n // \\x1b[K (clear to EOL) on each line, and reset style at end.\n let output = prefix + bufferToAnsi(next, ctx, maxOutputLines)\n\n // Erase leftover lines if visible area shrank.\n // Account for terminal scroll: when useScrollback writes frozen items and the\n // cursor overflows the terminal, the terminal scrolls up. The scroll amount\n // equals how far rawCursorOffset exceeds termRows-1 (the terminal's last row).\n // That many old render lines were pushed into scrollback and no longer need\n // erasing. The remaining visible old render lines end at lastOccupiedLine.\n // Lines beyond that contain frozen items — those must NOT be erased.\n // Note: computed from terminal geometry, independent of strict-output cursor bypass.\n const terminalScroll = termRows != null ? Math.max(0, rawCursorOffset - (termRows - 1)) : 0\n const lastOccupiedLine = Math.max(prevOutputLines - 1 - terminalScroll, 0)\n const nextLastLine = maxOutputLines - 1\n if (lastOccupiedLine > nextLastLine) {\n for (let y = nextLastLine + 1; y <= lastOccupiedLine; y++) {\n output += \"\\n\\r\\x1b[K\"\n }\n const up = lastOccupiedLine - nextLastLine\n if (up > 0) output += `\\x1b[${up}A`\n }\n\n // Position the real terminal cursor and show it.\n // If a component called useCursor(), place the cursor there.\n // Otherwise, just show it at the current position (end of content).\n output += inlineCursorSuffix(cursorPos ?? null, next, ctx)\n\n // Update cursor tracking for incremental rendering on next frame\n let startLine = 0\n if (termRows != null && nextContentLines > termRows) startLine = nextContentLines - termRows\n updateInlineCursorRow(state, cursorPos, maxOutputLines, startLine)\n\n return output\n}\n\n/**\n * Convert entire buffer to ANSI string.\n *\n * Mode is read from ctx.mode. maxRows is an explicit parameter because callers\n * sometimes pass a pre-computed value (e.g., maxOutputLines) rather than raw\n * ctx.termRows.\n *\n * @param maxRows Optional cap on number of rows to output (inline mode).\n * When content exceeds terminal height, this prevents scrollback corruption.\n */\nfunction bufferToAnsi(buffer: TerminalBuffer, ctx: OutputContext = defaultContext, maxRows?: number): string {\n const { mode } = ctx\n let output = \"\"\n let currentStyle: Style | null = null\n let currentHyperlink: string | undefined\n\n // Cap output to prevent rendering beyond terminal height.\n // Inline mode: render up to last content line; if taller than terminal, show bottom\n // (footer and latest content stay visible in scrollback).\n // Fullscreen mode: always start from top; cap at terminal height to prevent scroll\n // that desynchronizes prevBuffer from actual terminal state.\n let maxLine = mode === \"inline\" ? findLastContentLine(buffer) : buffer.height - 1\n let startLine = 0\n if (maxRows != null && maxLine >= maxRows) {\n if (mode === \"fullscreen\") {\n maxLine = maxRows - 1 // cap at terminal height, always from top\n } else {\n startLine = maxLine - maxRows + 1 // show bottom of content\n }\n }\n\n // Move cursor to start position based on mode\n if (mode === \"fullscreen\") {\n // Fullscreen: Move cursor to home position (top-left)\n output += \"\\x1b[H\"\n } else {\n // Inline: Hide cursor, start from current position\n output += \"\\x1b[?25l\"\n }\n\n // Reusable objects to avoid per-cell allocation in the inner loop\n const cell = createMutableCell()\n const cellStyle: Style = {\n fg: null,\n bg: null,\n underlineColor: null,\n attrs: {},\n }\n\n for (let y = startLine; y <= maxLine; y++) {\n // Move to start of line.\n // Fullscreen: CUP (absolute positioning) for rows after startLine.\n // After writing exactly `cols` chars, cursor enters pending-wrap state.\n // \\r goes to col 0 of the CURRENT row (doesn't resolve wrap in real\n // terminals), overwriting the row. CUP is unambiguous across all\n // terminals and our internal ANSI parser (replayAnsiWithStyles).\n // First row skips CUP — cursor is already at home via \\x1b[H.\n // Inline: \\r on EVERY row (including first) to ensure column 0.\n if (mode === \"inline\") {\n output += \"\\r\"\n } else if (y > startLine) {\n output += `\\x1b[${y + 1};1H`\n }\n\n // Render the line content\n for (let x = 0; x < buffer.width; x++) {\n buffer.readCellInto(x, y, cell)\n\n // No continuation skip here. Valid continuation cells are never reached\n // because `if (cell.wide) x++` below jumps past them. Orphaned\n // continuation cells (wide char overwritten by region clear) must NOT\n // be skipped — they write a space to keep the VT cursor in sync.\n\n // Handle OSC 8 hyperlink transitions (separate from SGR style)\n const cellHyperlink = cell.hyperlink\n if (cellHyperlink !== currentHyperlink) {\n if (currentHyperlink) {\n output += \"\\x1b]8;;\\x1b\\\\\" // Close previous hyperlink\n }\n if (cellHyperlink) {\n output += `\\x1b]8;;${cellHyperlink}\\x1b\\\\` // Open new hyperlink\n }\n currentHyperlink = cellHyperlink\n }\n\n // Build style from cell and check if changed.\n // readCellInto mutates cell.attrs in place, so we must snapshot attrs\n // only when the style actually changes (which is rare -- most adjacent\n // cells share the same style). This avoids per-cell object allocation.\n cellStyle.fg = cell.fg\n cellStyle.bg = cell.bg\n cellStyle.underlineColor = cell.underlineColor\n cellStyle.attrs = cell.attrs\n if (!styleEquals(currentStyle, cellStyle)) {\n // Snapshot: copy attrs so currentStyle isn't invalidated by next readCellInto\n const saved: Style = {\n fg: cell.fg,\n bg: cell.bg,\n underlineColor: cell.underlineColor,\n attrs: { ...cell.attrs },\n }\n output += styleTransition(currentStyle, saved, ctx)\n currentStyle = saved\n }\n\n // Write character — empty-string chars are treated as space to ensure\n // the terminal cursor advances (an empty string writes nothing, causing\n // all subsequent characters on the row to shift left by one column).\n const char = cell.char || \" \"\n output += wrapTextSizing(char, cell.wide, ctx)\n\n // Wide characters occupy 2 columns in the terminal. Skip the next cell\n // position since the terminal cursor already advanced by 2. We skip\n // unconditionally (not relying on the next cell's continuation flag)\n // because the buffer may have a corrupted continuation cell — e.g., when\n // an adjacent container's region clear overwrites the continuation.\n // Without this, the non-continuation cell at x+1 would also be written,\n // causing every subsequent character on the row to shift right by 1.\n if (cell.wide) {\n x++\n // Cursor re-sync: some terminals treat multi-codepoint wide chars\n // (flag emoji like 🇨🇦) as two width-1 chars instead of one width-2\n // char. This causes the terminal cursor to be 1 column behind where\n // we expect. Emit an explicit cursor position to re-sync, mirroring\n // the re-sync in changesToAnsi. Cost: ~8 bytes per wide char; wide\n // chars are rare so overhead is negligible.\n // After x++, x points to the continuation cell. The next character\n // to write is at x+1 (after the loop increment), so position the\n // cursor at 0-indexed column x+1 = 1-indexed column x+2.\n if (mode === \"fullscreen\") {\n output += `\\x1b[${y - startLine + 1};${x + 2}H`\n } else {\n // Inline: \\r resets to column 0, CUF moves to expected position.\n // Reset bg first to prevent bleed across traversed cells.\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n const nextCol = x + 1\n output += \"\\r\"\n if (nextCol > 0) output += nextCol === 1 ? \"\\x1b[C\" : `\\x1b[${nextCol}C`\n }\n }\n }\n\n // Close any open hyperlink at end of row\n if (currentHyperlink) {\n output += \"\\x1b]8;;\\x1b\\\\\"\n currentHyperlink = undefined\n }\n\n // Reset style before clear-to-end and newline to prevent background\n // color from filling the right margin or bleeding into the next line.\n // \\x1b[K] uses current SGR attributes for the erased area.\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n // Clear to end of line (removes any leftover content from previous render)\n output += \"\\x1b[K\"\n\n // Move to next line (except for last line)\n if (y < maxLine) {\n if (mode === \"inline\") {\n // In inline mode, use \\r\\n to cancel DECAWM pending-wrap state.\n // When the line fills exactly `cols` characters, the cursor enters\n // pending-wrap. A bare \\n in that state causes a double line advance\n // in some terminals (Ghostty, iTerm2). The \\r first moves to column 0\n // (canceling pending-wrap), then \\n advances one row cleanly.\n output += \"\\r\\n\"\n }\n // Fullscreen: no \\n needed — CUP at the start of the next row handles\n // positioning. Bare \\n in pending-wrap state is terminal-dependent\n // (some terminals double-advance, potentially causing scroll at the\n // bottom of the alternate screen).\n }\n }\n\n // Close any open hyperlink at end\n if (currentHyperlink) {\n output += \"\\x1b]8;;\\x1b\\\\\"\n }\n\n // Reset style at end\n output += \"\\x1b[0m\"\n\n return output\n}\n\n// ============================================================================\n// Buffer diffing (extracted to diff-buffers.ts)\n// ============================================================================\n\nimport { diffBuffers } from \"./diff-buffers\"\n\n/** Result from changesToAnsi: ANSI output string and final cursor position. */\ninterface ChangesResult {\n output: string\n /** Final render-relative cursor Y after emitting changes (-1 if no changes emitted). */\n finalY: number\n}\n\n/** Pre-allocated style object reused across changesToAnsi calls. */\nconst reusableCellStyle: Style = {\n fg: null,\n bg: null,\n underlineColor: null,\n attrs: {},\n}\n\n/**\n * Pre-allocated cell for looking up wide char main cells from the buffer\n * when an orphaned continuation cell is encountered in changesToAnsi.\n */\nconst wideCharLookupCell = createMutableCell()\n\n/**\n * Sort a sub-range of the pool by position for optimal cursor movement.\n * Uses a simple in-place sort on pool[0..count).\n */\nfunction sortPoolByPosition(pool: CellChange[], count: number): void {\n // Insertion sort is efficient for the typical case (mostly sorted or small count)\n for (let i = 1; i < count; i++) {\n const item = pool[i]!\n const iy = item.y\n const ix = item.x\n let j = i - 1\n while (j >= 0 && (pool[j]!.y > iy || (pool[j]!.y === iy && pool[j]!.x > ix))) {\n pool[j + 1] = pool[j]!\n j--\n }\n pool[j + 1] = item\n }\n}\n\n/**\n * Convert cell changes to optimized ANSI output.\n *\n * Wide characters are handled atomically: the main cell (wide:true) and its\n * continuation cell are treated as a single unit. When the main cell is in\n * the pool, it's emitted and the cursor advances by 2. When only the\n * continuation cell changed (e.g., bg color), the main cell is read from\n * the buffer and emitted to cover both columns.\n *\n * @param pool Pre-allocated pool of CellChange objects\n * @param count Number of valid entries in the pool\n * @param ctx Output context (mode read from ctx.mode)\n * @param buffer The current buffer, used to look up main cells for orphaned\n * continuation cells (optional for backward compatibility)\n * @param startLine For inline mode: first visible buffer row (for termRows capping)\n * @param maxOutputLines For inline mode: number of visible rows\n */\nfunction changesToAnsi(\n pool: CellChange[],\n count: number,\n ctx: OutputContext = defaultContext,\n buffer?: TerminalBuffer,\n startLine = 0,\n maxOutputLines = Infinity,\n): ChangesResult {\n const { mode } = ctx\n if (count === 0) return { output: \"\", finalY: -1 }\n\n // Sort by position for optimal cursor movement (in-place, no allocation)\n sortPoolByPosition(pool, count)\n\n let output = \"\"\n let currentStyle: Style | null = null\n let currentHyperlink: string | undefined\n const isInline = mode === \"inline\"\n const endLine = startLine + maxOutputLines // exclusive upper bound for inline filtering\n let finalY = -1\n let cursorX = -1\n let cursorY = -1\n let prevY = -1\n // Track the last emitted cell position to detect when a continuation\n // cell's main cell was already emitted in this pass.\n let lastEmittedX = -1\n let lastEmittedY = -1\n\n for (let i = 0; i < count; i++) {\n const change = pool[i]!\n let x = change.x\n const y = change.y\n let cell = change.cell\n\n // In inline mode, skip changes outside the visible range\n if (isInline && (y < startLine || y >= endLine)) continue\n\n // Handle continuation cells: these are the second column of a wide\n // character. If their main cell (x-1) was already emitted in this\n // pass, skip. Otherwise, look up and emit the main cell from the\n // buffer so the wide char covers both columns.\n if (cell.continuation) {\n // Main cell was already emitted — skip\n if (lastEmittedX === x - 1 && lastEmittedY === y) continue\n\n // Orphaned continuation cell: main cell didn't change but this\n // cell's style did. Read the main cell from the buffer and emit it.\n if (buffer && x > 0) {\n x = x - 1\n buffer.readCellInto(x, y, wideCharLookupCell)\n cell = wideCharLookupCell\n // If the looked-up cell is itself a continuation (shouldn't happen\n // with valid buffers) or not wide, fall back to skipping\n if (cell.continuation || !cell.wide) continue\n } else {\n continue\n }\n }\n\n // For inline mode, use render-region-relative row indices\n const renderY = isInline ? y - startLine : y\n\n // Close hyperlink on row change (hyperlinks must not span across rows)\n if (y !== prevY && currentHyperlink) {\n output += \"\\x1b]8;;\\x1b\\\\\"\n currentHyperlink = undefined\n }\n prevY = y\n\n // Move cursor if needed (cursor must be exactly at target position)\n if (renderY !== cursorY || x !== cursorX) {\n // Use \\r\\n optimization only if cursor is initialized AND we're moving\n // to the next line at column 0. Don't use it when cursorY is -1\n // (uninitialized) because that would incorrectly emit a newline at start.\n // Bug km-x7ih: This was causing the first row to appear at the bottom.\n if (cursorY >= 0 && renderY === cursorY + 1 && x === 0) {\n // Next line at column 0, use newline (more efficient)\n // Reset style before newline to prevent background color bleeding\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n output += \"\\r\\n\"\n } else if (cursorY >= 0 && renderY === cursorY && x > cursorX) {\n // Same row, forward: use CUF (Cursor Forward) for small jumps.\n // Reset bg before CUF to prevent background color bleeding into\n // skipped cells. Some terminals (e.g., Ghostty) may apply the\n // current bg to cells traversed by CUF, causing visual artifacts\n // when bg transitions from undefined→color (km-tui.col-header-dup).\n if (currentStyle && currentStyle.bg !== null) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n const dx = x - cursorX\n output += dx === 1 ? \"\\x1b[C\" : `\\x1b[${dx}C`\n } else if (cursorY >= 0 && renderY > cursorY && x === 0) {\n // Same column (0), down N rows: use \\r + CUD\n const dy = renderY - cursorY\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n output += dy === 1 ? \"\\r\\n\" : `\\r\\x1b[${dy}B`\n } else if (isInline) {\n // Inline mode: relative positioning (no absolute row numbers)\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n // When cursorY === -1 (first change in incremental render),\n // the cursor is at row 0 (set by inlineIncrementalRender prefix).\n const fromRow = cursorY >= 0 ? cursorY : 0\n if (renderY > fromRow) {\n output += `\\x1b[${renderY - fromRow}B\\r`\n } else if (renderY < fromRow) {\n output += `\\x1b[${fromRow - renderY}A\\r`\n } else {\n output += \"\\r\"\n }\n if (x > 0) output += x === 1 ? \"\\x1b[C\" : `\\x1b[${x}C`\n } else {\n // Fullscreen: absolute position (1-indexed)\n output += `\\x1b[${renderY + 1};${x + 1}H`\n }\n }\n\n // Handle OSC 8 hyperlink transitions (separate from SGR style)\n const cellHyperlink = cell.hyperlink\n if (cellHyperlink !== currentHyperlink) {\n if (currentHyperlink) {\n output += \"\\x1b]8;;\\x1b\\\\\" // Close previous hyperlink\n }\n if (cellHyperlink) {\n output += `\\x1b]8;;${cellHyperlink}\\x1b\\\\` // Open new hyperlink\n }\n currentHyperlink = cellHyperlink\n }\n\n // Update style if changed (reuse pre-allocated style object)\n reusableCellStyle.fg = cell.fg\n reusableCellStyle.bg = cell.bg\n reusableCellStyle.underlineColor = cell.underlineColor\n reusableCellStyle.attrs = cell.attrs\n if (!styleEquals(currentStyle, reusableCellStyle)) {\n // Snapshot: copy attrs so currentStyle isn't invalidated by next iteration\n const prevStyle = currentStyle\n currentStyle = {\n fg: cell.fg,\n bg: cell.bg,\n underlineColor: cell.underlineColor,\n attrs: { ...cell.attrs },\n }\n output += styleTransition(prevStyle, currentStyle, ctx)\n }\n\n // Write character — empty-string chars are treated as space to ensure\n // the terminal cursor advances and cursorX tracking stays correct.\n const char = cell.char || \" \"\n output += wrapTextSizing(char, cell.wide, ctx)\n cursorX = x + (cell.wide ? 2 : 1)\n cursorY = renderY\n lastEmittedX = x\n lastEmittedY = y\n\n // Wide char cursor re-sync: terminals may advance the cursor by 1\n // instead of 2 for certain emoji (flag sequences, text-presentation\n // emoji without OSC 66 support). In bufferToAnsi (full render) this\n // only causes a consistent per-row shift since every cell is written\n // sequentially. But in changesToAnsi, contiguous runs rely on cursor\n // auto-advance, so a width mismatch causes progressive drift —\n // characters appear at wrong positions, mixing old and new content.\n // Fix: emit an explicit cursor position after each wide char to\n // re-sync the terminal cursor with our tracking. Cost: ~8 bytes per\n // wide char (CUP in fullscreen, \\r+CUF in inline). Wide chars are\n // rare so the overhead is negligible.\n if (cell.wide) {\n if (isInline) {\n // Inline: \\r resets to column 0, CUF moves to expected position.\n // Reset bg first to prevent bleed across traversed cells.\n if (currentStyle && currentStyle.bg !== null) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n output += \"\\r\"\n if (cursorX > 0) output += cursorX === 1 ? \"\\x1b[C\" : `\\x1b[${cursorX}C`\n } else {\n // Fullscreen: CUP (absolute position) — no style reset needed.\n output += `\\x1b[${cursorY + 1};${cursorX + 1}H`\n }\n }\n }\n\n finalY = cursorY\n\n // Close any open hyperlink\n if (currentHyperlink) {\n output += \"\\x1b]8;;\\x1b\\\\\"\n }\n\n // Reset style at end\n if (currentStyle) {\n output += \"\\x1b[0m\"\n }\n\n return { output, finalY }\n}\n\n// =============================================================================\n// Color code helpers (imported from ansi/sgr-codes.ts)\n// =============================================================================\n\n/**\n * Convert style to ANSI escape sequence (chalk-compatible format).\n *\n * Emits only non-default attributes with no reset prefix. Called when there\n * is no previous style context (first cell or after all attributes are off),\n * so the terminal is already in reset state.\n *\n * Uses native 4-bit codes for basic colors (0-7), 256-color for extended,\n * and true-color for RGB. Each attribute gets its own \\x1b[Xm sequence to\n * match chalk's output format.\n *\n * Emits SGR codes including:\n * - Basic colors (30-37, 40-47)\n * - 256-color (38;5;N, 48;5;N)\n * - True color (38;2;r;g;b, 48;2;r;g;b)\n * - Underline styles (4:x where x = 0-5)\n * - Underline color (58;5;N or 58;2;r;g;b)\n * - Inverse uses SGR 7 so terminals swap fg/bg correctly (including default colors)\n */\nfunction styleToAnsi(style: Style, ctx: OutputContext = defaultContext): string {\n const fg = style.fg\n const bg = style.bg\n\n // Build individual escape sequences (chalk-compatible: one \\x1b[Xm per attribute)\n let result = \"\"\n\n // Foreground color\n if (fg !== null) {\n result += `\\x1b[${fgColorCode(fg)}m`\n }\n\n // Background color (DEFAULT_BG sentinel = terminal default, skip)\n if (bg !== null && !isDefaultBg(bg)) {\n result += `\\x1b[${bgColorCode(bg)}m`\n }\n\n // Attributes\n if (style.attrs.bold) result += \"\\x1b[1m\"\n if (style.attrs.dim) result += \"\\x1b[2m\"\n if (style.attrs.italic) result += \"\\x1b[3m\"\n\n // Underline: use SGR 4:x if style specified, otherwise simple SGR 4\n if (!ctx.caps.underlineStyles) {\n // Terminal doesn't support SGR 4:x — use simple SGR 4\n if (style.attrs.underline || style.attrs.underlineStyle) result += \"\\x1b[4m\"\n } else {\n const underlineStyle = style.attrs.underlineStyle\n const sgrSubparam = underlineStyleToSgr(underlineStyle)\n if (sgrSubparam !== null && sgrSubparam !== 0) {\n result += `\\x1b[4:${sgrSubparam}m`\n } else if (style.attrs.underline) {\n result += \"\\x1b[4m\"\n }\n }\n\n // Use SGR 7 for inverse — lets the terminal correctly swap fg/bg\n // (including default terminal colors that have no explicit ANSI code)\n if (style.attrs.blink) result += \"\\x1b[5m\"\n if (style.attrs.inverse) result += \"\\x1b[7m\"\n if (style.attrs.hidden) result += \"\\x1b[8m\"\n if (style.attrs.strikethrough) result += \"\\x1b[9m\"\n\n // Append underline color if specified (SGR 58) — skip for limited terminals\n if (ctx.caps.underlineColor && style.underlineColor !== null && style.underlineColor !== undefined) {\n if (typeof style.underlineColor === \"number\") {\n result += `\\x1b[58;5;${style.underlineColor}m`\n } else {\n result += `\\x1b[58;2;${style.underlineColor.r};${style.underlineColor.g};${style.underlineColor.b}m`\n }\n }\n\n return result\n}\n\n// =============================================================================\n// ANSI output verification via virtual terminal replay (vt100 backend)\n// =============================================================================\n\n// ============================================================================\n// Style-Aware ANSI Replay\n// ============================================================================\n\n/** SGR state tracked during ANSI replay. */\ninterface SgrState {\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/** A cell in the style-aware virtual terminal. */\ninterface StyledCell {\n char: string\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\nfunction createDefaultSgr(): SgrState {\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\nfunction createDefaultStyledCell(): StyledCell {\n return {\n char: \" \",\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 * Apply SGR parameters to the current state.\n * Handles all SGR codes used by styleTransition().\n */\nfunction applySgrParams(params: string, sgr: SgrState): void {\n if (params === \"\" || params === \"0\") {\n // Reset\n sgr.fg = null\n sgr.bg = null\n sgr.bold = false\n sgr.dim = false\n sgr.italic = false\n sgr.underline = false\n sgr.blink = false\n sgr.inverse = false\n sgr.hidden = false\n sgr.strikethrough = false\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 = parseInt(code.substring(0, colonIdx))\n if (mainCode === 4) {\n // Underline style subparameter\n const sub = parseInt(code.substring(colonIdx + 1))\n sgr.underline = sub > 0\n }\n i++\n continue\n }\n\n const n = parseInt(code)\n if (n === 0) {\n sgr.fg = null\n sgr.bg = null\n sgr.bold = false\n sgr.dim = false\n sgr.italic = false\n sgr.underline = false\n sgr.blink = false\n sgr.inverse = false\n sgr.hidden = false\n sgr.strikethrough = false\n } else if (n === 1) {\n sgr.bold = true\n } else if (n === 2) {\n sgr.dim = true\n } else if (n === 3) {\n sgr.italic = true\n } else if (n === 4) {\n sgr.underline = true\n } else if (n === 5 || n === 6) {\n sgr.blink = true\n } else if (n === 7) {\n sgr.inverse = true\n } else if (n === 8) {\n sgr.hidden = true\n } else if (n === 9) {\n sgr.strikethrough = true\n } else if (n === 22) {\n sgr.bold = false\n sgr.dim = false\n } else if (n === 23) {\n sgr.italic = false\n } else if (n === 24) {\n sgr.underline = false\n } else if (n === 25) {\n sgr.blink = false\n } else if (n === 27) {\n sgr.inverse = false\n } else if (n === 28) {\n sgr.hidden = false\n } else if (n === 29) {\n sgr.strikethrough = false\n } else if (n >= 30 && n <= 37) {\n sgr.fg = n - 30\n } else if (n === 38) {\n // Extended fg color\n if (i + 1 < parts.length && parts[i + 1] === \"5\" && i + 2 < parts.length) {\n sgr.fg = parseInt(parts[i + 2]!)\n i += 2\n } else if (i + 1 < parts.length && parts[i + 1] === \"2\" && i + 4 < parts.length) {\n sgr.fg = {\n r: parseInt(parts[i + 2]!),\n g: parseInt(parts[i + 3]!),\n b: parseInt(parts[i + 4]!),\n }\n i += 4\n }\n } else if (n === 39) {\n sgr.fg = null\n } else if (n >= 40 && n <= 47) {\n sgr.bg = n - 40\n } else if (n === 48) {\n // Extended bg color\n if (i + 1 < parts.length && parts[i + 1] === \"5\" && i + 2 < parts.length) {\n sgr.bg = parseInt(parts[i + 2]!)\n i += 2\n } else if (i + 1 < parts.length && parts[i + 1] === \"2\" && i + 4 < parts.length) {\n sgr.bg = {\n r: parseInt(parts[i + 2]!),\n g: parseInt(parts[i + 3]!),\n b: parseInt(parts[i + 4]!),\n }\n i += 4\n }\n } else if (n === 49) {\n sgr.bg = null\n } else if (n >= 90 && n <= 97) {\n sgr.fg = n - 90 + 8 // bright colors: 8-15\n } else if (n >= 100 && n <= 107) {\n sgr.bg = n - 100 + 8\n }\n // 58/59 (underline color) not tracked in cell comparison for now\n i++\n }\n}\n\n/**\n * Replay ANSI output tracking both characters AND SGR styles.\n * Returns a 2D grid of StyledCell objects.\n *\n * @deprecated Use `SILVERY_STRICT_TERMINAL=vt100` instead. This function is now\n * the internal implementation of the `vt100` backend for STRICT_TERMINAL verification.\n * Direct usage is discouraged — prefer the unified STRICT_TERMINAL env var interface.\n */\nexport function replayAnsiWithStyles(\n width: number,\n height: number,\n ansi: string,\n ctx: OutputContext = defaultContext,\n): StyledCell[][] {\n const screen: StyledCell[][] = Array.from({ length: height }, () =>\n Array.from({ length: width }, () => createDefaultStyledCell()),\n )\n let cx = 0\n let cy = 0\n const sgr = createDefaultSgr()\n let i = 0\n\n while (i < ansi.length) {\n if (ansi[i] === \"\\x1b\") {\n if (ansi[i + 1] === \"[\") {\n i += 2\n let params = \"\"\n while (\n i < ansi.length &&\n ((ansi[i]! >= \"0\" && ansi[i]! <= \"9\") || ansi[i] === \";\" || ansi[i] === \"?\" || ansi[i] === \":\")\n ) {\n params += ansi[i]\n i++\n }\n const cmd = ansi[i]\n i++\n if (cmd === \"H\") {\n if (params === \"\") {\n cx = 0\n cy = 0\n } else {\n const cmdParts = params.split(\";\")\n // Clamp to screen bounds (real terminals clamp CUP to valid range)\n cy = Math.min(height - 1, Math.max(0, (parseInt(cmdParts[0]!) || 1) - 1))\n cx = Math.min(width - 1, Math.max(0, (parseInt(cmdParts[1]!) || 1) - 1))\n }\n } else if (cmd === \"K\") {\n // Erase to end of line — fills with current bg (or default)\n if (cy >= height) continue // out of bounds — skip\n for (let x = cx; x < width; x++) {\n const cell = screen[cy]![x]!\n cell.char = \" \"\n cell.fg = null\n cell.bg = sgr.bg\n cell.bold = false\n cell.dim = false\n cell.italic = false\n cell.underline = false\n cell.blink = false\n cell.inverse = false\n cell.hidden = false\n cell.strikethrough = false\n }\n } else if (cmd === \"A\") {\n cy = Math.max(0, cy - (parseInt(params) || 1))\n } else if (cmd === \"B\") {\n cy = Math.min(height - 1, cy + (parseInt(params) || 1))\n } else if (cmd === \"C\") {\n cx = Math.min(width - 1, cx + (parseInt(params) || 1))\n } else if (cmd === \"D\") {\n cx = Math.max(0, cx - (parseInt(params) || 1))\n } else if (cmd === \"G\") {\n cx = Math.max(0, (parseInt(params) || 1) - 1)\n } else if (cmd === \"J\") {\n if (params === \"2\") {\n for (let y = 0; y < height; y++)\n for (let x = 0; x < width; x++) {\n screen[y]![x] = createDefaultStyledCell()\n }\n }\n } else if (cmd === \"m\") {\n // SGR — apply to current state\n applySgrParams(params, sgr)\n }\n // Skip DEC modes (h/l), etc.\n } else if (ansi[i + 1] === \"]\") {\n // OSC: extract payload and check for OSC 66 (text sizing)\n i += 2\n let oscPayload = \"\"\n while (i < ansi.length) {\n if (ansi[i] === \"\\x1b\" && ansi[i + 1] === \"\\\\\") {\n i += 2\n break\n }\n if (ansi[i] === \"\\x07\") {\n i++\n break\n }\n oscPayload += ansi[i]\n i++\n }\n // OSC 66: text sizing — format is \"66;w=N;TEXT\"\n // Extract TEXT and process it as a character with the declared width\n if (oscPayload.startsWith(\"66;\")) {\n const semiIdx = oscPayload.indexOf(\";\", 3)\n if (semiIdx !== -1) {\n const text = oscPayload.slice(semiIdx + 1)\n const widthParam = oscPayload.slice(3, semiIdx)\n const declaredWidth = widthParam.startsWith(\"w=\") ? parseInt(widthParam.slice(2)) || 1 : 1\n if (cy < height && cx < width) {\n const cell = screen[cy]![cx]!\n cell.char = text\n cell.fg = sgr.fg\n cell.bg = sgr.bg\n cell.bold = sgr.bold\n cell.dim = sgr.dim\n cell.italic = sgr.italic\n cell.underline = sgr.underline\n cell.blink = sgr.blink\n cell.inverse = sgr.inverse\n cell.hidden = sgr.hidden\n cell.strikethrough = sgr.strikethrough\n if (declaredWidth > 1 && cx + 1 < width) {\n const cont = screen[cy]![cx + 1]!\n cont.char = \" \"\n cont.fg = null\n cont.bg = sgr.bg\n cont.bold = false\n cont.dim = false\n cont.italic = false\n cont.underline = false\n cont.blink = false\n cont.inverse = false\n cont.hidden = false\n cont.strikethrough = false\n }\n cx += declaredWidth\n }\n }\n }\n // Other OSC sequences (8=hyperlinks, etc.) are skipped\n } else if (ansi[i + 1] === \">\") {\n i += 2\n while (i < ansi.length && ansi[i] !== \"\\x1b\") i++\n } else {\n i += 2\n }\n } else if (ansi[i] === \"\\r\") {\n cx = 0\n i++\n } else if (ansi[i] === \"\\n\") {\n cy = Math.min(height - 1, cy + 1)\n i++\n } else {\n // Extract a full grapheme cluster (handles surrogate pairs and multi-codepoint sequences\n // like flag emoji 🇺🇸 which are 2 regional indicator codepoints = 4 UTF-16 code units)\n const cp = ansi.codePointAt(i)!\n // Advance past this codepoint (2 code units if surrogate pair, 1 otherwise)\n const cpLen = cp > 0xffff ? 2 : 1\n // Collect combining marks and joiners that follow (ZWJ sequences, variation selectors, etc.)\n let grapheme = String.fromCodePoint(cp)\n let j = i + cpLen\n let prevWasZwj = false\n while (j < ansi.length) {\n const nextCp = ansi.codePointAt(j)!\n // Combining marks (U+0300-U+036F, U+20D0-U+20FF, U+FE00-U+FE0F variation selectors),\n // ZWJ (U+200D), regional indicators following another regional indicator.\n // After ZWJ, the next codepoint is always consumed (it's the joinee — e.g.,\n // 🏃‍♂️ = runner + ZWJ + male sign + VS16: male sign is NOT a combining mark\n // but must be part of this grapheme cluster).\n const isCombining =\n prevWasZwj || // Joinee after ZWJ\n (nextCp >= 0x0300 && nextCp <= 0x036f) || // Combining Diacritical Marks\n (nextCp >= 0x20d0 && nextCp <= 0x20ff) || // Combining Diacritical Marks for Symbols\n (nextCp >= 0xfe00 && nextCp <= 0xfe0f) || // Variation Selectors\n nextCp === 0xfe0e ||\n nextCp === 0xfe0f || // Text/Emoji presentation\n nextCp === 0x200d || // ZWJ\n (nextCp >= 0xe0100 && nextCp <= 0xe01ef) || // Variation Selectors Supplement\n // Skin tone modifiers (Fitzpatrick scale)\n (nextCp >= 0x1f3fb && nextCp <= 0x1f3ff) ||\n // Regional indicator following a regional indicator (flag sequences)\n (cp >= 0x1f1e6 && cp <= 0x1f1ff && nextCp >= 0x1f1e6 && nextCp <= 0x1f1ff)\n if (!isCombining) break\n prevWasZwj = nextCp === 0x200d\n const nextLen = nextCp > 0xffff ? 2 : 1\n grapheme += String.fromCodePoint(nextCp)\n j += nextLen\n }\n if (cy < height && cx < width) {\n const gw = outputGraphemeWidth(grapheme, ctx)\n const charWidth = gw || 1\n\n const cell = screen[cy]![cx]!\n cell.char = grapheme\n cell.fg = sgr.fg\n cell.bg = sgr.bg\n cell.bold = sgr.bold\n cell.dim = sgr.dim\n cell.italic = sgr.italic\n cell.underline = sgr.underline\n cell.blink = sgr.blink\n cell.inverse = sgr.inverse\n cell.hidden = sgr.hidden\n cell.strikethrough = sgr.strikethrough\n\n // Wide character overwrites the next cell (continuation cell)\n // Real terminals do this automatically — the wide char occupies 2 columns\n if (charWidth > 1 && cx + 1 < width) {\n const cont = screen[cy]![cx + 1]!\n cont.char = \" \"\n cont.fg = null\n cont.bg = sgr.bg\n cont.bold = false\n cont.dim = false\n cont.italic = false\n cont.underline = false\n cont.blink = false\n cont.inverse = false\n cont.hidden = false\n cont.strikethrough = false\n }\n cx += charWidth\n }\n i = j\n }\n }\n return screen\n}\n\n/** Format a color value for display. */\nfunction formatColor(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 * Verify that applying changesToAnsi output to a previous terminal state\n * produces the same visible characters AND styles as a fresh render of the\n * next buffer. Throws on mismatch.\n *\n * This catches SGR style bugs that character-only verification misses.\n */\n\n// =============================================================================\n// STRICT failure artifact capture\n// =============================================================================\n\n/**\n * Capture debug artifacts to disk when a STRICT verification fails.\n * Saves prev/next buffer snapshots, ANSI sequences, terminal size, and test context\n * to /tmp/silvery-strict-failure-<timestamp>/.\n *\n * Returns the artifact directory path (included in the error message).\n */\nexport function captureStrictFailureArtifacts(opts: {\n source: string\n errorMessage: string\n prev?: TerminalBuffer | null\n next?: TerminalBuffer | null\n incrOutput?: string\n freshOutput?: string\n ctx?: OutputContext\n frameCount?: number\n}): string {\n try {\n const fs = require(\"fs\")\n const path = require(\"path\")\n const timestamp = Date.now()\n const dir = `/tmp/silvery-strict-failure-${timestamp}`\n fs.mkdirSync(dir, { recursive: true })\n\n // Metadata\n const meta: Record<string, unknown> = {\n source: opts.source,\n timestamp: new Date().toISOString(),\n frameCount: opts.frameCount,\n prevSize: opts.prev ? { width: opts.prev.width, height: opts.prev.height } : null,\n nextSize: opts.next ? { width: opts.next.width, height: opts.next.height } : null,\n incrOutputLength: opts.incrOutput?.length,\n freshOutputLength: opts.freshOutput?.length,\n testName: (globalThis as any).__vitest_worker__?.current?.name as string | undefined,\n }\n fs.writeFileSync(path.join(dir, \"meta.json\"), JSON.stringify(meta, null, 2))\n\n // Error message\n fs.writeFileSync(path.join(dir, \"error.txt\"), opts.errorMessage)\n\n // ANSI sequences\n if (opts.incrOutput) {\n fs.writeFileSync(path.join(dir, \"incremental.ansi\"), opts.incrOutput)\n }\n if (opts.freshOutput) {\n fs.writeFileSync(path.join(dir, \"fresh.ansi\"), opts.freshOutput)\n }\n\n // Buffer snapshots (text representation)\n if (opts.prev) {\n const rows: string[] = []\n for (let y = 0; y < opts.prev.height; y++) {\n let row = \"\"\n for (let x = 0; x < opts.prev.width; x++) {\n const cell = opts.prev.getCell(x, y)\n row += cell.char || \" \"\n }\n rows.push(row.trimEnd())\n }\n fs.writeFileSync(path.join(dir, \"prev-buffer.txt\"), rows.join(\"\\n\"))\n }\n if (opts.next) {\n const rows: string[] = []\n for (let y = 0; y < opts.next.height; y++) {\n let row = \"\"\n for (let x = 0; x < opts.next.width; x++) {\n const cell = opts.next.getCell(x, y)\n row += cell.char || \" \"\n }\n rows.push(row.trimEnd())\n }\n fs.writeFileSync(path.join(dir, \"next-buffer.txt\"), rows.join(\"\\n\"))\n }\n\n // Fresh prev ANSI (for replay)\n if (opts.prev && opts.ctx) {\n const freshPrev = bufferToAnsi(opts.prev, opts.ctx)\n fs.writeFileSync(path.join(dir, \"fresh-prev.ansi\"), freshPrev)\n }\n\n return dir\n } catch {\n return \"(artifact capture failed)\"\n }\n}\n\nfunction verifyOutputEquivalence(\n prev: TerminalBuffer,\n next: TerminalBuffer,\n incrOutput: string,\n ctx: OutputContext = defaultContext,\n): void {\n const { mode } = ctx\n const w = Math.max(prev.width, next.width)\n // VT height must accommodate the larger buffer to prevent scrolling artifacts\n // when prev is taller than next (e.g., items removed from a scrollback list).\n // We only compare up to next.height rows — excess rows should be cleared.\n const vtHeight = Math.max(prev.height, next.height)\n const compareHeight = next.height\n // DEBUG: log buffer dimensions\n if (process.env.SILVERY_DEBUG_OUTPUT) {\n // eslint-disable-next-line no-console\n console.error(\n `[VERIFY] prev=${prev.width}x${prev.height} next=${next.width}x${next.height} vtSize=${w}x${vtHeight}`,\n )\n }\n // Replay: fresh prev render + incremental diff applied on top\n const freshPrev = bufferToAnsi(prev, ctx)\n if (process.env.SILVERY_DEBUG_OUTPUT) {\n // eslint-disable-next-line no-console\n console.error(`[VERIFY] freshPrev len=${freshPrev.length} incrOutput len=${incrOutput.length}`)\n // Show incrOutput as escaped string\n const escaped = incrOutput.replace(/\\x1b/g, \"\\\\e\").replace(/\\r/g, \"\\\\r\").replace(/\\n/g, \"\\\\n\")\n // eslint-disable-next-line no-console\n console.error(`[VERIFY] incrOutput: ${escaped.slice(0, 500)}`)\n }\n const screenIncr = replayAnsiWithStyles(w, vtHeight, freshPrev + incrOutput, ctx)\n // Replay: fresh render of next buffer\n const freshNext = bufferToAnsi(next, ctx)\n const screenFresh = replayAnsiWithStyles(w, vtHeight, freshNext, ctx)\n\n const _dumpRowWideCells = (buf: TerminalBuffer, row: number): string => {\n const parts: string[] = []\n for (let cx = 0; cx < buf.width; cx++) {\n const c = buf.getCell(cx, row)\n const cp = c.char\n ? [...c.char].map((ch) => \"U+\" + (ch.codePointAt(0) ?? 0).toString(16).toUpperCase().padStart(4, \"0\")).join(\",\")\n : \"empty\"\n if (c.wide) parts.push(`W@${cx}:${cp}(gw=${outputGraphemeWidth(c.char, ctx)})`)\n if (c.continuation) parts.push(`C@${cx}`)\n // Flag cells where written char width differs from buffer expectation\n const charToWrite = c.char || \" \"\n const vtWidth = outputGraphemeWidth(charToWrite, ctx)\n const bufWidth = c.wide ? 2 : 1\n if (!c.continuation && vtWidth !== bufWidth) {\n parts.push(`MISMATCH@${cx}:${cp}(vtW=${vtWidth},bufW=${bufWidth},tse=${outputTextSizingEnabled(ctx)})`)\n }\n }\n return parts.join(\" \")\n }\n\n // Compare character by character AND style by style.\n // Use vtHeight (not compareHeight) to catch stale rows after height shrink.\n // When prev.height > next.height, stale rows beyond next.height must be\n // verified as cleared — otherwise incremental output silently diverges.\n for (let y = 0; y < vtHeight; y++) {\n for (let x = 0; x < w; x++) {\n const incr = screenIncr[y]![x]!\n const fresh = screenFresh[y]![x]!\n\n // Check character\n if (incr.char !== fresh.char) {\n // Build context: show the row from both renders\n const incrRow = screenIncr[y]!.map((c) => c.char).join(\"\")\n const freshRow = screenFresh[y]!.map((c) => c.char).join(\"\")\n // Also show the prev buffer row for diagnosis\n const prevRow = screenIncr[y]!.map((_, cx) => {\n const prevCell = prev.getCell(cx, y)\n return prevCell.char\n }).join(\"\")\n // Show what changesToAnsi tried to write at this position\n const nextCell = next.getCell(x, y)\n const prevCell = prev.getCell(x, y)\n // Show detailed column-by-column comparison around the mismatch\n const contextStart = Math.max(0, x - 5)\n const contextEnd = Math.min(w, x + 10)\n const colDetails: string[] = []\n for (let cx = contextStart; cx < contextEnd; cx++) {\n const ic = screenIncr[y]![cx]!\n const fc = screenFresh[y]![cx]!\n const pc = prev.getCell(cx, y)\n const nc = next.getCell(cx, y)\n const marker = cx === x ? \" <<<\" : ic.char !== fc.char ? \" !!!\" : \"\"\n colDetails.push(\n ` col ${cx}: prev='${pc.char}'(w=${pc.wide},c=${pc.continuation}) next='${nc.char}' incr='${ic.char}' fresh='${fc.char}' wide=${nc.wide} cont=${nc.continuation}${marker}`,\n )\n }\n const msg =\n `STRICT_OUTPUT char mismatch at (${x},${y}): ` +\n `incremental='${incr.char}' fresh='${fresh.char}'\\n` +\n ` prev buffer cell: char='${prevCell.char}' bg=${prevCell.bg} wide=${prevCell.wide} cont=${prevCell.continuation}\\n` +\n ` next buffer cell: char='${nextCell.char}' bg=${nextCell.bg} wide=${nextCell.wide} cont=${nextCell.continuation}\\n` +\n ` incr row: ${incrRow}\\n` +\n ` fresh row: ${freshRow}\\n` +\n ` prev row: ${prevRow}\\n` +\n `Wide/cont cells on row ${y} (next buffer): ${_dumpRowWideCells(next, y)}\\n` +\n `Wide/cont cells on row ${y} (prev buffer): ${_dumpRowWideCells(prev, y)}\\n` +\n `Column detail around mismatch:\\n${colDetails.join(\"\\n\")}`\n const artifactDir = captureStrictFailureArtifacts({\n source: \"STRICT_OUTPUT\",\n errorMessage: msg,\n prev,\n next,\n incrOutput,\n freshOutput: freshNext,\n ctx,\n })\n const fullMsg = `${msg}\\n Artifacts: ${artifactDir}`\n // eslint-disable-next-line no-console\n console.error(fullMsg)\n throw new IncrementalRenderMismatchError(fullMsg)\n }\n\n // Check styles\n const diffs: string[] = []\n if (!sgrColorEquals(incr.fg, fresh.fg)) diffs.push(`fg: ${formatColor(incr.fg)} vs ${formatColor(fresh.fg)}`)\n if (!sgrColorEquals(incr.bg, fresh.bg)) diffs.push(`bg: ${formatColor(incr.bg)} vs ${formatColor(fresh.bg)}`)\n if (incr.bold !== fresh.bold) diffs.push(`bold: ${incr.bold} vs ${fresh.bold}`)\n if (incr.dim !== fresh.dim) diffs.push(`dim: ${incr.dim} vs ${fresh.dim}`)\n if (incr.italic !== fresh.italic) diffs.push(`italic: ${incr.italic} vs ${fresh.italic}`)\n if (incr.underline !== fresh.underline) diffs.push(`underline: ${incr.underline} vs ${fresh.underline}`)\n if (incr.inverse !== fresh.inverse) diffs.push(`inverse: ${incr.inverse} vs ${fresh.inverse}`)\n if (incr.strikethrough !== fresh.strikethrough)\n diffs.push(`strikethrough: ${incr.strikethrough} vs ${fresh.strikethrough}`)\n\n if (diffs.length > 0) {\n const msg =\n `STRICT_OUTPUT style mismatch at (${x},${y}) char='${incr.char}': ` +\n diffs.join(\", \") +\n `\\n incremental: fg=${formatColor(incr.fg)} bg=${formatColor(incr.bg)} bold=${incr.bold} dim=${incr.dim}` +\n `\\n fresh: fg=${formatColor(fresh.fg)} bg=${formatColor(fresh.bg)} bold=${fresh.bold} dim=${fresh.dim}`\n const artifactDir2 = captureStrictFailureArtifacts({\n source: \"STRICT_OUTPUT\",\n errorMessage: msg,\n prev,\n next,\n incrOutput,\n freshOutput: freshNext,\n ctx,\n })\n throw new IncrementalRenderMismatchError(`${msg}\\n Artifacts: ${artifactDir2}`)\n }\n }\n }\n}\n\n/**\n * Verify that the accumulated output from all frames produces the same\n * terminal state as a fresh render of the current buffer.\n * Catches compounding errors across multiple render frames.\n */\nfunction verifyAccumulatedOutput(\n currentBuffer: TerminalBuffer,\n ctx: OutputContext = defaultContext,\n accState: AccumulateState = defaultAccState,\n): void {\n const { mode } = ctx\n const w = accState.accumulateWidth\n const h = accState.accumulateHeight\n // Replay all accumulated output (first render + all incremental updates)\n const screenAccumulated = replayAnsiWithStyles(w, h, accState.accumulatedAnsi, ctx)\n // Replay fresh render of current buffer\n const freshOutput = bufferToAnsi(currentBuffer, ctx)\n const screenFresh = replayAnsiWithStyles(w, h, freshOutput, ctx)\n\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < w; x++) {\n const accum = screenAccumulated[y]![x]!\n const fresh = screenFresh[y]![x]!\n\n if (accum.char !== fresh.char) {\n const msg =\n `SILVERY_STRICT_ACCUMULATE char mismatch at (${x},${y}) after ${accState.accumulateFrameCount} frames: ` +\n `accumulated='${accum.char}' fresh='${fresh.char}'`\n const dir = captureStrictFailureArtifacts({\n source: \"STRICT_ACCUMULATE\",\n errorMessage: msg,\n next: currentBuffer,\n incrOutput: accState.accumulatedAnsi,\n freshOutput,\n ctx,\n frameCount: accState.accumulateFrameCount,\n })\n // eslint-disable-next-line no-console\n console.error(`${msg}\\n Artifacts: ${dir}`)\n throw new IncrementalRenderMismatchError(`${msg}\\n Artifacts: ${dir}`)\n }\n\n const diffs: string[] = []\n if (!sgrColorEquals(accum.fg, fresh.fg)) diffs.push(`fg: ${formatColor(accum.fg)} vs ${formatColor(fresh.fg)}`)\n if (!sgrColorEquals(accum.bg, fresh.bg)) diffs.push(`bg: ${formatColor(accum.bg)} vs ${formatColor(fresh.bg)}`)\n if (accum.bold !== fresh.bold) diffs.push(`bold: ${accum.bold} vs ${fresh.bold}`)\n if (accum.dim !== fresh.dim) diffs.push(`dim: ${accum.dim} vs ${fresh.dim}`)\n if (accum.italic !== fresh.italic) diffs.push(`italic: ${accum.italic} vs ${fresh.italic}`)\n if (accum.underline !== fresh.underline) diffs.push(`underline: ${accum.underline} vs ${fresh.underline}`)\n if (accum.inverse !== fresh.inverse) diffs.push(`inverse: ${accum.inverse} vs ${fresh.inverse}`)\n if (accum.strikethrough !== fresh.strikethrough)\n diffs.push(`strikethrough: ${accum.strikethrough} vs ${fresh.strikethrough}`)\n\n if (diffs.length > 0) {\n const msg =\n `SILVERY_STRICT_ACCUMULATE style mismatch at (${x},${y}) char='${accum.char}' after ${accState.accumulateFrameCount} frames: ` +\n diffs.join(\", \")\n const dir2 = captureStrictFailureArtifacts({\n source: \"STRICT_ACCUMULATE\",\n errorMessage: msg,\n next: currentBuffer,\n freshOutput,\n ctx,\n frameCount: accState.accumulateFrameCount,\n })\n // eslint-disable-next-line no-console\n console.error(`${msg}\\n Artifacts: ${dir2}`)\n throw new IncrementalRenderMismatchError(`${msg}\\n Artifacts: ${dir2}`)\n }\n }\n }\n}\n\n// =============================================================================\n// SILVERY_STRICT_TERMINAL: Independent xterm.js emulator verification\n// =============================================================================\n\n/** Lazily loaded termless factories — avoids import cost when STRICT_TERMINAL is off. */\nlet _createTerminal: typeof import(\"@termless/core\").createTerminal | null = null\nlet _createXtermBackend: typeof import(\"@termless/xtermjs\").createXtermBackend | null = null\nlet _createGhosttyBackend: typeof import(\"@termless/ghostty\").createGhosttyBackend | null = null\nlet _ghosttyInitPromise: Promise<void> | null = null\n\nfunction loadTermless(): {\n createTerminal: typeof import(\"@termless/core\").createTerminal\n createXtermBackend: typeof import(\"@termless/xtermjs\").createXtermBackend\n} {\n if (!_createTerminal || !_createXtermBackend) {\n _createTerminal = require(\"@termless/core\").createTerminal\n _createXtermBackend = require(\"@termless/xtermjs\").createXtermBackend\n }\n return { createTerminal: _createTerminal!, createXtermBackend: _createXtermBackend! }\n}\n\nfunction loadGhosttyBackend(): typeof import(\"@termless/ghostty\").createGhosttyBackend {\n if (!_createGhosttyBackend) {\n const mod = require(\"@termless/ghostty\")\n _createGhosttyBackend = mod.createGhosttyBackend\n // Start async WASM init — first call may block on this\n if (!_ghosttyInitPromise) {\n _ghosttyInitPromise = mod.initGhostty()\n }\n }\n return _createGhosttyBackend!\n}\n\n/**\n * Initialize the terminal verify state: create persistent terminal emulators\n * and feed the initial full render ANSI output.\n */\nfunction initTerminalVerifyState(state: TerminalVerifyState, width: number, height: number, initialAnsi: string): void {\n // Close any existing terminals from a previous run\n if (state.terminal) void state.terminal.close()\n if (state.ghosttyTerminal) void state.ghosttyTerminal.close()\n\n // Create xterm.js terminal if requested\n if (state.backends.includes(\"xterm\")) {\n const { createTerminal, createXtermBackend } = loadTermless()\n state.terminal = createTerminal({ backend: createXtermBackend(), cols: width, rows: height })\n state.terminal.feed(initialAnsi)\n } else {\n state.terminal = null\n }\n\n // Create Ghostty terminal if requested\n if (state.backends.includes(\"ghostty\")) {\n const { createTerminal } = loadTermless()\n const createGhostty = loadGhosttyBackend()\n state.ghosttyTerminal = createTerminal({ backend: createGhostty(), cols: width, rows: height })\n state.ghosttyTerminal.feed(initialAnsi)\n } else {\n state.ghosttyTerminal = null\n }\n\n state.width = width\n state.height = height\n state.frameCount = 0\n}\n\n/**\n * Verify that the cumulative incremental ANSI output (fed through a persistent\n * xterm.js terminal) produces the same visible state as a fresh full render\n * (fed through a second, throwaway xterm.js terminal).\n *\n * This is the ONE invariant that catches both OSC 66 and buffer overflow bugs:\n * \"For a given terminal capability profile and viewport size, the actual\n * terminal state after incremental rendering must equal the actual terminal\n * state after a fresh full redraw\" — checked in an independent emulator.\n */\nfunction verifyTerminalEquivalence(\n state: TerminalVerifyState,\n incrOutput: string,\n nextBuffer: TerminalBuffer,\n ctx: OutputContext,\n): void {\n // Buffer dimensions may change between frames (test renderers use content-sized\n // buffers). When dimensions change, the persistent terminal can't be meaningfully\n // compared — CUP commands and scrolling behave differently at different sizes.\n // Reinitialize the persistent terminal with a fresh render at the new dimensions.\n if (nextBuffer.width !== state.width || nextBuffer.height !== state.height) {\n const freshAnsi = bufferToAnsi(nextBuffer, ctx)\n initTerminalVerifyState(state, nextBuffer.width, nextBuffer.height, freshAnsi)\n state.frameCount++\n return\n }\n\n const freshAnsi = bufferToAnsi(nextBuffer, ctx)\n\n // Verify xterm.js terminal\n if (state.terminal) {\n state.terminal.feed(incrOutput)\n const { createTerminal, createXtermBackend } = loadTermless()\n const freshTerm = createTerminal({\n backend: createXtermBackend(),\n cols: state.width,\n rows: state.height,\n })\n freshTerm.feed(freshAnsi)\n try {\n compareTerminals(state.terminal, freshTerm, state, \"xterm\")\n } catch (e) {\n if (e instanceof IncrementalRenderMismatchError) {\n const dir = captureStrictFailureArtifacts({\n source: \"STRICT_TERMINAL[xterm]\",\n errorMessage: e.message,\n next: nextBuffer,\n incrOutput,\n freshOutput: freshAnsi,\n ctx,\n frameCount: state.frameCount,\n })\n throw new IncrementalRenderMismatchError(`${e.message}\\n Artifacts: ${dir}`)\n }\n throw e\n } finally {\n void freshTerm.close()\n }\n }\n\n // Verify Ghostty terminal\n if (state.ghosttyTerminal) {\n state.ghosttyTerminal.feed(incrOutput)\n const { createTerminal } = loadTermless()\n const createGhostty = loadGhosttyBackend()\n const freshTerm = createTerminal({\n backend: createGhostty(),\n cols: state.width,\n rows: state.height,\n })\n freshTerm.feed(freshAnsi)\n try {\n compareTerminals(state.ghosttyTerminal, freshTerm, state, \"ghostty\")\n } catch (e) {\n if (e instanceof IncrementalRenderMismatchError) {\n const dir = captureStrictFailureArtifacts({\n source: \"STRICT_TERMINAL[ghostty]\",\n errorMessage: e.message,\n next: nextBuffer,\n incrOutput,\n freshOutput: freshAnsi,\n ctx,\n frameCount: state.frameCount,\n })\n throw new IncrementalRenderMismatchError(`${e.message}\\n Artifacts: ${dir}`)\n }\n throw e\n } finally {\n void freshTerm.close()\n }\n }\n}\n\n/** Compare two terminal states cell-by-cell. Throws IncrementalRenderMismatchError on divergence. */\nfunction compareTerminals(\n incrTerm: import(\"@termless/core\").Terminal,\n freshTerm: import(\"@termless/core\").Terminal,\n state: TerminalVerifyState,\n backendName: string,\n): void {\n const w = state.width\n const h = state.height\n const prefix = `SILVERY_STRICT_TERMINAL[${backendName}]`\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < w; x++) {\n const incrCell = incrTerm.getCell(y, x)\n const freshCell = freshTerm.getCell(y, x)\n const incrChar = incrCell.char || \" \"\n const freshChar = freshCell.char || \" \"\n\n if (incrChar !== freshChar) {\n const incrRow = Array.from({ length: w }, (_, cx) => incrTerm.getCell(y, cx).char || \" \").join(\"\")\n const freshRow = Array.from({ length: w }, (_, cx) => freshTerm.getCell(y, cx).char || \" \").join(\"\")\n const msg =\n `${prefix} char mismatch at (${x},${y}) frame ${state.frameCount}: ` +\n `incremental='${incrChar}' fresh='${freshChar}'\\n` +\n ` incr row: ${incrRow.trimEnd()}\\n` +\n ` fresh row: ${freshRow.trimEnd()}`\n // eslint-disable-next-line no-console\n console.error(msg)\n throw new IncrementalRenderMismatchError(msg)\n }\n\n if (!rgbEquals(incrCell.fg, freshCell.fg)) {\n const msg =\n `${prefix} fg color mismatch at (${x},${y}) char='${incrChar}' frame ${state.frameCount}: ` +\n `incremental=${formatRgb(incrCell.fg)} fresh=${formatRgb(freshCell.fg)}`\n // eslint-disable-next-line no-console\n console.error(msg)\n throw new IncrementalRenderMismatchError(msg)\n }\n\n if (!rgbEquals(incrCell.bg, freshCell.bg)) {\n const msg =\n `${prefix} bg color mismatch at (${x},${y}) char='${incrChar}' frame ${state.frameCount}: ` +\n `incremental=${formatRgb(incrCell.bg)} fresh=${formatRgb(freshCell.bg)}`\n // eslint-disable-next-line no-console\n console.error(msg)\n throw new IncrementalRenderMismatchError(msg)\n }\n\n const attrDiffs: string[] = []\n if (incrCell.bold !== freshCell.bold) attrDiffs.push(`bold: ${incrCell.bold} vs ${freshCell.bold}`)\n if (incrCell.dim !== freshCell.dim) attrDiffs.push(`dim: ${incrCell.dim} vs ${freshCell.dim}`)\n if (incrCell.italic !== freshCell.italic) attrDiffs.push(`italic: ${incrCell.italic} vs ${freshCell.italic}`)\n if (incrCell.inverse !== freshCell.inverse) attrDiffs.push(`inverse: ${incrCell.inverse} vs ${freshCell.inverse}`)\n if (incrCell.strikethrough !== freshCell.strikethrough)\n attrDiffs.push(`strikethrough: ${incrCell.strikethrough} vs ${freshCell.strikethrough}`)\n\n if (attrDiffs.length > 0) {\n const msg =\n `${prefix} attr mismatch at (${x},${y}) char='${incrChar}' frame ${state.frameCount}: ` + attrDiffs.join(\", \")\n // eslint-disable-next-line no-console\n console.error(msg)\n throw new IncrementalRenderMismatchError(msg)\n }\n }\n }\n}\n\n/** Compare two RGB color values (from termless Cell). */\nfunction rgbEquals(\n a: { r: number; g: number; b: number } | null,\n b: { r: number; g: number; b: number } | null,\n): boolean {\n if (a === b) return true\n if (a === null || b === null) return false\n return a.r === b.r && a.g === b.g && a.b === b.b\n}\n\n/** Format an RGB value for diagnostic messages. */\nfunction formatRgb(c: { r: number; g: number; b: number } | null): string {\n if (c === null) return \"null\"\n return `rgb(${c.r},${c.g},${c.b})`\n}\n\n/** Compare two SGR color values. */\nfunction sgrColorEquals(\n a: number | { r: number; g: number; b: number } | null,\n b: number | { r: number; g: number; b: number } | null,\n): boolean {\n if (a === b) return true\n if (a === null || b === null) return false\n if (typeof a === \"number\" || typeof b === \"number\") return a === b\n return a.r === b.r && a.g === b.g && a.b === b.b\n}\n",
72
+ "/**\n * Silvery Render Pipeline\n *\n * The 5-phase rendering architecture:\n *\n * Phase 0: RECONCILIATION (React)\n * React reconciliation builds the SilveryNode tree.\n * Components register layout constraints via props.\n *\n * Phase 1: MEASURE (for fit-content nodes)\n * Traverse nodes with width/height=\"fit-content\"\n * Measure intrinsic content size\n * Set Yoga constraints based on measurement\n *\n * Phase 2: LAYOUT\n * Run yoga.calculateLayout()\n * Propagate computed dimensions to all nodes\n * Notify useContentRect() subscribers\n *\n * Phase 3: CONTENT RENDER\n * Render each node to the TerminalBuffer\n * Handle text truncation, styling, borders\n *\n * Phase 4: DIFF & OUTPUT\n * Compare current buffer with previous\n * Emit minimal ANSI sequences for changes\n */\n\nimport { createLogger } from \"loggily\"\nimport type { TerminalBuffer } from \"../buffer\"\nimport type { CursorState } from \"@silvery/ag-react/hooks/useCursor\"\nimport type { AgNode } from \"@silvery/ag/types\"\nimport { runWithMeasurer, type Measurer } from \"../unicode\"\nimport type { OutputPhaseFn } from \"./output-phase\"\nimport type { PipelineContext } from \"./types\"\n\nconst log = createLogger(\"silvery:pipeline\")\nconst baseLog = createLogger(\"@silvery/ag-react\")\n\n// Re-export types\nexport type {\n CellChange,\n BorderChars,\n PipelineContext,\n NodeRenderState,\n ClipBounds,\n ContentPhaseStats,\n NodeTraceEntry,\n BgConflictMode,\n} from \"./types\"\n\n// Re-export phase functions\nexport { measurePhase } from \"./measure-phase\"\nexport {\n layoutPhase,\n rectEqual,\n scrollPhase,\n stickyPhase,\n screenRectPhase,\n notifyLayoutSubscribers,\n} from \"./layout-phase\"\nexport { contentPhase, clearBgConflictWarnings, setBgConflictMode } from \"./content-phase\"\nexport { contentPhaseAdapter } from \"./content-phase-adapter\"\nexport { outputPhase } from \"./output-phase\"\n\nimport { contentPhaseAdapter } from \"./content-phase-adapter\"\nimport { clearBgConflictWarnings, contentPhase } from \"./content-phase\"\nimport { layoutPhase, notifyLayoutSubscribers, screenRectPhase, scrollPhase, stickyPhase } from \"./layout-phase\"\n// Import for orchestration\nimport { measurePhase } from \"./measure-phase\"\nimport { outputPhase } from \"./output-phase\"\n\n// ============================================================================\n// Execute Render (Orchestration)\n// ============================================================================\n\n/**\n * Options for executeRender.\n */\nexport interface ExecuteRenderOptions {\n /**\n * Render mode: fullscreen or inline.\n * Default: 'fullscreen'\n */\n mode?: \"fullscreen\" | \"inline\"\n\n /**\n * Skip notifying layout subscribers.\n * Use for static/one-shot renders where layout feedback isn't needed.\n * Default: false\n */\n skipLayoutNotifications?: boolean\n\n /**\n * Skip scroll state updates.\n * Use for fresh render comparisons (SILVERY_STRICT) to avoid mutating state.\n * Default: false\n */\n skipScrollStateUpdates?: boolean\n\n /**\n * Number of lines written to stdout between renders (inline mode only).\n * Used to adjust cursor positioning when external code (e.g., useScrollback)\n * writes directly to stdout between renders.\n * Default: 0\n */\n scrollbackOffset?: number\n\n /**\n * Terminal height in rows (inline mode only).\n * Used to clamp cursor-up offset when content exceeds terminal height.\n * Without this, content taller than the terminal causes rendering corruption\n * because cursor-up can't reach lines that scrolled off screen.\n */\n termRows?: number\n\n /**\n * Cursor position from useCursor() (inline mode only).\n * When provided, the output phase positions the real terminal cursor\n * at this location instead of leaving it at the end of content.\n */\n cursorPos?: CursorState | null\n}\n\n/**\n * Pipeline configuration from withRender().\n * Carries term-scoped width measurer and output phase.\n */\nexport interface PipelineConfig {\n /** Width measurer scoped to terminal capabilities */\n readonly measurer: Measurer\n /** Output phase function scoped to terminal capabilities */\n readonly outputPhaseFn: OutputPhaseFn\n}\n\n/**\n * Execute the full render pipeline.\n *\n * Pass null for prevBuffer on the first render; pass the returned buffer on\n * subsequent renders to enable incremental content rendering (<1ms vs 20-30ms).\n * SILVERY_DEV=1 warns at runtime if prevBuffer is null after the first frame.\n */\nexport function executeRender(\n root: AgNode,\n width: number,\n height: number,\n prevBuffer: TerminalBuffer | null,\n options: ExecuteRenderOptions | \"fullscreen\" | \"inline\" = \"fullscreen\",\n config?: PipelineConfig,\n): { output: string; buffer: TerminalBuffer } {\n // Create PipelineContext from config measurer (if provided).\n // The context is threaded explicitly through the pipeline, eliminating\n // the need for pipeline functions to read the _scopedMeasurer global.\n const ctx: PipelineContext | undefined = config?.measurer ? { measurer: config.measurer } : undefined\n\n if (config?.measurer) {\n // Keep runWithMeasurer for backward compat: output-phase and other\n // non-pipeline consumers still read the scoped measurer global.\n return runWithMeasurer(config.measurer, () => {\n return executeRenderCore(root, width, height, prevBuffer, options, config, ctx)\n })\n }\n return executeRenderCore(root, width, height, prevBuffer, options, config, ctx)\n}\n\n/** Internal: runs the full pipeline. */\nfunction executeRenderCore(\n root: AgNode,\n width: number,\n height: number,\n prevBuffer: TerminalBuffer | null,\n options: ExecuteRenderOptions | \"fullscreen\" | \"inline\" = \"fullscreen\",\n config?: PipelineConfig,\n ctx?: PipelineContext,\n): { output: string; buffer: TerminalBuffer } {\n // Normalize options (string shorthand for mode)\n const opts: ExecuteRenderOptions = typeof options === \"string\" ? { mode: options } : options\n const {\n mode = \"fullscreen\",\n skipLayoutNotifications = false,\n skipScrollStateUpdates = false,\n scrollbackOffset = 0,\n termRows,\n cursorPos,\n } = opts\n // Dev warning: prevBuffer null after first render means incremental is disabled.\n // Intentional null (SILVERY_STRICT, static/one-shot) passes skipLayoutNotifications.\n // console.warn (not loggily) — must fire regardless of logger config.\n if (process?.env?.SILVERY_DEV && prevBuffer === null && root.prevLayout !== null && !skipLayoutNotifications) {\n console.warn(\n \"[silvery] executeRender called with prevBuffer=null on frame 2+ — \" +\n \"incremental content rendering is disabled (full render every frame). \" +\n \"Track the returned buffer and pass it as prevBuffer on subsequent renders.\",\n )\n }\n\n const start = performance.now()\n\n using render = baseLog.span(\"pipeline\", { width, height, mode })\n\n // Clear per-render caches\n clearBgConflictWarnings()\n\n // Phase 1: Measure (for fit-content nodes)\n let tMeasure: number\n {\n using _measure = render.span(\"measure\")\n const t1 = performance.now()\n measurePhase(root, ctx)\n tMeasure = performance.now() - t1\n log.debug?.(`measure: ${tMeasure.toFixed(2)}ms`)\n }\n\n // Phase 2: Layout\n let tLayout: number\n {\n using _layout = render.span(\"layout\")\n const t2 = performance.now()\n layoutPhase(root, width, height)\n tLayout = performance.now() - t2\n log.debug?.(`layout: ${tLayout.toFixed(2)}ms`)\n }\n\n // Phase 2.5: Scroll calculation (for overflow='scroll' containers)\n let tScroll: number\n {\n using _scroll = render.span(\"scroll\")\n const t2s = performance.now()\n scrollPhase(root, { skipStateUpdates: skipScrollStateUpdates })\n tScroll = performance.now() - t2s\n }\n\n // Phase 2.55: Sticky phase (non-scroll container sticky children)\n stickyPhase(root)\n\n // Phase 2.6: Screen rect calculation (screen-relative positions)\n let tScreenRect: number\n {\n using _screenRect = render.span(\"screenRect\")\n const t2r = performance.now()\n screenRectPhase(root)\n tScreenRect = performance.now() - t2r\n }\n\n // Phase 2.7: Notify layout subscribers\n // This runs AFTER screenRectPhase so useScreenRectCallback reads correct positions\n // Skip for static renders where no one will respond to the feedback\n let tNotify = 0\n if (!skipLayoutNotifications) {\n using _notify = render.span(\"notify\")\n const t2n = performance.now()\n notifyLayoutSubscribers(root)\n tNotify = performance.now() - t2n\n }\n\n // Phase 3: Content render (incremental if we have prevBuffer)\n let buffer: TerminalBuffer\n let tContent: number\n {\n using _content = render.span(\"content\")\n const t3 = performance.now()\n buffer = contentPhase(root, prevBuffer, ctx)\n tContent = performance.now() - t3\n log.debug?.(`content: ${tContent.toFixed(2)}ms`)\n }\n\n // Phase 4: Diff and output\n let output: string\n let tOutput: number\n {\n using outputSpan = render.span(\"output\")\n const t4 = performance.now()\n const outputFn = config?.outputPhaseFn ?? outputPhase\n try {\n output = outputFn(prevBuffer, buffer, mode, scrollbackOffset, termRows, cursorPos)\n } catch (e) {\n // Output phase (STRICT output verification) may throw a diagnostic error.\n // Attach the content-phase buffer so callers can still save it for\n // incremental rendering continuity — the buffer is correct even when\n // the ANSI output verification fails.\n if (e instanceof Error) {\n ;(e as any).__silvery_buffer = buffer\n }\n throw e\n }\n tOutput = performance.now() - t4\n outputSpan.spanData.bytes = output.length\n log.debug?.(`output: ${tOutput.toFixed(2)}ms (${output.length} bytes)`)\n }\n\n const total = performance.now() - start\n log.debug?.(`total pipeline: ${total.toFixed(2)}ms`)\n\n // Expose phase timing and render count for benchmarking and diagnostics.\n // Retained on globalThis for programmatic consumers (tui.tsx perf overlay,\n // profile-startup.ts benchmarks, scrollback-perf.tsx demo).\n const pipelineTimings = {\n measure: tMeasure,\n layout: tLayout,\n scroll: tScroll,\n screenRect: tScreenRect,\n notify: tNotify,\n content: tContent,\n output: tOutput,\n total,\n incremental: prevBuffer !== null,\n }\n ;(globalThis as any).__silvery_last_pipeline = pipelineTimings\n ;(globalThis as any).__silvery_render_count = ((globalThis as any).__silvery_render_count ?? 0) + 1\n log.debug?.(\n `pipeline: measure=${tMeasure.toFixed(1)}ms layout=${tLayout.toFixed(1)}ms content=${tContent.toFixed(1)}ms output=${tOutput.toFixed(1)}ms total=${total.toFixed(1)}ms`,\n )\n\n return { output, buffer }\n}\n\n// ============================================================================\n// Execute Render (Adapter-aware)\n// ============================================================================\n\nimport { type RenderBuffer, getRenderAdapter, hasRenderAdapter } from \"../render-adapter\"\n\n/**\n * Execute the full render pipeline using the current RenderAdapter.\n *\n * This version works with any adapter (terminal, canvas, etc.) and returns\n * a RenderBuffer instead of a TerminalBuffer.\n *\n * @param root The root SilveryNode\n * @param width Width in adapter units (cells for terminal, pixels for canvas)\n * @param height Height in adapter units\n * @param prevBuffer Previous buffer for diffing (null on first render)\n * @param options Render options\n * @returns Object with output (if any) and current buffer\n */\nexport function executeRenderAdapter(\n root: AgNode,\n width: number,\n height: number,\n prevBuffer: RenderBuffer | null,\n options: ExecuteRenderOptions | \"fullscreen\" | \"inline\" = \"fullscreen\",\n): { output: string | void; buffer: RenderBuffer } {\n if (!hasRenderAdapter()) {\n throw new Error(\"executeRenderAdapter called without a render adapter set\")\n }\n\n const opts: ExecuteRenderOptions = typeof options === \"string\" ? { mode: options } : options\n const { skipLayoutNotifications = false } = opts\n const start = Date.now()\n const adapter = getRenderAdapter()\n\n using render = baseLog.span(\"pipeline-adapter\", {\n width,\n height,\n adapter: adapter.name,\n })\n\n // Clear per-render caches\n clearBgConflictWarnings()\n\n // Phase 1: Measure\n {\n using _measure = render.span(\"measure\")\n const t1 = Date.now()\n measurePhase(root)\n log.debug?.(`measure: ${Date.now() - t1}ms`)\n }\n\n // Phase 2: Layout\n {\n using _layout = render.span(\"layout\")\n const t2 = Date.now()\n layoutPhase(root, width, height)\n log.debug?.(`layout: ${Date.now() - t2}ms`)\n }\n\n // Phase 2.5: Scroll calculation\n {\n using _scroll = render.span(\"scroll\")\n scrollPhase(root)\n }\n\n // Phase 2.55: Sticky phase (non-scroll container sticky children)\n stickyPhase(root)\n\n // Phase 2.6: Screen rect calculation\n {\n using _screenRect = render.span(\"screenRect\")\n screenRectPhase(root)\n }\n\n // Phase 2.7: Notify layout subscribers\n if (!skipLayoutNotifications) {\n using _notify = render.span(\"notify\")\n notifyLayoutSubscribers(root)\n }\n\n // Phase 3: Content render (adapter-aware)\n let buffer: RenderBuffer\n {\n using _content = render.span(\"content\")\n const t3 = Date.now()\n buffer = contentPhaseAdapter(root)\n log.debug?.(`content: ${Date.now() - t3}ms`)\n }\n\n // Phase 4: Flush via adapter\n let output: string | void\n {\n using outputSpan = render.span(\"output\")\n const t4 = Date.now()\n output = adapter.flush(buffer, prevBuffer)\n if (typeof output === \"string\") {\n outputSpan.spanData.bytes = output.length\n }\n log.debug?.(`output: ${Date.now() - t4}ms`)\n }\n\n log.debug?.(`total pipeline: ${Date.now() - start}ms`)\n\n return { output, buffer }\n}\n",
73
+ "/**\n * Silvery Render Pipeline\n *\n * Re-exports from the pipeline/ directory for backwards compatibility.\n * See pipeline/index.ts for the main implementation.\n */\n\nexport {\n // Types\n type CellChange,\n type BorderChars,\n type ExecuteRenderOptions,\n type PipelineConfig,\n // Phase functions\n measurePhase,\n layoutPhase,\n rectEqual,\n scrollPhase,\n screenRectPhase,\n contentPhase,\n outputPhase,\n // Utilities\n clearBgConflictWarnings,\n setBgConflictMode,\n // Orchestration\n executeRender,\n executeRenderAdapter,\n type PipelineContext,\n} from \"./pipeline/index\"\n",
74
+ "/**\n * Reconciler Helper Functions\n *\n * Utility functions for props comparison and change detection\n * used by the React reconciler during updates.\n */\n\n/**\n * Set of layout-affecting props.\n */\nexport const LAYOUT_PROPS = new Set([\n \"width\",\n \"height\",\n \"minWidth\",\n \"minHeight\",\n \"maxWidth\",\n \"maxHeight\",\n \"flexDirection\",\n \"flexWrap\",\n \"justifyContent\",\n \"alignItems\",\n \"alignContent\",\n \"alignSelf\",\n \"flexGrow\",\n \"flexShrink\",\n \"flexBasis\",\n \"padding\",\n \"paddingX\",\n \"paddingY\",\n \"paddingTop\",\n \"paddingBottom\",\n \"paddingLeft\",\n \"paddingRight\",\n \"margin\",\n \"marginX\",\n \"marginY\",\n \"marginTop\",\n \"marginBottom\",\n \"marginLeft\",\n \"marginRight\",\n \"gap\",\n \"columnGap\",\n \"rowGap\",\n \"borderStyle\",\n \"borderTop\",\n \"borderBottom\",\n \"borderLeft\",\n \"borderRight\",\n \"display\",\n \"position\",\n \"top\",\n \"left\",\n \"bottom\",\n \"right\",\n \"aspectRatio\",\n \"overflow\",\n \"overflowX\",\n \"overflowY\",\n // Note: scrollTo intentionally excluded - it doesn't affect layout dimensions,\n // only scroll offset which is handled in scrollPhase (reads props.scrollTo directly)\n])\n\n/**\n * Check if layout-affecting props changed.\n */\nexport function layoutPropsChanged(oldProps: Record<string, unknown>, newProps: Record<string, unknown>): boolean {\n for (const prop of LAYOUT_PROPS) {\n if (oldProps[prop] !== newProps[prop]) {\n return true\n }\n }\n return false\n}\n\n/**\n * Check if content-affecting props changed.\n * Returns \"text\" for text content changes (affect layout dimensions),\n * \"style\" for style-only changes (affect paint but not layout),\n * or false if nothing content-related changed.\n */\nexport function contentPropsChanged(\n oldProps: Record<string, unknown>,\n newProps: Record<string, unknown>,\n): \"text\" | \"style\" | false {\n // Children change triggers content change ONLY for primitive children (text)\n // Array children are React elements that get reconciled separately\n const oldChildren = oldProps.children\n const newChildren = newProps.children\n if (oldChildren !== newChildren) {\n // Only trigger for primitive children (string, number) that affect text rendering\n const oldIsPrimitive = typeof oldChildren === \"string\" || typeof oldChildren === \"number\"\n const newIsPrimitive = typeof newChildren === \"string\" || typeof newChildren === \"number\"\n if (oldIsPrimitive || newIsPrimitive) {\n return \"text\" // Text changes affect layout (measure returns different result)\n }\n // Array/object children are React elements - don't set contentDirty\n // (child nodes will be updated via their own commitUpdate calls)\n }\n\n // Content props affect layout dimensions (trigger contentDirty + layoutDirty).\n // wrap changes text line count; internal_transform changes text width.\n const contentProps = [\"wrap\", \"internal_transform\"]\n\n for (const prop of contentProps) {\n if (oldProps[prop] !== newProps[prop]) {\n return \"text\" // Affects text layout (measure returns different result)\n }\n }\n\n // Style props affect content (paint) but NOT layout dimensions.\n // borderColor, color, bold, etc. don't change how much space a node takes.\n // borderStyle is also a layout prop (affects border widths), but it's included\n // here so stylePropsDirty is set — otherwise border add/remove doesn't trigger\n // renderBox to draw/clear border characters.\n const styleProps = [\n \"color\",\n \"backgroundColor\",\n \"bold\",\n \"dim\",\n \"dimColor\",\n \"italic\",\n \"underline\",\n \"underlineStyle\",\n \"underlineColor\",\n \"strikethrough\",\n \"inverse\",\n \"borderColor\",\n \"borderStyle\",\n \"outlineStyle\",\n \"outlineColor\",\n \"outlineDimColor\",\n \"outlineTop\",\n \"outlineBottom\",\n \"outlineLeft\",\n \"outlineRight\",\n \"theme\",\n ]\n\n for (const prop of styleProps) {\n if (oldProps[prop] !== newProps[prop]) {\n return \"style\"\n }\n }\n\n return false\n}\n\n/**\n * Shallow compare two prop objects.\n */\nexport function propsEqual(a: Record<string, unknown>, b: Record<string, unknown>): boolean {\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n\n if (keysA.length !== keysB.length) {\n return false\n }\n\n for (const key of keysA) {\n if (a[key] !== b[key]) {\n return false\n }\n }\n\n return true\n}\n",
75
+ "/**\n * Node Creation and Layout Application\n *\n * Functions for creating SilveryNodes and applying layout properties.\n */\n\nimport { createLogger } from \"loggily\"\nimport { type LayoutNode, getConstants, getLayoutEngine } from \"@silvery/ag-term/layout-engine\"\nimport { collectPlainTextSkipHidden as collectNodeTextContent } from \"@silvery/ag-term/pipeline/collect-text\"\nimport { type BoxProps, type AgNode, type AgNodeType, type TextProps, rectEqual } from \"@silvery/ag/types\"\nimport { type Measurer, displayWidth, wrapText } from \"@silvery/ag-term/unicode\"\n\nconst measureLog = createLogger(\"silvery:measure\")\n\n// Import from shared module (lives in @silvery/ag-term to keep barrel React-free)\n// Re-exported for consumers that imported from here previously\nimport { measureStats } from \"@silvery/ag-term/pipeline/measure-stats\"\nexport { measureStats }\n\n// ============================================================================\n// Node Creation\n// ============================================================================\n\n/**\n * Create a new SilveryNode with a fresh layout node.\n */\nexport function createNode(\n type: AgNodeType,\n props: BoxProps | TextProps | Record<string, unknown>,\n measurer?: Measurer,\n): AgNode {\n const layoutNode = getLayoutEngine().createNode()\n\n const node: AgNode = {\n type,\n props,\n children: [],\n parent: null,\n layoutNode,\n contentRect: null,\n screenRect: null,\n renderRect: null,\n prevLayout: null,\n prevScreenRect: null,\n prevRenderRect: null,\n layoutChangedThisFrame: false,\n layoutDirty: true,\n contentDirty: true,\n stylePropsDirty: true,\n bgDirty: true,\n subtreeDirty: true,\n childrenDirty: true,\n layoutSubscribers: new Set(),\n }\n\n // Apply initial flexbox props to layout node\n if (type === \"silvery-box\") {\n applyBoxProps(layoutNode, props as BoxProps)\n }\n\n // Set up measure function for text nodes\n // This tells the layout engine how to calculate the text's intrinsic size\n if (type === \"silvery-text\") {\n // Cache for measure results - avoid recalculating if text and constraints unchanged\n // Cache multiple (width, widthMode) -> result entries since layout calls measure with different widths\n let cachedText: string | null = null\n const measureCache = new Map<string, { width: number; height: number }>()\n\n layoutNode.setMeasureFunc((width, widthMode, height, heightMode) => {\n measureStats.calls++\n // Log measure calls through loggily (zero-cost when disabled via optional chaining)\n measureLog.debug?.(\n `measure \"${collectNodeTextContent(node).slice(0, 40)}\" width=${width} widthMode=${widthMode} height=${height} heightMode=${heightMode}`,\n )\n\n // Fast path: check if we have a cached result for this exact constraint\n // This avoids text collection entirely if we've measured this before\n const cacheKey = `${width}|${widthMode}|${height}|${heightMode}`\n const cached = measureCache.get(cacheKey)\n if (cached && cachedText !== null && !node.contentDirty) {\n measureStats.cacheHits++\n return cached\n }\n\n // Collect text content from this node and its raw text children\n // Use cached text if node hasn't been marked dirty (contentDirty)\n let text: string\n if (cachedText !== null && !node.contentDirty) {\n text = cachedText\n } else {\n measureStats.textCollects++\n const newText = collectNodeTextContent(node)\n // Only clear measurement cache if text actually changed\n if (newText !== cachedText) {\n measureCache.clear()\n }\n text = newText\n cachedText = text\n // Clear contentDirty so subsequent measure calls in same layout pass use cache.\n // NOTE: This means the content phase won't see contentDirty=true for text nodes\n // whose content changed. The content phase uses stylePropsDirty (which survives the\n // measure phase) combined with the node type check to correctly identify text\n // nodes that need region clearing. See contentAreaAffected in content-phase.ts.\n node.contentDirty = false\n }\n if (!text) {\n return { width: 0, height: 0 }\n }\n\n // Check cache again (may have been preserved if text unchanged)\n const cachedAfterCollect = measureCache.get(cacheKey)\n if (cachedAfterCollect) {\n measureStats.cacheHits++\n return cachedAfterCollect\n }\n\n // Calculate text dimensions\n const lines = text.split(\"\\n\")\n // Treat NaN width the same as unconstrained (can happen with auto-sized parents)\n const maxWidth = widthMode === \"undefined\" || Number.isNaN(width) ? Number.POSITIVE_INFINITY : width\n\n // Check if text will be truncated (not wrapped) — affects height calculation\n const { wrap } = node.props as TextProps\n const isTruncate =\n wrap === \"truncate\" ||\n wrap === \"truncate-start\" ||\n wrap === \"truncate-middle\" ||\n wrap === \"truncate-end\" ||\n wrap === \"clip\" ||\n wrap === false\n\n // Calculate actual dimensions based on wrapping\n // Use wrapText() for accurate line count — must match the render phase\n // (render-text.ts formatTextLines) which also uses wrapText()\n let totalHeight = 0\n let actualWidth = 0\n\n // Use explicit measurer when available, fall back to module-level convenience functions\n const dw = measurer ? measurer.displayWidth.bind(measurer) : displayWidth\n const wt = measurer ? measurer.wrapText.bind(measurer) : wrapText\n\n for (const line of lines) {\n measureStats.displayWidthCalls++\n const lineWidth = dw(line)\n if (isTruncate || lineWidth <= maxWidth) {\n // Truncated text always takes 1 line per source line\n totalHeight += 1\n actualWidth = Math.max(actualWidth, isTruncate ? Math.min(lineWidth, maxWidth) : lineWidth)\n } else {\n // Use same word-aware wrapping as render phase for accurate height\n const wrapped = wt(line, maxWidth, false, true)\n totalHeight += wrapped.length\n for (const wl of wrapped) {\n actualWidth = Math.max(actualWidth, dw(wl))\n }\n }\n }\n\n // Respect height constraint from layout engine.\n // When heightMode is \"at-most\", the text should not exceed the available height.\n // When heightMode is \"exactly\", the text should be exactly that height.\n // This prevents text from overflowing into parent border rows.\n let resultHeight = Math.max(1, totalHeight)\n if (heightMode === \"exactly\" && Number.isFinite(height)) {\n resultHeight = height\n } else if (heightMode === \"at-most\" && Number.isFinite(height)) {\n resultHeight = Math.min(resultHeight, height)\n }\n\n // Cache and return result\n const result = {\n width: Math.min(actualWidth, maxWidth),\n height: resultHeight,\n }\n measureCache.set(cacheKey, result)\n return result\n })\n }\n\n return node\n}\n\n// collectNodeTextContent is imported from @silvery/ag-term/pipeline/collect-text\n// as collectPlainTextSkipHidden. Previously duplicated here; now shared with\n// measure-phase.ts and render-text.ts via the shared collect-text module.\n\n/**\n * Create the root node for the Silvery tree.\n * Root is always column (document flow is top-to-bottom), regardless of\n * flexily's default flexDirection.\n */\nexport function createRootNode(): AgNode {\n const node = createNode(\"silvery-root\", {})\n const c = getConstants()\n node.layoutNode!.setFlexDirection(c.FLEX_DIRECTION_COLUMN)\n return node\n}\n\n/**\n * Create a virtual text node (for nested text elements).\n * Virtual text nodes don't have layout nodes and don't participate in layout.\n * They're used when Text is nested inside another Text.\n */\nexport function createVirtualTextNode(props: TextProps): AgNode {\n return {\n type: \"silvery-text\",\n props,\n children: [],\n parent: null,\n layoutNode: null, // No layout node for virtual text\n contentRect: null,\n screenRect: null,\n renderRect: null,\n prevLayout: null,\n prevScreenRect: null,\n prevRenderRect: null,\n layoutChangedThisFrame: false,\n layoutDirty: false,\n contentDirty: true,\n stylePropsDirty: true,\n bgDirty: true,\n subtreeDirty: true,\n childrenDirty: false,\n layoutSubscribers: new Set(),\n isRawText: false, // Not raw text, but virtual (nested) text\n inlineRects: null,\n }\n}\n\n// ============================================================================\n// Layout Property Application\n// ============================================================================\n\n/**\n * Apply BoxProps to a layout node.\n * This maps Ink/Silvery props to the layout engine API.\n */\nexport function applyBoxProps(layoutNode: LayoutNode, props: BoxProps, oldProps?: BoxProps): void {\n const c = getConstants()\n // Helper: true when a prop was set in oldProps but not in newProps (prop removed on rerender)\n const wasRemoved = (prop: keyof BoxProps): boolean => oldProps?.[prop] !== undefined && props[prop] === undefined\n\n // Dimensions\n if (props.width !== undefined) {\n if (typeof props.width === \"string\" && props.width.endsWith(\"%\")) {\n layoutNode.setWidthPercent(Number.parseFloat(props.width))\n } else if (typeof props.width === \"number\") {\n layoutNode.setWidth(props.width)\n } else if (props.width === \"auto\") {\n layoutNode.setWidthAuto()\n }\n } else if (wasRemoved(\"width\")) {\n layoutNode.setWidthAuto()\n }\n\n if (props.height !== undefined) {\n if (typeof props.height === \"string\" && props.height.endsWith(\"%\")) {\n layoutNode.setHeightPercent(Number.parseFloat(props.height))\n } else if (typeof props.height === \"number\") {\n layoutNode.setHeight(props.height)\n } else if (props.height === \"auto\") {\n layoutNode.setHeightAuto()\n }\n } else if (wasRemoved(\"height\")) {\n layoutNode.setHeightAuto()\n }\n\n // Min/Max dimensions\n if (props.minWidth !== undefined) {\n if (typeof props.minWidth === \"string\" && props.minWidth.endsWith(\"%\")) {\n layoutNode.setMinWidthPercent(Number.parseFloat(props.minWidth))\n } else if (typeof props.minWidth === \"number\") {\n layoutNode.setMinWidth(props.minWidth)\n }\n } else if (wasRemoved(\"minWidth\")) {\n layoutNode.setMinWidth(0)\n }\n\n if (props.minHeight !== undefined) {\n if (typeof props.minHeight === \"string\" && props.minHeight.endsWith(\"%\")) {\n layoutNode.setMinHeightPercent(Number.parseFloat(props.minHeight))\n } else if (typeof props.minHeight === \"number\") {\n layoutNode.setMinHeight(props.minHeight)\n }\n } else if (wasRemoved(\"minHeight\")) {\n layoutNode.setMinHeight(0)\n }\n\n if (props.maxWidth !== undefined) {\n if (typeof props.maxWidth === \"string\" && props.maxWidth.endsWith(\"%\")) {\n layoutNode.setMaxWidthPercent(Number.parseFloat(props.maxWidth))\n } else if (typeof props.maxWidth === \"number\") {\n layoutNode.setMaxWidth(props.maxWidth)\n }\n } else if (wasRemoved(\"maxWidth\")) {\n layoutNode.setMaxWidth(Number.POSITIVE_INFINITY)\n }\n\n if (props.maxHeight !== undefined) {\n if (typeof props.maxHeight === \"string\" && props.maxHeight.endsWith(\"%\")) {\n layoutNode.setMaxHeightPercent(Number.parseFloat(props.maxHeight))\n } else if (typeof props.maxHeight === \"number\") {\n layoutNode.setMaxHeight(props.maxHeight)\n }\n } else if (wasRemoved(\"maxHeight\")) {\n layoutNode.setMaxHeight(Number.POSITIVE_INFINITY)\n }\n\n // Flex properties\n if (props.flexGrow !== undefined) {\n layoutNode.setFlexGrow(props.flexGrow)\n } else if (wasRemoved(\"flexGrow\")) {\n layoutNode.setFlexGrow(0)\n }\n\n if (props.flexShrink !== undefined) {\n layoutNode.setFlexShrink(props.flexShrink)\n } else if (wasRemoved(\"flexShrink\")) {\n layoutNode.setFlexShrink(1)\n }\n\n if (props.flexBasis !== undefined) {\n if (typeof props.flexBasis === \"string\" && props.flexBasis.endsWith(\"%\")) {\n layoutNode.setFlexBasisPercent(Number.parseFloat(props.flexBasis))\n } else if (props.flexBasis === \"auto\") {\n layoutNode.setFlexBasisAuto()\n } else if (typeof props.flexBasis === \"number\") {\n layoutNode.setFlexBasis(props.flexBasis)\n }\n } else if (wasRemoved(\"flexBasis\")) {\n layoutNode.setFlexBasisAuto()\n }\n\n // Flex direction\n if (props.flexDirection !== undefined) {\n const directionMap: Record<string, number> = {\n row: c.FLEX_DIRECTION_ROW,\n column: c.FLEX_DIRECTION_COLUMN,\n \"row-reverse\": c.FLEX_DIRECTION_ROW_REVERSE,\n \"column-reverse\": c.FLEX_DIRECTION_COLUMN_REVERSE,\n }\n layoutNode.setFlexDirection(directionMap[props.flexDirection] ?? c.FLEX_DIRECTION_ROW)\n } else if (wasRemoved(\"flexDirection\")) {\n layoutNode.setFlexDirection(c.FLEX_DIRECTION_ROW)\n }\n\n // Flex wrap\n if (props.flexWrap !== undefined) {\n const wrapMap: Record<string, number> = {\n nowrap: c.WRAP_NO_WRAP,\n wrap: c.WRAP_WRAP,\n \"wrap-reverse\": c.WRAP_WRAP_REVERSE,\n }\n layoutNode.setFlexWrap(wrapMap[props.flexWrap] ?? c.WRAP_NO_WRAP)\n } else if (wasRemoved(\"flexWrap\")) {\n layoutNode.setFlexWrap(c.WRAP_NO_WRAP)\n }\n\n // Alignment\n if (props.alignItems !== undefined) {\n layoutNode.setAlignItems(alignToConstant(props.alignItems))\n } else if (wasRemoved(\"alignItems\")) {\n layoutNode.setAlignItems(c.ALIGN_STRETCH)\n }\n\n if (props.alignSelf !== undefined) {\n if (props.alignSelf === \"auto\") {\n layoutNode.setAlignSelf(c.ALIGN_AUTO)\n } else {\n layoutNode.setAlignSelf(alignToConstant(props.alignSelf))\n }\n } else if (wasRemoved(\"alignSelf\")) {\n layoutNode.setAlignSelf(c.ALIGN_AUTO)\n }\n\n if (props.alignContent !== undefined) {\n layoutNode.setAlignContent(alignToConstant(props.alignContent))\n } else if (wasRemoved(\"alignContent\")) {\n layoutNode.setAlignContent(c.ALIGN_FLEX_START)\n }\n\n if (props.justifyContent !== undefined) {\n layoutNode.setJustifyContent(justifyToConstant(props.justifyContent))\n } else if (wasRemoved(\"justifyContent\")) {\n layoutNode.setJustifyContent(c.JUSTIFY_FLEX_START)\n }\n\n // Padding\n applySpacing(layoutNode, \"padding\", props)\n\n // Margin\n applySpacing(layoutNode, \"margin\", props)\n\n // Gap\n if (props.gap !== undefined) {\n layoutNode.setGap(c.GUTTER_ALL, props.gap)\n } else if (wasRemoved(\"gap\")) {\n layoutNode.setGap(c.GUTTER_ALL, 0)\n }\n\n if (props.columnGap !== undefined) {\n layoutNode.setGap(c.GUTTER_COLUMN, props.columnGap)\n } else if (wasRemoved(\"columnGap\")) {\n layoutNode.setGap(c.GUTTER_COLUMN, 0)\n }\n\n if (props.rowGap !== undefined) {\n layoutNode.setGap(c.GUTTER_ROW, props.rowGap)\n } else if (wasRemoved(\"rowGap\")) {\n layoutNode.setGap(c.GUTTER_ROW, 0)\n }\n\n // Display\n if (props.display !== undefined) {\n layoutNode.setDisplay(props.display === \"none\" ? c.DISPLAY_NONE : c.DISPLAY_FLEX)\n } else if (wasRemoved(\"display\")) {\n layoutNode.setDisplay(c.DISPLAY_FLEX)\n }\n\n // Position\n // Note: 'sticky' is handled at render-time, not by layout engine. For layout purposes, treat as relative.\n if (props.position !== undefined) {\n if (props.position === \"absolute\") {\n layoutNode.setPositionType(c.POSITION_TYPE_ABSOLUTE)\n } else if (props.position === \"static\") {\n layoutNode.setPositionType(c.POSITION_TYPE_STATIC)\n } else {\n layoutNode.setPositionType(c.POSITION_TYPE_RELATIVE)\n }\n } else if (wasRemoved(\"position\")) {\n layoutNode.setPositionType(c.POSITION_TYPE_RELATIVE)\n }\n\n // Position offsets (top, left, bottom, right)\n // Skip offsets for position=\"static\" — static positioning ignores offsets (CSS spec).\n if (props.position !== \"static\") {\n applyPositionOffset(layoutNode, c.EDGE_TOP, props.top)\n applyPositionOffset(layoutNode, c.EDGE_LEFT, props.left)\n applyPositionOffset(layoutNode, c.EDGE_BOTTOM, props.bottom)\n applyPositionOffset(layoutNode, c.EDGE_RIGHT, props.right)\n }\n\n // Aspect ratio\n if (props.aspectRatio !== undefined) {\n layoutNode.setAspectRatio(props.aspectRatio)\n } else if (wasRemoved(\"aspectRatio\")) {\n layoutNode.setAspectRatio(NaN)\n }\n\n // Overflow\n // Derive effective overflow: explicit overflow takes precedence, then per-axis (hidden if either axis is hidden)\n const effectiveOverflow =\n props.overflow ?? (props.overflowX === \"hidden\" || props.overflowY === \"hidden\" ? \"hidden\" : undefined)\n if (effectiveOverflow !== undefined) {\n if (effectiveOverflow === \"hidden\") {\n layoutNode.setOverflow(c.OVERFLOW_HIDDEN)\n } else if (effectiveOverflow === \"scroll\") {\n layoutNode.setOverflow(c.OVERFLOW_SCROLL)\n } else {\n layoutNode.setOverflow(c.OVERFLOW_VISIBLE)\n }\n } else if (wasRemoved(\"overflow\") || wasRemoved(\"overflowX\") || wasRemoved(\"overflowY\")) {\n layoutNode.setOverflow(c.OVERFLOW_VISIBLE)\n }\n\n // Border (affects layout - 1 cell per border side)\n if (props.borderStyle) {\n const borderWidth = 1\n if (props.borderTop !== false) {\n layoutNode.setBorder(c.EDGE_TOP, borderWidth)\n } else {\n layoutNode.setBorder(c.EDGE_TOP, 0)\n }\n if (props.borderBottom !== false) {\n layoutNode.setBorder(c.EDGE_BOTTOM, borderWidth)\n } else {\n layoutNode.setBorder(c.EDGE_BOTTOM, 0)\n }\n if (props.borderLeft !== false) {\n layoutNode.setBorder(c.EDGE_LEFT, borderWidth)\n } else {\n layoutNode.setBorder(c.EDGE_LEFT, 0)\n }\n if (props.borderRight !== false) {\n layoutNode.setBorder(c.EDGE_RIGHT, borderWidth)\n } else {\n layoutNode.setBorder(c.EDGE_RIGHT, 0)\n }\n } else {\n // Reset all border widths when borderStyle is removed\n layoutNode.setBorder(c.EDGE_TOP, 0)\n layoutNode.setBorder(c.EDGE_BOTTOM, 0)\n layoutNode.setBorder(c.EDGE_LEFT, 0)\n layoutNode.setBorder(c.EDGE_RIGHT, 0)\n }\n}\n\n/**\n * Apply padding or margin to a layout node.\n */\nfunction applySpacing(layoutNode: LayoutNode, type: \"padding\" | \"margin\", props: BoxProps): void {\n const c = getConstants()\n const set = type === \"padding\" ? layoutNode.setPadding.bind(layoutNode) : layoutNode.setMargin.bind(layoutNode)\n\n const all = props[type] as number | undefined\n const x = props[`${type}X` as keyof BoxProps] as number | undefined\n const yy = props[`${type}Y` as keyof BoxProps] as number | undefined\n const top = props[`${type}Top` as keyof BoxProps] as number | undefined\n const bottom = props[`${type}Bottom` as keyof BoxProps] as number | undefined\n const left = props[`${type}Left` as keyof BoxProps] as number | undefined\n const right = props[`${type}Right` as keyof BoxProps] as number | undefined\n\n // Compute effective value per edge, resolving CSS-like specificity cascade:\n // individual > axis (X/Y) > all > 0\n // This handles the case where props are REMOVED (e.g., paddingLeft: 1 → undefined):\n // the edge is reset to 0 instead of retaining the stale Yoga value.\n set(c.EDGE_TOP, top ?? yy ?? all ?? 0)\n set(c.EDGE_BOTTOM, bottom ?? yy ?? all ?? 0)\n set(c.EDGE_LEFT, left ?? x ?? all ?? 0)\n set(c.EDGE_RIGHT, right ?? x ?? all ?? 0)\n}\n\n/**\n * Apply a position offset (top/left/bottom/right) to a layout node.\n * Supports both numeric (absolute) and percentage string values.\n */\nfunction applyPositionOffset(layoutNode: LayoutNode, edge: number, value: number | string | undefined): void {\n if (value === undefined) {\n // Unset stale position offset when prop is removed on rerender\n layoutNode.setPosition(edge, NaN)\n return\n }\n if (typeof value === \"string\" && value.endsWith(\"%\")) {\n layoutNode.setPositionPercent(edge, Number.parseFloat(value))\n } else if (typeof value === \"number\") {\n layoutNode.setPosition(edge, value)\n }\n}\n\n/**\n * Convert align value to layout constant.\n */\nfunction alignToConstant(align: string): number {\n const c = getConstants()\n const map: Record<string, number> = {\n \"flex-start\": c.ALIGN_FLEX_START,\n \"flex-end\": c.ALIGN_FLEX_END,\n center: c.ALIGN_CENTER,\n stretch: c.ALIGN_STRETCH,\n baseline: c.ALIGN_BASELINE,\n \"space-between\": c.ALIGN_SPACE_BETWEEN,\n \"space-around\": c.ALIGN_SPACE_AROUND,\n \"space-evenly\": c.ALIGN_SPACE_EVENLY,\n }\n return map[align] ?? c.ALIGN_STRETCH\n}\n\n/**\n * Convert justify value to layout constant.\n */\nfunction justifyToConstant(justify: string): number {\n const c = getConstants()\n const map: Record<string, number> = {\n \"flex-start\": c.JUSTIFY_FLEX_START,\n \"flex-end\": c.JUSTIFY_FLEX_END,\n center: c.JUSTIFY_CENTER,\n \"space-between\": c.JUSTIFY_SPACE_BETWEEN,\n \"space-around\": c.JUSTIFY_SPACE_AROUND,\n \"space-evenly\": c.JUSTIFY_SPACE_EVENLY,\n }\n return map[justify] ?? c.JUSTIFY_FLEX_START\n}\n\n// ============================================================================\n// Layout Calculation\n// ============================================================================\n\n/**\n * Calculate layout for the entire tree starting from root.\n */\nexport function calculateLayout(root: AgNode, width: number, height: number): void {\n const c = getConstants()\n if (!root.layoutNode) {\n throw new Error(\"Root node must have a layout node\")\n }\n root.layoutNode.calculateLayout(width, height, c.DIRECTION_LTR)\n propagateLayout(root, 0, 0)\n notifyLayoutSubscribers(root)\n}\n\n/**\n * Propagate computed layout from layout nodes to SilveryNodes.\n */\nfunction propagateLayout(node: AgNode, parentX: number, parentY: number): void {\n // Save previous layout for change detection\n node.prevLayout = node.contentRect\n\n // Get computed layout from layout node\n if (!node.layoutNode) {\n // Virtual nodes (raw text, nested text) inherit parent layout\n return\n }\n const left = node.layoutNode.getComputedLeft()\n const top = node.layoutNode.getComputedTop()\n const width = node.layoutNode.getComputedWidth()\n const height = node.layoutNode.getComputedHeight()\n\n node.contentRect = {\n x: parentX + left,\n y: parentY + top,\n width,\n height,\n }\n\n // Clear layout dirty flag\n node.layoutDirty = false\n\n // If dimensions changed, content needs re-render\n if (!rectEqual(node.prevLayout, node.contentRect)) {\n node.contentDirty = true\n }\n\n // Recursively propagate to children\n for (const child of node.children) {\n propagateLayout(child, node.contentRect.x, node.contentRect.y)\n }\n}\n\n/**\n * Notify all layout subscribers of layout changes.\n */\nfunction notifyLayoutSubscribers(node: AgNode): void {\n if (!rectEqual(node.prevLayout, node.contentRect)) {\n for (const subscriber of node.layoutSubscribers) {\n subscriber()\n }\n }\n\n for (const child of node.children) {\n notifyLayoutSubscribers(child)\n }\n}\n",
76
+ "/**\n * React Reconciler Host Config\n *\n * Defines how React creates, updates, and manages SilveryNodes.\n * This is the bridge between React's reconciliation algorithm\n * and our custom terminal node tree.\n */\n\nimport { createContext } from \"react\"\nimport { DefaultEventPriority, DiscreteEventPriority, NoEventPriority } from \"react-reconciler/constants.js\"\nimport type { BoxProps, AgNode, AgNodeType, TextProps } from \"@silvery/ag/types\"\nimport { contentPropsChanged, layoutPropsChanged, propsEqual } from \"./helpers\"\nimport { applyBoxProps, createNode, createVirtualTextNode } from \"./nodes\"\n\n/**\n * Normalize Ink intrinsic element types to Silvery equivalents.\n * Ink uses `ink-box` / `ink-text` as intrinsic element names;\n * Silvery uses `silvery-box` / `silvery-text`.\n */\nfunction normalizeNodeType(type: string): AgNodeType {\n if (type === \"ink-box\") return \"silvery-box\"\n if (type === \"ink-text\") return \"silvery-text\"\n return type as AgNodeType\n}\n\n// ============================================================================\n// Node Removal Hook\n// ============================================================================\n\n/**\n * Callback invoked when a node is removed from the tree.\n * Used by the app layer to coordinate focus cleanup — if the focused element\n * is within a removed subtree, focus must be cleared to prevent dangling\n * references and broken navigation (indexOf → -1, hasFocusWithin lies).\n */\nlet onNodeRemovedCallback: ((removedNode: AgNode) => void) | null = null\n\n/**\n * Register a callback to be called when any node is removed from the tree.\n * Returns a cleanup function to unregister. Only one callback at a time.\n */\nexport function setOnNodeRemoved(callback: ((removedNode: AgNode) => void) | null): void {\n onNodeRemovedCallback = callback\n}\n\n// ============================================================================\n// Subtree Dirty Propagation\n// ============================================================================\n\n/**\n * Mark this node and all ancestors as having dirty content/layout.\n * Used to enable fast-path subtree skipping in contentPhase.\n */\nfunction markSubtreeDirty(node: AgNode | null): void {\n while (node && !node.subtreeDirty) {\n node.subtreeDirty = true\n node = node.parent\n }\n}\n\n/**\n * When a child change (append/remove/insert/text-update) occurs inside a\n * virtual text subtree (no layoutNode), the nearest layout ancestor must be\n * notified so its measure function re-collects descendant text and the layout\n * engine recalculates dimensions. Without this, the measure cache stays stale\n * and contentPhase renders at the wrong size / doesn't clear old content.\n *\n * No-op when the node already has a layoutNode (normal path handles it).\n */\nfunction markLayoutAncestorDirty(node: AgNode): void {\n if (node.layoutNode) return\n let ancestor: AgNode | null = node.parent\n while (ancestor && !ancestor.layoutNode) {\n ancestor = ancestor.parent\n }\n if (ancestor?.layoutNode) {\n ancestor.contentDirty = true\n ancestor.stylePropsDirty = true\n ancestor.layoutDirty = true\n ancestor.layoutNode.markDirty()\n }\n}\n\n// ============================================================================\n// Dev Warnings\n// ============================================================================\n\n/**\n * Track whether we've already warned about Box-inside-Text\n * to avoid spamming the console on every re-render.\n */\nlet hasWarnedBoxInsideText = false\n\n/** Reset the warning flag (for testing). */\nexport function _resetBoxInsideTextWarning(): void {\n hasWarnedBoxInsideText = false\n}\n\n/**\n * Ink-compatible strict validation mode.\n * When enabled, the reconciler throws errors instead of warnings for:\n * - Raw text directly inside a Box (must be inside Text)\n * - Box nested inside Text\n */\nlet inkStrictValidation = false\n\n/** Enable/disable Ink-compatible strict validation. */\nexport function setInkStrictValidation(enabled: boolean): void {\n inkStrictValidation = enabled\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Container type - the root of our Silvery tree\n */\nexport interface Container {\n root: AgNode\n onRender: () => void\n}\n\n/**\n * Host context tracks whether we're inside a Text component\n */\ninterface HostContext {\n isInsideText: boolean\n}\n\n// ============================================================================\n// Update Priority Management (for react-reconciler 0.33+)\n// ============================================================================\n\nlet currentUpdatePriority = NoEventPriority\n\n/**\n * Run a callback with DiscreteEventPriority so React treats state\n * updates inside it as user-interaction priority (synchronous commit).\n * Use this for keyboard input handling to prevent React's concurrent\n * scheduler from deferring the commit.\n */\nexport function runWithDiscreteEvent(fn: () => void): void {\n const prev = currentUpdatePriority\n currentUpdatePriority = DiscreteEventPriority\n try {\n fn()\n } finally {\n currentUpdatePriority = prev\n }\n}\n\n// ============================================================================\n// Host Config\n// ============================================================================\n\n/**\n * The React Reconciler host config.\n * This defines how React creates, updates, and manages our custom SilveryNodes.\n */\nexport const hostConfig = {\n // Renderer identity (used by React DevTools to identify this renderer)\n rendererPackageName: \"@silvery/ag-react\",\n rendererVersion: \"0.0.1\",\n\n // Feature flags\n supportsMutation: true,\n supportsPersistence: false,\n supportsHydration: false,\n isPrimaryRenderer: true,\n\n // Scheduling\n scheduleTimeout: setTimeout,\n cancelTimeout: clearTimeout,\n noTimeout: -1,\n supportsMicrotasks: true,\n scheduleMicrotask: queueMicrotask,\n\n // Context - tracks whether we're inside a Text component\n getRootHostContext(): HostContext {\n return { isInsideText: false }\n },\n\n getChildHostContext(parentHostContext: HostContext, type: AgNodeType): HostContext {\n // Normalize Ink intrinsic types (ink-box → silvery-box, ink-text → silvery-text)\n const normalizedType = normalizeNodeType(type)\n // Once inside a text node, stay inside\n const isInsideText = parentHostContext.isInsideText || normalizedType === \"silvery-text\"\n if (isInsideText === parentHostContext.isInsideText) {\n return parentHostContext\n }\n return { isInsideText }\n },\n\n // Instance creation\n createInstance(\n type: AgNodeType,\n props: BoxProps | TextProps,\n _rootContainer: unknown,\n hostContext: HostContext,\n ): AgNode {\n // Normalize Ink intrinsic types (ink-box → silvery-box, ink-text → silvery-text)\n type = normalizeNodeType(type)\n // Ink-compat: flatten `style` prop from intrinsic ink-box/ink-text elements.\n // Ink's intrinsic elements use `<ink-box style={{marginLeft: 1}}>` where the\n // style object contains layout props. Silvery expects them as top-level props.\n if (\"style\" in props && props.style && typeof props.style === \"object\") {\n props = { ...props.style, ...props } as BoxProps | TextProps\n }\n // Ink-compat: throw when a Box is nested inside a Text\n if (type === \"silvery-box\" && hostContext.isInsideText) {\n if (inkStrictValidation) {\n throw new Error(\"<Box> can\\u2019t be nested inside <Text> component\")\n }\n if (process.env.NODE_ENV !== \"production\" && !hasWarnedBoxInsideText) {\n hasWarnedBoxInsideText = true\n console.warn(\"Warning: <Box> cannot be nested inside <Text>. This produces undefined layout behavior.\")\n }\n }\n\n // Nested text nodes become \"virtual\" - no layout node\n if (type === \"silvery-text\" && hostContext.isInsideText) {\n return createVirtualTextNode(props as TextProps)\n }\n return createNode(type, props)\n },\n\n createTextInstance(text: string, _rootContainer: unknown, hostContext: HostContext): AgNode {\n // Ink-compat: throw when text appears directly in a Box (outside Text)\n if (inkStrictValidation && !hostContext.isInsideText && text.trim().length > 0) {\n throw new Error(`Text string \"${text}\" must be rendered inside <Text> component`)\n }\n // Raw text nodes don't have layout nodes - they're just data nodes\n // Their content is rendered by their parent silvery-text element\n const node: AgNode = {\n type: \"silvery-text\",\n props: { children: text } as TextProps,\n children: [],\n parent: null,\n layoutNode: null, // No layout node for raw text\n contentRect: null,\n screenRect: null,\n renderRect: null,\n prevLayout: null,\n prevScreenRect: null,\n prevRenderRect: null,\n layoutChangedThisFrame: false,\n layoutDirty: false,\n contentDirty: true,\n stylePropsDirty: true,\n bgDirty: true,\n subtreeDirty: true,\n childrenDirty: false,\n layoutSubscribers: new Set(),\n textContent: text,\n isRawText: true,\n }\n return node\n },\n\n // Tree operations\n appendChild(parentInstance: AgNode, child: AgNode) {\n // React calls appendChild to move an existing child during keyed reorder.\n // Remove from old position first to avoid duplicating in the children array.\n const existingIndex = parentInstance.children.indexOf(child)\n if (existingIndex !== -1) {\n parentInstance.children.splice(existingIndex, 1)\n if (parentInstance.layoutNode && child.layoutNode) {\n parentInstance.layoutNode.removeChild(child.layoutNode)\n }\n }\n child.parent = parentInstance\n parentInstance.children.push(child)\n // Only add to layout tree if both nodes have layout nodes\n if (parentInstance.layoutNode && child.layoutNode) {\n // Count non-raw-text children for proper layout index\n const layoutIndex = parentInstance.children.filter((c) => c.layoutNode !== null).length - 1\n parentInstance.layoutNode.insertChild(child.layoutNode, layoutIndex)\n }\n parentInstance.childrenDirty = true\n parentInstance.contentDirty = true // Text measure cache must re-collect children\n parentInstance.layoutDirty = true\n parentInstance.layoutNode?.markDirty()\n markLayoutAncestorDirty(parentInstance)\n markSubtreeDirty(parentInstance)\n },\n\n appendInitialChild(parentInstance: AgNode, child: AgNode) {\n child.parent = parentInstance\n parentInstance.children.push(child)\n // Only add to layout tree if both nodes have layout nodes\n if (parentInstance.layoutNode && child.layoutNode) {\n const layoutIndex = parentInstance.children.filter((c) => c.layoutNode !== null).length - 1\n parentInstance.layoutNode.insertChild(child.layoutNode, layoutIndex)\n }\n },\n\n appendChildToContainer(container: Container, child: AgNode) {\n // Remove from old position if already a child (keyed reorder)\n const existingIndex = container.root.children.indexOf(child)\n if (existingIndex !== -1) {\n container.root.children.splice(existingIndex, 1)\n if (container.root.layoutNode && child.layoutNode) {\n container.root.layoutNode.removeChild(child.layoutNode)\n }\n }\n child.parent = container.root\n container.root.children.push(child)\n if (container.root.layoutNode && child.layoutNode) {\n const layoutIndex = container.root.children.filter((c) => c.layoutNode !== null).length - 1\n container.root.layoutNode.insertChild(child.layoutNode, layoutIndex)\n }\n container.root.childrenDirty = true\n container.root.contentDirty = true // Text measure cache must re-collect children\n container.root.layoutDirty = true\n container.root.layoutNode?.markDirty()\n markSubtreeDirty(container.root)\n },\n\n removeChild(parentInstance: AgNode, child: AgNode) {\n const index = parentInstance.children.indexOf(child)\n if (index !== -1) {\n // Notify focus manager before detaching (needs parent chain intact for subtree check)\n onNodeRemovedCallback?.(child)\n parentInstance.children.splice(index, 1)\n if (parentInstance.layoutNode && child.layoutNode) {\n parentInstance.layoutNode.removeChild(child.layoutNode)\n child.layoutNode.free()\n }\n child.parent = null\n parentInstance.childrenDirty = true\n parentInstance.contentDirty = true // Text measure cache must re-collect children\n parentInstance.layoutDirty = true\n parentInstance.layoutNode?.markDirty()\n markLayoutAncestorDirty(parentInstance)\n markSubtreeDirty(parentInstance)\n }\n },\n\n removeChildFromContainer(container: Container, child: AgNode) {\n const index = container.root.children.indexOf(child)\n if (index !== -1) {\n // Notify focus manager before detaching\n onNodeRemovedCallback?.(child)\n container.root.children.splice(index, 1)\n if (container.root.layoutNode && child.layoutNode) {\n container.root.layoutNode.removeChild(child.layoutNode)\n child.layoutNode.free()\n }\n child.parent = null\n container.root.childrenDirty = true\n container.root.contentDirty = true // Text measure cache must re-collect children\n container.root.layoutDirty = true\n container.root.layoutNode?.markDirty()\n markSubtreeDirty(container.root)\n }\n },\n\n insertBefore(parentInstance: AgNode, child: AgNode, beforeChild: AgNode) {\n // React calls insertBefore to move an existing child during keyed reorder.\n // Remove from old position first to avoid duplicating in the children array.\n const existingIndex = parentInstance.children.indexOf(child)\n if (existingIndex !== -1) {\n parentInstance.children.splice(existingIndex, 1)\n if (parentInstance.layoutNode && child.layoutNode) {\n parentInstance.layoutNode.removeChild(child.layoutNode)\n }\n }\n const beforeIndex = parentInstance.children.indexOf(beforeChild)\n if (beforeIndex !== -1) {\n child.parent = parentInstance\n parentInstance.children.splice(beforeIndex, 0, child)\n if (parentInstance.layoutNode && child.layoutNode) {\n // Count non-raw-text children before this position for proper layout index\n const layoutIndex = parentInstance.children.slice(0, beforeIndex).filter((c) => c.layoutNode !== null).length\n parentInstance.layoutNode.insertChild(child.layoutNode, layoutIndex)\n }\n parentInstance.childrenDirty = true\n parentInstance.contentDirty = true // Text measure cache must re-collect children\n parentInstance.layoutDirty = true\n parentInstance.layoutNode?.markDirty()\n markLayoutAncestorDirty(parentInstance)\n markSubtreeDirty(parentInstance)\n }\n },\n\n insertInContainerBefore(container: Container, child: AgNode, beforeChild: AgNode) {\n // Remove from old position if already a child (keyed reorder)\n const existingIndex = container.root.children.indexOf(child)\n if (existingIndex !== -1) {\n container.root.children.splice(existingIndex, 1)\n if (container.root.layoutNode && child.layoutNode) {\n container.root.layoutNode.removeChild(child.layoutNode)\n }\n }\n const beforeIndex = container.root.children.indexOf(beforeChild)\n if (beforeIndex !== -1) {\n child.parent = container.root\n container.root.children.splice(beforeIndex, 0, child)\n if (container.root.layoutNode && child.layoutNode) {\n const layoutIndex = container.root.children.slice(0, beforeIndex).filter((c) => c.layoutNode !== null).length\n container.root.layoutNode.insertChild(child.layoutNode, layoutIndex)\n }\n container.root.childrenDirty = true\n container.root.contentDirty = true // Text measure cache must re-collect children\n container.root.layoutDirty = true\n container.root.layoutNode?.markDirty()\n markSubtreeDirty(container.root)\n }\n },\n\n // Updates\n prepareUpdate(\n _instance: AgNode,\n _type: AgNodeType,\n oldProps: BoxProps | TextProps,\n newProps: BoxProps | TextProps,\n ): boolean | null {\n // Return true if we need to update\n return !propsEqual(oldProps as Record<string, unknown>, newProps as Record<string, unknown>)\n },\n\n // Note: react-reconciler 0.33+ changed the signature from\n // commitUpdate(instance, updatePayload, type, oldProps, newProps) to\n // commitUpdate(instance, type, oldProps, newProps, finishedWork)\n commitUpdate(\n instance: AgNode,\n _type: AgNodeType,\n oldProps: BoxProps | TextProps,\n newProps: BoxProps | TextProps,\n _finishedWork: unknown,\n ) {\n // Ink-compat: flatten `style` prop from intrinsic ink-box/ink-text elements\n if (\"style\" in oldProps && oldProps.style && typeof oldProps.style === \"object\") {\n oldProps = { ...oldProps.style, ...oldProps } as BoxProps | TextProps\n }\n if (\"style\" in newProps && newProps.style && typeof newProps.style === \"object\") {\n newProps = { ...newProps.style, ...newProps } as BoxProps | TextProps\n }\n // Early exit if props are equal (React may call commitUpdate even when nothing changed)\n if (propsEqual(oldProps as Record<string, unknown>, newProps as Record<string, unknown>)) {\n instance.props = newProps\n return\n }\n\n // Check if layout-affecting props changed\n if (layoutPropsChanged(oldProps as Record<string, unknown>, newProps as Record<string, unknown>)) {\n if (instance.layoutNode) {\n applyBoxProps(instance.layoutNode, newProps as BoxProps, oldProps as BoxProps)\n instance.layoutNode.markDirty()\n }\n instance.layoutDirty = true\n }\n\n // Check if content changed (text children, style props like backgroundColor)\n // Returns \"text\" for text content changes (affect layout) or \"style\" for\n // style-only changes (borderColor, color, etc. — don't affect layout).\n const contentChanged = contentPropsChanged(oldProps as Record<string, unknown>, newProps as Record<string, unknown>)\n if (contentChanged) {\n // stylePropsDirty: always set for any visual change. Content phase uses this\n // to know the node needs re-rendering (border, text style, bg, etc.).\n instance.stylePropsDirty = true\n // contentDirty: only for text content changes (not style-only changes).\n // Style-only changes (borderColor, color, bold) set stylePropsDirty but NOT\n // contentDirty, so content phase won't cascade to children for border-only\n // changes where the content area is unchanged.\n if (contentChanged === \"text\") {\n instance.contentDirty = true\n if (instance.layoutNode) {\n instance.layoutNode.markDirty()\n }\n }\n // bgDirty: specifically track backgroundColor changes (added/changed/removed).\n // Content phase uses this to cascade re-renders only when the content area\n // was actually affected (not for border-only paint changes).\n if (\n (oldProps as Record<string, unknown>).backgroundColor !== (newProps as Record<string, unknown>).backgroundColor\n ) {\n instance.bgDirty = true\n }\n // Border removal: when borderStyle goes from truthy to falsy, stale border\n // characters (╭╮╰╯│─) persist in the cloned buffer because renderBox doesn't\n // draw anything at those positions. Setting bgDirty makes contentAreaAffected\n // true, triggering clearNodeRegion to fill the area with inherited bg.\n // Border *addition* doesn't need this — renderBorder overwrites the old cells.\n if ((oldProps as Record<string, unknown>).borderStyle && !(newProps as Record<string, unknown>).borderStyle) {\n instance.bgDirty = true\n }\n // Outline removal: same issue — stale outline characters persist in the clone.\n if ((oldProps as Record<string, unknown>).outlineStyle && !(newProps as Record<string, unknown>).outlineStyle) {\n instance.bgDirty = true\n }\n // Theme change: all descendants need re-rendering with new token values.\n // bgDirty makes contentAreaAffected=true, cascading childrenNeedFreshRender\n // to force children to re-render with the new theme context.\n if ((oldProps as Record<string, unknown>).theme !== (newProps as Record<string, unknown>).theme) {\n instance.bgDirty = true\n }\n }\n\n instance.props = newProps\n\n // Only mark subtree/ancestor dirty when visual changes were detected.\n // Data attributes (data-*), event handlers, and other non-visual props\n // don't affect rendering, so propagating dirty flags wastes content phase\n // time traversing unchanged subtrees.\n //\n // scrollTo/scrollOffset changes affect rendering via scroll phase (children\n // shift position), so they must propagate subtreeDirty for content phase\n // traversal. Without this, the content phase fast-path skips ancestors of\n // the scroll container, never reaching the container to re-render at the\n // new scroll position.\n const scrollToChanged =\n (oldProps as Record<string, unknown>).scrollTo !== (newProps as Record<string, unknown>).scrollTo\n const scrollOffsetChanged =\n (oldProps as Record<string, unknown>).scrollOffset !== (newProps as Record<string, unknown>).scrollOffset\n if (instance.layoutDirty || contentChanged || scrollToChanged || scrollOffsetChanged) {\n markLayoutAncestorDirty(instance)\n markSubtreeDirty(instance)\n }\n },\n\n commitTextUpdate(textInstance: AgNode, _oldText: string, newText: string) {\n textInstance.textContent = newText\n textInstance.props = { children: newText } as TextProps\n textInstance.contentDirty = true\n textInstance.stylePropsDirty = true\n // Text content change affects layout (measure function will return different size)\n // Walk up to the nearest layout ancestor so its measure cache is invalidated\n markLayoutAncestorDirty(textInstance)\n markSubtreeDirty(textInstance)\n },\n\n // Finalization\n finalizeInitialChildren() {\n return false\n },\n\n prepareForCommit() {\n return null\n },\n\n resetAfterCommit(container: Container) {\n // Trigger render after React finishes committing\n container.onRender()\n },\n\n // Misc\n getPublicInstance(instance: AgNode) {\n return instance\n },\n\n shouldSetTextContent() {\n return false\n },\n\n clearContainer(container: Container) {\n // Notify focus manager before clearing — any child subtree may contain focus\n for (const child of container.root.children) {\n onNodeRemovedCallback?.(child)\n }\n for (const child of container.root.children) {\n if (container.root.layoutNode && child.layoutNode) {\n container.root.layoutNode.removeChild(child.layoutNode)\n child.layoutNode.free()\n }\n }\n container.root.children = []\n // Must invalidate dirty flags — same as removeChildFromContainer.\n // Without this, the pipeline can skip re-rendering after a root clear,\n // leaving stale buffer content (tree/buffer mismatch).\n container.root.childrenDirty = true\n container.root.contentDirty = true\n container.root.layoutDirty = true\n container.root.layoutNode?.markDirty()\n markSubtreeDirty(container.root)\n },\n\n preparePortalMount() {\n // No-op for terminal\n },\n\n getCurrentEventPriority() {\n if (currentUpdatePriority !== NoEventPriority) {\n return currentUpdatePriority\n }\n return DefaultEventPriority\n },\n\n getInstanceFromNode() {\n return null\n },\n\n beforeActiveInstanceBlur() {\n // No-op\n },\n\n afterActiveInstanceBlur() {\n // No-op\n },\n\n prepareScopeUpdate() {\n // No-op\n },\n\n getInstanceFromScope() {\n return null\n },\n\n detachDeletedInstance() {\n // No-op\n },\n\n // React 19 / react-reconciler 0.33+ required methods\n setCurrentUpdatePriority(newPriority: number) {\n currentUpdatePriority = newPriority\n },\n\n getCurrentUpdatePriority() {\n return currentUpdatePriority\n },\n\n resolveUpdatePriority() {\n if (currentUpdatePriority !== NoEventPriority) {\n return currentUpdatePriority\n }\n return DefaultEventPriority\n },\n\n maySuspendCommit() {\n return false\n },\n\n NotPendingTransition: null,\n HostTransitionContext: createContext(null),\n\n resetFormInstance() {\n // No-op\n },\n\n requestPostPaintCallback() {\n // No-op\n },\n\n shouldAttemptEagerTransition() {\n return false\n },\n\n trackSchedulerEvent() {\n // No-op\n },\n\n resolveEventType() {\n return null\n },\n\n resolveEventTimeStamp() {\n return -1.1\n },\n\n preloadInstance() {\n return true\n },\n\n startSuspendingCommit() {\n // No-op\n },\n\n suspendInstance() {\n // No-op\n },\n\n waitForCommitToBeReady() {\n return null\n },\n\n // ========================================================================\n // Suspense Support (hide/unhide)\n // ========================================================================\n\n /**\n * Hide an instance during Suspense.\n * Called when React needs to hide content while showing a fallback.\n *\n * Must set stylePropsDirty (content phase fast-path skip includes stylePropsDirty check),\n * layoutDirty + layoutNode.markDirty() (hiding changes measured content — the\n * layout engine must recalculate dimensions), and markLayoutAncestorDirty\n * (virtual text nodes without layoutNode need the nearest layout ancestor dirty).\n */\n hideInstance(instance: AgNode) {\n instance.hidden = true\n instance.contentDirty = true\n instance.stylePropsDirty = true\n instance.layoutDirty = true\n if (instance.layoutNode) {\n instance.layoutNode.markDirty()\n }\n // Mark parent dirty to trigger re-render\n if (instance.parent) {\n instance.parent.contentDirty = true\n }\n markLayoutAncestorDirty(instance)\n markSubtreeDirty(instance)\n },\n\n /**\n * Unhide an instance after Suspense resolves.\n * Called when the suspended content is ready to show.\n *\n * Same invalidation as hideInstance — the node's visibility change affects\n * layout (measured content changes) and paint (content must be re-rendered).\n */\n unhideInstance(instance: AgNode, _props: BoxProps | TextProps) {\n instance.hidden = false\n instance.contentDirty = true\n instance.stylePropsDirty = true\n instance.layoutDirty = true\n if (instance.layoutNode) {\n instance.layoutNode.markDirty()\n }\n // Mark parent dirty to trigger re-render\n if (instance.parent) {\n instance.parent.contentDirty = true\n }\n markLayoutAncestorDirty(instance)\n markSubtreeDirty(instance)\n },\n\n /**\n * Hide a text instance during Suspense.\n *\n * Text instances don't have layout nodes. markLayoutAncestorDirty walks up\n * to the nearest layout ancestor and marks it dirty so the measure function\n * re-collects descendant text (collectNodeTextContent skips hidden children).\n */\n hideTextInstance(textInstance: AgNode) {\n textInstance.hidden = true\n textInstance.contentDirty = true\n textInstance.stylePropsDirty = true\n if (textInstance.parent) {\n textInstance.parent.contentDirty = true\n }\n markLayoutAncestorDirty(textInstance)\n markSubtreeDirty(textInstance)\n },\n\n /**\n * Unhide a text instance after Suspense resolves.\n *\n * Same invalidation as hideTextInstance — the text content changes when\n * hidden children become visible again.\n */\n unhideTextInstance(textInstance: AgNode, _text: string) {\n textInstance.hidden = false\n textInstance.contentDirty = true\n textInstance.stylePropsDirty = true\n if (textInstance.parent) {\n textInstance.parent.contentDirty = true\n }\n markLayoutAncestorDirty(textInstance)\n markSubtreeDirty(textInstance)\n },\n}\n",
77
+ "/**\n * Silvery React Reconciler\n *\n * Custom React reconciler that builds a tree of SilveryNodes, each with a Yoga layout node.\n * This is the core of Silvery's architecture - separating structure (React reconciliation)\n * from content (terminal rendering).\n *\n * The reconciler creates SilveryNodes during React's reconciliation phase,\n * but actual terminal content is rendered later after Yoga computes layout.\n */\n\n// @ts-expect-error - react-reconciler has no type declarations\nimport Reconciler from \"react-reconciler\"\nimport type { AgNode } from \"@silvery/ag/types\"\nimport { type Container, hostConfig } from \"./host-config\"\nimport { createRootNode } from \"./nodes\"\n\n// Re-export only what's needed by render.tsx and testing/index.tsx\nexport type { Container } from \"./host-config\"\nexport {\n runWithDiscreteEvent,\n _resetBoxInsideTextWarning,\n setInkStrictValidation,\n setOnNodeRemoved,\n} from \"./host-config\"\n\n// ============================================================================\n// Reconciler Export\n// ============================================================================\n\n/**\n * Create the React reconciler instance.\n */\nexport const reconciler = Reconciler(hostConfig)\n\n/**\n * Create a container for rendering.\n */\nexport function createContainer(onRender: () => void): Container {\n const root = createRootNode()\n return { root, onRender }\n}\n\n/**\n * Create a React fiber root for a container (wraps the 10-argument reconciler call).\n */\nexport function createFiberRoot(container: Container) {\n return reconciler.createContainer(\n container,\n 1, // ConcurrentRoot\n null, // hydrationCallbacks\n false, // isStrictMode\n null, // concurrentUpdatesByDefaultOverride\n \"\", // identifierPrefix\n () => {}, // onUncaughtError\n () => {}, // onCaughtError\n () => {}, // onRecoverableError\n null, // onDefaultTransitionIndicator\n )\n}\n\n/**\n * Get the root SilveryNode from a container.\n */\nexport function getContainerRoot(container: Container): AgNode {\n return container.root\n}\n",
78
+ "/**\n * Separate React reconciler instance for renderStringSync.\n *\n * renderStringSync may be called from within React effects (e.g., useScrollback\n * freezing items to scrollback). If it uses the same reconciler singleton as the\n * main render tree, this causes re-entrancy: the nested reconciliation interferes\n * with the outer one, producing empty output.\n *\n * By using a dedicated reconciler instance, renderStringSync operates on an\n * independent fiber tree with no shared reconciler state.\n */\n\n// @ts-expect-error - react-reconciler has no type declarations\nimport Reconciler from \"react-reconciler\"\nimport { hostConfig } from \"./host-config\"\n\n/**\n * Dedicated reconciler for string rendering.\n *\n * Uses the same host config functions but overrides isPrimaryRenderer to false,\n * since this is a secondary renderer used only for one-shot string rendering.\n * This avoids conflicts with the main reconciler's hook ownership.\n */\nexport const stringReconciler = Reconciler({\n ...hostConfig,\n isPrimaryRenderer: false,\n})\n",
79
+ "/**\n * renderString - Static one-shot rendering to string\n *\n * Renders a React element to a string without needing a terminal.\n * Use for:\n * - CI output (no cursor control needed)\n * - Piped output\n * - One-shot reports/summaries\n * - Testing component output\n *\n * @example\n * ```tsx\n * import { renderString, Box, Text } from '@silvery/ag-react'\n *\n * // Basic usage\n * const output = renderString(<Summary stats={stats} />)\n * console.log(output)\n *\n * // Custom width\n * const wide = renderString(<Report />, { width: 120 })\n *\n * // Plain text (no ANSI)\n * const plain = renderString(<Report />, { plain: true })\n * ```\n */\n\nimport React, { type ReactElement, act } from \"react\"\n\nimport { createTerm } from \"@silvery/ag-term/ansi\"\n\nimport { bufferToStyledText, bufferToText, type TerminalBuffer } from \"@silvery/ag-term/buffer\"\nimport { StdoutContext, StderrContext, TermContext } from \"./context\"\nimport { isLayoutEngineInitialized } from \"@silvery/ag-term/layout-engine\"\nimport { executeRender, type PipelineConfig } from \"@silvery/ag-term/pipeline\"\nimport { createContainer, getContainerRoot } from \"./reconciler\"\nimport { stringReconciler } from \"./reconciler/string-reconciler\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Options for renderString().\n */\nexport interface RenderStringOptions {\n /**\n * Width in columns for layout calculations.\n * Default: 80\n */\n width?: number\n\n /**\n * Height in rows for layout calculations.\n * Default: 24\n */\n height?: number\n\n /**\n * Strip ANSI codes for plain text output.\n * Default: false (includes ANSI styling)\n */\n plain?: boolean\n\n /**\n * Pipeline configuration (scoped width measurer + output phase).\n * When provided, the render pipeline uses these for width measurement\n * and output generation instead of the global defaults.\n */\n pipelineConfig?: PipelineConfig\n\n /**\n * Trim trailing whitespace from each line.\n * Default: true (trims trailing spaces)\n *\n * Set to false when rendering to stdout where trailing spaces are\n * semantically significant (e.g., ink compat static renders).\n */\n trimTrailingWhitespace?: boolean\n\n /**\n * Trim trailing empty lines from the output.\n * Default: true (removes trailing empty lines)\n *\n * Set to false when padding/margin at the bottom should be preserved\n * (e.g., ink compat renders where `\\n\\n` padding is significant).\n */\n trimEmptyLines?: boolean\n\n /**\n * Callback to receive the computed content height (root node layout height).\n * Useful for callers that need to know the actual content bounds\n * (e.g., Ink compat layer needs to trim buffer padding but preserve\n * content-area empty lines).\n */\n onContentHeight?: (height: number) => void\n\n /**\n * Always use styled output (bufferToStyledText) even when plain=true.\n * Default: false\n *\n * When true, the output includes ANSI codes from embedded sequences in text\n * content (SGR, OSC hyperlinks) even though the mock term has no color\n * support (plain=true). This is needed for Ink compatibility: Ink passes\n * embedded ANSI sequences through regardless of chalk's color level, but\n * silvery's plain mode would strip them via bufferToText.\n *\n * The `plain` flag still controls whether the mock term has color support,\n * which determines whether Text component style props (color, bold, etc.)\n * produce style attributes in the buffer.\n */\n alwaysStyled?: boolean\n}\n\n// ============================================================================\n// Module State\n// ============================================================================\n\n// Track if we've initialized to avoid redundant imports\nlet engineInitialized = false\n\nasync function ensureLayoutEngine(): Promise<void> {\n if (engineInitialized || isLayoutEngineInitialized()) {\n return\n }\n // Use centralized default engine initialization\n const { ensureDefaultLayoutEngine } = await import(\"@silvery/ag-term/layout-engine\")\n await ensureDefaultLayoutEngine()\n engineInitialized = true\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Render a React element to a string (async version).\n *\n * Automatically initializes the layout engine if needed.\n * Use this when you're not sure if the layout engine is ready.\n *\n * @param element - React element to render\n * @param options - Render options (width, height, plain)\n * @returns Rendered string (with or without ANSI codes)\n *\n * @example\n * ```tsx\n * const output = await renderString(<Summary stats={stats} />)\n * console.log(output)\n * ```\n */\nexport async function renderString(element: ReactElement, options: RenderStringOptions = {}): Promise<string> {\n await ensureLayoutEngine()\n return renderStringSync(element, options)\n}\n\n/**\n * Render a React element to a string (sync version).\n *\n * Requires the layout engine to be already initialized.\n * Throws if the layout engine is not ready.\n *\n * @param element - React element to render\n * @param options - Render options (width, height, plain)\n * @returns Rendered string (with or without ANSI codes)\n *\n * @example\n * ```tsx\n * // After layout engine is initialized\n * const output = renderStringSync(<Summary stats={stats} />)\n * console.log(output)\n * ```\n */\nexport function renderStringSync(element: ReactElement, options: RenderStringOptions = {}): string {\n if (!isLayoutEngineInitialized()) {\n throw new Error(\"Layout engine not initialized. Use renderString() (async) or initialize with setLayoutEngine().\")\n }\n\n const {\n width = 80,\n height = 24,\n plain = false,\n pipelineConfig,\n trimTrailingWhitespace = true,\n trimEmptyLines = true,\n onContentHeight,\n alwaysStyled = false,\n } = options\n\n // Track whether React committed new work (from layout notifications etc.)\n let hadReactCommit = false\n const container = createContainer(() => {\n hadReactCommit = true\n })\n\n // Capture uncaught errors from the reconciler so they propagate to the caller\n let uncaughtError: unknown = null\n const onUncaughtError = (error: unknown) => {\n uncaughtError = error\n }\n\n // Create fiber root using the dedicated string reconciler (not the main one)\n const fiberRoot = stringReconciler.createContainer(\n container,\n 1, // ConcurrentRoot\n null, // hydrationCallbacks\n false, // isStrictMode\n null, // concurrentUpdatesByDefaultOverride\n \"\", // identifierPrefix\n onUncaughtError, // onUncaughtError\n () => {}, // onCaughtError\n () => {}, // onRecoverableError\n null, // onDefaultTransitionIndicator\n )\n\n // Create minimal mock stdout for components that use useStdout\n const mockStdout = {\n columns: width,\n rows: height,\n write: () => true,\n isTTY: false,\n on: () => mockStdout,\n off: () => mockStdout,\n once: () => mockStdout,\n removeListener: () => mockStdout,\n addListener: () => mockStdout,\n } as unknown as NodeJS.WriteStream\n\n // Create mock term for components that use useTerm()\n const mockTerm = createTerm({ color: plain ? null : \"truecolor\" })\n\n // Wrap with minimal contexts (no input handling needed)\n const wrapped = React.createElement(\n TermContext.Provider,\n { value: mockTerm },\n React.createElement(\n StdoutContext.Provider,\n {\n value: {\n stdout: mockStdout,\n write: () => {},\n },\n },\n React.createElement(\n StderrContext.Provider,\n {\n value: {\n stderr: process.stderr,\n write: (data: string) => {\n process.stderr.write(data)\n },\n },\n },\n element,\n ),\n ),\n )\n\n // Mount the React tree inside act() so layout feedback works\n withActEnvironment(() => {\n act(() => {\n stringReconciler.updateContainerSync(wrapped, fiberRoot, null, null)\n stringReconciler.flushSyncWork()\n })\n })\n\n // Propagate any uncaught render errors (e.g., text outside <Text> in strict mode)\n if (uncaughtError) {\n throw uncaughtError instanceof Error ? uncaughtError : new Error(String(uncaughtError))\n }\n\n // Layout stabilization loop: run the pipeline, flush React work from\n // layout notifications (useContentRect forceUpdate etc.), repeat until stable.\n // This matches the test renderer's multi-pass approach.\n let buffer!: TerminalBuffer\n let rootNode: ReturnType<typeof getContainerRoot> | undefined\n const MAX_ITERATIONS = 5\n for (let i = 0; i < MAX_ITERATIONS; i++) {\n hadReactCommit = false\n withActEnvironment(() => {\n act(() => {\n const root = getContainerRoot(container)\n rootNode = root\n const result = executeRender(root, width, height, null, undefined, pipelineConfig)\n buffer = result.buffer\n })\n if (!hadReactCommit) {\n act(() => {\n stringReconciler.flushSyncWork()\n })\n }\n })\n if (!hadReactCommit) break\n }\n\n // Report content height if callback provided.\n // Compute from children's outer bottom edges (including margins) rather than\n // root.contentRect.height which equals the buffer height (root stretches to fill).\n if (onContentHeight && rootNode) {\n let maxBottom = 0\n let hasChildren = false\n for (const child of rootNode.children) {\n if (child.contentRect) {\n hasChildren = true\n const props = child.props as Record<string, unknown>\n const mb = (props.marginBottom as number) ?? (props.marginY as number) ?? (props.margin as number) ?? 0\n const childBottom = child.contentRect.y + child.contentRect.height + mb\n if (childBottom > maxBottom) maxBottom = childBottom\n }\n }\n onContentHeight(hasChildren ? maxBottom : 0)\n }\n\n // Unmount (cleanup)\n withActEnvironment(() => {\n act(() => {\n stringReconciler.updateContainerSync(null, fiberRoot, null, null)\n stringReconciler.flushSyncWork()\n })\n })\n\n return plain && !alwaysStyled\n ? bufferToText(buffer, { trimTrailingWhitespace, trimEmptyLines })\n : bufferToStyledText(buffer, { trimTrailingWhitespace, trimEmptyLines })\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Run a function with IS_REACT_ACT_ENVIRONMENT temporarily set to true.\n * This ensures act() captures forceUpdate/setState from layout notifications.\n */\nfunction withActEnvironment(fn: () => void): void {\n const prev = (globalThis as any).IS_REACT_ACT_ENVIRONMENT\n ;(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true\n try {\n fn()\n } finally {\n ;(globalThis as any).IS_REACT_ACT_ENVIRONMENT = prev\n }\n}\n",
80
+ "/**\n * Ink compat utilities: chalk integration, terminal dimensions, contexts, VS16 handling.\n * @internal\n */\n\nimport React from \"react\"\nimport chalk from \"chalk\"\nimport { isTextPresentationEmoji } from \"@silvery/ag-term/unicode\"\n\n// =============================================================================\n// Chalk integration\n// =============================================================================\n\n/**\n * Get chalk's current color level at render time.\n * Tests may set chalk.level programmatically (e.g., chalk.level = 3 for\n * background color tests). We sync our renderer's color behavior with chalk.\n */\n/** @internal */\nexport function currentChalkLevel(): number {\n return chalk?.level ?? 0\n}\n\n// =============================================================================\n// Terminal dimensions\n// =============================================================================\n\n/**\n * Resolve terminal column count using Ink's fallback chain:\n * process.env.COLUMNS → process.stdout.columns → process.stderr.columns → 80\n */\nexport function resolveTerminalColumns(): number {\n if (process.env.COLUMNS) {\n const val = Number(process.env.COLUMNS)\n if (val > 0) return val\n }\n if (process.stdout?.columns && process.stdout.columns > 0) return process.stdout.columns\n if (process.stderr?.columns && process.stderr.columns > 0) return process.stderr.columns\n return 80\n}\n\n/**\n * Resolve terminal row count using Ink's fallback chain:\n * process.env.LINES → process.stdout.rows → process.stderr.rows → 24\n */\nexport function resolveTerminalRows(): number {\n if (process.env.LINES) {\n const val = Number(process.env.LINES)\n if (val > 0) return val\n }\n if (process.stdout?.rows && process.stdout.rows > 0) return process.stdout.rows\n if (process.stderr?.rows && process.stderr.rows > 0) return process.stderr.rows\n return 24\n}\n\n// =============================================================================\n// Contexts\n// =============================================================================\n\n/**\n * Context that signals style props should be passed to silvery's Text.\n * Always true in the render() path so buffer cells are styled for correct\n * content edge detection (trailing whitespace preservation).\n * When chalk has no colors AND no embedded ANSI, processBuffer strips ANSI\n * from the styled output to produce plain text.\n * The render() path sets this to true; renderToString() does not use it.\n */\nexport const ForceStylesCtx = React.createContext(false)\n\n/**\n * Per-render-instance state shared between InkText (render phase) and\n * processBuffer (output phase). Tracks whether any Text component in the\n * tree has user-embedded ANSI sequences (SGR, OSC 8) in its children.\n * When true and !chalkHasColors, processBuffer preserves ANSI in output;\n * when false, processBuffer strips all ANSI for correct plain-mode output.\n */\nexport interface InkRenderState {\n hasEmbeddedAnsi: boolean\n}\nexport const InkRenderStateCtx = React.createContext<InkRenderState | null>(null)\n\n// =============================================================================\n// ANSI detection\n// =============================================================================\n\n/** Check if a string contains ANSI escape sequences (ESC or C1 control chars). */\nexport function containsAnsiEscapes(text: string): boolean {\n for (let i = 0; i < text.length; i++) {\n const code = text.charCodeAt(i)\n if (code === 0x1b) return true // ESC\n if (code >= 0x80 && code <= 0x9f) return true // C1 control chars\n }\n return false\n}\n\n/** Recursively check if React children contain ANSI escape sequences. */\nexport function childrenContainAnsi(children: React.ReactNode): boolean {\n if (typeof children === \"string\") return containsAnsiEscapes(children)\n if (Array.isArray(children)) return children.some((c) => childrenContainAnsi(c as React.ReactNode))\n if (React.isValidElement(children)) {\n return childrenContainAnsi((children.props as Record<string, unknown>).children as React.ReactNode)\n }\n return false\n}\n\n// =============================================================================\n// VS16 (variation selector) handling\n// =============================================================================\n\n/**\n * Track text-presentation emoji codepoints that the user provided WITH VS16.\n * When silvery's ensureEmojiPresentation adds VS16 to bare text-presentation\n * emojis (e.g., ✔ → ✔️), stripSilveryVS16 strips them. But when the user's\n * original text already had VS16 (e.g., 🌡️, ⚠️), we must preserve it.\n *\n * This set is populated by the Ink compat Text component before rendering,\n * and consulted by stripSilveryVS16 during buffer post-processing.\n */\nexport const _userVS16Codepoints = new Set<number>()\n\n/**\n * Scan text content for text-presentation emojis that already have VS16.\n * Records their base codepoints in the module-level set so that\n * stripSilveryVS16 knows to preserve the user's VS16.\n */\nexport function registerUserVS16(text: string): void {\n if (!text.includes(\"\\uFE0F\")) return\n let i = 0\n while (i < text.length) {\n const cp = text.codePointAt(i)!\n const char = String.fromCodePoint(cp)\n const charLen = char.length\n if (i + charLen < text.length && text.charCodeAt(i + charLen) === 0xfe0f) {\n if (isTextPresentationEmoji(char)) {\n _userVS16Codepoints.add(cp)\n }\n }\n i += charLen\n }\n}\n\n/** Recursively scan React children for user-provided VS16 in string content. */\nexport function scanChildrenForVS16(children: React.ReactNode): void {\n if (typeof children === \"string\") {\n registerUserVS16(children)\n } else if (Array.isArray(children)) {\n for (const child of children) scanChildrenForVS16(child as React.ReactNode)\n } else if (React.isValidElement(children)) {\n scanChildrenForVS16((children.props as Record<string, unknown>).children as React.ReactNode)\n }\n}\n\n/**\n * Strip VS16 (U+FE0F) variation selectors that silvery adds to text-presentation\n * emoji characters. Silvery's ensureEmojiPresentation adds VS16 to characters that\n * are Extended_Pictographic but NOT Emoji_Presentation (e.g., ✔ U+2714, ☑ U+2611).\n *\n * Preserves VS16 for codepoints in the _userVS16Codepoints set — these had VS16\n * in the user's original text and should not be stripped.\n */\n/** @internal */\nexport function stripSilveryVS16(input: string): string {\n // Fast path: no VS16 in the string\n if (!input.includes(\"\\uFE0F\")) return input\n\n // Walk through the string, removing VS16 only after text-presentation emoji\n // that did NOT have VS16 in the user's original text\n let result = \"\"\n let i = 0\n while (i < input.length) {\n const cp = input.codePointAt(i)!\n const char = String.fromCodePoint(cp)\n const charLen = char.length\n\n // Check if next position has VS16\n if (i + charLen < input.length && input.charCodeAt(i + charLen) === 0xfe0f) {\n // Only strip VS16 if the preceding char is text-presentation emoji\n // AND the user's original text did NOT have VS16 for this codepoint\n if (isTextPresentationEmoji(char)) {\n if (!_userVS16Codepoints.has(cp)) {\n // This is a text-presentation emoji that silvery decorated with VS16 — strip it\n result += char\n i += charLen + 1 // skip char + VS16\n continue\n }\n }\n }\n\n result += char\n i += charLen\n }\n return result\n}\n",
81
+ "/**\n * ANSI escape sequence sanitizer.\n *\n * Strips dangerous escape sequences from text while preserving safe SGR\n * styling and OSC sequences (hyperlinks, etc.). Used for rendering untrusted\n * text safely in the terminal.\n *\n * @packageDocumentation\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * A parsed token from an ANSI-containing string.\n *\n * Token types:\n * - `text` — Plain text content\n * - `csi` — CSI (Control Sequence Introducer): ESC + '[' + params + final byte\n * - `osc` — OSC (Operating System Command): ESC + ']' + payload + ST/BEL\n * - `esc` — Simple two-byte escape: ESC + final byte\n * - `dcs` — DCS (Device Control String): ESC + 'P' + payload + ST\n * - `pm` — PM (Privacy Message): ESC + '^' + payload + ST\n * - `apc` — APC (Application Program Command): ESC + '_' + payload + ST\n * - `sos` — SOS (Start of String): ESC + 'X' + payload + ST\n * - `c1` — C1 control character (0x80–0x9F)\n */\nexport interface AnsiToken {\n type: \"text\" | \"csi\" | \"osc\" | \"esc\" | \"dcs\" | \"pm\" | \"apc\" | \"sos\" | \"c1\"\n value: string\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst ESC = 0x1b\n\n/** Characters that introduce ST-terminated string sequences after ESC. */\nconst STRING_SEQUENCE_INTROS: Record<number, AnsiToken[\"type\"]> = {\n 0x50: \"dcs\", // 'P' — Device Control String\n 0x5e: \"pm\", // '^' — Privacy Message\n 0x5f: \"apc\", // '_' — Application Program Command\n 0x58: \"sos\", // 'X' — Start of String\n}\n\n/** C1 control codes (8-bit mode) that correspond to string sequence introducers. */\nconst C1_STRING_SEQUENCE_MAP: Record<number, AnsiToken[\"type\"]> = {\n 0x90: \"dcs\", // DCS\n 0x9e: \"pm\", // PM\n 0x9f: \"apc\", // APC\n 0x98: \"sos\", // SOS\n}\n\n// =============================================================================\n// Tokenizer\n// =============================================================================\n\n/**\n * Tokenize a string into ANSI escape sequence tokens.\n *\n * Parses the string character by character, identifying escape sequences\n * and plain text segments. Each token includes its type and the raw string\n * value (including escape characters).\n *\n * @param text - Input string that may contain ANSI escape sequences\n * @returns Array of tokens\n */\nexport function tokenizeAnsi(text: string): AnsiToken[] {\n const tokens: AnsiToken[] = []\n const len = text.length\n let i = 0\n let textStart = i\n\n function flushText(): void {\n if (i > textStart) {\n tokens.push({ type: \"text\", value: text.slice(textStart, i) })\n }\n }\n\n while (i < len) {\n const code = text.charCodeAt(i)\n\n // Check for C1 control characters (0x80–0x9F) in 8-bit mode\n if (code >= 0x80 && code <= 0x9f) {\n flushText()\n\n const c1Type = C1_STRING_SEQUENCE_MAP[code]\n if (c1Type) {\n // C1 string sequence introducer — consume until ST\n const start = i\n i++\n i = findST(text, i, len)\n tokens.push({ type: c1Type, value: text.slice(start, i) })\n } else if (code === 0x9b) {\n // CSI in 8-bit mode\n const start = i\n i++\n i = consumeCSI(text, i, len)\n tokens.push({ type: \"csi\", value: text.slice(start, i) })\n } else if (code === 0x9d) {\n // OSC in 8-bit mode\n const start = i\n i++\n i = findOSCEnd(text, i, len)\n tokens.push({ type: \"osc\", value: text.slice(start, i) })\n } else {\n // Other C1 control character\n tokens.push({ type: \"c1\", value: text[i]! })\n i++\n }\n textStart = i\n continue\n }\n\n // Check for ESC (0x1B)\n if (code === ESC) {\n flushText()\n\n if (i + 1 >= len) {\n // Incomplete escape at end of string — treat as malformed\n tokens.push({ type: \"esc\", value: text[i]! })\n i++\n textStart = i\n continue\n }\n\n const next = text.charCodeAt(i + 1)\n\n // CSI: ESC + '['\n if (next === 0x5b) {\n const start = i\n i += 2\n i = consumeCSI(text, i, len)\n tokens.push({ type: \"csi\", value: text.slice(start, i) })\n textStart = i\n continue\n }\n\n // OSC: ESC + ']'\n if (next === 0x5d) {\n const start = i\n i += 2\n i = findOSCEnd(text, i, len)\n tokens.push({ type: \"osc\", value: text.slice(start, i) })\n textStart = i\n continue\n }\n\n // String sequences: DCS (P), PM (^), APC (_), SOS (X)\n const stringType = STRING_SEQUENCE_INTROS[next]\n if (stringType) {\n const start = i\n i += 2\n i = findST(text, i, len)\n tokens.push({ type: stringType, value: text.slice(start, i) })\n textStart = i\n continue\n }\n\n // ESC sequences with intermediate bytes:\n // ESC I... F where I is 0x20–0x2F (intermediate), F is 0x30–0x7E (final)\n // Examples: ESC # 8 (DECALN), ESC ( B (G0 charset)\n // If no valid final byte follows, consume to end of string (fail-safe\n // to prevent payload leaks from malformed sequences).\n if (next >= 0x20 && next <= 0x2f) {\n const start = i\n i += 2 // skip ESC + first intermediate\n // Consume additional intermediate bytes\n while (i < len) {\n const c = text.charCodeAt(i)\n if (c < 0x20 || c > 0x2f) break\n i++\n }\n // Consume final byte (0x30–0x7E) if present\n if (i < len) {\n const c = text.charCodeAt(i)\n if (c >= 0x30 && c <= 0x7e) {\n i++\n tokens.push({ type: \"esc\", value: text.slice(start, i) })\n } else {\n // No valid final byte — malformed sequence, consume to end of string\n i = len\n tokens.push({ type: \"esc\", value: text.slice(start, i) })\n }\n } else {\n // Incomplete (at end of string) — consume what we have\n tokens.push({ type: \"esc\", value: text.slice(start, i) })\n }\n textStart = i\n continue\n }\n\n // Simple two-byte escape sequence: ESC + byte (0x30–0x7E)\n // 0x30–0x3F: Fp (private use, e.g. ESC 7 = DECSC, ESC 8 = DECRC)\n // 0x40–0x5F: Fe (C1 equivalents, e.g. ESC D = IND, ESC M = RI)\n // 0x60–0x7E: Fs (independent functions)\n if (next >= 0x30 && next <= 0x7e) {\n tokens.push({ type: \"esc\", value: text.slice(i, i + 2) })\n i += 2\n textStart = i\n continue\n }\n\n // Unknown/malformed escape — emit just ESC as an esc token\n tokens.push({ type: \"esc\", value: text[i]! })\n i++\n textStart = i\n continue\n }\n\n i++\n }\n\n flushText()\n return tokens\n}\n\n// =============================================================================\n// CSI Parser\n// =============================================================================\n\n/**\n * Consume a CSI sequence starting after \"ESC [\" or the C1 CSI byte.\n * Returns the index after the final byte.\n *\n * CSI format: parameter bytes (0x30–0x3F)*, intermediate bytes (0x20–0x2F)*, final byte (0x40–0x7E)\n */\nfunction consumeCSI(text: string, i: number, len: number): number {\n // Parameter bytes: 0x30–0x3F (digits, semicolons, colons, etc.)\n while (i < len) {\n const c = text.charCodeAt(i)\n if (c < 0x30 || c > 0x3f) break\n i++\n }\n\n // Intermediate bytes: 0x20–0x2F (space, !, \", #, etc.)\n while (i < len) {\n const c = text.charCodeAt(i)\n if (c < 0x20 || c > 0x2f) break\n i++\n }\n\n // Final byte: 0x40–0x7E\n if (i < len) {\n const c = text.charCodeAt(i)\n if (c >= 0x40 && c <= 0x7e) {\n i++\n }\n }\n\n return i\n}\n\n// =============================================================================\n// String Terminator Finder\n// =============================================================================\n\n/**\n * Find the String Terminator (ST) for DCS, PM, APC, SOS sequences.\n * ST is ESC + '\\\\' (0x5C) or C1 ST (0x9C). Returns index after the ST.\n * If no ST found, returns end of string (consuming the malformed sequence).\n */\nfunction findST(text: string, i: number, len: number): number {\n while (i < len) {\n const code = text.charCodeAt(i)\n // C1 ST (0x9C)\n if (code === 0x9c) {\n return i + 1\n }\n // ESC + '\\' (7-bit ST)\n if (code === ESC && i + 1 < len && text.charCodeAt(i + 1) === 0x5c) {\n return i + 2 // past ESC + '\\'\n }\n i++\n }\n return len\n}\n\n/**\n * Find the end of an OSC sequence.\n * OSC is terminated by ST (ESC + '\\\\'), C1 ST (0x9C), or BEL (0x07).\n * Returns index after the terminator.\n */\nfunction findOSCEnd(text: string, i: number, len: number): number {\n while (i < len) {\n const code = text.charCodeAt(i)\n // BEL terminator\n if (code === 0x07) {\n return i + 1\n }\n // C1 ST (0x9C)\n if (code === 0x9c) {\n return i + 1\n }\n // ST terminator (ESC + '\\')\n if (code === ESC && i + 1 < len && text.charCodeAt(i + 1) === 0x5c) {\n return i + 2\n }\n i++\n }\n return len\n}\n\n// =============================================================================\n// Sanitizer\n// =============================================================================\n\n/**\n * Check whether a CSI sequence is an SGR (Select Graphic Rendition) sequence.\n *\n * SGR sequences set text styling (colors, bold, underline, etc.) and are safe.\n * They have the form: CSI <params> m\n *\n * A CSI is SGR when:\n * - The final byte is 'm'\n * - There are no intermediate bytes (0x20–0x2F)\n * - Parameter bytes are only 0x30–0x3F\n */\nexport function isCSISGR(value: string): boolean {\n // Must end with 'm'\n if (value.length < 2 || value.charCodeAt(value.length - 1) !== 0x6d) {\n return false\n }\n\n // Find start of parameters (skip ESC[ or C1 CSI)\n let start: number\n if (value.charCodeAt(0) === ESC) {\n // ESC [ ... m\n start = 2\n } else {\n // C1 CSI (0x9B) ... m\n start = 1\n }\n\n // Everything between start and the final 'm' must be standard parameter bytes:\n // digits (0x30–0x39), semicolons (0x3B), colons (0x3A).\n // Private-use parameter prefixes (<, =, >, ? at 0x3C–0x3F) indicate non-SGR.\n // Intermediate bytes (0x20–0x2F) also indicate non-SGR.\n for (let i = start; i < value.length - 1; i++) {\n const c = value.charCodeAt(i)\n // Allow: digits 0-9 (0x30-0x39), colon (0x3A), semicolon (0x3B)\n // Reject: < = > ? (0x3C-0x3F) — private-use parameter prefixes\n // Reject: anything outside 0x30-0x3B (intermediates, etc.)\n if (c < 0x30 || c > 0x3b) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * Sanitize a string by stripping dangerous ANSI escape sequences while\n * preserving safe SGR styling codes and OSC sequences (hyperlinks, etc.).\n *\n * Safe (preserved):\n * - Plain text\n * - CSI SGR sequences (colors, bold, underline — final byte 'm', no intermediates)\n * - OSC sequences (hyperlinks, window titles, etc.)\n *\n * Stripped:\n * - Non-SGR CSI sequences (cursor movement, screen clearing, etc.)\n * - DCS (Device Control String)\n * - PM (Privacy Message)\n * - APC (Application Program Command)\n * - SOS (Start of String)\n * - C1 control characters (0x80–0x9F)\n * - Simple ESC sequences (cursor save/restore, etc.)\n * - Malformed/incomplete escape sequences\n *\n * @param text - Input string that may contain ANSI escape sequences\n * @returns Sanitized string with only safe sequences preserved\n *\n * @example\n * ```ts\n * // SGR preserved\n * sanitizeAnsi('\\x1b[31mred\\x1b[0m') // '\\x1b[31mred\\x1b[0m'\n *\n * // Cursor movement stripped\n * sanitizeAnsi('\\x1b[2J\\x1b[H') // ''\n *\n * // Mixed: only SGR kept\n * sanitizeAnsi('\\x1b[31m\\x1b[2Jred\\x1b[0m') // '\\x1b[31mred\\x1b[0m'\n * ```\n */\nexport function sanitizeAnsi(text: string): string {\n if (text.length === 0) return \"\"\n\n const tokens = tokenizeAnsi(text)\n let result = \"\"\n\n for (const token of tokens) {\n switch (token.type) {\n case \"text\":\n result += token.value\n break\n case \"csi\":\n // Only keep SGR sequences (color/style codes)\n if (isCSISGR(token.value)) {\n result += token.value\n }\n break\n case \"osc\":\n // OSC sequences are safe (hyperlinks, titles, etc.)\n result += token.value\n break\n // Strip everything else: esc, dcs, pm, apc, sos, c1\n }\n }\n\n return result\n}\n\n// =============================================================================\n// Colon-format SGR round-trip tracking\n// =============================================================================\n\n/**\n * A colon→semicolon SGR replacement pair.\n */\nexport interface ColonSGRReplacement {\n semicolonForm: string\n colonForm: string\n}\n\n/**\n * Detect colon-format SGR sequences in an SGR token and return replacement pairs.\n *\n * Terminals use colon-separated parameters (e.g., `38:2::255:100:0`) for true color,\n * but silvery's pipeline normalizes to semicolons (`38;2;255;100;0`). This function\n * extracts the mapping so the original colon format can be restored after rendering.\n *\n * @param sgrSequence - A CSI SGR sequence (must end with 'm')\n * @returns Array of replacement pairs, empty if no colon-format params found\n */\nexport function extractColonSGRReplacements(sgrSequence: string): ColonSGRReplacement[] {\n const paramsMatch = sgrSequence.match(/\\x1b\\[([0-9;:]+)m/)\n if (!paramsMatch) return []\n\n const rawParams = paramsMatch[1]!\n if (!rawParams.includes(\":\")) return []\n\n const replacements: ColonSGRReplacement[] = []\n const parts = rawParams.split(\";\")\n for (const part of parts) {\n if (!part.includes(\":\")) continue\n const subs = part.split(\":\")\n const code = Number(subs[0])\n if ((code === 38 || code === 48) && Number(subs[1]) === 2) {\n // True color colon format: code:2::R:G:B or code:2:R:G:B\n // Extract R, G, B (skip empty colorspace ID)\n const nums = subs.map((s) => (s === \"\" ? 0 : Number(s)))\n const r = nums[3] ?? nums[2] ?? 0\n const g = nums[4] ?? nums[3] ?? 0\n const b = nums[5] ?? nums[4] ?? 0\n const semicolonForm = `\\x1b[${code};2;${r};${g};${b}m`\n replacements.push({ semicolonForm, colonForm: `\\x1b[${part}m` })\n }\n }\n return replacements\n}\n\n/**\n * Create a colon-format SGR tracker for round-trip preservation.\n *\n * Rendering is synchronous: sanitize → render → output in one call. The tracker\n * accumulates colon→semicolon mappings during sanitization, then `restore()` applies\n * them to the rendered output.\n *\n * @example\n * ```ts\n * const tracker = createColonSGRTracker()\n * // During sanitization, register SGR tokens:\n * tracker.register(sgrToken)\n * // After rendering, restore original colon format:\n * output = tracker.restore(output)\n * // Optionally clear for reuse:\n * tracker.clear()\n * ```\n */\nexport function createColonSGRTracker(): {\n register: (sgrSequence: string) => void\n restore: (output: string) => string\n clear: () => void\n} {\n const replacements: ColonSGRReplacement[] = []\n\n return {\n register(sgrSequence: string): void {\n const found = extractColonSGRReplacements(sgrSequence)\n for (const r of found) replacements.push(r)\n },\n\n restore(output: string): string {\n if (replacements.length === 0) return output\n let result = output\n for (const { semicolonForm, colonForm } of replacements) {\n result = result.replaceAll(semicolonForm, colonForm)\n }\n return result\n },\n\n clear(): void {\n replacements.length = 0\n },\n }\n}\n",
82
+ "/**\n * Ink compat ANSI sanitization: sanitize sequences, track colon-format SGR, OSC8 filtering.\n * @internal\n */\n\nimport { tokenizeAnsi as tokenizeAnsiEsc, isCSISGR, createColonSGRTracker } from \"@silvery/ag-term/ansi-sanitize\"\n\n// =============================================================================\n// Colon-format SGR tracking (delegated to @silvery/ag-term)\n// =============================================================================\n\n/**\n * Module-level colon-format SGR tracker.\n * Populated by sanitizeAnsi when it encounters colon-separated SGR (e.g., 38:2::R:G:B).\n * Consumed by restoreColonFormatSGR to convert semicolon output back to colon format.\n *\n * This is safe because rendering is synchronous: sanitize → render → output in one call.\n */\nexport const colonSGRTracker = createColonSGRTracker()\n\n/**\n * Restore colon-format SGR sequences in output.\n * Replaces semicolon-format sequences that were originally colon-format.\n *\n * Note: does NOT clear the tracker — the render() path may call\n * processBuffer multiple times (handleBufferReady + writeFrame), and each\n * call needs access to the same replacements. Replacements are naturally\n * replaced when sanitizeAnsi re-populates them on the next render cycle.\n */\nexport function restoreColonFormatSGR(output: string): string {\n return colonSGRTracker.restore(output)\n}\n\n// =============================================================================\n// OSC helpers\n// =============================================================================\n\n/** Check if an OSC sequence is properly terminated (BEL or ST). */\nfunction isOSCTerminated(value: string): boolean {\n if (value.length === 0) return false\n const last = value.charCodeAt(value.length - 1)\n // BEL terminator (0x07)\n if (last === 0x07) return true\n // C1 ST terminator (0x9C)\n if (last === 0x9c) return true\n // 7-bit ST: ESC + '\\' — check last two chars\n if (value.length >= 2 && last === 0x5c && value.charCodeAt(value.length - 2) === 0x1b) {\n return true\n }\n return false\n}\n\n/** Check if an OSC token is OSC 8 (hyperlink). */\nfunction isOSC8(value: string): boolean {\n // OSC 8 starts with ESC]8; or \\x9D8;\n if (value.charCodeAt(0) === 0x1b) {\n // ESC ] 8 ;\n return value.charCodeAt(2) === 0x38 && value.charCodeAt(3) === 0x3b\n }\n // C1 OSC: \\x9D 8 ;\n return value.charCodeAt(1) === 0x38 && value.charCodeAt(2) === 0x3b\n}\n\n// =============================================================================\n// ANSI sanitization\n// =============================================================================\n\n/**\n * Sanitize ANSI sequences in text content using silvery's tokenizer.\n *\n * Preserves SGR (colors/styles) and OSC 8 hyperlinks.\n * Strips cursor movement, screen clearing, non-hyperlink OSC, DCS, PM, APC, SOS, C1 controls.\n * Also tracks colon-format SGR for round-trip restoration via restoreColonFormatSGR().\n */\nexport function sanitizeAnsi(text: string): string {\n if (text.length === 0) return \"\"\n\n const tokens = tokenizeAnsiEsc(text)\n let result = \"\"\n\n for (const token of tokens) {\n switch (token.type) {\n case \"text\":\n result += token.value\n break\n case \"csi\":\n // Only keep SGR sequences: final byte 'm', no intermediate bytes,\n // no private-use parameter prefixes (<, =, >, ?)\n if (isCSISGR(token.value)) {\n result += token.value\n colonSGRTracker.register(token.value)\n }\n break\n case \"osc\":\n // Only keep properly terminated OSC 8 (hyperlinks).\n // Strip unterminated OSC (no BEL/ST terminator) to prevent payload leaks.\n if (isOSC8(token.value) && isOSCTerminated(token.value)) {\n result += token.value\n }\n break\n // Strip everything else: esc, dcs, pm, apc, sos, c1\n }\n }\n\n return result\n}\n\n/** Recursively sanitize string children, preserving React elements. */\nexport function sanitizeChildren(children: import(\"react\").ReactNode): import(\"react\").ReactNode {\n if (typeof children === \"string\") {\n return sanitizeAnsi(children)\n }\n if (Array.isArray(children)) {\n return children.map((child, i) => sanitizeChildren(child as import(\"react\").ReactNode))\n }\n return children\n}\n",
83
+ "/**\n * Ink compat components: Box, Text, Static, Newline, Spacer, Transform.\n * @internal\n */\n\nimport React, { createContext, useContext } from \"react\"\nimport { Box as SilveryBox, type BoxProps as SilveryBoxProps, type BoxHandle } from \"@silvery/ag-react/components/Box\"\nimport { Static as SilveryStatic } from \"@silvery/ag-react/components/Static\"\nimport { Text as SilveryText } from \"@silvery/ag-react/components/Text\"\nimport type { TextProps as SilveryTextProps, TextHandle as SilveryTextHandle } from \"@silvery/ag-react/components/Text\"\n\nimport {\n currentChalkLevel,\n ForceStylesCtx,\n InkRenderStateCtx,\n scanChildrenForVS16,\n childrenContainAnsi,\n} from \"./ink-utils\"\nimport { sanitizeChildren } from \"./ink-sanitize\"\n\n// =============================================================================\n// Box\n// =============================================================================\n\nexport type { BoxHandle } from \"@silvery/ag-react/components/Box\"\n\n/**\n * Ink-compatible Box props. Same as silvery's BoxProps.\n */\nexport type BoxProps = SilveryBoxProps\n\n/**\n * Ink-compatible Box component.\n *\n * Wraps silvery's Box with Ink's default flex properties:\n * - flexGrow: 0\n * - flexShrink: 1\n * - flexWrap: 'nowrap'\n *\n * These match Ink's Box.tsx line 83-88 defaults. User-provided props override.\n * flexDirection defaults to 'row' to match Ink's behavior (Ink Box.tsx line 85).\n */\nexport const Box = React.forwardRef<BoxHandle, BoxProps>(function InkBox(props, ref) {\n // When chalk has no color support, strip visual style props to match Ink behavior.\n // Ink uses chalk internally for border/background colors, so chalk.level=0 means\n // no styles are applied. But embedded ANSI in text content is still preserved.\n const hasColors = currentChalkLevel() > 0\n return React.createElement(SilveryBox, {\n flexDirection: \"row\",\n flexGrow: 0,\n flexShrink: 1,\n ...props,\n color: hasColors ? (props as any).color : undefined,\n backgroundColor: hasColors ? (props as any).backgroundColor : undefined,\n borderColor: hasColors ? (props as any).borderColor : undefined,\n // borderDimColor is an Ink-specific prop not in silvery's BoxProps\n borderDimColor: hasColors ? (props as any).borderDimColor : undefined,\n ref,\n } as any)\n})\n\n// =============================================================================\n// Text\n// =============================================================================\n\nexport type { TextProps, TextHandle } from \"@silvery/ag-react/components/Text\"\n\n/**\n * Ink-compatible Text component.\n *\n * Wraps silvery's Text with ANSI sequence sanitization:\n * - Preserves SGR sequences (colors, bold, etc.)\n * - Preserves OSC sequences (hyperlinks, etc.)\n * - Strips cursor movement, screen clearing, and other control sequences\n * - Strips DCS, PM, APC, SOS control strings\n *\n * This matches Ink's text sanitization behavior from sanitize-ansi.ts.\n */\nexport const Text = React.forwardRef<SilveryTextHandle, SilveryTextProps>(function InkText(props, ref) {\n // Scan original children for user-provided VS16 before sanitization\n scanChildrenForVS16(props.children)\n const sanitizedChildren = sanitizeChildren(props.children)\n // Track embedded ANSI in children so processBuffer knows whether to strip\n // prop-based ANSI codes when chalk has no colors (FORCE_COLOR=0).\n const renderState = React.useContext(InkRenderStateCtx)\n if (renderState && !renderState.hasEmbeddedAnsi) {\n if (childrenContainAnsi(sanitizedChildren)) {\n renderState.hasEmbeddedAnsi = true\n }\n }\n // ForceStylesCtx is always true in the render() path so buffer cells\n // are styled for correct content edge detection (trailing whitespace).\n // When chalk has no colors, processBuffer handles ANSI stripping.\n const hasColors = currentChalkLevel() > 0\n const forceStyles = React.useContext(ForceStylesCtx)\n const passProps =\n hasColors || forceStyles\n ? {\n ...props,\n color: props.color,\n backgroundColor: props.backgroundColor,\n ref,\n children: sanitizedChildren,\n }\n : {\n // Only pass layout-affecting props, not visual style props\n wrap: props.wrap,\n ref,\n children: sanitizedChildren,\n }\n return React.createElement(SilveryText, passProps)\n})\n\n// =============================================================================\n// Static\n// =============================================================================\n\n/**\n * Store for tracking Static component output.\n * Ink renders static content separately from dynamic content:\n * - Static output is accumulated across renders (fullStaticOutput)\n * - In debug mode, each frame writes fullStaticOutput + dynamicOutput\n * - Static output always gets a trailing \\n appended\n */\nexport interface InkStaticStore {\n /** All rendered static items as text lines */\n renderedCount: number\n /** Accumulated full static output (grows with each new item) */\n fullStaticOutput: string\n}\n\nexport const InkStaticStoreCtx = createContext<InkStaticStore | null>(null)\n\n/**\n * Extract plain text from a React element tree.\n * Used to convert Static item render output to text without going through\n * the full silvery render pipeline.\n */\nfunction extractTextFromElement(node: React.ReactNode): string {\n if (node == null || typeof node === \"boolean\") return \"\"\n if (typeof node === \"string\") return node\n if (typeof node === \"number\") return String(node)\n if (Array.isArray(node)) return (node as React.ReactNode[]).map(extractTextFromElement).join(\"\")\n if (React.isValidElement(node)) {\n const props = node.props as Record<string, any>\n return extractTextFromElement(props.children)\n }\n return \"\"\n}\n\n/**\n * Ink-compatible Static component for the compat layer.\n *\n * Renders nothing to the tree (returns null). Instead, converts items to text\n * and stores them in the InkStaticStore context. The render/writeFrame functions\n * read the store and prepend static output to the frame.\n *\n * This matches Ink's behavior where Static content is rendered separately\n * and placed above the dynamic content.\n */\nexport function Static<T>({\n items,\n children: renderItem,\n style,\n}: {\n items: T[]\n children: (item: T, index: number) => React.ReactNode\n style?: Record<string, any>\n}): React.ReactElement | null {\n const store = useContext(InkStaticStoreCtx)\n\n // When no static store is available (e.g., called outside the compat render()),\n // delegate to silvery's native Static component which handles both inline\n // (scrollback promotion) and fullscreen (render in tree) modes.\n if (!store) {\n return React.createElement(SilveryStatic, { items, children: renderItem, style } as any)\n }\n\n // Compute new items since last render\n if (items.length > store.renderedCount) {\n // Strip any previous padding suffix before appending new items\n const paddingBottom = (style?.paddingBottom as number) ?? 0\n if (paddingBottom > 0 && store.fullStaticOutput.length > 0) {\n // Remove trailing padding that was added in a previous render\n const paddingSuffix = \"\\n\".repeat(paddingBottom)\n if (store.fullStaticOutput.endsWith(paddingSuffix)) {\n store.fullStaticOutput = store.fullStaticOutput.slice(0, -paddingSuffix.length)\n }\n }\n\n const newItems = items.slice(store.renderedCount)\n const newLines = newItems.map((item, i) => {\n const element = renderItem(item, store.renderedCount + i)\n return extractTextFromElement(element)\n })\n // Each item is on its own line, static output gets trailing \\n from Ink's renderer\n const newStaticOutput = newLines.join(\"\\n\") + \"\\n\"\n store.fullStaticOutput += newStaticOutput\n store.renderedCount = items.length\n\n // Apply paddingBottom from style — adds extra blank lines after items\n if (paddingBottom > 0) {\n store.fullStaticOutput += \"\\n\".repeat(paddingBottom)\n }\n }\n\n // Return null — Static content is handled outside the normal render tree\n return null\n}\n\n// =============================================================================\n// Re-exports\n// =============================================================================\n\nexport { Newline } from \"@silvery/ag-react/components/Newline\"\nexport { Spacer } from \"@silvery/ag-react/components/Spacer\"\nexport { Transform } from \"@silvery/ag-react/components/Transform\"\nexport type { TransformProps } from \"@silvery/ag-react/components/Transform\"\n",
84
+ "/**\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 useContentRect/useScreenRect 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 getContentRect(): Rect | null\n /** Get the current screen-relative layout rect */\n getScreenRect(): Rect | null\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * Flexbox container component for terminal UIs.\n *\n * Provides NodeContext to children, enabling useContentRect/useScreenRect 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.contentRect\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.contentRect) {\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 getContentRect: () => nodeRef.current?.contentRect ?? null,\n getScreenRect: () => nodeRef.current?.screenRect ?? 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",
85
+ "/**\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 uses\n * useScrollback to push items to terminal scrollback. Items leave the React\n * tree once promoted — the terminal owns them. This is the correct behavior\n * for inline mode where content flows into the scrollback buffer.\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 { useScrollback } from \"../hooks/useScrollback\"\nimport { renderStringSync } from \"../render-string\"\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 useInlineScrollback = !!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\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 }\n\n // In inline mode, use useScrollback to promote items to terminal scrollback.\n // All items are immediately frozen (write-once semantics = always frozen).\n const frozenCount = useScrollback(items, {\n frozen: () => useInlineScrollback,\n render: (_item: T, index: number) => {\n const element = renderedRef.current[index]\n if (!element) return \"\"\n try {\n return renderStringSync(element as React.ReactElement, {\n width: term?.cols ?? 80,\n plain: false,\n })\n } catch {\n return `[static item ${index}]`\n }\n },\n width: useInlineScrollback ? (term?.cols ?? 80) : undefined,\n })\n\n if (useInlineScrollback) {\n // In inline mode, only render items not yet promoted to scrollback.\n // Frozen items have been written to the terminal scrollback by useScrollback.\n const liveElements = renderedRef.current.slice(frozenCount)\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",
86
+ "/**\n * useScrollback - Push frozen items to terminal scrollback.\n *\n * Tracks a contiguous frozen prefix of items. When the frozen count\n * increases, renders newly frozen items and writes them to stdout.\n * Pair with VirtualList's `virtualized` prop for the complete experience.\n *\n * In inline mode, notifies the scheduler about lines written to stdout\n * so that cursor positioning accounts for the displacement.\n *\n * On terminal resize (width change), clears the entire terminal (scrollback\n * + screen via ED3 + ED2) and re-emits ALL frozen items at the new width.\n * This is the only correct approach — terminal scrollback is opaque and\n * cannot be selectively modified. Any attempt to track which items are\n * \"visible\" vs \"in scrollback\" drifts after terminal reflow, causing\n * cumulative duplication on each resize.\n *\n * Supports optional OSC 133 semantic markers for terminal prompt navigation\n * (Cmd+Up/Cmd+Down in iTerm2, Kitty, WezTerm, Ghostty).\n *\n * NOTE: DECSTBM scroll regions CANNOT be used here. Lines that scroll out\n * of a DECSTBM region are discarded — they never enter terminal scrollback.\n * All scrollback management must be done via explicit stdout.write() calls.\n */\n\nimport { useContext, useLayoutEffect, useRef } from \"react\"\nimport { StdoutContext } from \"../context\"\nimport { OSC133 } from \"@silvery/ag-term/osc-markers\"\n\n/** Custom marker callbacks for per-item control. */\nexport interface ScrollbackMarkerCallbacks<T> {\n /** Called before each frozen item's output. Return marker string or empty. */\n before?: (item: T, index: number) => string\n /** Called after each frozen item's output. Return marker string or empty. */\n after?: (item: T, index: number) => string\n}\n\nexport interface UseScrollbackOptions<T> {\n /** Predicate: return true for items that should be frozen */\n frozen: (item: T, index: number) => boolean\n /** Render an item to a string for stdout output */\n render: (item: T, index: number) => string\n /** Output stream (defaults to process.stdout) */\n stdout?: { write(data: string): boolean }\n /**\n * Emit OSC 133 semantic markers around each frozen item for terminal navigation.\n *\n * - `true`: emit `OSC133.promptStart` before and `OSC133.commandEnd(0)` after each item\n * - Object with `before`/`after` callbacks: call for custom marker strings per item\n */\n markers?: boolean | ScrollbackMarkerCallbacks<T>\n /** Terminal width in columns. When this changes, frozen items are re-rendered and\n * re-emitted if the content changed at the new width. */\n width?: number\n}\n\n/**\n * Count the number of newlines in a string.\n */\nfunction countNewlines(s: string): number {\n let count = 0\n for (let i = 0; i < s.length; i++) {\n if (s.charCodeAt(i) === 10) count++\n }\n return count\n}\n\n/**\n * Resolve the before/after marker strings for a given item.\n */\nfunction resolveMarkers<T>(\n markers: boolean | ScrollbackMarkerCallbacks<T> | undefined,\n item: T,\n index: number,\n): { before: string; after: string } {\n if (!markers) return { before: \"\", after: \"\" }\n if (markers === true) {\n return { before: OSC133.promptStart, after: OSC133.commandEnd(0) }\n }\n return {\n before: markers.before?.(item, index) ?? \"\",\n after: markers.after?.(item, index) ?? \"\",\n }\n}\n\n/**\n * Track frozen items and write newly frozen ones to stdout.\n *\n * @returns The current frozen count (contiguous prefix length).\n */\nexport function useScrollback<T>(items: T[], options: UseScrollbackOptions<T>): number {\n const { frozen, render, stdout = process.stdout, markers, width } = options\n const stdoutCtx = useContext(StdoutContext)\n\n // Compute contiguous frozen prefix\n let frozenCount = 0\n for (let i = 0; i < items.length; i++) {\n if (!frozen(items[i]!, i)) break\n frozenCount++\n }\n\n const prevFrozenCountRef = useRef(0)\n\n // Stored rendered strings for content-change detection on resize\n const renderedStringsRef = useRef<Map<number, string>>(new Map())\n const prevWidthRef = useRef(width)\n\n // Track cumulative frozen line count for visible-range calculation on resize.\n // This is the total number of terminal lines occupied by all frozen items.\n // Used to determine which items have scrolled into terminal scrollback (and\n // therefore can't be re-emitted — the terminal owns them).\n const totalFrozenLinesRef = useRef(0)\n\n // Refs for current values — avoid stale closures in useLayoutEffect\n const renderRef = useRef(render)\n renderRef.current = render\n const itemsRef = useRef(items)\n itemsRef.current = items\n const markersRef = useRef(markers)\n markersRef.current = markers\n const frozenCountRef = useRef(frozenCount)\n frozenCountRef.current = frozenCount\n\n // Normal freeze path: write newly frozen items to scrollback.\n //\n // In inline mode with promoteScrollback (run() runtime), delegates frozen\n // content to the output phase. The output phase writes frozen content + live\n // content in a single target.write() — no screen clearing, no cursor desync,\n // no flicker.\n //\n // Without promoteScrollback (test renderer, static rendering, or older runtimes),\n // falls back to direct stdout writes with notifyScrollback.\n useLayoutEffect(() => {\n const prev = prevFrozenCountRef.current\n if (frozenCount > prev) {\n if (stdoutCtx?.promoteScrollback) {\n // Inline mode: build frozen content string and delegate to output phase.\n // No direct stdout writes — the output phase handles everything.\n let frozenContent = \"\"\n let linesWritten = 0\n for (let i = prev; i < frozenCount; i++) {\n const { before, after } = resolveMarkers(markers, items[i]!, i)\n if (before) frozenContent += before\n const text = render(items[i]!, i) + \"\\n\"\n frozenContent += text.replace(/\\n/g, \"\\x1b[K\\r\\n\")\n linesWritten += countNewlines(text)\n renderedStringsRef.current.set(i, text)\n if (after) frozenContent += after\n }\n totalFrozenLinesRef.current += linesWritten\n stdoutCtx.promoteScrollback(frozenContent, linesWritten)\n } else {\n // Non-inline / legacy: write only newly frozen items directly\n let linesWritten = 0\n for (let i = prev; i < frozenCount; i++) {\n const { before, after } = resolveMarkers(markers, items[i]!, i)\n if (before) stdout.write(before)\n const text = render(items[i]!, i) + \"\\n\"\n stdout.write(text.replace(/\\n/g, \"\\r\\n\"))\n linesWritten += countNewlines(text)\n renderedStringsRef.current.set(i, text)\n if (after) stdout.write(after)\n }\n totalFrozenLinesRef.current += linesWritten\n stdoutCtx?.notifyScrollback?.(linesWritten)\n }\n }\n prevFrozenCountRef.current = frozenCount\n }, [frozenCount, items, render, stdout, stdoutCtx, markers])\n\n // Resize path: clear scrollback + screen, re-emit ALL frozen items.\n //\n // Terminal scrollback is opaque — the application cannot query, modify, or\n // selectively clear it. Previous attempts to track which items are \"visible\"\n // vs \"in scrollback\" via totalFrozenLinesRef drifted after terminal reflow,\n // causing cumulative duplication on each resize.\n //\n // The correct approach is simple: on width change, clear EVERYTHING (scrollback\n // + screen via ED3 + ED2), then re-emit all frozen items from scratch at the\n // new width. This eliminates drift entirely — no guessing what the terminal did.\n //\n // Trade-off: brief visual flash on resize (acceptable for a resize event).\n // ED3 (\\x1b[3J) is supported by: Ghostty, iTerm2, xterm, Alacritty, WezTerm,\n // kitty, VTE terminals, Windows Terminal.\n useLayoutEffect(() => {\n if (width === undefined) return\n\n const prevWidth = prevWidthRef.current\n prevWidthRef.current = width\n\n if (prevWidth === undefined || width === prevWidth) return\n const currentFrozenCount = frozenCountRef.current\n if (currentFrozenCount === 0) return\n\n const currentItems = itemsRef.current\n const currentRender = renderRef.current\n const currentMarkers = markersRef.current\n\n // 1. Reset output phase cursor tracking\n stdoutCtx?.resetInlineCursor?.()\n\n // 2. Clear scrollback buffer + visible screen\n // \\x1b[3J = ED3 (Erase Saved Lines — clears scrollback)\n // \\x1b[H = CUP (move cursor to 1,1)\n // \\x1b[2J = ED2 (Erase entire screen)\n stdout.write(\"\\x1b[3J\\x1b[H\\x1b[2J\")\n\n // 3. Re-emit ALL frozen items at new width\n let linesWritten = 0\n for (let i = 0; i < currentFrozenCount; i++) {\n const { before, after } = resolveMarkers(currentMarkers, currentItems[i]!, i)\n\n if (before) stdout.write(before)\n\n const text = currentRender(currentItems[i]!, i) + \"\\n\"\n stdout.write(text.replace(/\\n/g, \"\\r\\n\"))\n linesWritten += countNewlines(text)\n renderedStringsRef.current.set(i, text)\n\n if (after) stdout.write(after)\n }\n\n // 4. Reset totalFrozenLines to accurate count (no drift possible)\n totalFrozenLinesRef.current = linesWritten\n\n // 5. Do NOT call notifyScrollback here. The re-emitted frozen items are\n // the new scrollback baseline, not a displacement offset. resetInlineCursor()\n // already set forceFirstRender=true, which makes the output phase treat the\n // next render as the first frame — cursor starts fresh after the frozen items.\n\n // 6. Sync prevFrozenCountRef to prevent double-writes\n prevFrozenCountRef.current = currentFrozenCount\n }, [width, stdout, stdoutCtx])\n\n return frozenCount\n}\n",
87
+ "/**\n * OSC 133 Semantic Prompt Markers\n *\n * Shell integration protocol that marks prompts and commands in terminal output.\n * Terminals like iTerm2, Kitty, and WezTerm use these markers to provide\n * \"jump to previous/next prompt\" navigation (Cmd+Up/Cmd+Down in iTerm2).\n *\n * Protocol: OSC 133\n * - Prompt start: ESC ] 133 ; A BEL (before user input)\n * - Prompt end: ESC ] 133 ; B BEL (after user input, before command output)\n * - Command output start: ESC ] 133 ; C BEL (before command output)\n * - Command output end: ESC ] 133 ; D ; N BEL (after command output, N = exit code)\n *\n * For a chat-style app, each \"exchange\" (user prompt + assistant response) maps to:\n * - 133;A before the user's message\n * - 133;B after the user's message\n * - 133;C before the assistant's response\n * - 133;D;0 after the assistant's response\n *\n * Supported by: iTerm2, Kitty, WezTerm, foot, Ghostty\n */\n\nexport const OSC133 = {\n /** Mark prompt start (before user input) */\n promptStart: \"\\x1b]133;A\\x07\",\n /** Mark prompt end (after user input, before command output) */\n promptEnd: \"\\x1b]133;B\\x07\",\n /** Mark command output start */\n commandStart: \"\\x1b]133;C\\x07\",\n /** Mark command output end (exit code defaults to 0 = success) */\n commandEnd: (exitCode?: number) => `\\x1b]133;D;${exitCode ?? 0}\\x07`,\n} as const\n",
88
+ "/**\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",
89
+ "/**\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",
90
+ "/**\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",
91
+ "/**\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",
92
+ "/**\n * Ink compat hooks: useFocus, useFocusManager, useStdin, usePaste, useCursor, useBoxMetrics, etc.\n * @internal\n */\n\nimport { useContext, useEffect, useLayoutEffect, useCallback, useState, useMemo, useRef } from \"react\"\nimport { StdoutContext } from \"@silvery/ag-react/context\"\nimport { RuntimeContext } from \"@silvery/ag-react/context\"\nimport { InkCursorStoreCtx } from \"./with-ink-cursor\"\nimport { InkFocusContext } from \"./with-ink-focus\"\nimport { InkStdinCtx } from \"./ink-stdin\"\nimport { useInput as silveryUseInput } from \"@silvery/ag-react/hooks/useInput\"\n\n// =============================================================================\n// Focus hooks\n// =============================================================================\n\n/**\n * Ink-compatible useFocus hook.\n * Registers a focusable component and tracks focus state.\n */\nexport function useFocus(opts?: { isActive?: boolean; autoFocus?: boolean; id?: string }): {\n isFocused: boolean\n focus: (id: string) => void\n} {\n const { isActive = true, autoFocus = false, id: customId } = opts ?? {}\n const ctx = useContext(InkFocusContext)\n\n const id = useMemo(() => customId ?? Math.random().toString().slice(2, 7), [customId])\n\n useEffect(() => {\n ctx.add(id, { autoFocus })\n return () => {\n ctx.remove(id)\n }\n }, [id, autoFocus])\n\n useEffect(() => {\n if (isActive) {\n ctx.activate(id)\n } else {\n ctx.deactivate(id)\n }\n }, [isActive, id])\n\n return {\n isFocused: Boolean(id) && ctx.activeId === id,\n focus: ctx.focus,\n }\n}\n\n/**\n * Ink-compatible useFocusManager hook.\n */\nexport function useFocusManager(): {\n enableFocus: () => void\n disableFocus: () => void\n focusNext: () => void\n focusPrevious: () => void\n focus: (id: string) => void\n activeId: string | undefined\n} {\n const ctx = useContext(InkFocusContext)\n return {\n enableFocus: ctx.enableFocus,\n disableFocus: ctx.disableFocus,\n focusNext: ctx.focusNext,\n focusPrevious: ctx.focusPrevious,\n focus: ctx.focus,\n activeId: ctx.activeId,\n }\n}\n\nexport type UseFocusOptions = { isActive?: boolean; autoFocus?: boolean; id?: string }\nexport type UseFocusResult = { isFocused: boolean; focus: (id: string) => void }\nexport type InkUseFocusManagerResult = ReturnType<typeof useFocusManager>\n\n// =============================================================================\n// Stdin hooks\n// =============================================================================\n\n/**\n * Ink-compatible useStdin hook.\n * Returns stdin stream and raw mode controls.\n */\nexport function useStdin() {\n const ctx = useContext(InkStdinCtx)\n return {\n stdin: ctx.stdin,\n setRawMode: ctx.setRawMode,\n isRawModeSupported: ctx.isRawModeSupported,\n }\n}\n\n/**\n * Ink-compatible usePaste hook.\n *\n * Enables bracketed paste mode and calls the handler when the user pastes text.\n * Paste content is delivered as a single string, not forwarded to useInput handlers.\n */\nexport function usePaste(handler: (text: string) => void, options: { isActive?: boolean } = {}): void {\n const ctx = useContext(InkStdinCtx)\n const rt = useContext(RuntimeContext)\n\n useEffect(() => {\n if (options.isActive === false) return\n ctx.setRawMode(true)\n ctx.setBracketedPasteMode(true)\n return () => {\n ctx.setRawMode(false)\n ctx.setBracketedPasteMode(false)\n }\n }, [options.isActive, ctx.setRawMode, ctx.setBracketedPasteMode])\n\n // Subscribe to paste events from silvery's RuntimeContext (interactive path)\n useEffect(() => {\n if (options.isActive === false) return\n if (!rt) return\n return rt.on(\"paste\", (text: string) => {\n handler(text)\n })\n }, [options.isActive, rt, handler])\n\n // Subscribe to paste events from InkStdinCtx (test renderer path)\n useEffect(() => {\n if (options.isActive === false) return\n const handlePaste = (text: string) => {\n handler(text)\n }\n ctx.internal_eventEmitter.on(\"paste\", handlePaste)\n return () => {\n ctx.internal_eventEmitter.removeListener(\"paste\", handlePaste)\n }\n }, [options.isActive, ctx.internal_eventEmitter, handler])\n}\n\n// =============================================================================\n// Cursor hook\n// =============================================================================\n\n/**\n * Ink-compatible useCursor hook.\n *\n * Bridges Ink's imperative `setCursorPosition({ x, y })` API to silvery's\n * cursor store. Writes directly to the per-instance CursorStore rather than\n * going through silvery's useCursor hook (which needs NodeContext for layout\n * coordinate translation — unnecessary here since Ink provides absolute coords).\n *\n * On unmount, clears cursor state (hides cursor).\n */\nexport function useCursor() {\n const store = useContext(InkCursorStoreCtx)\n\n // Buffer for render-phase setCursorPosition calls.\n // Applied in useLayoutEffect (after commit) to prevent cursor state from\n // leaking when a component renders but doesn't commit (e.g., Suspense).\n const pendingRef = useRef<{ x: number; y: number } | null | undefined>(undefined)\n\n // Apply buffered cursor state after commit, clear on unmount.\n // No deps array: runs every render to pick up position changes from render phase.\n useLayoutEffect(() => {\n if (store && pendingRef.current !== undefined) {\n const pos = pendingRef.current\n if (pos) {\n store.setCursorState({ x: pos.x, y: pos.y, visible: true })\n } else {\n store.setCursorState(null)\n }\n }\n return () => {\n store?.setCursorState(null)\n }\n })\n\n const setCursorPosition = useCallback(\n (position: { x: number; y: number } | undefined) => {\n if (!store) return\n // Buffer the position — applied in useLayoutEffect after React commits\n pendingRef.current = position ?? null\n },\n [store],\n )\n\n return { setCursorPosition }\n}\n\n// =============================================================================\n// Window size hook\n// =============================================================================\n\nexport { useWindowSize } from \"@silvery/ag-react/hooks/useWindowSize\"\n\n// =============================================================================\n// Box metrics\n// =============================================================================\n\n/**\n * Extract the AgNode from a ref that may point to a BoxHandle or a AgNode.\n * In silvery, Box's forwardRef exposes a BoxHandle via useImperativeHandle,\n * which has getNode(). Ink users pass refs expecting direct DOM-like access.\n */\nfunction resolveAgNode(refValue: any): import(\"@silvery/tea/types\").AgNode | null {\n if (!refValue) return null\n // BoxHandle from silvery's Box component\n if (typeof refValue.getNode === \"function\") {\n return refValue.getNode()\n }\n // Direct AgNode (has layoutNode property)\n if (refValue.layoutNode !== undefined || refValue.contentRect !== undefined) {\n return refValue\n }\n return null\n}\n\n/**\n * Metrics state for useBoxMetrics.\n */\ninterface BoxMetrics {\n width: number\n height: number\n left: number\n top: number\n hasMeasured: boolean\n}\n\nconst ZERO_METRICS: BoxMetrics = { width: 0, height: 0, left: 0, top: 0, hasMeasured: false }\n\n/**\n * Compare two BoxMetrics objects for equality.\n */\nfunction metricsEqual(a: BoxMetrics, b: BoxMetrics): boolean {\n return (\n a.width === b.width &&\n a.height === b.height &&\n a.left === b.left &&\n a.top === b.top &&\n a.hasMeasured === b.hasMeasured\n )\n}\n\n/**\n * Ink-compatible useBoxMetrics hook.\n * Returns layout metrics for a tracked box element.\n *\n * Wires into silvery's layout system by subscribing to layout changes\n * on the referenced AgNode's layoutSubscribers.\n */\nexport function useBoxMetrics(ref: import(\"react\").RefObject<any>) {\n const [metrics, setMetrics] = useState<BoxMetrics>(ZERO_METRICS)\n\n // Track the previously resolved node so we can detect ref switches\n const prevNodeRef = useRef<import(\"@silvery/tea/types\").AgNode | null>(null)\n // Track the last metrics we set to avoid unnecessary state updates\n const lastMetricsRef = useRef<BoxMetrics>(ZERO_METRICS)\n\n /**\n * Update metrics only if they changed, to prevent infinite re-render loops.\n */\n const updateMetrics = useCallback((next: BoxMetrics) => {\n if (!metricsEqual(lastMetricsRef.current, next)) {\n lastMetricsRef.current = next\n setMetrics(next)\n }\n }, [])\n\n // Subscribe to layout changes. Re-runs on every render (no deps) to\n // pick up ref changes (e.g., memoized component's ref becoming available).\n useEffect(() => {\n const node = resolveAgNode(ref.current)\n\n // Detect ref switch\n if (node !== prevNodeRef.current) {\n prevNodeRef.current = node\n if (!node) {\n updateMetrics(ZERO_METRICS)\n return\n }\n }\n\n if (!node) return\n\n const onLayoutChange = () => {\n const rect = node.contentRect\n if (rect) {\n updateMetrics({\n width: rect.width,\n height: rect.height,\n left: rect.x,\n top: rect.y,\n hasMeasured: true,\n })\n }\n }\n\n // Read current layout if already computed\n if (node.contentRect) {\n onLayoutChange()\n }\n\n // Subscribe to future layout changes\n node.layoutSubscribers.add(onLayoutChange)\n\n return () => {\n node.layoutSubscribers.delete(onLayoutChange)\n }\n })\n\n // Listen for resize events on stdout to trigger re-measurement\n const ctx = useContext(StdoutContext)\n const stdout = ctx?.stdout ?? process.stdout\n\n useEffect(() => {\n const onResize = () => {\n const node = resolveAgNode(ref.current)\n if (node?.contentRect) {\n updateMetrics({\n width: node.contentRect.width,\n height: node.contentRect.height,\n left: node.contentRect.x,\n top: node.contentRect.y,\n hasMeasured: true,\n })\n }\n }\n stdout.on(\"resize\", onResize)\n return () => {\n stdout.off(\"resize\", onResize)\n }\n }, [stdout, ref, updateMetrics])\n\n return metrics\n}\n\n// =============================================================================\n// Re-exported hooks\n// =============================================================================\n\nexport { useInput, type Key, type InputHandler, type UseInputOptions } from \"@silvery/ag-react/hooks/useInput\"\nexport { useApp } from \"@silvery/ag-react/hooks/useApp\"\nexport type { UseAppResult } from \"@silvery/ag-react/hooks/useApp\"\nexport { useStdout } from \"@silvery/ag-react/hooks/useStdout\"\nexport type { UseStdoutResult } from \"@silvery/ag-react/hooks/useStdout\"\nexport { useStderr } from \"@silvery/ag-react/hooks/useStderr\"\n\n// =============================================================================\n// Kitty Keyboard Protocol — delegates to @silvery/ag-term\n// =============================================================================\n\nimport { KittyFlags, type KittyManagerOptions } from \"@silvery/ag-term\"\n\n/**\n * Kitty keyboard protocol flags (Ink-compatible names).\n * Delegates to KittyFlags from @silvery/ag-term.\n * @see https://sw.kovidgoyal.net/kitty/keyboard-protocol/\n */\nexport const kittyFlags = {\n disambiguateEscapeCodes: KittyFlags.DISAMBIGUATE,\n reportEventTypes: KittyFlags.REPORT_EVENTS,\n reportAlternateKeys: KittyFlags.REPORT_ALTERNATE,\n reportAllKeysAsEscapeCodes: KittyFlags.REPORT_ALL_KEYS,\n reportAssociatedText: KittyFlags.REPORT_TEXT,\n} as const\n\n/** Valid flag names for the kitty keyboard protocol. */\nexport type KittyFlagName = keyof typeof kittyFlags\n\n/** Converts an array of flag names to the corresponding bitmask value. */\nexport function resolveFlags(flags: KittyFlagName[]): number {\n let result = 0\n for (const flag of flags) {\n result |= kittyFlags[flag]\n }\n return result\n}\n\n/**\n * Kitty keyboard modifier bits.\n * Used in the modifier parameter of CSI u sequences.\n * Note: The actual modifier value is (modifiers - 1) as per the protocol.\n */\nexport const kittyModifiers = {\n shift: 1,\n alt: 2,\n ctrl: 4,\n super: 8,\n hyper: 16,\n meta: 32,\n capsLock: 64,\n numLock: 128,\n} as const\n\n/** Options for configuring kitty keyboard protocol. */\nexport type KittyKeyboardOptions = {\n mode?: \"auto\" | \"enabled\" | \"disabled\"\n flags?: KittyFlagName[]\n}\n\n// =============================================================================\n// Kitty Protocol Manager — delegates to @silvery/ag-term\n// =============================================================================\n\n/** Convert Ink-compatible KittyKeyboardOptions to @silvery/ag-term KittyManagerOptions. */\nexport function resolveKittyManagerOptions(opts: KittyKeyboardOptions | undefined): KittyManagerOptions | undefined {\n if (!opts) return undefined\n return {\n mode: opts.mode,\n flags: opts.flags ? resolveFlags(opts.flags) : undefined,\n }\n}\n",
93
+ "/**\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",
94
+ "/**\n * useCursor - Show and position the terminal's blinking cursor.\n *\n * Maps component-relative (col, row) to absolute terminal coordinates\n * using useScreenRectCallback. Per-instance last-writer-wins: only one cursor\n * can be active at a time per silvery instance (the terminal has one hardware cursor).\n *\n * Cursor state is isolated per silvery instance via CursorContext. Each runtime\n * (run(), createApp(), test renderer) provides its own CursorProvider so\n * multiple silvery instances don't fight over cursor position.\n *\n * Falls back to module-level globals when no CursorProvider is present\n * (backward compatibility / deprecation path).\n */\n\nimport React, {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n type ReactNode,\n} from \"react\"\nimport type { CursorShape } from \"@silvery/ag-term/output\"\nimport { useScreenRectCallback, type Rect } from \"./useLayout\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CursorPosition {\n /** Column offset within the component (0-indexed) */\n col: number\n /** Row offset within the component (0-indexed) */\n row: number\n /** Whether the cursor should be visible. Default: true */\n visible?: boolean\n /** Terminal cursor shape (DECSCUSR). Default: terminal default */\n shape?: CursorShape\n}\n\nexport interface CursorState {\n /** Absolute terminal X position (0-indexed) */\n x: number\n /** Absolute terminal Y position (0-indexed) */\n y: number\n /** Whether cursor is visible */\n visible: boolean\n /** Terminal cursor shape (DECSCUSR) */\n shape?: CursorShape\n}\n\n// ============================================================================\n// Cursor Accessors — imperative interface for non-React consumers\n// ============================================================================\n\n/**\n * Imperative cursor accessors for non-React code (scheduler, output phase).\n * Created by createCursorStore() and threaded to consumers that can't use hooks.\n */\nexport interface CursorAccessors {\n getCursorState(): CursorState | null\n subscribeCursor(listener: () => void): () => void\n}\n\n// ============================================================================\n// Cursor Store — per-instance state + accessors\n// ============================================================================\n\nexport interface CursorStore {\n state: CursorState | null\n listeners: Set<() => void>\n accessors: CursorAccessors\n setCursorState(state: CursorState | null): void\n}\n\n/**\n * Create an isolated cursor store. Each silvery instance gets one.\n * Returns the store (for CursorProvider) and accessors (for scheduler/output).\n */\nexport function createCursorStore(): CursorStore {\n const store: CursorStore = {\n state: null,\n listeners: new Set(),\n accessors: null!,\n setCursorState(s: CursorState | null) {\n store.state = s\n for (const listener of store.listeners) listener()\n },\n }\n store.accessors = {\n getCursorState: () => store.state,\n subscribeCursor: (listener: () => void) => {\n store.listeners.add(listener)\n return () => {\n store.listeners.delete(listener)\n }\n },\n }\n return store\n}\n\n// ============================================================================\n// React Context\n// ============================================================================\n\nconst CursorCtx = createContext<CursorStore | null>(null)\n\n/**\n * Provider that gives its subtree an isolated cursor store.\n * Wrap your silvery app root in this to isolate cursor state per instance.\n */\nexport function CursorProvider({ store, children }: { store: CursorStore; children?: ReactNode }) {\n return React.createElement(CursorCtx.Provider, { value: store }, children)\n}\n\n// ============================================================================\n// Module-level Fallback (deprecated — for bare render() without provider)\n// ============================================================================\n\nlet _globalCursorState: CursorState | null = null\nlet _globalCursorListeners = new Set<() => void>()\n\nfunction globalSetCursorState(state: CursorState | null): void {\n _globalCursorState = state\n for (const listener of _globalCursorListeners) listener()\n}\n\nfunction globalGetCursorState(): CursorState | null {\n return _globalCursorState\n}\n\nfunction globalSubscribeCursor(listener: () => void): () => void {\n _globalCursorListeners.add(listener)\n return () => {\n _globalCursorListeners.delete(listener)\n }\n}\n\n/** For testing -- reset global fallback state between tests. */\nexport function resetCursorState(): void {\n _globalCursorState = null\n _globalCursorListeners = new Set()\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\n/**\n * Show and position the terminal's blinking cursor within this component.\n *\n * The cursor position is relative to the component's screen position.\n * Only one cursor can be active per silvery instance -- last caller with visible=true wins.\n *\n * Uses CursorContext if a CursorProvider is present (per-instance isolation).\n * Falls back to module-level globals otherwise (backward compat).\n */\nexport function useCursor(position: CursorPosition): void {\n const { col, row, visible = true, shape } = position\n const store = useContext(CursorCtx)\n\n // Resolve set/get functions from context or global fallback\n const set = store ? store.setCursorState.bind(store) : globalSetCursorState\n const get = store ? store.accessors.getCursorState : globalGetCursorState\n\n // Keep current args in refs so the callback always reads fresh values\n const colRef = useRef(col)\n const rowRef = useRef(row)\n const visibleRef = useRef(visible)\n const shapeRef = useRef(shape)\n const setRef = useRef(set)\n const getRef = useRef(get)\n const lastRectRef = useRef<Rect | null>(null)\n colRef.current = col\n rowRef.current = row\n visibleRef.current = visible\n shapeRef.current = shape\n setRef.current = set\n getRef.current = get\n\n // Called synchronously during layout (useLayoutEffect) whenever\n // the component's screen position changes.\n useScreenRectCallback(\n useCallback((rect) => {\n lastRectRef.current = rect\n if (!visibleRef.current) {\n return\n }\n setRef.current({\n x: rect.x + colRef.current,\n y: rect.y + rowRef.current,\n visible: true,\n shape: shapeRef.current,\n })\n }, []),\n )\n\n // When col/row/shape change without a layout change, update cursor\n // position from the last known screen rect. This handles the common case\n // where typing moves the cursor within a component but the component's\n // layout position stays the same (e.g., TextInput cursor moves on keystroke).\n useLayoutEffect(() => {\n const rect = lastRectRef.current\n if (!rect || !visible) return\n set({\n x: rect.x + col,\n y: rect.y + row,\n visible: true,\n shape,\n })\n }, [col, row, shape, visible, set])\n\n // On unmount or when visible becomes false, clear cursor state\n useEffect(() => {\n if (!visible) {\n // If we are hiding, clear state now\n const current = getRef.current()\n if (current) {\n setRef.current(null)\n }\n }\n\n return () => {\n // On unmount, clear cursor state\n setRef.current(null)\n }\n }, [visible])\n}\n\n// ============================================================================\n// Exports for scheduler integration\n// ============================================================================\n\n/**\n * @deprecated Use CursorAccessors from createCursorStore() instead.\n * These module-level functions are the global fallback for backward compat.\n */\nfunction getCursorState(): CursorState | null {\n return globalGetCursorState()\n}\n\nfunction subscribeCursor(listener: () => void): () => void {\n return globalSubscribeCursor(listener)\n}\n\nexport { getCursorState, subscribeCursor }\n",
95
+ "/**\n * Layout Hooks\n *\n * Hooks for accessing element positions in silvery components.\n *\n * Two coordinate systems:\n * - Content rect: Position within scrollable content (like CSS offsetTop/offsetLeft)\n * - Screen rect: Position on terminal screen (like CSS getBoundingClientRect)\n */\n\nimport { useContext, useLayoutEffect, useReducer, useRef } from \"react\"\nimport { NodeContext } from \"../context\"\nimport { type Rect, rectEqual } from \"@silvery/ag/types\"\n\nexport type { Rect }\n\n// ============================================================================\n// Content Rect Hooks (position within scrollable content)\n// ============================================================================\n\n/**\n * Returns the content-relative position for the current component.\n * Like CSS offsetTop/offsetLeft - position within scrollable content.\n *\n * On first render, returns { x: 0, y: 0, width: 0, height: 0 }.\n * After layout completes, automatically re-renders with actual dimensions.\n *\n * @example\n * ```tsx\n * function Header() {\n * const { width } = useContentRect();\n * return <Text>{'='.repeat(width)}</Text>;\n * }\n * ```\n */\nexport function useContentRect(): Rect {\n const node = useContext(NodeContext)\n const [, forceUpdate] = useReducer((x: number) => x + 1, 0)\n\n useLayoutEffect(() => {\n if (!node) return\n\n const handleLayoutComplete = () => {\n if (!rectEqual(node.prevLayout, node.contentRect)) {\n forceUpdate()\n }\n }\n\n node.layoutSubscribers.add(handleLayoutComplete)\n return () => {\n node.layoutSubscribers.delete(handleLayoutComplete)\n }\n }, [node])\n\n return node?.contentRect ?? { x: 0, y: 0, width: 0, height: 0 }\n}\n\n/**\n * Callback invoked with content-relative position after render.\n * Does NOT cause re-renders - use for position registration in large lists.\n *\n * @example\n * ```tsx\n * function Card({ id, onLayout }) {\n * useContentRectCallback((rect) => {\n * onLayout(id, rect);\n * });\n * return <Box>...</Box>;\n * }\n * ```\n */\nexport function useContentRectCallback(callback: (rect: Rect) => void): void {\n const node = useContext(NodeContext)\n\n // Use ref to always have current callback without re-subscribing\n const callbackRef = useRef(callback)\n callbackRef.current = callback\n\n useLayoutEffect(() => {\n if (!node) return\n\n const handleLayoutComplete = () => {\n if (node.contentRect) {\n callbackRef.current(node.contentRect)\n }\n }\n\n node.layoutSubscribers.add(handleLayoutComplete)\n\n // Also call immediately if layout already computed\n if (node.contentRect) {\n callbackRef.current(node.contentRect)\n }\n\n return () => {\n node.layoutSubscribers.delete(handleLayoutComplete)\n }\n }, [node])\n}\n\n// ============================================================================\n// Screen Rect Hooks (position on terminal screen)\n// ============================================================================\n\n/**\n * Returns the screen-relative position for the current component.\n * Like CSS getBoundingClientRect - actual position on terminal screen.\n *\n * Accounts for scroll offsets of all ancestor containers.\n * Use this for visual navigation between columns with different scroll positions.\n *\n * On first render, returns { x: 0, y: 0, width: 0, height: 0 }.\n * After layout completes, automatically re-renders with actual dimensions.\n *\n * @example\n * ```tsx\n * function Card({ id }) {\n * const { y } = useScreenRect();\n * // y is the actual screen row, accounting for scroll\n * return <Box>Card at screen row {y}</Box>;\n * }\n * ```\n */\nexport function useScreenRect(): Rect {\n const node = useContext(NodeContext)\n const [, forceUpdate] = useReducer((x: number) => x + 1, 0)\n const prevScreenRectRef = useRef<Rect | null>(null)\n\n useLayoutEffect(() => {\n if (!node) return\n\n const handleLayoutComplete = () => {\n // Re-render when screenRect changes (can happen from scroll offset changes\n // even when contentRect stays the same)\n if (!rectEqual(prevScreenRectRef.current, node.screenRect)) {\n prevScreenRectRef.current = node.screenRect\n forceUpdate()\n }\n }\n\n node.layoutSubscribers.add(handleLayoutComplete)\n return () => {\n node.layoutSubscribers.delete(handleLayoutComplete)\n }\n }, [node])\n\n return node?.screenRect ?? { x: 0, y: 0, width: 0, height: 0 }\n}\n\n/**\n * Callback invoked with screen-relative position after render.\n * Does NOT cause re-renders - use for position registration in large lists.\n *\n * This is the recommended hook for cross-column visual navigation:\n * register card positions with screen coordinates to find cards at\n * the same visual Y position regardless of column scroll state.\n *\n * @example\n * ```tsx\n * function Card({ id, onLayout }) {\n * useScreenRectCallback((rect) => {\n * // rect.y is screen position, accounting for scroll\n * onLayout(id, rect.y);\n * });\n * return <Box>...</Box>;\n * }\n * ```\n */\nexport function useScreenRectCallback(callback: (rect: Rect) => void): void {\n const node = useContext(NodeContext)\n\n // Use ref to always have current callback without re-subscribing\n const callbackRef = useRef(callback)\n callbackRef.current = callback\n\n useLayoutEffect(() => {\n if (!node) return\n\n const handleLayoutComplete = () => {\n if (node.screenRect) {\n callbackRef.current(node.screenRect)\n }\n }\n\n node.layoutSubscribers.add(handleLayoutComplete)\n\n // Also call immediately if screen rect already computed\n if (node.screenRect) {\n callbackRef.current(node.screenRect)\n }\n\n return () => {\n node.layoutSubscribers.delete(handleLayoutComplete)\n }\n }, [node])\n}\n\n// ============================================================================\n// Render Rect Hooks (actual render position, accounting for sticky offsets)\n// ============================================================================\n\n/**\n * Returns the actual render position for the current component.\n * For non-sticky nodes, this equals `useScreenRect()`.\n * For sticky nodes (position=\"sticky\"), this accounts for sticky render\n * offsets — the position where pixels are actually painted on screen.\n *\n * Use this for hit testing, cursor positioning, and any feature that\n * needs to know where a node visually appears on screen.\n *\n * On first render, returns { x: 0, y: 0, width: 0, height: 0 }.\n * After layout completes, automatically re-renders with actual dimensions.\n *\n * @example\n * ```tsx\n * function StickyHeader() {\n * const { y } = useRenderRect();\n * // y is the actual render row, accounting for sticky offset\n * return <Box position=\"sticky\" stickyTop={0}>Header at row {y}</Box>;\n * }\n * ```\n */\nexport function useRenderRect(): Rect {\n const node = useContext(NodeContext)\n const [, forceUpdate] = useReducer((x: number) => x + 1, 0)\n const prevRenderRectRef = useRef<Rect | null>(null)\n\n useLayoutEffect(() => {\n if (!node) return\n\n const handleLayoutComplete = () => {\n // Re-render when renderRect changes (can happen from sticky offset\n // changes even when screenRect stays the same)\n if (!rectEqual(prevRenderRectRef.current, node.renderRect)) {\n prevRenderRectRef.current = node.renderRect\n forceUpdate()\n }\n }\n\n node.layoutSubscribers.add(handleLayoutComplete)\n return () => {\n node.layoutSubscribers.delete(handleLayoutComplete)\n }\n }, [node])\n\n return node?.renderRect ?? { x: 0, y: 0, width: 0, height: 0 }\n}\n\n/**\n * Callback invoked with actual render position after render.\n * Does NOT cause re-renders - use for position registration in large lists.\n *\n * For non-sticky nodes, the rect equals screenRect. For sticky nodes,\n * it reflects the actual render position accounting for sticky offsets.\n *\n * @example\n * ```tsx\n * function Card({ id, onLayout }) {\n * useRenderRectCallback((rect) => {\n * // rect.y is actual render row, accounting for sticky\n * onLayout(id, rect.y);\n * });\n * return <Box>...</Box>;\n * }\n * ```\n */\nexport function useRenderRectCallback(callback: (rect: Rect) => void): void {\n const node = useContext(NodeContext)\n\n // Use ref to always have current callback without re-subscribing\n const callbackRef = useRef(callback)\n callbackRef.current = callback\n\n useLayoutEffect(() => {\n if (!node) return\n\n const handleLayoutComplete = () => {\n if (node.renderRect) {\n callbackRef.current(node.renderRect)\n }\n }\n\n node.layoutSubscribers.add(handleLayoutComplete)\n\n // Also call immediately if render rect already computed\n if (node.renderRect) {\n callbackRef.current(node.renderRect)\n }\n\n return () => {\n node.layoutSubscribers.delete(handleLayoutComplete)\n }\n }, [node])\n}\n",
96
+ "/**\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",
97
+ "/**\n * Ink compat stdin state: raw mode tracking, bracketed paste mode, event bridging.\n * @internal\n */\n\nimport { createContext } from \"react\"\nimport EventEmitter from \"node:events\"\n\n// =============================================================================\n// Ink-compatible stdin state\n// =============================================================================\n\n/**\n * Context for per-instance stdin management.\n * Tracks raw mode reference counting and stdin ref/unref.\n */\nexport interface InkStdinState {\n stdin: NodeJS.ReadStream\n isRawModeSupported: boolean\n /** Number of active raw mode subscribers */\n rawModeCount: number\n setRawMode: (value: boolean) => void\n setBracketedPasteMode: (value: boolean) => void\n internal_eventEmitter: EventEmitter\n}\n\nexport const InkStdinCtx = createContext<InkStdinState>({\n stdin: process.stdin,\n isRawModeSupported: process.stdin.isTTY ?? false,\n rawModeCount: 0,\n setRawMode: () => {},\n setBracketedPasteMode: () => {},\n internal_eventEmitter: new EventEmitter(),\n})\n\n/**\n * Create stdin state for a render instance.\n * Implements raw mode reference counting:\n * - First subscriber enables raw mode + refs stdin\n * - Last subscriber disables raw mode + unrefs stdin\n * - Throws if raw mode is not supported (stdin.isTTY is false)\n */\nexport function createInkStdinState(stdin: NodeJS.ReadStream, stdout?: NodeJS.WriteStream): InkStdinState {\n const isRawModeSupported = stdin.isTTY ?? false\n let rawModeCount = 0\n let bracketedPasteModeEnabledCount = 0\n const internal_eventEmitter = new EventEmitter()\n internal_eventEmitter.setMaxListeners(Infinity)\n\n const setRawMode = (value: boolean) => {\n if (!isRawModeSupported) {\n throw new Error(\n \"Raw mode is not supported on the current process.stdin, which Ink uses as input stream by default.\\nRead about how to prevent this error on https://github.com/vadimdemedes/ink/#nested-ink-rendering\",\n )\n }\n\n if (value) {\n rawModeCount++\n if (rawModeCount === 1) {\n // First subscriber: enable raw mode and ref stdin\n if (stdin.setRawMode) stdin.setRawMode(true)\n if (stdin.ref) stdin.ref()\n }\n } else {\n rawModeCount = Math.max(0, rawModeCount - 1)\n if (rawModeCount === 0) {\n // Last subscriber: disable raw mode and unref stdin\n if (stdin.setRawMode) stdin.setRawMode(false)\n if (stdin.unref) stdin.unref()\n }\n }\n }\n\n const setBracketedPasteMode = (value: boolean) => {\n const out = stdout ?? process.stdout\n if (!(out as any).isTTY) return\n\n if (value) {\n if (bracketedPasteModeEnabledCount === 0) {\n out.write(\"\\x1b[?2004h\")\n }\n bracketedPasteModeEnabledCount++\n } else {\n if (bracketedPasteModeEnabledCount === 0) return\n if (--bracketedPasteModeEnabledCount === 0) {\n out.write(\"\\x1b[?2004l\")\n }\n }\n }\n\n return {\n stdin,\n isRawModeSupported,\n rawModeCount: 0,\n setRawMode,\n setBracketedPasteMode,\n internal_eventEmitter,\n }\n}\n",
98
+ "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",
99
+ "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",
100
+ "/**\n * Silvery useInput Hook\n *\n * Handles keyboard input via the unified RuntimeContext.\n * Compatible with Ink's useInput API.\n *\n * Throws if called outside a runtime (run(), render(), createApp(), test renderer).\n * Use useRuntime() for components that need to work in both static and interactive modes.\n */\n\nimport { useContext, useEffect, useRef } from \"react\"\nimport { RuntimeContext } from \"../context\"\nimport type { Key } from \"@silvery/ag/keys\"\n\n/**\n * Detect modifier-only key events (Cmd, Shift, Ctrl, Alt pressed alone).\n * With REPORT_ALL_KEYS, these fire as key events with empty input and\n * no actionable key flags — only modifier flags are set.\n * Consumed by useModifierKeys, not dispatched to useInput handlers.\n */\nfunction isModifierOnlyEvent(input: string, key: Key): boolean {\n if (input !== \"\") return false\n // If any actionable key flag is set, it's not modifier-only\n if (\n key.upArrow ||\n key.downArrow ||\n key.leftArrow ||\n key.rightArrow ||\n key.pageDown ||\n key.pageUp ||\n key.home ||\n key.end ||\n key.return ||\n key.escape ||\n key.tab ||\n key.backspace ||\n key.delete\n )\n return false\n // Empty input + no actionable flags = modifier-only event\n return true\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\n// Re-export Key for consumers that import from useInput\nexport type { Key } from \"@silvery/ag/keys\"\n\n/**\n * Input handler callback type.\n */\nexport type InputHandler = (input: string, key: Key) => void\n\n/**\n * Options for useInput hook.\n */\nexport interface UseInputOptions {\n /**\n * Enable or disable input handling.\n * Useful when there are multiple useInput hooks and you want to disable some.\n * @default true\n */\n isActive?: boolean\n\n /**\n * Callback for bracketed paste events.\n * When the terminal has bracketed paste mode enabled,\n * pasted text is delivered as a single string instead of\n * individual keystrokes.\n */\n onPaste?: (text: string) => void\n\n /**\n * Callback for key release events.\n * Requires Kitty protocol with REPORT_EVENTS flag enabled.\n * When provided, release events are dispatched here instead of being silently dropped.\n *\n * @example\n * ```tsx\n * useInput((input, key) => {\n * // Handle press/repeat events\n * }, {\n * onRelease: (input, key) => {\n * // Handle release events (e.g., stop scrolling, end drag)\n * },\n * })\n * ```\n */\n onRelease?: InputHandler\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/**\n * Hook for handling user input.\n *\n * Throws if RuntimeContext is not provided (i.e., outside a runtime).\n * Use useRuntime() for components that work in both interactive and static modes.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * useInput((input, key) => {\n * if (input === 'q') {\n * // Quit\n * }\n * if (key.upArrow) {\n * // Move up\n * }\n * }, {\n * onRelease: (input, key) => {\n * // Handle key release (requires Kitty REPORT_EVENTS)\n * },\n * });\n *\n * return <Text>Press q to quit</Text>;\n * }\n * ```\n */\nexport function useInput(inputHandler: InputHandler, options: UseInputOptions = {}): void {\n const rt = useContext(RuntimeContext)\n\n if (!rt) {\n throw new Error(\n \"useInput requires a runtime (run/render/createApp/test renderer). \" +\n \"Use useRuntime() for components that work in both static and interactive modes.\",\n )\n }\n\n const { isActive = true, onPaste, onRelease } = options\n\n // Stable ref for the handler — avoids tearing down/recreating the\n // subscription on every render. Without this, rapid keystrokes between\n // effect cleanup and setup are lost.\n const handlerRef = useRef(inputHandler)\n handlerRef.current = inputHandler\n\n const onPasteRef = useRef(onPaste)\n onPasteRef.current = onPaste\n\n const onReleaseRef = useRef(onRelease)\n onReleaseRef.current = onRelease\n\n // Subscribe to input events via RuntimeContext\n useEffect(() => {\n if (!isActive) return\n\n return rt.on(\"input\", (input: string, key: Key) => {\n // Skip modifier-only keys (Cmd, Shift, Ctrl, Alt pressed alone).\n // These are handled by useModifierKeys, not useInput consumers.\n if (isModifierOnlyEvent(input, key)) return\n // Release events are dispatched to onRelease if provided,\n // otherwise silently dropped (handlers expect press-only semantics).\n if (key.eventType === \"release\") {\n onReleaseRef.current?.(input, key)\n return\n }\n handlerRef.current(input, key)\n })\n }, [isActive, rt])\n\n // Subscribe to paste events via RuntimeContext\n useEffect(() => {\n if (!isActive) return\n\n return rt.on(\"paste\", (text: string) => {\n onPasteRef.current?.(text)\n })\n }, [isActive, rt])\n}\n",
101
+ "/**\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",
102
+ "/**\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",
103
+ "/**\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",
104
+ "/**\n * @silvery/ag-term — Terminal rendering target for silvery.\n *\n * Provides terminal buffer, pipeline, output, input protocols,\n * layout engine, render adapters, and Unicode text utilities.\n *\n * @packageDocumentation\n */\n\n// =============================================================================\n// Buffer\n// =============================================================================\n\nexport type { TerminalBuffer, Color } from \"./buffer\"\nexport { colorEquals, DEFAULT_BG, isDefaultBg } from \"./buffer\"\n\n// =============================================================================\n// Pipeline\n// =============================================================================\n\nexport { executeRender, type PipelineConfig, type ExecuteRenderOptions } from \"./pipeline\"\nexport {\n outputPhase,\n setOutputCaps,\n createOutputPhase,\n type OutputPhaseFn,\n type OutputCaps,\n} from \"./pipeline/output-phase\"\nexport type { PipelineContext } from \"./pipeline/types\"\n\n// =============================================================================\n// App Types\n// =============================================================================\n\nexport type { App } from \"./app\"\nexport type { BoundTerm } from \"./bound-term\"\n\n// =============================================================================\n// Layout Engine\n// =============================================================================\n\nexport type { LayoutEngine, LayoutNode, LayoutConstants, MeasureFunc, MeasureMode } from \"./layout-engine\"\n\n// =============================================================================\n// Render Adapter\n// =============================================================================\n\nexport {\n setRenderAdapter,\n getRenderAdapter,\n hasRenderAdapter,\n getTextMeasurer,\n ensureRenderAdapterInitialized,\n} from \"./render-adapter\"\nexport type {\n RenderAdapter,\n RenderBuffer,\n RenderStyle,\n TextMeasurer,\n TextMeasureResult,\n TextMeasureStyle,\n BorderChars,\n} from \"./render-adapter\"\n\n// Canvas adapter\nexport { createCanvasAdapter, CanvasRenderBuffer } from \"./adapters/canvas-adapter\"\nexport type { CanvasAdapterConfig } from \"./adapters/canvas-adapter\"\n\n// DOM adapter\nexport { createDOMAdapter, DOMRenderBuffer, injectDOMStyles } from \"./adapters/dom-adapter\"\nexport type { DOMAdapterConfig } from \"./adapters/dom-adapter\"\n\n// =============================================================================\n// ANSI Sanitizer\n// =============================================================================\n\nexport {\n sanitizeAnsi,\n tokenizeAnsi,\n isCSISGR,\n extractColonSGRReplacements,\n createColonSGRTracker,\n} from \"./ansi-sanitize\"\nexport type { AnsiToken, ColonSGRReplacement } from \"./ansi-sanitize\"\n\n// =============================================================================\n// ANSI Escape Sequences / Output\n// =============================================================================\n\nexport {\n ANSI,\n BEL,\n enableMouse,\n disableMouse,\n KittyFlags,\n enableKittyKeyboard,\n disableKittyKeyboard,\n queryKittyKeyboard,\n notify,\n notifyITerm2,\n notifyKitty,\n reportDirectory,\n setWindowTitle,\n setWindowAndIconTitle,\n resetWindowTitle,\n setCursorStyle,\n resetCursorStyle,\n setMouseCursorShape,\n resetMouseCursorShape,\n} from \"./output\"\nexport type { CursorShape, MouseCursorShape } from \"./output\"\n\n// =============================================================================\n// Bracketed Paste\n// =============================================================================\n\nexport {\n enableBracketedPaste,\n disableBracketedPaste,\n parseBracketedPaste,\n PASTE_START,\n PASTE_END,\n} from \"./bracketed-paste\"\nexport type { BracketedPasteResult } from \"./bracketed-paste\"\n\n// =============================================================================\n// OSC 52 Clipboard\n// =============================================================================\n\nexport { copyToClipboard, requestClipboard, parseClipboardResponse } from \"./clipboard\"\n\n// =============================================================================\n// OSC 4 Palette Color Query/Set\n// =============================================================================\n\nexport { queryPaletteColor, setPaletteColor, parsePaletteResponse, queryMultiplePaletteColors } from \"./osc-palette\"\n\n// =============================================================================\n// OSC 133 Semantic Prompt Markers\n// =============================================================================\n\nexport { OSC133 } from \"./osc-markers\"\n\n// =============================================================================\n// Kitty Protocol Detection\n// =============================================================================\n\nexport { detectKittySupport, detectKittyFromStdio, type KittyDetectResult } from \"./kitty-detect\"\n\n// =============================================================================\n// Kitty Protocol Manager\n// =============================================================================\n\nexport { createKittyManager, type KittyManager, type KittyManagerOptions } from \"./kitty-manager\"\n\n// =============================================================================\n// Terminal Capability Detection\n// =============================================================================\n\nexport { detectTerminalCaps, defaultCaps, type TerminalCaps } from \"./terminal-caps\"\n\n// =============================================================================\n// Terminal Capability Visual Test\n// =============================================================================\n\nexport { runTermtest, TERMTEST_SECTIONS, type TermtestSection, type TermtestOptions } from \"./termtest\"\n\n// =============================================================================\n// Text Sizing (OSC 66)\n// =============================================================================\n\nexport { textSized, isPrivateUseArea, isTextSizingLikelySupported, detectTextSizingSupport } from \"./text-sizing\"\n\n// =============================================================================\n// CSI 6n Cursor Position Query\n// =============================================================================\n\nexport { queryCursorPosition, queryCursorFromStdio } from \"./cursor-query\"\n\n// =============================================================================\n// OSC 10/11/12 Terminal Color Queries\n// =============================================================================\n\nexport {\n queryForegroundColor,\n queryBackgroundColor,\n queryCursorColor,\n setForegroundColor,\n setBackgroundColor,\n setCursorColor,\n resetForegroundColor,\n resetBackgroundColor,\n resetCursorColor,\n detectColorScheme,\n} from \"./terminal-colors\"\n\n// =============================================================================\n// DA1/DA2/DA3 + XTVERSION Device Attribute Queries\n// =============================================================================\n\nexport {\n queryPrimaryDA,\n querySecondaryDA,\n queryTertiaryDA,\n queryTerminalVersion,\n queryDeviceAttributes,\n type DeviceAttributes,\n} from \"./device-attrs\"\n\n// =============================================================================\n// Focus Reporting\n// =============================================================================\n\nexport { enableFocusReporting, disableFocusReporting, parseFocusEvent } from \"./focus-reporting\"\n\n// =============================================================================\n// DECRQM Mode Query\n// =============================================================================\n\nexport { queryMode, queryModes, DecMode } from \"./mode-query\"\n\n// =============================================================================\n// CSI 14t/18t Pixel and Text Area Size\n// =============================================================================\n\nexport { queryTextAreaPixels, queryTextAreaSize, queryCellSize } from \"./pixel-size\"\n\n// =============================================================================\n// TermDef Resolution\n// =============================================================================\n\nexport { resolveTermDef, resolveFromTerm, isTerm, isTermDef, createInputEvents, type ResolvedTermDef } from \"./term-def\"\n\n// =============================================================================\n// Hit Registry (Mouse Support) — React-free core only\n// =============================================================================\n//\n// The barrel exports only the pure core (class, types, constants).\n// React hooks and context are available via @silvery/ag-term/hit-registry.\n//\n\nexport { HitRegistry, resetHitRegionIdCounter, Z_INDEX } from \"./hit-registry-core\"\nexport type { HitTarget, HitRegion } from \"./hit-registry-core\"\n\n// =============================================================================\n// Mouse Parsing (SGR mode 1006)\n// =============================================================================\n\nexport { parseMouseSequence, isMouseSequence, type ParsedMouse } from \"./mouse\"\n\n// =============================================================================\n// Mouse Events (DOM-level)\n// =============================================================================\n\nexport {\n hitTest,\n createMouseEvent,\n createWheelEvent,\n dispatchMouseEvent,\n processMouseEvent,\n createMouseEventProcessor,\n checkDoubleClick,\n createDoubleClickState,\n computeEnterLeave,\n type SilveryMouseEvent,\n type SilveryWheelEvent,\n type MouseEventProps,\n type MouseEventProcessorOptions,\n type MouseEventProcessorState,\n type KeyboardModifierState,\n updateKeyboardModifiers,\n} from \"./mouse-events\"\n\n// =============================================================================\n// Non-TTY Utilities\n// =============================================================================\n\nexport { isTTY, resolveNonTTYMode, stripAnsi } from \"./non-tty\"\nexport type { NonTTYOptions, ResolvedNonTTYMode } from \"./non-tty\"\n\n// =============================================================================\n// DevTools — available via @silvery/ag-term/devtools (not re-exported here to\n// keep this barrel React-free; devtools imports the React reconciler)\n// =============================================================================\n\n// =============================================================================\n// Inspector\n// =============================================================================\n\nexport {\n enableInspector,\n disableInspector,\n isInspectorEnabled,\n inspectTree,\n inspectFrame,\n autoEnableInspector,\n} from \"./inspector\"\nexport type { InspectorOptions } from \"./inspector\"\n\n// =============================================================================\n// Unicode Text Utilities\n// =============================================================================\n\nexport {\n // Measurement\n displayWidth,\n displayWidthAnsi,\n measureText,\n // Manipulation\n wrapText,\n truncateText,\n padText,\n constrainText,\n sliceByWidth,\n sliceByWidthRange,\n sliceByWidthFromEnd,\n // ANSI handling\n hasAnsi,\n parseAnsiText,\n stripAnsi as stripAnsiUnicode,\n truncateAnsi,\n // Grapheme operations\n splitGraphemes,\n graphemeCount,\n graphemeWidth,\n // Character detection\n isWideGrapheme,\n isZeroWidthGrapheme,\n isCJK,\n isLikelyEmoji,\n hasWideCharacters,\n hasZeroWidthCharacters,\n // Emoji presentation\n ensureEmojiPresentation,\n // Text sizing state\n setTextSizingEnabled,\n isTextSizingEnabled,\n // Text-presentation emoji width\n setTextEmojiWide,\n // Buffer writing\n writeTextToBuffer,\n writeTextTruncated,\n writeLinesToBuffer,\n // Utilities\n normalizeText,\n getFirstCodePoint,\n} from \"./unicode\"\nexport type { StyledSegment } from \"./unicode\"\n\n// Width measurer factory\nexport { createWidthMeasurer, createMeasurer, runWithMeasurer, type Measurer, type WidthMeasurer } from \"./unicode\"\n\n// Measurer composition (term + measurement)\nexport { withMeasurer, createPipeline, type MeasuredTerm } from \"./measurer\"\n\n// withRender plugin — available via @silvery/tea/with-render (not re-exported\n// here to keep this barrel React-free; withRender's renderStatic() pulls React)\n\n// =============================================================================\n// Scroll Utilities\n// =============================================================================\n\nexport { calcEdgeBasedScrollOffset } from \"./scroll-utils\"\n\n// Scroll Region Optimization (DECSTBM)\nexport {\n setScrollRegion,\n resetScrollRegion,\n scrollUp,\n scrollDown,\n moveCursor,\n supportsScrollRegions,\n} from \"./scroll-region\"\nexport type { ScrollRegionConfig } from \"./scroll-region\"\n\n// =============================================================================\n// Pane Manager\n// =============================================================================\n\nexport type { LayoutNode as SplitLayoutNode } from \"./pane-manager\"\nexport {\n createLeaf,\n splitPane,\n removePane,\n getPaneIds,\n findAdjacentPane,\n resizeSplit,\n swapPanes,\n getTabOrder as getSplitTabOrder,\n} from \"./pane-manager\"\n\n// =============================================================================\n// Scheduler\n// =============================================================================\n\nexport { IncrementalRenderMismatchError } from \"./errors\"\n\n// =============================================================================\n// ANSI Primitives (merged from @silvery/ansi)\n// =============================================================================\n\n// Term factory and lazy instance\nexport { createTerm, term } from \"./ansi/index\"\nexport type { Term, StyleChain, TermEmulatorBackend } from \"./ansi/index\"\n\n// Console patching\nexport { patchConsole } from \"./ansi/index\"\nexport type { PatchedConsole, PatchConsoleOptions, ConsoleStats } from \"./ansi/index\"\n\n// Types\nexport type {\n UnderlineStyle,\n RGB,\n ColorLevel,\n Color as AnsiColor,\n AnsiColorName,\n StyleOptions,\n ConsoleMethod,\n ConsoleEntry,\n CreateTermOptions,\n} from \"./ansi/index\"\n\n// Detection\nexport { detectCursor, detectInput, detectColor, detectUnicode, detectExtendedUnderline } from \"./ansi/index\"\n\n// Utilities\nexport { ANSI_REGEX, displayLength } from \"./ansi/index\"\n\n// Underline functions\nexport {\n underline as ansiUnderline,\n curlyUnderline,\n dottedUnderline,\n dashedUnderline,\n doubleUnderline,\n underlineColor,\n styledUnderline,\n} from \"./ansi/index\"\n\n// Hyperlinks\nexport { hyperlink } from \"./ansi/index\"\n\n// ANSI control helpers (re-exported as ansi* to avoid conflicts with term's own)\nexport {\n enterAltScreen,\n leaveAltScreen,\n clearScreen,\n clearLine,\n cursorTo,\n cursorHome,\n cursorHide,\n cursorShow,\n cursorStyle as ansiCursorStyle,\n setTitle,\n enableSyncUpdate,\n disableSyncUpdate,\n} from \"./ansi/index\"\n\n// Background override\nexport { BG_OVERRIDE_CODE, bgOverride } from \"./ansi/index\"\n\n// =============================================================================\n// Selection\n// =============================================================================\n\nexport {\n selectionUpdate,\n createSelectionState,\n normalizeRange,\n extractText,\n type SelectionState,\n type SelectionRange,\n type SelectionPosition,\n type SelectionAction,\n type SelectionEffect,\n} from \"./selection\"\n\nexport { renderSelectionOverlay } from \"./selection-renderer\"\n\n// =============================================================================\n// Virtual Scrollback\n// =============================================================================\n\nexport { createVirtualScrollback, type VirtualScrollback, type VirtualScrollbackOptions } from \"./virtual-scrollback\"\n\n// =============================================================================\n// History Buffer\n// =============================================================================\n\nexport { createHistoryBuffer, createHistoryItem, type HistoryItem, type HistoryBuffer } from \"./history-buffer\"\n\n// =============================================================================\n// Viewport Compositor\n// =============================================================================\n\nexport { composeViewport, type ViewportCompositorConfig, type ComposedViewport } from \"./viewport-compositor\"\n\n// =============================================================================\n// List Document\n// =============================================================================\n\nexport { createListDocument, type ListDocument, type DocumentSource, type LiveItemBlock } from \"./list-document\"\n\n// =============================================================================\n// Text Surface\n// =============================================================================\n\nexport { createTextSurface, type TextSurface, type SurfaceCapabilities } from \"./text-surface\"\n\n// =============================================================================\n// Search Overlay\n// =============================================================================\n\nexport {\n createSearchState,\n searchUpdate,\n renderSearchBar,\n type SearchState,\n type SearchMatch,\n type SearchAction,\n type SearchEffect,\n} from \"./search-overlay\"\n",
105
+ "/**\n * ANSI output constants and terminal control utilities.\n *\n * NOTE: Buffer rendering is handled by pipeline/output-phase.ts.\n * This file contains only terminal control sequences and constants.\n */\n\nimport { hostname } from \"node:os\"\n\n// ============================================================================\n// ANSI Escape Codes\n// ============================================================================\n\nconst ESC = \"\\x1b\"\nconst CSI = `${ESC}[`\n\n// Cursor control\nconst CURSOR_HIDE = `${CSI}?25l`\nconst CURSOR_SHOW = `${CSI}?25h`\nconst CURSOR_HOME = `${CSI}H`\n\n// Synchronized Update Mode (DEC private mode 2026)\n// Tells the terminal to batch output and paint atomically, preventing tearing.\n// Supported by: Ghostty, Kitty, WezTerm, iTerm2, Foot, Alacritty 0.14+, tmux 3.2+\n// Terminals that don't support it safely ignore these sequences.\nconst SYNC_BEGIN = `${CSI}?2026h`\nconst SYNC_END = `${CSI}?2026l`\n\n// Style reset\nconst RESET = `${CSI}0m`\n\n// SGR (Select Graphic Rendition) codes\nconst SGR = {\n // Attributes\n bold: 1,\n dim: 2,\n italic: 3,\n underline: 4,\n blink: 5,\n inverse: 7,\n hidden: 8,\n strikethrough: 9,\n\n // Attribute resets\n boldOff: 22, // Also resets dim\n italicOff: 23,\n underlineOff: 24,\n blinkOff: 25,\n inverseOff: 27,\n hiddenOff: 28,\n strikethroughOff: 29,\n\n // Colors (foreground)\n fgDefault: 39,\n fgBlack: 30,\n fgRed: 31,\n fgGreen: 32,\n fgYellow: 33,\n fgBlue: 34,\n fgMagenta: 35,\n fgCyan: 36,\n fgWhite: 37,\n fgBrightBlack: 90,\n fgBrightRed: 91,\n fgBrightGreen: 92,\n fgBrightYellow: 93,\n fgBrightBlue: 94,\n fgBrightMagenta: 95,\n fgBrightCyan: 96,\n fgBrightWhite: 97,\n\n // Colors (background)\n bgDefault: 49,\n bgBlack: 40,\n bgRed: 41,\n bgGreen: 42,\n bgYellow: 43,\n bgBlue: 44,\n bgMagenta: 45,\n bgCyan: 46,\n bgWhite: 47,\n bgBrightBlack: 100,\n bgBrightRed: 101,\n bgBrightGreen: 102,\n bgBrightYellow: 103,\n bgBrightBlue: 104,\n bgBrightMagenta: 105,\n bgBrightCyan: 106,\n bgBrightWhite: 107,\n} as const\n\n// ============================================================================\n// Cursor Movement\n// ============================================================================\n\n/**\n * Generate ANSI sequence to move cursor to position.\n * Terminal positions are 1-indexed.\n */\nfunction moveCursor(x: number, y: number): string {\n return `${CSI}${y + 1};${x + 1}H`\n}\n\n/**\n * Generate ANSI sequence to move cursor up N lines.\n */\nfunction cursorUp(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}A`\n return `${CSI}${n}A`\n}\n\n/**\n * Generate ANSI sequence to move cursor down N lines.\n */\nfunction cursorDown(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}B`\n return `${CSI}${n}B`\n}\n\n/**\n * Generate ANSI sequence to move cursor right N columns.\n */\nfunction cursorRight(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}C`\n return `${CSI}${n}C`\n}\n\n/**\n * Generate ANSI sequence to move cursor left N columns.\n */\nfunction cursorLeft(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}D`\n return `${CSI}${n}D`\n}\n\n/**\n * Generate ANSI sequence to move cursor to column.\n */\nfunction cursorToColumn(x: number): string {\n return `${CSI}${x + 1}G`\n}\n\n// ============================================================================\n// Cursor Shape (DECSCUSR)\n// ============================================================================\n\n/**\n * Terminal cursor shape. Combined with blink parameter in setCursorStyle().\n */\nexport type CursorShape = \"block\" | \"underline\" | \"bar\"\n\nconst CURSOR_SHAPE_CODES: Record<CursorShape, { blink: number; steady: number }> = {\n block: { blink: 1, steady: 2 },\n underline: { blink: 3, steady: 4 },\n bar: { blink: 5, steady: 6 },\n}\n\n/**\n * Set the terminal cursor shape via DECSCUSR (CSI Ps SP q).\n *\n * Supported by: xterm, Ghostty, Kitty, WezTerm, iTerm2, Alacritty, foot.\n * Terminals that don't support it safely ignore the sequence.\n *\n * @param shape - \"block\", \"underline\", or \"bar\"\n * @param blink - Whether the cursor should blink (default: false)\n */\nexport function setCursorStyle(shape: CursorShape, blink = false): string {\n const code = blink ? CURSOR_SHAPE_CODES[shape].blink : CURSOR_SHAPE_CODES[shape].steady\n return `${CSI}${code} q`\n}\n\n/**\n * Reset the terminal cursor style to the terminal's default (DECSCUSR 0).\n */\nexport function resetCursorStyle(): string {\n return `${CSI}0 q`\n}\n\n// ============================================================================\n// Terminal Control\n// ============================================================================\n\n/**\n * Enter alternate screen buffer, clear screen, and hide cursor.\n * Cursor is hidden by default - applications must explicitly show it for text input.\n *\n * The clear screen (\\x1b[2J) and cursor home (\\x1b[H) are essential after entering\n * the alternate buffer to ensure a clean slate. Without this, the terminal may have\n * leftover content from previous sessions that causes rendering artifacts like\n * content appearing at wrong Y positions (bug km-x7ih).\n */\nexport function enterAlternateScreen(): string {\n return `${CSI}?1049h${CSI}2J${CURSOR_HOME}${CURSOR_HIDE}`\n}\n\n/**\n * Leave alternate screen buffer and restore cursor.\n * Includes SYNC_END as a safety belt — ensures synchronized update mode is\n * reset even if the process was interrupted mid-render. Sending SYNC_END\n * when not in sync mode is a harmless no-op.\n */\nexport function leaveAlternateScreen(): string {\n return `${SYNC_END}${CURSOR_SHOW}${CSI}?1049l`\n}\n\n/**\n * Enable mouse tracking.\n */\nexport function enableMouse(): string {\n // 1003: any-event tracking (all mouse motion — clicks, drags, and hover)\n // 1006: SGR encoding (decimal coordinates, no 223-column limit)\n // 1003 supersedes 1000 (click) and 1002 (button-event), so we only need these two.\n return `${CSI}?1003h${CSI}?1006h`\n}\n\n/**\n * Disable mouse tracking.\n */\nexport function disableMouse(): string {\n return `${CSI}?1006l${CSI}?1003l`\n}\n\n/**\n * Kitty keyboard protocol flags (bitfield).\n *\n * | Flag | Bit | Description |\n * | ---- | --- | ---------------------------------------------- |\n * | 1 | 0 | Disambiguate escape codes |\n * | 2 | 1 | Report event types (press/repeat/release) |\n * | 4 | 2 | Report alternate keys |\n * | 8 | 3 | Report all keys as escape codes |\n * | 16 | 4 | Report associated text |\n */\nexport const KittyFlags = {\n DISAMBIGUATE: 1,\n REPORT_EVENTS: 2,\n REPORT_ALTERNATE: 4,\n REPORT_ALL_KEYS: 8,\n REPORT_TEXT: 16,\n} as const\n\n/**\n * Enable Kitty keyboard protocol (push mode).\n * Sends CSI > flags u to opt into the specified modes.\n * Default flags=1 (disambiguate only) for maximum compatibility.\n * Supported: Ghostty, Kitty, WezTerm, foot. Ignored by unsupported terminals.\n *\n * @param flags Bitfield of KittyFlags (default: DISAMBIGUATE)\n */\nexport function enableKittyKeyboard(flags: number = KittyFlags.DISAMBIGUATE): string {\n return `${CSI}>${flags}u`\n}\n\n/**\n * Query Kitty keyboard protocol support.\n * Sends CSI ? u — terminal responds with CSI ? flags u if supported.\n * Parse the response to detect which flags the terminal supports.\n */\nexport function queryKittyKeyboard(): string {\n return `${CSI}?u`\n}\n\n/**\n * Disable Kitty keyboard protocol (pop mode stack).\n * Sends CSI < u to restore previous keyboard mode.\n */\nexport function disableKittyKeyboard(): string {\n return `${CSI}<u`\n}\n\n// ============================================================================\n// Terminal Notifications\n// ============================================================================\n\n/** BEL character — basic terminal bell/notification */\nexport const BEL = \"\\x07\"\n\n/** iTerm2 notification (OSC 9) */\nexport function notifyITerm2(message: string): string {\n return `${ESC}]9;${message}${BEL}`\n}\n\n/** Kitty notification (OSC 99) with optional title */\nexport function notifyKitty(message: string, opts?: { title?: string }): string {\n const params = opts?.title ? `;t=t;${opts.title}` : \"\"\n return `${ESC}]99;i=1:d=0${params};${message}${ESC}\\\\`\n}\n\n/**\n * Send a terminal notification using the best available method.\n *\n * Auto-detects terminal type via TERM_PROGRAM / TERM env vars:\n * - iTerm2 → OSC 9\n * - Kitty → OSC 99\n * - Others → BEL (audible/visual bell)\n */\nexport function notify(stdout: NodeJS.WriteStream, message: string, opts?: { title?: string }): void {\n const termProgram = process.env.TERM_PROGRAM ?? \"\"\n const term = process.env.TERM ?? \"\"\n\n if (termProgram === \"iTerm.app\") {\n stdout.write(notifyITerm2(message))\n } else if (term === \"xterm-kitty\") {\n stdout.write(notifyKitty(message, opts))\n } else {\n stdout.write(BEL)\n }\n}\n\n// ============================================================================\n// Window Title (OSC 0/2)\n// ============================================================================\n\n/**\n * Set the terminal window title using OSC 2 (window title only).\n * Does not affect icon title (tab name in some terminals).\n * Widely supported: xterm, Ghostty, iTerm2, Kitty, WezTerm, Alacritty, foot.\n */\nexport function setWindowTitle(stdout: NodeJS.WriteStream, title: string): void {\n stdout.write(`${ESC}]2;${title}${BEL}`)\n}\n\n/**\n * Set both the window title and icon title using OSC 0.\n * Some terminals treat OSC 0 as equivalent to OSC 2; others also change the\n * dock/taskbar icon name.\n */\nexport function setWindowAndIconTitle(stdout: NodeJS.WriteStream, title: string): void {\n stdout.write(`${ESC}]0;${title}${BEL}`)\n}\n\n/**\n * Reset the terminal window title by sending an empty OSC 2 sequence.\n * The terminal typically reverts to its default title (shell command, etc.).\n */\nexport function resetWindowTitle(stdout: NodeJS.WriteStream): void {\n stdout.write(`${ESC}]2;${BEL}`)\n}\n\n// ============================================================================\n// Directory Reporting\n// ============================================================================\n\n/** Report current working directory to the terminal via OSC 7.\n * Used by terminals (iTerm2, Ghostty, WezTerm) for tab/split directory inheritance.\n */\nexport function reportDirectory(stdout: NodeJS.WriteStream, path: string): void {\n // OSC 7 format: ESC ] 7 ; file://hostname/path BEL\n const host = hostname()\n const encoded = encodeURI(path).replace(/#/g, \"%23\")\n stdout.write(`${ESC}]7;file://${host}${encoded}${BEL}`)\n}\n\n// ============================================================================\n// Mouse Cursor Shape (OSC 22)\n// ============================================================================\n\n/**\n * Mouse cursor shape names for OSC 22.\n *\n * Uses X11/CSS cursor names. Supported by: Ghostty, Kitty (>=0.33), foot,\n * WezTerm (partial). Terminals that don't support OSC 22 safely ignore it.\n */\nexport type MouseCursorShape = \"default\" | \"text\" | \"pointer\" | \"crosshair\" | \"move\" | \"not-allowed\" | \"wait\" | \"help\"\n\n/**\n * Generate OSC 22 sequence to set the mouse cursor shape.\n *\n * @param shape - X11/CSS cursor name\n * @returns ANSI escape sequence string\n */\nexport function setMouseCursorShape(shape: MouseCursorShape): string {\n return `${ESC}]22;${shape}${BEL}`\n}\n\n/**\n * Generate OSC 22 sequence to reset mouse cursor to default.\n *\n * @returns ANSI escape sequence string\n */\nexport function resetMouseCursorShape(): string {\n return `${ESC}]22;default${BEL}`\n}\n\n// ============================================================================\n// Export Constants\n// ============================================================================\n\nexport const ANSI = {\n ESC,\n CSI,\n CURSOR_HIDE,\n CURSOR_SHOW,\n CURSOR_HOME,\n SYNC_BEGIN,\n SYNC_END,\n RESET,\n SGR,\n moveCursor,\n cursorUp,\n cursorDown,\n cursorLeft,\n cursorRight,\n cursorToColumn,\n} as const\n",
106
+ "/**\n * OSC 52 Clipboard Support\n *\n * Provides clipboard access via the OSC 52 terminal protocol.\n * This works across SSH sessions and in terminals that support it.\n *\n * Protocol: OSC 52\n * - Copy: ESC ] 52 ; c ; <base64> BEL\n * - Query: ESC ] 52 ; c ; ? BEL\n * - Response: ESC ] 52 ; c ; <base64> BEL (or ST terminator)\n *\n * Supported by: Ghostty, Kitty, WezTerm, iTerm2, xterm, foot, tmux\n */\n\nconst ESC = \"\\x1b\"\nconst BEL = \"\\x07\"\n\n// ============================================================================\n// Clipboard Operations\n// ============================================================================\n\n/**\n * Copy text to the system clipboard via OSC 52.\n * Encodes the text as base64 and writes the OSC 52 sequence to stdout.\n */\nexport function copyToClipboard(stdout: NodeJS.WriteStream, text: string): void {\n const base64 = Buffer.from(text).toString(\"base64\")\n stdout.write(`${ESC}]52;c;${base64}${BEL}`)\n}\n\n/**\n * Request clipboard contents via OSC 52.\n * Writes the OSC 52 query sequence. The terminal will respond with\n * an OSC 52 response containing the clipboard contents as base64.\n * Use parseClipboardResponse() to decode the response.\n */\nexport function requestClipboard(stdout: NodeJS.WriteStream): void {\n stdout.write(`${ESC}]52;c;?${BEL}`)\n}\n\n// ============================================================================\n// Response Parsing\n// ============================================================================\n\n/** OSC 52 response prefix */\nconst OSC52_PREFIX = `${ESC}]52;c;`\n\n/**\n * Parse an OSC 52 clipboard response and decode the base64 content.\n *\n * Returns the decoded clipboard text, or null if the input is not\n * an OSC 52 clipboard response.\n *\n * Handles both BEL (\\x07) and ST (ESC \\) terminators.\n */\nexport function parseClipboardResponse(input: string): string | null {\n const prefixIdx = input.indexOf(OSC52_PREFIX)\n if (prefixIdx === -1) return null\n\n const contentStart = prefixIdx + OSC52_PREFIX.length\n\n // Reject the query marker — it's not a response\n if (input[contentStart] === \"?\") return null\n\n // Find terminator: BEL (\\x07) or ST (ESC \\)\n let contentEnd = input.indexOf(BEL, contentStart)\n if (contentEnd === -1) {\n contentEnd = input.indexOf(`${ESC}\\\\`, contentStart)\n }\n if (contentEnd === -1) return null\n\n const base64 = input.slice(contentStart, contentEnd)\n return Buffer.from(base64, \"base64\").toString(\"utf-8\")\n}\n",
107
+ "/**\n * Kitty keyboard protocol manager.\n *\n * Handles lifecycle (enable/disable/auto-detect) for the Kitty keyboard\n * protocol. Used by both test and interactive rendering paths.\n *\n * @see https://sw.kovidgoyal.net/kitty/keyboard-protocol/\n */\n\nimport { enableKittyKeyboard, disableKittyKeyboard, queryKittyKeyboard } from \"./output\"\n\n/** Regex to match a Kitty keyboard query response: CSI ? <digits> u */\nconst KITTY_RESPONSE_RE = /\\x1b\\[\\?(\\d+)u/\n\n/** Regex to match a partial Kitty keyboard query response: ESC [ ? <digits> (at least one digit, no trailing 'u') */\nconst KITTY_PARTIAL_RE = /\\x1b\\[\\?\\d+$/\n\n/** Kitty protocol manager handle. */\nexport interface KittyManager {\n /** Whether the kitty keyboard protocol is currently enabled. */\n enabled: boolean\n /** Disable the protocol and clean up any pending detection. */\n cleanup(): void\n}\n\n/** Options for configuring the kitty keyboard protocol manager. */\nexport interface KittyManagerOptions {\n /** Detection mode: \"enabled\" activates immediately, \"auto\" probes the terminal, \"disabled\" does nothing. */\n mode?: \"auto\" | \"enabled\" | \"disabled\"\n /** Bitmask of KittyFlags to enable. Defaults to KittyFlags.DISAMBIGUATE (1). */\n flags?: number\n}\n\n/**\n * Create a kitty protocol manager that handles setup and teardown.\n *\n * Supports three modes:\n * - \"enabled\": enable immediately if stdin/stdout are TTYs\n * - \"auto\": probe the terminal for support, enable if detected\n * - \"disabled\" / undefined: do nothing\n */\nexport function createKittyManager(\n stdin: NodeJS.ReadStream,\n stdout: NodeJS.WriteStream,\n opts: KittyManagerOptions | undefined,\n): KittyManager {\n let enabled = false\n let cancelDetection: (() => void) | undefined\n\n function enable(flagBitmask: number): void {\n stdout.write(enableKittyKeyboard(flagBitmask))\n enabled = true\n }\n\n if (opts) {\n const mode = opts.mode ?? \"auto\"\n const flagBitmask = opts.flags ?? 1 // Default: DISAMBIGUATE\n const isTTY = (stdin as any)?.isTTY && (stdout as any)?.isTTY\n\n if (isTTY) {\n if (mode === \"enabled\") {\n enable(flagBitmask)\n } else if (mode === \"auto\") {\n cancelDetection = initKittyAutoDetection(stdin, stdout, flagBitmask, enable)\n }\n }\n }\n\n return {\n get enabled() {\n return enabled\n },\n cleanup() {\n if (cancelDetection) {\n cancelDetection()\n cancelDetection = undefined\n }\n if (enabled) {\n stdout.write(disableKittyKeyboard())\n enabled = false\n }\n },\n }\n}\n\n/**\n * Initialize kitty keyboard auto-detection.\n *\n * Queries the terminal for support, listens for the response, and enables\n * the protocol if supported. Returns a cleanup function to cancel detection.\n *\n * Uses a synchronous event-based approach (not async) because render() must\n * return synchronously. Delegates to @silvery/ag-term for escape sequences.\n */\nfunction initKittyAutoDetection(\n stdin: NodeJS.ReadStream,\n stdout: NodeJS.WriteStream,\n flagBitmask: number,\n onEnable: (flags: number) => void,\n): () => void {\n // Buffer incoming data as raw bytes to preserve binary integrity (e.g., split UTF-8 sequences).\n // We always work with the concatenated raw bytes and only decode to string for regex matching.\n const rawChunks: Buffer[] = []\n let cleaned = false\n let unmounted = false\n\n /** Decode the full concatenated buffer to string for regex matching. */\n function getBufferAsString(): string {\n return Buffer.concat(rawChunks).toString()\n }\n\n const cleanup = (): void => {\n if (cleaned) return\n cleaned = true\n clearTimeout(timer)\n stdin.removeListener(\"data\", onData)\n\n // Re-emit any buffered data that wasn't the protocol response.\n // Strip both complete protocol responses and partial protocol prefixes\n // (e.g., \"\\x1b[?1\" without the trailing \"u\") — these are protocol artifacts, not user data.\n const allBytes = Buffer.concat(rawChunks)\n rawChunks.length = 0\n const fullString = allBytes.toString()\n let remaining = fullString.replace(KITTY_RESPONSE_RE, \"\")\n remaining = remaining.replace(KITTY_PARTIAL_RE, \"\")\n\n if (remaining.length > 0) {\n // Find where the remaining content starts in the original byte stream\n // by computing the byte offset of the protocol prefix that was stripped.\n const protocolPrefix = fullString.slice(0, fullString.indexOf(remaining))\n const prefixByteLen = Buffer.byteLength(protocolPrefix)\n stdin.unshift(allBytes.subarray(prefixByteLen))\n }\n }\n\n const onData = (data: Uint8Array | string): void => {\n // Buffer raw bytes. For strings, convert to Buffer to preserve byte-level integrity.\n rawChunks.push(typeof data === \"string\" ? Buffer.from(data) : Buffer.from(data))\n\n // Decode the full accumulated buffer to check for the protocol response.\n // This ensures correct handling of multi-byte sequences split across chunks.\n if (KITTY_RESPONSE_RE.test(getBufferAsString())) {\n cleanup()\n if (!unmounted) {\n onEnable(flagBitmask)\n }\n }\n }\n\n // Attach listener before writing the query so synchronous responses are not missed\n stdin.on(\"data\", onData)\n const timer = setTimeout(cleanup, 200)\n\n stdout.write(queryKittyKeyboard())\n\n return () => {\n unmounted = true\n cleanup()\n }\n}\n",
108
+ "/**\n * Terminal capability detection -- re-exported from the ansi submodule.\n */\n\nexport { detectTerminalCaps, defaultCaps, type TerminalCaps } from \"./ansi/detection\"\n",
109
+ "/**\n * Terminal Capability Test\n *\n * Renders labeled test patterns for each terminal feature.\n * Run in any terminal to visually verify what it supports.\n *\n * Usage:\n * import { runTermtest } from \"@silvery/ag-react\"\n * runTermtest() // all sections\n * runTermtest({ sections: [\"emoji\", \"colors\"] }) // specific sections\n *\n * Compare output across terminals to build/verify profiles.\n */\n\nimport { detectTerminalCaps } from \"./terminal-caps\"\n\nconst ESC = \"\\x1b\"\nconst CSI = `${ESC}[`\nconst RESET = `${CSI}0m`\n\nfunction sgr(...codes: (string | number)[]): string {\n return `${CSI}${codes.join(\";\")}m`\n}\n\nfunction sectionHeader(title: string): string {\n return `\\n${sgr(1)}═══ ${title} ═══${RESET}\\n`\n}\n\nfunction row(label: string, content: string): string {\n return ` ${label.padEnd(24)} ${content}${RESET}`\n}\n\n/** Available test sections */\nexport const TERMTEST_SECTIONS = [\n \"sgr\",\n \"underline\",\n \"colors\",\n \"256\",\n \"truecolor\",\n \"unicode\",\n \"emoji\",\n \"borders\",\n \"inverse\",\n \"profile\",\n] as const\n\nexport type TermtestSection = (typeof TERMTEST_SECTIONS)[number]\n\nexport interface TermtestOptions {\n /** Writable stream (defaults to process.stdout) */\n output?: { write(s: string): boolean }\n /** Show only these sections. Omit or empty = all sections. */\n sections?: TermtestSection[]\n}\n\n/**\n * Run the terminal capability test.\n * Pass section names to filter: `runTermtest({ sections: [\"emoji\"] })`\n */\nexport function runTermtest(options?: TermtestOptions): void {\n const w = options?.output ?? process.stdout\n const filter = options?.sections\n const show = (s: TermtestSection) => !filter || filter.length === 0 || filter.includes(s)\n\n const caps = detectTerminalCaps()\n\n w.write(`\\n${sgr(1)}Terminal Capability Test${RESET}\\n`)\n w.write(` Program: ${caps.program || \"(unknown)\"}\\n`)\n w.write(` TERM: ${caps.term || \"(unknown)\"}\\n`)\n w.write(` COLORTERM: ${process.env.COLORTERM || \"(unset)\"}\\n`)\n w.write(` Detected: color=${caps.colorLevel} dark=${caps.darkBackground} nerdfont=${caps.nerdfont}\\n`)\n w.write(` Underline: styles=${caps.underlineStyles} color=${caps.underlineColor}\\n`)\n w.write(` Emoji wide: ${caps.textEmojiWide}\\n`)\n\n if (show(\"sgr\")) {\n w.write(sectionHeader(\"SGR Text Attributes\"))\n w.write(row(\"Bold\", `${sgr(1)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Dim\", `${sgr(2)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Italic\", `${sgr(3)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Underline\", `${sgr(4)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Strikethrough\", `${sgr(9)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Inverse\", `${sgr(7)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Blink\", `${sgr(5)}The quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"Bold+Italic\", `${sgr(1, 3)}The quick brown fox${RESET}`) + \"\\n\")\n }\n\n if (show(\"underline\")) {\n w.write(sectionHeader(\"SGR 4:x Underline Styles (Terminal.app BREAKS here)\"))\n w.write(row(\"4:1 Single\", `${CSI}4:1mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"4:2 Double\", `${CSI}4:2mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"4:3 Curly\", `${CSI}4:3mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"4:4 Dotted\", `${CSI}4:4mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"4:5 Dashed\", `${CSI}4:5mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(\n row(\"After 4:x (clean?)\", `${CSI}4:3m${RESET}This text should be normal — if garbled, 4:x corrupted SGR state`) +\n \"\\n\",\n )\n\n w.write(sectionHeader(\"SGR 58 Underline Color (Terminal.app BREAKS here)\"))\n w.write(row(\"58;5;1 Red UL\", `${sgr(4)}${CSI}58;5;1mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"58;5;2 Green UL\", `${sgr(4)}${CSI}58;5;2mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"58;5;4 Blue UL\", `${sgr(4)}${CSI}58;5;4mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(row(\"58;2;R;G;B TC UL\", `${sgr(4)}${CSI}58;2;255;128;0mThe quick brown fox${RESET}`) + \"\\n\")\n w.write(\n row(\n \"After SGR 58 (clean?)\",\n `${sgr(4)}${CSI}58;5;1m${RESET}This text should be normal — if garbled, 58 corrupted SGR state`,\n ) + \"\\n\",\n )\n }\n\n if (show(\"colors\")) {\n w.write(sectionHeader(\"ANSI 16 Colors\"))\n const colorNames = [\"Black\", \"Red\", \"Green\", \"Yellow\", \"Blue\", \"Magenta\", \"Cyan\", \"White\"]\n let fgLine = \" FG: \"\n for (let i = 0; i < 8; i++) fgLine += `${sgr(30 + i)} ${colorNames[i]}${RESET}`\n w.write(fgLine + \"\\n\")\n let fgBrLine = \" Br: \"\n for (let i = 0; i < 8; i++) fgBrLine += `${sgr(90 + i)} ${colorNames[i]}${RESET}`\n w.write(fgBrLine + \"\\n\")\n let bgLine = \" BG: \"\n for (let i = 0; i < 8; i++) bgLine += `${sgr(40 + i)} ${colorNames[i]} ${RESET}`\n w.write(bgLine + \"\\n\")\n let bgBrLine = \" BrBG:\"\n for (let i = 0; i < 8; i++) bgBrLine += `${sgr(100 + i)} ${colorNames[i]} ${RESET}`\n w.write(bgBrLine + \"\\n\")\n }\n\n if (show(\"256\")) {\n w.write(sectionHeader(\"256 Colors (indices 0-15, 16-231, 232-255)\"))\n let stdLine = \" 0-15: \"\n for (let i = 0; i < 16; i++) stdLine += `${CSI}48;5;${i}m ${RESET}`\n w.write(stdLine + \"\\n\")\n let cubeLine = \" Cube: \"\n for (let i = 16; i < 52; i++) cubeLine += `${CSI}48;5;${i}m ${RESET}`\n w.write(cubeLine + \"\\n\")\n let grayLine = \" Gray: \"\n for (let i = 232; i < 256; i++) grayLine += `${CSI}48;5;${i}m ${RESET}`\n w.write(grayLine + \"\\n\")\n }\n\n if (show(\"truecolor\")) {\n w.write(sectionHeader(\"Truecolor (38;2;R;G;B / 48;2;R;G;B)\"))\n let tcLine = \" Gradient: \"\n for (let i = 0; i < 40; i++) {\n const r = Math.round((i / 39) * 255)\n const g = Math.round(((39 - i) / 39) * 128)\n tcLine += `${CSI}48;2;${r};${g};80m ${RESET}`\n }\n w.write(tcLine + \"\\n\")\n w.write(row(\"If solid blocks →\", \"Truecolor NOT supported (256-color fallback)\") + \"\\n\")\n }\n\n if (show(\"unicode\")) {\n w.write(sectionHeader(\"Unicode, Emoji, PUA (Nerd Fonts)\"))\n w.write(row(\"ASCII\", \"Hello World! 0123456789\") + \"\\n\")\n w.write(row(\"Latin Extended\", \"àéîõü ñ ß ø å\") + \"\\n\")\n w.write(row(\"CJK\", \"你好世界 日本語 한국어\") + \"\\n\")\n w.write(row(\"Box Drawing\", \"┌─┬─┐ ╔═╦═╗ ╭─╮\") + \"\\n\")\n w.write(row(\"Block Elements\", \"▀▄█▌▐░▒▓\") + \"\\n\")\n w.write(row(\"Symbols\", \"● ○ ◉ ▶ ◀ ⚠ ✓ ✗ ⋮ §\") + \"\\n\")\n w.write(row(\"Emoji\", \"🎉 🚀 📁 📄 ⭐ 🔥 👍\") + \"\\n\")\n w.write(row(\"Nerd Font PUA\", \"\\uF114 folder \\uF0F6 file \\uE0B0 arrow \\uF013 gear\") + \"\\n\")\n w.write(row(\"If PUA = boxes →\", \"Nerd Fonts not installed\") + \"\\n\")\n }\n\n if (show(\"emoji\")) {\n // Each test line places a character then fills to exactly 10 visible columns.\n // If the _'s don't align with the ruler, the terminal's character width\n // disagrees with the expected width (shown in parentheses).\n w.write(sectionHeader(\"Emoji Width Alignment (_'s should align with ruler)\"))\n w.write(\" Ruler: |1234567890|\\n\")\n w.write(\" ASCII 'A': |A_________| (w=1)\\n\")\n w.write(\" CJK '你': |你________| (w=2)\\n\")\n w.write(\" Flag 🇨🇦: |🇨🇦________| (w=2)\\n\")\n w.write(\" Flag 🇺🇸: |🇺🇸________| (w=2)\\n\")\n w.write(\" Emoji 📁: |📁________| (w=2)\\n\")\n w.write(\" Emoji 📄: |📄________| (w=2)\\n\")\n w.write(\" Emoji 📋: |📋________| (w=2)\\n\")\n w.write(\" Emoji ⚠️: |⚠️________| (w=2)\\n\")\n w.write(\" Text ⚠: |⚠_________| (w=1 text, 2 emoji)\\n\")\n w.write(\" Text ⭐: |⭐________| (w=1 text, 2 emoji)\\n\")\n w.write(\" Text ☑: |☑_________| (w=1 text, 2 emoji)\\n\")\n w.write(\" Emoji 🏠: |🏠________| (w=2)\\n\")\n w.write(\" Emoji 👓: |👓________| (w=2)\\n\")\n w.write(\" ZWJ 👨🏻‍💻: |👨🏻‍💻________| (w=2)\\n\")\n w.write(\" Arrow →: |→_________| (w=1)\\n\")\n w.write(\" Arrow ▸: |▸_________| (w=1)\\n\")\n w.write(\" Circle ○: |○_________| (w=1)\\n\")\n w.write(\" Square □: |□_________| (w=1)\\n\")\n w.write(\" Check ✓: |✓_________| (w=1)\\n\")\n }\n\n if (show(\"borders\")) {\n w.write(sectionHeader(\"Box Drawing Borders\"))\n w.write(\" ┌──────────┐ ╔══════════╗ ╭──────────╮\\n\")\n w.write(\" │ single │ ║ double ║ │ round │\\n\")\n w.write(\" └──────────┘ ╚══════════╝ ╰──────────╯\\n\")\n }\n\n if (show(\"inverse\")) {\n w.write(sectionHeader(\"Inverse + Background (potential artifact source)\"))\n w.write(row(\"Red FG + Inverse\", `${sgr(31, 7)}This should have red background${RESET}`) + \"\\n\")\n w.write(row(\"Cyan BG + White FG\", `${sgr(46, 37)}Cyan background, white text${RESET}`) + \"\\n\")\n w.write(row(\"Black BG + White FG\", `${sgr(40, 97)}Black bg, bright white text${RESET}`) + \"\\n\")\n w.write(row(\"White BG + Black FG\", `${sgr(107, 30)}White bg, black text${RESET}`) + \"\\n\")\n\n w.write(sectionHeader(\"Reset Sanity Check\"))\n w.write(\" This line should be completely normal with no formatting artifacts.\\n\")\n w.write(\" If you see colors, underlines, or other styling above, the terminal\\n\")\n w.write(\" failed to process an SGR reset (\\\\x1b[0m) correctly.\\n\")\n }\n\n if (show(\"profile\")) {\n w.write(sectionHeader(\"Detected Terminal Profile\"))\n const entries = Object.entries(caps) as [string, unknown][]\n for (const [key, value] of entries) {\n const indicator = value === true ? \"✓\" : value === false ? \"✗\" : String(value)\n w.write(` ${key.padEnd(20)} ${indicator}\\n`)\n }\n }\n\n w.write(\"\\n\")\n}\n",
110
+ "/**\n * TermDef Resolution\n *\n * Converts TermDef (minimal render config) into resolved values for rendering.\n * Handles auto-detection of events from stdin, dimension defaults, etc.\n */\n\nimport type { ColorLevel, Term } from \"./ansi/index\"\nimport type { Event, TermDef } from \"@silvery/ag/types\"\n\n// ============================================================================\n// Resolved TermDef\n// ============================================================================\n\n/**\n * Resolved values from a TermDef, ready for use by the render system.\n */\nexport interface ResolvedTermDef {\n /** Output stream (may be mock for static rendering) */\n stdout: NodeJS.WriteStream | null\n\n /** Width in columns */\n width: number\n\n /** Height in rows */\n height: number\n\n /** Color level (null = no colors) */\n colors: ColorLevel | null\n\n /** Event source (null = static mode) */\n events: AsyncIterable<Event> | null\n\n /** Whether this is static mode (no events = render until stable) */\n isStatic: boolean\n}\n\n// ============================================================================\n// Resolution Logic\n// ============================================================================\n\n/**\n * Default dimensions when not detectable.\n */\nconst DEFAULT_WIDTH = 80\nconst DEFAULT_HEIGHT = 24\n\n/**\n * Check if a value is a Term instance (duck typing).\n */\nexport function isTerm(value: unknown): value is Term {\n // Term can be a callable Proxy (typeof === 'function') or object\n if (!value || (typeof value !== \"object\" && typeof value !== \"function\")) {\n return false\n }\n const obj = value as Record<string, unknown>\n return (\n typeof obj.hasCursor === \"function\" &&\n typeof obj.hasInput === \"function\" &&\n typeof obj.hasColor === \"function\" &&\n typeof obj.write === \"function\"\n )\n}\n\n/**\n * Check if a value is a TermDef (not a Term).\n */\nexport function isTermDef(value: unknown): value is TermDef {\n if (!value || typeof value !== \"object\") return false\n // TermDef doesn't have hasCursor method\n const obj = value as Record<string, unknown>\n return typeof obj.hasCursor !== \"function\"\n}\n\n/**\n * Resolve a TermDef into concrete values.\n *\n * @param def - TermDef to resolve\n * @returns Resolved values ready for rendering\n */\nexport function resolveTermDef(def: TermDef): ResolvedTermDef {\n // Resolve dimensions\n const width = def.width ?? def.stdout?.columns ?? DEFAULT_WIDTH\n const height = def.height ?? def.stdout?.rows ?? DEFAULT_HEIGHT\n\n // Resolve colors\n let colors: ColorLevel | null = null\n if (def.colors === true) {\n // Auto-detect from stdout\n colors = detectColorLevel(def.stdout)\n } else if (def.colors === false || def.colors === null) {\n colors = null\n } else if (def.colors) {\n colors = def.colors\n } else {\n // Default: auto-detect\n colors = detectColorLevel(def.stdout)\n }\n\n // Resolve events\n let events: AsyncIterable<Event> | null = null\n if (def.events) {\n // Explicit events provided\n events = def.events\n } else if (def.stdin) {\n // Auto-create events from stdin\n events = createInputEvents(def.stdin)\n }\n\n return {\n stdout: def.stdout ?? null,\n width,\n height,\n colors,\n events,\n isStatic: events === null,\n }\n}\n\n/**\n * Resolve a Term instance into ResolvedTermDef.\n *\n * @param term - Term instance\n * @returns Resolved values\n */\nexport function resolveFromTerm(term: Term): ResolvedTermDef {\n return {\n stdout: term.stdout,\n width: term.cols ?? DEFAULT_WIDTH,\n height: term.rows ?? DEFAULT_HEIGHT,\n colors: term.hasColor(),\n // Term instances always have interactive capabilities\n events: createInputEvents(term.stdin),\n isStatic: false,\n }\n}\n\n// ============================================================================\n// Color Detection\n// ============================================================================\n\n/**\n * Detect color level from stdout stream.\n */\nfunction detectColorLevel(stdout?: NodeJS.WriteStream): ColorLevel | null {\n // Check environment variables\n if (process.env.NO_COLOR !== undefined) {\n return null\n }\n\n if (process.env.FORCE_COLOR !== undefined) {\n const level = Number.parseInt(process.env.FORCE_COLOR, 10)\n if (level === 0) return null\n if (level === 1) return \"basic\"\n if (level === 2) return \"256\"\n if (level >= 3) return \"truecolor\"\n return \"basic\"\n }\n\n // Check COLORTERM for truecolor\n if (process.env.COLORTERM === \"truecolor\" || process.env.COLORTERM === \"24bit\") {\n return \"truecolor\"\n }\n\n // Check if TTY\n if (!stdout?.isTTY) {\n return null\n }\n\n // Check TERM for 256 color support\n const term = process.env.TERM ?? \"\"\n if (term.includes(\"256color\") || term.includes(\"256\")) {\n return \"256\"\n }\n\n // Default to basic if TTY\n return \"basic\"\n}\n\n// ============================================================================\n// Input Events\n// ============================================================================\n\n/**\n * Create an async iterable of input events from a stdin stream.\n *\n * This enables interactive mode by providing a source of keyboard events.\n */\nexport function createInputEvents(stdin: NodeJS.ReadStream): AsyncIterable<Event> {\n return {\n [Symbol.asyncIterator](): AsyncIterator<Event> {\n const buffer: Event[] = []\n let resolveNext: ((value: IteratorResult<Event>) => void) | null = null\n let done = false\n\n // Set up stdin reading\n const handleData = (chunk: Buffer | string) => {\n const data = typeof chunk === \"string\" ? chunk : chunk.toString(\"utf8\")\n\n // Convert raw input to key events\n // This is simplified - real implementation would parse ANSI sequences\n for (const char of data) {\n const event: Event = {\n type: \"key\",\n key: char,\n ctrl: char.charCodeAt(0) < 32 && char !== \"\\r\" && char !== \"\\n\" && char !== \"\\t\",\n }\n\n if (resolveNext) {\n resolveNext({ value: event, done: false })\n resolveNext = null\n } else {\n buffer.push(event)\n }\n }\n }\n\n const handleEnd = () => {\n done = true\n if (resolveNext) {\n resolveNext({ value: undefined as unknown as Event, done: true })\n resolveNext = null\n }\n }\n\n // Only set up if stdin supports raw mode\n if (stdin.isTTY && typeof stdin.setRawMode === \"function\") {\n stdin.setEncoding(\"utf8\")\n stdin.on(\"data\", handleData)\n stdin.on(\"end\", handleEnd)\n }\n\n return {\n next(): Promise<IteratorResult<Event>> {\n // Return buffered event if available\n const buffered = buffer.shift()\n if (buffered) {\n return Promise.resolve({ value: buffered, done: false })\n }\n\n // If done, return done\n if (done) {\n return Promise.resolve({\n value: undefined as unknown as Event,\n done: true,\n })\n }\n\n // Wait for next event\n return new Promise((resolve) => {\n resolveNext = resolve\n })\n },\n\n return(): Promise<IteratorResult<Event>> {\n done = true\n stdin.off(\"data\", handleData)\n stdin.off(\"end\", handleEnd)\n return Promise.resolve({\n value: undefined as unknown as Event,\n done: true,\n })\n },\n }\n },\n }\n}\n",
111
+ "/**\n * DOM-level Mouse Events for silvery\n *\n * Provides React DOM-compatible mouse event infrastructure:\n * - SilveryMouseEvent / SilveryWheelEvent synthetic event objects\n * - Tree-based hit testing using screenRect (replaces manual HitRegistry)\n * - Event dispatch with bubbling (target → root, stopPropagation support)\n * - Double-click detection (300ms / 2-cell threshold)\n * - mouseenter/mouseleave tracking (no bubble, like DOM spec)\n */\n\nimport { createLogger } from \"loggily\"\nimport type { FocusManager } from \"@silvery/ag/focus-manager\"\nimport { findFocusableAncestor } from \"@silvery/tea/focus-queries\"\nimport type { ParsedMouse } from \"./mouse\"\nimport { getAncestorPath, pointInRect } from \"@silvery/ag/tree-utils\"\nimport type { AgNode } from \"@silvery/ag/types\"\n\nconst mouseLog = createLogger(\"silvery:mouse\")\n\n// ============================================================================\n// Event Types\n// ============================================================================\n\n/**\n * Synthetic mouse event, mirroring React.MouseEvent / DOM MouseEvent.\n */\nexport interface SilveryMouseEvent {\n /** Terminal column (0-indexed) */\n clientX: number\n /** Terminal row (0-indexed) */\n clientY: number\n /** Mouse button: 0=left, 1=middle, 2=right */\n button: number\n /** Modifier keys */\n altKey: boolean\n ctrlKey: boolean\n metaKey: boolean\n shiftKey: boolean\n /** Deepest node under cursor */\n target: AgNode\n /** Node whose handler is currently firing (changes during bubble) */\n currentTarget: AgNode\n /** Event type */\n type: \"click\" | \"dblclick\" | \"mousedown\" | \"mouseup\" | \"mousemove\" | \"mouseenter\" | \"mouseleave\" | \"wheel\"\n /** Stop event from bubbling to parent nodes */\n stopPropagation(): void\n /** Prevent default behavior */\n preventDefault(): void\n /** Whether stopPropagation() was called */\n readonly propagationStopped: boolean\n /** Whether preventDefault() was called */\n readonly defaultPrevented: boolean\n /** Raw parsed mouse data from SGR protocol */\n nativeEvent: ParsedMouse\n}\n\n/**\n * Synthetic wheel event, extending SilveryMouseEvent with scroll deltas.\n */\nexport interface SilveryWheelEvent extends SilveryMouseEvent {\n /** Vertical scroll: -1 (up) or +1 (down) */\n deltaY: number\n /** Horizontal scroll: always 0 for terminals */\n deltaX: number\n}\n\n// ============================================================================\n// Mouse Event Handler Props (added to BoxProps/TextProps)\n// ============================================================================\n\nexport interface MouseEventProps {\n onClick?: (event: SilveryMouseEvent) => void\n onDoubleClick?: (event: SilveryMouseEvent) => void\n onMouseDown?: (event: SilveryMouseEvent) => void\n onMouseUp?: (event: SilveryMouseEvent) => void\n onMouseMove?: (event: SilveryMouseEvent) => void\n onMouseEnter?: (event: SilveryMouseEvent) => void\n onMouseLeave?: (event: SilveryMouseEvent) => void\n onWheel?: (event: SilveryWheelEvent) => void\n}\n\n// ============================================================================\n// Event Factory\n// ============================================================================\n\n/**\n * Create a synthetic mouse event.\n *\n * Modifier keys are merged from two sources:\n * - SGR mouse protocol: reports Ctrl, Alt/Meta, Shift (reliable)\n * - Keyboard tracking: reports Super/Cmd, Hyper, CapsLock, NumLock (via Kitty protocol)\n *\n * `metaKey` = keyboard-tracked Super (Cmd on macOS). SGR \"meta\" maps to `altKey`.\n */\nexport function createMouseEvent(\n type: SilveryMouseEvent[\"type\"],\n x: number,\n y: number,\n target: AgNode,\n parsed: ParsedMouse,\n keyboardMods?: KeyboardModifierState,\n): SilveryMouseEvent {\n let propagationStopped = false\n let defaultPrevented = false\n const metaKey = keyboardMods?.super ?? false\n if (type === \"click\" || type === \"mousedown\") {\n mouseLog.debug?.(`createMouseEvent(${type}) metaKey=${metaKey} keyboardMods.super=${keyboardMods?.super}`)\n }\n\n return {\n type,\n clientX: x,\n clientY: y,\n button: parsed.button,\n altKey: parsed.meta,\n ctrlKey: parsed.ctrl,\n metaKey,\n shiftKey: parsed.shift,\n target,\n currentTarget: target,\n nativeEvent: parsed,\n get propagationStopped() {\n return propagationStopped\n },\n get defaultPrevented() {\n return defaultPrevented\n },\n stopPropagation() {\n propagationStopped = true\n },\n preventDefault() {\n defaultPrevented = true\n },\n }\n}\n\n/**\n * Create a synthetic wheel event.\n */\nexport function createWheelEvent(\n x: number,\n y: number,\n target: AgNode,\n parsed: ParsedMouse,\n keyboardMods?: KeyboardModifierState,\n): SilveryWheelEvent {\n const base = createMouseEvent(\"wheel\", x, y, target, parsed, keyboardMods) as SilveryWheelEvent\n base.deltaY = parsed.delta ?? 0\n base.deltaX = 0\n return base\n}\n\n// ============================================================================\n// Hit Testing\n// ============================================================================\n\n/**\n * Tree-based hit test: find the deepest node whose screenRect contains (x, y).\n * Uses reverse child order (last sibling wins = highest z-order, like DOM).\n * Respects overflow:hidden clipping.\n */\nexport function hitTest(node: AgNode, x: number, y: number): AgNode | null {\n const rect = node.screenRect\n if (!rect) return null\n\n // Check if point is within this node's bounds\n if (!pointInRect(x, y, rect)) return null\n\n // pointerEvents=\"none\" makes this node and its subtree invisible to hit testing\n const props = node.props as { overflow?: string; pointerEvents?: string }\n if (props.pointerEvents === \"none\") return null\n\n // Check overflow clipping — if overflow is \"hidden\" or \"scroll\",\n // children outside this node's rect are not hittable\n const clips = props.overflow === \"hidden\" || props.overflow === \"scroll\"\n\n // DFS: check children in reverse order (last child = top z-order, like DOM)\n for (let i = node.children.length - 1; i >= 0; i--) {\n const child = node.children[i]!\n // If parent clips, skip children whose screenRect doesn't overlap parent\n if (clips) {\n const childRect = child.screenRect\n if (childRect && !pointInRect(x, y, rect)) {\n continue\n }\n }\n const hit = hitTest(child, x, y)\n if (hit) return hit\n }\n\n // Check virtual text children with inlineRects (nested Text inside Text).\n // These don't have screenRect/layoutNode, so standard DFS misses them.\n if (node.type === \"silvery-text\") {\n for (let i = node.children.length - 1; i >= 0; i--) {\n const child = node.children[i]!\n if (child.inlineRects) {\n for (const inlineRect of child.inlineRects) {\n if (pointInRect(x, y, inlineRect)) return child\n }\n }\n }\n }\n\n // No child matched — this node is the target (if it has a screenRect)\n return node\n}\n\n// ============================================================================\n// Event Dispatch\n// ============================================================================\n\n/** Map event type to the handler prop name */\nconst EVENT_HANDLER_MAP: Record<string, keyof MouseEventProps> = {\n click: \"onClick\",\n dblclick: \"onDoubleClick\",\n mousedown: \"onMouseDown\",\n mouseup: \"onMouseUp\",\n mousemove: \"onMouseMove\",\n mouseenter: \"onMouseEnter\",\n mouseleave: \"onMouseLeave\",\n wheel: \"onWheel\",\n}\n\n/**\n * Dispatch a mouse event through the render tree with DOM-style bubbling.\n *\n * Bubbles from target → root, calling the appropriate handler on each node.\n * stopPropagation() halts bubbling. mouseenter/mouseleave do NOT bubble (DOM spec).\n */\nexport function dispatchMouseEvent(event: SilveryMouseEvent): void {\n const handlerProp = EVENT_HANDLER_MAP[event.type]\n if (!handlerProp) return\n\n // mouseenter/mouseleave don't bubble (DOM spec)\n const noBubble = event.type === \"mouseenter\" || event.type === \"mouseleave\"\n\n if (noBubble) {\n // Only fire on the target itself\n const handler = (event.target.props as Record<string, unknown>)[handlerProp] as\n | ((e: SilveryMouseEvent) => void)\n | undefined\n if (handler) {\n const mutableEvent = event as { currentTarget: AgNode }\n mutableEvent.currentTarget = event.target\n handler(event)\n }\n return\n }\n\n // Bubble phase: fire from target up to root\n const path = getAncestorPath(event.target)\n for (const node of path) {\n if (event.propagationStopped) break\n\n const handler = (node.props as Record<string, unknown>)[handlerProp] as ((e: SilveryMouseEvent) => void) | undefined\n if (handler) {\n const mutableEvent = event as { currentTarget: AgNode }\n mutableEvent.currentTarget = node\n handler(event)\n }\n }\n}\n\n// ============================================================================\n// Double-Click Detection\n// ============================================================================\n\nexport interface DoubleClickState {\n lastClickTime: number\n lastClickX: number\n lastClickY: number\n lastClickButton: number\n}\n\nexport function createDoubleClickState(): DoubleClickState {\n return {\n lastClickTime: 0,\n lastClickX: -999,\n lastClickY: -999,\n lastClickButton: -1,\n }\n}\n\nconst DOUBLE_CLICK_TIME_MS = 300\nconst DOUBLE_CLICK_DISTANCE = 2\n\n/**\n * Check if a click qualifies as a double-click, given the previous click state.\n * Updates the state for the next check.\n * Returns true if this is a double-click.\n */\nexport function checkDoubleClick(\n state: DoubleClickState,\n x: number,\n y: number,\n button: number,\n now: number = Date.now(),\n): boolean {\n const timeDelta = now - state.lastClickTime\n const dx = Math.abs(x - state.lastClickX)\n const dy = Math.abs(y - state.lastClickY)\n const sameButton = button === state.lastClickButton\n\n const isDouble =\n sameButton && timeDelta <= DOUBLE_CLICK_TIME_MS && dx <= DOUBLE_CLICK_DISTANCE && dy <= DOUBLE_CLICK_DISTANCE\n\n // Update state\n state.lastClickTime = now\n state.lastClickX = x\n state.lastClickY = y\n state.lastClickButton = button\n\n // If double-click, reset so triple-click doesn't register as another double\n if (isDouble) {\n state.lastClickTime = 0\n }\n\n return isDouble\n}\n\n// ============================================================================\n// Mouse Enter/Leave Tracking\n// ============================================================================\n\n/**\n * Compute mouseenter/mouseleave transitions between two ancestor paths.\n *\n * Returns { entered, left } — arrays of nodes that were entered or left.\n * Mirrors the DOM spec: fire mouseleave on nodes in prevPath not in nextPath,\n * and mouseenter on nodes in nextPath not in prevPath.\n */\nexport function computeEnterLeave(prevPath: AgNode[], nextPath: AgNode[]): { entered: AgNode[]; left: AgNode[] } {\n const prevSet = new Set(prevPath)\n const nextSet = new Set(nextPath)\n\n const entered = nextPath.filter((n) => !prevSet.has(n))\n const left = prevPath.filter((n) => !nextSet.has(n))\n\n return { entered, left }\n}\n\n// ============================================================================\n// High-Level Mouse Event Processor\n// ============================================================================\n\n/**\n * Options for creating a mouse event processor.\n */\nexport interface MouseEventProcessorOptions {\n /** Optional focus manager — enables click-to-focus behavior.\n * On mousedown, the deepest focusable ancestor of the hit target is focused. */\n focusManager?: FocusManager\n}\n\n/**\n * State for the mouse event processor.\n */\n/**\n * Keyboard modifier state tracked from Kitty protocol key events.\n * Merged into mouse events to provide accurate modifier detection\n * (SGR mouse protocol reports Ctrl/Alt/Shift but NOT Cmd/Super).\n */\nexport interface KeyboardModifierState {\n super: boolean\n hyper: boolean\n capsLock: boolean\n numLock: boolean\n}\n\nexport interface MouseEventProcessorState {\n doubleClick: DoubleClickState\n /** Previous hover path (for enter/leave tracking) */\n hoverPath: AgNode[]\n /** Whether the left button is currently down (for click detection) */\n mouseDownTarget: AgNode | null\n /** Optional focus manager for click-to-focus */\n focusManager?: FocusManager\n /** Modifier state from Kitty keyboard events, merged into mouse events */\n keyboardModifiers: KeyboardModifierState\n}\n\nexport function createMouseEventProcessor(options?: MouseEventProcessorOptions): MouseEventProcessorState {\n return {\n doubleClick: createDoubleClickState(),\n hoverPath: [],\n mouseDownTarget: null,\n focusManager: options?.focusManager,\n keyboardModifiers: { super: false, hyper: false, capsLock: false, numLock: false },\n }\n}\n\n/**\n * Update keyboard modifier state from a parsed key event.\n * Call this for every keyboard event so mouse events can include accurate modifiers.\n */\nexport function updateKeyboardModifiers(\n state: MouseEventProcessorState,\n key: { super?: boolean; hyper?: boolean; capsLock?: boolean; numLock?: boolean; eventType?: string },\n): void {\n // On key release events, clear the modifier. On press/repeat, set it.\n const isRelease = key.eventType === \"release\"\n const prevSuper = state.keyboardModifiers.super\n if (key.super !== undefined) state.keyboardModifiers.super = isRelease ? false : key.super\n if (key.hyper !== undefined) state.keyboardModifiers.hyper = isRelease ? false : key.hyper\n if (key.capsLock !== undefined) state.keyboardModifiers.capsLock = key.capsLock\n if (key.numLock !== undefined) state.keyboardModifiers.numLock = key.numLock\n if (state.keyboardModifiers.super !== prevSuper) {\n mouseLog.debug?.(\n `keyboardModifiers.super: ${prevSuper} → ${state.keyboardModifiers.super} (key.super=${key.super}, eventType=${key.eventType})`,\n )\n }\n}\n\n/**\n * Process a raw ParsedMouse event and dispatch DOM-level events on the render tree.\n *\n * Call this for every SGR mouse event received. It handles:\n * - mousedown / mouseup\n * - click (on mouseup if same target as mousedown)\n * - dblclick (based on timing)\n * - mousemove + mouseenter/mouseleave\n * - wheel\n */\nexport function processMouseEvent(state: MouseEventProcessorState, parsed: ParsedMouse, root: AgNode): boolean {\n const { x, y, action } = parsed\n const target = hitTest(root, x, y)\n if (action === \"move\") {\n const nodeType = target?.type ?? \"null\"\n const nodeId = target ? ((target.props as Record<string, unknown>).id ?? \"\") : \"\"\n // Check entire ancestor path for onMouseEnter\n let enterAncestor = \"\"\n if (target) {\n let n: AgNode | null = target\n while (n) {\n if (\"onMouseEnter\" in (n.props as Record<string, unknown>)) {\n enterAncestor = `${n.type}#${(n.props as Record<string, unknown>).id ?? \"\"}`\n break\n }\n n = n.parent\n }\n }\n const newPath = target ? getAncestorPath(target) : []\n const { entered } = computeEnterLeave(state.hoverPath, newPath)\n mouseLog.debug?.(\n `move x=${x} y=${y} target=${nodeType}#${nodeId} enterAncestor=${enterAncestor || \"none\"} entered=${entered.length} prevPath=${state.hoverPath.length}`,\n )\n }\n if (!target) return false\n let defaultPrevented = false\n\n if (action === \"down\") {\n state.mouseDownTarget = target\n\n // Click-to-focus: find nearest focusable ancestor and focus it\n if (state.focusManager) {\n const focusable = findFocusableAncestor(target)\n if (focusable) {\n state.focusManager.focus(focusable, \"mouse\")\n }\n }\n\n const event = createMouseEvent(\"mousedown\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(event)\n } else if (action === \"up\") {\n const event = createMouseEvent(\"mouseup\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(event)\n\n // Click = mouseup on the same node (or ancestor) where mousedown happened\n // DOM actually fires click even if up is on a different element, but the target\n // is the nearest common ancestor. For simplicity, we fire click on the up target\n // if mousedown was on the same target or a descendant.\n if (state.mouseDownTarget) {\n const clickEvent = createMouseEvent(\"click\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(clickEvent)\n if (clickEvent.defaultPrevented) defaultPrevented = true\n\n // Check for double-click\n const isDouble = checkDoubleClick(state.doubleClick, x, y, parsed.button)\n if (isDouble) {\n const dblEvent = createMouseEvent(\"dblclick\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(dblEvent)\n if (dblEvent.defaultPrevented) defaultPrevented = true\n }\n }\n\n state.mouseDownTarget = null\n } else if (action === \"move\") {\n const event = createMouseEvent(\"mousemove\", x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(event)\n\n // Compute enter/leave transitions\n const newPath = getAncestorPath(target)\n const { entered, left } = computeEnterLeave(state.hoverPath, newPath)\n\n // Fire mouseleave on nodes that were left (reverse order = deepest first)\n for (const node of left) {\n const leaveEvent = createMouseEvent(\"mouseleave\", x, y, node, parsed, state.keyboardModifiers)\n dispatchMouseEvent(leaveEvent)\n }\n\n // Fire mouseenter on newly entered nodes (forward order = shallowest first)\n for (const node of entered.reverse()) {\n const enterEvent = createMouseEvent(\"mouseenter\", x, y, node, parsed, state.keyboardModifiers)\n dispatchMouseEvent(enterEvent)\n }\n\n state.hoverPath = newPath\n } else if (action === \"wheel\") {\n const event = createWheelEvent(x, y, target, parsed, state.keyboardModifiers)\n dispatchMouseEvent(event)\n if (event.defaultPrevented) defaultPrevented = true\n }\n return defaultPrevented\n}\n",
112
+ "/**\n * Focus Queries — pure tree query functions for the silvery focus system.\n *\n * All functions are pure: no state, no React, no side effects.\n * They operate on the SilveryNode tree to resolve focusable elements,\n * tab order, spatial navigation targets, and explicit focus links.\n */\n\nimport type { AgNode, Rect } from \"./types\"\n\n// ============================================================================\n// Focusable Detection\n// ============================================================================\n\n/** Check if a node has the focusable prop set to true (or truthy). */\nfunction isFocusable(node: AgNode): boolean {\n if (node.hidden) return false\n const props = node.props as Record<string, unknown>\n return Boolean(props.focusable) && props.display !== \"none\"\n}\n\n/** Check if a node creates a focus scope (isolated Tab cycle). */\nfunction isFocusScope(node: AgNode): boolean {\n const props = node.props as Record<string, unknown>\n return Boolean(props.focusScope)\n}\n\n// ============================================================================\n// Tree Queries\n// ============================================================================\n\n/**\n * Walk up from node to nearest ancestor (or self) with focusable prop.\n * Useful for mouse clicks — find the focusable target from a deep text node.\n */\nexport function findFocusableAncestor(node: AgNode): AgNode | null {\n let current: AgNode | null = node\n while (current) {\n if (isFocusable(current)) return current\n current = current.parent\n }\n return null\n}\n\n/**\n * DFS traversal of focusable nodes in tab order, optionally scoped.\n *\n * When scope is provided, only nodes within that scope subtree are included.\n * If a focusScope node is encountered during traversal, its children are\n * skipped (they belong to a different scope), unless that scope IS the\n * provided scope node.\n */\nexport function getTabOrder(root: AgNode, scope?: AgNode): AgNode[] {\n const result: AgNode[] = []\n const walkRoot = scope ?? root\n\n function walk(node: AgNode): void {\n // Skip hidden nodes (Suspense) and display: none — entire subtree is excluded\n if (node.hidden) return\n const props = node.props as Record<string, unknown>\n if (props.display === \"none\") return\n\n // If this node is a focusScope boundary and it's NOT the walk root,\n // skip its children — they belong to a different Tab cycle.\n // The focusScope node itself may still be focusable (included below).\n if (node !== walkRoot && isFocusScope(node)) {\n // Include the scope node itself if it's focusable, but don't descend\n if (isFocusable(node)) {\n result.push(node)\n }\n return\n }\n\n if (isFocusable(node)) {\n result.push(node)\n }\n\n for (const child of node.children) {\n walk(child)\n }\n }\n\n walk(walkRoot)\n return result\n}\n\n/**\n * Walk up from a node to find the nearest ancestor (or self) with focusScope prop.\n * Returns the testID of the enclosing scope, or null if none found.\n */\nexport function findEnclosingScope(node: AgNode): string | null {\n let current: AgNode | null = node\n while (current) {\n if (isFocusScope(current)) {\n const props = current.props as Record<string, unknown>\n return typeof props.testID === \"string\" ? props.testID : null\n }\n current = current.parent\n }\n return null\n}\n\n/**\n * Find a node by testID in the subtree rooted at root.\n * DFS, returns the first match.\n */\nexport function findByTestID(root: AgNode, testID: string): AgNode | null {\n const props = root.props as Record<string, unknown>\n if (props.testID === testID) return root\n\n for (const child of root.children) {\n const found = findByTestID(child, testID)\n if (found) return found\n }\n return null\n}\n\n// ============================================================================\n// Spatial Navigation\n// ============================================================================\n\n/**\n * Compute center point of a Rect.\n */\nfunction rectCenter(rect: Rect): { cx: number; cy: number } {\n return {\n cx: rect.x + rect.width / 2,\n cy: rect.y + rect.height / 2,\n }\n}\n\n/**\n * Check if a candidate point falls within a 45-degree cone from source\n * in the given direction (tvOS-style spatial navigation).\n *\n * The cone extends from the center of the source rect in the specified\n * direction with a 45-degree half-angle (90-degree total aperture).\n */\nfunction isInCone(\n source: { cx: number; cy: number },\n candidate: { cx: number; cy: number },\n direction: \"up\" | \"down\" | \"left\" | \"right\",\n): boolean {\n const dx = candidate.cx - source.cx\n const dy = candidate.cy - source.cy\n\n // Must be in the correct general direction\n switch (direction) {\n case \"up\":\n if (dy >= 0) return false\n // Within 45-degree cone: |dx| <= |dy|\n return Math.abs(dx) <= Math.abs(dy)\n case \"down\":\n if (dy <= 0) return false\n return Math.abs(dx) <= Math.abs(dy)\n case \"left\":\n if (dx >= 0) return false\n return Math.abs(dy) <= Math.abs(dx)\n case \"right\":\n if (dx <= 0) return false\n return Math.abs(dy) <= Math.abs(dx)\n }\n}\n\n/**\n * Euclidean distance between two points.\n */\nfunction distance(a: { cx: number; cy: number }, b: { cx: number; cy: number }): number {\n const dx = a.cx - b.cx\n const dy = a.cy - b.cy\n return Math.sqrt(dx * dx + dy * dy)\n}\n\n/**\n * Find the nearest focusable candidate in a given direction using\n * 45-degree cone heuristic (tvOS-style spatial navigation).\n *\n * From the center of the source rect, draw a cone in the target direction.\n * Filter candidates whose center falls within the cone. Pick the closest\n * by Euclidean distance.\n *\n * @param from - The currently focused node\n * @param direction - Direction to search\n * @param candidates - All focusable nodes to consider\n * @param layoutFn - Function to get screen rect for a node (null if not laid out)\n */\nexport function findSpatialTarget(\n from: AgNode,\n direction: \"up\" | \"down\" | \"left\" | \"right\",\n candidates: AgNode[],\n layoutFn: (node: AgNode) => Rect | null,\n): AgNode | null {\n const sourceRect = layoutFn(from)\n if (!sourceRect) return null\n\n const source = rectCenter(sourceRect)\n\n let best: AgNode | null = null\n let bestDist = Infinity\n\n for (const candidate of candidates) {\n if (candidate === from) continue\n\n const candidateRect = layoutFn(candidate)\n if (!candidateRect) continue\n\n const target = rectCenter(candidateRect)\n\n if (!isInCone(source, target, direction)) continue\n\n const dist = distance(source, target)\n if (dist < bestDist) {\n bestDist = dist\n best = candidate\n }\n }\n\n return best\n}\n\n// ============================================================================\n// Explicit Focus Links\n// ============================================================================\n\n/**\n * Check if a node has an explicit nextFocus{Direction} override prop.\n *\n * These props allow components to declare explicit focus targets for\n * spatial navigation, overriding the cone heuristic.\n *\n * @param node - The node to check\n * @param direction - Direction string: \"up\", \"down\", \"left\", \"right\"\n * @returns The testID of the explicit target, or null\n */\nexport function getExplicitFocusLink(node: AgNode, direction: string): string | null {\n const props = node.props as Record<string, unknown>\n // Props follow the pattern: nextFocusUp, nextFocusDown, nextFocusLeft, nextFocusRight\n const propName = `nextFocus${direction.charAt(0).toUpperCase()}${direction.slice(1)}`\n const value = props[propName]\n return typeof value === \"string\" ? value : null\n}\n",
113
+ "/**\n * Shared tree utilities for silvery event systems.\n *\n * Functions used by both focus-events.ts and mouse-events.ts.\n */\n\nimport type { AgNode, Rect } from \"./types.js\"\n\n/**\n * Collect the ancestor path from target to root (inclusive).\n */\nexport function getAncestorPath(node: AgNode): AgNode[] {\n const path: AgNode[] = []\n let current: AgNode | null = node\n while (current) {\n path.push(current)\n current = current.parent\n }\n return path\n}\n\n/**\n * Check if a point is inside a rect.\n */\nexport function pointInRect(x: number, y: number, rect: Rect): boolean {\n return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height\n}\n",
114
+ "/**\n * Non-TTY Mode Support for Silvery\n *\n * Provides detection and rendering modes for non-interactive environments:\n * - Piped output (process.stdout.isTTY === false)\n * - CI environments\n * - TERM=dumb\n *\n * When in non-TTY mode, silvery avoids cursor positioning codes that garble\n * output in non-interactive environments.\n *\n * Modes:\n * - 'auto': Detect based on environment (default)\n * - 'tty': Force TTY mode (normal cursor positioning)\n * - 'line-by-line': Simple newline-separated output, no cursor movement\n * - 'static': Single output at end (no updates)\n * - 'plain': Strip all ANSI codes\n */\n\nimport { stripAnsi } from \"./unicode\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Non-TTY rendering mode.\n *\n * - 'auto': Auto-detect based on environment\n * - 'tty': Force TTY mode with cursor positioning\n * - 'line-by-line': Output lines without cursor repositioning\n * - 'static': Single final output only\n * - 'plain': Strip all ANSI escape codes\n */\nexport type NonTTYMode = \"auto\" | \"tty\" | \"line-by-line\" | \"static\" | \"plain\"\n\n/**\n * Options for non-TTY output.\n */\nexport interface NonTTYOptions {\n /** The rendering mode. Default: 'auto' */\n mode?: NonTTYMode\n /** Output stream to check for TTY status. Default: process.stdout */\n stdout?: NodeJS.WriteStream\n}\n\n/**\n * Resolved non-TTY mode after auto-detection.\n */\nexport type ResolvedNonTTYMode = Exclude<NonTTYMode, \"auto\">\n\n// ============================================================================\n// Detection\n// ============================================================================\n\n/**\n * Check if the environment is a TTY.\n *\n * Returns false if:\n * - stdout.isTTY is false or undefined\n * - TERM=dumb\n * - CI environment variables are set\n */\nexport function isTTY(stdout: NodeJS.WriteStream = process.stdout): boolean {\n // Check stdout.isTTY\n if (!stdout.isTTY) {\n return false\n }\n\n // Check TERM=dumb\n if (process.env.TERM === \"dumb\") {\n return false\n }\n\n // Check common CI environment variables\n if (\n process.env.CI ||\n process.env.GITHUB_ACTIONS ||\n process.env.GITLAB_CI ||\n process.env.JENKINS_URL ||\n process.env.BUILDKITE ||\n process.env.CIRCLECI ||\n process.env.TRAVIS\n ) {\n return false\n }\n\n return true\n}\n\n/**\n * Resolve the non-TTY mode based on options and environment.\n *\n * When mode is 'auto':\n * - If TTY detected: returns 'tty'\n * - If non-TTY detected: returns 'line-by-line'\n */\nexport function resolveNonTTYMode(options: NonTTYOptions = {}): ResolvedNonTTYMode {\n const { mode = \"auto\", stdout = process.stdout } = options\n\n if (mode !== \"auto\") {\n return mode\n }\n\n // Auto-detect based on environment\n return isTTY(stdout) ? \"tty\" : \"line-by-line\"\n}\n\n// Re-export stripAnsi from unicode.ts (canonical implementation)\nexport { stripAnsi } from \"./unicode\"\n\n// ============================================================================\n// Line-by-Line Output\n// ============================================================================\n\n/**\n * Convert buffer output to line-by-line format.\n *\n * Instead of using cursor positioning, outputs each line with a simple\n * carriage return and clear-to-end-of-line sequence.\n *\n * @param content The rendered content (may contain ANSI codes but no cursor positioning)\n * @param prevLineCount Number of lines in the previous frame (for clearing)\n * @returns Output string suitable for non-TTY rendering\n */\nexport function toLineByLineOutput(content: string, prevLineCount: number): string {\n const lines = content.split(\"\\n\")\n let output = \"\"\n\n // Move cursor up to overwrite previous content (if any)\n if (prevLineCount > 0) {\n // Move to start of first line\n output += \"\\r\"\n // Move up\n if (prevLineCount > 1) {\n output += `\\x1b[${prevLineCount - 1}A`\n }\n }\n\n // Output each line\n for (let i = 0; i < lines.length; i++) {\n if (i > 0) {\n output += \"\\n\"\n }\n output += lines[i]\n // Clear to end of line (removes leftover content from longer previous lines)\n output += \"\\x1b[K\"\n }\n\n // Clear any remaining lines from previous frame\n const extraLines = prevLineCount - lines.length\n if (extraLines > 0) {\n for (let i = 0; i < extraLines; i++) {\n output += \"\\n\\x1b[K\"\n }\n // Move cursor back up to end of content\n output += `\\x1b[${extraLines}A`\n }\n\n return output\n}\n\n/**\n * Convert buffer output to plain text format.\n *\n * Strips all ANSI codes and outputs simple newline-separated text.\n * No cursor movement or clearing.\n *\n * @param content The rendered content\n * @param prevLineCount Number of lines in the previous frame (unused in plain mode)\n * @returns Plain text output\n */\nexport function toPlainOutput(content: string, _prevLineCount: number): string {\n // Strip ANSI codes\n const plain = stripAnsi(content)\n\n // Trim trailing whitespace from each line but preserve structure\n const lines = plain.split(\"\\n\").map((line) => line.trimEnd())\n\n // Remove trailing empty lines\n while (lines.length > 0 && lines[lines.length - 1] === \"\") {\n lines.pop()\n }\n\n return lines.join(\"\\n\")\n}\n\n// ============================================================================\n// Output Helpers\n// ============================================================================\n\n/**\n * Create an output transformer based on the non-TTY mode.\n *\n * @param mode The resolved non-TTY mode\n * @returns A function that transforms output based on the mode\n */\nexport function createOutputTransformer(mode: ResolvedNonTTYMode): (content: string, prevLineCount: number) => string {\n switch (mode) {\n case \"tty\":\n // Pass through unchanged\n return (content) => content\n\n case \"line-by-line\":\n return toLineByLineOutput\n\n case \"static\":\n // For static mode, we return empty string for intermediate renders\n // The final render is handled by the caller\n return () => \"\"\n\n case \"plain\":\n return toPlainOutput\n }\n}\n\n/**\n * Count the number of lines in a string.\n */\nexport function countLines(str: string): number {\n if (!str) return 0\n return str.split(\"\\n\").length\n}\n",
115
+ "/**\n * silvery Inspector — Debug introspection for rendering pipeline.\n *\n * Activate with SILVERY_DEV=1 env var or by calling enableInspector().\n * Outputs debug info to stderr or a log file (never to the TUI stdout).\n *\n * Features:\n * - Component tree dump (with layout rects)\n * - Focus path display\n * - Render stats (frame time, dirty rows, cell changes)\n * - Dirty region visualization\n *\n * This is DISTINCT from React DevTools (devtools.ts). This inspector provides\n * silvery-specific introspection: render pipeline stats, focus tree, dirty regions,\n * layout info.\n */\n\nimport type { createWriteStream as createWriteStreamType } from \"node:fs\"\nimport type { RenderStats } from \"./scheduler\"\nimport type { AgNode } from \"@silvery/ag/types\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface InspectorOptions {\n /** Output stream (default: process.stderr) */\n output?: NodeJS.WritableStream\n /** Log file path (overrides output stream) */\n logFile?: string\n /** Include layout rects in tree dump */\n showLayout?: boolean\n /** Include style info in tree dump */\n showStyles?: boolean\n}\n\n// =============================================================================\n// State\n// =============================================================================\n\nlet inspectorEnabled = false\nlet inspectorOutput: NodeJS.WritableStream = process.stderr\n\n// =============================================================================\n// Public API\n// =============================================================================\n\n/** Enable the silvery inspector. */\nexport function enableInspector(options?: InspectorOptions): void {\n inspectorEnabled = true\n if (options?.logFile) {\n // Dynamic require to avoid pulling in fs for non-inspector users\n const fs: { createWriteStream: typeof createWriteStreamType } = require(\"node:fs\")\n inspectorOutput = fs.createWriteStream(options.logFile, { flags: \"a\" })\n } else if (options?.output) {\n inspectorOutput = options.output\n } else {\n inspectorOutput = process.stderr\n }\n}\n\n/** Disable the inspector. */\nexport function disableInspector(): void {\n inspectorEnabled = false\n}\n\n/** Check if inspector is active. */\nexport function isInspectorEnabled(): boolean {\n return inspectorEnabled\n}\n\n/**\n * Log render stats after each frame.\n *\n * Called by the scheduler after executeRender completes. When the inspector\n * is disabled this is a no-op (zero overhead).\n */\nexport function inspectFrame(stats: RenderStats): void {\n if (!inspectorEnabled) return\n const line =\n `[silvery] frame #${stats.renderCount} ` +\n `${stats.lastRenderTime.toFixed(1)}ms ` +\n `avg=${stats.avgRenderTime.toFixed(1)}ms ` +\n `skipped=${stats.skippedCount}\\n`\n inspectorOutput.write(line)\n}\n\n/**\n * Dump the component tree structure as indented text.\n *\n * Walks the SilveryNode tree and formats each node with its type, testID,\n * layout rect, and dirty flags.\n */\nexport function inspectTree(rootNode: AgNode, options?: { depth?: number; showLayout?: boolean }): string {\n const maxDepth = options?.depth ?? 10\n const showLayout = options?.showLayout ?? true\n const lines: string[] = []\n\n function walk(node: AgNode, indent: number): void {\n if (indent > maxDepth) return\n\n const prefix = \" \".repeat(indent)\n const type = node.type\n const testID = (node.props as Record<string, unknown>)?.testID\n const idStr = testID ? ` #${testID}` : \"\"\n\n // Layout rect from computed layout node or contentRect\n let rectStr = \"\"\n if (showLayout) {\n if (node.contentRect) {\n const r = node.contentRect\n rectStr = ` [${r.x},${r.y} ${r.width}x${r.height}]`\n } else if (node.layoutNode) {\n const ln = node.layoutNode\n rectStr = ` [${ln.getComputedLeft()},${ln.getComputedTop()} ${ln.getComputedWidth()}x${ln.getComputedHeight()}]`\n }\n }\n\n // Dirty flags\n const dirtyFlags: string[] = []\n if (node.layoutDirty) dirtyFlags.push(\"layout\")\n if (node.contentDirty) dirtyFlags.push(\"content\")\n if (node.stylePropsDirty) dirtyFlags.push(\"paint\")\n if (node.bgDirty) dirtyFlags.push(\"bg\")\n if (node.subtreeDirty) dirtyFlags.push(\"subtree\")\n if (node.childrenDirty) dirtyFlags.push(\"children\")\n const dirtyStr = dirtyFlags.length > 0 ? ` dirty=[${dirtyFlags.join(\",\")}]` : \"\"\n\n // Text content (for text nodes)\n const textStr = node.textContent\n ? ` \"${node.textContent.slice(0, 30)}${node.textContent.length > 30 ? \"...\" : \"\"}\"`\n : \"\"\n\n lines.push(`${prefix}${type}${idStr}${rectStr}${dirtyStr}${textStr}`)\n\n for (const child of node.children) {\n walk(child, indent + 1)\n }\n }\n\n walk(rootNode, 0)\n return lines.join(\"\\n\")\n}\n\n/**\n * Auto-enable if SILVERY_DEV=1 is set.\n *\n * Call this at startup to respect the environment variable convention.\n */\nexport function autoEnableInspector(): void {\n if (process.env.SILVERY_DEV === \"1\" || process.env.SILVERY_DEV === \"true\") {\n const logFile = process.env.SILVERY_DEV_LOG\n enableInspector(logFile ? { logFile } : undefined)\n }\n}\n",
116
+ "/**\n * Measurer composition layer.\n *\n * Creates term-scoped measurers and pipeline configs.\n * Bridges ansi Term with silvery measurement capabilities.\n */\n\nimport type { Term, TerminalCaps } from \"./ansi/index\"\nimport { createWidthMeasurer, type Measurer } from \"./unicode\"\nimport { createOutputPhase } from \"./pipeline/output-phase\"\nimport type { PipelineConfig } from \"./pipeline\"\n\nexport type { Measurer } from \"./unicode\"\n\n/**\n * Term extended with measurement capabilities.\n */\nexport interface MeasuredTerm extends Term, Measurer {}\n\n/**\n * Extend a Term with measurement capabilities.\n *\n * Creates a width measurer from the term's caps and adds measurement\n * methods (displayWidth, graphemeWidth, wrapText, etc.) to the term.\n */\nexport function withMeasurer(term: Term): MeasuredTerm {\n const caps = term.caps\n const measurer = createWidthMeasurer(\n caps ? { textEmojiWide: caps.textEmojiWide, textSizingEnabled: caps.textSizingSupported } : {},\n )\n\n return Object.create(term, {\n textEmojiWide: { get: () => measurer.textEmojiWide, enumerable: true },\n textSizingEnabled: { get: () => measurer.textSizingEnabled, enumerable: true },\n displayWidth: { value: measurer.displayWidth.bind(measurer), enumerable: true },\n displayWidthAnsi: { value: measurer.displayWidthAnsi.bind(measurer), enumerable: true },\n graphemeWidth: { value: measurer.graphemeWidth.bind(measurer), enumerable: true },\n wrapText: { value: measurer.wrapText.bind(measurer), enumerable: true },\n sliceByWidth: { value: measurer.sliceByWidth.bind(measurer), enumerable: true },\n sliceByWidthFromEnd: { value: measurer.sliceByWidthFromEnd.bind(measurer), enumerable: true },\n }) as MeasuredTerm\n}\n\n/**\n * Create a pipeline configuration from caps and/or measurer.\n *\n * This is the single factory for PipelineConfig -- use it instead of\n * manually constructing { measurer, outputPhaseFn }.\n *\n * @param options.caps - Terminal capabilities (for output phase SGR generation)\n * @param options.measurer - Explicit measurer (if omitted, created from caps)\n */\nexport function createPipeline(\n options: {\n caps?: TerminalCaps\n measurer?: Measurer\n } = {},\n): PipelineConfig {\n const { caps, measurer: explicitMeasurer } = options\n const measurer =\n explicitMeasurer ??\n createWidthMeasurer(caps ? { textEmojiWide: caps.textEmojiWide, textSizingEnabled: caps.textSizingSupported } : {})\n const outputPhaseFn = createOutputPhase(\n caps\n ? {\n underlineStyles: caps.underlineStyles,\n underlineColor: caps.underlineColor,\n colorLevel: caps.colorLevel,\n }\n : {},\n measurer,\n )\n return { measurer, outputPhaseFn }\n}\n",
117
+ "/**\n * Virtual scrollback buffer for storing historical rendered content.\n *\n * Used by virtual inline mode to maintain scrollable history while\n * rendering in altscreen (which normally has no scrollback).\n *\n * Implementation uses a circular buffer for O(1) push and bounded memory.\n */\n\nimport { stripAnsi } from \"./unicode\"\n\nexport interface VirtualScrollbackOptions {\n /** Maximum number of lines to store. Default: 10000 */\n maxLines?: number\n}\n\nexport interface VirtualScrollback {\n /** Push rendered lines into history */\n push(lines: string[]): void\n /** Get visible lines at a scroll offset. offset=0 = most recent (bottom) */\n getVisibleRows(offset: number, count: number): string[]\n /** Total number of lines stored */\n readonly totalLines: number\n /** Search for text across all stored lines. Returns indices of matching lines (0 = oldest). */\n search(query: string): number[]\n /** Clear all stored content */\n clear(): void\n}\n\nexport function createVirtualScrollback(options?: VirtualScrollbackOptions): VirtualScrollback {\n const maxLines = options?.maxLines ?? 10_000\n const ansiLines: string[] = new Array(maxLines)\n const plainLines: string[] = new Array(maxLines)\n let head = 0 // next write position\n let count = 0 // total lines stored (capped at maxLines)\n\n return {\n push(lines: string[]): void {\n for (const line of lines) {\n ansiLines[head] = line\n plainLines[head] = stripAnsi(line)\n head = (head + 1) % maxLines\n if (count < maxLines) count++\n }\n },\n\n getVisibleRows(offset: number, rowCount: number): string[] {\n const result: string[] = []\n for (let i = 0; i < rowCount; i++) {\n const logicalIndex = count - offset - rowCount + i\n if (logicalIndex < 0 || logicalIndex >= count) {\n result.push(\"\")\n } else {\n // Convert logical index (0=oldest) to physical position\n const physical = (head - count + logicalIndex + maxLines) % maxLines\n result.push(ansiLines[physical]!)\n }\n }\n return result\n },\n\n get totalLines(): number {\n return count\n },\n\n search(query: string): number[] {\n if (!query) return []\n const lowerQuery = query.toLowerCase()\n const matches: number[] = []\n for (let i = 0; i < count; i++) {\n // logical index i (0=oldest), convert to physical\n const physical = (head - count + i + maxLines) % maxLines\n if (plainLines[physical]!.toLowerCase().includes(lowerQuery)) {\n matches.push(i)\n }\n }\n return matches\n },\n\n clear(): void {\n head = 0\n count = 0\n },\n }\n}\n",
118
+ "/**\n * Ring buffer for frozen scrollback items.\n *\n * Each item represents a rendered list entry (card, row, etc.) with its\n * ANSI snapshot, split rows, and plain-text rows for searching.\n * When total rows exceed maxRows, the oldest items are evicted.\n */\n\nimport { stripAnsi } from \"./unicode\"\n\nexport interface HistoryItem {\n key: string | number\n ansi: string\n rows: string[]\n plainTextRows: string[]\n width: number\n}\n\nexport interface HistoryBuffer {\n push(item: HistoryItem): void\n readonly totalRows: number\n readonly itemCount: number\n getRows(startRow: number, count: number): string[]\n getPlainTextRows(startRow: number, count: number): string[]\n search(query: string): number[]\n getItemAtRow(row: number): { item: HistoryItem; localRow: number } | null\n clear(): void\n readonly maxRows: number\n}\n\nexport function createHistoryItem(key: string | number, ansi: string, width: number): HistoryItem {\n const rows = ansi.split(\"\\n\")\n const plainTextRows = rows.map((r) => stripAnsi(r))\n return { key, ansi, rows, plainTextRows, width }\n}\n\nexport function createHistoryBuffer(maxRows = 10_000): HistoryBuffer {\n // Store items in insertion order; evict from front when over budget.\n let items: HistoryItem[] = []\n let _totalRows = 0\n\n function evict(): void {\n while (_totalRows > maxRows && items.length > 0) {\n const removed = items.shift()!\n _totalRows -= removed.rows.length\n }\n }\n\n /** Walk items to find which item contains the given document row. */\n function resolveRow(row: number): { itemIndex: number; localRow: number } | null {\n if (row < 0 || row >= _totalRows) return null\n let cumulative = 0\n for (let i = 0; i < items.length; i++) {\n const itemRows = items[i]!.rows.length\n if (row < cumulative + itemRows) {\n return { itemIndex: i, localRow: row - cumulative }\n }\n cumulative += itemRows\n }\n return null\n }\n\n return {\n push(item: HistoryItem): void {\n items.push(item)\n _totalRows += item.rows.length\n evict()\n },\n\n get totalRows(): number {\n return _totalRows\n },\n\n get itemCount(): number {\n return items.length\n },\n\n get maxRows(): number {\n return maxRows\n },\n\n getRows(startRow: number, count: number): string[] {\n const result: string[] = []\n for (let r = startRow; r < startRow + count; r++) {\n const resolved = resolveRow(r)\n if (resolved) {\n result.push(items[resolved.itemIndex]!.rows[resolved.localRow]!)\n } else {\n result.push(\"\")\n }\n }\n return result\n },\n\n getPlainTextRows(startRow: number, count: number): string[] {\n const result: string[] = []\n for (let r = startRow; r < startRow + count; r++) {\n const resolved = resolveRow(r)\n if (resolved) {\n result.push(items[resolved.itemIndex]!.plainTextRows[resolved.localRow]!)\n } else {\n result.push(\"\")\n }\n }\n return result\n },\n\n search(query: string): number[] {\n if (!query) return []\n const lowerQuery = query.toLowerCase()\n const matches: number[] = []\n let rowOffset = 0\n for (const item of items) {\n for (let r = 0; r < item.plainTextRows.length; r++) {\n if (item.plainTextRows[r]!.toLowerCase().includes(lowerQuery)) {\n matches.push(rowOffset + r)\n }\n }\n rowOffset += item.rows.length\n }\n return matches\n },\n\n getItemAtRow(row: number): { item: HistoryItem; localRow: number } | null {\n const resolved = resolveRow(row)\n if (!resolved) return null\n return { item: items[resolved.itemIndex]!, localRow: resolved.localRow }\n },\n\n clear(): void {\n items = []\n _totalRows = 0\n },\n }\n}\n",
119
+ "/**\n * Read/query facade over a ListDocument.\n *\n * Provides text extraction, search, hit-testing, and reveal for\n * pane-based scrollback UIs. Pure data — no React, no rendering.\n */\n\nimport type { ListDocument } from \"./list-document\"\nimport type { SearchMatch } from \"./search-overlay\"\nimport { stripAnsi } from \"./unicode\"\n\nexport interface SurfaceCapabilities {\n paneSafe: boolean\n searchableHistory: boolean\n selectableHistory: boolean\n overlayHistory: boolean\n}\n\nexport interface TextSurface {\n readonly id: string\n readonly document: ListDocument\n getText(startRow: number, startCol: number, endRow: number, endCol: number): string\n search(query: string): SearchMatch[]\n hitTest(viewportRow: number, viewportCol: number): { row: number; col: number } | null\n reveal(row: number): void\n /** Notify subscribers that content has changed (search results may be stale) */\n notifyContentChange(): void\n subscribe(listener: () => void): () => void\n readonly capabilities: SurfaceCapabilities\n}\n\nexport function createTextSurface(config: {\n id: string\n document: ListDocument\n viewportToDocument: (viewportRow: number) => number\n onReveal: (documentRow: number) => void\n capabilities: SurfaceCapabilities\n}): TextSurface {\n const listeners = new Set<() => void>()\n\n function notify(): void {\n for (const fn of listeners) fn()\n }\n\n return {\n get id(): string {\n return config.id\n },\n\n get document(): ListDocument {\n return config.document\n },\n\n getText(startRow: number, startCol: number, endRow: number, endCol: number): string {\n const rows = config.document.getRows(startRow, endRow - startRow + 1)\n const lines: string[] = []\n for (let i = 0; i < rows.length; i++) {\n const plain = stripAnsi(rows[i]!)\n const row = startRow + i\n if (row === startRow && row === endRow) {\n lines.push(plain.slice(startCol, endCol))\n } else if (row === startRow) {\n lines.push(plain.slice(startCol))\n } else if (row === endRow) {\n lines.push(plain.slice(0, endCol))\n } else {\n lines.push(plain)\n }\n }\n return lines.join(\"\\n\")\n },\n\n search(query: string): SearchMatch[] {\n return config.document.search(query)\n },\n\n hitTest(viewportRow: number, viewportCol: number): { row: number; col: number } | null {\n const docRow = config.viewportToDocument(viewportRow)\n if (docRow < 0 || docRow >= config.document.totalRows) return null\n return { row: docRow, col: viewportCol }\n },\n\n reveal(row: number): void {\n config.onReveal(row)\n notify()\n },\n\n notifyContentChange(): void {\n notify()\n },\n\n subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n\n get capabilities(): SurfaceCapabilities {\n return config.capabilities\n },\n }\n}\n",
120
+ "/**\n * Ink compat render functions: render(), renderToString(), initInkCompat(), measureElement().\n * @internal\n */\n\nimport React, { useContext } from \"react\"\nimport { StdoutContext, StderrContext, TermContext } from \"@silvery/ag-react/context\"\nimport { bufferToStyledText, bufferToText, type TerminalBuffer } from \"@silvery/ag-term/buffer\"\nimport { stripAnsi } from \"@silvery/ag-term/unicode\"\nimport { createTerm } from \"@silvery/ag-term/ansi\"\nimport { createCursorStore, CursorProvider } from \"@silvery/ag-react/hooks/useCursor\"\nimport { SilveryErrorBoundary } from \"@silvery/ag-react/error-boundary\"\nimport { InkCursorStoreCtx } from \"./with-ink-cursor\"\nimport { InkFocusContext, InkFocusProvider } from \"./with-ink-focus\"\nimport { useInput as silveryUseInput } from \"@silvery/ag-react/hooks/useInput\"\nimport { renderScreenReaderOutput } from \"@silvery/ag-react/accessibility\"\nimport { createKittyManager } from \"@silvery/ag-term\"\nimport { renderSync, type Instance } from \"@silvery/ag-react/render\"\nimport { render as silveryTestRender } from \"@silvery/ag-term/renderer\"\nimport { setInkStrictValidation } from \"@silvery/ag-react/reconciler/host-config\"\nimport { renderStringSync } from \"@silvery/ag-react/render-string\"\nimport { isLayoutEngineInitialized, setLayoutEngine, ensureDefaultLayoutEngine } from \"@silvery/ag-term/layout-engine\"\nimport { createFlexilyZeroEngine } from \"@silvery/ag-term/adapters/flexily-zero-adapter\"\nimport { measureElement as baseMeasureElement } from \"@silvery/ag-react/measureElement\"\nimport { calculateLayout } from \"@silvery/ag-react/reconciler/nodes\"\n\nimport {\n currentChalkLevel,\n ForceStylesCtx,\n InkRenderStateCtx,\n type InkRenderState,\n stripSilveryVS16,\n resolveTerminalColumns,\n resolveTerminalRows,\n} from \"./ink-utils\"\nimport { restoreColonFormatSGR, colonSGRTracker } from \"./ink-sanitize\"\nimport { InkStaticStoreCtx, type InkStaticStore } from \"./ink-components\"\nimport { InkStdinCtx, createInkStdinState } from \"./ink-stdin\"\nimport { type KittyKeyboardOptions, resolveKittyManagerOptions } from \"./ink-hooks\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type { RenderOptions, Instance } from \"@silvery/ag-react/render\"\nexport type { MeasureElementOutput } from \"@silvery/ag-react/measureElement\"\n\n/**\n * Ink-compatible Instance type with additional Ink-specific methods.\n */\ninterface InkInstance extends Instance {\n /** Promise that resolves after pending render output is flushed to stdout */\n waitUntilRenderFlush: () => Promise<void>\n /** Unmount and remove internal instance for this stdout */\n cleanup: () => void\n /** Send raw input to the renderer (equivalent to app.stdin.write) */\n stdin?: { write: (data: string) => void }\n}\n\n// =============================================================================\n// render()\n// =============================================================================\n\n/**\n * Ink-compatible render function.\n *\n * When a custom stdout is provided (fake/spy stdout from tests): delegates to\n * silvery's test renderer with autoRender + onFrame for Ink-compatible output.\n *\n * When no custom stdout (real terminal): delegates to renderSync() which\n * creates a full SilveryInstance with scheduler.\n */\nexport function render(element: import(\"react\").ReactNode, options?: Record<string, unknown>): InkInstance {\n // Enable Ink-compatible strict validation (text must be inside <Text>,\n // <Box> cannot be inside <Text>)\n setInkStrictValidation(true)\n\n // Ensure layout engine is initialized synchronously.\n // For Yoga, call initInkCompat() before render() to async-init the engine.\n if (!isLayoutEngineInitialized()) {\n setLayoutEngine(createFlexilyZeroEngine())\n }\n\n const stdout = options?.stdout as NodeJS.WriteStream | undefined\n const stdin = options?.stdin as NodeJS.ReadStream | undefined\n const isScreenReaderEnabled = (options?.isScreenReaderEnabled as boolean) ?? false\n\n // Screen reader mode: walk the React element tree to produce accessible text\n if (isScreenReaderEnabled && stdout) {\n const screenReaderOutput = renderScreenReaderOutput(element)\n stdout.write(screenReaderOutput)\n let unmounted = false\n const instance: InkInstance = {\n rerender: (newElement: import(\"react\").ReactNode) => {\n if (unmounted) return\n const output = renderScreenReaderOutput(newElement)\n stdout.write(output)\n },\n unmount: () => {\n unmounted = true\n },\n [Symbol.dispose]() {\n instance.unmount()\n },\n waitUntilExit: () => Promise.resolve(),\n waitUntilRenderFlush: () => Promise.resolve(),\n cleanup: () => {\n instance.unmount()\n },\n clear: () => {},\n flush: () => {},\n pause: () => {},\n resume: () => {},\n }\n return instance\n }\n\n // When custom stdout is provided (test mode): delegate to silvery's test\n // renderer with autoRender for async state changes and onFrame for stdout writes.\n if (stdout) {\n return renderTestMode(element, options!, stdout, stdin)\n }\n\n // Interactive mode (real terminal): use renderSync with Ink-compatible defaults\n return renderInteractiveMode(element, options, stdout, stdin)\n}\n\n// =============================================================================\n// Test mode render (custom stdout)\n// =============================================================================\n\nfunction renderTestMode(\n element: import(\"react\").ReactNode,\n options: Record<string, unknown>,\n stdout: NodeJS.WriteStream,\n stdin: NodeJS.ReadStream | undefined,\n): InkInstance {\n // Always render with ANSI codes (plain=false) and ForceStylesCtx=true so that\n // buffer cells are styled even when chalk has no colors. Styled cells enable\n // getContentEdge() to detect content trailing spaces (e.g., chalk.red(' ERROR '))\n // and prevent them from being trimmed as buffer padding.\n // When chalk has no colors, processBuffer strips prop-based ANSI from the output\n // unless the tree contains user-embedded ANSI (tracked via InkRenderState).\n const plain = false\n const chalkHasColors = currentChalkLevel() > 0\n // Per-instance render state: InkText sets hasEmbeddedAnsi when children contain ANSI.\n // processBuffer reads it to decide whether to strip ANSI in plain mode.\n const renderState: InkRenderState = { hasEmbeddedAnsi: false }\n\n // Alternate screen: enter on mount, exit on unmount.\n // Ink requires all three: alternateScreen=true, interactive mode, and stdout.isTTY.\n // interactive defaults to stdout.isTTY when not explicitly set.\n const isTTY = (stdout as any).isTTY === true\n const resolvedInteractive = options?.interactive !== undefined ? Boolean(options.interactive) : isTTY\n const useAltScreen = (options?.alternateScreen as boolean) === true && resolvedInteractive && isTTY\n let altScreenExited = false\n\n if (useAltScreen) {\n stdout.write(\"\\x1b[?1049h\")\n }\n\n const stderr = options?.stderr as NodeJS.WriteStream | undefined\n const debug = (options?.debug as boolean) ?? false\n\n // Per-instance stdin state for raw mode tracking and paste event bridging\n const stdinState = createInkStdinState((stdin ?? process.stdin) as NodeJS.ReadStream, stdout)\n\n // Kitty keyboard protocol support (test renderer path)\n const kittyManager = createKittyManager(\n (stdin ?? process.stdin) as NodeJS.ReadStream,\n stdout,\n resolveKittyManagerOptions(options?.kittyKeyboard as KittyKeyboardOptions | undefined),\n )\n\n // Per-instance cursor store for Ink's useCursor hook\n const cursorStore = createCursorStore()\n let cursorWasShown = false\n\n // Per-instance static output store for Ink's Static component\n const staticStore: InkStaticStore = { renderedCount: 0, fullStaticOutput: \"\" }\n\n // Track latest rendered output for debug mode replay (useStdout/useStderr write).\n // Set in writeFrame (onFrame callback) after each render. In debug mode,\n // hook writes that fire before the first frame are deferred and flushed\n // when writeFrame first runs.\n let lastOutput = \"\"\n // Deferred debug writes: queued when effects fire before the first writeFrame\n let pendingDebugWrites: Array<{ target: \"stdout\" | \"stderr\"; data: string }> = []\n\n /**\n * Compute processed output from a terminal buffer.\n * Converts buffer to text, strips VS16, applies chalk compat.\n * Uses contentHeight to trim buffer padding while preserving\n * layout-meaningful empty rows (from margin, padding, explicit height).\n *\n * @param buffer - Terminal buffer to process\n * @param contentHeight - Root layout height (from renderer callback)\n */\n function processBuffer(buffer: TerminalBuffer, contentHeight?: number): string {\n // ForceStylesCtx is always true, so buffer cells are styled even when chalk\n // has no colors. This enables correct content edge detection for trailing\n // whitespace preservation (e.g., `<Text color=\"red\">{' ERROR '}</Text>`).\n //\n // When chalk has no colors AND no embedded ANSI: use bufferToText for plain\n // output with getContentEdge-based trimming (detects styled cells in buffer).\n // When chalk has no colors AND has embedded ANSI: use bufferToStyledText to\n // preserve user-provided ANSI sequences (SGR, OSC 8) in the output.\n // When chalk has colors: use bufferToStyledText for full ANSI output.\n let output: string\n if (!chalkHasColors && !renderState.hasEmbeddedAnsi) {\n // Plain mode: styled buffer → plain text with content-edge-aware trimming.\n // bufferToText uses getContentEdge() which detects styled cells (from\n // ForceStylesCtx=true props) and preserves their trailing spaces.\n output = bufferToText(buffer, {\n trimTrailingWhitespace: true,\n trimEmptyLines: false,\n })\n output = stripSilveryVS16(output)\n } else {\n // Styled mode: use bufferToStyledText for ANSI-preserving output.\n // Don't trim empty lines here — we trim to content height below.\n output = bufferToStyledText(buffer, {\n trimTrailingWhitespace: true,\n trimEmptyLines: false,\n })\n output = stripSilveryVS16(output)\n // Restore colon-format SGR sequences that were registered during sanitization.\n // silvery's pipeline converts colon-format (38:2::R:G:B) to semicolon-format\n // (38;2;R;G;B) during rendering. This converts them back to match Ink's behavior.\n output = restoreColonFormatSGR(output)\n }\n if (plain) output = stripAnsi(output)\n\n // Trim buffer padding: keep only lines up to the root's layout content height.\n // The buffer is sized to the terminal (e.g., 24 rows), but layout content may\n // only occupy a subset. Layout-meaningful empty rows (from padding, margin,\n // explicit height) are preserved; buffer-padding rows beyond are trimmed.\n if (contentHeight != null) {\n if (contentHeight > 0) {\n const lines = output.split(\"\\n\")\n if (lines.length > contentHeight) {\n output = lines.slice(0, contentHeight).join(\"\\n\")\n }\n } else {\n // Content height is 0 (empty tree): strip all content\n output = output.replace(/\\n+$/, \"\")\n }\n } else {\n // Fallback when content height not available: strip trailing empty lines.\n output = output.replace(/\\n+$/, \"\")\n }\n\n return output\n }\n\n /**\n * Flush any deferred debug writes (queued before the first frame was ready).\n * Called from writeFrame once lastOutput is available.\n * @returns true if stdout writes were flushed (frame already included in output)\n */\n function flushPendingDebugWrites(): boolean {\n if (pendingDebugWrites.length === 0) return false\n const pending = pendingDebugWrites\n pendingDebugWrites = []\n let hadStdoutWrites = false\n for (const { target, data } of pending) {\n // Append \\n after lastOutput to match PTY behavior (frame always ends with newline).\n // processBuffer strips trailing newlines, but the real PTY/Ink output has them.\n const frameWithNewline = lastOutput.endsWith(\"\\n\") ? lastOutput : lastOutput + \"\\n\"\n if (target === \"stdout\") {\n stdout.write(data + frameWithNewline)\n hadStdoutWrites = true\n } else {\n const stderrTarget = stderr ?? process.stderr\n stderrTarget.write(data)\n stdout.write(frameWithNewline)\n hadStdoutWrites = true\n }\n }\n return hadStdoutWrites\n }\n\n // Bridge component: uses silvery's useInput to forward Tab/Shift+Tab/Escape\n // to Ink's InkFocusContext. This sits inside both RuntimeContext (for useInput)\n // and InkFocusProvider (for focus context access).\n function InkFocusBridge({ children }: { children: React.ReactNode }) {\n const focusCtx = useContext(InkFocusContext)\n silveryUseInput((_input, key) => {\n if (!focusCtx.isFocusEnabled) return\n if (key.tab && !key.shift) focusCtx.focusNext()\n else if (key.tab && key.shift) focusCtx.focusPrevious()\n else if (key.escape) focusCtx.blur()\n })\n return React.createElement(React.Fragment, null, children)\n }\n\n /**\n * Ink-compatible writeToStdout: writes data to stdout.\n * Matches Ink's behavior: in interactive mode, clears the current frame,\n * writes the data, then re-renders the frame below it. This ensures that\n * useStdout().write() output appears above the rendered component output.\n * If no frame is available yet (initial mount effects), queues for deferred write.\n */\n function writeToStdout(data: string): void {\n if (lastOutput) {\n // Ink clears the frame, writes data, then re-renders frame below.\n // We emulate this by writing data + frame (the initial frame in writes[]\n // acts as the \"cleared\" first line that slice(1) skips).\n stdout.write(data + lastOutput)\n } else {\n pendingDebugWrites.push({ target: \"stdout\", data })\n }\n }\n\n /**\n * Ink-compatible writeToStderr: writes data to stderr.\n * In debug mode, writes data to stderr and replays the latest frame to stdout.\n * In non-debug mode, writes data to stderr (or stdout as fallback).\n * If no frame is available yet (initial mount effects), queues for deferred write.\n */\n function writeToStderr(data: string): void {\n const target = stderr ?? process.stderr\n if (debug) {\n if (lastOutput) {\n target.write(data)\n stdout.write(lastOutput)\n } else {\n pendingDebugWrites.push({ target: \"stderr\", data })\n }\n } else {\n target.write(data)\n }\n }\n\n // Ink-specific root wrapper: error boundary + focus system + cursor store + stdio contexts\n function wrapWithInkProviders(el: import(\"react\").ReactElement): import(\"react\").ReactElement {\n // Override StdoutContext with Ink-compatible write that supports debug mode\n const stdoutCtxValue = { stdout, write: writeToStdout }\n // Provide stderr context for useStderr hook (via silvery core StderrContext)\n const stderrCtxValue = { stderr: stderr ?? process.stderr, write: writeToStderr }\n\n return React.createElement(\n ForceStylesCtx.Provider,\n { value: true },\n React.createElement(\n InkRenderStateCtx.Provider,\n { value: renderState },\n React.createElement(\n SilveryErrorBoundary,\n null,\n React.createElement(\n InkStaticStoreCtx.Provider,\n { value: staticStore },\n React.createElement(\n InkStdinCtx.Provider,\n { value: stdinState },\n React.createElement(\n CursorProvider,\n { store: cursorStore },\n React.createElement(\n InkCursorStoreCtx.Provider,\n { value: cursorStore },\n React.createElement(\n StdoutContext.Provider,\n { value: stdoutCtxValue },\n React.createElement(\n StderrContext.Provider,\n { value: stderrCtxValue },\n React.createElement(InkFocusProvider, null, React.createElement(InkFocusBridge, null, el)),\n ),\n ),\n ),\n ),\n ),\n ),\n ),\n ),\n )\n }\n\n /**\n * onBufferReady: fires inside act() before effects on subsequent renders.\n * Sets lastOutput so debug-mode writeToStdout/writeToStderr can replay the frame.\n * Note: On the initial render, effects fire before onBufferReady (different code path\n * in renderer.ts), so deferred writes handle that case.\n */\n function handleBufferReady(_frame: string, buffer: TerminalBuffer, contentHeight?: number): void {\n let result = processBuffer(buffer, contentHeight)\n if (staticStore.fullStaticOutput) {\n result = staticStore.fullStaticOutput + result\n }\n lastOutput = result\n }\n\n /**\n * Post-process a rendered buffer and write to stdout.\n * Converts buffer to text, applies VS16 stripping, chalk compat, line trimming, and cursor emission.\n * Also flushes any deferred debug writes that were queued before the first frame.\n */\n function writeFrame(_frame: string, buffer: TerminalBuffer, contentHeight?: number): void {\n // Suppress output after alternate screen exit to prevent replay on primary screen\n if (altScreenExited) return\n\n let result = processBuffer(buffer, contentHeight)\n\n // Prepend accumulated static output (Ink writes fullStaticOutput + dynamicOutput in debug mode)\n if (staticStore.fullStaticOutput) {\n result = staticStore.fullStaticOutput + result\n }\n\n // Update lastOutput and flush deferred debug writes.\n // When deferred writes were flushed, the frame is already included in the\n // flush output (data + lastOutput), so skip the normal frame write to avoid\n // duplicating it. This matches Ink's behavior where writeToStdout() clears\n // the previous frame, writes data, then restores the frame.\n lastOutput = result\n const hadDeferredFlush = flushPendingDebugWrites()\n if (hadDeferredFlush) return\n\n // Cursor: only emit sequences when useCursor() is actively used.\n // Ink hides the cursor once at startup via cli-cursor, not per-frame.\n // We track transitions: emit show when cursor becomes visible, hide when it was visible and now isn't.\n // When cursor was previously shown, hide it before writing the frame to prevent visual jumping.\n const cursorState = cursorStore.accessors.getCursorState()\n const hidePrefix = cursorWasShown ? \"\\x1b[?25l\" : \"\"\n if (cursorState?.visible) {\n let cursorEsc = cursorState.x === 0 ? \"\\x1b[G\" : `\\x1b[${cursorState.x + 1}G`\n if (cursorState.y > 0) {\n const rowsUp = result.split(\"\\n\").length - 1 - cursorState.y\n if (rowsUp > 0) cursorEsc += `\\x1b[${rowsUp}A`\n }\n cursorEsc += \"\\x1b[?25h\"\n cursorWasShown = true\n stdout.write(hidePrefix + result + cursorEsc)\n } else if (cursorWasShown) {\n // Cursor was visible but now isn't — emit hide sequence\n cursorWasShown = false\n stdout.write(hidePrefix + result)\n } else {\n stdout.write(result)\n }\n }\n\n // Delegate to silvery's test renderer with wrapRoot for Ink contexts\n // and stdin bridging handled natively by the renderer\n // Resolve terminal dimensions with Ink-compatible fallback chain:\n // stdout.columns/rows → process.env.COLUMNS/LINES → process.stdout → process.stderr → defaults\n const resolvedCols = (stdout as any).columns || resolveTerminalColumns()\n const resolvedRows = (stdout as any).rows != null ? (stdout as any).rows : resolveTerminalRows()\n const app = silveryTestRender(element as import(\"react\").ReactElement, {\n cols: resolvedCols,\n rows: resolvedRows,\n autoRender: true,\n onFrame: writeFrame,\n onBufferReady: handleBufferReady,\n wrapRoot: wrapWithInkProviders,\n stdin: stdin as NodeJS.ReadStream | undefined,\n })\n\n // Listen for resize events on stdout\n const onResize = () => {\n const newCols = (stdout as any).columns || resolveTerminalColumns()\n const newRows = (stdout as any).rows != null ? (stdout as any).rows : resolveTerminalRows()\n app.resize(newCols, newRows)\n }\n stdout.on(\"resize\", onResize)\n\n /** Exit the alternate screen and suppress further output */\n function exitAlternateScreen() {\n if (useAltScreen && !altScreenExited) {\n altScreenExited = true\n stdout.write(\"\\x1b[?1049l\")\n // Restore cursor visibility after leaving alternate screen\n stdout.write(\"\\x1b[?25h\")\n }\n }\n\n let unmounted = false\n const instance: InkInstance = {\n rerender: (newElement: import(\"react\").ReactNode) => {\n if (unmounted) return\n app.rerender(newElement as import(\"react\").ReactElement)\n },\n unmount: () => {\n if (unmounted) return\n unmounted = true\n kittyManager.cleanup()\n exitAlternateScreen()\n stdout.off(\"resize\", onResize)\n app.unmount()\n },\n [Symbol.dispose]() {\n instance.unmount()\n },\n waitUntilExit: () => {\n // In Ink, exit() triggers unmount + resolves/rejects waitUntilExit.\n // Silvery's test renderer doesn't auto-unmount on exit(), so we do it here.\n if (app.exitCalled()) {\n instance.unmount()\n const err = app.exitError()\n return err ? Promise.reject(err) : Promise.resolve()\n }\n return app.waitUntilExit()\n },\n waitUntilRenderFlush: () => Promise.resolve(),\n cleanup: () => {\n instance.unmount()\n },\n clear: () => {},\n flush: () => {},\n pause: () => {},\n resume: () => {},\n stdin: { write: (data: string) => app.stdin.write(data) },\n }\n return instance\n}\n\n// =============================================================================\n// Interactive mode render (real terminal)\n// =============================================================================\n\nfunction renderInteractiveMode(\n element: import(\"react\").ReactNode,\n options: Record<string, unknown> | undefined,\n stdout: NodeJS.WriteStream | undefined,\n stdin: NodeJS.ReadStream | undefined,\n): InkInstance {\n const inkOptions: Record<string, unknown> = {\n ...options,\n // Ink defaults: no alternate screen, inline mode, no console patching\n alternateScreen: (options?.alternateScreen as boolean) ?? false,\n mode: \"inline\" as const,\n patchConsole: (options?.patchConsole as boolean) ?? false,\n exitOnCtrlC: (options?.exitOnCtrlC as boolean) ?? true,\n debug: (options?.debug as boolean) ?? false,\n }\n\n // Always provide stdout and stdin for the interactive path\n // so renderSync creates a full interactive instance (not static mode)\n const resolvedStdout = (stdout ?? process.stdout) as NodeJS.WriteStream\n const resolvedStdin = (stdin ?? process.stdin) as NodeJS.ReadStream\n const termDef: Record<string, unknown> = {\n stdout: resolvedStdout,\n stdin: resolvedStdin,\n }\n\n // Enable raw mode on stdin BEFORE rendering so it's active before any React\n // effects fire. This prevents a race condition where the PTY's ICRNL flag\n // converts \\r to \\n: Ink fixtures write __READY__ from a child useEffect\n // (which fires before the parent SilveryApp's input subscription effect that\n // enables raw mode). Without early raw mode, \\r written by the test after\n // seeing __READY__ may arrive before raw mode disables ICRNL.\n const earlyRawMode = resolvedStdin.isTTY === true\n if (earlyRawMode) {\n resolvedStdin.setRawMode(true)\n }\n\n // Per-instance stdin state for raw mode tracking and paste event bridging\n const interactiveStdinState = createInkStdinState(resolvedStdin, resolvedStdout)\n\n // Kitty keyboard protocol support\n const kittyManager = createKittyManager(\n resolvedStdin,\n resolvedStdout,\n resolveKittyManagerOptions(options?.kittyKeyboard as KittyKeyboardOptions | undefined),\n )\n\n // Wrap element with InkStdinCtx.Provider so usePaste can access setBracketedPasteMode\n const wrappedElement = React.createElement(InkStdinCtx.Provider, { value: interactiveStdinState }, element)\n\n const silveryInstance = renderSync(wrappedElement as any, termDef as any, inkOptions as any)\n\n // Wrap with Ink-specific methods\n const instance: InkInstance = {\n ...silveryInstance,\n waitUntilRenderFlush: () => Promise.resolve(),\n cleanup: () => {\n silveryInstance.unmount()\n },\n }\n\n // Override unmount to clean up kitty protocol\n const origUnmount = instance.unmount\n instance.unmount = () => {\n kittyManager.cleanup()\n origUnmount()\n }\n\n return instance\n}\n\n// =============================================================================\n// initInkCompat\n// =============================================================================\n\n/**\n * Pre-initialize the compat layer with a specific layout engine.\n * Call before render() to use Yoga (which requires async WASM loading):\n *\n * await initInkCompat(\"yoga\");\n * render(<App />, { stdout });\n *\n * Without this, render() defaults to Flexily (synchronous).\n * Also respects SILVERY_ENGINE env var.\n */\nexport async function initInkCompat(engine?: \"flexily\" | \"yoga\"): Promise<void> {\n await ensureDefaultLayoutEngine(engine)\n}\n\n// =============================================================================\n// renderToString\n// =============================================================================\n\n/**\n * Ink-compatible renderToString.\n * Maps ink's `renderToString(element, { columns })` to silvery's `renderStringSync`.\n * Automatically initializes the layout engine if needed (using sync flexily).\n *\n * When `isScreenReaderEnabled` is true, walks the React element tree and produces\n * accessible text with ARIA roles, labels, and states instead of visual rendering.\n */\nexport function renderToString(\n node: import(\"react\").ReactNode,\n options?: { columns?: number; isScreenReaderEnabled?: boolean },\n): string {\n if (options?.isScreenReaderEnabled) {\n return renderScreenReaderOutput(node)\n }\n\n // Enable Ink-compatible strict validation so raw text outside <Text> throws\n setInkStrictValidation(true)\n\n if (!isLayoutEngineInitialized()) {\n setLayoutEngine(createFlexilyZeroEngine())\n }\n // Sync color detection with chalk: tests may set chalk.level = 3 programmatically\n // even when FORCE_COLOR=0, so we must respect chalk's runtime level\n const chalkHasColors = currentChalkLevel() > 0\n const colorLevel = chalkHasColors ? (\"truecolor\" as const) : null\n const term = createTerm({ color: colorLevel })\n // Always render with color enabled (plain=false) so that embedded ANSI sequences\n // in text children are preserved in the buffer output. Ink preserves embedded ANSI\n // even when chalk has no color support — only chalk-applied style props are skipped\n // (which the Text component already handles by stripping style props when !chalkHasColors).\n const plain = false\n // Create a static store for the Static component to populate during renderStringSync\n const staticStore: InkStaticStore = { renderedCount: 0, fullStaticOutput: \"\" }\n const wrapped = React.createElement(\n InkStaticStoreCtx.Provider,\n { value: staticStore },\n React.createElement(TermContext.Provider, { value: term }, node),\n )\n const bufferHeight = 24\n let layoutContentHeight = 0\n let output = renderStringSync(wrapped as import(\"react\").ReactElement, {\n width: options?.columns ?? 80,\n height: bufferHeight,\n plain,\n trimTrailingWhitespace: true,\n trimEmptyLines: false,\n onContentHeight: (h: number) => {\n layoutContentHeight = h\n },\n })\n // Strip VS16 variation selectors that silvery adds for text-presentation emoji\n output = stripSilveryVS16(output)\n // Trim buffer padding rows using content height from layout\n if (layoutContentHeight > 0 && layoutContentHeight < bufferHeight) {\n const lines = output.split(\"\\n\")\n output = lines.slice(0, layoutContentHeight).join(\"\\n\")\n } else {\n // Fall back: strip trailing empty lines (content height unknown or fills buffer)\n const lines = output.split(\"\\n\")\n while (lines.length > 0 && lines[lines.length - 1] === \"\") lines.pop()\n output = lines.join(\"\\n\")\n }\n // If result is only whitespace/newlines/ANSI resets (empty fragment), return empty string\n if (stripAnsi(output).trim() === \"\") {\n // Even if the visual buffer is empty, there might be static output\n if (staticStore.fullStaticOutput) {\n // Static-only output: prepend static output. Ink writes fullStaticOutput + dynamicOutput.\n // Dynamic is empty string when tree is empty.\n return staticStore.fullStaticOutput.replace(/\\n$/, \"\")\n }\n return \"\"\n }\n // Prepend static output if present (Ink writes fullStaticOutput + dynamicOutput)\n // Restore colon-format SGR sequences (e.g., 38:2::R:G:B) that silvery converted\n // to semicolon-format during rendering\n const dynamicOutput = restoreColonFormatSGR(output)\n // Clear for renderToString (synchronous, single-use)\n colonSGRTracker.clear()\n if (staticStore.fullStaticOutput) {\n return staticStore.fullStaticOutput + dynamicOutput\n }\n return dynamicOutput\n}\n\n// =============================================================================\n// measureElement\n// =============================================================================\n\n/**\n * Check if a node or any of its ancestors has dirty layout.\n * When the reconciler adds/removes children, it marks the parent as layoutDirty\n * and propagates subtreeDirty up to the root.\n */\nfunction needsLayoutRecalculation(node: any): boolean {\n // Walk up from node to root checking dirty flags\n let current = node\n while (current) {\n if (current.layoutDirty || current.subtreeDirty || current.childrenDirty) return true\n current = current.parent\n }\n return false\n}\n\n/**\n * Ink-compatible measureElement that handles BoxHandle refs and computes\n * layout on demand when contentRect is stale or hasn't been set yet.\n *\n * This bridges the timing gap between Ink (Yoga runs during commit, so\n * effects see layout) and silvery (layout runs in a separate pipeline pass).\n */\nexport function measureElement(nodeOrHandle: any): import(\"@silvery/ag-react/measureElement\").MeasureElementOutput {\n // Resolve BoxHandle → AgNode\n const node = typeof nodeOrHandle?.getNode === \"function\" ? nodeOrHandle.getNode() : nodeOrHandle\n if (!node) return { width: 0, height: 0 }\n\n // If contentRect exists AND layout is not stale, use cached values\n if (node.contentRect && !needsLayoutRecalculation(node)) {\n return baseMeasureElement(node)\n }\n\n // contentRect is null or layout is dirty — walk up to root and\n // calculate layout on demand so effects can read correct dimensions.\n let root = node\n while (root.parent) {\n root = root.parent\n }\n\n if (root.layoutNode) {\n // Use a sensible width — check process.stdout or default to 100\n const termWidth = process.stdout?.columns || 100\n const termHeight = (process.stdout as any)?.rows || 24\n try {\n calculateLayout(root, termWidth, termHeight)\n } catch {\n // Layout may fail if engine not initialized — fall back gracefully\n }\n }\n\n return baseMeasureElement(node)\n}\n",
121
+ "/**\n * ErrorBoundary — Built-in error boundary for silvery apps.\n *\n * Catches render errors in the component tree and displays a rich\n * error message with source location, code excerpt, and stack trace\n * using low-level silvery host elements (no dependency on Box/Text\n * from @silvery/ag-react/ui). This is the default root wrapper for all\n * silvery apps — createApp() and run() wrap the element tree with it\n * automatically.\n *\n * Uses `silvery-box` and `silvery-text` host elements directly to\n * avoid circular deps with higher-level component libraries.\n *\n * @packageDocumentation\n */\n\nimport * as fs from \"node:fs\"\nimport React, { Component } from \"react\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface SilveryErrorBoundaryProps {\n children?: React.ReactNode\n /** Called when an error is caught. Use for logging or cleanup. */\n onError?: (error: Error) => void\n}\n\ninterface SilveryErrorBoundaryState {\n error: Error | null\n}\n\n// ============================================================================\n// Stack parsing utilities\n// ============================================================================\n\n/**\n * Parse a stack line to extract function name, file, line, column.\n * Handles both `at Foo (file:line:col)` and `at file:line:col` formats.\n */\nfunction parseStackLine(line: string): { function?: string; file?: string; line?: number; column?: number } | null {\n const trimmed = line.trim()\n if (!trimmed.startsWith(\"at \")) return null\n\n const rest = trimmed.slice(3)\n // Match: functionName (file:line:col)\n const match1 = rest.match(/^(.+?)\\s+\\((.+?):(\\d+):(\\d+)\\)$/)\n if (match1) {\n return {\n function: match1[1],\n file: match1[2],\n line: Number(match1[3]),\n column: Number(match1[4]),\n }\n }\n // Match: file:line:col (no function name)\n const match2 = rest.match(/^(.+?):(\\d+):(\\d+)$/)\n if (match2) {\n return { file: match2[1], line: Number(match2[2]), column: Number(match2[3]) }\n }\n return null\n}\n\n/**\n * Clean up file path by removing cwd prefix and file:// protocol.\n */\nfunction cleanupPath(filePath: string | undefined): string | undefined {\n if (!filePath) return filePath\n let p = filePath\n const cwdPath = process.cwd()\n // Remove file:// protocol\n p = p.replace(/^file:\\/\\//, \"\")\n // Remove cwd prefix\n for (const prefix of [cwdPath, `/private${cwdPath}`]) {\n if (p.startsWith(`${prefix}/`)) {\n p = p.slice(prefix.length + 1)\n break\n }\n }\n return p\n}\n\n/**\n * Get source code excerpt around a line number (±3 lines).\n */\nfunction getCodeExcerpt(filePath: string, line: number): Array<{ line: number; value: string }> | null {\n try {\n if (!fs.existsSync(filePath)) return null\n const source = fs.readFileSync(filePath, \"utf8\")\n const lines = source.split(\"\\n\")\n const start = Math.max(0, line - 4)\n const end = Math.min(lines.length, line + 3)\n const result: Array<{ line: number; value: string }> = []\n for (let i = start; i < end; i++) {\n result.push({ line: i + 1, value: (lines[i] ?? \"\").replace(/\\t/g, \" \") })\n }\n return result\n } catch {\n return null\n }\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * Rich error boundary for silvery's runtime layer.\n *\n * Must be a class component (React limitation for error boundaries).\n * Renders error info using silvery-box/silvery-text host elements — no Box/Text dependency.\n * Shows: ERROR label, error message, file location, source code excerpt, and stack trace.\n */\nexport class SilveryErrorBoundary extends Component<SilveryErrorBoundaryProps, SilveryErrorBoundaryState> {\n override state: SilveryErrorBoundaryState = { error: null }\n\n static getDerivedStateFromError(error: Error): SilveryErrorBoundaryState {\n return { error }\n }\n\n override componentDidCatch(error: Error) {\n this.props.onError?.(error)\n }\n\n override render() {\n if (this.state.error) {\n const err = this.state.error\n const stack = err.stack ? err.stack.split(\"\\n\").slice(1) : []\n const origin = stack.length > 0 ? parseStackLine(stack[0]!) : null\n const filePath = cleanupPath(origin?.file)\n\n // Get source code excerpt\n let excerpt: Array<{ line: number; value: string }> | null = null\n let lineWidth = 0\n if (filePath && origin?.line) {\n excerpt = getCodeExcerpt(filePath, origin.line)\n if (excerpt) {\n for (const { line } of excerpt) {\n lineWidth = Math.max(lineWidth, String(line).length)\n }\n }\n }\n\n // Build the error display using silvery host elements\n // Format: padding=1 column layout with ERROR label, location, code, and stack\n const children: React.ReactNode[] = []\n\n // ERROR label + message\n children.push(\n React.createElement(\n \"silvery-box\",\n { key: \"header\" },\n React.createElement(\"silvery-text\", { backgroundColor: \"red\", color: \"white\" }, \" ERROR \"),\n React.createElement(\"silvery-text\", {}, ` ${err.message}`),\n ),\n )\n\n // File location\n if (filePath && origin) {\n children.push(\n React.createElement(\n \"silvery-box\",\n { key: \"location\", marginTop: 1 },\n React.createElement(\"silvery-text\", { dimColor: true }, `${filePath}:${origin.line}:${origin.column}`),\n ),\n )\n }\n\n // Source code excerpt\n if (excerpt && origin) {\n const codeLines = excerpt.map(({ line, value }) => {\n const lineNum = String(line).padStart(lineWidth, \" \")\n return React.createElement(\n \"silvery-box\",\n { key: `code-${line}` },\n React.createElement(\n \"silvery-text\",\n {\n dimColor: line !== origin.line,\n backgroundColor: line === origin.line ? \"red\" : undefined,\n color: line === origin.line ? \"white\" : undefined,\n },\n `${lineNum}:`,\n ),\n React.createElement(\n \"silvery-text\",\n {\n backgroundColor: line === origin.line ? \"red\" : undefined,\n color: line === origin.line ? \"white\" : undefined,\n },\n ` ${value}`,\n ),\n )\n })\n children.push(\n React.createElement(\"silvery-box\", { key: \"code\", marginTop: 1, flexDirection: \"column\" }, ...codeLines),\n )\n }\n\n // Stack trace\n if (stack.length > 0) {\n const stackLines = stack.map((line, i) => {\n const parsed = parseStackLine(line)\n if (!parsed) {\n return React.createElement(\n \"silvery-box\",\n { key: `stack-${i}` },\n React.createElement(\"silvery-text\", { dimColor: true }, `- ${line.trim()}`),\n )\n }\n const cleanFile = cleanupPath(parsed.file)\n return React.createElement(\n \"silvery-box\",\n { key: `stack-${i}` },\n React.createElement(\"silvery-text\", { dimColor: true }, \"- \"),\n React.createElement(\"silvery-text\", { dimColor: true, bold: true }, parsed.function ?? \"\"),\n React.createElement(\n \"silvery-text\",\n { dimColor: true, color: \"gray\" },\n ` (${cleanFile ?? \"\"}:${parsed.line}:${parsed.column})`,\n ),\n )\n })\n children.push(\n React.createElement(\"silvery-box\", { key: \"stack\", marginTop: 1, flexDirection: \"column\" }, ...stackLines),\n )\n }\n\n return React.createElement(\"silvery-box\", { flexDirection: \"column\", padding: 1 }, ...children)\n }\n return this.props.children\n }\n}\n",
122
+ "/**\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",
123
+ "/**\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 { type ResolvedTermDef, isTerm, isTermDef, resolveFromTerm, resolveTermDef } from \"@silvery/ag-term/term-def\"\nimport type { TermDef } from \"@silvery/ag/types\"\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\ntype InputHandler = (input: string, key: import(\"@silvery/tea/keys\").Key) => void\ntype PasteHandler = (text: string) => void\n\ninterface SubscriberList {\n input: Set<InputHandler>\n paste: Set<PasteHandler>\n}\n\nfunction createSubscriberList(): SubscriberList {\n return { input: new Set(), paste: new Set() }\n}\n\n// ============================================================================\n// App Props — callback-based, no Node.js types in the callback interface\n// ============================================================================\n\ninterface AppProps {\n children: ReactNode\n /** Subscribe to raw input chunks. Returns cleanup. Called by setRawMode. */\n onInputSubscribe?: (handler: (chunk: string) => void) => () => void\n /** Whether Ctrl+C should exit the app */\n exitOnCtrlC: boolean\n /** Write function for stdout context */\n stdoutWrite: (data: string) => void\n /** The raw stdout stream (for StdoutContext.stdout) — still NodeJS.WriteStream for now */\n stdout: NodeJS.WriteStream\n onExit: (error?: Error) => void\n onPause?: () => void\n onResume?: () => void\n onScrollback?: (lines: number) => void\n /** Get the root AgNode for focus navigation. Provided by SilveryInstance. */\n getRoot?: () => import(\"@silvery/tea/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/tea/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 InputHandler\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 PasteHandler\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\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 // 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\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 })\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 this.stdout.write(data)\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 // 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",
124
+ "/**\n * Focus Manager — standalone state container for the silvery focus system.\n *\n * Pure TypeScript, no React dependency. The subscribe/getSnapshot pattern\n * enables useSyncExternalStore in hooks.\n *\n * Replaces the flat focus list in context.ts (FocusContext with focusables Map).\n */\n\nimport type { AgNode, Rect } from \"./types\"\nimport {\n findByTestID,\n findFocusableAncestor,\n getTabOrder,\n findSpatialTarget,\n getExplicitFocusLink,\n} from \"@silvery/tea/focus-queries\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type FocusOrigin = \"keyboard\" | \"mouse\" | \"programmatic\"\n\n/**\n * Callback fired when focus changes. Used by the runtime to dispatch\n * DOM-level focus/blur events without coupling FocusManager to the event system.\n *\n * @param oldNode - The node losing focus (null if nothing was focused)\n * @param newNode - The node gaining focus (null on blur)\n * @param origin - How focus was acquired\n */\nexport type FocusChangeCallback = (oldNode: AgNode | null, newNode: AgNode | null, origin: FocusOrigin | null) => void\n\nexport interface FocusSnapshot {\n activeId: string | null\n previousId: string | null\n focusOrigin: FocusOrigin | null\n scopeStack: readonly string[]\n /** The currently active peer scope (WPF FocusScope model) */\n activeScopeId: string | null\n}\n\nexport interface FocusManagerOptions {\n /** Called when focus changes — wire up event dispatch here */\n onFocusChange?: FocusChangeCallback\n}\n\nexport interface FocusManager {\n /** Currently focused node */\n readonly activeElement: AgNode | null\n /** testID of the currently focused node */\n readonly activeId: string | null\n /** Previously focused node */\n readonly previousElement: AgNode | null\n /** testID of the previously focused node */\n readonly previousId: string | null\n /** How focus was most recently acquired */\n readonly focusOrigin: FocusOrigin | null\n /** Stack of active focus scope IDs */\n readonly scopeStack: readonly string[]\n /** Map of scope ID -> last focused testID within that scope */\n readonly scopeMemory: Readonly<Record<string, string>>\n\n /** Focus a specific node */\n focus(node: AgNode, origin?: FocusOrigin): void\n /** Focus a node by testID (requires root for tree search) */\n focusById(id: string, root: AgNode, origin?: FocusOrigin): void\n /** Clear focus */\n blur(): void\n\n /**\n * Handle a subtree being removed from the tree.\n * If the focused node (or previous node) is within the removed subtree,\n * clear the reference to prevent dead node retention and broken navigation.\n */\n handleSubtreeRemoved(removedRoot: AgNode): void\n\n /** Push a focus scope onto the stack */\n enterScope(scopeId: string): void\n /** Pop the current focus scope */\n exitScope(): void\n\n /** The currently active peer scope ID (WPF FocusScope model) */\n readonly activeScopeId: string | null\n /**\n * Activate a peer focus scope. Saves current focus in the old scope's memory,\n * switches to the new scope, and restores the remembered focus (or focuses\n * the first focusable element in the scope subtree).\n */\n activateScope(scopeId: string, root: AgNode): void\n\n /** Get the testID path from focused node to root */\n getFocusPath(root: AgNode): string[]\n /** Check if a subtree rooted at testID contains the focused node */\n hasFocusWithin(root: AgNode, testID: string): boolean\n\n /** Focus the next focusable node in tab order */\n focusNext(root: AgNode, scope?: AgNode): void\n /** Focus the previous focusable node in tab order */\n focusPrev(root: AgNode, scope?: AgNode): void\n /** Focus in a spatial direction (up/down/left/right) */\n focusDirection(\n root: AgNode,\n direction: \"up\" | \"down\" | \"left\" | \"right\",\n layoutFn?: (node: AgNode) => Rect | null,\n ): void\n\n /** Subscribe for React integration (useSyncExternalStore) */\n subscribe(listener: () => void): () => void\n /** Get immutable snapshot for useSyncExternalStore */\n getSnapshot(): FocusSnapshot\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\nexport function createFocusManager(options?: FocusManagerOptions): FocusManager {\n const onFocusChange = options?.onFocusChange\n\n // Internal state\n let activeElement: AgNode | null = null\n let activeId: string | null = null\n let previousElement: AgNode | null = null\n let previousId: string | null = null\n let focusOrigin: FocusOrigin | null = null\n const scopeStack: string[] = []\n const scopeMemory: Record<string, string> = {}\n let activeScopeId: string | null = null\n\n // Subscriber management\n const listeners = new Set<() => void>()\n let snapshot: FocusSnapshot | null = null\n /** Counter incremented on every notify(); used by activateScope to detect inner notifications. */\n let notifyCount = 0\n\n function notify(): void {\n snapshot = null // Invalidate cached snapshot\n notifyCount++\n for (const listener of listeners) {\n listener()\n }\n }\n\n function getTestID(node: AgNode): string | null {\n const props = node.props as Record<string, unknown>\n return typeof props.testID === \"string\" ? props.testID : null\n }\n\n // ---- Focus operations ----\n\n function focus(node: AgNode, origin: FocusOrigin = \"programmatic\"): void {\n // Skip if already focused on this node\n if (activeElement === node) {\n // Still update origin if different\n if (focusOrigin !== origin) {\n focusOrigin = origin\n notify()\n }\n return\n }\n\n const oldElement = activeElement\n previousElement = activeElement\n previousId = activeId\n activeElement = node\n activeId = getTestID(node)\n focusOrigin = origin\n\n // Remember this focus in the current scope\n if (activeId && scopeStack.length > 0) {\n scopeMemory[scopeStack[scopeStack.length - 1]!] = activeId\n }\n\n notify()\n\n // Fire focus change callback (after state is updated)\n onFocusChange?.(oldElement, node, origin)\n }\n\n function focusById(id: string, root: AgNode, origin: FocusOrigin = \"programmatic\"): void {\n const node = findByTestID(root, id)\n if (node) {\n // Walk up to the nearest focusable ancestor if the found node isn't focusable\n const focusable = findFocusableAncestor(node)\n if (focusable) {\n focus(focusable, origin)\n return\n }\n }\n // Virtual focus: set the ID without a DOM node. This enables named focus\n // targets (e.g. \"board-area\", \"detail-pane\") without requiring wrapper Boxes\n // that would disrupt layout.\n const oldElement = activeElement\n previousElement = activeElement\n previousId = activeId\n activeElement = null\n activeId = id\n focusOrigin = origin\n notify()\n\n // Fire focus change callback (old element blurs, no new node for virtual focus)\n onFocusChange?.(oldElement, null, origin)\n }\n\n function blur(): void {\n if (!activeElement && !activeId) return\n\n const oldElement = activeElement\n previousElement = activeElement\n previousId = activeId\n activeElement = null\n activeId = null\n focusOrigin = null\n\n notify()\n\n // Fire focus change callback (after state is updated)\n onFocusChange?.(oldElement, null, null)\n }\n\n // ---- Subtree removal ----\n\n /**\n * Check if a node is the given target or contains it as a descendant.\n */\n function subtreeContains(subtreeRoot: AgNode, target: AgNode): boolean {\n if (subtreeRoot === target) return true\n for (const child of subtreeRoot.children) {\n if (subtreeContains(child, target)) return true\n }\n return false\n }\n\n /**\n * Handle a subtree being removed from the tree. If the active or previous\n * element lives within the removed subtree, clear the dangling reference.\n * This prevents dead node retention and broken navigation (indexOf → -1).\n */\n function handleSubtreeRemoved(removedRoot: AgNode): void {\n let changed = false\n\n if (activeElement && subtreeContains(removedRoot, activeElement)) {\n const oldElement = activeElement\n previousElement = activeElement\n previousId = activeId\n activeElement = null\n activeId = null\n focusOrigin = null\n changed = true\n onFocusChange?.(oldElement, null, null)\n }\n\n if (previousElement && subtreeContains(removedRoot, previousElement)) {\n previousElement = null\n previousId = null\n changed = true\n }\n\n if (changed) {\n notify()\n }\n }\n\n // ---- Scope management ----\n\n function enterScope(scopeId: string): void {\n scopeStack.push(scopeId)\n notify()\n }\n\n function exitScope(): void {\n const exited = scopeStack.pop()\n if (exited === undefined) return\n\n // Restore focus to the remembered element in the parent scope\n // (Caller is responsible for providing root to restore if needed)\n notify()\n }\n\n // ---- Peer scope activation (WPF FocusScope model) ----\n\n function activateScope(scopeId: string, root: AgNode): void {\n // Save current focus in the outgoing scope's memory\n if (activeScopeId && activeId) {\n scopeMemory[activeScopeId] = activeId\n }\n\n // Switch scope\n activeScopeId = scopeId\n\n // Restore focus: remembered element, or first focusable in scope.\n // Track whether notify() fired during focus/focusById to avoid double-notify.\n const countBefore = notifyCount\n const remembered = scopeMemory[scopeId]\n if (remembered) {\n focusById(remembered, root, \"programmatic\")\n } else {\n const scopeNode = findByTestID(root, scopeId)\n if (scopeNode) {\n const order = getTabOrder(root, scopeNode)\n if (order.length > 0) {\n focus(order[0]!, \"programmatic\")\n }\n }\n }\n\n // Only notify if focus/focusById didn't already notify.\n if (notifyCount === countBefore) {\n notify()\n }\n }\n\n // ---- Tree queries ----\n\n function getFocusPath(root: AgNode): string[] {\n if (!activeElement) return []\n\n const path: string[] = []\n let current: AgNode | null = activeElement\n while (current && current !== root.parent) {\n const id = getTestID(current)\n if (id) path.push(id)\n current = current.parent\n }\n return path\n }\n\n function hasFocusWithin(root: AgNode, testID: string): boolean {\n if (!activeElement) return false\n\n // Find the node with the given testID\n const target = findByTestID(root, testID)\n if (!target) return false\n\n // Walk up from activeElement to see if we pass through target\n let current: AgNode | null = activeElement\n while (current) {\n if (current === target) return true\n current = current.parent\n }\n return false\n }\n\n // ---- Navigation ----\n\n /**\n * Resolve the effective scope node for tab navigation.\n * If an explicit scope is provided, use it. Otherwise, if the scopeStack\n * is non-empty, find the topmost scope node in the tree by testID.\n */\n function resolveScope(root: AgNode, explicitScope?: AgNode): AgNode | undefined {\n if (explicitScope) return explicitScope\n\n if (scopeStack.length > 0) {\n const scopeId = scopeStack[scopeStack.length - 1]!\n const scopeNode = findByTestID(root, scopeId)\n if (scopeNode) return scopeNode\n }\n\n return undefined\n }\n\n function focusNext(root: AgNode, scope?: AgNode): void {\n const effectiveScope = resolveScope(root, scope)\n const order = getTabOrder(root, effectiveScope)\n if (order.length === 0) return\n\n if (!activeElement) {\n // Nothing focused — focus the first element\n focus(order[0]!, \"keyboard\")\n return\n }\n\n const currentIndex = order.indexOf(activeElement)\n // Wrap around to the first element\n const nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % order.length\n focus(order[nextIndex]!, \"keyboard\")\n }\n\n function focusPrev(root: AgNode, scope?: AgNode): void {\n const effectiveScope = resolveScope(root, scope)\n const order = getTabOrder(root, effectiveScope)\n if (order.length === 0) return\n\n if (!activeElement) {\n // Nothing focused — focus the last element\n focus(order[order.length - 1]!, \"keyboard\")\n return\n }\n\n const currentIndex = order.indexOf(activeElement)\n // Wrap around to the last element\n const prevIndex = currentIndex <= 0 ? order.length - 1 : currentIndex - 1\n focus(order[prevIndex]!, \"keyboard\")\n }\n\n function focusDirection(\n root: AgNode,\n direction: \"up\" | \"down\" | \"left\" | \"right\",\n layoutFn?: (node: AgNode) => Rect | null,\n ): void {\n if (!activeElement) return\n\n // Check for explicit focus link first\n const explicitTarget = getExplicitFocusLink(activeElement, direction)\n if (explicitTarget) {\n focusById(explicitTarget, root, \"keyboard\")\n return\n }\n\n // Fall back to spatial navigation\n const candidates = getTabOrder(root)\n const resolvedLayoutFn = layoutFn ?? ((node: AgNode) => node.screenRect)\n const target = findSpatialTarget(activeElement, direction, candidates, resolvedLayoutFn)\n if (target) {\n focus(target, \"keyboard\")\n }\n }\n\n // ---- Subscribe/snapshot for useSyncExternalStore ----\n\n function subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n }\n\n function getSnapshot(): FocusSnapshot {\n if (!snapshot) {\n snapshot = {\n activeId,\n previousId,\n focusOrigin,\n scopeStack: [...scopeStack],\n activeScopeId,\n }\n }\n return snapshot\n }\n\n // ---- Public interface ----\n\n return {\n get activeElement() {\n return activeElement\n },\n get activeId() {\n return activeId\n },\n get previousElement() {\n return previousElement\n },\n get previousId() {\n return previousId\n },\n get focusOrigin() {\n return focusOrigin\n },\n get scopeStack() {\n return [...scopeStack] as readonly string[]\n },\n get scopeMemory() {\n return scopeMemory as Readonly<Record<string, string>>\n },\n get activeScopeId() {\n return activeScopeId\n },\n\n focus,\n focusById,\n blur,\n handleSubtreeRemoved,\n\n enterScope,\n exitScope,\n activateScope,\n\n getFocusPath,\n hasFocusWithin,\n\n focusNext,\n focusPrev,\n focusDirection,\n\n subscribe,\n getSnapshot,\n }\n}\n",
125
+ "/**\n * Silvery Render Scheduler\n *\n * Batches rapid state updates to prevent flicker and improve performance.\n * Uses queueMicrotask for coalescing multiple synchronous state changes\n * into a single render pass.\n *\n * Features:\n * - Microtask-based batching (coalesces synchronous updates)\n * - Frame batching to prevent flicker\n * - Resize handling with debounce\n * - Clean shutdown\n */\n\nimport { appendFileSync } from \"node:fs\"\nimport { type Logger, createLogger } from \"loggily\"\nimport { type TerminalBuffer, bufferToText, cellEquals } from \"./buffer\"\nimport { buildMismatchContext, formatMismatchContext } from \"@silvery/test/debug-mismatch\"\nimport {\n type ResolvedNonTTYMode as ResolvedMode,\n countLines,\n createOutputTransformer,\n resolveNonTTYMode,\n stripAnsi,\n} from \"./non-tty\"\nimport { getCursorState as globalGetCursorState, type CursorAccessors } from \"@silvery/ag-react/hooks/useCursor\"\nimport { copyToClipboard as copyToClipboardImpl } from \"./clipboard\"\nimport { ANSI, notify as notifyTerminal, setCursorStyle, resetCursorStyle } from \"./output\"\nimport { executeRender, type PipelineConfig } from \"./pipeline\"\nimport type { ContentPhaseStats } from \"./pipeline/types\"\nimport type { AgNode } from \"@silvery/ag/types\"\n\nconst log = createLogger(\"silvery:scheduler\")\n\n/**\n * Whether synchronized update mode (DEC 2026) is enabled.\n *\n * Disabled by default due to a Ghostty rendering bug where incremental\n * cursor-positioned updates inside a sync region cause progressive visual\n * corruption. Works correctly in Kitty. Full renders (bufferToAnsi) work\n * fine with sync — only incremental diff output (changesToAnsi) triggers it.\n *\n * Set SILVERY_SYNC_UPDATE=1 to force-enable (e.g., for testing in Kitty).\n * TODO: Re-enable by default once the Ghostty bug is fixed.\n * See: https://github.com/ghostty-org/ghostty/discussions/11002\n */\nconst SYNC_UPDATE_ENABLED = process.env.SILVERY_SYNC_UPDATE === \"1\" || process.env.SILVERY_SYNC_UPDATE === \"true\"\n\n// ============================================================================\n// Errors\n// ============================================================================\n\n// Re-export from errors.ts (kept separate for React-free barrel imports)\nexport { IncrementalRenderMismatchError } from \"./errors\"\nimport { IncrementalRenderMismatchError } from \"./errors\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Non-TTY mode for rendering in non-interactive environments.\n */\nexport type NonTTYMode = \"auto\" | \"tty\" | \"line-by-line\" | \"static\" | \"plain\"\n\n/**\n * Resolved non-TTY mode after auto-detection.\n */\nexport type ResolvedNonTTYMode = Exclude<NonTTYMode, \"auto\">\n\nexport interface SchedulerOptions {\n /** stdout stream for writing output */\n stdout: NodeJS.WriteStream\n /** Root Silvery node */\n root: AgNode\n /** Debug mode - logs render timing */\n debug?: boolean\n /** Minimum time between frames in ms (default: 16 for ~60fps) */\n minFrameTime?: number\n /** Render mode: fullscreen (absolute positioning) or inline (relative positioning) */\n mode?: \"fullscreen\" | \"inline\"\n /**\n * Non-TTY mode for non-interactive environments (default: 'auto')\n *\n * - 'auto': Detect based on environment\n * - 'tty': Force TTY mode\n * - 'line-by-line': Simple line output\n * - 'static': Only output final frame\n * - 'plain': Strip all ANSI codes\n */\n nonTTYMode?: NonTTYMode\n /** Slow frame warning threshold in ms (default: 50). Set to 0 to disable. */\n slowFrameThreshold?: number\n /** Pipeline configuration (caps-scoped measurer + output phase) */\n pipelineConfig?: PipelineConfig\n /** Per-instance cursor accessors. Falls back to module-level globals if not provided. */\n cursorAccessors?: CursorAccessors\n}\n\nexport interface RenderStats {\n /** Number of renders executed */\n renderCount: number\n /** Number of renders skipped (batched) */\n skippedCount: number\n /** Last render duration in ms */\n lastRenderTime: number\n /** Average render time in ms */\n avgRenderTime: number\n}\n\n// ============================================================================\n// RenderScheduler Class\n// ============================================================================\n\n/**\n * Schedules and batches render operations.\n *\n * Usage:\n * ```ts\n * const scheduler = new RenderScheduler({\n * stdout: process.stdout,\n * root: rootNode,\n * });\n *\n * // Schedule renders (automatically batched)\n * scheduler.scheduleRender();\n * scheduler.scheduleRender(); // This won't cause duplicate render\n *\n * // Force immediate render\n * scheduler.forceRender();\n *\n * // Clean shutdown\n * scheduler.dispose();\n * ```\n */\nexport class RenderScheduler {\n private stdout: NodeJS.WriteStream\n private root: AgNode\n private debugMode: boolean\n private minFrameTime: number\n private slowFrameThreshold: number\n private mode: \"fullscreen\" | \"inline\"\n private pipelineConfig?: PipelineConfig\n private getCursorState: () => import(\"@silvery/ag-react/hooks/useCursor\").CursorState | null\n private nonTTYMode: ResolvedMode\n private outputTransformer: (content: string, prevLineCount: number) => string\n private log: Logger\n\n /** Previous buffer for diffing */\n private prevBuffer: TerminalBuffer | null = null\n\n /** Line count of previous render (for non-TTY modes) */\n private prevLineCount = 0\n\n /** Accumulated output for static mode */\n private staticOutput = \"\"\n\n /** Is a render currently scheduled? */\n private renderScheduled = false\n\n /** Last render timestamp */\n private lastRenderTime = 0\n\n /** Pending frame timeout (for frame rate limiting) */\n private frameTimeout: ReturnType<typeof setTimeout> | null = null\n\n /** Resize listener cleanup */\n private resizeCleanup: (() => void) | null = null\n\n /** Render statistics */\n private stats: RenderStats = {\n renderCount: 0,\n skippedCount: 0,\n lastRenderTime: 0,\n avgRenderTime: 0,\n }\n\n /** Is the scheduler disposed? */\n private disposed = false\n\n /** Is the scheduler paused? When paused, renders are deferred until resume. */\n private paused = false\n\n /** Was a render requested while paused? */\n private pendingWhilePaused = false\n\n /**\n * Lines written to stdout between renders (inline mode only).\n * When useScrollback or other code writes to stdout, those lines\n * displace the terminal cursor. This offset is consumed on the next render.\n */\n private scrollbackOffset = 0\n\n constructor(options: SchedulerOptions) {\n this.stdout = options.stdout\n this.root = options.root\n this.debugMode = options.debug ?? false\n this.minFrameTime = options.minFrameTime ?? 16\n this.slowFrameThreshold = options.slowFrameThreshold ?? 50\n this.mode = options.mode ?? \"fullscreen\"\n this.pipelineConfig = options.pipelineConfig\n this.getCursorState = options.cursorAccessors?.getCursorState ?? globalGetCursorState\n this.log = createLogger(\"silvery:scheduler\") as unknown as Logger\n\n // Resolve non-TTY mode based on environment\n this.nonTTYMode = resolveNonTTYMode({\n mode: options.nonTTYMode,\n stdout: this.stdout,\n })\n this.outputTransformer = createOutputTransformer(this.nonTTYMode)\n\n log.debug?.(`non-TTY mode resolved to: ${this.nonTTYMode}`)\n\n // Listen for terminal resize (only in TTY mode)\n if (this.nonTTYMode === \"tty\") {\n this.setupResizeListener()\n }\n }\n\n /**\n * Get the resolved non-TTY mode.\n */\n getNonTTYMode(): ResolvedMode {\n return this.nonTTYMode\n }\n\n // ==========================================================================\n // Public API\n // ==========================================================================\n\n /**\n * Schedule a render on the next microtask.\n *\n * Multiple calls within the same synchronous execution will be\n * coalesced into a single render.\n */\n scheduleRender(): void {\n if (this.disposed) return\n\n if (this.paused) {\n this.pendingWhilePaused = true\n return\n }\n\n if (this.renderScheduled) {\n this.stats.skippedCount++\n log.debug?.(`render skipped (batched), total: ${this.stats.skippedCount}`)\n return\n }\n\n this.renderScheduled = true\n log.debug?.(\"render scheduled\")\n\n // Use queueMicrotask for batching synchronous updates\n queueMicrotask(() => {\n this.renderScheduled = false\n\n if (this.disposed) return\n\n // Check frame rate limiting\n const now = Date.now()\n const timeSinceLastRender = now - this.lastRenderTime\n\n if (timeSinceLastRender < this.minFrameTime) {\n // Schedule for next frame\n log.debug?.(`frame limited, delay: ${this.minFrameTime - timeSinceLastRender}ms`)\n this.scheduleNextFrame(this.minFrameTime - timeSinceLastRender)\n } else {\n this.executeRender()\n }\n })\n }\n\n /**\n * Force an immediate render, bypassing batching.\n */\n forceRender(): void {\n if (this.disposed) return\n\n if (this.paused) {\n this.pendingWhilePaused = true\n return\n }\n\n // Cancel any pending scheduled render\n this.renderScheduled = false\n if (this.frameTimeout) {\n clearTimeout(this.frameTimeout)\n this.frameTimeout = null\n }\n\n this.executeRender()\n }\n\n /**\n * Get render statistics.\n */\n getStats(): RenderStats {\n return { ...this.stats }\n }\n\n /**\n * Report lines written to stdout between renders (inline mode only).\n * This adjusts cursor position tracking so the next render accounts\n * for the extra lines. Used by useScrollback to notify the scheduler\n * when it writes frozen items to stdout.\n */\n addScrollbackLines(lines: number): void {\n if (this.mode !== \"inline\" || lines <= 0) return\n this.scrollbackOffset += lines\n }\n\n /**\n * Send a terminal notification.\n *\n * Auto-detects terminal type and uses the best available method:\n * - iTerm2 → OSC 9\n * - Kitty → OSC 99\n * - Others → BEL\n */\n notify(message: string, opts?: { title?: string }): void {\n if (this.disposed) return\n notifyTerminal(this.stdout, message, opts)\n }\n\n /**\n * Copy text to the system clipboard via OSC 52.\n * Works across SSH sessions in terminals that support it.\n */\n copyToClipboard(text: string): void {\n if (this.disposed) return\n copyToClipboardImpl(this.stdout, text)\n }\n\n /**\n * Pause rendering. While paused, scheduled and forced renders are deferred.\n * Input handling continues normally. Call resume() to unpause and force a\n * full redraw. Used for screen-switching (alt screen ↔ normal screen).\n */\n pause(): void {\n if (this.disposed || this.paused) return\n this.paused = true\n this.pendingWhilePaused = false\n log.debug?.(\"scheduler paused\")\n }\n\n /**\n * Resume rendering after pause. Resets the previous buffer so the next\n * render outputs everything (full redraw), then forces an immediate render.\n */\n resume(): void {\n if (this.disposed || !this.paused) return\n this.paused = false\n log.debug?.(\"scheduler resumed\")\n\n // Reset buffer for full redraw (alt screen was switched)\n this.prevBuffer = null\n\n // If anything was deferred, render now\n if (this.pendingWhilePaused) {\n this.pendingWhilePaused = false\n this.executeRender()\n }\n }\n\n /**\n * Whether the scheduler is currently paused.\n */\n isPaused(): boolean {\n return this.paused\n }\n\n /**\n * Clear the terminal and reset buffer.\n */\n clear(): void {\n if (this.disposed) return\n\n // Clear screen and keep cursor hidden\n this.stdout.write(\"\\x1b[2J\\x1b[H\\x1b[?25l\")\n\n // Reset buffer so next render outputs everything\n this.prevBuffer = null\n }\n\n /**\n * Dispose the scheduler and clean up resources.\n */\n [Symbol.dispose](): void {\n this.dispose()\n }\n\n dispose(): void {\n if (this.disposed) return\n\n log.info?.(\n `dispose: renders=${this.stats.renderCount}, skipped=${this.stats.skippedCount}, avg=${Math.round(this.stats.avgRenderTime)}ms`,\n )\n this.disposed = true\n\n // Cancel pending renders\n this.renderScheduled = false\n if (this.frameTimeout) {\n clearTimeout(this.frameTimeout)\n this.frameTimeout = null\n }\n\n // Remove resize listener\n if (this.resizeCleanup) {\n this.resizeCleanup()\n this.resizeCleanup = null\n }\n\n // In static mode, output the final frame on dispose\n if (this.nonTTYMode === \"static\" && this.staticOutput) {\n this.stdout.write(this.staticOutput)\n this.stdout.write(\"\\n\")\n }\n }\n\n /**\n * Get the last rendered output (for static mode).\n * Returns the plain text output that would be written on dispose.\n */\n getStaticOutput(): string {\n return this.staticOutput\n }\n\n // ==========================================================================\n // Private Methods\n // ==========================================================================\n\n /**\n * Schedule render for next frame (frame rate limiting).\n */\n private scheduleNextFrame(delay: number): void {\n if (this.frameTimeout) return\n\n this.frameTimeout = setTimeout(() => {\n this.frameTimeout = null\n if (!this.disposed) {\n this.executeRender()\n }\n }, delay)\n }\n\n /**\n * Execute the actual render.\n */\n private executeRender(): void {\n using render = this.log.span(\"render\")\n const startTime = Date.now()\n\n try {\n // Get terminal dimensions\n const width = this.stdout.columns ?? 80\n // Inline mode: use NaN height so layout engine auto-sizes to content.\n // Fullscreen mode: use terminal rows as the constraint.\n const height = this.mode === \"inline\" ? NaN : (this.stdout.rows ?? 24)\n\n log.debug?.(`render #${this.stats.renderCount + 1}: ${width}x${height}, nonTTYMode=${this.nonTTYMode}`)\n\n // Run render pipeline\n const scrollbackOffset = this.scrollbackOffset\n this.scrollbackOffset = 0 // Consume the offset\n // For inline mode, pass cursor state into the pipeline so the output\n // phase can position the real terminal cursor at the useCursor() location.\n const inlineCursor = this.mode === \"inline\" ? this.getCursorState() : undefined\n const { output, buffer } = executeRender(\n this.root,\n width,\n height,\n this.prevBuffer,\n {\n mode: this.mode,\n scrollbackOffset,\n termRows: this.mode === \"inline\" ? (this.stdout.rows ?? 24) : undefined,\n cursorPos: inlineCursor,\n },\n this.pipelineConfig,\n )\n\n // Transform output based on non-TTY mode\n let transformedOutput: string\n if (this.nonTTYMode === \"tty\") {\n // Pass through unchanged\n transformedOutput = output\n } else if (this.nonTTYMode === \"static\") {\n // Store for final output, don't write yet\n this.staticOutput = stripAnsi(output)\n transformedOutput = \"\"\n } else {\n // Apply line-by-line or plain transformation\n transformedOutput = this.outputTransformer(output, this.prevLineCount)\n this.prevLineCount = countLines(output)\n }\n\n // Build cursor control suffix (position + show/hide).\n // This goes after rendered content so the terminal cursor lands\n // at the right spot after painting.\n let cursorSuffix = \"\"\n if (this.nonTTYMode === \"tty\") {\n const cursor = this.getCursorState()\n if (cursor?.visible) {\n const shapeSeq = cursor.shape ? setCursorStyle(cursor.shape) : resetCursorStyle()\n cursorSuffix = ANSI.moveCursor(cursor.x, cursor.y) + shapeSeq + ANSI.CURSOR_SHOW\n } else {\n cursorSuffix = ANSI.CURSOR_HIDE\n }\n }\n\n // Write output wrapped with synchronized update (DEC 2026) for TTY mode.\n // This tells the terminal to batch the output and paint atomically,\n // preventing tearing during rapid screen updates.\n if (transformedOutput.length > 0 || cursorSuffix.length > 0) {\n const fullOutput =\n this.nonTTYMode === \"tty\" && SYNC_UPDATE_ENABLED\n ? `${ANSI.SYNC_BEGIN}${transformedOutput}${cursorSuffix}${ANSI.SYNC_END}`\n : transformedOutput + cursorSuffix\n\n // Debug: log output sizes to detect potential pipe buffer splits\n if (log.debug) {\n const bytes = Buffer.byteLength(fullOutput)\n log.debug?.(\n `stdout.write: ${bytes} bytes (${transformedOutput.length} chars output + ${cursorSuffix.length} chars cursor)`,\n )\n if (bytes > 16384) {\n log.warn?.(\n `large output: ${bytes} bytes may exceed pipe buffer (16KB on macOS), risk of mid-sequence split`,\n )\n }\n }\n\n // Capture raw ANSI output to file for debugging garbled rendering\n const captureFile = process.env.SILVERY_CAPTURE_OUTPUT\n if (captureFile) {\n const fs = require(\"fs\")\n fs.appendFileSync(\n captureFile,\n `--- FRAME ${this.stats.renderCount + 1} (${Buffer.byteLength(fullOutput)} bytes) ---\\n`,\n )\n fs.appendFileSync(captureFile, fullOutput)\n fs.appendFileSync(captureFile, \"\\n\")\n }\n\n this.stdout.write(fullOutput)\n }\n\n // Save buffer for next diff\n this.prevBuffer = buffer\n\n // SILVERY_STRICT: compare incremental render against fresh render\n const strictEnv = process.env.SILVERY_STRICT\n const strictMode = strictEnv && strictEnv !== \"0\" && strictEnv !== \"false\"\n if (strictMode && this.stats.renderCount > 0) {\n const renderNum = this.stats.renderCount + 1\n const { buffer: freshBuffer } = executeRender(\n this.root,\n width,\n height,\n null,\n {\n mode: this.mode === \"fullscreen\" ? \"fullscreen\" : \"inline\",\n skipLayoutNotifications: true,\n },\n this.pipelineConfig,\n )\n let found = false\n for (let y = 0; y < buffer.height && !found; y++) {\n for (let x = 0; x < buffer.width && !found; x++) {\n const a = buffer.getCell(x, y)\n const b = freshBuffer.getCell(x, y)\n if (!cellEquals(a, b)) {\n found = true\n\n // Build rich debug context\n const ctx = buildMismatchContext(this.root, x, y, a, b, renderNum)\n\n // Capture content-phase instrumentation snapshot\n const contentPhaseStats: ContentPhaseStats | undefined = (globalThis as any).__silvery_content_detail\n ? structuredClone((globalThis as any).__silvery_content_detail)\n : undefined\n\n const debugInfo = formatMismatchContext(ctx, contentPhaseStats)\n\n // Include text output for full picture\n const incText = bufferToText(buffer)\n const freshText = bufferToText(freshBuffer)\n const msg = debugInfo + `--- incremental ---\\n${incText}\\n--- fresh ---\\n${freshText}`\n\n if (process.env.DEBUG_LOG) {\n appendFileSync(process.env.DEBUG_LOG, msg + \"\\n\")\n }\n log.error?.(msg)\n // Throw special error that won't be caught by general error handler\n throw new IncrementalRenderMismatchError(msg, {\n contentPhaseStats,\n mismatchContext: ctx,\n })\n }\n }\n }\n if (!found && process.env.DEBUG_LOG) {\n appendFileSync(process.env.DEBUG_LOG, `SILVERY_STRICT: render #${renderNum} OK\\n`)\n }\n }\n\n // Update stats\n const renderTime = Date.now() - startTime\n this.stats.renderCount++\n this.stats.lastRenderTime = renderTime\n this.stats.avgRenderTime =\n (this.stats.avgRenderTime * (this.stats.renderCount - 1) + renderTime) / this.stats.renderCount\n this.lastRenderTime = Date.now()\n\n // Record span data\n render.spanData.renderCount = this.stats.renderCount\n render.spanData.renderTime = renderTime\n render.spanData.bytes = transformedOutput.length\n\n log.debug?.(\n `render #${this.stats.renderCount} complete: ${renderTime}ms, output: ${transformedOutput.length} bytes`,\n )\n\n // First render is always slow (initialization); use 5x threshold for it\n const threshold = this.stats.renderCount <= 1 ? this.slowFrameThreshold * 5 : this.slowFrameThreshold\n if (threshold > 0 && renderTime > threshold) {\n log.warn?.(\n `slow frame: render #${this.stats.renderCount} took ${renderTime}ms (threshold: ${this.slowFrameThreshold}ms, bytes: ${transformedOutput.length})`,\n )\n }\n\n if (this.debugMode) {\n this.logDebug(`Render #${this.stats.renderCount} took ${renderTime}ms`)\n }\n } catch (error) {\n // Log and re-throw all render errors - the app should handle cleanup\n log.error?.(`render error: ${error}`)\n this.logError(\"Render error:\", error)\n throw error\n }\n }\n\n /**\n * Set up terminal resize listener.\n */\n private setupResizeListener(): void {\n let resizeTimeout: ReturnType<typeof setTimeout> | null = null\n\n const handleResize = () => {\n // Debounce resize events\n if (resizeTimeout) {\n clearTimeout(resizeTimeout)\n }\n\n resizeTimeout = setTimeout(() => {\n resizeTimeout = null\n\n // Reset buffer to force full redraw\n this.prevBuffer = null\n\n // Schedule render\n this.scheduleRender()\n }, 50) // 50ms debounce\n }\n\n this.stdout.on(\"resize\", handleResize)\n\n this.resizeCleanup = () => {\n this.stdout.off(\"resize\", handleResize)\n if (resizeTimeout) {\n clearTimeout(resizeTimeout)\n }\n }\n }\n\n /**\n * Log debug message.\n */\n private logDebug(message: string): void {\n // Write to stderr to avoid corrupting terminal output\n process.stderr.write(`[Silvery Debug] ${message}\\n`)\n }\n\n /**\n * Log error message.\n */\n private logError(message: string, error: unknown): void {\n process.stderr.write(`[Silvery Error] ${message}\\n`)\n if (error instanceof Error) {\n process.stderr.write(`${error.stack ?? error.message}\\n`)\n } else {\n process.stderr.write(`${String(error)}\\n`)\n }\n }\n}\n\n// ============================================================================\n// Factory Function\n// ============================================================================\n\n/**\n * Create a render scheduler.\n *\n * @param options Scheduler options\n * @returns A new RenderScheduler instance\n */\nexport function createScheduler(options: SchedulerOptions): RenderScheduler {\n return new RenderScheduler(options)\n}\n\n// ============================================================================\n// Utility: Simple Render (for testing/debugging)\n// ============================================================================\n\n/**\n * Render once to a string (for testing).\n *\n * Does not batch or diff - just runs the pipeline and returns ANSI output.\n */\nexport function renderToString(root: AgNode, width: number, height: number): string {\n const { output } = executeRender(root, width, height, null)\n return output\n}\n",
126
+ "/**\n * Debug utilities for incremental render mismatch diagnostics.\n *\n * When SILVERY_STRICT detects a mismatch between incremental and fresh renders,\n * these utilities help identify the root cause by providing:\n * - Node attribution (which node owns the mismatched cell)\n * - Dirty flag state (what flags were set before render)\n * - Layout changes (prevLayout vs contentRect)\n * - Scroll context (offset changes, hidden items)\n */\n\nimport type { Cell } from \"@silvery/ag-term/buffer\"\nimport type { BoxProps, AgNode, Rect, TextProps } from \"@silvery/ag/types\"\nimport type { ContentPhaseStats } from \"@silvery/ag-term/pipeline/types\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Debug info about a node at a screen position */\nexport interface NodeDebugInfo {\n /** Node ID (if set via props.id) */\n id: string | undefined\n /** Node type (silvery-box, silvery-text, silvery-root) */\n type: string\n /** Path from root to this node (IDs or indices) */\n path: string\n /** Index within parent's children array */\n childIndex: number | null\n /** Dirty flags at time of mismatch */\n dirtyFlags: {\n contentDirty: boolean\n stylePropsDirty: boolean\n subtreeDirty: boolean\n childrenDirty: boolean\n layoutDirty: boolean\n }\n /** Layout info */\n layout: {\n prevLayout: Rect | null\n contentRect: Rect | null\n screenRect: Rect | null\n layoutChanged: boolean\n }\n /** Scroll context (if this is a scroll container or inside one) */\n scroll?: {\n offset: number\n prevOffset: number\n offsetChanged: boolean\n contentHeight: number\n viewportHeight: number\n hiddenAbove: number\n hiddenBelow: number\n firstVisibleChild: number\n lastVisibleChild: number\n }\n /** Background color from props */\n backgroundColor: string | undefined\n /** Number of children */\n childCount: number\n /** Whether node is hidden (Suspense) */\n hidden: boolean\n}\n\n/** Full mismatch debug context */\nexport interface MismatchDebugContext {\n /** Screen position of the mismatch */\n position: { x: number; y: number }\n /** Cell values */\n cells: {\n incremental: Cell\n fresh: Cell\n }\n /** Render number */\n renderNum: number\n /** Node that owns this screen position (innermost) */\n node: NodeDebugInfo | null\n /** Scroll container ancestry (if any) */\n scrollAncestors: NodeDebugInfo[]\n /** All nodes whose screenRect contains this position */\n containingNodes: NodeDebugInfo[]\n /** Fast-path analysis - why the node was likely skipped */\n fastPathAnalysis: string[]\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Find the innermost node at a screen position.\n */\nexport function findNodeAtPosition(root: AgNode, x: number, y: number): AgNode | null {\n let result: AgNode | null = null\n\n function visit(node: AgNode): void {\n const rect = node.screenRect\n if (!rect) return\n\n // Check if position is within this node's screenRect\n if (x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height) {\n result = node // This node contains the position\n\n // Check children (later children render on top of earlier ones)\n for (const child of node.children) {\n visit(child)\n }\n }\n }\n\n visit(root)\n return result\n}\n\n/**\n * Find all nodes whose screenRect contains the given position.\n * Returns nodes from root to innermost (outermost first).\n */\nexport function findAllContainingNodes(root: AgNode, x: number, y: number): AgNode[] {\n const result: AgNode[] = []\n\n function visit(node: AgNode): void {\n const rect = node.screenRect\n if (!rect) return\n\n if (x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height) {\n result.push(node)\n for (const child of node.children) {\n visit(child)\n }\n }\n }\n\n visit(root)\n return result\n}\n\n/**\n * Get the path from root to a node (for identification).\n */\nfunction getNodePath(node: AgNode): string {\n const parts: string[] = []\n let current: AgNode | null = node\n\n while (current) {\n const props = current.props as BoxProps & TextProps\n if (props.id) {\n parts.unshift(`#${props.id}`)\n } else if (current.parent) {\n const idx = current.parent.children.indexOf(current)\n parts.unshift(`[${idx}]`)\n } else {\n parts.unshift(\"root\")\n }\n current = current.parent\n }\n\n return parts.join(\" > \")\n}\n\n/**\n * Check if a rect changed (position or size).\n */\nfunction rectChanged(a: Rect | null, b: Rect | null): boolean {\n if (a === b) return false\n if (!a || !b) return true\n return a.x !== b.x || a.y !== b.y || a.width !== b.width || a.height !== b.height\n}\n\n/**\n * Extract debug info from a node.\n */\nexport function getNodeDebugInfo(node: AgNode): NodeDebugInfo {\n const props = node.props as BoxProps & TextProps\n\n // Get child index within parent\n let childIndex: number | null = null\n if (node.parent) {\n childIndex = node.parent.children.indexOf(node)\n }\n\n return {\n id: props.id,\n type: node.type,\n path: getNodePath(node),\n childIndex,\n dirtyFlags: {\n contentDirty: node.contentDirty,\n stylePropsDirty: node.stylePropsDirty,\n subtreeDirty: node.subtreeDirty,\n childrenDirty: node.childrenDirty,\n layoutDirty: node.layoutDirty,\n },\n layout: {\n prevLayout: node.prevLayout,\n contentRect: node.contentRect,\n screenRect: node.screenRect,\n layoutChanged: rectChanged(node.prevLayout, node.contentRect),\n },\n scroll: node.scrollState\n ? {\n offset: node.scrollState.offset,\n prevOffset: node.scrollState.prevOffset,\n offsetChanged: node.scrollState.offset !== node.scrollState.prevOffset,\n contentHeight: node.scrollState.contentHeight,\n viewportHeight: node.scrollState.viewportHeight,\n hiddenAbove: node.scrollState.hiddenAbove,\n hiddenBelow: node.scrollState.hiddenBelow,\n firstVisibleChild: node.scrollState.firstVisibleChild,\n lastVisibleChild: node.scrollState.lastVisibleChild,\n }\n : undefined,\n backgroundColor: props.backgroundColor,\n childCount: node.children.length,\n hidden: node.hidden ?? false,\n }\n}\n\n/**\n * Find scroll container ancestors for a node.\n */\nfunction findScrollAncestors(node: AgNode): AgNode[] {\n const result: AgNode[] = []\n let current = node.parent\n\n while (current) {\n if (current.scrollState) {\n result.push(current)\n }\n current = current.parent\n }\n\n return result\n}\n\n/**\n * Analyze why a node might have been incorrectly skipped by fast-path.\n */\nfunction analyzeFastPath(node: AgNode | null, scrollAncestors: AgNode[]): string[] {\n const analysis: string[] = []\n\n if (!node) {\n analysis.push(\"⚠ No node found at mismatch position - possible virtualization issue\")\n return analysis\n }\n\n const flags = node\n const allClean =\n !flags.contentDirty && !flags.stylePropsDirty && !flags.subtreeDirty && !flags.childrenDirty && !flags.layoutDirty\n\n if (allClean) {\n analysis.push(\"⚠ ALL DIRTY FLAGS FALSE - fast-path likely skipped this node\")\n }\n\n // Check if node is in a scroll container\n const scrollParent = scrollAncestors[0]\n if (scrollParent?.scrollState) {\n const ss = scrollParent.scrollState\n const childIndex = node.parent ? node.parent.children.indexOf(node) : -1\n\n // Check if this node SHOULD be in visible range\n const inVisibleRange = childIndex >= ss.firstVisibleChild && childIndex <= ss.lastVisibleChild\n if (!inVisibleRange && childIndex >= 0) {\n analysis.push(\n `⚠ Node index ${childIndex} is OUTSIDE visible range [${ss.firstVisibleChild}..${ss.lastVisibleChild}]`,\n )\n analysis.push(\" → Node should have been skipped, but mismatch suggests it should render\")\n } else if (inVisibleRange) {\n analysis.push(`✓ Node index ${childIndex} is in visible range [${ss.firstVisibleChild}..${ss.lastVisibleChild}]`)\n }\n\n // Check scroll offset\n if (ss.offset === ss.prevOffset) {\n analysis.push(\"✓ Scroll offset unchanged (fast-path enabled for children)\")\n } else {\n analysis.push(`⚠ Scroll offset CHANGED: ${ss.prevOffset} → ${ss.offset}`)\n }\n\n // Check if visible range might have changed\n if (ss.firstVisibleChild !== 0 || ss.lastVisibleChild !== scrollParent.children.length - 1) {\n analysis.push(\n ` Visible range is partial: [${ss.firstVisibleChild}..${ss.lastVisibleChild}] of ${scrollParent.children.length} children`,\n )\n analysis.push(\" → If visible range changed, newly visible children need rendering\")\n }\n }\n\n // Check prevLayout\n const layoutChanged = rectChanged(node.prevLayout, node.contentRect)\n if (!layoutChanged && node.prevLayout) {\n analysis.push(\"✓ Layout unchanged (prevLayout matches contentRect)\")\n } else if (!node.prevLayout) {\n analysis.push(\"⚠ prevLayout is NULL - node may never have been rendered before\")\n } else {\n analysis.push(\"⚠ Layout CHANGED but node still skipped - dirty flag not set?\")\n }\n\n // Check for sibling child position changes\n if (node.parent && node.parent.children.length > 1) {\n let siblingMoved = false\n for (const sibling of node.parent.children) {\n if (sibling !== node && sibling.contentRect && sibling.prevLayout) {\n if (sibling.contentRect.x !== sibling.prevLayout.x || sibling.contentRect.y !== sibling.prevLayout.y) {\n siblingMoved = true\n break\n }\n }\n }\n if (siblingMoved) {\n analysis.push(\"⚠ SIBLING POSITION CHANGED - parent should have detected this\")\n }\n }\n\n // Check hidden state\n if (node.hidden) {\n analysis.push(\"⚠ Node is HIDDEN (Suspense) - should not be rendered\")\n }\n\n return analysis\n}\n\n/**\n * Build full mismatch debug context.\n */\nexport function buildMismatchContext(\n root: AgNode,\n x: number,\n y: number,\n incrementalCell: Cell,\n freshCell: Cell,\n renderNum: number,\n): MismatchDebugContext {\n const innermost = findNodeAtPosition(root, x, y)\n const containing = findAllContainingNodes(root, x, y)\n const scrollAncestorNodes = innermost ? findScrollAncestors(innermost) : []\n\n return {\n position: { x, y },\n cells: {\n incremental: incrementalCell,\n fresh: freshCell,\n },\n renderNum,\n node: innermost ? getNodeDebugInfo(innermost) : null,\n scrollAncestors: scrollAncestorNodes.map(getNodeDebugInfo),\n containingNodes: containing.map(getNodeDebugInfo),\n fastPathAnalysis: analyzeFastPath(innermost, scrollAncestorNodes),\n }\n}\n\n/**\n * Format mismatch context as a human-readable string.\n *\n * @param ctx - The mismatch debug context (node attribution, dirty flags, scroll, fast-path)\n * @param contentPhaseStats - Optional content-phase instrumentation snapshot (auto-included by SILVERY_STRICT)\n */\nexport function formatMismatchContext(ctx: MismatchDebugContext, contentPhaseStats?: ContentPhaseStats): string {\n const lines: string[] = []\n\n // Header\n lines.push(`SILVERY_STRICT: MISMATCH at (${ctx.position.x}, ${ctx.position.y}) on render #${ctx.renderNum}`)\n lines.push(\"\")\n\n // Cell values\n const { incremental, fresh } = ctx.cells\n lines.push(\"CELL VALUES:\")\n lines.push(\n ` incremental: char=${JSON.stringify(incremental.char)} fg=${JSON.stringify(incremental.fg)} bg=${JSON.stringify(incremental.bg)} attrs=${JSON.stringify(incremental.attrs)}`,\n )\n lines.push(\n ` fresh: char=${JSON.stringify(fresh.char)} fg=${JSON.stringify(fresh.fg)} bg=${JSON.stringify(fresh.bg)} attrs=${JSON.stringify(fresh.attrs)}`,\n )\n lines.push(\"\")\n\n // Node attribution\n if (ctx.node) {\n lines.push(\"INNERMOST NODE:\")\n lines.push(` path: ${ctx.node.path}`)\n lines.push(` type: ${ctx.node.type}`)\n if (ctx.node.backgroundColor) {\n lines.push(` backgroundColor: ${ctx.node.backgroundColor}`)\n }\n lines.push(\"\")\n\n // Dirty flags\n const flags = ctx.node.dirtyFlags\n const activeFlags = Object.entries(flags)\n .filter(([, v]) => v)\n .map(([k]) => k)\n lines.push(\"DIRTY FLAGS:\")\n if (activeFlags.length > 0) {\n lines.push(` active: ${activeFlags.join(\", \")}`)\n } else {\n lines.push(\" active: (none - node was clean)\")\n }\n lines.push(\n ` all: contentDirty=${flags.contentDirty} stylePropsDirty=${flags.stylePropsDirty} subtreeDirty=${flags.subtreeDirty} childrenDirty=${flags.childrenDirty} layoutDirty=${flags.layoutDirty}`,\n )\n lines.push(\"\")\n\n // Layout info\n const { layout } = ctx.node\n lines.push(\"LAYOUT:\")\n if (layout.layoutChanged) {\n lines.push(\" ⚠ LAYOUT CHANGED:\")\n lines.push(` prevLayout: ${formatRect(layout.prevLayout)}`)\n lines.push(` contentRect: ${formatRect(layout.contentRect)}`)\n } else {\n lines.push(` contentRect: ${formatRect(layout.contentRect)}`)\n }\n lines.push(` screenRect: ${formatRect(layout.screenRect)}`)\n lines.push(\"\")\n\n // Scroll context\n if (ctx.node.scroll) {\n lines.push(\"SCROLL STATE (this node):\")\n formatScrollState(lines, ctx.node.scroll)\n lines.push(\"\")\n }\n } else {\n lines.push(\"INNERMOST NODE: (none found at this position)\")\n lines.push(\"\")\n }\n\n // Scroll ancestors\n if (ctx.scrollAncestors.length > 0) {\n lines.push(\"SCROLL ANCESTORS:\")\n for (const ancestor of ctx.scrollAncestors) {\n lines.push(` ${ancestor.path}:`)\n if (ancestor.scroll) {\n formatScrollState(lines, ancestor.scroll, \" \")\n }\n }\n lines.push(\"\")\n }\n\n // Containing nodes (for debugging layering issues)\n if (ctx.containingNodes.length > 1) {\n lines.push(\"ALL CONTAINING NODES (outermost to innermost):\")\n for (const node of ctx.containingNodes) {\n const flags = Object.entries(node.dirtyFlags)\n .filter(([, v]) => v)\n .map(([k]) => k.replace(\"Dirty\", \"\"))\n .join(\",\")\n const flagStr = flags ? ` [${flags}]` : \" [clean]\"\n const bgStr = node.backgroundColor ? ` bg=${node.backgroundColor}` : \"\"\n const childStr = node.childIndex !== null ? ` child[${node.childIndex}]` : \"\"\n lines.push(` ${node.path}${flagStr}${bgStr}${childStr}`)\n }\n lines.push(\"\")\n }\n\n // Fast-path analysis\n if (ctx.fastPathAnalysis.length > 0) {\n lines.push(\"FAST-PATH ANALYSIS:\")\n for (const line of ctx.fastPathAnalysis) {\n lines.push(` ${line}`)\n }\n lines.push(\"\")\n }\n\n // Content-phase instrumentation stats\n if (contentPhaseStats) {\n const s = contentPhaseStats\n lines.push(\"CONTENT PHASE STATS:\")\n lines.push(` nodesVisited: ${s.nodesVisited} nodesRendered: ${s.nodesRendered} nodesSkipped: ${s.nodesSkipped}`)\n lines.push(` textNodes: ${s.textNodes} boxNodes: ${s.boxNodes} clearOps: ${s.clearOps}`)\n // Per-flag breakdown (why nodes weren't skipped)\n const flagLines: string[] = []\n if (s.noPrevBuffer) flagLines.push(`noPrevBuffer=${s.noPrevBuffer}`)\n if (s.flagContentDirty) flagLines.push(`contentDirty=${s.flagContentDirty}`)\n if (s.flagStylePropsDirty) flagLines.push(`stylePropsDirty=${s.flagStylePropsDirty}`)\n if (s.flagLayoutChanged) flagLines.push(`layoutChanged=${s.flagLayoutChanged}`)\n if (s.flagSubtreeDirty) flagLines.push(`subtreeDirty=${s.flagSubtreeDirty}`)\n if (s.flagChildrenDirty) flagLines.push(`childrenDirty=${s.flagChildrenDirty}`)\n if (s.flagChildPositionChanged) flagLines.push(`childPositionChanged=${s.flagChildPositionChanged}`)\n if (flagLines.length > 0) {\n lines.push(` render reasons: ${flagLines.join(\", \")}`)\n }\n // Scroll container diagnostics\n if (s.scrollContainerCount > 0) {\n lines.push(` scrollContainers: ${s.scrollContainerCount} viewportCleared: ${s.scrollViewportCleared}`)\n if (s.scrollClearReason) lines.push(` scrollClearReason: ${s.scrollClearReason}`)\n }\n // Normal container diagnostics\n if (s.normalChildrenRepaint > 0) {\n lines.push(` normalChildrenRepaint: ${s.normalChildrenRepaint}`)\n if (s.normalRepaintReason) lines.push(` normalRepaintReason: ${s.normalRepaintReason}`)\n }\n // Cascade diagnostics\n if (s.cascadeMinDepth < 999) {\n lines.push(` cascadeMinDepth: ${s.cascadeMinDepth}`)\n if (s.cascadeNodes) lines.push(` cascadeNodes: ${s.cascadeNodes}`)\n }\n lines.push(\"\")\n }\n\n return lines.join(\"\\n\")\n}\n\nfunction formatRect(rect: Rect | null): string {\n if (!rect) return \"(null)\"\n return `{x:${rect.x}, y:${rect.y}, w:${rect.width}, h:${rect.height}}`\n}\n\nfunction formatScrollState(lines: string[], scroll: NonNullable<NodeDebugInfo[\"scroll\"]>, indent = \" \"): void {\n if (scroll.offsetChanged) {\n lines.push(`${indent}⚠ SCROLL CHANGED: offset ${scroll.prevOffset} → ${scroll.offset}`)\n } else {\n lines.push(`${indent}offset: ${scroll.offset}`)\n }\n lines.push(\n `${indent}viewport: ${scroll.viewportHeight}/${scroll.contentHeight} (hidden: ▲${scroll.hiddenAbove} ▼${scroll.hiddenBelow})`,\n )\n lines.push(`${indent}visibleRange: [${scroll.firstVisibleChild}..${scroll.lastVisibleChild}]`)\n}\n",
127
+ "/**\n * Unified Render API for silvery\n *\n * Composable primitives:\n * - render(element, opts | store) — always sync, returns full App\n * - createStore(providers) — flattens providers into { cols, rows, events() }\n * - run(app, events?) — event loop driver (sync or async)\n * - createApp(element, providers) — sugar: render + createStore + run\n * - createRenderer(opts | store) — factory with auto-cleanup\n */\n\nimport { EventEmitter } from \"node:events\"\nimport React, { type ReactElement, type ReactNode, act } from \"react\"\nimport { type App, buildApp } from \"./app.js\"\nimport { type TerminalBuffer, cellEquals } from \"./buffer.js\"\nimport {\n FocusManagerContext,\n RuntimeContext,\n type RuntimeContextValue,\n StdoutContext,\n TermContext,\n} from \"@silvery/ag-react/context\"\nimport { createFocusManager } from \"@silvery/ag/focus-manager\"\nimport {\n type LayoutEngine,\n ensureDefaultLayoutEngine,\n isLayoutEngineInitialized,\n setLayoutEngine,\n} from \"./layout-engine.js\"\nimport { executeRender } from \"./pipeline.js\"\nimport {\n createContainer,\n createFiberRoot,\n getContainerRoot,\n reconciler,\n setOnNodeRemoved,\n} from \"@silvery/ag-react/reconciler\"\n\nimport { createTerm } from \"./ansi/index\"\nimport { bufferToText } from \"./buffer.js\"\nimport { buildMismatchContext, formatMismatchContext } from \"@silvery/test/debug-mismatch\"\nimport { createCursorStore, CursorProvider } from \"@silvery/ag-react/hooks/useCursor\"\nimport { keyToAnsi, parseKey, splitRawInput } from \"@silvery/ag/keys\"\nimport { parseBracketedPaste } from \"./bracketed-paste\"\nimport { IncrementalRenderMismatchError } from \"./scheduler.js\"\nimport type { ContentPhaseStats } from \"./pipeline/types\"\nimport { debugTree } from \"@silvery/test/debug\"\n\n// ============================================================================\n// Defensive Guards\n// ============================================================================\n\n/**\n * Track all active (mounted) render instances to detect leaks.\n * Uses a Set of WeakRefs so GC can clean up unreferenced apps.\n */\nconst activeRenders = new Set<WeakRef<{ unmount: () => void; id: number }>>()\nlet renderIdCounter = 0\n\n/**\n * Maximum number of active render instances before throwing.\n * Set high to allow large test files (each test may create a render without unmount),\n * but catch genuine leaks like infinite loops creating renders.\n */\nconst ACTIVE_RENDER_LEAK_THRESHOLD = 1000\n\n/**\n * Prune GC'd entries from activeRenders and return live count.\n */\nfunction pruneAndCountActiveRenders(): number {\n let count = 0\n for (const ref of activeRenders) {\n if (ref.deref() === undefined) {\n activeRenders.delete(ref)\n } else {\n count++\n }\n }\n return count\n}\n\n/**\n * Assert that the layout engine is initialized before rendering.\n * This catches the common mistake of calling render() without await ensureEngine().\n */\nfunction assertLayoutEngine(): void {\n if (!isLayoutEngineInitialized()) {\n throw new Error(\n \"silvery: Layout engine not initialized. \" +\n \"Call `await ensureEngine()` before render(), or use the testing module \" +\n \"which initializes it automatically via top-level await.\",\n )\n }\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Options for headless render (no terminal).\n */\nexport interface RenderOptions {\n /** Terminal width for layout. Default: 80 */\n cols?: number\n /** Terminal height for layout. Default: 24 */\n rows?: number\n /** Layout engine to use. Default: current global engine */\n layoutEngine?: LayoutEngine\n /** Enable debug output. Default: false */\n debug?: boolean\n /** Enable incremental rendering. Default: true */\n incremental?: boolean\n /** Use Kitty keyboard protocol encoding for press(). When true, press() uses keyToKittyAnsi. */\n kittyMode?: boolean\n /**\n * Use production-like single-pass layout in doRender().\n *\n * When false (default), doRender() runs a synchronous layout stabilization\n * loop (up to 5 iterations) that re-runs executeRender whenever React\n * commits new work from layout notifications (useContentRect, etc.).\n *\n * When true, doRender() does a single executeRender call (matching\n * production's create-app.tsx behavior). Layout feedback effects are\n * flushed via a separate act()/flushSyncWork() loop after doRender(),\n * mimicking production's processEventBatch flush pattern.\n *\n * Use this to make tests exercise the same rendering pipeline as production.\n */\n singlePassLayout?: boolean\n /**\n * Auto-render on async React commits (e.g., setTimeout → setState).\n *\n * When true, the renderer schedules a microtask re-render whenever React\n * commits new work outside of explicit render/sendInput/rerender calls.\n * This enables test components with async state updates to produce new\n * frames automatically.\n *\n * Default: false (renderer only renders on explicit triggers).\n */\n autoRender?: boolean\n /**\n * Callback fired after each frame render.\n *\n * Called with the frame output string and the underlying TerminalBuffer.\n * Fires after initial render, sendInput, rerender, and (if autoRender)\n * async state changes.\n */\n onFrame?: (frame: string, buffer: TerminalBuffer, contentHeight?: number) => void\n /**\n * Callback fired after each pipeline execution, before React effects flush.\n *\n * Called inside act() after executeRender produces the buffer but before\n * useLayoutEffect/useEffect callbacks run. Use this to make pipeline output\n * available to effects (e.g., Ink compat debug mode where useStdout().write()\n * needs to replay the latest frame).\n */\n onBufferReady?: (frame: string, buffer: TerminalBuffer, contentHeight?: number) => void\n /**\n * Wrap the root element with additional providers.\n * Called with the element after silvery's internal contexts are applied.\n * Use this to inject additional context providers (e.g., Ink compatibility wrappers).\n * The wrapper is applied INSIDE silvery's contexts, so wrapped providers\n * can access silvery's Term, Stdout, FocusManager, and Runtime contexts.\n */\n wrapRoot?: (element: React.ReactElement) => React.ReactElement\n /**\n * External stdin stream to bridge to the renderer's input.\n * When provided, readable data from this stream is forwarded to the renderer's\n * input handler (equivalent to calling app.stdin.write()).\n */\n stdin?: NodeJS.ReadStream\n}\n\n/**\n * Store — the TermDef-like environment for render().\n * Provides cols, rows, and optionally an event stream for interactive mode.\n */\nexport interface Store {\n /** Terminal columns */\n readonly cols: number\n /** Terminal rows */\n readonly rows: number\n /** Async event stream (if present, enables interactive mode) */\n events?(): AsyncIterable<StoreEvent>\n}\n\n/**\n * Event from a store's event stream.\n */\nexport interface StoreEvent {\n type: string\n data: unknown\n}\n\n/**\n * Provider options for createStore().\n */\nexport interface StoreOptions {\n /** Terminal columns. Default: 80 */\n cols?: number\n /** Terminal rows. Default: 24 */\n rows?: number\n /** Event source for interactive mode */\n events?: AsyncIterable<StoreEvent>\n}\n\n// ============================================================================\n// Module Initialization\n// ============================================================================\n\n// Layout engine initialization promise (lazy)\nlet engineReady: Promise<void> | null = null\n\n/**\n * Ensure layout engine is initialized (async, cached).\n */\nexport async function ensureEngine(): Promise<void> {\n if (isLayoutEngineInitialized()) return\n if (!engineReady) {\n engineReady = ensureDefaultLayoutEngine()\n }\n await engineReady\n}\n\n// ============================================================================\n// render() — sync, returns full App\n// ============================================================================\n\n/**\n * Internal state for a render instance.\n */\ninterface RenderInstance {\n frames: string[]\n container: ReturnType<typeof createContainer>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- React reconciler internal type\n fiberRoot: any\n prevBuffer: TerminalBuffer | null\n mounted: boolean\n /** True while inside act() or doRender() — detects re-entrant calls */\n rendering: boolean\n columns: number\n rows: number\n inputEmitter: EventEmitter\n debug: boolean\n incremental: boolean\n /** Render count for SILVERY_STRICT checking (skip first render) */\n renderCount: number\n /** Use production-like single-pass layout (no stabilization loop) */\n singlePassLayout: boolean\n}\n\nfunction isStore(arg: unknown): arg is Store {\n // Store has cols and rows as required (not optional) properties.\n // RenderOptions has them as optional. Disambiguate by checking for\n // RenderOptions-only traits.\n if (arg === null || typeof arg !== \"object\") return false\n const obj = arg as Record<string, unknown>\n return (\n typeof obj.cols === \"number\" &&\n typeof obj.rows === \"number\" &&\n !(\"layoutEngine\" in obj) &&\n !(\"debug\" in obj) &&\n !(\"incremental\" in obj) &&\n !(\"singlePassLayout\" in obj) &&\n !(\"autoRender\" in obj) &&\n !(\"onFrame\" in obj) &&\n !(\"kittyMode\" in obj) &&\n !(\"wrapRoot\" in obj) &&\n !(\"stdin\" in obj)\n )\n}\n\n/**\n * Render a React element synchronously. Returns a full App with locators,\n * press(), text, ansi, etc.\n *\n * Layout engine must be initialized before calling (use ensureEngine() or\n * the top-level await in testing module).\n *\n * Overloads:\n * - render(element, { cols, rows }) — headless with dimensions\n * - render(element, store) — with a Store from createStore()\n *\n * @example\n * ```tsx\n * const app = render(<Counter />, { cols: 80, rows: 24 })\n * expect(app.text).toContain('Count: 0')\n * await app.press('j')\n * expect(app.text).toContain('Count: 1')\n * ```\n */\nexport function render(element: ReactElement, optsOrStore: RenderOptions | Store = {}): App {\n // Guard: layout engine must be initialized\n assertLayoutEngine()\n\n const storeMode = isStore(optsOrStore)\n const cols = storeMode ? optsOrStore.cols : (optsOrStore.cols ?? 80)\n const rows = storeMode ? optsOrStore.rows : (optsOrStore.rows ?? 24)\n const debug = storeMode ? false : (optsOrStore.debug ?? false)\n // Incremental rendering is enabled by default for all renders\n // Store mode also supports incremental - the RenderInstance tracks prevBuffer\n const incremental = storeMode ? true : (optsOrStore.incremental ?? true)\n const singlePassLayout = storeMode ? false : (optsOrStore.singlePassLayout ?? false)\n const kittyMode = storeMode ? false : (optsOrStore.kittyMode ?? false)\n const autoRender = storeMode ? false : (optsOrStore.autoRender ?? false)\n const onFrame = storeMode ? undefined : optsOrStore.onFrame\n const onBufferReady = storeMode ? undefined : optsOrStore.onBufferReady\n const wrapRoot = storeMode ? undefined : optsOrStore.wrapRoot\n const stdinStream = storeMode ? undefined : optsOrStore.stdin\n\n // Guard: detect render leaks (absurd number of active instances)\n const liveCount = pruneAndCountActiveRenders()\n if (liveCount >= ACTIVE_RENDER_LEAK_THRESHOLD) {\n throw new Error(\n `silvery: ${liveCount} active render instances without unmount(). ` +\n \"This is a render leak. Use createRenderer() for auto-cleanup, \" +\n \"or call unmount() when done with each render.\",\n )\n }\n\n // Set layout engine if provided\n if (!storeMode && optsOrStore.layoutEngine) {\n setLayoutEngine(optsOrStore.layoutEngine)\n }\n\n // Unique ID for this render instance (for tracking/debugging)\n const renderId = ++renderIdCounter\n\n const instance: RenderInstance = {\n frames: [],\n container: null as unknown as ReturnType<typeof createContainer>,\n fiberRoot: null,\n prevBuffer: null,\n mounted: true,\n rendering: false,\n columns: cols,\n rows: rows,\n inputEmitter: new EventEmitter(),\n debug,\n incremental,\n renderCount: 0,\n singlePassLayout,\n }\n\n // Track whether React committed new work (from layout notifications etc.)\n let hadReactCommit = false\n let autoRenderScheduled = false\n let inRenderCycle = false // true during doRender() and explicit operations\n instance.container = createContainer(() => {\n hadReactCommit = true\n // Auto-render: schedule a microtask re-render on async React commits\n // (e.g., setTimeout → setState). Skipped during explicit render operations\n // (rendering=true or inRenderCycle=true) since those call doRender() themselves.\n if (autoRender && !instance.rendering && !inRenderCycle && !autoRenderScheduled && instance.mounted) {\n autoRenderScheduled = true\n queueMicrotask(() => {\n autoRenderScheduled = false\n if (!instance.mounted || instance.rendering || inRenderCycle) return\n inRenderCycle = true\n try {\n const newFrame = doRender()\n instance.frames.push(newFrame)\n onFrame?.(newFrame, instance.prevBuffer!, getRootContentHeight())\n } finally {\n inRenderCycle = false\n }\n })\n }\n })\n\n instance.fiberRoot = createFiberRoot(instance.container)\n\n /**\n * Get the content extent of the root node's children (for buffer padding trimming).\n * Returns the max outer bottom edge of the root's direct children, including\n * margins. The root itself stretches to fill the terminal, but its children\n * have specific layout-computed heights plus margins that extend beyond.\n * Returns 0 if no children have layout (empty tree).\n * Returns undefined if root has no layout at all.\n */\n const getRootContentHeight = (): number | undefined => {\n try {\n const root = getContainerRoot(instance.container)\n if (!root?.contentRect) return undefined\n let maxBottom = 0\n let hasChildren = false\n for (const child of root.children) {\n if (child.contentRect) {\n hasChildren = true\n // contentRect includes marginTop in the y position but NOT marginBottom\n // in the height. Read marginBottom from props to get the full outer extent.\n const props = child.props as Record<string, unknown>\n const mb = (props.marginBottom as number) ?? (props.marginY as number) ?? (props.margin as number) ?? 0\n const childBottom = child.contentRect.y + child.contentRect.height + mb\n if (childBottom > maxBottom) maxBottom = childBottom\n }\n }\n return hasChildren ? maxBottom : 0\n } catch {\n return undefined\n }\n }\n\n // Track exit state\n let exitCalledFlag = false\n let exitErrorValue: Error | undefined\n\n const handleExit = (error?: Error) => {\n exitCalledFlag = true\n exitErrorValue = error\n if (debug) {\n console.log(\"[silvery] exit() called\", error ? `with error: ${error.message}` : \"\")\n }\n }\n\n // Create mock stdout with mutable dimensions and event support (for resize)\n const stdoutEmitter = new EventEmitter()\n const mockStdout = {\n columns: instance.columns,\n rows: instance.rows,\n write: () => true,\n isTTY: true,\n on: (event: string, listener: (...args: unknown[]) => void) => {\n stdoutEmitter.on(event, listener)\n return mockStdout\n },\n off: (event: string, listener: (...args: unknown[]) => void) => {\n stdoutEmitter.off(event, listener)\n return mockStdout\n },\n once: (event: string, listener: (...args: unknown[]) => void) => {\n stdoutEmitter.once(event, listener)\n return mockStdout\n },\n removeListener: (event: string, listener: (...args: unknown[]) => void) => {\n stdoutEmitter.removeListener(event, listener)\n return mockStdout\n },\n addListener: (event: string, listener: (...args: unknown[]) => void) => {\n stdoutEmitter.addListener(event, listener)\n return mockStdout\n },\n } as unknown as NodeJS.WriteStream\n\n // Create mock term with the mock stdout so useWindowSize reads correct dimensions\n const mockTerm = createTerm({ color: \"truecolor\", stdout: mockStdout })\n\n // Focus manager (tree-based focus system)\n const focusManager = createFocusManager()\n\n // Wire up focus cleanup on node removal\n setOnNodeRemoved((removedNode) => focusManager.handleSubtreeRemoved(removedNode))\n\n // Per-instance cursor state (replaces module-level globals)\n const cursorStore = createCursorStore()\n\n // RuntimeContext — typed event bus bridging from test renderer's inputEmitter\n const runtimeValue: RuntimeContextValue = {\n on(event, handler) {\n if (event === \"input\") {\n const wrapped = (data: string | Buffer) => {\n const [input, key] = parseKey(data)\n ;(handler as (input: string, key: import(\"@silvery/tea/keys\").Key) => void)(input, key)\n }\n instance.inputEmitter.on(\"input\", wrapped)\n return () => {\n instance.inputEmitter.removeListener(\"input\", wrapped)\n }\n }\n if (event === \"paste\") {\n instance.inputEmitter.on(\"paste\", handler)\n return () => {\n instance.inputEmitter.removeListener(\"paste\", handler)\n }\n }\n return () => {} // Unknown event — no-op cleanup\n },\n emit() {\n // Test renderer doesn't support view → runtime events\n },\n exit: handleExit,\n }\n\n // Wrap element with contexts\n function wrapWithContexts(el: ReactElement): ReactElement {\n const inner = wrapRoot ? wrapRoot(el) : el\n return React.createElement(\n CursorProvider,\n { store: cursorStore },\n React.createElement(\n TermContext.Provider,\n { value: mockTerm },\n React.createElement(\n StdoutContext.Provider,\n { value: { stdout: mockStdout, write: () => {} } },\n React.createElement(\n FocusManagerContext.Provider,\n { value: focusManager },\n React.createElement(RuntimeContext.Provider, { value: runtimeValue }, inner),\n ),\n ),\n ),\n )\n }\n\n // Check SILVERY_STRICT for automatic incremental checking (like scheduler does)\n // Note: \"0\" and \"false\" are treated as disabled\n const strictEnv = process.env.SILVERY_STRICT\n const strictMode = incremental && strictEnv && strictEnv !== \"0\" && strictEnv !== \"false\"\n\n // Render function that executes the pipeline.\n //\n // Two modes:\n // 1. Multi-pass (default): Layout stabilization loop (up to 5 iterations).\n // After executeRender fires notifyLayoutSubscribers (Phase 2.7), hooks\n // like useContentRect call forceUpdate(). These React updates are flushed\n // and the pipeline re-run until stable.\n //\n // 2. Single-pass (singlePassLayout=true): Matches production create-app.tsx.\n // Single executeRender call per doRender(), with a separate effect flush\n // loop afterward (like production's processEventBatch). This ensures tests\n // exercise the same rendering pipeline as production.\n //\n // Key insight: executeRender must run inside act() so that forceUpdate/setState\n // calls from layout notifications are properly captured by React's scheduler.\n // With IS_REACT_ACT_ENVIRONMENT=true (set by silvery/testing), state updates\n // outside act() boundaries may be dropped.\n // Max iterations for singlePassLayout mode. Normally 1-2 passes, but resize\n // can need 3+ (pass 0 stale zustand + pass 1 updated dims + pass 2+ layout\n // feedback stabilization). Matches classic path's cap of 5.\n const MAX_SINGLE_PASS_ITERATIONS = 5\n\n function doRender(): string {\n let output: string\n let buffer!: TerminalBuffer\n\n if (instance.singlePassLayout) {\n // Production-matching single-pass: one executeRender, no stabilization\n // loop. This matches create-app.tsx doRender() which does a single\n // reconcile + pipeline pass. Layout feedback effects (useContentRect\n // etc.) are NOT re-run within this doRender — they're flushed by the\n // caller (sendInput) in a separate loop, matching production's\n // processEventBatch flush pattern.\n //\n // IMPORTANT: Do NOT flush sync work here. executeRender fires\n // notifyLayoutSubscribers (Phase 2.7) which may call forceUpdate().\n // If we flushed that commit here, the pipeline output would still\n // reflect the pre-forceUpdate state. Instead, let the sendInput\n // flush loop detect the pending commit and call doRender() again\n // with the updated React tree.\n // Single-pass: run executeRender once, then flush any pending React\n // work from layout notifications. If React committed new work, run\n // additional passes to stabilize. Normally 1-2 passes suffice, but\n // resize can need 3 (pass 0 with stale zustand, pass 1 with updated\n // dimensions, pass 2 for layout feedback from pass 1).\n let singlePassCount = 0\n for (let pass = 0; pass < MAX_SINGLE_PASS_ITERATIONS; pass++) {\n hadReactCommit = false\n singlePassCount++\n let renderError: Error | null = null\n withActEnvironment(() => {\n act(() => {\n const root = getContainerRoot(instance.container)\n try {\n const result = executeRender(\n root,\n instance.columns,\n instance.rows,\n incremental ? instance.prevBuffer : null,\n )\n output = result.output\n buffer = result.buffer\n } catch (e) {\n // STRICT output verification may throw from the output phase.\n // The content phase buffer is still valid and attached to the\n // error by executeRenderCore — extract it so lastBuffer()\n // returns the correct frame, not a stale one.\n renderError = e as Error\n const attachedBuffer = (e as any)?.__silvery_buffer\n if (attachedBuffer) {\n buffer = attachedBuffer\n }\n }\n // Always update prevBuffer when a new buffer was produced,\n // even if the output phase threw. The buffer from contentPhase\n // is correct; the STRICT verification exception is a diagnostic that\n // should not corrupt incremental rendering state.\n if (buffer) {\n instance.prevBuffer = buffer\n }\n instance.renderCount++\n onBufferReady?.(output, buffer, getRootContentHeight())\n })\n if (!hadReactCommit) {\n act(() => {\n reconciler.flushSyncWork()\n })\n }\n })\n // Re-throw non-diagnostic errors. IncrementalRenderMismatchError from\n // STRICT output verification is diagnostic — the buffer was saved above, and\n // the content-phase STRICT check below will detect real mismatches.\n // Propagating diagnostic throws would crash sendInput() callers.\n if (renderError) {\n if (!((renderError as Error) instanceof IncrementalRenderMismatchError)) {\n throw renderError\n }\n }\n if (!hadReactCommit) break\n }\n\n if (hadReactCommit && singlePassCount >= MAX_SINGLE_PASS_ITERATIONS) {\n if (process.env.SILVERY_STRICT) {\n console.warn(\n `[SILVERY] singlePassLayout exhausted ${MAX_SINGLE_PASS_ITERATIONS} iterations ` +\n `with pending React commit — output may be stale`,\n )\n }\n }\n\n // When multiple passes ran, the final buffer's dirty rows only cover\n // the LAST pass's content phase writes. Mark all rows dirty so the\n // output phase does a full diff scan.\n if (incremental && buffer && singlePassCount > 1) {\n buffer.markAllRowsDirty()\n }\n } else {\n // Classic multi-pass layout stabilization loop\n const MAX_LAYOUT_ITERATIONS = 5\n let iterationCount = 0\n\n for (let iteration = 0; iteration < MAX_LAYOUT_ITERATIONS; iteration++) {\n hadReactCommit = false\n iterationCount++\n\n // Run the render pipeline inside act() so that forceUpdate/setState\n // from notifyLayoutSubscribers (Phase 2.7) are properly captured.\n let classicRenderError: Error | null = null\n withActEnvironment(() => {\n act(() => {\n const root = getContainerRoot(instance.container)\n try {\n const result = executeRender(\n root,\n instance.columns,\n instance.rows,\n incremental ? instance.prevBuffer : null,\n )\n output = result.output\n buffer = result.buffer\n } catch (e) {\n classicRenderError = e as Error\n const attachedBuffer = (e as any)?.__silvery_buffer\n if (attachedBuffer) {\n buffer = attachedBuffer\n }\n }\n if (buffer) {\n instance.prevBuffer = buffer\n }\n instance.renderCount++\n onBufferReady?.(output, buffer, getRootContentHeight())\n })\n // Flush any React work scheduled during executeRender (e.g. from\n // useSyncExternalStore updates triggered by Phase 2.7 callbacks).\n // Without this, external store changes from layout notification callbacks\n // (Phase 2.7) won't be committed until after doRender returns, causing\n // stale text in the buffer (e.g. breadcrumb showing old cursor position).\n if (!hadReactCommit) {\n act(() => {\n reconciler.flushSyncWork()\n })\n }\n })\n if (classicRenderError) {\n if (!((classicRenderError as Error) instanceof IncrementalRenderMismatchError)) {\n throw classicRenderError\n }\n }\n\n // If React didn't commit any new work from layout notifications,\n // the layout is stable — no more iterations needed.\n if (!hadReactCommit) break\n }\n\n if (hadReactCommit && iterationCount >= MAX_LAYOUT_ITERATIONS) {\n if (process.env.SILVERY_STRICT) {\n console.warn(\n `[SILVERY] classic layout loop exhausted ${MAX_LAYOUT_ITERATIONS} iterations ` +\n `with pending React commit — output may be stale`,\n )\n }\n }\n\n // When multiple iterations ran, the final buffer's dirty rows only cover\n // the LAST iteration's content phase writes. Rows changed in earlier\n // iterations but not the last are invisible to diffBuffers' dirty row\n // scan, causing those rows to be skipped → garbled output. Mark all rows\n // dirty so the output phase does a full diff scan.\n if (incremental && buffer && iterationCount > 1) {\n buffer.markAllRowsDirty()\n }\n }\n\n // SILVERY_STRICT: Compare incremental vs fresh on every render (like scheduler)\n // Skip first render (nothing to compare against)\n if (strictMode && instance.renderCount > 1) {\n const root = getContainerRoot(instance.container)\n const freshBuffer = doFreshRender()\n for (let y = 0; y < buffer!.height; y++) {\n for (let x = 0; x < buffer!.width; x++) {\n const a = buffer!.getCell(x, y)\n const b = freshBuffer.getCell(x, y)\n if (!cellEquals(a, b)) {\n // Re-run fresh render with write trap to capture what writes here\n let trapInfo = \"\"\n const trap = { x, y, log: [] as string[] }\n ;(globalThis as any).__silvery_write_trap = trap\n try {\n doFreshRender()\n } catch {\n // ignore\n }\n ;(globalThis as any).__silvery_write_trap = null\n if (trap.log.length > 0) {\n trapInfo = `\\nWRITE TRAP (${trap.log.length} writes to (${x},${y})):\\n${trap.log.join(\"\\n\")}\\n`\n } else {\n trapInfo = `\\nWRITE TRAP: NO WRITES to (${x},${y})\\n`\n }\n\n // Build rich debug context\n const ctx = buildMismatchContext(root, x, y, a, b, instance.renderCount)\n\n // Capture content-phase instrumentation snapshot\n const contentPhaseStats: ContentPhaseStats | undefined = (globalThis as any).__silvery_content_detail\n ? structuredClone((globalThis as any).__silvery_content_detail)\n : undefined\n\n const debugInfo = formatMismatchContext(ctx, contentPhaseStats)\n\n // Include text output for full picture\n const incText = bufferToText(buffer!)\n const freshText = bufferToText(freshBuffer)\n const msg = debugInfo + trapInfo + `--- incremental ---\\n${incText}\\n--- fresh ---\\n${freshText}`\n throw new IncrementalRenderMismatchError(msg, {\n contentPhaseStats,\n mismatchContext: ctx,\n })\n }\n }\n }\n }\n\n return output!\n }\n\n // Fresh render: renders from scratch without updating incremental state\n function doFreshRender(): TerminalBuffer {\n const root = getContainerRoot(instance.container)\n const { buffer } = executeRender(root, instance.columns, instance.rows, null, {\n skipLayoutNotifications: true,\n skipScrollStateUpdates: true,\n })\n return buffer\n }\n\n // Synchronously update React tree within act()\n instance.rendering = true\n try {\n withActEnvironment(() => {\n act(() => {\n reconciler.updateContainerSync(wrapWithContexts(element), instance.fiberRoot, null, null)\n reconciler.flushSyncWork()\n })\n })\n } finally {\n instance.rendering = false\n }\n\n // Execute the render pipeline.\n // The initial render always uses the multi-pass stabilization loop regardless\n // of singlePassLayout, because hooks like useContentRect need multiple passes\n // to stabilize (subscribe → layout → forceUpdate → re-render). This matches\n // production where the initial render runs once and the first user-visible\n // frame comes after the event loop starts. For tests, we need the initial\n // state to be stable. singlePassLayout only affects subsequent renders\n // (sendInput/press) to match production's processEventBatch path.\n const savedSinglePass = instance.singlePassLayout\n instance.singlePassLayout = false\n const output = doRender()\n instance.singlePassLayout = savedSinglePass\n\n instance.frames.push(output)\n onFrame?.(output, instance.prevBuffer!, getRootContentHeight())\n\n if (debug) {\n console.log(\"[silvery] Initial render:\", output)\n }\n\n // Set up stdin bridge: forward external stdin data to the renderer's input\n let stdinOnReadable: (() => void) | undefined\n if (stdinStream) {\n stdinOnReadable = () => {\n let chunk: string | null\n while ((chunk = (stdinStream as any).read?.()) !== null && chunk !== undefined) {\n instance.inputEmitter.emit(\"input\", chunk)\n }\n }\n stdinStream.on(\"readable\", stdinOnReadable)\n }\n\n // Helper functions for App\n const getContainer = () => getContainerRoot(instance.container)\n const getBuffer = () => instance.prevBuffer\n\n const sendInput = (data: string) => {\n if (!instance.mounted) {\n throw new Error(\"Cannot write to stdin after unmount\")\n }\n if (instance.rendering) {\n throw new Error(\n \"silvery: Re-entrant render detected. \" +\n \"Cannot call press()/stdin.write() from inside a React render or effect. \" +\n \"Use setTimeout or an event handler instead.\",\n )\n }\n const t0 = performance.now()\n instance.rendering = true\n try {\n // Check for bracketed paste before splitting into individual keys.\n // Paste content is delivered as a single \"paste\" event, not individual keystrokes.\n // This mirrors the production path in term-provider.ts.\n const pasteResult = parseBracketedPaste(data)\n if (pasteResult) {\n withActEnvironment(() => {\n act(() => {\n instance.inputEmitter.emit(\"paste\", pasteResult.content)\n })\n })\n } else {\n // Split multi-character data into individual keypresses.\n // This mirrors the production path (render.tsx handleReadable)\n // where stdin.read() can return buffered characters.\n withActEnvironment(() => {\n for (const keypress of splitRawInput(data)) {\n // Default Tab/Shift+Tab focus cycling and Escape blur.\n // Matches production behavior in run.tsx and render.tsx.\n // Tab events are consumed (not passed to useInput handlers).\n // Each focus change runs in its own act() boundary so React\n // commits the re-render before the next keypress or doRender().\n const [, key] = parseKey(keypress)\n if (key.tab && !key.shift) {\n act(() => {\n const root = getContainerRoot(instance.container)\n focusManager.focusNext(root)\n })\n continue\n }\n if (key.tab && key.shift) {\n act(() => {\n const root = getContainerRoot(instance.container)\n focusManager.focusPrev(root)\n })\n continue\n }\n if (key.escape && focusManager.activeElement) {\n act(() => {\n focusManager.blur()\n })\n continue\n }\n act(() => {\n instance.inputEmitter.emit(\"input\", keypress)\n })\n }\n })\n } // end else (non-paste input)\n } finally {\n instance.rendering = false\n }\n\n const t1 = performance.now()\n // doRender() handles SILVERY_STRICT checking internally\n let newFrame = doRender()\n\n // In single-pass mode, flush effects after doRender() — matching\n // production's processEventBatch pattern (lines 1107-1118 of create-app.tsx).\n // Production does: doRender → await Promise.resolve() → check pendingRerender → repeat.\n // In tests, we use act(flushSyncWork) as the synchronous equivalent.\n let doRenderCount = 1\n if (instance.singlePassLayout) {\n const MAX_EFFECT_FLUSHES = 5\n for (let flush = 0; flush < MAX_EFFECT_FLUSHES; flush++) {\n hadReactCommit = false\n withActEnvironment(() => {\n act(() => {\n reconciler.flushSyncWork()\n })\n })\n if (!hadReactCommit) break\n // React committed new work from effects — re-render\n newFrame = doRender()\n doRenderCount++\n }\n }\n\n // When multiple doRender() calls ran (layout feedback, effects), the final\n // buffer's dirty rows only cover the LAST call's writes. Rows changed in\n // earlier doRender calls are invisible to callers using outputPhase to diff\n // against an older prevBuffer. Mark all rows dirty for correctness.\n if (incremental && doRenderCount > 1 && instance.prevBuffer) {\n instance.prevBuffer.markAllRowsDirty()\n }\n\n const t2 = performance.now()\n instance.frames.push(newFrame)\n onFrame?.(newFrame, instance.prevBuffer!, getRootContentHeight())\n if (debug) {\n console.log(\"[silvery] stdin.write:\", newFrame)\n }\n // Expose timing on global for benchmarking\n ;(globalThis as any).__silvery_last_timing = {\n actMs: t1 - t0,\n renderMs: t2 - t1,\n }\n }\n\n const rerenderFn = (newElement: ReactNode) => {\n if (!instance.mounted) {\n throw new Error(\"Cannot rerender after unmount\")\n }\n if (instance.rendering) {\n throw new Error(\n \"silvery: Re-entrant render detected. \" + \"Cannot call rerender() from inside a React render or effect.\",\n )\n }\n instance.rendering = true\n try {\n withActEnvironment(() => {\n act(() => {\n reconciler.updateContainerSync(wrapWithContexts(newElement as ReactElement), instance.fiberRoot, null, null)\n reconciler.flushSyncWork()\n })\n })\n } finally {\n instance.rendering = false\n }\n const newFrame = doRender()\n instance.frames.push(newFrame)\n onFrame?.(newFrame, instance.prevBuffer!, getRootContentHeight())\n if (debug) {\n console.log(\"[silvery] Rerender:\", newFrame)\n }\n }\n\n // Track this render for leak detection\n const renderTracker = { unmount: () => {}, id: renderId }\n const renderRef = new WeakRef(renderTracker)\n activeRenders.add(renderRef)\n\n const unmountFn = () => {\n if (!instance.mounted) {\n throw new Error(\"Already unmounted\")\n }\n withActEnvironment(() => {\n act(() => {\n reconciler.updateContainer(null, instance.fiberRoot, null, () => {})\n })\n })\n instance.mounted = false\n instance.inputEmitter.removeAllListeners()\n\n // Clean up stdin bridge\n if (stdinStream && stdinOnReadable) {\n stdinStream.removeListener(\"readable\", stdinOnReadable)\n }\n\n // Unregister node removal hook\n setOnNodeRemoved(null)\n\n // Untrack this render\n activeRenders.delete(renderRef)\n\n if (debug) {\n console.log(\"[silvery] Unmounted\")\n }\n }\n renderTracker.unmount = unmountFn\n\n const clearFn = () => {\n instance.frames.length = 0\n instance.prevBuffer = null\n }\n\n const debugFn = () => {\n console.log(debugTree(getContainerRoot(instance.container)))\n }\n\n // actAndRender: wrap a callback in act() so React state updates are flushed,\n // then doRender() to update the buffer. Used by click/wheel/doubleClick.\n const actAndRenderFn = (fn: () => void) => {\n if (!instance.mounted) return\n withActEnvironment(() => {\n act(() => {\n fn()\n })\n })\n const newFrame = doRender()\n instance.frames.push(newFrame)\n onFrame?.(newFrame, instance.prevBuffer!, getRootContentHeight())\n }\n\n // Resize: update dimensions, clear prevBuffer, re-render (matches scheduler resize behavior)\n const resizeFn = (newCols: number, newRows: number) => {\n if (!instance.mounted) {\n throw new Error(\"Cannot resize after unmount\")\n }\n instance.columns = newCols\n instance.rows = newRows\n mockStdout.columns = newCols\n mockStdout.rows = newRows\n // Emit resize event so component-level listeners (e.g., ScrollbackView's\n // width tracking) fire before the render, matching real terminal behavior.\n stdoutEmitter.emit(\"resize\")\n // Clear prevBuffer to force full redraw (matches scheduler.setupResizeListener)\n instance.prevBuffer = null\n // Re-render at new dimensions\n const newFrame = doRender()\n instance.frames.push(newFrame)\n onFrame?.(newFrame, instance.prevBuffer!, getRootContentHeight())\n if (debug) {\n console.log(\"[silvery] Resize:\", newCols, \"x\", newRows)\n }\n }\n\n // Build unified App instance\n return buildApp({\n getContainer,\n getBuffer,\n sendInput,\n rerender: rerenderFn,\n unmount: unmountFn,\n waitUntilExit: () => Promise.resolve(),\n clear: clearFn,\n exitCalled: () => exitCalledFlag,\n exitError: () => exitErrorValue,\n freshRender: doFreshRender,\n debugFn,\n frames: instance.frames,\n columns: cols,\n rows: rows,\n kittyMode,\n actAndRender: actAndRenderFn,\n resize: resizeFn,\n focusManager,\n getCursorState: cursorStore.accessors.getCursorState,\n })\n}\n\n// ============================================================================\n// createStore() — flatten providers into { cols, rows, events() }\n// ============================================================================\n\n/**\n * Create a Store from provider options.\n *\n * A Store is the TermDef-like environment: cols, rows, and optionally events.\n *\n * @example\n * ```tsx\n * const store = createStore({ cols: 80, rows: 24 })\n * const app = render(<App />, store)\n * ```\n */\nexport function createStore(options: StoreOptions = {}): Store {\n const { cols = 80, rows = 24, events: eventsSource } = options\n\n const store: Store = {\n cols,\n rows,\n }\n\n if (eventsSource) {\n store.events = () => eventsSource\n }\n\n return store\n}\n\n// ============================================================================\n// createRenderer() — factory with auto-cleanup\n// ============================================================================\n\n/**\n * Per-render overrides for createRenderer's returned function.\n */\nexport interface PerRenderOptions {\n /** Enable incremental rendering for this render. */\n incremental?: boolean\n /** Use production-like single-pass layout. See RenderOptions.singlePassLayout. */\n singlePassLayout?: boolean\n /** Use Kitty keyboard protocol encoding for press(). */\n kittyMode?: boolean\n}\n\n/**\n * Create a render function that auto-cleans previous renders.\n *\n * By default, incremental rendering is ENABLED for test renders.\n * This matches production behavior (live scheduler uses incremental)\n * and enables withDiagnostics to catch incremental vs fresh mismatches.\n *\n * @example\n * ```tsx\n * const render = createRenderer({ cols: 80, rows: 24 })\n * const app1 = render(<Foo />) // incremental: true by default\n * const app2 = render(<Bar />) // unmounts app1\n *\n * // Explicitly disable incremental if needed\n * const render2 = createRenderer({ cols: 80, rows: 24, incremental: false })\n * ```\n */\nexport function createRenderer(\n optsOrStore: RenderOptions | Store = {},\n): (el: ReactElement, overrides?: PerRenderOptions) => App {\n let current: App | null = null\n\n // Default to incremental: true for test renders (matches production behavior)\n // User can explicitly pass incremental: false to disable\n // Note: When passed a Store-like object (cols/rows only), convert to RenderOptions with incremental\n const baseOpts = isStore(optsOrStore)\n ? { incremental: true, cols: optsOrStore.cols, rows: optsOrStore.rows }\n : { incremental: true, ...optsOrStore }\n\n return (element: ReactElement, overrides?: PerRenderOptions): App => {\n if (current) {\n try {\n current.unmount()\n } catch {\n // Already unmounted\n }\n }\n let opts = baseOpts\n if (overrides && !isStore(opts)) {\n opts = { ...opts, ...overrides }\n }\n current = render(element, opts)\n return current\n }\n}\n\n// ============================================================================\n// run() — event loop driver\n// ============================================================================\n\n/**\n * Result of run() with sync events — iterable over events.\n */\nexport interface SyncRunResult extends Iterable<string> {\n /** Current rendered text */\n readonly text: string\n /** The app being driven */\n readonly app: App\n}\n\n/**\n * Result of run() with async events — async iterable and awaitable.\n */\nexport interface AsyncRunResult extends AsyncIterable<string>, PromiseLike<void> {\n /** Current rendered text */\n readonly text: string\n /** The app being driven */\n readonly app: App\n /** Unmount and stop the event loop */\n unmount(): void\n}\n\n/**\n * Drive an App with events.\n *\n * - `run(app, ['j', 'k', 'Enter'])` — sync, explicit key events\n * - `run(app, syncIterable)` — sync iteration over events\n * - `await run(app)` — async, reads events from store (if rendered with one)\n * - `for await (const text of run(app, asyncEvents))` — async iteration\n *\n * @example\n * ```tsx\n * const app = render(<Counter />, { cols: 80, rows: 24 })\n * run(app, ['j', 'k', 'Enter'])\n * expect(app.text).toContain('Count: 2')\n * ```\n */\nexport function run(app: App, events: string[]): SyncRunResult\nexport function run(app: App, events: Iterable<string>): SyncRunResult\nexport function run(app: App, events?: AsyncIterable<string>): AsyncRunResult\nexport function run(\n app: App,\n events?: string[] | Iterable<string> | AsyncIterable<string>,\n): SyncRunResult | AsyncRunResult {\n // Sync path: array or sync iterable\n if (events !== undefined && !isAsyncIterable(events)) {\n const iter = Array.isArray(events) ? events : events\n const processedEvents: string[] = []\n\n for (const key of iter) {\n app.stdin.write(keyToAnsi(key))\n processedEvents.push(key)\n }\n\n return {\n get text() {\n return app.text\n },\n app,\n [Symbol.iterator]() {\n return processedEvents[Symbol.iterator]()\n },\n }\n }\n\n // Async path\n let stopped = false\n const unmount = () => {\n stopped = true\n app.unmount()\n }\n\n const asyncResult: AsyncRunResult = {\n get text() {\n return app.text\n },\n app,\n unmount,\n\n // PromiseLike — `await run(app)` or `await run(app, asyncEvents)`\n then<TResult1 = void, TResult2 = never>(\n onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n const promise = (async () => {\n if (events) {\n for await (const key of events) {\n if (stopped) break\n await app.press(key)\n }\n } else {\n // No events — wait until exit\n await app.run()\n }\n })()\n return promise.then(onfulfilled, onrejected)\n },\n\n // AsyncIterable — `for await (const text of run(app, asyncEvents))`\n [Symbol.asyncIterator](): AsyncIterator<string> {\n if (!events) {\n // No events source — yield current text, then done\n let yielded = false\n return {\n async next(): Promise<IteratorResult<string>> {\n if (yielded || stopped) {\n return { done: true, value: undefined as unknown as string }\n }\n yielded = true\n return { done: false, value: app.text }\n },\n }\n }\n\n const iter = (events as AsyncIterable<string>)[Symbol.asyncIterator]()\n return {\n async next(): Promise<IteratorResult<string>> {\n if (stopped) {\n return { done: true, value: undefined as unknown as string }\n }\n const result = await iter.next()\n if (result.done) {\n return { done: true, value: undefined as unknown as string }\n }\n await app.press(result.value)\n return { done: false, value: app.text }\n },\n async return(): Promise<IteratorResult<string>> {\n unmount()\n return { done: true, value: undefined as unknown as string }\n },\n }\n },\n }\n\n return asyncResult\n}\n\nfunction isAsyncIterable(value: unknown): value is AsyncIterable<unknown> {\n return value !== null && typeof value === \"object\" && Symbol.asyncIterator in value\n}\n\n/**\n * Run a function with IS_REACT_ACT_ENVIRONMENT temporarily set to true.\n * This ensures act() works correctly without polluting the global scope.\n */\nfunction withActEnvironment(fn: () => void): void {\n const prev = (globalThis as any).IS_REACT_ACT_ENVIRONMENT\n ;(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true\n try {\n fn()\n } finally {\n ;(globalThis as any).IS_REACT_ACT_ENVIRONMENT = prev\n }\n}\n\n// ============================================================================\n// Test Utilities\n// ============================================================================\n\n/**\n * Get the number of currently active (mounted) render instances.\n * Useful for tests to verify cleanup.\n */\nexport function getActiveRenderCount(): number {\n return pruneAndCountActiveRenders()\n}\n\n// ============================================================================\n// Re-exports\n// ============================================================================\n\nexport { keyToAnsi } from \"@silvery/ag/keys\"\nexport type { App } from \"./app.js\"\n",
128
+ "/**\n * AutoLocator - Self-refreshing Playwright-style locator (canonical implementation)\n *\n * This is the primary locator API. Prefer `App.locator()` / `App.getByTestId()` /\n * `App.getByText()` which use AutoLocator internally.\n *\n * Unlike the static SilveryLocator in `testing/locator.ts` (legacy, deprecated),\n * AutoLocator re-evaluates queries against the current tree on each access.\n * This eliminates the stale locator problem in tests.\n *\n * @example\n * ```tsx\n * const app = render(<Board />)\n * const cursor = app.locator('[data-cursor]')\n *\n * // Same locator, fresh result after state change\n * expect(cursor.textContent()).toBe('item1')\n * await app.press('j')\n * expect(cursor.textContent()).toBe('item2') // Auto-refreshed!\n * ```\n */\n\nimport type { AgNode, Rect } from \"@silvery/ag/types\"\n\n/**\n * Filter options for locator narrowing\n */\nexport interface FilterOptions {\n /** Match nodes containing this text */\n hasText?: string | RegExp\n /** Match nodes with this testID */\n hasTestId?: string\n /** Match nodes with this attribute value */\n has?: { attr: string; value?: string }\n}\n\n/**\n * AutoLocator interface - lazy, self-refreshing reference to nodes\n */\nexport interface AutoLocator {\n // Core queries (return new AutoLocators)\n getByText(text: string | RegExp): AutoLocator\n getByTestId(id: string): AutoLocator\n locator(selector: string): AutoLocator\n\n // Filtering\n filter(options: FilterOptions): AutoLocator\n filter(predicate: (node: AgNode) => boolean): AutoLocator\n\n // Narrowing\n first(): AutoLocator\n last(): AutoLocator\n nth(index: number): AutoLocator\n\n // Resolution (actually finds nodes - re-evaluates on each call)\n resolve(): AgNode | null\n resolveAll(): AgNode[]\n count(): number\n\n // Utilities (resolve then read)\n textContent(): string\n getAttribute(name: string): string | undefined\n boundingBox(): Rect | null\n isVisible(): boolean\n}\n\n// Query predicate type\ntype NodePredicate = (node: AgNode) => boolean\n\n/**\n * Create an AutoLocator from a container getter function.\n * The getter is called fresh on each resolution.\n */\nexport function createAutoLocator(getContainer: () => AgNode): AutoLocator {\n return new AutoLocatorImpl(getContainer, [])\n}\n\n/**\n * AutoLocator implementation\n */\nclass AutoLocatorImpl implements AutoLocator {\n constructor(\n private getContainer: () => AgNode,\n private predicates: NodePredicate[],\n private indexSelector?: { type: \"first\" | \"last\" | \"nth\"; index?: number },\n ) {}\n\n getByText(text: string | RegExp): AutoLocator {\n const predicate: NodePredicate = (node) => {\n const content = getNodeTextContent(node)\n if (!content) return false\n\n // Only match silvery-text nodes (not containers)\n if (node.type !== \"silvery-text\") {\n return false\n }\n\n // Skip raw text nodes if their parent also matches\n if (node.isRawText && node.parent?.type === \"silvery-text\") {\n return false\n }\n\n if (typeof text === \"string\") {\n return content.includes(text)\n }\n return text.test(content)\n }\n return new AutoLocatorImpl(this.getContainer, [...this.predicates, predicate])\n }\n\n getByTestId(id: string): AutoLocator {\n const predicate: NodePredicate = (node) => {\n return getNodeProp(node, \"testID\") === id\n }\n return new AutoLocatorImpl(this.getContainer, [...this.predicates, predicate])\n }\n\n locator(selector: string): AutoLocator {\n const predicate = parseSelector(selector)\n if (!predicate) {\n // Invalid selector - return locator that matches nothing\n return new AutoLocatorImpl(this.getContainer, [() => false])\n }\n return new AutoLocatorImpl(this.getContainer, [...this.predicates, predicate])\n }\n\n filter(optionsOrPredicate: FilterOptions | ((node: AgNode) => boolean)): AutoLocator {\n let predicate: NodePredicate\n\n if (typeof optionsOrPredicate === \"function\") {\n predicate = optionsOrPredicate\n } else {\n const opts = optionsOrPredicate\n predicate = (node: AgNode) => {\n if (opts.hasText !== undefined) {\n const content = getNodeTextContent(node)\n if (typeof opts.hasText === \"string\") {\n if (!content.includes(opts.hasText)) return false\n } else {\n if (!opts.hasText.test(content)) return false\n }\n }\n if (opts.hasTestId !== undefined) {\n if (getNodeProp(node, \"testID\") !== opts.hasTestId) return false\n }\n if (opts.has !== undefined) {\n const value = getNodeProp(node, opts.has.attr)\n if (opts.has.value !== undefined) {\n if (value !== opts.has.value) return false\n } else {\n if (value === undefined) return false\n }\n }\n return true\n }\n }\n\n return new AutoLocatorImpl(this.getContainer, [...this.predicates, predicate])\n }\n\n first(): AutoLocator {\n return new AutoLocatorImpl(this.getContainer, this.predicates, {\n type: \"first\",\n })\n }\n\n last(): AutoLocator {\n return new AutoLocatorImpl(this.getContainer, this.predicates, {\n type: \"last\",\n })\n }\n\n nth(index: number): AutoLocator {\n return new AutoLocatorImpl(this.getContainer, this.predicates, {\n type: \"nth\",\n index,\n })\n }\n\n resolve(): AgNode | null {\n const nodes = this.resolveAll()\n if (this.indexSelector) {\n switch (this.indexSelector.type) {\n case \"first\":\n return nodes[0] ?? null\n case \"last\":\n return nodes[nodes.length - 1] ?? null\n case \"nth\":\n return nodes[this.indexSelector.index ?? 0] ?? null\n }\n }\n return nodes[0] ?? null\n }\n\n resolveAll(): AgNode[] {\n // Get fresh container on each resolution\n const container = this.getContainer()\n\n if (this.predicates.length === 0) {\n return [container]\n }\n\n const matches: AgNode[] = []\n walkTree(container, (node) => {\n if (this.predicates.every((p) => p(node))) {\n matches.push(node)\n }\n })\n return matches\n }\n\n count(): number {\n return this.resolveAll().length\n }\n\n textContent(): string {\n const node = this.resolve()\n if (!node) return \"\"\n return getNodeTextContent(node)\n }\n\n getAttribute(name: string): string | undefined {\n const node = this.resolve()\n if (!node) return undefined\n return getNodeProp(node, name)\n }\n\n boundingBox(): Rect | null {\n const node = this.resolve()\n if (!node) return null\n return node.screenRect ?? null\n }\n\n isVisible(): boolean {\n const box = this.boundingBox()\n if (!box) return false\n return box.width > 0 && box.height > 0\n }\n}\n\n// ============================================================================\n// Tree Walking Helpers\n// ============================================================================\n\n/**\n * Walk tree depth-first, calling visitor for each node\n */\nfunction walkTree(node: AgNode, visitor: (node: AgNode) => void): void {\n visitor(node)\n for (const child of node.children) {\n walkTree(child, visitor)\n }\n}\n\n/**\n * Get text content of a node (concatenated from all text descendants)\n */\nfunction getNodeTextContent(node: AgNode): string {\n if (node.textContent !== undefined) {\n return node.textContent\n }\n return node.children.map(getNodeTextContent).join(\"\")\n}\n\n/**\n * Get a prop value from node\n */\nfunction getNodeProp(node: AgNode, name: string): string | undefined {\n const props = node.props as Record<string, unknown>\n const value = props[name]\n if (value === undefined || value === null) return undefined\n return String(value)\n}\n\n// ============================================================================\n// Selector Parsing (from locator.ts)\n// ============================================================================\n\n/**\n * Parse CSS-like selector into predicate\n */\nfunction parseSelector(selector: string): NodePredicate | null {\n const trimmed = selector.trim()\n\n // Check for combinators\n if (trimmed.includes(\">\")) {\n return parseChildCombinator(trimmed)\n }\n if (trimmed.includes(\"+\")) {\n return parseAdjacentSiblingCombinator(trimmed)\n }\n if (trimmed.includes(\" \") && !trimmed.startsWith(\"[\")) {\n return parseDescendantCombinator(trimmed)\n }\n\n return parseSingleSelector(trimmed)\n}\n\n/**\n * Parse a single selector (no combinators)\n */\nfunction parseSingleSelector(selector: string): NodePredicate | null {\n const parts: NodePredicate[] = []\n let remaining = selector\n\n // Universal selector - matches all nodes\n if (remaining === \"*\") {\n return () => true\n }\n\n // Extract ID if present\n const idMatch = remaining.match(/^#([a-zA-Z0-9_-]+)/)\n if (idMatch) {\n const id = idMatch[1]!\n parts.push((node: AgNode) => getNodeProp(node, \"id\") === id)\n remaining = remaining.slice(idMatch[0].length)\n }\n\n // Extract all attribute selectors\n const attrRegex = /\\[([a-zA-Z_][a-zA-Z0-9_-]*)(?:([~^$*]?)=[\"']([^\"']*)[\"'])?\\]/g\n for (const match of remaining.matchAll(attrRegex)) {\n const [, attr, op, value] = match\n if (!attr) continue\n\n if (value === undefined) {\n parts.push((node: AgNode) => getNodeProp(node, attr) !== undefined)\n } else {\n parts.push((node: AgNode) => {\n const nodeValue = getNodeProp(node, attr)\n if (nodeValue === undefined) return false\n switch (op) {\n case \"\":\n return nodeValue === value\n case \"^\":\n return nodeValue.startsWith(value ?? \"\")\n case \"$\":\n return nodeValue.endsWith(value ?? \"\")\n case \"*\":\n return nodeValue.includes(value ?? \"\")\n default:\n return false\n }\n })\n }\n }\n\n if (parts.length === 0) return null\n\n return (node: AgNode) => parts.every((pred) => pred(node))\n}\n\n/**\n * Parse child combinator: A > B\n */\nfunction parseChildCombinator(selector: string): NodePredicate | null {\n const parts = selector.split(\">\").map((s) => s.trim())\n if (parts.length !== 2) return null\n\n const [parentSel, childSel] = parts\n const parentPred = parseSingleSelector(parentSel!)\n const childPred = parseSingleSelector(childSel!)\n\n if (!parentPred || !childPred) return null\n\n return (node: AgNode) => {\n if (!childPred(node)) return false\n return node.parent !== null && parentPred(node.parent)\n }\n}\n\n/**\n * Parse adjacent sibling combinator: A + B\n */\nfunction parseAdjacentSiblingCombinator(selector: string): NodePredicate | null {\n const parts = selector.split(\"+\").map((s) => s.trim())\n if (parts.length !== 2) return null\n\n const [prevSel, nextSel] = parts\n const prevPred = parseSingleSelector(prevSel!)\n const nextPred = parseSingleSelector(nextSel!)\n\n if (!prevPred || !nextPred) return null\n\n return (node: AgNode) => {\n if (!nextPred(node)) return false\n if (!node.parent) return false\n\n const siblings = node.parent.children\n const index = siblings.indexOf(node)\n if (index <= 0) return false\n\n const prevSibling = siblings[index - 1]\n return prevSibling !== undefined && prevPred(prevSibling)\n }\n}\n\n/**\n * Parse descendant combinator: A B\n */\nfunction parseDescendantCombinator(selector: string): NodePredicate | null {\n const parts = selector.split(/\\s+/).filter((s) => s.length > 0)\n if (parts.length !== 2) return null\n\n const [ancestorSel, descendantSel] = parts\n const ancestorPred = parseSingleSelector(ancestorSel!)\n const descendantPred = parseSingleSelector(descendantSel!)\n\n if (!ancestorPred || !descendantPred) return null\n\n return (node: AgNode) => {\n if (!descendantPred(node)) return false\n\n let current = node.parent\n while (current) {\n if (ancestorPred(current)) return true\n current = current.parent\n }\n return false\n }\n}\n",
129
+ "/**\n * BoundTerm - Terminal buffer with node awareness\n *\n * Bridges the terminal buffer (screen space) with the SilveryNode tree.\n * Provides screen-coordinate queries that return nodes.\n *\n * @example\n * ```tsx\n * const app = render(<Board />)\n *\n * // Screen-space access\n * const cell = app.term.cell(10, 5)\n * const node = app.term.nodeAt(10, 5)\n * console.log(app.term.text)\n * ```\n */\n\nimport type { Cell, TerminalBuffer } from \"./buffer\"\nimport type { AgNode } from \"@silvery/ag/types\"\n\n/**\n * BoundTerm interface - terminal with node awareness\n */\nexport interface BoundTerm {\n /** Get cell at screen coordinates */\n cell(x: number, y: number): Cell\n\n /** Get node at screen coordinates */\n nodeAt(x: number, y: number): AgNode | null\n\n /** Get visible text (plain, no ANSI) */\n readonly text: string\n\n /** Terminal dimensions */\n readonly columns: number\n readonly rows: number\n\n /** Access underlying buffer */\n readonly buffer: TerminalBuffer\n}\n\n/**\n * Create a BoundTerm from a buffer and root node getter\n */\nexport function createBoundTerm(buffer: TerminalBuffer, getRoot: () => AgNode, getText: () => string): BoundTerm {\n return {\n cell(x: number, y: number): Cell {\n return buffer.getCell(x, y)\n },\n\n nodeAt(x: number, y: number): AgNode | null {\n const root = getRoot()\n return findNodeAtScreenPosition(root, x, y)\n },\n\n get text(): string {\n return getText()\n },\n\n get columns(): number {\n return buffer.width\n },\n\n get rows(): number {\n return buffer.height\n },\n\n get buffer(): TerminalBuffer {\n return buffer\n },\n }\n}\n\n/**\n * Find the deepest node at the given screen coordinates\n */\nfunction findNodeAtScreenPosition(node: AgNode, x: number, y: number): AgNode | null {\n const rect = node.screenRect\n if (!rect) return null\n\n // Check if point is within this node's bounds\n if (x < rect.x || x >= rect.x + rect.width || y < rect.y || y >= rect.y + rect.height) {\n return null\n }\n\n // Check children (deepest match wins)\n for (const child of node.children) {\n const found = findNodeAtScreenPosition(child, x, y)\n if (found) return found\n }\n\n // Check virtual text children with inlineRects (nested Text inside Text).\n // These don't have screenRect/layoutNode, so standard DFS misses them.\n if (node.type === \"silvery-text\") {\n for (let i = node.children.length - 1; i >= 0; i--) {\n const child = node.children[i]!\n if (child.inlineRects) {\n for (const inlineRect of child.inlineRects) {\n if (\n x >= inlineRect.x &&\n x < inlineRect.x + inlineRect.width &&\n y >= inlineRect.y &&\n y < inlineRect.y + inlineRect.height\n ) {\n return child\n }\n }\n }\n }\n }\n\n // No child matched, this node is the deepest match\n return node\n}\n",
130
+ "/**\n * App - Unified render API for silvery\n *\n * Both production and testing return an App instance with the same interface.\n * Key improvements over the old API:\n * - Auto-refreshing locators (no stale locator problem)\n * - Playwright-style API (app.press(), app.getByTestId())\n * - Bound terminal (app.term) with node awareness\n *\n * @example\n * ```tsx\n * // Both production and testing\n * const app = await render(<App />, term)\n *\n * // Query and interact\n * app.text // rendered text (no ANSI)\n * app.getByTestId('modal') // auto-refreshing locator\n * await app.press('ArrowUp') // send key\n * await app.waitUntilExit() // wait until exit\n *\n * // Terminal access\n * app.term.cell(x, y) // { char, fg, bg, attrs }\n * app.term.nodeAt(x, y) // node at screen coords\n * ```\n */\n\nimport type { ReactNode } from \"react\"\nimport { type AutoLocator, createAutoLocator } from \"@silvery/test/auto-locator\"\nimport { type BoundTerm, createBoundTerm } from \"./bound-term\"\nimport type { TerminalBuffer } from \"./buffer\"\nimport { bufferToHTML, bufferToStyledText, bufferToText } from \"./buffer\"\nimport { type Screenshotter, createScreenshotter } from \"./screenshot\"\nimport { keyToAnsi, keyToKittyAnsi, parseHotkey } from \"@silvery/ag/keys\"\nimport { updateKeyboardModifiers } from \"./mouse-events\"\nimport type { ParsedMouse } from \"./mouse\"\nimport { createMouseEventProcessor, processMouseEvent } from \"./mouse-events\"\nimport type { FocusManager } from \"@silvery/ag/focus-manager\"\nimport { pointInRect } from \"@silvery/ag/tree-utils\"\nimport type { AgNode } from \"@silvery/ag/types\"\n\n/**\n * App interface - unified return type from render()\n */\nexport interface App {\n // === Content/Document Perspective ===\n\n /** Full rendered text (no ANSI codes) */\n readonly text: string\n\n /** Full rendered text with ANSI styling */\n readonly ansi: string\n\n /** Get node at content coordinates */\n nodeAt(x: number, y: number): AgNode | null\n\n /** Get locator by testID attribute */\n getByTestId(id: string): AutoLocator\n\n /** Get locator by text content */\n getByText(text: string | RegExp): AutoLocator\n\n /** Get locator by CSS-style selector */\n locator(selector: string): AutoLocator\n\n // === Actions (return this for chaining) ===\n\n /** Send a key press (uses keyToAnsi internally) */\n press(key: string): Promise<this>\n\n /** Send multiple key presses */\n pressSequence(...keys: string[]): Promise<this>\n\n /** Type text input */\n type(text: string): Promise<this>\n\n /** Simulate a mouse click at (x, y) terminal coordinates */\n click(\n x: number,\n y: number,\n options?: { button?: number; shift?: boolean; meta?: boolean; ctrl?: boolean; cmd?: boolean },\n ): Promise<this>\n\n /** Simulate a double-click at (x, y) terminal coordinates */\n doubleClick(\n x: number,\n y: number,\n options?: { button?: number; shift?: boolean; meta?: boolean; ctrl?: boolean; cmd?: boolean },\n ): Promise<this>\n\n /** Simulate a mouse move/hover at (x, y) terminal coordinates */\n hover(x: number, y: number): Promise<this>\n\n /** Simulate a mouse wheel event at (x, y) with delta (-1=up, +1=down) */\n wheel(x: number, y: number, delta: number): Promise<this>\n\n /** Resize the virtual terminal and re-render. Only available in test renderer. */\n resize(cols: number, rows: number): void\n\n /** Wait until app exits */\n run(): Promise<void>\n\n // === Terminal Binding ===\n\n /** Bound terminal for screen-space access */\n readonly term: BoundTerm\n\n // === Lifecycle (Instance compatibility) ===\n\n /** Re-render with a new element */\n rerender(element: ReactNode): void\n\n /** Unmount the component and clean up */\n unmount(): void\n\n /** Dispose (alias for unmount) — enables `using` */\n [Symbol.dispose](): void\n\n /** Promise that resolves when the app exits (alias for run()) */\n waitUntilExit(): Promise<void>\n\n /** Clear the terminal output */\n clear(): void\n\n // === Screenshot ===\n\n /** Render current buffer to PNG. Requires Playwright (lazy-loaded on first call). */\n screenshot(outputPath?: string): Promise<Buffer>\n\n // === Debug ===\n\n /** Print component tree to console */\n debug(): void\n\n // === Testing extras ===\n\n /** Render the current tree from scratch (no incremental buffer reuse).\n * Returns the fresh buffer without updating incremental state.\n * Only available in test renderer - throws otherwise. */\n freshRender(): TerminalBuffer\n\n /** Check if exit() was called */\n exitCalled(): boolean\n\n /** Get error passed to exit() */\n exitError(): Error | undefined\n\n /** Send raw stdin input (for sync test helpers; prefer app.press() for new code) */\n readonly stdin: { write: (data: string) => void }\n\n // === Internal/Legacy (kept for silvery test compatibility, not for external use) ===\n\n /** All rendered frames (internal) */\n readonly frames: string[]\n\n /** Get last frame with ANSI codes (internal - use app.ansi instead) */\n lastFrame(): string | undefined\n\n /** Get last buffer (internal - use app.term.buffer instead) */\n lastBuffer(): TerminalBuffer | undefined\n\n /** Get last frame as plain text (internal - use app.text instead) */\n lastFrameText(): string | undefined\n\n /** Get container root node (internal - use app.locator() instead) */\n getContainer(): AgNode\n\n // === Focus System ===\n\n /** Focus a node by testID */\n focus(testID: string): void\n\n /** Get the focus path from focused node to root (testID[]) */\n getFocusPath(): string[]\n\n /** Direct access to the FocusManager instance */\n readonly focusManager: FocusManager\n\n // === Cursor State ===\n\n /** Get the current cursor state for this silvery instance (per-instance, not global). */\n getCursorState(): import(\"@silvery/ag-react/hooks/useCursor\").CursorState | null\n}\n\n/**\n * Options for creating an App instance\n */\nexport interface AppOptions {\n /** Function to get current container root */\n getContainer: () => AgNode\n\n /** Function to get current buffer */\n getBuffer: () => TerminalBuffer | null\n\n /** Function to send input */\n sendInput: (data: string) => void\n\n /** Function to rerender */\n rerender: (element: ReactNode) => void\n\n /** Function to unmount */\n unmount: () => void\n\n /** Function to wait for exit */\n waitUntilExit: () => Promise<void>\n\n /** Function to clear output */\n clear: () => void\n\n /** Function to check if exit was called */\n exitCalled?: () => boolean\n\n /** Function to get exit error */\n exitError?: () => Error | undefined\n\n /** Fresh render function (test renderer only) */\n freshRender?: () => TerminalBuffer\n\n /** Debug print function */\n debugFn?: () => void\n\n /** Captured frames array (internal) */\n frames?: string[]\n\n /** Terminal dimensions */\n columns: number\n rows: number\n\n /** Use Kitty keyboard protocol encoding for press(). When true, press() uses keyToKittyAnsi. */\n kittyMode?: boolean\n\n /** Wrap a callback in act() + doRender() for the test renderer. Ensures React state updates from mouse handlers are flushed. */\n actAndRender?: (fn: () => void) => void\n\n /** Resize the virtual terminal (test renderer only). */\n resize?: (cols: number, rows: number) => void\n\n /** Focus manager instance for focus system */\n focusManager?: FocusManager\n\n /** Per-instance cursor state accessor */\n getCursorState?: () => import(\"@silvery/ag-react/hooks/useCursor\").CursorState | null\n}\n\n/**\n * Create an App instance\n */\nexport function buildApp(options: AppOptions): App {\n const {\n getContainer,\n getBuffer,\n sendInput,\n rerender,\n unmount,\n waitUntilExit,\n clear,\n exitCalled = () => false,\n exitError = () => undefined,\n freshRender: freshRenderFn,\n debugFn,\n frames = [],\n columns,\n rows,\n kittyMode = false,\n actAndRender,\n resize: resizeFn,\n focusManager: fm,\n } = options\n\n // Create auto-refreshing locator factory\n const createLocator = () => createAutoLocator(getContainer)\n\n // Create bound terminal\n const getText = () => {\n const buffer = getBuffer()\n return buffer ? bufferToText(buffer) : \"\"\n }\n\n // Note: BoundTerm is created lazily since buffer may not exist initially\n let boundTerm: BoundTerm | null = null\n\n // Mouse event processor for click/doubleClick/wheel\n const mouseState = createMouseEventProcessor()\n\n // Screenshotter is created lazily on first screenshot() call\n let screenshotter: Screenshotter | null = null\n\n const app: App = {\n // === Content/Document Perspective ===\n\n get text(): string {\n return getText()\n },\n\n get ansi(): string {\n const buffer = getBuffer()\n return buffer ? bufferToStyledText(buffer) : \"\"\n },\n\n nodeAt(x: number, y: number): AgNode | null {\n const root = getContainer()\n return findNodeAtContentPosition(root, x, y)\n },\n\n getByTestId(id: string): AutoLocator {\n return createLocator().getByTestId(id)\n },\n\n getByText(text: string | RegExp): AutoLocator {\n return createLocator().getByText(text)\n },\n\n locator(selector: string): AutoLocator {\n return createLocator().locator(selector)\n },\n\n // === Actions ===\n\n async press(key: string): Promise<App> {\n // Update keyboard modifier state so subsequent mouse events have accurate metaKey etc.\n const hotkey = parseHotkey(key)\n updateKeyboardModifiers(mouseState, {\n super: hotkey.super,\n hyper: hotkey.hyper,\n eventType: \"press\",\n })\n const sequence = kittyMode ? keyToKittyAnsi(key) : keyToAnsi(key)\n sendInput(sequence)\n // Allow microtask to flush for test synchronization\n await Promise.resolve()\n return app\n },\n\n async pressSequence(...keys: string[]): Promise<App> {\n for (const key of keys) {\n await app.press(key)\n }\n return app\n },\n\n async type(text: string): Promise<App> {\n for (const char of text) {\n sendInput(char)\n }\n await Promise.resolve()\n return app\n },\n\n async click(\n x: number,\n y: number,\n options?: { button?: number; shift?: boolean; meta?: boolean; ctrl?: boolean; cmd?: boolean },\n ): Promise<App> {\n const button = options?.button ?? 0\n // cmd is an alias for setting keyboard-tracked Super (Cmd on macOS)\n if (options?.cmd) mouseState.keyboardModifiers.super = true\n const doClick = () => {\n const parsed: ParsedMouse = {\n button,\n x,\n y,\n action: \"down\",\n shift: options?.shift ?? false,\n meta: options?.meta ?? false,\n ctrl: options?.ctrl ?? false,\n }\n processMouseEvent(mouseState, parsed, getContainer())\n const upParsed: ParsedMouse = { ...parsed, action: \"up\" }\n processMouseEvent(mouseState, upParsed, getContainer())\n }\n if (actAndRender) {\n actAndRender(doClick)\n } else {\n doClick()\n }\n // Reset keyboard modifier override after click\n if (options?.cmd) mouseState.keyboardModifiers.super = false\n await Promise.resolve()\n return app\n },\n\n async doubleClick(\n x: number,\n y: number,\n options?: { button?: number; shift?: boolean; meta?: boolean; ctrl?: boolean; cmd?: boolean },\n ): Promise<App> {\n const button = options?.button ?? 0\n if (options?.cmd) mouseState.keyboardModifiers.super = true\n const doDblClick = () => {\n const baseParsed: ParsedMouse = {\n button,\n x,\n y,\n action: \"down\",\n shift: options?.shift ?? false,\n meta: options?.meta ?? false,\n ctrl: options?.ctrl ?? false,\n }\n // First click\n processMouseEvent(mouseState, baseParsed, getContainer())\n processMouseEvent(mouseState, { ...baseParsed, action: \"up\" }, getContainer())\n // Second click (triggers double-click detection)\n processMouseEvent(mouseState, baseParsed, getContainer())\n processMouseEvent(mouseState, { ...baseParsed, action: \"up\" }, getContainer())\n }\n if (actAndRender) {\n actAndRender(doDblClick)\n } else {\n doDblClick()\n }\n if (options?.cmd) mouseState.keyboardModifiers.super = false\n await Promise.resolve()\n return app\n },\n\n async hover(x: number, y: number): Promise<App> {\n const doHover = () => {\n const parsed: ParsedMouse = {\n button: 0,\n x,\n y,\n action: \"move\",\n shift: false,\n meta: false,\n ctrl: false,\n }\n processMouseEvent(mouseState, parsed, getContainer())\n }\n if (actAndRender) {\n actAndRender(doHover)\n } else {\n doHover()\n }\n await Promise.resolve()\n return app\n },\n\n async wheel(x: number, y: number, delta: number): Promise<App> {\n const doWheel = () => {\n const parsed: ParsedMouse = {\n button: 0,\n x,\n y,\n action: \"wheel\",\n delta,\n shift: false,\n meta: false,\n ctrl: false,\n }\n processMouseEvent(mouseState, parsed, getContainer())\n }\n if (actAndRender) {\n actAndRender(doWheel)\n } else {\n doWheel()\n }\n await Promise.resolve()\n return app\n },\n\n resize(cols: number, rows: number): void {\n if (!resizeFn) {\n throw new Error(\"resize() is only available in test renderer\")\n }\n resizeFn(cols, rows)\n },\n\n async run(): Promise<void> {\n return waitUntilExit()\n },\n\n // === Terminal Binding ===\n\n get term(): BoundTerm {\n const buffer = getBuffer()\n if (!buffer) {\n // Return a dummy bound term if no buffer yet\n const dummyBuffer = {\n width: columns,\n height: rows,\n getCell: () => ({\n char: \" \",\n fg: null,\n bg: null,\n attrs: {},\n wide: false,\n continuation: false,\n }),\n setCell: () => {},\n clear: () => {},\n inBounds: () => false,\n } as unknown as TerminalBuffer\n return createBoundTerm(dummyBuffer, getContainer, getText)\n }\n if (!boundTerm || boundTerm.buffer !== buffer) {\n boundTerm = createBoundTerm(buffer, getContainer, getText)\n }\n return boundTerm\n },\n\n // === Screenshot ===\n\n async screenshot(outputPath?: string): Promise<Buffer> {\n const buffer = getBuffer()\n if (!buffer) {\n throw new Error(\"No buffer available for screenshot\")\n }\n const html = bufferToHTML(buffer)\n if (!screenshotter) {\n screenshotter = createScreenshotter()\n }\n return screenshotter.capture(html, outputPath)\n },\n\n // === Lifecycle ===\n\n rerender,\n unmount() {\n // Close screenshotter if it was created\n if (screenshotter) {\n screenshotter.close().catch(() => {})\n screenshotter = null\n }\n unmount()\n },\n [Symbol.dispose]() {\n app.unmount()\n },\n waitUntilExit,\n clear,\n\n // === Debug ===\n\n debug(): void {\n if (debugFn) {\n debugFn()\n } else {\n console.log(app.text)\n }\n },\n\n // === Testing extras ===\n\n freshRender(): TerminalBuffer {\n if (!freshRenderFn) {\n throw new Error(\"freshRender() is only available in test renderer\")\n }\n return freshRenderFn()\n },\n\n exitCalled,\n exitError,\n\n stdin: {\n write: sendInput,\n },\n\n // Internal/Legacy (kept for silvery test compatibility)\n frames,\n\n lastFrame(): string | undefined {\n return frames[frames.length - 1]\n },\n\n lastBuffer(): TerminalBuffer | undefined {\n return getBuffer() ?? undefined\n },\n\n lastFrameText(): string | undefined {\n const buffer = getBuffer()\n return buffer ? bufferToText(buffer) : undefined\n },\n\n getContainer(): AgNode {\n return getContainer()\n },\n\n // === Focus System ===\n\n focus(testID: string): void {\n if (fm) {\n const root = getContainer()\n fm.focusById(testID, root, \"programmatic\")\n }\n },\n\n getFocusPath(): string[] {\n if (fm) {\n const root = getContainer()\n return fm.getFocusPath(root)\n }\n return []\n },\n\n get focusManager(): FocusManager {\n if (!fm) {\n throw new Error(\"FocusManager not available — pass focusManager to buildApp()\")\n }\n return fm\n },\n\n getCursorState() {\n return options.getCursorState?.() ?? null\n },\n }\n\n return app\n}\n\n/**\n * Find node at content coordinates (not screen coordinates)\n */\nfunction findNodeAtContentPosition(node: AgNode, x: number, y: number): AgNode | null {\n const rect = node.contentRect\n if (!rect) return null\n\n if (!pointInRect(x, y, rect)) {\n return null\n }\n\n for (const child of node.children) {\n const found = findNodeAtContentPosition(child, x, y)\n if (found) return found\n }\n\n return node\n}\n",
131
+ "import { writeFile } from \"node:fs/promises\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface Screenshotter {\n /** Render HTML to PNG. First call starts Playwright (~3-5s), subsequent calls ~200ms */\n capture(html: string, outputPath?: string): Promise<Buffer>\n /** Close browser */\n close(): Promise<void>\n [Symbol.asyncDispose](): Promise<void>\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\nexport function createScreenshotter(): Screenshotter {\n let browser: import(\"playwright\").Browser | null = null\n let page: import(\"playwright\").Page | null = null\n\n async function ensureBrowser() {\n if (browser && page) return page\n\n const { chromium } = await import(\"playwright\")\n browser = await chromium.launch()\n const context = await browser.newContext()\n page = await context.newPage()\n return page\n }\n\n async function capture(html: string, outputPath?: string): Promise<Buffer> {\n const p = await ensureBrowser()\n await p.setContent(html, { waitUntil: \"load\" })\n await p.waitForTimeout(50)\n const buffer = (await p.screenshot({ fullPage: true })) as Buffer\n if (outputPath) {\n await writeFile(outputPath, buffer)\n }\n return buffer\n }\n\n async function close() {\n if (browser) {\n await browser.close()\n browser = null\n page = null\n }\n }\n\n return {\n capture,\n close,\n [Symbol.asyncDispose]: close,\n }\n}\n",
132
+ "/**\n * Debug Tree Inspection\n *\n * Pretty-prints SilveryNode trees for debugging TUI tests.\n * Similar to React DevTools component tree or browser DOM inspection.\n *\n * @example\n * ```tsx\n * import { debugTree } from '@silvery/test'\n *\n * const { getContainer } = render(<MyComponent />)\n * console.log(debugTree(getContainer()))\n * // Output:\n * // <silvery-root [0,0 80×24]>\n * // <silvery-box testID=\"main\" [0,0 80×24]>\n * // <silvery-text \"Hello World\" [0,0 11×1]>\n * ```\n */\n\nimport type { AgNode } from \"@silvery/ag/types\"\n\nexport interface DebugTreeOptions {\n /** Maximum depth to traverse (default: unlimited) */\n depth?: number\n /** Include layout rectangles (default: true) */\n showRects?: boolean\n /** Include text content (default: true) */\n showText?: boolean\n}\n\n/**\n * Pretty-print SilveryNode tree for debugging.\n *\n * @param node - Root node to inspect\n * @param options - Display options\n * @returns Formatted tree string\n */\nexport function debugTree(node: AgNode, options: DebugTreeOptions = {}): string {\n const { depth = Number.POSITIVE_INFINITY, showRects = true, showText = true } = options\n const lines: string[] = []\n\n // Safe JSON.stringify that handles cyclic references\n function safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value)\n } catch {\n // Handle cyclic structures or other stringify errors\n if (typeof value === \"object\" && value !== null) {\n return \"[object]\"\n }\n return String(value)\n }\n }\n\n function walk(n: AgNode, indent: number, currentDepth: number): void {\n if (currentDepth > depth) return\n\n // Build props string (exclude children and internal props)\n const props = Object.entries(n.props ?? {})\n .filter(([k]) => ![\"children\"].includes(k))\n .filter(([, v]) => v !== undefined && v !== null && v !== false)\n .map(([k, v]) => {\n if (typeof v === \"string\") return `${k}=\"${v}\"`\n if (typeof v === \"boolean\") return k\n return `${k}=${safeStringify(v)}`\n })\n .join(\" \")\n\n // Build rect string\n let rect = \"\"\n if (showRects && n.screenRect) {\n const { x, y, width, height } = n.screenRect\n rect = ` [${x},${y} ${width}×${height}]`\n }\n\n // Build text content string\n let text = \"\"\n if (showText && n.textContent) {\n // Truncate long text\n const content = n.textContent.length > 40 ? n.textContent.slice(0, 37) + \"...\" : n.textContent\n text = ` \"${content}\"`\n }\n\n // Format line\n const propsStr = props ? \" \" + props : \"\"\n lines.push(\" \".repeat(indent) + `<${n.type}${propsStr}${text}${rect}>`)\n\n // Recurse into children\n for (const child of n.children) {\n walk(child, indent + 1, currentDepth + 1)\n }\n }\n\n walk(node, 0, 0)\n return lines.join(\"\\n\")\n}\n",
133
+ "/**\n * Silvery measureElement\n *\n * Backward-compatible API for measuring element dimensions.\n * This is provided for Ink compatibility - prefer using the useContentRect() 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 useContentRect() hook is preferred as it automatically re-renders\n * when dimensions change:\n *\n * ```tsx\n * function MyComponent() {\n * const { width } = useContentRect();\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 useContentRect() 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 contentRect (set by silvery pipeline after layout phase)\n // This is the canonical source of truth after a render\n if (node.contentRect) {\n return {\n width: node.contentRect.width,\n height: node.contentRect.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",
134
+ "/**\n * silvery/ink — Drop-in Ink replacement.\n *\n * ```tsx\n * // Before:\n * import { Box, Text, render, useInput, useApp } from 'ink'\n *\n * // After:\n * import { Box, Text, render, useInput, useApp } from 'silvery/ink'\n * ```\n *\n * For silvery-native features beyond Ink's API:\n * - `@silvery/ag-react` — base components, reconciler, hooks\n * - `@silvery/ag-react/ui` — TextInput, TextArea, Table, Picker, Modal, etc.\n * - `@silvery/ag-term` — runtime, pipeline, terminal protocols\n * - `@silvery/ansi` — styling, colors, terminal control\n * - `@silvery/theme` — ThemeProvider, useTheme, semantic tokens\n * - `@silvery/tea` — store, core types, tree utilities\n * - `@silvery/test` — testing utilities, buffer assertions\n *\n * Or import everything from `silvery`.\n *\n * @packageDocumentation\n */\n\n// =============================================================================\n// Utilities (chalk integration, terminal dimensions, VS16 handling)\n// =============================================================================\n\nexport { currentChalkLevel, stripSilveryVS16 } from \"./ink-utils\"\n\n// =============================================================================\n// ANSI sanitization\n// =============================================================================\n\nexport { restoreColonFormatSGR } from \"./ink-sanitize\"\n\n// =============================================================================\n// Components\n// =============================================================================\n\nexport { Box, Text, Static, Newline, Spacer, Transform } from \"./ink-components\"\nexport type { BoxProps, BoxHandle, TextProps, TextHandle, TransformProps } from \"./ink-components\"\n\n// =============================================================================\n// Hooks\n// =============================================================================\n\nexport {\n useFocus,\n useFocusManager,\n useStdin,\n usePaste,\n useCursor,\n useBoxMetrics,\n useWindowSize,\n useInput,\n useApp,\n useStdout,\n useStderr,\n} from \"./ink-hooks\"\nexport type {\n UseFocusOptions,\n UseFocusResult,\n InkUseFocusManagerResult,\n Key,\n InputHandler,\n UseInputOptions,\n UseAppResult,\n UseStdoutResult,\n} from \"./ink-hooks\"\n\n// =============================================================================\n// Render\n// =============================================================================\n\nexport { render, renderToString, initInkCompat, measureElement } from \"./ink-render\"\nexport type { RenderOptions, Instance, MeasureElementOutput } from \"./ink-render\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type DOMElement = any\n\n// =============================================================================\n// Term primitives (so consumers don't need ansi directly)\n// =============================================================================\n\nexport { createTerm, term } from \"@silvery/ag-term/ansi\"\nexport type { Term } from \"@silvery/ag-term/ansi\"\n\n// =============================================================================\n// Measurement\n// =============================================================================\n\nexport { measureText } from \"./ink-measure-text\"\n\n// =============================================================================\n// Kitty keyboard protocol\n// =============================================================================\n\nexport { kittyFlags, resolveFlags, kittyModifiers } from \"./ink-hooks\"\nexport type { KittyFlagName, KittyKeyboardOptions } from \"./ink-hooks\"\n",
135
+ "/**\n * Ink-compatible measureText with caching.\n *\n * Ink's measureText returns cached results (same reference) for repeated calls\n * with identical input. Silvery's measureText doesn't cache at the object level,\n * so we wrap it here to match Ink's behavior.\n */\n\nimport { measureText as silveryMeasureText } from \"@silvery/ag-term/unicode\"\n\ntype MeasureResult = { width: number; height: number }\n\nconst cache = new Map<string, MeasureResult>()\n\n/**\n * Measure the dimensions of text, returning cached results for repeated calls.\n *\n * Compatible with Ink's `measureText` — returns `{ width, height }` and\n * guarantees reference equality for identical inputs.\n */\nexport function measureText(text: string): MeasureResult {\n if (text.length === 0) {\n return { width: 0, height: 0 }\n }\n\n const cached = cache.get(text)\n if (cached) {\n return cached\n }\n\n const result = silveryMeasureText(text)\n cache.set(text, result)\n return result\n}\n"
136
+ ],
137
+ "mappings": "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a,UACb,WAAY,UACZ,oBAAqB,UACrB,oBAAqB,SACvB,EAGa,cAA8B,CACzC,KAAM,iBACN,KAAM,GACN,MAAO,UACP,IAAK,UACL,MAAO,UACP,OAAQ,UACR,KAAM,UACN,QAAS,UACT,KAAM,UACN,MAAO,UACP,YAAa,UACb,UAAW,UACX,YAAa,SAAS,UAAW,IAAI,EACrC,aAAc,SAAS,UAAW,IAAI,EACtC,WAAY,SAAS,UAAW,IAAI,EACpC,cAAe,UACf,WAAY,SAAS,UAAW,IAAI,EACpC,YAAa,UACb,WAAY,UACZ,WAAY,UACZ,YAAa,UACb,WAAY,UACZ,oBAAqB,UACrB,oBAAqB,SACvB,QCKa,gBAoHA,iBAGA,+CAjLb,cAIA,kBACA,YACA,eACA,iBACA,mBACA,gBACA,eACA,iBACA,gBACA,kBACA,eACA,cACA,gBACA,iBACA,WACA,gBACA,eACA,eACA,gBACA,iBACA,eACA,YACA,aAGA,kBACA,YACA,eACA,iBACA,mBACA,gBACA,eACA,iBACA,gBACA,kBACA,eACA,cACA,gBACA,iBACA,WACA,gBACA,eACA,eACA,gBACA,iBACA,eACA,YACA,aAOa,gBAAyB,CACpC,KAAM,cACN,GAAI,GACJ,GAAI,cACJ,MAAO,QACP,QAAS,QACT,QAAS,cACT,UAAW,QACX,QAAS,cACT,UAAW,QACX,QAAS,QACT,UAAW,cACX,OAAQ,QACR,SAAU,SACV,UAAW,QACX,YAAa,SACb,QAAS,SACT,UAAW,QACX,UAAW,QACX,YAAa,QACb,OAAQ,aACR,SAAU,QACV,MAAO,YACP,QAAS,QACT,QAAS,SACT,UAAW,QACX,QAAS,cACT,UAAW,QACX,KAAM,OACN,OAAQ,QACR,OAAQ,OACR,YAAa,OACb,YAAa,aACb,KAAM,aACN,WAAY,OACZ,QAAS,CACP,QACA,MACA,QACA,SACA,OACA,UACA,OACA,QACA,cACA,YACA,cACA,eACA,aACA,gBACA,aACA,aACF,CACF,EA+Da,iBAA0B,YAAY,IAAI,EAG1C,kBAA2B,YAAY,eAAe,IC5J5D,SAAS,cAAc,EAAU,CACtC,OAAO,cAAc,OAAS,EAAI,cA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",
138
+ "debugId": "A789448E573EDBD064756E2164756E21",
139
+ "names": []
140
+ }