silvery 0.18.2 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/dist/{animation-DhINOJk8.mjs → animation-Cn64yepo.mjs} +1 -1
  2. package/dist/{animation-DhINOJk8.mjs.map → animation-Cn64yepo.mjs.map} +1 -1
  3. package/dist/{ansi-C6Qs1Wn2.mjs → ansi-CLOitHKx.mjs} +1 -1
  4. package/dist/ansi-CLOitHKx.mjs.map +1 -0
  5. package/dist/{ansi-CsjnZtAw.d.mts → ansi-Cc33mW54.d.mts} +1 -1
  6. package/dist/{ansi-CsjnZtAw.d.mts.map → ansi-Cc33mW54.d.mts.map} +1 -1
  7. package/dist/{chunk-BSw8zbkd.mjs → chunk-Vs_PY4HZ.mjs} +1 -1
  8. package/dist/cli-BKp0YtBD.mjs +4 -0
  9. package/dist/{context-BjWgrikx.mjs → context-BU5LkkIy.mjs} +8 -7
  10. package/dist/context-BU5LkkIy.mjs.map +1 -0
  11. package/dist/devtools-9QY4teqI.mjs +2 -0
  12. package/dist/{devtools-CeO9X_uv.mjs → devtools-DxkSLXDA.mjs} +4 -5
  13. package/dist/devtools-DxkSLXDA.mjs.map +1 -0
  14. package/dist/{eta-BnQSZcWf.mjs → eta-Bb3RH3wh.mjs} +1 -1
  15. package/dist/{eta-BnQSZcWf.mjs.map → eta-Bb3RH3wh.mjs.map} +1 -1
  16. package/dist/{flexily-zero-adapter-BOM0cl8R.mjs → flexily-zero-adapter-BlQa46nr.mjs} +21 -64
  17. package/dist/flexily-zero-adapter-BlQa46nr.mjs.map +1 -0
  18. package/dist/{flexily-zero-adapter-V8R3HQtK.mjs → flexily-zero-adapter-CMxXhdOL.mjs} +1 -1
  19. package/dist/{image-B0zMbVUr.mjs → image-CTII5QWI.mjs} +3 -3
  20. package/dist/image-CTII5QWI.mjs.map +1 -0
  21. package/dist/{index-Bh3U1K09.d.mts → index-BXslOebb.d.mts} +547 -137
  22. package/dist/index-BXslOebb.d.mts.map +1 -0
  23. package/dist/{index-C4vrhbud.d.mts → index-BnA7mNpo.d.mts} +1 -1
  24. package/dist/{index-C4vrhbud.d.mts.map → index-BnA7mNpo.d.mts.map} +1 -1
  25. package/dist/index-D3saHouR.d.mts +1392 -0
  26. package/dist/index-D3saHouR.d.mts.map +1 -0
  27. package/dist/index.d.mts +5 -33
  28. package/dist/index.d.mts.map +1 -1
  29. package/dist/index.mjs +13 -13
  30. package/dist/{layout-engine--drvrWjD.mjs → layout-engine-B6Cdz1yZ.mjs} +1 -1
  31. package/dist/{layout-engine-Dr3cY5U4.mjs → layout-engine-ClUgv6jB.mjs} +3 -3
  32. package/dist/{layout-engine-Dr3cY5U4.mjs.map → layout-engine-ClUgv6jB.mjs.map} +1 -1
  33. package/dist/{multi-progress-CcdqJFlf.mjs → multi-progress-Bq9Oi_WI.mjs} +3 -3
  34. package/dist/{multi-progress-CcdqJFlf.mjs.map → multi-progress-Bq9Oi_WI.mjs.map} +1 -1
  35. package/dist/{multi-progress-DQ-uUzLf.d.mts → multi-progress-DAQC7eap.d.mts} +2 -2
  36. package/dist/{multi-progress-DQ-uUzLf.d.mts.map → multi-progress-DAQC7eap.d.mts.map} +1 -1
  37. package/dist/{node-CP5WChgr.mjs → node-BeWlnCPY.mjs} +4 -4
  38. package/dist/node-BeWlnCPY.mjs.map +1 -0
  39. package/dist/{progress-bar-IrUjkLfU.mjs → progress-bar-CXE5Qfkd.mjs} +4 -4
  40. package/dist/progress-bar-CXE5Qfkd.mjs.map +1 -0
  41. package/dist/reconciler-Cwgm8hRR.mjs +8459 -0
  42. package/dist/reconciler-Cwgm8hRR.mjs.map +1 -0
  43. package/dist/{render-string-DVfgc8xr.mjs → render-string-Cbuf63Ya.mjs} +936 -136
  44. package/dist/render-string-Cbuf63Ya.mjs.map +1 -0
  45. package/dist/{render-string-BwLG7rIX.mjs → render-string-Tv-jqM16.mjs} +1 -1
  46. package/dist/runtime.d.mts +2 -2
  47. package/dist/runtime.mjs +3 -3
  48. package/dist/{spinner-BRkaJI0N.d.mts → spinner-CGo34vyR.d.mts} +2 -2
  49. package/dist/{spinner-BRkaJI0N.d.mts.map → spinner-CGo34vyR.d.mts.map} +1 -1
  50. package/dist/{spinner-BmldKx0M.mjs → spinner-CeOmcuw_.mjs} +3 -3
  51. package/dist/spinner-CeOmcuw_.mjs.map +1 -0
  52. package/dist/src-B5GjfG7g.mjs +4305 -0
  53. package/dist/src-B5GjfG7g.mjs.map +1 -0
  54. package/dist/{src-CJPXf3fC.mjs → src-C2uvC-r0.mjs} +7535 -6467
  55. package/dist/src-C2uvC-r0.mjs.map +1 -0
  56. package/dist/{src-D8kLrQBT.mjs → src-CChwjk0Z.mjs} +8 -86
  57. package/dist/src-CChwjk0Z.mjs.map +1 -0
  58. package/dist/{src-D_BS-as7.mjs → src-NCKb8kE5.mjs} +777 -776
  59. package/dist/src-NCKb8kE5.mjs.map +1 -0
  60. package/dist/theme.d.mts +2 -130
  61. package/dist/theme.mjs +3 -8
  62. package/dist/{types-B4A8Ebba.d.mts → types-BH_v3iMT.d.mts} +1 -1
  63. package/dist/{types-B4A8Ebba.d.mts.map → types-BH_v3iMT.d.mts.map} +1 -1
  64. package/dist/{types-e4dpfbSa.mjs → types-Bk2yw9Qj.mjs} +3 -3
  65. package/dist/types-Bk2yw9Qj.mjs.map +1 -0
  66. package/dist/ui/animation.d.mts +1 -1
  67. package/dist/ui/animation.mjs +1 -1
  68. package/dist/ui/ansi.d.mts +1 -1
  69. package/dist/ui/ansi.mjs +1 -1
  70. package/dist/ui/cli.d.mts +3 -3
  71. package/dist/ui/cli.mjs +5 -5
  72. package/dist/ui/display.d.mts +1 -1
  73. package/dist/ui/display.mjs.map +1 -1
  74. package/dist/ui/image.d.mts +1 -1
  75. package/dist/ui/image.mjs +1 -1
  76. package/dist/ui/input.d.mts +1 -1
  77. package/dist/ui/input.d.mts.map +1 -1
  78. package/dist/ui/input.mjs +2 -4
  79. package/dist/ui/input.mjs.map +1 -1
  80. package/dist/ui/progress.d.mts +3 -3
  81. package/dist/ui/progress.d.mts.map +1 -1
  82. package/dist/ui/progress.mjs +3 -3
  83. package/dist/ui/progress.mjs.map +1 -1
  84. package/dist/ui/react.d.mts +1 -1
  85. package/dist/ui/react.d.mts.map +1 -1
  86. package/dist/ui/react.mjs +2 -2
  87. package/dist/ui/react.mjs.map +1 -1
  88. package/dist/ui/utils.mjs +1 -1
  89. package/dist/ui/wrappers.d.mts +2 -2
  90. package/dist/ui/wrappers.mjs +1 -1
  91. package/dist/ui.d.mts +5 -5
  92. package/dist/ui.mjs +6 -6
  93. package/dist/{useLatest-6xqnGIU6.d.mts → useLatest-Bg2x4bfP.d.mts} +1 -1
  94. package/dist/{useLatest-6xqnGIU6.d.mts.map → useLatest-Bg2x4bfP.d.mts.map} +1 -1
  95. package/dist/{with-text-input-lUh9gYAG.d.mts → with-text-input-CRfoiFFG.d.mts} +3 -3
  96. package/dist/with-text-input-CRfoiFFG.d.mts.map +1 -0
  97. package/dist/{wrappers-JrEYTuKA.mjs → wrappers-UTADQkSY.mjs} +4 -4
  98. package/dist/wrappers-UTADQkSY.mjs.map +1 -0
  99. package/dist/{yoga-adapter-Bc8XT9cN.mjs → yoga-adapter-8oRGRw8V.mjs} +2 -2
  100. package/dist/{yoga-adapter-Bc8XT9cN.mjs.map → yoga-adapter-8oRGRw8V.mjs.map} +1 -1
  101. package/dist/yoga-adapter-D_CcxSt5.mjs +2 -0
  102. package/package.json +54 -45
  103. package/dist/UPNG-DvKjM6wE.mjs +0 -5076
  104. package/dist/UPNG-DvKjM6wE.mjs.map +0 -1
  105. package/dist/__vite-browser-external-2447137e-DPKHHqQK.mjs +0 -6
  106. package/dist/__vite-browser-external-2447137e-DPKHHqQK.mjs.map +0 -1
  107. package/dist/ansi-C6Qs1Wn2.mjs.map +0 -1
  108. package/dist/apng-CvSlLBtc.mjs +0 -3
  109. package/dist/apng-DFFVOItr.mjs +0 -70
  110. package/dist/apng-DFFVOItr.mjs.map +0 -1
  111. package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
  112. package/dist/backend-DU0Y938U.mjs +0 -13396
  113. package/dist/backend-DU0Y938U.mjs.map +0 -1
  114. package/dist/backends-BihMKFY_.mjs +0 -1181
  115. package/dist/backends-BihMKFY_.mjs.map +0 -1
  116. package/dist/backends-Dk_5G_gC.mjs +0 -3
  117. package/dist/cli-GwJ0S2In.mjs +0 -4
  118. package/dist/context-BjWgrikx.mjs.map +0 -1
  119. package/dist/derive-O_Kb1Bk_.d.mts +0 -28
  120. package/dist/derive-O_Kb1Bk_.d.mts.map +0 -1
  121. package/dist/devtools-CeO9X_uv.mjs.map +0 -1
  122. package/dist/devtools-nX4tj6OH.mjs +0 -2
  123. package/dist/flexily-zero-adapter-BOM0cl8R.mjs.map +0 -1
  124. package/dist/gif-B9Uq4qZA.mjs +0 -73
  125. package/dist/gif-B9Uq4qZA.mjs.map +0 -1
  126. package/dist/gif-BdrLRBmM.mjs +0 -3
  127. package/dist/gifenc-DfhOb4xr.mjs +0 -730
  128. package/dist/gifenc-DfhOb4xr.mjs.map +0 -1
  129. package/dist/image-B0zMbVUr.mjs.map +0 -1
  130. package/dist/index-Bh3U1K09.d.mts.map +0 -1
  131. package/dist/index-dehZ18K-.d.mts +0 -679
  132. package/dist/index-dehZ18K-.d.mts.map +0 -1
  133. package/dist/key-mapping-7k2ufK2b.mjs +0 -3
  134. package/dist/key-mapping-WLUmxjx1.mjs +0 -132
  135. package/dist/key-mapping-WLUmxjx1.mjs.map +0 -1
  136. package/dist/node-CP5WChgr.mjs.map +0 -1
  137. package/dist/progress-bar-IrUjkLfU.mjs.map +0 -1
  138. package/dist/reconciler-B8uxQxaU.mjs +0 -16482
  139. package/dist/reconciler-B8uxQxaU.mjs.map +0 -1
  140. package/dist/render-string-DVfgc8xr.mjs.map +0 -1
  141. package/dist/resvg-js-Cwipz-_J.mjs +0 -203
  142. package/dist/resvg-js-Cwipz-_J.mjs.map +0 -1
  143. package/dist/spinner-BmldKx0M.mjs.map +0 -1
  144. package/dist/src-C0sOQW-t.mjs +0 -3866
  145. package/dist/src-C0sOQW-t.mjs.map +0 -1
  146. package/dist/src-CJPXf3fC.mjs.map +0 -1
  147. package/dist/src-D8kLrQBT.mjs.map +0 -1
  148. package/dist/src-D_BS-as7.mjs.map +0 -1
  149. package/dist/theme.d.mts.map +0 -1
  150. package/dist/theme.mjs.map +0 -1
  151. package/dist/types-e4dpfbSa.mjs.map +0 -1
  152. package/dist/with-text-input-lUh9gYAG.d.mts.map +0 -1
  153. package/dist/wrapper-CE6GQ27z.mjs +0 -3527
  154. package/dist/wrapper-CE6GQ27z.mjs.map +0 -1
  155. package/dist/wrappers-JrEYTuKA.mjs.map +0 -1
  156. package/dist/yoga-adapter-B8LZpQcE.mjs +0 -2
@@ -1 +0,0 @@
1
- {"version":3,"file":"render-string-DVfgc8xr.mjs","names":["defaultGraphemeWidth","collectTextContent","getTextWidth","traverseTree","log","log"],"sources":["../packages/ag-term/src/pipeline/prepared-text.ts","../packages/ag-term/src/pipeline/pretext.ts","../packages/ag-term/src/pipeline/helpers.ts","../packages/ag-term/src/pipeline/measure-phase.ts","../packages/ag-term/src/pipeline/layout-phase.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/decoration-phase.ts","../packages/ag-term/src/pipeline/cascade-predicates.ts","../packages/ag-term/src/pipeline/reactive-node.ts","../packages/ag-term/src/pipeline/render-phase.ts","../packages/ag-term/src/pipeline/backdrop-phase.ts","../packages/ag-term/src/ag.ts","../packages/ag-react/src/reconciler/string-reconciler.ts","../packages/ag-react/src/render-string.tsx"],"sourcesContent":["/**\n * PreparedText: Per-node text analysis cache (Design G, Steps 1-3).\n *\n * Caches three levels of text analysis on a WeakMap<AgNode> to avoid\n * redundant work across frames:\n *\n * Level 0 — Plain text (measure phase + maxDisplayWidth computation)\n * Level 1 — Collected styled text with bg segments (render phase)\n * Level 2 — Formatted lines per width (render phase, LRU by width)\n *\n * Invalidation uses the epoch-based dirty flag system:\n * - Plain text: CONTENT_BIT | CHILDREN_BIT\n * - Collected text: CONTENT_BIT | CHILDREN_BIT | STYLE_PROPS_BIT | BG_BIT\n * - Format entries: cleared when collected text is invalidated; keyed by (width, wrap, trim)\n *\n * The WeakMap ensures automatic cleanup when nodes are removed from the tree.\n * Format entries use a small LRU (4 entries) to handle Flexily measure probes\n * (min-content, max-content, final width) without unbounded growth.\n */\n\nimport type { AgNode } from \"@silvery/ag/types\"\nimport { isDirty, CONTENT_BIT, CHILDREN_BIT, STYLE_PROPS_BIT, BG_BIT, SUBTREE_BIT } from \"@silvery/ag/epoch\"\nimport type { TextAnalysis } from \"./pretext\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Cached formatted lines for a specific width/wrap/trim combination. */\nexport interface FormatEntry {\n width: number\n wrap: string | boolean | undefined\n trim: boolean\n lines: string[]\n lineOffsets: Array<{ start: number; end: number }>\n hasLineOffsets: boolean\n}\n\n/** Minimal shape of collected text result. Structurally matches TextWithBg. */\nexport interface CollectedTextResult {\n text: string\n bgSegments: readonly { start: number; end: number; bg: unknown }[]\n childSpans: readonly { node: AgNode; start: number; end: number }[]\n plainLen: number\n}\n\n/** Per-text-node cache entry. */\ninterface TextNodeCache {\n // Level 0: plain text\n plainText: string | null\n plainTextLineCount: number\n\n // Level 1: collected styled text\n collected: CollectedTextResult | null\n collectedMaxDisplayWidth: number | undefined\n\n // Level 2: formatted lines (LRU, max 4 entries)\n formats: FormatEntry[]\n\n // Level 3: Pretext analysis (cumWidths, breakpoints, etc.)\n // Built from collected text, invalidated when collected text changes\n analysis: TextAnalysis | null\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst MAX_FORMAT_ENTRIES = 4\n\n/** Content-affecting flags that invalidate plain text. */\nconst PLAIN_TEXT_DIRTY = CONTENT_BIT | CHILDREN_BIT\n\n/**\n * All flags that affect collected text (ANSI codes, bg segments, child spans).\n * SUBTREE_BIT is included because collectTextWithBg recurses into virtual text\n * children — a child's style change sets SUBTREE_BIT on the parent without\n * setting STYLE_PROPS_BIT (the parent's own props didn't change).\n */\nconst COLLECTED_TEXT_DIRTY = CONTENT_BIT | CHILDREN_BIT | STYLE_PROPS_BIT | BG_BIT | SUBTREE_BIT\n\n// ============================================================================\n// Storage\n// ============================================================================\n\n/** Set to true to disable all caching (for testing/debugging). */\nlet _cacheDisabled = !!process.env.SILVERY_NO_TEXT_CACHE\nexport function setPreparedTextCacheEnabled(enabled: boolean): void {\n _cacheDisabled = !enabled\n}\n\nconst textCaches = new WeakMap<AgNode, TextNodeCache>()\n\nfunction getOrCreate(node: AgNode): TextNodeCache {\n let entry = textCaches.get(node)\n if (!entry) {\n entry = {\n plainText: null,\n plainTextLineCount: 0,\n collected: null,\n collectedMaxDisplayWidth: undefined,\n formats: [],\n analysis: null,\n }\n textCaches.set(node, entry)\n }\n return entry\n}\n\n// ============================================================================\n// Level 0: Plain text (measure phase + maxDisplayWidth computation)\n// ============================================================================\n\n/**\n * Get cached plain text and line count.\n * Returns null on cache miss (content/children changed or first access).\n */\nexport function getCachedPlainText(node: AgNode): { text: string; lineCount: number } | null {\n if (_cacheDisabled) return null\n const entry = textCaches.get(node)\n if (entry?.plainText == null) return null\n if (isDirty(node.dirtyBits, node.dirtyEpoch, PLAIN_TEXT_DIRTY)) {\n entry.plainText = null\n return null\n }\n return { text: entry.plainText, lineCount: entry.plainTextLineCount }\n}\n\n/** Store plain text in cache. */\nexport function setCachedPlainText(node: AgNode, text: string, lineCount: number): void {\n const entry = getOrCreate(node)\n entry.plainText = text\n entry.plainTextLineCount = lineCount\n}\n\n// ============================================================================\n// Level 1: Collected styled text (render phase)\n// ============================================================================\n\n/**\n * Get cached collected text (from collectTextWithBg).\n * Invalidated by content, children, style, or bg changes, or maxDisplayWidth mismatch.\n */\nexport function getCachedCollectedText(node: AgNode, maxDisplayWidth: number | undefined): CollectedTextResult | null {\n if (_cacheDisabled) return null\n const entry = textCaches.get(node)\n if (!entry?.collected) return null\n\n if (isDirty(node.dirtyBits, node.dirtyEpoch, COLLECTED_TEXT_DIRTY)) {\n entry.collected = null\n entry.formats = [] // collected text changed → format stale\n entry.analysis = null // analysis depends on collected text\n return null\n }\n\n if (entry.collectedMaxDisplayWidth !== maxDisplayWidth) {\n entry.collected = null\n entry.formats = []\n entry.analysis = null\n return null\n }\n\n return entry.collected\n}\n\n/** Store collected text in cache. */\nexport function setCachedCollectedText(\n node: AgNode,\n result: CollectedTextResult,\n maxDisplayWidth: number | undefined,\n): void {\n const entry = getOrCreate(node)\n entry.collected = result\n entry.collectedMaxDisplayWidth = maxDisplayWidth\n}\n\n// ============================================================================\n// Level 2: Formatted lines per width (render phase, LRU)\n// ============================================================================\n\n/**\n * Get cached formatted lines for the given width/wrap/trim.\n * Returns null on cache miss.\n */\nexport function getCachedFormat(\n node: AgNode,\n width: number,\n wrap: string | boolean | undefined,\n trim: boolean,\n): FormatEntry | null {\n if (_cacheDisabled) return null\n const entry = textCaches.get(node)\n if (!entry || entry.formats.length === 0) return null\n\n for (let i = 0; i < entry.formats.length; i++) {\n const f = entry.formats[i]!\n if (f.width === width && f.wrap === wrap && f.trim === trim) {\n // LRU: move to end (most recently used)\n if (i < entry.formats.length - 1) {\n entry.formats.splice(i, 1)\n entry.formats.push(f)\n }\n return f\n }\n }\n return null\n}\n\n/** Store formatted lines in cache (LRU, evicts oldest when full). */\nexport function setCachedFormat(\n node: AgNode,\n width: number,\n wrap: string | boolean | undefined,\n trim: boolean,\n lines: string[],\n lineOffsets: Array<{ start: number; end: number }>,\n hasLineOffsets: boolean,\n): void {\n const entry = getOrCreate(node)\n\n // Replace existing entry for same key\n for (let i = 0; i < entry.formats.length; i++) {\n const f = entry.formats[i]!\n if (f.width === width && f.wrap === wrap && f.trim === trim) {\n entry.formats[i] = { width, wrap, trim, lines, lineOffsets, hasLineOffsets }\n return\n }\n }\n\n // Evict oldest if at capacity\n if (entry.formats.length >= MAX_FORMAT_ENTRIES) {\n entry.formats.shift()\n }\n entry.formats.push({ width, wrap, trim, lines, lineOffsets, hasLineOffsets })\n}\n\n// ============================================================================\n// Level 3: Pretext analysis (cumWidths, breakpoints — for snug-content/even wrap)\n// ============================================================================\n\n/**\n * Get cached text analysis. Invalidated when content changes.\n * Uses PLAIN_TEXT_DIRTY (not COLLECTED_TEXT_DIRTY) because analysis\n * is built from plain text in measure phase, not styled text.\n */\nexport function getCachedAnalysis(node: AgNode): TextAnalysis | null {\n if (_cacheDisabled) return null\n const entry = textCaches.get(node)\n if (!entry?.analysis) return null\n if (isDirty(node.dirtyBits, node.dirtyEpoch, PLAIN_TEXT_DIRTY)) {\n entry.analysis = null\n return null\n }\n return entry.analysis\n}\n\n/** Store text analysis in cache. */\nexport function setCachedAnalysis(node: AgNode, analysis: TextAnalysis): void {\n const entry = getOrCreate(node)\n entry.analysis = analysis\n}\n","/**\n * Pretext: Grapheme-indexed text analysis for layout queries.\n *\n * Inspired by https://chenglou.me/pretext/ — prepare text once, measure at\n * any width cheaply. Enables layout algorithms CSS can't express:\n *\n * - **Shrinkwrap**: find the narrowest width that keeps the same line count\n * - **Balanced**: equalize line widths (reduce raggedness)\n * - **Knuth-Plass**: optimal paragraph breaking (minimize total raggedness)\n * - **Height prediction**: exact line count at any width without full wrapping\n */\n\nimport {\n graphemeWidth as defaultGraphemeWidth,\n splitGraphemesAnsiAware,\n isWordBoundary,\n canBreakAnywhere,\n wrapText,\n} from \"../unicode\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Grapheme-level text analysis for fast width queries. */\nexport interface TextAnalysis {\n /** ANSI-aware graphemes (visible chars + zero-width ANSI tokens). */\n graphemes: string[]\n /** Display width per grapheme (0 for ANSI tokens). */\n widths: number[]\n /** Prefix sums: cumWidths[i] = sum(widths[0..i-1]). cumWidths[0] = 0. */\n cumWidths: number[]\n /** Total display width of all graphemes. */\n totalWidth: number\n /** Width of the widest unbreakable word segment. */\n maxWordWidth: number\n /** Width of the widest single grapheme. */\n maxGraphemeWidth: number\n /** Grapheme indices where newlines occur. */\n newlineIndices: number[]\n /** Grapheme indices where word breaks are legal. */\n breakIndices: number[]\n /** Original text (for delegating to wrapText). */\n text: string\n}\n\n// ============================================================================\n// Build\n// ============================================================================\n\n/**\n * Build text analysis from an ANSI-embedded text string.\n * O(N) where N is grapheme count. Call once per text change (cached by PreparedText).\n */\nexport function buildTextAnalysis(text: string, gWidthFn: (g: string) => number = defaultGraphemeWidth): TextAnalysis {\n const graphemes = splitGraphemesAnsiAware(text)\n const len = graphemes.length\n const widths = new Array<number>(len)\n const cumWidths = new Array<number>(len + 1)\n const newlineIndices: number[] = []\n const breakIndices: number[] = []\n\n cumWidths[0] = 0\n let maxWordWidth = 0\n let maxGraphemeWidth = 0\n let currentWordWidth = 0\n\n for (let i = 0; i < len; i++) {\n const g = graphemes[i]!\n const w = gWidthFn(g)\n widths[i] = w\n cumWidths[i + 1] = cumWidths[i]! + w\n if (w > maxGraphemeWidth) maxGraphemeWidth = w\n\n if (g === \"\\n\") {\n newlineIndices.push(i)\n maxWordWidth = Math.max(maxWordWidth, currentWordWidth)\n currentWordWidth = 0\n } else if (isWordBoundary(g)) {\n breakIndices.push(i + 1)\n maxWordWidth = Math.max(maxWordWidth, currentWordWidth)\n currentWordWidth = 0\n } else if (canBreakAnywhere(g)) {\n breakIndices.push(i)\n maxWordWidth = Math.max(maxWordWidth, currentWordWidth)\n currentWordWidth = w\n } else if (w > 0) {\n currentWordWidth += w\n }\n }\n maxWordWidth = Math.max(maxWordWidth, currentWordWidth)\n\n return {\n graphemes,\n widths,\n cumWidths,\n totalWidth: cumWidths[len]!,\n maxWordWidth,\n maxGraphemeWidth,\n newlineIndices,\n breakIndices,\n text,\n }\n}\n\n// ============================================================================\n// Line counting\n// ============================================================================\n\n/**\n * Count how many lines text would occupy at a given width.\n *\n * Delegates to wrapText for correctness — the greedy wrapping algorithm has\n * subtle boundary-char handling (spaces consumed on overflow, leading space\n * trimming on continuation lines) that's error-prone to reimplement.\n *\n * For terminal text (20-200 chars), wrapText is ~5-12µs per call.\n * Shrinkwrap does ~7-9 calls (log2(width)), so total is ~50-100µs.\n */\nexport function countLinesAtWidth(analysis: TextAnalysis, width: number): number {\n if (width <= 0) return Infinity\n if (analysis.totalWidth <= width && analysis.newlineIndices.length === 0) return 1\n return wrapText(analysis.text, width, true, true).length\n}\n\n// ============================================================================\n// Shrinkwrap\n// ============================================================================\n\n/**\n * Find the narrowest integer width that produces the same line count as maxWidth.\n *\n * CSS fit-content uses the widest wrapped line — leaving dead space when the\n * last line is short. Shrinkwrap binary-searches for the tightest width that\n * keeps the same number of lines, eliminating wasted area in bubbles/cards.\n *\n * O(log(maxWidth) × wrapText) — ~7-9 iterations for terminal widths.\n */\nexport function shrinkwrapWidth(analysis: TextAnalysis, maxWidth: number): number {\n if (maxWidth <= 0) return 0\n const targetLineCount = countLinesAtWidth(analysis, maxWidth)\n if (targetLineCount <= 1) {\n return Math.min(Math.ceil(analysis.totalWidth), maxWidth)\n }\n\n // Lower bound: max grapheme width (character wrap allows widths below maxWordWidth)\n // Upper bound: maxWidth (can't return wider than the container)\n let lo = Math.max(1, analysis.maxGraphemeWidth)\n let hi = maxWidth\n\n // Guard: if lo >= hi, nothing to search\n if (lo >= hi) return Math.min(hi, maxWidth)\n\n while (lo < hi) {\n const mid = (lo + hi) >> 1\n if (countLinesAtWidth(analysis, mid) <= targetLineCount) {\n hi = mid\n } else {\n lo = mid + 1\n }\n }\n\n // Clamp to maxWidth (safety)\n return Math.min(lo, maxWidth)\n}\n\n// ============================================================================\n// Balanced breaking\n// ============================================================================\n\n/**\n * Find a width that produces lines of approximately equal length.\n *\n * Strategy: compute total width / line count as the ideal per-line width,\n * then find the narrowest width at that line count via shrinkwrap.\n */\nexport function balancedWidth(analysis: TextAnalysis, maxWidth: number): number {\n if (maxWidth <= 0) return 0\n const lineCount = countLinesAtWidth(analysis, maxWidth)\n if (lineCount <= 1) return Math.min(Math.ceil(analysis.totalWidth), maxWidth)\n\n // Ideal balanced width: total / lines, rounded up\n const idealWidth = Math.ceil(analysis.totalWidth / lineCount)\n\n // Clamp to [maxGraphemeWidth, maxWidth]\n const candidateWidth = Math.max(analysis.maxGraphemeWidth, Math.min(idealWidth, maxWidth))\n\n // Verify this doesn't increase line count\n if (countLinesAtWidth(analysis, candidateWidth) > lineCount) {\n return shrinkwrapWidth(analysis, maxWidth)\n }\n\n // Further tighten via shrinkwrap at the balanced line count\n return shrinkwrapWidth(analysis, candidateWidth)\n}\n\n// ============================================================================\n// Knuth-Plass optimal paragraph breaking\n// ============================================================================\n\n/**\n * Find optimal line breaks that minimize total raggedness.\n *\n * Runs per-paragraph (split by newlines) to avoid penalty interactions\n * around forced breaks. Falls back to greedy wrapping for paragraphs\n * where the DP finds no feasible solution (overlong words).\n *\n * O(breakpoints²) per paragraph, typically much less with pruning.\n */\nexport function knuthPlassBreaks(analysis: TextAnalysis, width: number): number[] {\n if (width <= 0) return []\n if (analysis.totalWidth <= width && analysis.newlineIndices.length === 0) return []\n\n // Split into paragraphs at newlines and process each independently\n const { newlineIndices, graphemes } = analysis\n const allBreaks: number[] = []\n\n const paragraphStarts = [0]\n for (const nl of newlineIndices) {\n paragraphStarts.push(nl + 1)\n }\n\n for (let p = 0; p < paragraphStarts.length; p++) {\n const pStart = paragraphStarts[p]!\n const pEnd = p + 1 < paragraphStarts.length ? paragraphStarts[p + 1]! - 1 : graphemes.length // -1 to exclude newline\n\n if (pStart >= pEnd) continue // empty paragraph\n\n const breaks = knuthPlassForParagraph(analysis, pStart, pEnd, width)\n allBreaks.push(...breaks)\n\n // Add newline break if not the last paragraph\n if (p < paragraphStarts.length - 1 && pEnd < graphemes.length) {\n allBreaks.push(pEnd + 1) // after the newline\n }\n }\n\n return allBreaks\n}\n\n/** DP for a single paragraph (no newlines). */\nfunction knuthPlassForParagraph(analysis: TextAnalysis, pStart: number, pEnd: number, width: number): number[] {\n const { cumWidths, breakIndices, widths, graphemes } = analysis\n\n // Build candidates for this paragraph\n const candidates: number[] = [pStart]\n for (const bp of breakIndices) {\n if (bp > pStart && bp <= pEnd) candidates.push(bp)\n }\n candidates.push(pEnd)\n\n const n = candidates.length\n if (n <= 2) return [] // single segment, no breaks needed\n\n const cost = new Array<number>(n).fill(Infinity)\n const next = new Array<number>(n).fill(-1)\n cost[n - 1] = 0\n\n for (let i = n - 2; i >= 0; i--) {\n const lineStart = candidates[i]!\n const lineStartCum = cumWidths[lineStart]!\n\n for (let j = i + 1; j < n; j++) {\n const lineEnd = candidates[j]!\n\n // Compute line width, trimming trailing whitespace\n let trimEnd = lineEnd\n while (trimEnd > lineStart) {\n const prevG = graphemes[trimEnd - 1]\n const prevW = widths[trimEnd - 1]\n if (prevW === 0) {\n trimEnd--\n continue\n } // skip ANSI\n if (prevG === \" \" || prevG === \"\\t\") {\n trimEnd--\n continue\n }\n break\n }\n const lineWidth = cumWidths[trimEnd]! - lineStartCum\n\n if (lineWidth > width) break // too wide, skip wider candidates\n\n const leftover = width - lineWidth\n const lineCost = j === n - 1 ? 0 : leftover * leftover\n const totalCost = lineCost + cost[j]!\n\n if (totalCost < cost[i]!) {\n cost[i] = totalCost\n next[i] = j\n }\n }\n }\n\n // If DP failed (no feasible path), return empty (caller falls back to greedy)\n if (cost[0] === Infinity) return []\n\n // Trace back\n const breaks: number[] = []\n let idx = 0\n while (idx < n - 1 && next[idx]! >= 0) {\n idx = next[idx]!\n if (idx < n - 1) {\n breaks.push(candidates[idx]!)\n }\n }\n\n return breaks\n}\n\n/**\n * Wrap text using Knuth-Plass optimal breaks.\n * Returns line strings — drop-in replacement for greedy wrap.\n * Falls back to greedy wrapText when DP finds no feasible solution.\n */\nexport function optimalWrap(text: string, analysis: TextAnalysis, width: number): string[] {\n const breaks = knuthPlassBreaks(analysis, width)\n if (breaks.length === 0) {\n // No breaks found — either single line or DP infeasible → fall back to greedy\n if (analysis.totalWidth <= width && analysis.newlineIndices.length === 0) return [text]\n return wrapText(text, width, true, true)\n }\n\n const { graphemes, widths } = analysis\n const lines: string[] = []\n let lineStart = 0\n\n for (const bp of breaks) {\n // Trim trailing whitespace (skip zero-width ANSI tokens)\n let lineEnd = bp\n while (lineEnd > lineStart) {\n const w = widths[lineEnd - 1]!\n if (w === 0) {\n lineEnd--\n continue\n } // ANSI token\n const g = graphemes[lineEnd - 1]!\n if (g === \" \" || g === \"\\t\" || g === \"\\n\") {\n lineEnd--\n continue\n }\n break\n }\n lines.push(graphemes.slice(lineStart, lineEnd).join(\"\"))\n\n // Skip leading whitespace on next line (skip ANSI tokens)\n lineStart = bp\n while (lineStart < graphemes.length) {\n const g = graphemes[lineStart]!\n if (g === \" \" || g === \"\\t\") {\n lineStart++\n continue\n }\n break\n }\n }\n\n // Last line\n if (lineStart < graphemes.length) {\n lines.push(graphemes.slice(lineStart).join(\"\"))\n }\n\n return lines\n}\n","/**\n * Shared helper functions for silvery pipeline phases.\n */\n\nimport type { BoxProps } from \"@silvery/ag/types\"\nimport { getActiveLineHeight } from \"../unicode\"\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 * In pixel/canvas mode (lineHeight > 1), borders are visual-only (fillRoundedRect)\n * and don't affect content positioning — returns 0.\n */\nexport function getBorderSize(props: BoxProps): {\n top: number\n bottom: number\n left: number\n right: number\n} {\n if (!props.borderStyle || getActiveLineHeight() > 1) {\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","/**\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, graphemeWidth, wrapText, getActiveLineHeight } from \"../unicode\"\nimport { collectPlainText as collectTextContent } from \"./collect-text\"\nimport { getCachedPlainText, setCachedPlainText, getCachedAnalysis, setCachedAnalysis } from \"./prepared-text\"\nimport { buildTextAnalysis, shrinkwrapWidth } from \"./pretext\"\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 // width=\"fit-content\" is now handled natively by Flexily (UNIT_FIT_CONTENT).\n // The reconciler calls setWidthFitContent() directly.\n // width=\"snug-content\" uses Flexily's UNIT_SNUG_CONTENT for basic sizing,\n // but still needs the binary-search tightening pass here.\n // height=\"fit-content\" still needs the pre-layout polyfill.\n const isSnugContent = props.width === \"snug-content\"\n const isHeightFitContent = props.height === \"fit-content\"\n\n if (isSnugContent || isHeightFitContent) {\n // Pass an available-width constraint to child measurement whenever a\n // definite upper bound exists — either a fixed width (height=\"fit-content\"\n // + width:number case) or a maxWidth cap on the snug-content box itself.\n let availableWidth: number | undefined\n const widthIsFixed = typeof props.width === \"number\"\n let definiteUpperWidth: number | undefined =\n widthIsFixed && isHeightFitContent\n ? (props.width as number)\n : typeof props.maxWidth === \"number\"\n ? (props.maxWidth as number)\n : undefined\n if (definiteUpperWidth === undefined) {\n definiteUpperWidth = findAncestorDefiniteWidth(node)\n }\n if (definiteUpperWidth !== undefined) {\n const padding = getPadding(props)\n availableWidth = definiteUpperWidth - 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 if (isSnugContent) {\n const intrinsicSize = measureIntrinsicSize(node, ctx, availableWidth)\n // Fit-snug: find the narrowest width that keeps the same line count.\n // Binary search for tightest width on top of Flexily's native fit-content.\n const shrunkWidth = computeSnugContentWidth(node, intrinsicSize.width, ctx)\n // setMaxWidth caps the snug-content box at the binary-searched width.\n // Flexily's UNIT_SNUG_CONTENT handles the shrink-wrap + available clamping.\n node.layoutNode.setMaxWidth(shrunkWidth)\n }\n if (isHeightFitContent) {\n const intrinsicSize = measureIntrinsicSize(node, ctx, availableWidth)\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 // PreparedText cache: reuse plain text from previous frames when content unchanged\n const cached = getCachedPlainText(node)\n let text: string\n if (cached) {\n text = cached.text\n } else {\n text = collectTextContent(node)\n const lineCount = (text.match(/\\n/g)?.length ?? 0) + 1\n setCachedPlainText(node, text, lineCount)\n }\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 * getActiveLineHeight(),\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 === \"hard\" || wrap === \"even\" || wrap === true || wrap === undefined\n}\n\n/**\n * Compute snug-content width for a node.\n * Uses Pretext analysis to binary-search for the tightest width\n * that keeps the same line count as the fit-content width.\n */\nfunction computeSnugContentWidth(node: AgNode, fitContentWidth: number, ctx?: PipelineContext): number {\n const props = node.props as BoxProps\n\n // Subtract padding + border from fitContentWidth to get CONTENT width.\n // measureIntrinsicSize includes padding+border in its result, but\n // shrinkwrapWidth operates on text content width only.\n let overhead = 0\n const padding = getPadding(props)\n overhead += padding.left + padding.right\n if (props.borderStyle) {\n const border = getBorderSize(props)\n overhead += border.left + border.right\n }\n const contentWidth = fitContentWidth - overhead\n\n // Get or build text analysis\n let analysis = getCachedAnalysis(node)\n if (!analysis) {\n const cached = getCachedPlainText(node)\n const text = cached ? cached.text : collectTextContent(node)\n const gWidthFn = ctx?.measurer?.graphemeWidth?.bind(ctx.measurer) ?? graphemeWidth\n analysis = buildTextAnalysis(text, gWidthFn)\n setCachedAnalysis(node, analysis)\n if (!cached) {\n const lineCount = (text.match(/\\n/g)?.length ?? 0) + 1\n setCachedPlainText(node, text, lineCount)\n }\n }\n\n // Shrinkwrap the content, then add overhead back\n return shrinkwrapWidth(analysis, contentWidth) + overhead\n}\n\n/**\n * Walk up the tree from a node to find the nearest ancestor with a definite\n * width (a fixed number, not \"fit-content\" or \"snug-content\"). Returns the\n * ancestor's inner content width (after subtracting its own padding and border).\n * Returns undefined if no definite-width ancestor is found.\n */\nfunction findAncestorDefiniteWidth(node: AgNode): number | undefined {\n let current = node.parent\n while (current) {\n const p = current.props as BoxProps\n if (typeof p.width === \"number\") {\n let inner = p.width as number\n const padding = getPadding(p)\n inner -= padding.left + padding.right\n if (p.borderStyle) {\n const border = getBorderSize(p)\n inner -= border.left + border.right\n }\n return inner > 0 ? inner : 1\n }\n if (typeof p.maxWidth === \"number\") {\n let inner = p.maxWidth as number\n const padding = getPadding(p)\n inner -= padding.left + padding.right\n if (p.borderStyle) {\n const border = getBorderSize(p)\n inner -= border.left + border.right\n }\n return inner > 0 ? inner : 1\n }\n current = current.parent\n }\n return 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","/**\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\"\n// Layout dirty gate: Flexily's root.layoutNode.isDirty() is the sole source\n// of truth. No silvery-side layout dirty tracking needed.\nimport {\n getRenderEpoch,\n INITIAL_EPOCH,\n isCurrentEpoch,\n isDirty,\n SUBTREE_BIT,\n CHILDREN_BIT,\n ABS_CHILD_BIT,\n DESC_OVERFLOW_BIT,\n} from \"@silvery/ag/epoch\"\nimport { getBorderSize, getPadding } from \"./helpers\"\nimport { syncRectSignals } from \"@silvery/ag/layout-signals\"\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.boxRect\n const dimensionsChanged = prevLayout && (prevLayout.width !== width || prevLayout.height !== height)\n\n // Only recalculate if something changed (dirty nodes or dimensions).\n // Flexily's root isDirty() propagates from any markDirty() call —\n // no silvery-side tracking needed.\n if (!dimensionsChanged && !root.layoutNode?.isDirty()) {\n // Even when layout is clean, style-only changes (outline add/remove,\n // absolute child structural changes) need cascade input caching.\n // These checks run in propagateLayout normally, but when the layout\n // phase skips, they're never computed. Run a lightweight traversal\n // that follows only subtreeDirty paths to cache these inputs.\n if (isDirty(root.dirtyBits, root.dirtyEpoch, SUBTREE_BIT)) {\n propagateCascadeInputs(root)\n }\n return\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 // When dimensions haven't changed, enable incremental skip: subtrees\n // whose Flexily-computed rect matches their existing boxRect are skipped\n // entirely (O(1) rect comparison prunes O(subtree) walk).\n // On dimension change, the root constraint changed so all nodes may get\n // new results — skip nothing, propagate the full tree.\n const incrementalSkip = !dimensionsChanged\n propagateLayout(root, 0, 0, incrementalSkip)\n\n // NOTE: Subscribers are NOT notified here anymore.\n // They are notified by the pipeline AFTER scrollrectPhase completes,\n // so useScrollRect 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 * Propagate computed layout from Yoga nodes to SilveryNodes.\n * Sets boxRect (content-relative position) on each node.\n *\n * When `incrementalSkip` is true, nodes whose Flexily-computed rect matches\n * their existing boxRect can skip the entire subtree — their layout is\n * unchanged. This converts the O(N) tree walk into O(dirty) for frames\n * where only a few nodes changed layout.\n *\n * The skip is safe because:\n * - Flexily's internal fingerprint caching guarantees identical output for\n * subtrees whose inputs didn't change\n * - If the parent's rect matches, all descendants' rects also match\n * (Flexily computes absolute positions from parent dimensions)\n * - prevLayout and layoutChangedThisFrame (stale epoch, won't match\n * current) all retain correct values\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 * @param incrementalSkip When true, skip subtrees where Flexily results match existing boxRect\n */\nfunction propagateLayout(node: AgNode, parentX: number, parentY: number, incrementalSkip: boolean): void {\n // Virtual/raw text nodes (no layoutNode) inherit parent's position\n if (!node.layoutNode) {\n // Save previous layout for change detection\n node.prevLayout = node.boxRect\n const rect: Rect = {\n x: parentX,\n y: parentY,\n width: 0,\n height: 0,\n }\n node.boxRect = rect\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, incrementalSkip)\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\n // Container-level layout skip: if incremental mode is enabled and this\n // node's Flexily-computed rect matches the existing boxRect, the entire\n // subtree is unchanged. Skip propagation — all descendants retain correct\n // prevLayout, boxRect, and layoutChangedThisFrame (stale epoch) from the\n // previous frame.\n //\n // This check is O(1) per node (4 number comparisons + 1 epoch check) and\n // prunes entire subtrees, converting propagateLayout from O(N) to O(changed).\n // Note: prevLayout is already synced to boxRect by syncPrevLayout() at\n // the end of the previous render pass, so skipping is safe.\n //\n // subtreeDirtyEpoch guard: even when this node's rect is unchanged, a\n // descendant may need processing (e.g., new child mounted via appendChild).\n // The reconciler's markSubtreeDirty propagates the current epoch upward,\n // so checking subtreeDirtyEpoch ensures we don't skip over dirty descendants.\n if (\n incrementalSkip &&\n node.boxRect &&\n !isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT) &&\n !isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT)\n ) {\n if (\n rect.x === node.boxRect.x &&\n rect.y === node.boxRect.y &&\n rect.width === node.boxRect.width &&\n rect.height === node.boxRect.height\n ) {\n return\n }\n }\n\n // Save previous layout for change detection (must happen AFTER the skip\n // check above — skipped nodes don't need prevLayout updated since\n // syncPrevLayout already set prevLayout = boxRect after the previous frame)\n node.prevLayout = node.boxRect\n node.boxRect = rect\n\n // Set authoritative \"layout changed this frame\" epoch stamp.\n // Unlike !rectEqual(prevLayout, boxRect) which becomes stale when\n // layout phase skips on subsequent frames, this epoch is explicitly set\n // each time propagateLayout runs and expires when the render epoch advances.\n const layoutDidChange = !!(node.prevLayout && !rectEqual(node.prevLayout, node.boxRect))\n node.layoutChangedThisFrame = layoutDidChange ? getRenderEpoch() : INITIAL_EPOCH\n\n // STRICT invariant: if layoutChangedThisFrame is current epoch, prevLayout must differ from boxRect.\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 && isCurrentEpoch(node.layoutChangedThisFrame)) {\n if (rectEqual(node.prevLayout, node.boxRect)) {\n const props = node.props as BoxProps\n throw new Error(\n `[SILVERY_STRICT] layoutChangedThisFrame=true but prevLayout equals boxRect ` +\n `(node: ${props.id ?? node.type}, rect: ${JSON.stringify(node.boxRect)})`,\n )\n }\n }\n\n // When layout changes, mark ancestors subtreeDirty so renderPhase 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 (isCurrentEpoch(node.layoutChangedThisFrame)) {\n const epoch = getRenderEpoch()\n let ancestor = node.parent\n while (ancestor && !isDirty(ancestor.dirtyBits, ancestor.dirtyEpoch, SUBTREE_BIT)) {\n if (ancestor.dirtyEpoch !== epoch) {\n ancestor.dirtyBits = SUBTREE_BIT\n ancestor.dirtyEpoch = epoch\n } else {\n ancestor.dirtyBits |= SUBTREE_BIT\n }\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, incrementalSkip)\n }\n\n // Cache cascade inputs that render-phase would otherwise compute via tree walks.\n // Both checks require children to have finalized layoutChangedThisFrame, boxRect,\n // prevLayout, childrenDirtyEpoch, and subtreeDirtyEpoch — all set above.\n // Guard: only compute when subtreeDirty (matches buildCascadeInputs guard).\n if (isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT) && node.children.length > 0) {\n const epoch = getRenderEpoch()\n\n // absoluteChildMutated: check direct children for absolute-positioned nodes\n // that had structural changes (children mount/unmount/reorder, layout change,\n // child position shift).\n const absChild = _hasAbsoluteChildMutated(node.children)\n\n // descendantOverflowChanged: recursive check for descendants whose prevLayout\n // extended beyond THIS node's rect and had layoutChangedThisFrame.\n const descOverflow = _hasDescendantOverflowChanged(node, rect)\n\n // Set or clear the layout-phase bits\n let bits = node.dirtyBits\n if (absChild) bits |= ABS_CHILD_BIT\n else bits &= ~ABS_CHILD_BIT\n if (descOverflow) bits |= DESC_OVERFLOW_BIT\n else bits &= ~DESC_OVERFLOW_BIT\n node.dirtyBits = bits\n node.dirtyEpoch = epoch\n } else {\n // Clear layout-phase bits (keep reconciler bits intact)\n if (node.dirtyEpoch === getRenderEpoch()) {\n node.dirtyBits &= ~(ABS_CHILD_BIT | DESC_OVERFLOW_BIT)\n }\n }\n}\n\n/**\n * Lightweight cascade input caching when the layout phase skips.\n *\n * When no layout nodes are dirty and dimensions haven't changed,\n * `layoutPhase` returns early and `propagateLayout` never runs.\n * But structural changes (absolute child mount/unmount, descendant overflow)\n * still need cascade input bits (ABS_CHILD_BIT, DESC_OVERFLOW_BIT) to be\n * computed for the render phase.\n *\n * This traversal follows only subtreeDirty paths (O(changed) not O(N))\n * and computes the same cascade inputs as propagateLayout's caching block.\n * No layout changes, no prevLayout updates, no layoutChangedThisFrame.\n */\nfunction propagateCascadeInputs(node: AgNode): void {\n if (!isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT)) return\n if (!node.children || node.children.length === 0) return\n\n // Recurse into dirty children first (they need their own cascade inputs)\n for (const child of node.children) {\n if (isDirty(child.dirtyBits, child.dirtyEpoch, SUBTREE_BIT)) {\n propagateCascadeInputs(child)\n }\n }\n\n // Compute cascade inputs for this node (same logic as in propagateLayout)\n const epoch = getRenderEpoch()\n const absChild = _hasAbsoluteChildMutated(node.children)\n const descOverflow = node.boxRect ? _hasDescendantOverflowChanged(node, node.boxRect) : false\n\n let bits = node.dirtyBits\n if (absChild) bits |= ABS_CHILD_BIT\n else bits &= ~ABS_CHILD_BIT\n if (descOverflow) bits |= DESC_OVERFLOW_BIT\n else bits &= ~DESC_OVERFLOW_BIT\n node.dirtyBits = bits\n node.dirtyEpoch = epoch\n}\n\n/**\n * Check if any direct child is position=\"absolute\" and had structural changes.\n */\nfunction _hasAbsoluteChildMutated(children: readonly AgNode[]): boolean {\n for (const child of children) {\n const cp = child.props as BoxProps\n if (\n cp.position === \"absolute\" &&\n (isDirty(child.dirtyBits, child.dirtyEpoch, CHILDREN_BIT) ||\n isCurrentEpoch(child.layoutChangedThisFrame) ||\n _hasChildPositionChanged(child))\n ) {\n return true\n }\n }\n return false\n}\n\n/**\n * Check if any child's position changed (boxRect vs prevLayout).\n */\nfunction _hasChildPositionChanged(node: AgNode): boolean {\n for (const child of node.children) {\n if (child.boxRect && child.prevLayout) {\n if (child.boxRect.x !== child.prevLayout.x || child.boxRect.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: follows subtreeDirty paths for efficiency.\n */\nfunction _hasDescendantOverflowChanged(node: AgNode, rect: Rect): boolean {\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 if (child.prevLayout && isCurrentEpoch(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 if (isDirty(child.dirtyBits, child.dirtyEpoch, SUBTREE_BIT) && 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 * Notify all layout subscribers of dimension changes.\n *\n * Called by the pipeline AFTER scrollrectPhase completes,\n * so useScrollRect can read correct screen positions.\n *\n * Notifies when EITHER boxRect, scrollRect, or screenRect changed.\n * scrollRect can change from scroll offset changes even when\n * boxRect stays the same — subscribers (like useScrollRect)\n * need notification in both cases. screenRect can change from sticky\n * offset changes even when scrollRect 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.boxRect)\n const screenChanged = !rectEqual(node.prevScrollRect, node.scrollRect)\n const renderChanged = !rectEqual(node.prevScreenRect, node.screenRect)\n // Sync rect values into alien-signals (for signal-based hooks).\n // Always sync — even when no rect changed — because the signal may\n // have been created after the last sync (lazy initialization).\n syncRectSignals(node)\n\n // Recurse to children\n for (const child of node.children) {\n notifyLayoutSubscribers(child)\n }\n}\n\n// ============================================================================\n// STRICT Layout Overflow Invariant\n// ============================================================================\n\n/**\n * Verify that no child's boxRect.width exceeds its parent's inner content width.\n *\n * This catches fit-content/snug-content bugs at the source — any measure-phase\n * or correction-pass error fires immediately.\n *\n * - SILVERY_STRICT=1: console.warn on violation\n * - SILVERY_STRICT=2: throw on violation\n *\n * Exceptions:\n * - Parent has overflow: \"scroll\" or \"hidden\" (overflow is allowed)\n * - Child has position: \"absolute\" (absolute nodes can overflow)\n */\nexport function strictLayoutOverflowCheck(root: AgNode): void {\n const strict = process?.env?.SILVERY_STRICT\n if (!strict) return\n\n const shouldThrow = strict === \"2\"\n\n function walk(node: AgNode): void {\n for (const child of node.children) {\n if (child.boxRect && node.boxRect) {\n const childProps = child.props as BoxProps\n\n // Skip absolute-positioned children — they're allowed to overflow\n if (childProps.position === \"absolute\") {\n walk(child)\n continue\n }\n\n const parentProps = node.props as BoxProps\n\n // Skip if parent allows overflow (scroll or hidden)\n if (parentProps.overflow === \"scroll\" || parentProps.overflow === \"hidden\") {\n walk(child)\n continue\n }\n\n // Compute parent's inner content width\n const border = parentProps.borderStyle ? getBorderSize(parentProps) : { top: 0, bottom: 0, left: 0, right: 0 }\n const padding = getPadding(parentProps)\n const parentInnerWidth = node.boxRect.width - padding.left - padding.right - border.left - border.right\n\n if (child.boxRect.width > parentInnerWidth) {\n const childId = (childProps as any).id ?? child.type\n const parentId = (parentProps as any).id ?? node.type\n const msg =\n `[SILVERY_STRICT] Layout overflow: child \"${childId}\" width ${child.boxRect.width} ` +\n `exceeds parent \"${parentId}\" inner width ${parentInnerWidth} ` +\n `(parent box: ${node.boxRect.width}, border: ${border.left}+${border.right}, padding: ${padding.left}+${padding.right})`\n\n if (shouldThrow) {\n throw new Error(msg)\n } else {\n console.warn(msg)\n }\n }\n }\n\n walk(child)\n }\n }\n\n walk(root)\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.boxRect\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.boxRect) continue\n\n const childTop = child.boxRect.y - layout.y - border.top - padding.top\n const childBottom = childTop + child.boxRect.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 }\n\n // Clamp to valid range — applies to both scrollTo and explicit scrollOffset.\n // Without this, explicit scrollOffset can scroll past content into blank space.\n scrollOffset = Math.max(0, scrollOffset)\n scrollOffset = Math.min(scrollOffset, Math.max(0, contentHeight - viewportHeight))\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, renderPhase 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 const epoch = getRenderEpoch()\n if (node.dirtyEpoch !== epoch) {\n node.dirtyBits = SUBTREE_BIT\n node.dirtyEpoch = epoch\n } else {\n node.dirtyBits |= SUBTREE_BIT\n }\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 const epoch = getRenderEpoch()\n if (node.dirtyEpoch !== epoch) {\n node.dirtyBits = SUBTREE_BIT\n node.dirtyEpoch = epoch\n } else {\n node.dirtyBits |= SUBTREE_BIT\n }\n }\n return\n }\n\n const layout = node.boxRect\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.boxRect) continue\n\n // Natural position relative to parent content area\n const naturalY = child.boxRect.y - layout.y - border.top - padding.top\n const childHeight = child.boxRect.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 const epoch = getRenderEpoch()\n if (node.dirtyEpoch !== epoch) {\n node.dirtyBits = SUBTREE_BIT\n node.dirtyEpoch = epoch\n } else {\n node.dirtyBits |= SUBTREE_BIT\n }\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 `screenRect` which accounts for sticky render offsets.\n * For non-sticky nodes, screenRect === scrollRect. For sticky nodes,\n * screenRect reflects the actual pixel position where the node is painted.\n *\n * Screen position = content position - sum of ancestor scroll offsets\n */\nexport function scrollrectPhase(root: AgNode): void {\n propagateScrollRect(root, 0)\n}\n\n/**\n * Fast path for scrollrectPhase when no scroll containers or sticky nodes exist.\n *\n * When there are no scroll containers and no sticky nodes, ancestorScrollOffset\n * is always 0, so scrollRect === boxRect and screenRect === scrollRect. This\n * avoids the overhead of accumulating scroll offsets through the tree.\n */\nexport function scrollrectPhaseSimple(root: AgNode): void {\n propagateScrollRectSimple(root)\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 propagateScrollRect(node: AgNode, ancestorScrollOffset: number): void {\n // Save previous rects for change detection in notifyLayoutSubscribers\n node.prevScrollRect = node.scrollRect\n node.prevScreenRect = node.screenRect\n\n const content = node.boxRect\n if (!content) {\n node.scrollRect = null\n node.screenRect = null\n for (const child of node.children) {\n propagateScrollRect(child, ancestorScrollOffset)\n }\n return\n }\n\n // Compute screen position by subtracting ancestor scroll offsets\n node.scrollRect = {\n x: content.x,\n y: content.y - ancestorScrollOffset,\n width: content.width,\n height: content.height,\n }\n\n // Default: screenRect equals scrollRect (overridden below for sticky nodes)\n node.screenRect = node.scrollRect\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 screenRect 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 computeStickyScreenRects(node)\n\n // Recurse to children\n for (const child of node.children) {\n propagateScrollRect(child, childScrollOffset)\n }\n}\n\n/**\n * Compute screenRect for sticky children of a node.\n *\n * For sticky children, the actual render position differs from the layout\n * position (scrollRect). The renderOffset from the scroll/sticky phase\n * determines where pixels are actually painted. This function sets\n * screenRect on those children to reflect the true screen position.\n *\n * @param parent The parent node whose sticky children need screenRect computation\n */\nfunction computeStickyScreenRects(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 parentScrollRect = parent.scrollRect\n if (!parentScrollRect) 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 = parentScrollRect.y + border.top + padding.top\n\n for (const sticky of stickyList) {\n const child = parent.children[sticky.index]\n if (!child?.scrollRect) continue\n\n // screenRect has the same x, width, height as scrollRect,\n // but Y is adjusted to the sticky render position\n child.screenRect = {\n x: child.scrollRect.x,\n y: contentOriginY + sticky.renderOffset,\n width: child.scrollRect.width,\n height: child.scrollRect.height,\n }\n }\n}\n\n// ============================================================================\n// Simple scrollRect propagation (no scroll/sticky)\n// ============================================================================\n\n/**\n * Simple scrollRect propagation for trees without scroll containers or sticky nodes.\n * When ancestorScrollOffset is always 0, scrollRect === boxRect and screenRect === scrollRect.\n * Saves the overhead of accumulating scroll offsets and computing sticky screen rects.\n */\nfunction propagateScrollRectSimple(node: AgNode): void {\n node.prevScrollRect = node.scrollRect\n node.prevScreenRect = node.screenRect\n\n const content = node.boxRect\n if (!content) {\n node.scrollRect = null\n node.screenRect = null\n for (const child of node.children) {\n propagateScrollRectSimple(child)\n }\n return\n }\n\n // No scroll offset — scrollRect equals boxRect\n node.scrollRect = {\n x: content.x,\n y: content.y,\n width: content.width,\n height: content.height,\n }\n node.screenRect = node.scrollRect\n\n for (const child of node.children) {\n propagateScrollRectSimple(child)\n }\n}\n\n// ============================================================================\n// Feature Detection\n// ============================================================================\n\n/**\n * Pipeline feature flags — tracks which optional phases the tree needs.\n *\n * Flags are one-way: once set to true, they stay true for the lifetime\n * of the Ag instance. This ensures that if a component dynamically mounts\n * a scroll container or sticky child, the phase starts running immediately\n * and never gets skipped again.\n */\nexport interface PipelineFeatures {\n /** Tree contains at least one `overflow=\"scroll\"` node. */\n hasScroll: boolean\n /** Tree contains at least one `position=\"sticky\"` node. */\n hasSticky: boolean\n}\n\n/**\n * Scan the tree for features that require optional pipeline phases.\n *\n * Returns feature flags. This is called on every layout pass so newly\n * mounted components are detected. The caller should merge flags with\n * one-way semantics (false → true, never true → false).\n */\nexport function detectPipelineFeatures(root: AgNode): PipelineFeatures {\n let hasScroll = false\n let hasSticky = false\n\n function scan(node: AgNode): void {\n const props = node.props as BoxProps\n if (props.overflow === \"scroll\") hasScroll = true\n if (props.position === \"sticky\") hasSticky = true\n // Early exit if both features detected\n if (hasScroll && hasSticky) return\n for (const child of node.children) {\n scan(child)\n if (hasScroll && hasSticky) return\n }\n }\n\n scan(root)\n return { hasScroll, hasSticky }\n}\n","/**\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 { getActiveColorLevel, getActiveTheme } from \"@silvery/theme/state\"\nimport { resolveThemeColor } from \"@silvery/theme/resolve\"\nimport { monoAttrsForColorString, type MonoAttr } from \"@silvery/ansi\"\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 // \"currentColor\" is a CSS synonym — both keywords resolve identically here.\n // For child-cascade purposes, render-phase detects these keywords directly\n // (before parseColor) so the parent's inheritedFg is preserved in children.\n if (color === \"inherit\" || color === \"currentColor\") 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 // At monochrome tier, strip all token-resolved colors. Hierarchy is carried\n // by per-token SGR attrs (see getTextStyle → monoAttrsForColorString). The\n // output phase sees `null` and emits SGR 39/49 (terminal default), never\n // an RGB sequence.\n if (getActiveColorLevel() === \"none\") return null\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 * Collect monochrome attrs from a color string (`\"$primary\"` → `[\"bold\"]`).\n *\n * At mono tier, `parseColor` strips the color (returns `null`). The hierarchy\n * signal lives in the attrs bag. This helper merges the mapped attrs from\n * `DEFAULT_MONO_ATTRS` into a mutable accumulator. Called per color-carrying\n * prop in `getTextStyle`.\n *\n * No-op when the color is not a `$token` — non-token hex / named colors\n * pass through with no attrs (spec: \"apps that hardcoded #FF0000 get nothing\").\n */\nfunction collectMonoAttrs(color: string | undefined, into: Set<MonoAttr>): void {\n if (!color) return\n const attrs = monoAttrsForColorString(color, getActiveTheme())\n if (!attrs) return\n for (const a of attrs) into.add(a)\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 // Start with the user-specified attrs.\n let bold = props.bold\n let dim = props.dim || props.dimColor // dimColor is Ink compatibility alias\n let italic = props.italic\n let underline = props.underline || !!underlineStyle\n let strikethrough = props.strikethrough\n let inverse = props.inverse\n\n // Monochrome tier: inject per-token SGR attrs from DEFAULT_MONO_ATTRS. Colors\n // are stripped by parseColor (returns null for $tokens at mono tier). The\n // attrs carry the hierarchy: $primary → bold, $muted → dim, $error →\n // bold+inverse, $link → underline, etc. User-supplied attrs always OR-in.\n if (getActiveColorLevel() === \"none\") {\n const monoAttrs = new Set<MonoAttr>()\n collectMonoAttrs(props.color, monoAttrs)\n collectMonoAttrs(props.backgroundColor, monoAttrs)\n if (monoAttrs.has(\"bold\")) bold = true\n if (monoAttrs.has(\"dim\")) dim = true\n if (monoAttrs.has(\"italic\")) italic = true\n if (monoAttrs.has(\"underline\")) {\n underline = true\n if (!underlineStyle) underlineStyle = \"single\"\n }\n if (monoAttrs.has(\"strikethrough\")) strikethrough = true\n if (monoAttrs.has(\"inverse\")) inverse = true\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,\n dim,\n italic,\n underline,\n underlineStyle,\n strikethrough,\n 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","/**\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 {\n getCachedPlainText,\n setCachedPlainText,\n getCachedCollectedText,\n setCachedCollectedText,\n getCachedFormat,\n setCachedFormat,\n getCachedAnalysis,\n setCachedAnalysis,\n} from \"./prepared-text\"\nimport { buildTextAnalysis, balancedWidth as computeBalancedWidth, optimalWrap } from \"./pretext\"\nimport type { BgConflictMode, NodeRenderState, PipelineContext } from \"./types\"\nimport { createLogger } from \"loggily\"\n\nconst log = createLogger(\"silvery:content\")\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 = typeof process !== \"undefined\" ? process.env.SILVERY_BG_CONFLICT?.toLowerCase() : undefined\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/** Format a Color value for bg conflict diagnostics */\nfunction formatBgConflictColor(c: number | { r: number; g: number; b: number } | null | undefined): string {\n if (c === null || c === undefined) return \"none\"\n if (typeof c === \"number\") {\n // Packed RGB (0x1000000 marker) or ANSI palette index\n if (c & 0x1000000) {\n const r = (c >> 16) & 0xff\n const g = (c >> 8) & 0xff\n const b = c & 0xff\n return `#${r.toString(16).padStart(2, \"0\")}${g.toString(16).padStart(2, \"0\")}${b.toString(16).padStart(2, \"0\")}`\n }\n // Map SGR codes to names for readability\n const names: Record<number, string> = {\n 40: \"black\",\n 41: \"red\",\n 42: \"green\",\n 43: \"yellow\",\n 44: \"blue\",\n 45: \"magenta\",\n 46: \"cyan\",\n 47: \"white\",\n 100: \"brightBlack\",\n 101: \"brightRed\",\n 102: \"brightGreen\",\n 103: \"brightYellow\",\n 104: \"brightBlue\",\n 105: \"brightMagenta\",\n 106: \"brightCyan\",\n 107: \"brightWhite\",\n }\n return names[c] ?? `palette(${c})`\n }\n return `rgb(${c.r},${c.g},${c.b})`\n}\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 underlineStyle?: string | false\n underlineColor?: string\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 parts: string[] = []\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 parts.push(`38;5;${color}`)\n } else {\n parts.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) parts.push(\"1\")\n if (style.dim) parts.push(\"2\")\n if (style.italic) parts.push(\"3\")\n // Underline: prefer underlineStyle (SGR 4:x subparam) over boolean (SGR 4)\n if (style.underlineStyle) {\n const styleMap: Record<string, string> = {\n single: \"4:1\",\n double: \"4:2\",\n curly: \"4:3\",\n dotted: \"4:4\",\n dashed: \"4:5\",\n }\n parts.push(styleMap[style.underlineStyle] ?? \"4\")\n } else if (style.underline) {\n parts.push(\"4\")\n }\n // Underline color (SGR 58;5;N or 58;2;r;g;b).\n // \"currentColor\"/\"inherit\" → resolve to the merged fg (style.color), so the\n // underline tracks whatever color the surrounding text ended up as.\n if (style.underlineColor) {\n const underlineSource =\n style.underlineColor === \"currentColor\" || style.underlineColor === \"inherit\"\n ? style.color\n : style.underlineColor\n if (underlineSource) {\n const ulColor = parseColor(underlineSource)\n if (ulColor !== null) {\n if (typeof ulColor === \"number\") {\n parts.push(`58;5;${ulColor}`)\n } else {\n parts.push(`58;2;${ulColor.r};${ulColor.g};${ulColor.b}`)\n }\n }\n }\n }\n if (style.inverse) parts.push(\"7\")\n if (style.strikethrough) parts.push(\"9\")\n\n if (parts.length === 0) {\n return \"\"\n }\n\n return `\\x1b[${parts.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 // color=\"inherit\"/\"currentColor\" on a virtual-text child is a pass-through:\n // the child's effective color is whatever the parent already resolved to.\n // Without this, nested <Text color=\"inherit\"> clobbered the merged context\n // with the raw keyword, which styleToAnsi then mapped to null (no fg SGR).\n const isInheritKeyword = childProps.color === \"inherit\" || childProps.color === \"currentColor\"\n const effectiveChildColor = isInheritKeyword ? parent.color : childProps.color\n return {\n color: effectiveChildColor ?? 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 ?? (childProps as any).underlineStyle) ? true : parent.underline,\n underlineStyle: (childProps as any).underlineStyle ?? parent.underlineStyle,\n underlineColor: (childProps as any).underlineColor ?? parent.underlineColor,\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 // Hard wrap: character-level wrapping without regard to word boundaries.\n // Matches Ink's wrap=\"hard\" behavior (wrap-ansi with wordWrap=false), so\n // \"Hello World\" at width=7 becomes [\"Hello W\", \"orld\"] — the break lands\n // mid-word rather than at the space. Multi-line input is hard-wrapped\n // line-by-line; each line is repeatedly sliced by display width.\n if (wrap === \"hard\") {\n const sliceFn = ctx ? ctx.measurer.sliceByWidth : sliceByWidth\n const out: string[] = []\n for (const line of lines) {\n if (line === \"\") {\n out.push(\"\")\n continue\n }\n let remaining = line\n // Guard against infinite loops when sliceByWidth cannot advance\n // (e.g., a single grapheme wider than `width`). In that case, push\n // the remaining text as-is and break.\n while (getTextWidth(remaining, ctx) > width) {\n const head = sliceFn(remaining, width)\n if (head.length === 0) break\n out.push(head)\n remaining = remaining.slice(head.length)\n }\n out.push(remaining)\n }\n return out\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 // Optimal wrapping (Knuth-Plass): minimize total raggedness across all lines.\n // Uses dynamic programming over breakpoints for globally optimal line breaks.\n if (wrap === \"even\") {\n const gWidthFn = ctx?.measurer?.graphemeWidth?.bind(ctx.measurer) ?? graphemeWidth\n const analysis = buildTextAnalysis(normalizedText, gWidthFn)\n return optimalWrap(normalizedText, analysis, width)\n }\n\n // Balanced wrapping: disabled — the heuristic (totalWidth / lineCount) doesn't\n // reliably produce better results than optimal. It narrows the width (changing\n // line count) rather than optimizing break placement. Keep the algorithm in\n // pretext.ts for potential future use; just treat \"balanced\" as greedy here.\n\n // wrap === true or wrap === 'wrap' or wrap === 'balanced' - 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 minCol?: number,\n): number {\n if (hasAnsi(text)) {\n return renderAnsiTextLineReturn(buffer, x, y, text, baseStyle, maxCol, inheritedBg, ctx, minCol)\n }\n return renderGraphemes(buffer, splitGraphemes(text), x, y, baseStyle, maxCol, inheritedBg, ctx, minCol)\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 * @param minCol - Left edge of the visible region (inclusive). Graphemes\n * whose end position is at or before minCol are skipped (col still advances).\n * Used to clip text that overflows the LEFT edge of an overflow:hidden\n * container with a border (so the border isn't overwritten).\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 minCol?: number,\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 // Effective left boundary: max of clipBounds.left and 0 (no negative columns)\n const leftEdge = minCol !== undefined ? Math.max(minCol, 0) : 0\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 // Skip graphemes whose end is still left of leftEdge (still advance col).\n // This clips text that overflows the LEFT edge of an overflow:hidden\n // container — without this, the text would overwrite the parent's left\n // border or padding cells.\n if (col + width <= leftEdge) {\n col += width\n continue\n }\n\n // Partial overlap: a wide grapheme straddling the left edge. Replace with\n // a space at leftEdge so the visible cell is preserved without the\n // grapheme's continuation cell extending outside the clip region.\n if (col < leftEdge) {\n // Skip this grapheme (the visible portion is its right cell which we\n // can't draw without the leading half). Advance to leftEdge.\n col = leftEdge\n // Don't draw a partial wide char — fall through to the next grapheme.\n continue\n }\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 minCol?: number,\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 chalkBg = formatBgConflictColor(segment.bg)\n const silveryBg =\n baseStyle.bg !== null\n ? `Text.bg=${formatBgConflictColor(baseStyle.bg)}`\n : `bufferBg=${formatBgConflictColor(existingBufBg)}`\n // Show a snippet of the raw ANSI text around the conflict for debugging\n const textPreview = text.length > 80 ? text.slice(0, 80) + \"…\" : text\n const msg = `[silvery] Background conflict at (${col},${y}): chalk bg=${chalkBg} on silvery ${silveryBg}. Text: \"${preview}${segment.text.length > 30 ? \"…\" : \"\"}\". Raw ANSI (first 80): ${JSON.stringify(textPreview)}. Chalk bg will override only text characters, causing visual gaps in padding. Use ansi.bgOverride() to suppress if intentional.`\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 log.warn?.(msg)\n }\n }\n }\n\n col = renderGraphemes(buffer, splitGraphemes(segment.text), col, y, style, maxCol, inheritedBg, ctx, minCol)\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 // --- PreparedText cache: Level 0 (plain text for maxDisplayWidth) ---\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 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 cachedPlain = getCachedPlainText(node)\n let lineCount: number\n if (cachedPlain) {\n lineCount = cachedPlain.lineCount\n } else {\n const plainText = collectPlainText(node)\n lineCount = (plainText.match(/\\n/g)?.length ?? 0) + 1\n setCachedPlainText(node, plainText, lineCount)\n }\n maxDisplayWidth = (width + 1) * lineCount\n }\n\n // --- PreparedText cache: Level 1 (collected styled text) ---\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 let text: string\n let bgSegments: BgSegment[]\n let childSpans: ChildSpan[]\n\n // Seed the collection parent context with this Text's own effective props so\n // nested virtual <Text color=\"inherit\"> children can resolve \"inherit\" /\n // \"currentColor\" back to the owner Text's color. Without this, virtual\n // children only see \"{}\" and keyword lookups produced no fg.\n const rootContext: StyleContext = {\n color: props.color,\n backgroundColor: props.backgroundColor,\n bold: props.bold,\n dim: props.dim || props.dimColor,\n italic: props.italic,\n underline: !!(props.underline || props.underlineStyle),\n underlineStyle: props.underlineStyle,\n underlineColor: props.underlineColor,\n inverse: props.inverse,\n strikethrough: props.strikethrough,\n }\n\n const cachedCollected = getCachedCollectedText(node, maxDisplayWidth)\n if (cachedCollected) {\n text = cachedCollected.text\n bgSegments = cachedCollected.bgSegments as BgSegment[]\n childSpans = cachedCollected.childSpans as ChildSpan[]\n } else {\n const collected = collectTextWithBg(node, rootContext, 0, maxDisplayWidth, ctx)\n text = collected.text\n bgSegments = collected.bgSegments\n childSpans = collected.childSpans\n setCachedCollectedText(node, collected, maxDisplayWidth)\n }\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 // underlineColor=\"currentColor\"/\"inherit\" tracks the text's resolved fg.\n // getTextStyle already produced style.underlineColor = null for the keyword\n // (parseColor(\"currentColor\") === null). Upgrade it here — AFTER fg\n // inheritance has been applied — so the underline matches the visible text.\n if (props.underlineColor === \"currentColor\" || props.underlineColor === \"inherit\") {\n style.underlineColor = style.fg\n }\n\n // --- PreparedText cache: Level 2 (formatted lines per width) ---\n // When text has background color, preserve trailing spaces so bg covers them.\n const hasBg = style.bg !== null || bgSegments.length > 0 || (inheritedBg !== undefined && inheritedBg !== null)\n const trim = !hasBg\n const internalTransform = props.internal_transform\n\n let lines: string[]\n let lineOffsets: Array<{ start: number; end: number }>\n\n // Skip format cache when internal_transform is present (may depend on external state)\n const cachedFmt = !internalTransform ? getCachedFormat(node, width, props.wrap, trim) : null\n if (cachedFmt) {\n lines = cachedFmt.lines\n lineOffsets = cachedFmt.hasLineOffsets ? cachedFmt.lineOffsets : []\n } else {\n lines = formatTextLines(text, width, props.wrap, ctx, trim)\n if (internalTransform) {\n lines = lines.map((line, index) => internalTransform(line, index))\n }\n const needLineOffsets = bgSegments.length > 0 || childSpans.length > 0\n lineOffsets = needLineOffsets ? mapLinesToCharOffsets(text, lines, ctx) : []\n if (!internalTransform) {\n setCachedFormat(node, width, props.wrap, trim, lines, lineOffsets, needLineOffsets)\n }\n }\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 // Clip left edge to horizontal clip bounds. Without this, text rendered\n // by a node whose x is BEFORE the parent's clip-left (e.g., negative\n // marginLeft inside an overflow:hidden container with a border) would\n // overwrite the parent's left border or padding cells.\n const minCol = clipBounds && \"left\" in clipBounds && clipBounds.left !== undefined ? clipBounds.left : undefined\n const endCol = renderTextLineReturn(buffer, x, lineY, line, style, maxCol, inheritedBg, ctx, minCol)\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 // Respect minCol so we don't clear cells inside the parent's left border.\n const clearStart = minCol !== undefined ? Math.max(endCol, minCol) : endCol\n if (clearStart < maxCol) {\n const clearBg = inheritedBg ?? null\n for (let cx = clearStart; 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","/**\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 render-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 bgOnlyChange = false,\n inheritedFg?: 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 //\n // bgOnlyChange: when ONLY backgroundColor changed (no content/layout/children\n // changes), use fillBg() which updates bg without overwriting chars. This\n // preserves child content from the cloned buffer, enabling the cascade\n // optimization where clean children are skipped entirely.\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 if (bgOnlyChange) {\n buffer.fillBg(clippedX, clippedY, clippedWidth, clippedHeight, bg)\n } else {\n buffer.fill(clippedX, clippedY, clippedWidth, clippedHeight, { bg })\n }\n }\n } else {\n if (bgOnlyChange) {\n buffer.fillBg(x, y, width, height, bg)\n } else {\n buffer.fill(x, y, width, height, { bg })\n }\n }\n }\n\n // Render border if set\n if (props.borderStyle) {\n renderBorder(buffer, x, y, width, height, props, clipBounds, inheritedBg, inheritedFg)\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 inheritedFg?: Color | null,\n): void {\n const chars = getBorderChars(props.borderStyle ?? \"single\")\n // borderColor=\"currentColor\"/\"inherit\" resolves to the Box's own fg —\n // explicit props.color if set, else the inherited fg from the nearest\n // ancestor with a color. Mirrors CSS `border-color: currentColor`.\n let color: Color | null\n if (props.borderColor === \"currentColor\" || props.borderColor === \"inherit\") {\n color = props.color ? parseColor(props.color) : (inheritedFg ?? null)\n } else {\n color = props.borderColor ? parseColor(props.borderColor) : null\n }\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 baseBg = props.backgroundColor ? parseColor(props.backgroundColor) : (inheritedBg ?? null)\n\n // Per-side border background colors — each side falls back to the shorthand\n // borderBackgroundColor, then to the box's own bg / inherited bg.\n const borderBgStr = (props as BoxProps).borderBackgroundColor\n const borderBgBase = borderBgStr ? parseColor(borderBgStr) : baseBg\n const topBorderBgStr = (props as BoxProps).borderTopBackgroundColor\n const bottomBorderBgStr = (props as BoxProps).borderBottomBackgroundColor\n const leftBorderBgStr = (props as BoxProps).borderLeftBackgroundColor\n const rightBorderBgStr = (props as BoxProps).borderRightBackgroundColor\n const topBg = topBorderBgStr ? parseColor(topBorderBgStr) : borderBgBase\n const bottomBg = bottomBorderBgStr ? parseColor(bottomBorderBgStr) : borderBgBase\n const leftBg = leftBorderBgStr ? parseColor(leftBorderBgStr) : borderBgBase\n const rightBg = rightBorderBgStr ? parseColor(rightBorderBgStr) : borderBgBase\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 — corners use the bg of the horizontal side (top/bottom)\n if (showTop && isRowVisible(y)) {\n if (showLeft && isColVisible(x)) buffer.setCell(x, y, { char: chars.topLeft, fg: color, bg: topBg })\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: topBg })\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: topBg })\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: leftBg })\n if (showRight && x + width - 1 < buffer.width && isColVisible(x + width - 1)) {\n buffer.setCell(x + width - 1, row, { char: rightVertical, fg: color, bg: rightBg })\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: bottomBg })\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: bottomBg })\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: bottomBg,\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 OUTSIDE the box — one cell beyond each edge, in the gap/margin\n * space between siblings. This matches CSS `outline` semantics.\n *\n * The outline occupies cells at (x-1, y-1) through (x+width, y+height) —\n * entirely outside the box's own rect. Content is never overlapped.\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 // Outline draws OUTSIDE the box: one cell beyond each edge\n const ox = x - 1 // outline left column\n const oy = y - 1 // outline top row\n const ow = width + 2 // outline total width\n const oh = height + 2 // outline total height\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 (one row above the box)\n if (showTop && isRowVisible(oy)) {\n if (showLeft && isColVisible(ox)) buffer.setCell(ox, oy, { char: chars.topLeft, fg: color, bg, attrs })\n for (let col = ox + 1; col < ox + ow - 1 && col < buffer.width; col++) {\n if (isColVisible(col)) buffer.setCell(col, oy, { char: chars.horizontal, fg: color, bg, attrs })\n }\n if (showRight && ox + ow - 1 < buffer.width && isColVisible(ox + ow - 1)) {\n buffer.setCell(ox + ow - 1, oy, { char: chars.topRight, fg: color, bg, attrs })\n }\n }\n\n // Side borders — run along the box's own height (y to y+height-1)\n const outlineRightVertical = chars.rightVertical ?? chars.vertical\n const sideStart = showTop ? oy + 1 : oy\n const sideEnd = showBottom ? oy + oh - 1 : oy + oh\n for (let row = sideStart; row < sideEnd; row++) {\n if (!isRowVisible(row)) continue\n if (showLeft && isColVisible(ox)) buffer.setCell(ox, row, { char: chars.vertical, fg: color, bg, attrs })\n if (showRight && ox + ow - 1 < buffer.width && isColVisible(ox + ow - 1)) {\n buffer.setCell(ox + ow - 1, row, { char: outlineRightVertical, fg: color, bg, attrs })\n }\n }\n\n // Bottom border (one row below the box)\n const outlineBottomHorizontal = chars.bottomHorizontal ?? chars.horizontal\n const bottomY = oy + oh - 1\n if (showBottom && isRowVisible(bottomY)) {\n if (showLeft && isColVisible(ox)) {\n buffer.setCell(ox, bottomY, { char: chars.bottomLeft, fg: color, bg, attrs })\n }\n for (let col = ox + 1; col < ox + ow - 1 && col < buffer.width; col++) {\n if (isColVisible(col)) buffer.setCell(col, bottomY, { char: outlineBottomHorizontal, fg: color, bg, attrs })\n }\n if (showRight && ox + ow - 1 < buffer.width && isColVisible(ox + ow - 1)) {\n buffer.setCell(ox + ow - 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","/**\n * Phase 3.5: Decoration Phase\n *\n * Draws \"outside\" decorations — content that writes pixels OUTSIDE the owning\n * node's rect, into the parent's pixel space. Currently handles outlines.\n *\n * ## Why a separate phase?\n *\n * Outlines draw 1 cell beyond each edge of a Box. Those cells are NOT part of\n * the box's own region — they live in the parent's or even grandparent's\n * space. This breaks the per-node incremental dirty cascade:\n * - The box can be clean while its outline changed\n * - The outline pixels are not in any single node's \"region\"\n * - Clearing on removal requires knowing where the outline previously drew\n *\n * Instead of folding outlines into the cascade (which produced subtle\n * false-positive bugs at realistic scale), we treat them as a separate\n * decoration pass that runs AFTER content rendering on every frame:\n *\n * 1. Before content: restore cells under previous frame's outlines\n * (using snapshots captured when we drew them last time)\n * 2. Content render runs as normal — no outline-specific tracking\n * 3. After content: walk the tree, draw outlines for every node with\n * `outlineStyle`, snapshotting each written cell so the next frame\n * can restore it\n *\n * The snapshots are stored on the TerminalBuffer and travel with it via\n * clone(), so the cascade is naturally fed by the same prevBuffer that\n * drives incremental rendering.\n *\n * This pattern generalizes to other overlays (focus rings, hover halos,\n * selection borders) — any decoration that draws outside its owning node.\n */\n\nimport type { TerminalBuffer, Cell, Color } from \"../buffer\"\nimport type { BoxProps, AgNode } from \"@silvery/ag/types\"\nimport type { Theme } from \"@silvery/theme/types\"\nimport { getBorderSize, getPadding } from \"./helpers\"\nimport { renderOutline, getEffectiveBg } from \"./render-box\"\nimport { parseColor } from \"./render-helpers\"\nimport type { ClipBounds } from \"./types\"\n\n/**\n * Snapshot of a single cell, used to restore it when the overlaying outline\n * is removed on the next frame. Captures the full cell state so restore is\n * byte-identical to what was there before we drew the outline.\n */\nexport interface OutlineCellSnapshot {\n x: number\n y: number\n cell: Cell\n}\n\n/**\n * Restore cells at previously-drawn outline positions to their pre-outline\n * state. Called at the start of each incremental render, before the content\n * phase, on the cloned buffer. No-op when there are no previous snapshots\n * (fresh render or no outlines last frame).\n */\nexport function clearPreviousOutlines(buffer: TerminalBuffer): void {\n const snapshots = buffer.outlineSnapshots\n if (!snapshots || snapshots.length === 0) return\n for (const snap of snapshots) {\n buffer.setCell(snap.x, snap.y, snap.cell)\n }\n buffer.outlineSnapshots = []\n}\n\n/**\n * Walk the node tree, drawing outlines for every node with `outlineStyle`.\n * Captures per-cell snapshots so the next frame can restore these positions.\n *\n * Called AFTER the content render phase on every frame (both fresh and\n * incremental). Mirrors `renderNodeToBuffer`'s state threading for scroll\n * offsets, clip bounds, and inherited background — but does nothing except\n * visit the tree and draw outlines.\n */\nexport function renderDecorationPass(buffer: TerminalBuffer, root: AgNode): void {\n const snapshots: OutlineCellSnapshot[] = []\n walk(root, buffer, 0, undefined, { color: null }, snapshots)\n buffer.outlineSnapshots = snapshots\n}\n\n/**\n * Recursive tree walk. Each invocation corresponds to the state at a single\n * node — scroll offset, clip bounds, inherited background — matching what\n * `renderNodeToBuffer` would have threaded through its `NodeRenderState`.\n */\nfunction walk(\n node: AgNode,\n buffer: TerminalBuffer,\n scrollOffset: number,\n clipBounds: ClipBounds | undefined,\n inheritedBg: { color: Color },\n snapshots: OutlineCellSnapshot[],\n): void {\n // Virtual text nodes have no layout — they're rendered by their parent's\n // text collection pass. Nothing to draw and no children to descend.\n if (!node.layoutNode) return\n const layout = node.boxRect\n if (!layout) return\n\n // Respect the same visibility gates as the content phase.\n if (node.hidden) return\n const props = node.props as BoxProps\n if (props.display === \"none\") return\n\n // Apply scroll offset to the effective y position.\n const y = layout.y - scrollOffset\n\n // Off-screen viewport clipping — mirrors renderNodeToBuffer. Keeps the\n // walker cheap: large scroll containers with many hidden children return\n // early for any node entirely outside the visible window.\n if (y >= buffer.height || y + layout.height <= 0) return\n\n // Compute the effective background the outline should inherit from this\n // box — matches the `boxInheritedBg` calculation in renderOwnContent.\n const effectiveBg = getEffectiveBg(props)\n const theme = props.theme as Theme | undefined\n const childInheritedBg: { color: Color } = effectiveBg\n ? { color: parseColor(effectiveBg) }\n : theme\n ? { color: parseColor(theme.bg) }\n : inheritedBg\n\n // Draw the outline AFTER content — this means we paint on top of whatever\n // siblings rendered in the gap around this box. The snapshot captures\n // those pre-outline pixels so the next frame can restore them.\n if (node.type === \"silvery-box\" && props.outlineStyle) {\n const boxInheritedBg = effectiveBg ? undefined : inheritedBg.color\n const positions = collectOutlineCells(layout.x, y, layout.width, layout.height, props, clipBounds, buffer)\n for (const pos of positions) {\n // Snapshot the cell BEFORE the outline overwrites it.\n snapshots.push({ x: pos.x, y: pos.y, cell: buffer.getCell(pos.x, pos.y) })\n }\n renderOutline(buffer, layout.x, y, layout.width, layout.height, props, clipBounds, boxInheritedBg)\n }\n\n // Descend into children with the appropriate state. Scroll containers\n // override scrollOffset for their normal-flow children; sticky children\n // use their computed sticky offset. Clip bounds tighten for overflow\n // containers.\n if (node.children.length === 0) return\n\n const isScrollContainer = props.overflow === \"scroll\" && node.scrollState\n const clipX = (props.overflowX ?? props.overflow) === \"hidden\"\n const clipY = (props.overflowY ?? props.overflow) === \"hidden\"\n\n if (isScrollContainer) {\n const ss = node.scrollState!\n const childClip = computeChildClip(layout, props, clipBounds, 0, false, true)\n // Non-sticky children: rendered with container scroll offset.\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i]\n if (!child) continue\n const cp = child.props as BoxProps\n if (cp.position === \"sticky\") continue\n if (i < ss.firstVisibleChild || i > ss.lastVisibleChild) continue\n walk(child, buffer, ss.offset, childClip, childInheritedBg, snapshots)\n }\n // Sticky children: rendered at their computed sticky offset.\n if (ss.stickyChildren) {\n for (const sticky of ss.stickyChildren) {\n const child = node.children[sticky.index]\n if (!child) continue\n const stickyOffset = sticky.naturalTop - sticky.renderOffset\n walk(child, buffer, stickyOffset, childClip, childInheritedBg, snapshots)\n }\n }\n } else {\n const childClip = clipX || clipY ? computeChildClip(layout, props, clipBounds, scrollOffset, clipX, clipY) : clipBounds\n for (const child of node.children) {\n walk(child, buffer, scrollOffset, childClip, childInheritedBg, snapshots)\n }\n }\n}\n\n/**\n * Compute the cells an outline would write, given the same inputs\n * `renderOutline` uses. Kept in lockstep with render-box.ts — any change to\n * the outline geometry (e.g., new outline styles, per-side toggles) must be\n * mirrored here. If they drift, the snapshots won't cover every cell the\n * next `renderOutline` call writes, and stale pixels will leak through.\n *\n * Uses the SAME visibility checks as `renderOutline` so the snapshot set is\n * an exact match for the cell set the renderer will overwrite.\n */\nfunction collectOutlineCells(\n x: number,\n y: number,\n width: number,\n height: number,\n props: BoxProps,\n clipBounds: ClipBounds | undefined,\n buffer: TerminalBuffer,\n): { x: number; y: number }[] {\n const out: { x: number; y: number }[] = []\n const ox = x - 1\n const oy = y - 1\n const ow = width + 2\n const oh = height + 2\n\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 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 row\n if (showTop && isRowVisible(oy)) {\n if (showLeft && isColVisible(ox)) out.push({ x: ox, y: oy })\n for (let col = ox + 1; col < ox + ow - 1 && col < buffer.width; col++) {\n if (isColVisible(col)) out.push({ x: col, y: oy })\n }\n if (showRight && ox + ow - 1 < buffer.width && isColVisible(ox + ow - 1)) {\n out.push({ x: ox + ow - 1, y: oy })\n }\n }\n\n // Sides\n const sideStart = showTop ? oy + 1 : oy\n const sideEnd = showBottom ? oy + oh - 1 : oy + oh\n for (let row = sideStart; row < sideEnd; row++) {\n if (!isRowVisible(row)) continue\n if (showLeft && isColVisible(ox)) out.push({ x: ox, y: row })\n if (showRight && ox + ow - 1 < buffer.width && isColVisible(ox + ow - 1)) {\n out.push({ x: ox + ow - 1, y: row })\n }\n }\n\n // Bottom row\n const bottomY = oy + oh - 1\n if (showBottom && isRowVisible(bottomY)) {\n if (showLeft && isColVisible(ox)) out.push({ x: ox, y: bottomY })\n for (let col = ox + 1; col < ox + ow - 1 && col < buffer.width; col++) {\n if (isColVisible(col)) out.push({ x: col, y: bottomY })\n }\n if (showRight && ox + ow - 1 < buffer.width && isColVisible(ox + ow - 1)) {\n out.push({ x: ox + ow - 1, y: bottomY })\n }\n }\n\n return out\n}\n\n/**\n * Local copy of `computeChildClipBounds` from render-phase.ts. The decoration\n * walker can't import private render-phase helpers without tangling modules,\n * so we reproduce the same computation here. Must stay in sync.\n */\nfunction computeChildClip(\n layout: NonNullable<AgNode[\"boxRect\"]>,\n props: BoxProps,\n parentClip: ClipBounds | undefined,\n scrollOffset: number,\n horizontal: boolean,\n vertical: boolean,\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 result.left = parentClip.left\n result.right = parentClip.right\n }\n return result\n}\n","/**\n * Cascade Predicates — Pure boolean logic extracted from renderNodeToBuffer.\n *\n * TEST/STRICT-ONLY ORACLE: In production, the reactive system (alien-signals)\n * drives cascade computation. This module is only used as a verification oracle\n * when SILVERY_REACTIVE_VERIFY=1 or SILVERY_REACTIVE=0 (fallback mode). The\n * bundler tree-shakes it when STRICT is off since all call sites are gated\n * behind `_reactiveVerifyEnabled` or `!_reactiveEnabled`.\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 render-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: render-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. Outlines are NOT │\n * │ tracked here — they're handled by a separate decoration pass. │\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// ============================================================================\n// COMPLETE INVALIDATION MODEL\n// ============================================================================\n//\n// This section documents EVERY condition that affects skip/render/clear/buffer\n// decisions in the silvery render pipeline. The 14 CascadeInputs below model the\n// core boolean algebra, but the real pipeline has additional conditions checked\n// inline in render-phase.ts that are not captured in computeCascade(). This\n// document is the authoritative inventory.\n//\n// ┌─────────────────────────────────────────────────────────────────────────────┐\n// │ PART 1: ALL INVALIDATION INPUTS │\n// │ │\n// │ Input │ Owner │ Set by │ Cleared by │ Lifetime │ In computeCascade? │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ contentDirtyEpoch │ reconciler │ commitUpdate, │ advanceRenderEpoch (O(1)) │ per-render-pass │ YES (contentDirty) │\n// │ │ │ commitTextUpdate, │ clearNodeDirtyFlags (skip) │ │ │\n// │ │ │ appendChild, etc. │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ stylePropsDirtyEpoch │ reconciler │ commitUpdate │ advanceRenderEpoch │ per-render-pass │ YES (stylePropsDirty)│\n// │ │ │ (always for visual │ clearNodeDirtyFlags (skip) │ │ │\n// │ │ │ changes; survives │ │ │ │\n// │ │ │ measure phase │ │ │ │\n// │ │ │ clearing of │ │ │ │\n// │ │ │ contentDirty) │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ bgDirtyEpoch │ reconciler │ commitUpdate (when │ advanceRenderEpoch │ per-render-pass │ YES (bgDirty) │\n// │ │ │ backgroundColor, │ clearNodeDirtyFlags (skip) │ │ │\n// │ │ │ borderStyle removed,│ │ │ │\n// │ │ │ outlineStyle │ │ │ │\n// │ │ │ removed, or theme │ │ │ │\n// │ │ │ changed) │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ subtreeDirtyEpoch │ reconciler │ markSubtreeDirty │ advanceRenderEpoch │ per-render-pass │ YES (subtreeDirty) │\n// │ │ + layout │ (walks up from any │ clearNodeDirtyFlags (skip) │ │ │\n// │ │ │ dirty descendant); │ │ │ │\n// │ │ │ propagateLayout │ │ │ │\n// │ │ │ (when child rect │ │ │ │\n// │ │ │ changes); scrollPhase│ │ │ │\n// │ │ │ (on offset/range │ │ │ │\n// │ │ │ change); stickyPhase│ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ childrenDirtyEpoch │ reconciler │ appendChild, │ advanceRenderEpoch │ per-render-pass │ YES (childrenDirty)│\n// │ │ │ removeChild, │ clearNodeDirtyFlags (skip) │ │ │\n// │ │ │ insertBefore, │ │ │ │\n// │ │ │ clearContainer │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ layoutChangedThisFrame │ layout │ propagateLayout │ advanceRenderEpoch │ per-render-pass │ YES (layoutChanged)│\n// │ │ │ (when rect differs │ clearNodeDirtyFlags (skip) │ │ via isCurrentEpoch │\n// │ │ │ from prevLayout) │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ Flexily isDirty │ reconciler │ commitUpdate (when │ cleared by Flexily after │ per-layout-pass │ NO — consumed by │\n// │ (via markDirty()) │ │ layout props change)│ calculateLayout() runs │ │ layout phase only │\n// │ │ │ │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ childPositionChanged │ render │ hasChildPositionChanged│ implicit (per-frame │ per-render-frame│ YES │\n// │ (computed inline) │ │ compares child │ computation, not stored) │ │ │\n// │ │ │ boxRect.x/y vs │ │ │ │\n// │ │ │ prevLayout.x/y │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ ancestorLayoutChanged │ render │ propagated top-down │ implicit (per-frame │ per-render-frame│ YES │\n// │ (threaded state) │ │ via NodeRenderState │ propagation via nodeState) │ │ │\n// │ │ │ = isCurrentEpoch( │ │ │ │\n// │ │ │ node.layoutChanged │ │ │ │\n// │ │ │ ThisFrame) || │ │ │ │\n// │ │ │ parent's value │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ ancestorCleared │ render │ propagated top-down │ implicit (per-frame │ per-render-frame│ YES │\n// │ (threaded state) │ │ = contentRegion- │ propagation via nodeState) │ │ │\n// │ │ │ Cleared || (parent │ │ │ │\n// │ │ │ ancestorCleared && │ │ │ │\n// │ │ │ !effectiveBg) │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ hasPrevBuffer │ render │ top-level: prevBuf │ implicit (per-frame │ per-render-frame│ YES │\n// │ (threaded state) │ │ && same dimensions; │ propagation via nodeState) │ │ │\n// │ │ │ per-child: false │ │ │ │\n// │ │ │ when childrenDirty │ │ │ │\n// │ │ │ || childPosition- │ │ │ │\n// │ │ │ Changed || children-│ │ │ │\n// │ │ │ NeedFreshRender │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ isTextNode │ inherent │ node.type at │ never (immutable) │ node lifetime │ YES │\n// │ │ │ creation │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ hasBgColor │ render │ getEffectiveBg( │ implicit (per-frame │ per-render-frame│ YES │\n// │ (computed inline) │ │ props): explicit │ computation from props) │ │ │\n// │ │ │ backgroundColor or │ │ │ │\n// │ │ │ theme.bg │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ absoluteChildMutated │ render │ buildCascadeInputs: │ implicit (per-frame │ per-render-frame│ YES │\n// │ (computed inline) │ │ any absolute child │ computation) │ │ │\n// │ │ │ has childrenDirty, │ │ │ │\n// │ │ │ layoutChanged, or │ │ │ │\n// │ │ │ childPositionChanged│ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ descendantOverflowChanged │ render │ buildCascadeInputs │ implicit (per-frame │ per-render-frame│ YES │\n// │ (computed inline) │ │ → hasDescendant- │ computation) │ │ │\n// │ │ │ OverflowChanged: │ │ │ │\n// │ │ │ recursive check if │ │ │ │\n// │ │ │ descendant prev- │ │ │ │\n// │ │ │ Layout overflows │ │ │ │\n// │ │ │ this node's rect │ │ │ │\n// │ │ │ AND layoutChanged- │ │ │ │\n// │ │ │ ThisFrame is current │ │ │ │\n// ├──────────────────────────┼────────────┼─────────────────────┼────────────────────────────┼─────────────────┼────────────────────┤\n// │ │\n// │ THE FOLLOWING INPUTS ARE NOT IN computeCascade — CHECKED INLINE IN render-phase.ts │\n// │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ scrollOffsetChanged │ render │ inline comparison: │ implicit (per-frame │ per-render-frame│ NO — checked in │\n// │ │ │ node.scrollState. │ computation) │ │ canSkipEntireSub- │\n// │ │ │ offset !== node. │ │ │ tree (prevents skip│\n// │ │ │ scrollState. │ │ │ when scroll offset │\n// │ │ │ prevOffset │ │ │ changed) │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ node.hidden │ reconciler │ hideInstance / │ unhideInstance │ until unhidden │ NO — early return │\n// │ │ │ hideTextInstance │ │ │ before cascade │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ display=\"none\" │ reconciler │ commitUpdate (prop) │ commitUpdate (prop change) │ until prop change│ NO — early return │\n// │ │ │ │ │ │ before cascade │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ !node.layoutNode │ reconciler │ node creation │ never (immutable) │ node lifetime │ NO — early return │\n// │ (virtual text node) │ │ (virtual text has │ │ │ (rendered by parent │\n// │ │ │ no Yoga layout) │ │ │ collectTextContent) │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ off-screen (viewport │ render │ inline: screenY >= │ implicit (per-frame │ per-render-frame│ NO — early return │\n// │ clipping) │ │ buffer.height || │ computation) │ │ (dirty flags │\n// │ │ │ screenY + height │ │ │ preserved for when │\n// │ │ │ <= 0 │ │ │ scrolled into view)│\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ prevBuffer dimension │ render │ renderPhase entry: │ implicit (per-frame) │ per-render-pass │ NO — sets top-level│\n// │ mismatch │ │ prevBuffer.width │ │ │ hasPrevBuffer=false│\n// │ │ │ !== layout.width │ │ │ (full fresh render)│\n// │ │ │ || height mismatch │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ bufferIsCloned │ render │ set once at render- │ implicit (per-frame) │ per-render-pass │ NO — guards │\n// │ (threaded state) │ │ Phase entry from │ │ │ clearExcessArea │\n// │ │ │ hasPrevBuffer │ │ │ (no stale pixels │\n// │ │ │ │ │ │ on fresh buffer) │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ hasDescendantWithBg │ render │ inline tree walk │ implicit (per-frame) │ per-render-frame│ NO — disables │\n// │ (computed inline) │ │ when bgOnlyChange │ │ │ bgOnlyChange fast │\n// │ │ │ is true │ │ │ path │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ isStyleOnlyDirty │ reconciler │ trackStyleOnlyDirty │ clearDirtyTracking (post- │ per-render-pass │ NO — enables text │\n// │ (module set) │ │ in commitUpdate │ render) │ │ restyle fast path │\n// │ │ │ when contentChanged │ │ │ (CURRENTLY DISABLED)│\n// │ │ │ =\"style\" && no │ │ │ │\n// │ │ │ layout/content/bg │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ hasChildWithBg │ render │ inline tree walk │ implicit (per-frame) │ per-render-frame│ NO — disables text │\n// │ (computed inline) │ │ when text style- │ │ │ restyle fast path │\n// │ │ │ only path active │ │ │ (CURRENTLY DISABLED)│\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ scroll container tier │ render │ planScrollRender() │ implicit (per-frame) │ per-render-frame│ NO — separate tier │\n// │ (shift/clear/subtree) │ │ pure function from │ │ │ planner with own │\n// │ │ │ scrollOffsetChanged,│ │ │ inputs (see below) │\n// │ │ │ visibleRangeChanged,│ │ │ │\n// │ │ │ hasStickyChildren, │ │ │ │\n// │ │ │ childrenNeedFresh- │ │ │ │\n// │ │ │ Render, childrenDirty│ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ stickyForceRefresh │ render │ scroll: planScroll- │ implicit (per-frame) │ per-render-frame│ NO — forces all │\n// │ │ │ Render (Tier 3 + │ │ │ first-pass children│\n// │ │ │ hasStickyChildren); │ │ │ to hasPrevBuffer= │\n// │ │ │ normal: hasPrev && │ │ │ false │\n// │ │ │ hasStickyChildren │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ visibleRangeChanged │ render │ inline comparison: │ implicit (per-frame) │ per-render-frame│ NO — input to │\n// │ (scroll containers) │ │ firstVisibleChild │ │ │ planScrollRender │\n// │ │ │ !== prev or last │ │ │ │\n// │ │ │ !== prev │ │ │ │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ nodeTheme (theme prop) │ reconciler │ commitUpdate (prop) │ per-frame (read from props)│ per-render-frame│ NO — pushContext- │\n// │ │ │ │ │ │ Theme/popContext- │\n// │ │ │ │ │ │ Theme during render│\n// │ │ │ │ │ │ (bgDirtyEpoch set │\n// │ │ │ │ │ │ on theme change) │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ clipBounds │ render │ computeChildClip- │ implicit (per-frame) │ per-render-frame│ NO — passed through│\n// │ (threaded state) │ │ Bounds from │ │ │ nodeState; used for│\n// │ │ │ overflow=hidden/ │ │ │ clipping fills and │\n// │ │ │ scroll containers │ │ │ text rendering │\n// │──────────────────────────│────────────│─────────────────────│────────────────────────────│─────────────────│────────────────────│\n// │ inheritedBg / inheritedFg │ render │ threaded top-down: │ implicit (per-frame) │ per-render-frame│ NO — used for text │\n// │ (threaded state) │ │ computed from │ │ │ bg inheritance and │\n// │ │ │ effectiveBg/color/ │ │ │ region clearing │\n// │ │ │ theme at each node │ │ │ │\n// └──────────────────────────┴────────────┴─────────────────────┴────────────────────────────┴─────────────────┴────────────────────┘\n//\n// ┌─────────────────────────────────────────────────────────────────────────────┐\n// │ PART 2: NON-LOCAL DEPENDENCIES THAT CAN FORCE REPAINT │\n// └─────────────────────────────────────────────────────────────────────────────┘\n//\n// 1. ANCESTOR BACKGROUND CHANGE\n// When an ancestor's backgroundColor changes, the ancestor's bgDirty triggers\n// contentAreaAffected, which cascades childrenNeedFreshRender=true to all\n// descendants. Descendants get hasPrevBuffer=false, forcing full re-render.\n// If bgOnlyChange were enabled (currently disabled), the fillBg() path would\n// update bg in-place, but only when no descendant has its own bg\n// (hasDescendantWithBg check).\n//\n// 2. ANCESTOR LAYOUT CHANGE\n// When an ancestor moves or resizes, ancestorLayoutChanged propagates down\n// through the entire subtree. Even if a descendant has no dirty flags, its\n// pixels in the cloned buffer are at wrong absolute coordinates. The skip\n// condition checks !ancestorLayoutChanged. Additionally, the parent's\n// childrenNeedFreshRender cascade sets childHasPrev=false.\n// Key: ancestorLayoutChanged does NOT break at backgroundColor boundaries\n// (unlike ancestorCleared), because bg fill doesn't fix wrong coordinates.\n//\n// 3. SIBLING POSITION SHIFT\n// When a sibling resizes, other siblings shift positions (flexbox reflow).\n// hasChildPositionChanged detects this at the parent level by comparing each\n// child's boxRect.x/y to prevLayout.x/y. Triggers childrenNeedRepaint=true,\n// setting childHasPrev=false for all children.\n//\n// 4. ANCESTOR CLEAR CASCADE\n// When an ancestor without backgroundColor clears its region (contentRegion-\n// Cleared=true), ancestorCleared propagates down. This cascade BREAKS at\n// nodes with backgroundColor — their bg fill covers the stale pixels, so\n// their children don't see stale buffer content.\n//\n// 5. ABSOLUTE CHILD MUTATION → PARENT CLEAR\n// When an absolute-positioned child changes structure (children mount/unmount,\n// layout shift), absoluteChildMutated forces the PARENT's contentAreaAffected\n// =true. This clears the parent's entire region and re-renders all normal-flow\n// children, removing stale overlay pixels from gap areas.\n//\n// 6. DESCENDANT OVERFLOW → ANCESTOR CLEAR\n// When a descendant overflows beyond an ancestor's rect and then shrinks,\n// the stale overflow pixels are OUTSIDE the descendant's parent's content\n// area. hasDescendantOverflowChanged recursively detects this at the ancestor\n// level and triggers contentAreaAffected + clearDescendantOverflowRegions.\n//\n// 7. SCROLL OFFSET CHANGE → SUBTREE RE-RENDER\n// The scroll phase sets subtreeDirty on the scroll container when offset or\n// visible range changes. The render phase then plans a scroll tier:\n// - Tier 1 (shift): buffer.scrollRegion shifts pixels; only edges re-render\n// - Tier 2 (clear): full viewport clear; all children get hasPrevBuffer=false\n// - Tier 3 (subtree): only dirty descendants re-render\n// Tier 1 is UNSAFE with sticky children (sticky overwrite + shift = corruption).\n//\n// 8. STICKY CHILDREN → stickyForceRefresh\n// When sticky children exist in Tier 3 (or in normal containers), all\n// first-pass children are forced to re-render (hasPrevBuffer=false). The\n// cloned buffer has stale content from previous frames' sticky positions.\n// A pre-clear to null bg ensures fresh render baseline.\n//\n// 9. THEME CHANGE CASCADE\n// When a node's `theme` prop changes, bgDirtyEpoch is set (because theme\n// contains bg). pushContextTheme/popContextTheme during rendering ensures\n// all $token-based colors resolve to new values. Children re-render because\n// bgDirty → contentAreaAffected → childrenNeedFreshRender.\n//\n// 10. VISIBLE RANGE CHANGE (scroll containers)\n// When firstVisibleChild/lastVisibleChild changes (scroll phase detects\n// this), the scroll container's subtreeDirtyEpoch is set and\n// visibleRangeChanged feeds into planScrollRender to select the tier.\n//\n// ┌─────────────────────────────────────────────────────────────────────────────┐\n// │ PART 3: BUFFER VALIDITY STATES AND TRANSITIONS │\n// └─────────────────────────────────────────────────────────────────────────────┘\n//\n// A node's cached buffer (pixels in the cloned TerminalBuffer) can be in one of\n// these validity states:\n//\n// VALID (canSkipEntireSubtree = true)\n// All of: hasPrevBuffer=true, no dirty flags, no ancestorLayoutChanged,\n// no scrollOffsetChanged. The cloned buffer has correct pixels at correct\n// positions. Node is skipped entirely.\n//\n// STALE_CONTENT (hasPrevBuffer=true, some dirty flag set)\n// The buffer has pixels from the previous frame but they're wrong:\n// - contentDirty: text content or content-affecting props changed\n// - stylePropsDirty: visual style changed (color, border, etc.)\n// - bgDirty: background color changed\n// - childrenDirty: children added/removed/reordered\n// - layoutChanged: node moved or resized\n// - subtreeDirty: some descendant changed (node itself may be skippable)\n// - childPositionChanged: siblings shifted positions\n// - absoluteChildMutated: overlay child changed\n// - descendantOverflowChanged: overflow descendant changed\n// Action: re-render with clearing as appropriate.\n//\n// STALE_POSITION (hasPrevBuffer=true, ancestorLayoutChanged=true)\n// The buffer has correct content but at WRONG coordinates because an\n// ancestor moved/resized. The node itself has no dirty flags.\n// Action: must re-render at new position; can't skip.\n//\n// STALE_SCROLL (hasPrevBuffer=true, scrollOffsetChanged=true)\n// The buffer has correct content but scroll offset changed, so visible\n// children may have shifted. Defensive check in canSkipEntireSubtree.\n// Action: apply scroll tier strategy (shift/clear/subtree).\n//\n// CLEARED (hasPrevBuffer=false or true, ancestorCleared=true)\n// An ancestor erased this node's buffer region. The buffer at this\n// position contains inherited bg (spaces), not previous content.\n// - If hasPrevBuffer=false: fresh render, no clearing needed.\n// - If ancestorCleared=true: parent cleared, descendants may still need\n// their own clearing for sub-regions.\n// Breaks at backgroundColor boundaries (bg fill covers cleared area).\n//\n// FRESH (hasPrevBuffer=false, ancestorCleared=false)\n// First render or dimension change. Buffer is a blank TerminalBuffer\n// (all cells empty). No clearing needed, no skipping possible.\n// contentRegionCleared=false and childrenNeedFreshRender=false because\n// both are gated on (hasPrevBuffer || ancestorCleared).\n//\n// FRESH_OVERLAY (hasPrevBuffer=false, ancestorCleared=false, absolute/sticky)\n// Buffer at this position contains first-pass content from normal-flow\n// siblings (not \"previous frame\" content). Used for absolute and sticky\n// children in the second/third rendering pass.\n// - ancestorCleared=false prevents transparent overlays from clearing\n// the normal-flow content underneath.\n//\n// State transitions per frame:\n// VALID --[dirty flag set]--> STALE_*\n// VALID --[ancestor layout]--> STALE_POSITION\n// VALID --[scroll change]---> STALE_SCROLL\n// STALE_* --[re-render]-----> VALID (dirty flags cleared)\n// FRESH --[first render]----> VALID (buffer populated)\n// any --[dimension change]--> FRESH (new buffer allocated)\n// any --[parent cascade]----> CLEARED (parent cleared region)\n//\n// ┌─────────────────────────────────────────────────────────────────────────────┐\n// │ PART 4: REASONS A CACHED BUFFER CAN BE INVALID │\n// └─────────────────────────────────────────────────────────────────────────────┘\n//\n// The cloned buffer can have stale pixels for these reasons:\n//\n// 1. Text content changed (contentDirtyEpoch) — old chars in buffer\n// 2. Style props changed (stylePropsDirtyEpoch) — old colors/attrs in buffer\n// 3. Background changed (bgDirtyEpoch) — old bg color, or removed bg leaving\n// stale colored pixels that need clearing\n// 4. Children restructured (childrenDirtyEpoch) — old children's pixels remain;\n// gap areas may have orphaned content\n// 5. Layout changed (layoutChangedThisFrame) — pixels at wrong position;\n// node may have grown (new area uninitialized) or shrunk (excess area stale)\n// 6. Child position shifted (childPositionChanged) — siblings moved, gap areas\n// between children have stale pixels from old positions\n// 7. Absolute child mutated (absoluteChildMutated) — overlay pixels in gap areas\n// between current children are stale from old overlay positions\n// 8. Descendant overflow changed (descendantOverflowChanged) — pixels beyond this\n// node's rect are stale from previous overflow that no longer extends there\n// 9. Scroll offset changed (scrollOffsetChanged) — children's visual positions\n// shifted; buffer has content at old scroll positions\n// 10. Ancestor layout changed (ancestorLayoutChanged) — this node's absolute\n// position in the buffer is wrong even though its own layout didn't change\n// 11. Ancestor cleared (ancestorCleared) — an ancestor erased this area; buffer\n// has inherited bg, not this node's content\n// 12. Sticky children stale positioning — cloned buffer has pixels from previous\n// frames' sticky render positions; stickyForceRefresh pre-clears and forces\n// all first-pass children to re-render\n// 13. Node shrunk (clearExcessArea) — old bounds were larger; excess pixels remain\n// in the right/bottom margin of the old rect\n// 14. Node hidden/unhidden — Suspense boundary toggled visibility\n// 15. display=\"none\" toggled — node occupies 0x0 space but old pixels may remain\n// 16. Border/outline removed (bgDirtyEpoch) — stale border/outline characters\n// persist in the clone; bgDirty ensures contentAreaAffected triggers clearing\n// 17. Theme changed (bgDirtyEpoch) — all $token colors resolve differently;\n// the entire subtree's colors are stale\n//\n// ┌─────────────────────────────────────────────────────────────────────────────┐\n// │ PART 5: PIPELINE PHASE OWNERSHIP SUMMARY │\n// └─────────────────────────────────────────────────────────────────────────────┘\n//\n// RECONCILER (host-config.ts):\n// Sets: contentDirtyEpoch, stylePropsDirtyEpoch, bgDirtyEpoch,\n// childrenDirtyEpoch, hidden\n// Calls: layoutNode.markDirty() (Flexily's isDirty propagates to root)\n// Propagates: subtreeDirtyEpoch (upward via markSubtreeDirty)\n// Tracks: contentDirtyNodes, styleOnlyDirtyNodes,\n// scrollDirtyNodes (in dirty-tracking.ts)\n//\n// LAYOUT PHASE (layout-phase.ts):\n// Sets: layoutChangedThisFrame (via propagateLayout)\n// Propagates: subtreeDirtyEpoch (upward when layout changes)\n// Checks: root.layoutNode.isDirty() — sole gate for running layout\n// Computes: boxRect, prevLayout, scrollState (scroll phase),\n// stickyChildren (sticky phase), scrollRect/screenRect\n//\n// RENDER PHASE (render-phase.ts):\n// Reads: ALL epoch flags, layoutChangedThisFrame, scrollState, stickyChildren\n// Computes: childPositionChanged, absoluteChildMutated, descendantOverflow-\n// Changed, scrollOffsetChanged, clipBounds, inheritedBg/Fg\n// Threads: hasPrevBuffer, ancestorCleared, ancestorLayoutChanged,\n// bufferIsCloned, scrollOffset, clipBounds, inheritedBg, inheritedFg\n// Calls: computeCascade() for core boolean algebra\n// Clears: all dirty epoch flags via advanceRenderEpoch() (O(1))\n// Syncs: prevLayout = boxRect via syncPrevLayout() (post-render)\n//\n// ┌─────────────────────────────────────────────────────────────────────────────┐\n// │ PART 6: DISABLED FAST PATHS (currently hardcoded false) │\n// └─────────────────────────────────────────────────────────────────────────────┘\n//\n// bgOnlyChange (line 180 in this file):\n// DISABLED — hardcoded `const bgOnlyChange = false`.\n// When only backgroundColor changed on a Box with bg, fillBg() would preserve\n// child chars. Disabled because it causes incremental rendering mismatches\n// (fg colors lost on child nodes). Additional safety conditions exist even\n// in the enabled path:\n// - hasDescendantWithBg: descendant bg would be overwritten by fillBg\n// - !ancestorLayoutChanged: children positions may have shifted\n// - !ancestorCleared: parent cleared stale pixels, children must re-render\n//\n// useTextStyleFastPath (render-phase.ts):\n// DISABLED — hardcoded `const useTextStyleFastPath = false`.\n// When only visual style props changed on a Text node (isStyleOnlyDirty),\n// buffer.restyleRegion() would update fg/bg/attrs in-place without re-\n// collecting text. Disabled because it causes incremental rendering mismatches\n// (fg colors lost). Additional safety conditions:\n// - !contentDirty, !childrenDirty, !bgDirty\n// - !ancestorCleared, !ancestorLayoutChanged\n// - !hasChildWithBg (nested children with own bg)\n// - hasPrevBuffer=true\n//\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 * True when bgDirty is the ONLY reason contentAreaAffected is true, and the\n * node has a backgroundColor. In this case, renderBox can use fillBg() (which\n * preserves existing chars) instead of fill() (which overwrites with spaces).\n * This avoids the cascade to children — clean children keep their chars from\n * the cloned buffer with the new bg applied.\n *\n * Requirements: hasPrevBuffer, bgDirty, hasBgColor, no other contentAreaAffected triggers.\n */\n bgOnlyChange: 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 render-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 // Is bgDirty the ONLY trigger for contentAreaAffected?\n // When true AND hasBgColor: we can use fillBg() (preserves chars) instead of\n // fill() (overwrites with spaces), eliminating the cascade to children.\n const bgOnlyAffected =\n bgDirty &&\n !contentDirty &&\n !layoutChanged &&\n !childPositionChanged &&\n !childrenDirty &&\n !textPaintDirty &&\n !absoluteChildMutated &&\n !descendantOverflowChanged\n\n // Style-only fast path: when only bg changed on a Box with bg, use fillBg\n // to preserve child chars. Children see hasPrevBuffer=true (skippable).\n //\n // Additional safety checks:\n // - !ancestorLayoutChanged: children's positions may have shifted in the clone\n // - !ancestorCleared: parent cleared stale pixels, children must re-render\n //\n // IMPORTANT: this is only safe when no descendant has its own explicit\n // backgroundColor that would be incorrectly overwritten by fillBg. The\n // render phase checks this condition (hasDescendantWithBg) and falls back\n // to the full path when descendants have their own bg.\n // DISABLED: bgOnlyChange fast path causes incremental rendering mismatches\n // (fg colors lost on child nodes). Needs investigation before re-enabling.\n const bgOnlyChange = false\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 // bgOnlyChange on nodes WITHOUT bg still needs clearing (bg removed).\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 // Exception: bgOnlyChange uses fillBg() which preserves chars, so children\n // don't need fresh render — they keep their correct chars from the clone.\n const childrenNeedFreshRender =\n (hasPrevBuffer || ancestorCleared) && (contentAreaAffected || bgRefillNeeded) && !bgOnlyChange\n\n return {\n canSkipEntireSubtree,\n contentAreaAffected,\n bgRefillNeeded,\n contentRegionCleared,\n skipBgFill,\n childrenNeedFreshRender,\n bgOnlyChange,\n }\n}\n","/**\n * Reactive Node State — alien-signals wrappers for cascade derivations.\n *\n * E+ Phase 2: Replace manual cascade formula computation with reactive\n * computeds. The cascade-predicates.ts `computeCascade()` function is the\n * oracle — this module must produce identical outputs.\n *\n * Incremental approach:\n * 1. Writable signals mirror epoch-stamped dirty flags\n * 2. Computeds derive cascade outputs (isDirty, contentAreaAffected, etc.)\n * 3. `syncToSignals()` bridges epoch flags → signals at render-phase entry\n * 4. Dev-mode assertions verify reactive === oracle\n *\n * alien-signals computeds are lazy (pull-based): reading a computed re-evaluates\n * only if a dependency changed. No batching needed for correctness — the\n * reconciler's synchronous commit writes epoch stamps, and the render phase\n * reads computeds after all commits are done.\n */\n\nimport { signal, computed } from \"@silvery/signals\"\nimport type { AgNode } from \"@silvery/ag/types\"\nimport { isDirty, CONTENT_BIT, STYLE_PROPS_BIT, BG_BIT, CHILDREN_BIT, SUBTREE_BIT } from \"@silvery/ag/epoch\"\nimport { computeCascade, type CascadeInputs, type CascadeOutputs } from \"./cascade-predicates.ts\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Writable signal — call with no args to read, call with value to write.\n * Type alias for clarity (alien-signals returns this shape from `signal()`).\n */\ntype Signal<T> = {\n (): T\n (value: T): void\n}\n\n/** Read-only computed — call with no args to read. */\ntype Computed<T> = () => T\n\n/**\n * Per-node reactive state that lives alongside an AgNode.\n *\n * Writable signals are synced from epoch-stamped flags before each render pass.\n * Computed derivations automatically recompute when inputs change.\n */\nexport interface ReactiveNodeState {\n // --- Writable signals (synced from epoch flags) ---\n readonly contentDirty: Signal<boolean>\n readonly stylePropsDirty: Signal<boolean>\n readonly bgDirty: Signal<boolean>\n readonly childrenDirty: Signal<boolean>\n readonly subtreeDirty: Signal<boolean>\n readonly layoutChanged: Signal<boolean>\n\n // --- Context signals (set per-node during tree traversal) ---\n readonly hasPrevBuffer: Signal<boolean>\n readonly childPositionChanged: Signal<boolean>\n readonly ancestorLayoutChanged: Signal<boolean>\n readonly ancestorCleared: Signal<boolean>\n readonly isTextNode: Signal<boolean>\n readonly hasBgColor: Signal<boolean>\n readonly absoluteChildMutated: Signal<boolean>\n readonly descendantOverflowChanged: Signal<boolean>\n\n // --- Computed derivations ---\n readonly canSkipEntireSubtree: Computed<boolean>\n readonly textPaintDirty: Computed<boolean>\n readonly contentAreaAffected: Computed<boolean>\n readonly bgRefillNeeded: Computed<boolean>\n readonly contentRegionCleared: Computed<boolean>\n readonly skipBgFill: Computed<boolean>\n readonly childrenNeedFreshRender: Computed<boolean>\n readonly bgOnlyChange: Computed<boolean>\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Create a reactive state wrapper for a node.\n *\n * The computed formulas exactly replicate cascade-predicates.ts `computeCascade()`.\n * They are NOT a replacement — the oracle function remains authoritative.\n * In dev mode, assertions verify equivalence.\n */\nexport function createReactiveNodeState(): ReactiveNodeState {\n // Writable signals — all start false, synced before each render pass\n const contentDirty = signal(false)\n const stylePropsDirty = signal(false)\n const bgDirty = signal(false)\n const childrenDirty = signal(false)\n const subtreeDirty = signal(false)\n const layoutChanged = signal(false)\n\n // Context signals — set per-node during tree traversal\n const hasPrevBuffer = signal(false)\n const childPositionChanged = signal(false)\n const ancestorLayoutChanged = signal(false)\n const ancestorCleared = signal(false)\n const isTextNode = signal(false)\n const hasBgColor = signal(false)\n const absoluteChildMutated = signal(false)\n const descendantOverflowChanged = signal(false)\n\n // --- Computed derivations (formulas from cascade-predicates.ts) ---\n\n const canSkipEntireSubtree = computed(\n () =>\n hasPrevBuffer() &&\n !contentDirty() &&\n !stylePropsDirty() &&\n !layoutChanged() &&\n !subtreeDirty() &&\n !childrenDirty() &&\n !childPositionChanged() &&\n !ancestorLayoutChanged(),\n )\n\n const textPaintDirty = computed(() => isTextNode() && stylePropsDirty())\n\n const contentAreaAffected = computed(\n () =>\n contentDirty() ||\n layoutChanged() ||\n childPositionChanged() ||\n childrenDirty() ||\n bgDirty() ||\n textPaintDirty() ||\n absoluteChildMutated() ||\n descendantOverflowChanged(),\n )\n\n // DISABLED: bgOnlyChange fast path causes incremental rendering mismatches.\n // Matches cascade-predicates.ts which hardcodes `false`.\n const bgOnlyChange = computed(() => false)\n\n const bgRefillNeeded = computed(() => hasPrevBuffer() && !contentAreaAffected() && subtreeDirty() && hasBgColor())\n\n const contentRegionCleared = computed(\n () => (hasPrevBuffer() || ancestorCleared()) && contentAreaAffected() && !hasBgColor(),\n )\n\n const skipBgFill = computed(\n () => hasPrevBuffer() && !ancestorCleared() && !contentAreaAffected() && !bgRefillNeeded(),\n )\n\n const childrenNeedFreshRender = computed(\n () => (hasPrevBuffer() || ancestorCleared()) && (contentAreaAffected() || bgRefillNeeded()) && !bgOnlyChange(),\n )\n\n return {\n contentDirty,\n stylePropsDirty,\n bgDirty,\n childrenDirty,\n subtreeDirty,\n layoutChanged,\n hasPrevBuffer,\n childPositionChanged,\n ancestorLayoutChanged,\n ancestorCleared,\n isTextNode,\n hasBgColor,\n absoluteChildMutated,\n descendantOverflowChanged,\n canSkipEntireSubtree,\n textPaintDirty,\n contentAreaAffected,\n bgRefillNeeded,\n contentRegionCleared,\n skipBgFill,\n childrenNeedFreshRender,\n bgOnlyChange,\n }\n}\n\n// ============================================================================\n// Sync: Epoch flags → Signals\n// ============================================================================\n\n/**\n * Sync a node's epoch-stamped dirty flags into the reactive signals.\n *\n * Called once per node at the start of renderNodeToBuffer, BEFORE reading\n * any computed. Context-dependent inputs (hasPrevBuffer, ancestorCleared, etc.)\n * are also set here since they vary per tree-traversal position.\n */\nexport function syncToSignals(\n state: ReactiveNodeState,\n node: AgNode,\n ctx: {\n hasPrevBuffer: boolean\n layoutChanged: boolean\n childPositionChanged: boolean\n ancestorLayoutChanged: boolean\n ancestorCleared: boolean\n absoluteChildMutated: boolean\n descendantOverflowChanged: boolean\n hasBgColor: boolean\n },\n): void {\n // Sync epoch flags → boolean signals\n state.contentDirty(isDirty(node.dirtyBits, node.dirtyEpoch, CONTENT_BIT))\n state.stylePropsDirty(isDirty(node.dirtyBits, node.dirtyEpoch, STYLE_PROPS_BIT))\n state.bgDirty(isDirty(node.dirtyBits, node.dirtyEpoch, BG_BIT))\n state.childrenDirty(isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT))\n state.subtreeDirty(isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT))\n state.layoutChanged(ctx.layoutChanged)\n\n // Sync context-dependent inputs\n state.hasPrevBuffer(ctx.hasPrevBuffer)\n state.childPositionChanged(ctx.childPositionChanged)\n state.ancestorLayoutChanged(ctx.ancestorLayoutChanged)\n state.ancestorCleared(ctx.ancestorCleared)\n state.isTextNode(node.type === \"silvery-text\")\n state.hasBgColor(ctx.hasBgColor)\n state.absoluteChildMutated(ctx.absoluteChildMutated)\n state.descendantOverflowChanged(ctx.descendantOverflowChanged)\n}\n\n// ============================================================================\n// Reactive-driven cascade (replaces computeCascade for production)\n// ============================================================================\n\n/**\n * Read all cascade outputs from the reactive computeds.\n * Call AFTER `syncToSignals()`. Returns the same CascadeOutputs shape\n * as `computeCascade()` but derived from the signal graph.\n */\nexport function readReactiveCascade(state: ReactiveNodeState): CascadeOutputs {\n return {\n canSkipEntireSubtree: state.canSkipEntireSubtree(),\n contentAreaAffected: state.contentAreaAffected(),\n bgRefillNeeded: state.bgRefillNeeded(),\n contentRegionCleared: state.contentRegionCleared(),\n skipBgFill: state.skipBgFill(),\n childrenNeedFreshRender: state.childrenNeedFreshRender(),\n bgOnlyChange: state.bgOnlyChange(),\n }\n}\n\n// ============================================================================\n// Oracle Verification\n// ============================================================================\n\n/**\n * Verify that reactive computeds match the cascade oracle.\n *\n * Call in dev mode (SILVERY_STRICT=1) after syncToSignals + computeCascade.\n * Throws on mismatch with a detailed diff.\n */\nexport function assertReactiveMatchesOracle(state: ReactiveNodeState, oracle: CascadeOutputs, nodeId: string): void {\n const fields: (keyof CascadeOutputs)[] = [\n \"canSkipEntireSubtree\",\n \"contentAreaAffected\",\n \"bgRefillNeeded\",\n \"contentRegionCleared\",\n \"skipBgFill\",\n \"childrenNeedFreshRender\",\n \"bgOnlyChange\",\n ]\n\n const mismatches: string[] = []\n for (const field of fields) {\n const reactiveValue = state[field]()\n const oracleValue = oracle[field]\n if (reactiveValue !== oracleValue) {\n mismatches.push(` ${field}: reactive=${reactiveValue}, oracle=${oracleValue}`)\n }\n }\n\n if (mismatches.length > 0) {\n throw new Error(`ReactiveNodeState mismatch for ${nodeId || \"(unnamed)\"}:\\n${mismatches.join(\"\\n\")}`)\n }\n}\n\n// ============================================================================\n// Node State Cache\n// ============================================================================\n\n/**\n * WeakMap from AgNode to its reactive state.\n *\n * Lazily created on first access. Automatically garbage-collected when the\n * node is removed from the tree (WeakMap semantics).\n */\nconst nodeStates = new WeakMap<AgNode, ReactiveNodeState>()\n\n/**\n * Get or create the reactive state for a node.\n *\n * Uses a WeakMap so states are automatically cleaned up when nodes are GC'd.\n */\nexport function getReactiveState(node: AgNode): ReactiveNodeState {\n let state = nodeStates.get(node)\n if (!state) {\n state = createReactiveNodeState()\n nodeStates.set(node, state)\n }\n return state\n}\n","/**\n * Phase 3: Render 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 * renderPhase → renderNodeToBuffer → buildCascadeInputs + computeCascade (oracle)\n * → traceRenderDecision (diagnostics)\n * → executeRegionClearing\n * → renderOwnContent\n * → renderScrollContainerChildren / renderNormalChildren\n * Helpers: clearDirtyFlags, hasChildPositionChanged, computeChildClipBounds\n * Region clearing: 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, renderScrollIndicators, getEffectiveBg } from \"./render-box\"\nimport { clearPreviousOutlines, renderDecorationPass } from \"./decoration-phase\"\nimport { getTextStyle, 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\"\n// cascade-predicates is the imperative oracle — used for STRICT verification\n// and as the fallback when SILVERY_REACTIVE=0 (bench only).\nimport { computeCascade } from \"./cascade-predicates\"\nimport { isStyleOnlyDirty } from \"@silvery/ag/dirty-tracking\"\nimport {\n isCurrentEpoch,\n isAnyDirty,\n INITIAL_EPOCH,\n advanceRenderEpoch,\n isDirty,\n CONTENT_BIT,\n STYLE_PROPS_BIT,\n BG_BIT,\n CHILDREN_BIT,\n SUBTREE_BIT,\n ABS_CHILD_BIT,\n DESC_OVERFLOW_BIT,\n} from \"@silvery/ag/epoch\"\nimport type { CascadeOutputs } from \"./cascade-predicates\"\nimport type { ClipBounds, RenderPhaseStats, NodeRenderState, NodeTraceEntry, PipelineContext } from \"./types\"\nimport { getReactiveState, syncToSignals, readReactiveCascade, assertReactiveMatchesOracle } from \"./reactive-node\"\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 renderPhase(root: AgNode, prevBuffer?: TerminalBuffer | null, ctx?: PipelineContext): TerminalBuffer {\n const layout = root.boxRect\n if (!layout) {\n throw new Error(\"renderPhase called before layout phase\")\n }\n\n const instr = resolveInstrumentation(ctx)\n\n // Clone prevBuffer if same dimensions, else create fresh\n const hasPrevBuffer = prevBuffer && prevBuffer.width === layout.width && prevBuffer.height === layout.height\n\n // No-op frame skip: if nothing changed since last render and we have a valid\n // prev buffer, return it unchanged. Saves buffer clone + tree walk + epoch advance.\n //\n // Currently doesn't fire with React (createElement always produces new children\n // array → commitUpdate sets CHILDREN_BIT on root). Will fire with:\n // - Signal-driven updates (v1.5) that bypass React\n // - Timer-driven re-renders where nothing changed\n // - Scheduler polling without React pending work\n if (hasPrevBuffer && !isAnyDirty(root.dirtyBits, root.dirtyEpoch) && !isCurrentEpoch(root.layoutChangedThisFrame)) {\n if (instr.enabled) instr.stats._noopSkip = 1\n advanceRenderEpoch()\n return prevBuffer\n }\n\n if (instr.enabled) {\n instr.stats._prevBufferNull = prevBuffer == null ? 1 : 0\n instr.stats._prevBufferDimMismatch = prevBuffer && !hasPrevBuffer ? 1 : 0\n instr.stats._hasPrevBuffer = hasPrevBuffer ? 1 : 0\n instr.stats._layoutW = layout.width\n instr.stats._layoutH = layout.height\n instr.stats._prevW = prevBuffer?.width ?? 0\n instr.stats._prevH = prevBuffer?.height ?? 0\n }\n\n const t0 = instr.enabled ? performance.now() : 0\n const buffer = hasPrevBuffer ? prevBuffer.clone() : new TerminalBuffer(layout.width, layout.height)\n const tClone = instr.enabled ? performance.now() - t0 : 0\n\n // Default: root is selectable (userSelect defaults to \"text\").\n // renderNodeToBuffer will override per-node as it traverses.\n buffer.setSelectableMode(true)\n\n // Restore cells under previous-frame outlines BEFORE content rendering.\n // Outlines draw OUTSIDE their owning node into the parent's pixel space,\n // so they don't fit the per-node cascade. We treat them as a separate\n // decoration pass: clear prev positions here, redraw new positions after\n // the content phase below. The snapshots were captured when we drew the\n // previous frame and travel with the cloned buffer. No-op for fresh\n // buffers (no snapshots on a newly constructed TerminalBuffer).\n clearPreviousOutlines(buffer)\n\n const t1 = instr.enabled ? 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 inheritedBg: { color: null, ancestorRect: null },\n inheritedFg: null,\n },\n ctx,\n )\n const tRender = instr.enabled ? performance.now() - t1 : 0\n\n // Decoration phase: draw outlines AFTER content rendering. Walks the full\n // tree (O(N)) independent of dirty flags — outlines are idempotent per\n // frame and cheap to redraw (~5000 cells at worst). Populates fresh\n // snapshots on the buffer for the next frame's `clearPreviousOutlines`.\n renderDecorationPass(buffer, root)\n\n if (instr.enabled) {\n emitRenderPhaseStats(instr.stats, instr.nodeTrace, instr.nodeTraceEnabled, tClone, tRender)\n }\n\n // Sync prevLayout after render phase to prevent staleness on subsequent frames.\n // Skip when no layout changed this frame (cursor move, style-only changes).\n // The layout phase sets layoutChangedThisFrame on affected nodes; if root's\n // subtree has any, we need the full sync. If not, prevLayout is already correct.\n const anyLayoutChanged =\n isCurrentEpoch(root.layoutChangedThisFrame) || isDirty(root.dirtyBits, root.dirtyEpoch, SUBTREE_BIT)\n syncPrevLayout(root, anyLayoutChanged || !hasPrevBuffer)\n\n // Advance the render epoch — all dirty flags stamped with the old epoch\n // instantly become \"not dirty\". This replaces the O(N) clearDirtyFlags walk\n // for rendered nodes (skipped nodes still need explicit clearing).\n advanceRenderEpoch()\n\n return buffer\n}\n\n/**\n * Sync prevLayout to boxRect for all nodes in the tree.\n *\n * Called at the end of each renderPhase pass. This prevents:\n * 1. The O(N) staleness bug where prevLayout drifts from boxRect\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 */\n/**\n * Sync prevLayout = boxRect for nodes whose layout changed.\n *\n * Previously walked ALL nodes O(N) every frame. Now only visits nodes\n * with layoutChangedThisFrame (set by propagateLayout in layout phase).\n * Falls back to full walk when layout phase ran (dimensions changed or\n * Flexily isDirty) since any node may have moved.\n *\n * For cursor move (no layout change): O(1) — no nodes to sync.\n * For resize: O(N) — all nodes may have moved (same as before).\n */\nfunction syncPrevLayout(root: AgNode, layoutPhaseRan: boolean): void {\n // When layout phase ran, any node could have moved — full walk needed.\n // When layout was skipped (cursor move, style-only), no rects changed.\n if (!layoutPhaseRan) return\n\n const stack: AgNode[] = [root]\n while (stack.length > 0) {\n const node = stack.pop()!\n node.prevLayout = node.boxRect\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/** Check if an env var is truthy (treats \"0\" and \"false\" as disabled). */\nfunction envTruthy(val: string | undefined): boolean {\n return !!val && val !== \"0\" && val !== \"false\"\n}\n\n/** Instrumentation enabled when SILVERY_STRICT or SILVERY_INSTRUMENT is set */\nconst _instrumentEnabled =\n typeof process !== \"undefined\" &&\n (envTruthy(process.env?.SILVERY_STRICT) || envTruthy(process.env?.SILVERY_INSTRUMENT))\n\n/** Mutable stats counters — reset after each renderPhase call */\nconst _renderPhaseStats: RenderPhaseStats = {\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 _noopSkip: 0,\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 _renderPhaseCallCount = 0\n\n/** Module-level node trace (fallback when ctx.nodeTrace is not provided) */\nconst _nodeTrace: NodeTraceEntry[] = []\nconst _nodeTraceEnabled = typeof process !== \"undefined\" && envTruthy(process.env?.SILVERY_STRICT)\n\n/**\n * Reactive cascade: alien-signals computeds drive rendering (production path).\n * SILVERY_REACTIVE=0: fall back to imperative computeCascade() (bench only).\n * SILVERY_STRICT=1: oracle verifies reactive output matches imperative.\n */\nlet _reactiveEnabled = typeof process === \"undefined\" || process.env?.SILVERY_REACTIVE !== \"0\"\nlet _reactiveVerifyEnabled =\n _reactiveEnabled && typeof process !== \"undefined\" && envTruthy(process.env?.SILVERY_STRICT)\n\n/** Toggle reactive cascade mode at runtime (for three-way bench). */\nexport function setReactiveEnabled(enabled: boolean): void {\n _reactiveEnabled = enabled\n _reactiveVerifyEnabled = enabled && typeof process !== \"undefined\" && envTruthy(process.env?.SILVERY_STRICT)\n}\n\n// ============================================================================\n// Instrumentation Helpers\n// ============================================================================\n\n/** Resolved instrumentation state — avoids repeating ctx ?? module fallbacks. */\ninterface InstrumentState {\n readonly enabled: boolean\n readonly stats: RenderPhaseStats\n readonly nodeTrace: NodeTraceEntry[]\n readonly nodeTraceEnabled: boolean\n}\n\n/** Resolve instrumentation from PipelineContext or module-level defaults. */\nfunction resolveInstrumentation(ctx?: PipelineContext): InstrumentState {\n return {\n enabled: ctx?.instrumentEnabled ?? _instrumentEnabled,\n stats: ctx?.stats ?? _renderPhaseStats,\n nodeTrace: ctx?.nodeTrace ?? _nodeTrace,\n nodeTraceEnabled: ctx?.nodeTraceEnabled ?? _nodeTraceEnabled,\n }\n}\n\n/** Cell debug state — read once from globalThis per function scope. */\ntype CellDebug = { x: number; y: number; log: string[] }\n\n/** Read the cell debug target (set by SILVERY_CELL_DEBUG env var). */\nfunction getCellDebug(): CellDebug | undefined {\n return (globalThis as any).__silvery_cell_debug as CellDebug | undefined\n}\n\n/** Check if a rect covers the cell debug target point. */\nfunction cellCoversPoint(cellDbg: CellDebug, x: number, y: number, width: number, height: number): boolean {\n return x <= cellDbg.x && x + width > cellDbg.x && y <= cellDbg.y && y + height > cellDbg.y\n}\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/**\n * Emit render-phase stats to globalThis + loggily, then reset counters.\n * Called at end of renderPhase when instrumentation is active.\n */\nfunction emitRenderPhaseStats(\n stats: RenderPhaseStats,\n nodeTrace: NodeTraceEntry[],\n nodeTraceEnabled: boolean,\n tClone: number,\n tRender: number,\n): void {\n _renderPhaseCallCount++\n stats._callCount = _renderPhaseCallCount\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 RenderPhaseStats)[]) {\n ;(stats as any)[key] = 0\n }\n stats.cascadeMinDepth = 999\n stats.cascadeNodes = \"\"\n stats.scrollClearReason = \"\"\n stats.normalRepaintReason = \"\"\n\n // Export node trace for SILVERY_STRICT diagnosis\n if (nodeTraceEnabled && nodeTrace.length > 0) {\n const traceArr = ((globalThis as any).__silvery_node_trace ??= [] as NodeTraceEntry[][])\n traceArr.push([...nodeTrace])\n traceLog.debug?.(`${nodeTrace.length} nodes traced`)\n nodeTrace.length = 0\n }\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 const instr = resolveInstrumentation(ctx)\n if (instr.enabled) instr.stats.nodesVisited++\n const layout = node.boxRect\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 clearVirtualTextFlags(node)\n return\n }\n\n // Skip hidden nodes (Suspense support)\n if (node.hidden) {\n clearDirtyFlags(node)\n return\n }\n\n const props = node.props as BoxProps & TextProps\n\n // Resolve userSelect for SELECTABLE_FLAG stamping.\n const prevSelectableMode = buffer.getSelectableMode()\n const userSelect = props.userSelect\n if (userSelect === \"none\") {\n buffer.setSelectableMode(false)\n } else if (userSelect === \"text\" || userSelect === \"contain\") {\n buffer.setSelectableMode(true)\n }\n\n // Skip display=\"none\" nodes\n if (props.display === \"none\") {\n clearDirtyFlags(node)\n buffer.setSelectableMode(prevSelectableMode)\n return\n }\n\n // Skip nodes entirely off-screen (viewport clipping).\n // IMPORTANT: Don't clear dirty flags on off-screen nodes — they need their\n // flags intact so they render correctly when scrolled into view.\n const screenY = layout.y - scrollOffset\n if (screenY >= buffer.height || screenY + layout.height <= 0) {\n buffer.setSelectableMode(prevSelectableMode)\n return\n }\n\n // FAST PATH: Skip entire subtree if unchanged and we have a previous buffer.\n // layoutChanged uses layoutChangedThisFrame (symmetric between incremental/fresh).\n const layoutChanged = isCurrentEpoch(node.layoutChangedThisFrame)\n const childPositionChanged = !!(hasPrevBuffer && !layoutChanged && hasChildPositionChanged(node))\n const scrollOffsetChanged = !!(node.scrollState && node.scrollState.offset !== node.scrollState.prevOffset)\n\n const canSkipEntireSubtree =\n hasPrevBuffer &&\n !isDirty(node.dirtyBits, node.dirtyEpoch, CONTENT_BIT) &&\n !isDirty(node.dirtyBits, node.dirtyEpoch, STYLE_PROPS_BIT) &&\n !layoutChanged &&\n !isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT) &&\n !isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT) &&\n !childPositionChanged &&\n !ancestorLayoutChanged &&\n !scrollOffsetChanged\n\n // Diagnostics: node ID, cell debug, node trace\n const _nodeId = instr.enabled ? ((props.id as string | undefined) ?? \"\") : \"\"\n const _traceThis = instr.enabled && instr.nodeTraceEnabled && _nodeId\n const _cellDbg = getCellDebug()\n const _coversCellNow = _cellDbg && cellCoversPoint(_cellDbg, layout.x, screenY, layout.width, layout.height)\n const _coversCellPrev =\n _cellDbg &&\n node.prevLayout &&\n cellCoversPoint(\n _cellDbg,\n node.prevLayout.x,\n node.prevLayout.y - scrollOffset,\n node.prevLayout.width,\n node.prevLayout.height,\n )\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 (instr.enabled) {\n instr.stats.nodesSkipped++\n if (_traceThis) {\n instr.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 buffer.setSelectableMode(prevSelectableMode)\n return\n }\n if (instr.enabled) {\n instr.stats.nodesRendered++\n if (!hasPrevBuffer) instr.stats.noPrevBuffer++\n if (isDirty(node.dirtyBits, node.dirtyEpoch, CONTENT_BIT)) instr.stats.flagContentDirty++\n if (isDirty(node.dirtyBits, node.dirtyEpoch, STYLE_PROPS_BIT)) instr.stats.flagStylePropsDirty++\n if (layoutChanged) instr.stats.flagLayoutChanged++\n if (isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT)) instr.stats.flagSubtreeDirty++\n if (isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT)) instr.stats.flagChildrenDirty++\n if (childPositionChanged) instr.stats.flagChildPositionChanged++\n if (ancestorLayoutChanged) instr.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).\n const { absoluteChildMutated, descendantOverflowChanged } = buildCascadeInputs(node, hasPrevBuffer)\n\n // Cascade computation: reactive (alien-signals) is the production path.\n // SILVERY_REACTIVE=0 falls back to imperative computeCascade (bench only).\n const cascadeInputs = {\n hasPrevBuffer,\n contentDirty: isDirty(node.dirtyBits, node.dirtyEpoch, CONTENT_BIT),\n stylePropsDirty: isDirty(node.dirtyBits, node.dirtyEpoch, STYLE_PROPS_BIT),\n layoutChanged,\n subtreeDirty: isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT),\n childrenDirty: isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT),\n childPositionChanged,\n ancestorLayoutChanged,\n ancestorCleared,\n bgDirty: isDirty(node.dirtyBits, node.dirtyEpoch, BG_BIT),\n isTextNode: node.type === \"silvery-text\",\n hasBgColor: !!getEffectiveBg(props),\n absoluteChildMutated,\n descendantOverflowChanged,\n }\n let cascade: CascadeOutputs\n if (_reactiveEnabled) {\n const reactiveState = getReactiveState(node)\n syncToSignals(reactiveState, node, {\n hasPrevBuffer,\n layoutChanged,\n childPositionChanged,\n ancestorLayoutChanged,\n ancestorCleared,\n absoluteChildMutated,\n descendantOverflowChanged,\n hasBgColor: cascadeInputs.hasBgColor,\n })\n cascade = readReactiveCascade(reactiveState)\n\n // STRICT: verify reactive matches imperative oracle.\n if (_reactiveVerifyEnabled) {\n assertReactiveMatchesOracle(reactiveState, computeCascade(cascadeInputs), (props.id as string) ?? node.type)\n }\n } else {\n cascade = computeCascade(cascadeInputs)\n }\n\n // bgOnlyChange safety check: fillBg updates ALL cells in the region, which\n // would incorrectly overwrite children with their own explicit backgroundColor.\n // Fall back to the full path when any descendant has its own bg.\n if (cascade.bgOnlyChange && hasDescendantWithBg(node)) {\n const childrenNeedFreshRender =\n (hasPrevBuffer || ancestorCleared) && (cascade.contentAreaAffected || cascade.bgRefillNeeded)\n cascade = { ...cascade, bgOnlyChange: false, childrenNeedFreshRender }\n }\n const { contentRegionCleared, skipBgFill, childrenNeedFreshRender } = cascade\n\n // DIAG: Per-node trace, cascade tracking, and cell debug\n if (instr.enabled || (_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 instr.enabled,\n instr.stats,\n instr.nodeTrace,\n )\n }\n\n // DISABLED: text style-only fast path causes incremental rendering mismatches\n // (fg colors lost). Needs investigation before re-enabling.\n const useTextStyleFastPath = false\n\n // Clear stale regions in the cloned buffer before rendering content.\n // Suppress clearing when using the text style-only fast path — chars are\n // correct in the clone and clearNodeRegion would destroy them with spaces.\n executeRegionClearing(\n node,\n buffer,\n layout,\n scrollOffset,\n clipBounds,\n bufferIsCloned,\n layoutChanged,\n useTextStyleFastPath ? false : contentRegionCleared,\n descendantOverflowChanged,\n instr.enabled,\n instr.stats,\n nodeState.inheritedBg,\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 isDirty(node.dirtyBits, node.dirtyEpoch, STYLE_PROPS_BIT) ||\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) ? nodeState.inheritedBg.color : undefined\n if (needsOwnRepaint) {\n renderOwnContent(\n node,\n buffer,\n layout,\n props,\n nodeState,\n skipBgFill,\n instr.enabled,\n instr.stats,\n ctx,\n cascade.bgOnlyChange,\n useTextStyleFastPath,\n )\n }\n\n // Compute inherited bg/fg for children. If this node sets backgroundColor,\n // color, or theme, children inherit from this node. Otherwise, inherit from parent.\n const effectiveBg = getEffectiveBg(props)\n const childInheritedBg = effectiveBg\n ? { color: parseColor(effectiveBg), ancestorRect: node.boxRect }\n : nodeTheme\n ? { color: parseColor(nodeTheme.bg), ancestorRect: node.boxRect }\n : nodeState.inheritedBg\n // color=\"inherit\"/\"currentColor\" is a pass-through — children inherit\n // from this node's OWN inheritedFg (our parent's color), not from null.\n // Without this, an intermediate \"inherit\" node breaks the cascade to\n // grandchildren because parseColor(\"inherit\") returns null, clobbering\n // the ancestor fg. See Bead km-silvery.color-inherit.\n const isInheritKeyword = props.color === \"inherit\" || props.color === \"currentColor\"\n const childInheritedFg = isInheritKeyword\n ? nodeState.inheritedFg\n : props.color\n ? parseColor(props.color)\n : nodeTheme\n ? parseColor(nodeTheme.fg)\n : nodeState.inheritedFg\n\n // Render children — pass inherited bg/fg so children don't walk the parent chain\n const childState: NodeRenderState = { ...nodeState, inheritedBg: childInheritedBg, inheritedFg: childInheritedFg }\n if (isScrollContainer) {\n renderScrollContainerChildren(node, buffer, props, childState, 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 childState,\n childPositionChanged,\n contentRegionCleared,\n childrenNeedFreshRender,\n ctx,\n )\n }\n\n // Outlines are NOT rendered here — the decoration phase (post-content)\n // walks the tree independently and draws outlines for every node with\n // outlineStyle, using per-cell snapshots to clear prev positions next\n // frame. See decoration-phase.ts.\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 // Restore parent's selectable mode\n buffer.setSelectableMode(prevSelectableMode)\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 || !isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT) || node.children === undefined) {\n return { absoluteChildMutated: false, descendantOverflowChanged: false }\n }\n\n // Phase 3b: Read cached epoch values computed during layout phase\n // (propagateLayout), avoiding per-node tree walks in the render phase.\n return {\n absoluteChildMutated: isDirty(node.dirtyBits, node.dirtyEpoch, ABS_CHILD_BIT),\n descendantOverflowChanged: isDirty(node.dirtyBits, node.dirtyEpoch, DESC_OVERFLOW_BIT),\n }\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[\"boxRect\"]>,\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: RenderPhaseStats,\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 isDirty(node.dirtyBits, node.dirtyEpoch, CONTENT_BIT) && \"C\",\n isDirty(node.dirtyBits, node.dirtyEpoch, STYLE_PROPS_BIT) && \"P\",\n isDirty(node.dirtyBits, node.dirtyEpoch, BG_BIT) && \"B\",\n isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT) && \"S\",\n isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT) && \"Ch\",\n childPositionChanged && \"CP\",\n ]\n .filter(Boolean)\n .join(\",\")\n const childrenNeedRepaint_ =\n isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT) || 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 isDirty(node.dirtyBits, node.dirtyEpoch, CONTENT_BIT) && \"C\",\n isDirty(node.dirtyBits, node.dirtyEpoch, STYLE_PROPS_BIT) && \"P\",\n isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT) && \"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 isDirty(node.dirtyBits, node.dirtyEpoch, CONTENT_BIT) && \"C\",\n isDirty(node.dirtyBits, node.dirtyEpoch, STYLE_PROPS_BIT) && \"P\",\n layoutChanged && \"L\",\n isDirty(node.dirtyBits, node.dirtyEpoch, SUBTREE_BIT) && \"S\",\n isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT) && \"Ch\",\n childPositionChanged && \"CP\",\n isDirty(node.dirtyBits, node.dirtyEpoch, BG_BIT) && \"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[\"boxRect\"]>,\n scrollOffset: number,\n clipBounds: ClipBounds | undefined,\n bufferIsCloned: boolean,\n layoutChanged: boolean,\n contentRegionCleared: boolean,\n descendantOverflowChanged: boolean,\n instrumentEnabled: boolean,\n stats: RenderPhaseStats,\n threadedInheritedBg: NodeRenderState[\"inheritedBg\"],\n): void {\n if (contentRegionCleared) {\n if (instrumentEnabled) stats.clearOps++\n clearNodeRegion(node, buffer, layout, scrollOffset, clipBounds, layoutChanged, threadedInheritedBg)\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, threadedInheritedBg)\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, threadedInheritedBg)\n }\n\n // Outline cleanup is handled entirely by the decoration phase (outlines\n // are treated as a separate pass that snapshots under-cells and restores\n // them next frame). No outline-aware work needed here.\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[\"boxRect\"]>,\n props: BoxProps & TextProps,\n nodeState: NodeRenderState,\n skipBgFill: boolean,\n instrumentEnabled: boolean,\n stats: RenderPhaseStats,\n ctx?: PipelineContext,\n bgOnlyChange = false,\n useTextStyleFastPath = false,\n): Color | undefined {\n // O(1) inherited bg/fg from nodeState — threaded top-down, no parent chain walks.\n const boxInheritedBg = node.type === \"silvery-box\" && !getEffectiveBg(props) ? nodeState.inheritedBg.color : undefined\n\n if (node.type === \"silvery-box\") {\n if (instrumentEnabled) stats.boxNodes++\n // inheritedFg is threaded so renderBorder can resolve borderColor=\"currentColor\".\n renderBox(node, buffer, layout, props, nodeState, skipBgFill, boxInheritedBg, bgOnlyChange, nodeState.inheritedFg)\n } else if (node.type === \"silvery-text\") {\n if (instrumentEnabled) stats.textNodes++\n // O(1) inherited bg/fg — threaded top-down through nodeState.\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 = nodeState.inheritedBg.color\n const textInheritedFg = nodeState.inheritedFg\n\n // Style-only fast path for text nodes: when only visual style props changed\n // (color, bold, dim, inverse, etc.) but text content is identical, skip the\n // expensive collectTextWithBg → formatTextLines → renderGraphemes pipeline.\n // Instead, restyle existing cells in-place with the new style.\n //\n // Conditions (pre-computed as useTextStyleFastPath):\n // 1. hasPrevBuffer: cloned buffer has correct chars from previous frame\n // 2. isStyleOnlyDirty: only style props changed (no content, bg, or children)\n // 3. No nested children with bg: restyleRegion would overwrite their bg\n // 4. Not ancestorCleared/ancestorLayoutChanged: cells are at correct positions\n //\n // This avoids O(text_length) text processing for the common case of\n // cursor/selection styling (just color/bold/inverse changes on text nodes).\n if (useTextStyleFastPath) {\n const style = getTextStyle(props)\n if (style.fg === null && textInheritedFg !== undefined) {\n style.fg = textInheritedFg\n }\n const effectiveBg = style.bg !== null ? style.bg : (textInheritedBg ?? null)\n const { x, width, height } = layout\n const y = layout.y - nodeState.scrollOffset\n buffer.restyleRegion(x, y, width, height, {\n fg: style.fg,\n bg: effectiveBg,\n underlineColor: style.underlineColor ?? null,\n attrs: style.attrs,\n })\n } else {\n renderText(node, buffer, layout, props, nodeState, textInheritedBg, textInheritedFg, ctx)\n }\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 {\n clipBounds,\n hasPrevBuffer,\n ancestorCleared,\n bufferIsCloned,\n ancestorLayoutChanged,\n inheritedBg,\n inheritedFg,\n } = nodeState\n const instr = resolveInstrumentation(ctx)\n const layout = node.boxRect\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 // Scroll containers clip vertically (viewport) but not horizontally —\n // horizontal containment is handled by text wrapping, not clipping.\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 ||\n isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT) ||\n childrenNeedFreshRender ||\n visibleRangeChanged\n ? getEffectiveBg(props)\n ? parseColor(getEffectiveBg(props)!)\n : inheritedBg.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: isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT),\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 (instr.enabled) {\n instr.stats.scrollContainerCount++\n if (tier !== \"subtree-only\" || stickyForceRefresh) {\n instr.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 instr.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 = isCurrentEpoch(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.boxRect\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 // Phase 4: dirty set pre-check — skip clean subtrees without function call overhead.\n if (canSkipChildSubtree(child, thisChildHasPrev, childAncestorLayoutChanged)) {\n continue\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 inheritedBg,\n inheritedFg,\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?.boxRect) 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 inheritedBg,\n inheritedFg,\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 {\n scrollOffset,\n clipBounds,\n hasPrevBuffer,\n ancestorCleared,\n bufferIsCloned,\n ancestorLayoutChanged,\n inheritedBg,\n inheritedFg,\n } = nodeState\n const instr = resolveInstrumentation(ctx)\n const layout = node.boxRect\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 =\n isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT) || childPositionChanged || childrenNeedFreshRender\n if (instr.enabled && childrenNeedRepaint && hasPrevBuffer) {\n instr.stats.normalChildrenRepaint++\n const reasons: string[] = []\n if (isDirty(node.dirtyBits, node.dirtyEpoch, CHILDREN_BIT)) reasons.push(\"childrenDirty\")\n if (childPositionChanged) reasons.push(\"childPositionChanged\")\n if (childrenNeedFreshRender) reasons.push(\"childrenNeedFreshRender\")\n instr.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 = isCurrentEpoch(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 // Phase 4: dirty set pre-check — skip clean subtrees without function call overhead.\n // The existing canSkipEntireSubtree inside renderNodeToBuffer is preserved as\n // the second line of defense for edge cases the pre-check doesn't cover.\n if (canSkipChildSubtree(child, childHasPrev, childAncestorLayoutChanged)) {\n continue\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 inheritedBg,\n inheritedFg,\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?.boxRect) 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 inheritedBg,\n inheritedFg,\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 inheritedBg,\n inheritedFg,\n },\n ctx,\n )\n }\n }\n}\n\n// ============================================================================\n// Dirty Set Pre-Check (Phase 4)\n// ============================================================================\n\n/**\n * O(1) pre-check: can we skip calling renderNodeToBuffer() on this child entirely?\n *\n * This is the Phase 4 \"dirty set rendering\" optimization. Instead of calling\n * renderNodeToBuffer() on every child (which checks canSkipEntireSubtree and\n * returns early for clean nodes), we skip the function call entirely for\n * subtrees with no dirty descendants.\n *\n * The pre-check is CONSERVATIVE: it only skips when we're certain the subtree\n * is clean. False negatives (calling renderNodeToBuffer unnecessarily) are\n * harmless — the existing canSkipEntireSubtree check inside handles them.\n *\n * Key insight: subtreeDirtyEpoch is propagated from every dirty node to the\n * root by markSubtreeDirty (reconciler) and propagateLayout (layout phase).\n * If subtreeDirtyEpoch !== currentEpoch, no descendant is dirty. Combined\n * with layoutChangedThisFrame (set by layout phase but NOT included in\n * subtreeDirtyEpoch on self — only on parent), this covers all dirty paths.\n */\nfunction canSkipChildSubtree(child: AgNode, childHasPrev: boolean, childAncestorLayoutChanged: boolean): boolean {\n // Can't skip without a previous buffer (first render or dimension change)\n if (!childHasPrev) return false\n // Ancestor layout change forces re-render at new position\n if (childAncestorLayoutChanged) return false\n // Any descendant dirty (includes own flags via markSubtreeDirty)\n if (isDirty(child.dirtyBits, child.dirtyEpoch, SUBTREE_BIT)) return false\n // Own layout changed (layout phase sets this but only propagates\n // subtreeDirtyEpoch to PARENT, not self)\n if (isCurrentEpoch(child.layoutChangedThisFrame)) return false\n // Defensive: scroll offset changed without dirty propagation\n if (child.scrollState && child.scrollState.offset !== child.scrollState.prevOffset) return false\n return true\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 *\n * With epoch-stamped flags, this is only needed when a subtree is SKIPPED\n * by the fast path (clearDirtyFlags on skipped subtrees) or for the\n * render-phase-adapter. The normal render path relies on advanceRenderEpoch()\n * to expire all flags at once — O(1) instead of O(N).\n */\nfunction clearNodeDirtyFlags(node: AgNode): void {\n node.dirtyBits = 0\n node.dirtyEpoch = INITIAL_EPOCH\n node.layoutChangedThisFrame = INITIAL_EPOCH\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.boxRect && child.prevLayout) {\n if (child.boxRect.x !== child.prevLayout.x || child.boxRect.y !== child.prevLayout.y) {\n return true\n }\n }\n }\n return false\n}\n\n// hasDescendantOverflowChanged and _checkDescendantOverflow removed in Phase 3b:\n// Now cached as descendantOverflowChangedEpoch on AgNode, computed during layout phase.\n\n/**\n * Check if any descendant has an explicit backgroundColor.\n *\n * Used by the bgOnlyChange fast path: fillBg() updates ALL cells in the region\n * with the parent's bg. If a descendant has its own bg, those cells would be\n * incorrectly overwritten (the descendant is clean and won't re-render to fix it).\n *\n * Only checks Box nodes with explicit backgroundColor or effective bg from theme.\n * Text nodes with backgroundColor are also checked since they render their own bg.\n * Stops at first match (early exit).\n *\n * Performance: walks the child tree, but only runs when bgOnlyChange is true\n * (bg changed, no other flags). This is the cursor-move hot path where trees\n * are typically small (card contents: ~5-20 nodes).\n */\nfunction hasDescendantWithBg(node: AgNode): boolean {\n for (const child of node.children) {\n if (getEffectiveBg(child.props as BoxProps)) return true\n if (child.children.length > 0 && hasDescendantWithBg(child)) return true\n }\n return false\n}\n\n/**\n * Check if a text node has any virtual text children with explicit backgroundColor.\n *\n * Used by the text style-only fast path: restyleRegion() applies a uniform\n * style to all cells. If nested children have their own bg, the uniform restyle\n * would overwrite it (those children rendered their own bg during the original\n * renderText, and won't re-render to restore it).\n */\nfunction hasChildWithBg(node: AgNode): boolean {\n for (const child of node.children) {\n if ((child.props as BoxProps).backgroundColor) return true\n if (child.children.length > 0 && hasChildWithBg(child)) return true\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[\"boxRect\"]>,\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 * (horizontal containment is via layout OVERFLOW_HIDDEN, not render clipping). */\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 * 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[\"boxRect\"]>,\n scrollOffset: number,\n clipBounds: ClipBounds | undefined,\n threadedInheritedBg: NodeRenderState[\"inheritedBg\"],\n): void {\n const clearBg = threadedInheritedBg.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 && isCurrentEpoch(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 (isDirty(child.dirtyBits, child.dirtyEpoch, SUBTREE_BIT) && 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 boxRect (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[\"boxRect\"]>,\n scrollOffset: number,\n clipBounds: ClipBounds | undefined,\n layoutChanged: boolean,\n threadedInheritedBg: NodeRenderState[\"inheritedBg\"],\n): void {\n const inherited = threadedInheritedBg\n const clearBg = inherited.color\n const screenY = layout.y - scrollOffset\n\n // Clip to parent's boxRect to prevent oversized children from clearing\n // beyond their parent's bounds and bleeding inherited bg into sibling regions.\n const parentRect = node.parent?.boxRect\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 const _cellDbg2 = getCellDebug()\n if (_cellDbg2 && cellCoversPoint(_cellDbg2, clearX, clearY, clearWidth, clearHeight)) {\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 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 boxRect.\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[\"boxRect\"]>,\n scrollOffset: number,\n clipBounds: ClipBounds | undefined,\n layoutChanged: boolean,\n inherited: NodeRenderState[\"inheritedBg\"],\n): void {\n if (!layoutChanged || !node.prevLayout) return\n const prev = node.prevLayout\n\n const _cellDbg3 = getCellDebug()\n const _prevCoversCell3 =\n _cellDbg3 && cellCoversPoint(_cellDbg3, prev.x, prev.y - scrollOffset, prev.width, prev.height)\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 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?.boxRect\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?.boxRect) {\n const parentProps = parent.props as BoxProps\n const border = getBorderSize(parentProps)\n const padding = getPadding(parentProps)\n const parentRight = parent.boxRect.x + parent.boxRect.width - border.right - padding.right\n const parentBottom = parent.boxRect.y - scrollOffset + parent.boxRect.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","/**\n * Backdrop fade pass.\n *\n * Runs AFTER the content + decoration phases, BEFORE the output phase. Walks\n * the tree to find nodes with `data-backdrop-fade` or\n * `data-backdrop-fade-excluded` markers, then applies a cell-level color\n * transform to the affected rect(s) on the buffer.\n *\n * Tiers (`colorLevel`):\n * - `truecolor` / `256`: `cell.fg = blend(fg, bg, fadeAmount)` in OKLab via\n * `@silvery/color`. Fully deterministic — produces hex output.\n * - `basic` (ANSI 16): stamps `attrs.dim` (SGR 2) on each cell. Can't blend\n * arbitrary palette slots, so this is best-effort.\n * - `none` (monochrome): no-op. Modal border + box-drawing carry separation.\n *\n * # Incremental correctness\n *\n * The pass mutates the final buffer in place after the decoration phase. The\n * same buffer is what `ag.render()` stores as `_prevBuffer`. This is safe\n * because:\n *\n * 1. The backdrop pass is a pure function of (tree markers, buffer cells).\n * 2. `renderPhase` writes the same pre-transform pixels on both fresh and\n * incremental paths (this is the existing incremental invariant).\n * 3. Running the same pure transform over both paths produces identical post-\n * transform buffers — `SILVERY_STRICT=1` (cell-by-cell compare between\n * incremental and fresh render) stays green.\n * 4. On the NEXT frame, `renderPhase` clones the post-transform buffer.\n * Cells in backdrop regions stay faded (fast-path skipped). Dirty cells\n * get re-rendered with pre-transform content, then the pass re-applies\n * fade. Result matches a fresh render.\n *\n * If the backdrop region itself moves (modal open/close, Backdrop mount/\n * unmount), the tree change triggers dirty re-renders in the affected area.\n * The new region is computed from the current tree — the pass doesn't carry\n * state across frames.\n */\n\nimport { blend } from \"@silvery/theme\"\nimport type { AgNode, Rect } from \"@silvery/ag/types\"\nimport { ansi256ToRgb, isDefaultBg, type Color, type TerminalBuffer } from \"../buffer\"\n\nexport type BackdropColorLevel = \"none\" | \"basic\" | \"256\" | \"truecolor\"\n\nexport interface BackdropFadeOptions {\n /** Terminal color tier. Controls which transform strategy runs. */\n colorLevel?: BackdropColorLevel\n}\n\nconst FADE_ATTR = \"data-backdrop-fade\"\nconst FADE_EXCLUDE_ATTR = \"data-backdrop-fade-excluded\"\n\ninterface FadeRect {\n rect: Rect\n amount: number\n}\n\n/**\n * Apply backdrop-fade to the buffer based on tree markers.\n *\n * Returns `true` if at least one region was modified; `false` if nothing\n * changed (no markers found, or colorLevel is `none`).\n */\n/**\n * Quick check: does the tree contain any backdrop markers? Used as a gate so\n * we don't clone the buffer every frame when no fade is active. Walks the\n * full tree once (O(N)) — the alternative (tracking dirty markers in the\n * reconciler) is more complex and the walk is cheap compared to the pass.\n */\nexport function hasBackdropMarkers(root: AgNode): boolean {\n const props = root.props as Record<string, unknown>\n if (props[FADE_ATTR] !== undefined || props[FADE_EXCLUDE_ATTR] !== undefined) return true\n for (const child of root.children) {\n if (hasBackdropMarkers(child)) return true\n }\n return false\n}\n\nexport function applyBackdropFade(\n root: AgNode,\n buffer: TerminalBuffer,\n options?: BackdropFadeOptions,\n): boolean {\n const colorLevel: BackdropColorLevel = options?.colorLevel ?? \"truecolor\"\n if (colorLevel === \"none\") return false\n\n const includes: FadeRect[] = []\n const excludes: FadeRect[] = []\n collectBackdropMarkers(root, includes, excludes)\n\n if (includes.length === 0 && excludes.length === 0) return false\n\n const strategy: FadeStrategy = colorLevel === \"basic\" ? \"dim\" : \"blend\"\n\n let modified = false\n\n // Pass 1: data-backdrop-fade — fade cells INSIDE each marked rect.\n for (const { rect, amount } of includes) {\n if (amount <= 0) continue\n if (fadeRect(buffer, rect, amount, strategy)) modified = true\n }\n\n // Pass 2: data-backdrop-fade-excluded — fade everything OUTSIDE each marked\n // rect (the modal \"cuts a hole\"). When multiple excluded rects exist, each\n // is processed independently: the union of their rects is the crisp region.\n if (excludes.length > 0) {\n const fullRect: Rect = { x: 0, y: 0, width: buffer.width, height: buffer.height }\n for (const { rect, amount } of excludes) {\n if (amount <= 0) continue\n if (fadeRectExcluding(buffer, fullRect, rect, amount, strategy)) modified = true\n }\n }\n\n return modified\n}\n\ntype FadeStrategy = \"blend\" | \"dim\"\n\nfunction collectBackdropMarkers(node: AgNode, includes: FadeRect[], excludes: FadeRect[]): void {\n const props = node.props as Record<string, unknown>\n const includeRaw = props[FADE_ATTR]\n const excludeRaw = props[FADE_EXCLUDE_ATTR]\n\n if (includeRaw !== undefined || excludeRaw !== undefined) {\n const rect = node.screenRect ?? node.scrollRect ?? node.boxRect\n if (rect && rect.width > 0 && rect.height > 0) {\n const inc = parseFade(includeRaw)\n if (inc !== null) includes.push({ rect, amount: inc })\n const exc = parseFade(excludeRaw)\n if (exc !== null) excludes.push({ rect, amount: exc })\n }\n }\n\n for (const child of node.children) {\n collectBackdropMarkers(child, includes, excludes)\n }\n}\n\nfunction parseFade(raw: unknown): number | null {\n if (raw === undefined || raw === null) return null\n const n = typeof raw === \"number\" ? raw : Number(raw)\n if (!Number.isFinite(n)) return null\n if (n <= 0) return null\n return n > 1 ? 1 : n\n}\n\nfunction fadeRect(\n buffer: TerminalBuffer,\n rect: Rect,\n amount: number,\n strategy: FadeStrategy,\n): boolean {\n const x0 = Math.max(0, rect.x)\n const y0 = Math.max(0, rect.y)\n const x1 = Math.min(buffer.width, rect.x + rect.width)\n const y1 = Math.min(buffer.height, rect.y + rect.height)\n if (x0 >= x1 || y0 >= y1) return false\n\n let any = false\n for (let y = y0; y < y1; y++) {\n for (let x = x0; x < x1; x++) {\n if (fadeCell(buffer, x, y, amount, strategy)) any = true\n }\n }\n return any\n}\n\nfunction fadeRectExcluding(\n buffer: TerminalBuffer,\n outer: Rect,\n inner: Rect,\n amount: number,\n strategy: FadeStrategy,\n): boolean {\n const ox0 = Math.max(0, outer.x)\n const oy0 = Math.max(0, outer.y)\n const ox1 = Math.min(buffer.width, outer.x + outer.width)\n const oy1 = Math.min(buffer.height, outer.y + outer.height)\n\n const ix0 = Math.max(ox0, inner.x)\n const iy0 = Math.max(oy0, inner.y)\n const ix1 = Math.min(ox1, inner.x + inner.width)\n const iy1 = Math.min(oy1, inner.y + inner.height)\n const innerValid = ix0 < ix1 && iy0 < iy1\n\n let any = false\n for (let y = oy0; y < oy1; y++) {\n for (let x = ox0; x < ox1; x++) {\n if (innerValid && x >= ix0 && x < ix1 && y >= iy0 && y < iy1) continue\n if (fadeCell(buffer, x, y, amount, strategy)) any = true\n }\n }\n return any\n}\n\n/**\n * Fade a single cell. Returns true if the cell was modified.\n *\n * - `blend` strategy: mix fg toward bg in OKLab. When either color is null or\n * the default-bg sentinel, also stamps `dim`.\n * - `dim` strategy: stamp `dim` attribute.\n *\n * Wide-char continuation cells are skipped — they share styling with the\n * leading cell and modifying them separately would desync.\n */\nfunction fadeCell(\n buffer: TerminalBuffer,\n x: number,\n y: number,\n amount: number,\n strategy: FadeStrategy,\n): boolean {\n // Skip continuation half of wide chars — the leading cell carries the style.\n if (buffer.isCellContinuation(x, y)) return false\n\n const cell = buffer.getCell(x, y)\n\n if (strategy === \"dim\") {\n if (cell.attrs.dim) return false\n buffer.setCell(x, y, { ...cell, attrs: { ...cell.attrs, dim: true } })\n return true\n }\n\n // strategy === \"blend\"\n const fgHex = colorToHex(cell.fg)\n const bgHex = colorToHex(cell.bg)\n\n if (fgHex && bgHex) {\n // OKLab blend fg toward bg. amount=0.4 means 40% of the way to bg.\n const blendedHex = blend(fgHex, bgHex, amount)\n const blendedRgb = hexToRgb(blendedHex)\n if (!blendedRgb) return false\n buffer.setCell(x, y, { ...cell, fg: blendedRgb })\n return true\n }\n\n // Fallback — bg unresolvable (DEFAULT_BG / null) or fg null. Stamp dim so\n // the cell still reads as \"backdrop\". This covers cells that inherit the\n // terminal bg where we can't compute a blend target.\n if (cell.attrs.dim) return false\n buffer.setCell(x, y, { ...cell, attrs: { ...cell.attrs, dim: true } })\n return true\n}\n\n/** Convert a buffer Color to a `#rrggbb` hex string, or null if unresolvable. */\nfunction colorToHex(color: Color): string | null {\n if (color === null) return null\n if (typeof color === \"number\") {\n const rgb = ansi256ToRgb(color)\n return rgbToHex(rgb.r, rgb.g, rgb.b)\n }\n if (isDefaultBg(color)) return null\n return rgbToHex(color.r, color.g, color.b)\n}\n\nfunction rgbToHex(r: number, g: number, b: number): string {\n const clamp = (n: number) => {\n const v = Math.max(0, Math.min(255, Math.round(n)))\n return v.toString(16).padStart(2, \"0\")\n }\n return `#${clamp(r)}${clamp(g)}${clamp(b)}`\n}\n\nfunction hexToRgb(hex: string): { r: number; g: number; b: number } | null {\n if (typeof hex !== \"string\") return null\n let s = hex\n if (s.startsWith(\"#\")) s = s.slice(1)\n if (s.length === 3) {\n s = s[0]! + s[0]! + s[1]! + s[1]! + s[2]! + s[2]!\n }\n if (s.length !== 6) return null\n const r = parseInt(s.slice(0, 2), 16)\n const g = parseInt(s.slice(2, 4), 16)\n const b = parseInt(s.slice(4, 6), 16)\n if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) return null\n return { r, g, b }\n}\n","/**\n * Ag — tree + layout engine + renderer.\n *\n * The sole pipeline entry point. Two independent phases:\n * - ag.layout(dims) — measure + flexbox → positions/sizes\n * - ag.render() — positioned tree → cell grid → TextFrame\n *\n * The output phase (buffer → ANSI) is NOT part of ag — it lives in term.paint().\n *\n * @example\n * ```ts\n * const ag = createAg(root, { measurer })\n * ag.layout({ cols: 80, rows: 24 })\n * const { frame, buffer } = ag.render()\n * const output = term.paint(buffer, prevBuffer)\n * ```\n */\n\nimport { createLogger } from \"loggily\"\nimport type { AgNode, AgNodeType } from \"@silvery/ag/types\"\nimport { getRenderEpoch, INITIAL_EPOCH, ALL_RECONCILER_BITS, CONTENT_BIT, STYLE_PROPS_BIT } from \"@silvery/ag/epoch\"\nimport { getLayoutEngine } from \"./layout-engine\"\nimport type { TextFrame } from \"@silvery/ag/text-frame\"\nimport { type TerminalBuffer, createTextFrame } from \"./buffer\"\nimport { runWithMeasurer, type Measurer } from \"./unicode\"\nimport { measurePhase } from \"./pipeline/measure-phase\"\nimport {\n layoutPhase,\n scrollPhase,\n stickyPhase,\n scrollrectPhase,\n scrollrectPhaseSimple,\n notifyLayoutSubscribers,\n detectPipelineFeatures,\n strictLayoutOverflowCheck,\n} from \"./pipeline/layout-phase\"\nimport { renderPhase, clearBgConflictWarnings } from \"./pipeline/render-phase\"\nimport { applyBackdropFade, hasBackdropMarkers, type BackdropColorLevel } from \"./pipeline/backdrop-phase\"\nimport { clearDirtyTracking, hasScrollDirty } from \"@silvery/ag/dirty-tracking\"\nimport type { PipelineContext } from \"./pipeline/types\"\n\nconst log = createLogger(\"silvery:render\")\nconst baseLog = createLogger(\"@silvery/ag-react\")\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface AgLayoutOptions {\n skipLayoutNotifications?: boolean\n skipScrollStateUpdates?: boolean\n}\n\nexport interface AgRenderOptions {\n /** Force fresh render — no incremental, doesn't update internal prevBuffer. */\n fresh?: boolean\n /** Override prevBuffer for this render (bypasses internal tracking). */\n prevBuffer?: TerminalBuffer | null\n}\n\nexport interface CreateAgOptionsInternal {\n /** Width measurer scoped to terminal capabilities. */\n measurer?: Measurer\n /**\n * Terminal color tier for the backdrop-fade pass (see `backdrop-phase.ts`).\n * Defaults to `\"truecolor\"` (OKLab blend). Set to `\"basic\"` at ANSI 16 tier\n * (SGR 2 dim) or `\"none\"` to disable the pass entirely.\n */\n colorLevel?: BackdropColorLevel\n}\n\nexport interface AgRenderResult {\n /** Immutable TextFrame snapshot of the rendered output. */\n readonly frame: TextFrame\n /**\n * Post-transform buffer for painting. Includes backdrop-fade cell transforms\n * (if any). Pass this to `term.paint()` / `outputPhase()` as `next`.\n */\n readonly buffer: TerminalBuffer\n /**\n * Pre-transform buffer. Identical to `buffer` when no backdrop-fade markers\n * are present. Callers managing their own incremental prev-buffer state must\n * carry THIS (not `buffer`) forward, so the next frame's render phase starts\n * from pre-fade cells and the fade pass re-applies deterministically.\n */\n readonly carryForwardBuffer: TerminalBuffer\n /** Previous frame's buffer (null on first render). For output-phase diffing. */\n readonly prevBuffer: TerminalBuffer | null\n}\n\nexport interface Ag {\n /** The root AgNode tree. */\n readonly root: AgNode\n\n // -------------------------------------------------------------------------\n // Pipeline\n // -------------------------------------------------------------------------\n\n /**\n * Run layout phases: measure → flexbox → scroll → sticky → scrollRect → notify.\n * Mutates layout nodes in place.\n */\n layout(dims: { cols: number; rows: number }, options?: AgLayoutOptions): void\n\n /**\n * Run the render phase: positioned tree → cell grid → TextFrame.\n * Uses internal prevBuffer for incremental rendering.\n * Returns frame (public read API) + buffer/prevBuffer (for output phase).\n */\n render(options?: AgRenderOptions): AgRenderResult\n\n /** Reset internal prevBuffer (call on resize — forces fresh render next frame). */\n resetBuffer(): void\n\n // -------------------------------------------------------------------------\n // Tree Mutation API (Phase 4)\n // -------------------------------------------------------------------------\n\n /** Create a new AgNode with a layout node. */\n createNode(type: AgNodeType, props: Record<string, unknown>): AgNode\n\n /** Insert child at index in both ag tree and layout tree. */\n insertChild(parent: AgNode, child: AgNode, index: number): void\n\n /** Remove child from both ag tree and layout tree. */\n removeChild(parent: AgNode, child: AgNode): void\n\n /** Update node props (applies to layout node if layout-affecting). */\n updateProps(node: AgNode, props: Record<string, unknown>, oldProps?: Record<string, unknown>): void\n\n /** Update text content on a node. */\n setText(node: AgNode, text: string): void\n\n /** Structural text representation (no layout). */\n toString(): string\n}\n\nexport interface CreateAgOptions {\n /** Width measurer scoped to terminal capabilities. */\n measurer?: Measurer\n /**\n * Terminal color tier for the backdrop-fade pass. Defaults to `\"truecolor\"`.\n * See `backdrop-phase.ts` for tier semantics.\n */\n colorLevel?: BackdropColorLevel\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createAg(root: AgNode, options?: CreateAgOptions): Ag {\n const measurer = options?.measurer\n const colorLevel: BackdropColorLevel = options?.colorLevel ?? \"truecolor\"\n const ctx: PipelineContext | undefined = measurer ? { measurer } : undefined\n let _prevBuffer: TerminalBuffer | null = null\n\n // Feature flags — one-way: once true, stays true for the lifetime of this Ag.\n // This ensures dynamically mounted scroll/sticky components enable their phases\n // and never get skipped again.\n let hasScroll = false\n let hasSticky = false\n\n function doLayout(\n cols: number,\n rows: number,\n opts?: AgLayoutOptions,\n ): { tMeasure: number; tLayout: number; tScroll: number; tScrollRect: number; tNotify: number } {\n // Layout-on-demand gate: skip ALL layout phases when Flexily reports\n // no dirty nodes, no scroll offset changed, and dimensions haven't changed.\n // This eliminates ~38% of per-frame pipeline cost for cursor/style-only changes.\n // First render always has isDirty (Flexily nodes start dirty on creation).\n // scrollTo/scrollOffset changes don't affect Flexily (they don't change\n // dimensions) but DO need scroll/sticky/scrollRect/notify phases to run.\n const prevRootLayout = root.boxRect\n const dimensionsChanged = prevRootLayout && (prevRootLayout.width !== cols || prevRootLayout.height !== rows)\n if (!dimensionsChanged && !root.layoutNode?.isDirty() && !hasScrollDirty()) {\n log.debug?.(\"layout: skipped (Flexily clean, no scrollDirty, dimensions unchanged)\")\n // Even when the full layout phase is skipped, style-only changes\n // (outline add/remove, absolute child structural changes) need cascade\n // input bits computed for the render phase. Without this, the render\n // phase can't detect outline mutations and stale outline pixels persist.\n layoutPhase(root, cols, rows)\n return { tMeasure: 0, tLayout: 0, tScroll: 0, tScrollRect: 0, tNotify: 0 }\n }\n\n using render = baseLog.span(\"pipeline\", { width: cols, height: rows })\n\n let tMeasure: number\n {\n using _m = render.span(\"measure\")\n const t = performance.now()\n measurePhase(root, ctx)\n tMeasure = performance.now() - t\n log.debug?.(`measure: ${tMeasure.toFixed(2)}ms`)\n }\n\n let tLayout: number\n {\n using _l = render.span(\"layout\")\n const t = performance.now()\n layoutPhase(root, cols, rows)\n tLayout = performance.now() - t\n log.debug?.(`layout: ${tLayout.toFixed(2)}ms`)\n }\n\n // STRICT invariant: verify no child overflows its parent's inner width.\n // Catches fit-content/snug-content/measure-phase bugs at the source.\n strictLayoutOverflowCheck(root)\n\n // Detect features for phase skipping. One-way merge: false → true only.\n // This scan runs every layout pass to catch newly mounted components.\n if (!hasScroll || !hasSticky) {\n const features = detectPipelineFeatures(root)\n if (features.hasScroll) hasScroll = true\n if (features.hasSticky) hasSticky = true\n }\n\n let tScroll: number\n if (hasScroll) {\n using _s = render.span(\"scroll\")\n const t = performance.now()\n scrollPhase(root, { skipStateUpdates: opts?.skipScrollStateUpdates })\n tScroll = performance.now() - t\n } else {\n tScroll = 0\n }\n\n if (hasSticky) {\n stickyPhase(root)\n }\n\n let tScrollRect: number\n {\n using _r = render.span(\"scrollRect\")\n const t = performance.now()\n if (hasScroll || hasSticky) {\n scrollrectPhase(root)\n } else {\n // Fast path: no scroll offsets or sticky positions to account for.\n // scrollRect === boxRect, screenRect === scrollRect.\n scrollrectPhaseSimple(root)\n }\n tScrollRect = performance.now() - t\n }\n\n let tNotify = 0\n if (!opts?.skipLayoutNotifications) {\n using _n = render.span(\"notify\")\n const t = performance.now()\n notifyLayoutSubscribers(root)\n tNotify = performance.now() - t\n }\n\n // Bench instrumentation: accumulate per-phase timings in a global counter\n // that a harness can read + reset between iterations. Cheap: five `+=` ops.\n // See __silvery_bench_accumulate / __silvery_bench_reset helpers below.\n const acc = (globalThis as any).__silvery_bench_phases\n if (acc) {\n acc.measure += tMeasure\n acc.layout += tLayout\n acc.scroll += tScroll\n acc.scrollRect += tScrollRect\n acc.notify += tNotify\n acc.layoutTotal += tMeasure + tLayout + tScroll + tScrollRect + tNotify\n }\n\n return { tMeasure, tLayout, tScroll, tScrollRect, tNotify }\n }\n\n function doRender(opts?: AgRenderOptions): AgRenderResult & { tContent: number } {\n clearBgConflictWarnings()\n const prevBuffer = opts?.fresh ? null : opts?.prevBuffer !== undefined ? opts.prevBuffer : _prevBuffer\n\n let tContent: number\n let buffer: TerminalBuffer\n {\n const t = performance.now()\n buffer = renderPhase(root, prevBuffer, ctx)\n tContent = performance.now() - t\n log.debug?.(`content: ${tContent.toFixed(2)}ms`)\n }\n\n // Backdrop-fade pass — runs after content + decoration, before output.\n //\n // Incremental invariant: fast-path cells carry the PREVIOUS frame's\n // pixels into the clone inside renderPhase. If those pixels are\n // POST-fade, the fade pass re-fades already-faded cells and the result\n // compounds across frames (STRICT: incremental post-fade diverges from\n // fresh post-fade after 2+ frames).\n //\n // Solution: snapshot the PRE-transform buffer BEFORE applying fade.\n // Store it as `_prevBuffer` (for internal ag state) AND return it as\n // `carryForwardBuffer` so external callers managing their own prev\n // state (renderer.ts) can track pre-fade. The post-fade `buffer` is\n // what gets painted; pre-fade is what gets cloned for incremental.\n let carryForwardBuffer: TerminalBuffer\n if (hasBackdropMarkers(root)) {\n carryForwardBuffer = buffer.clone()\n if (!opts?.fresh) {\n _prevBuffer = carryForwardBuffer\n }\n applyBackdropFade(root, buffer, { colorLevel })\n } else {\n carryForwardBuffer = buffer\n if (!opts?.fresh) {\n _prevBuffer = buffer\n }\n }\n\n // Clear the module-level dirty tracking after each render pass.\n // Content dirty nodes were processed by renderPhase; layout dirty is\n // managed by Flexily internally (isDirty cleared after calculateLayout).\n clearDirtyTracking()\n\n // Bench instrumentation: accumulate content-phase timing.\n const acc = (globalThis as any).__silvery_bench_phases\n if (acc) {\n acc.content += tContent\n acc.renderCalls += 1\n }\n\n const frame = createTextFrame(buffer)\n return { frame, buffer, carryForwardBuffer, prevBuffer, tContent }\n }\n\n // -------------------------------------------------------------------------\n // Tree Mutation\n // -------------------------------------------------------------------------\n\n function agCreateNode(type: AgNodeType, props: Record<string, unknown>): AgNode {\n const engine = getLayoutEngine()\n const layoutNode = engine.createNode()\n return {\n type,\n props,\n children: [],\n parent: null,\n layoutNode,\n boxRect: null,\n scrollRect: null,\n screenRect: null,\n prevLayout: null,\n prevScrollRect: null,\n prevScreenRect: null,\n layoutChangedThisFrame: INITIAL_EPOCH,\n dirtyBits: ALL_RECONCILER_BITS,\n dirtyEpoch: getRenderEpoch(),\n }\n }\n\n function agInsertChild(parent: AgNode, child: AgNode, index: number): void {\n // Remove from old parent if already in a tree (keyed reorder)\n if (child.parent) {\n agRemoveChild(child.parent, child)\n }\n\n // Insert into children array\n parent.children.splice(index, 0, child)\n child.parent = parent\n\n // Sync layout tree\n if (parent.layoutNode && child.layoutNode) {\n // Layout index = count of children with layoutNode before this position\n const layoutIndex = parent.children.slice(0, index).filter((c) => c.layoutNode !== null).length\n parent.layoutNode.insertChild(child.layoutNode, layoutIndex)\n }\n }\n\n function agRemoveChild(parent: AgNode, child: AgNode): void {\n const index = parent.children.indexOf(child)\n if (index === -1) return\n\n parent.children.splice(index, 1)\n\n if (parent.layoutNode && child.layoutNode) {\n parent.layoutNode.removeChild(child.layoutNode)\n child.layoutNode.free()\n }\n\n child.parent = null\n }\n\n return {\n root,\n\n // Pipeline\n layout(dims, options) {\n if (measurer) {\n runWithMeasurer(measurer, () => doLayout(dims.cols, dims.rows, options))\n } else {\n doLayout(dims.cols, dims.rows, options)\n }\n },\n\n render(options) {\n const result = measurer ? runWithMeasurer(measurer, () => doRender(options)) : doRender(options)\n return {\n frame: result.frame,\n buffer: result.buffer,\n carryForwardBuffer: result.carryForwardBuffer,\n prevBuffer: result.prevBuffer,\n }\n },\n\n resetBuffer() {\n _prevBuffer = null\n },\n\n // Tree mutations\n createNode: agCreateNode,\n insertChild: agInsertChild,\n removeChild: agRemoveChild,\n\n updateProps(node, props, oldProps) {\n node.props = props\n if (node.layoutNode) {\n node.layoutNode.markDirty()\n }\n },\n\n setText(node, text) {\n ;(node as any).textContent = text\n const epoch = getRenderEpoch()\n const bits = CONTENT_BIT | STYLE_PROPS_BIT\n node.dirtyBits = node.dirtyEpoch !== epoch ? bits : node.dirtyBits | bits\n node.dirtyEpoch = epoch\n if (node.layoutNode) {\n node.layoutNode.markDirty()\n }\n },\n\n toString() {\n return `[Ag root=${root.type} children=${root.children.length}]`\n },\n }\n}\n","/**\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","/**\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 type { PipelineConfig } from \"@silvery/ag-term/pipeline\"\nimport { createAg } from \"@silvery/ag-term/ag\"\nimport { runWithMeasurer } from \"@silvery/ag-term/unicode\"\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 (useBoxRect 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 measurer = pipelineConfig?.measurer\n const doRender = () => {\n const ag = createAg(root, { measurer })\n ag.layout({ cols: width, rows: height })\n return ag.render()\n }\n const result = measurer ? runWithMeasurer(measurer, doRender) : doRender()\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.boxRect.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.boxRect) {\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.boxRect.y + child.boxRect.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"],"mappings":"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"}