silvery 0.21.1 → 0.23.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 (171) hide show
  1. package/dist/{Text-BRf-59j2.mjs → Text-Ci6UCSTL.mjs} +46 -46
  2. package/dist/Text-Ci6UCSTL.mjs.map +1 -0
  3. package/dist/{ag-BeFC4S2N.mjs → ag-q9lRlKlt.mjs} +42 -19
  4. package/dist/ag-q9lRlKlt.mjs.map +1 -0
  5. package/dist/{animation-N8MFybTk.mjs → animation-CZ4x2R7C.mjs} +1 -1
  6. package/dist/{animation-N8MFybTk.mjs.map → animation-CZ4x2R7C.mjs.map} +1 -1
  7. package/dist/{ansi-C6Qs1Wn2.mjs → ansi-CLmIMnop.mjs} +1 -1
  8. package/dist/{ansi-C6Qs1Wn2.mjs.map → ansi-CLmIMnop.mjs.map} +1 -1
  9. package/dist/{ansi-CBkam1ty.d.mts → ansi-zmNzgkPB.d.mts} +1 -1
  10. package/dist/{ansi-CBkam1ty.d.mts.map → ansi-zmNzgkPB.d.mts.map} +1 -1
  11. package/dist/backend-DSiSIc-h.mjs +13396 -0
  12. package/dist/backend-DSiSIc-h.mjs.map +1 -0
  13. package/dist/chain-bridge-CkYcMxlq.mjs +4149 -0
  14. package/dist/chain-bridge-CkYcMxlq.mjs.map +1 -0
  15. package/dist/{chunk-hT5z_Zn9.mjs → chunk-BEJ448es.mjs} +1 -1
  16. package/dist/cli-VNqUKlXw.mjs +4 -0
  17. package/dist/{context-BU5LkkIy.mjs → context-CEhuD1R_.mjs} +1 -1
  18. package/dist/{context-BU5LkkIy.mjs.map → context-CEhuD1R_.mjs.map} +1 -1
  19. package/dist/devtools-BF4IMumr.mjs +2 -0
  20. package/dist/{devtools-9zfhpFyG.mjs → devtools-td2AjFwc.mjs} +5 -4
  21. package/dist/{devtools-9zfhpFyG.mjs.map → devtools-td2AjFwc.mjs.map} +1 -1
  22. package/dist/{easing-B0oZKDki.d.mts → easing-C5MrdO7V.d.mts} +1 -1
  23. package/dist/{easing-B0oZKDki.d.mts.map → easing-C5MrdO7V.d.mts.map} +1 -1
  24. package/dist/{eta-DGOuC8yU.mjs → eta-BwYWEjfS.mjs} +1 -5
  25. package/dist/{eta-DGOuC8yU.mjs.map → eta-BwYWEjfS.mjs.map} +1 -1
  26. package/dist/{flexily-zero-adapter-D6hcFgrH.mjs → flexily-zero-adapter-DktX3HsX.mjs} +1 -1
  27. package/dist/{flexily-zero-adapter-CEJOcbNp.mjs → flexily-zero-adapter-VoBGHG14.mjs} +1 -1
  28. package/dist/{flexily-zero-adapter-CEJOcbNp.mjs.map → flexily-zero-adapter-VoBGHG14.mjs.map} +1 -1
  29. package/dist/hit-registry-D_nxOivS.d.mts +71 -0
  30. package/dist/hit-registry-D_nxOivS.d.mts.map +1 -0
  31. package/dist/{image-1nRyKa60.mjs → image-CIF75xMr.mjs} +24 -24
  32. package/dist/image-CIF75xMr.mjs.map +1 -0
  33. package/dist/{index-2E1jYgak.d.mts → index-377zdPLP.d.mts} +35 -12
  34. package/dist/index-377zdPLP.d.mts.map +1 -0
  35. package/dist/{index-DnuadDNL.d.mts → index-BcSr0RAQ.d.mts} +8590 -11173
  36. package/dist/index-BcSr0RAQ.d.mts.map +1 -0
  37. package/dist/{index-Dg1YaeJb.d.mts → index-DLnpxDAm.d.mts} +2 -2
  38. package/dist/{index-Dg1YaeJb.d.mts.map → index-DLnpxDAm.d.mts.map} +1 -1
  39. package/dist/{index-Bi4Jdz5g.d.mts → index-DlNmckTq.d.mts} +2 -2
  40. package/dist/{index-Bi4Jdz5g.d.mts.map → index-DlNmckTq.d.mts.map} +1 -1
  41. package/dist/index.d.mts +12 -8
  42. package/dist/index.d.mts.map +1 -1
  43. package/dist/index.mjs +18 -17
  44. package/dist/index.mjs.map +1 -1
  45. package/dist/{layout-engine-Ca_nbtfL.mjs → layout-engine-BgR58vL0.mjs} +3 -3
  46. package/dist/{layout-engine-Ca_nbtfL.mjs.map → layout-engine-BgR58vL0.mjs.map} +1 -1
  47. package/dist/{layout-engine-CgsoBRIn.mjs → layout-engine-DNjTha8f.mjs} +1 -1
  48. package/dist/{layout-signals-Dch2EiCy.mjs → layout-signals-CGYbHmxy.mjs} +2 -2
  49. package/dist/layout-signals-CGYbHmxy.mjs.map +1 -0
  50. package/dist/{bound-term-BumfuXXW.d.mts → mouse-events-B2z5tbsL.d.mts} +540 -401
  51. package/dist/mouse-events-B2z5tbsL.d.mts.map +1 -0
  52. package/dist/{mouse-events-hnbJZRwK.mjs → mouse-events-WQ2h-uqG.mjs} +14 -6
  53. package/dist/mouse-events-WQ2h-uqG.mjs.map +1 -0
  54. package/dist/{multi-progress-Bg4ngK80.d.mts → multi-progress-BaEFfHKP.d.mts} +2 -2
  55. package/dist/{multi-progress-Bg4ngK80.d.mts.map → multi-progress-BaEFfHKP.d.mts.map} +1 -1
  56. package/dist/{multi-progress-CaXTuL9G.mjs → multi-progress-zCWHEm9P.mjs} +3 -3
  57. package/dist/{multi-progress-CaXTuL9G.mjs.map → multi-progress-zCWHEm9P.mjs.map} +1 -1
  58. package/dist/{node-BiFu8I9Y.mjs → node-CJ3J_rW-.mjs} +4 -4
  59. package/dist/node-CJ3J_rW-.mjs.map +1 -0
  60. package/dist/{progress-B_UPy6zk.mjs → progress-6vJ7nxUg.mjs} +4 -4
  61. package/dist/{progress-B_UPy6zk.mjs.map → progress-6vJ7nxUg.mjs.map} +1 -1
  62. package/dist/{progress-bar-DmIMPdL0.mjs → progress-bar-BlFNsZL8.mjs} +4 -4
  63. package/dist/{progress-bar-DmIMPdL0.mjs.map → progress-bar-BlFNsZL8.mjs.map} +1 -1
  64. package/dist/{reconciler-NBDSEm8k.mjs → reconciler--_-biN23.mjs} +111 -150
  65. package/dist/reconciler--_-biN23.mjs.map +1 -0
  66. package/dist/{render-string-BntLj7Xq.mjs → render-string-DaghHpLN.mjs} +1 -1
  67. package/dist/{render-string-B4h4SmK7.mjs → render-string-DtHZP0cU.mjs} +15 -11
  68. package/dist/render-string-DtHZP0cU.mjs.map +1 -0
  69. package/dist/runtime.d.mts +3 -2
  70. package/dist/runtime.mjs +3 -2
  71. package/dist/{schemes-DYt2ushj.mjs → schemes-DWQPxXAi.mjs} +2 -2
  72. package/dist/{schemes-DYt2ushj.mjs.map → schemes-DWQPxXAi.mjs.map} +1 -1
  73. package/dist/{spinner-Py8_-hn9.mjs → spinner-B50hILqx.mjs} +3 -3
  74. package/dist/{spinner-Py8_-hn9.mjs.map → spinner-B50hILqx.mjs.map} +1 -1
  75. package/dist/{spinner-CLgzJ_QF.d.mts → spinner-DN10UfF0.d.mts} +2 -2
  76. package/dist/{spinner-CLgzJ_QF.d.mts.map → spinner-DN10UfF0.d.mts.map} +1 -1
  77. package/dist/{src-Oe6x5PrS.mjs → src-B8lf1kiR.mjs} +190 -83
  78. package/dist/src-B8lf1kiR.mjs.map +1 -0
  79. package/dist/{src-Dvq-s8iD.mjs → src-BOLdi3WG.mjs} +3 -3
  80. package/dist/{src-Dvq-s8iD.mjs.map → src-BOLdi3WG.mjs.map} +1 -1
  81. package/dist/{src-B9S_woYc.mjs → src-BXeTHHlL.mjs} +20289 -23262
  82. package/dist/src-BXeTHHlL.mjs.map +1 -0
  83. package/dist/{src-WeA_J4BV.mjs → src-BqeBFB3X.mjs} +90 -47
  84. package/dist/src-BqeBFB3X.mjs.map +1 -0
  85. package/dist/{src-CbhWmUnF.mjs → src-DbqcSrhy.mjs} +1400 -1427
  86. package/dist/src-DbqcSrhy.mjs.map +1 -0
  87. package/dist/{steps-DYrzCUCK.d.mts → steps-B2kBmFpb.d.mts} +1 -1
  88. package/dist/{steps-DYrzCUCK.d.mts.map → steps-B2kBmFpb.d.mts.map} +1 -1
  89. package/dist/{svg-DhxQkz-O.mjs → svg-NmV9dgB9.mjs} +1 -1
  90. package/dist/{svg-DhxQkz-O.mjs.map → svg-NmV9dgB9.mjs.map} +1 -1
  91. package/dist/{svg-Hk7lIl4F.d.mts → svg-VUr78l5d.d.mts} +1 -1
  92. package/dist/{svg-Hk7lIl4F.d.mts.map → svg-VUr78l5d.d.mts.map} +1 -1
  93. package/dist/term.d.mts +2 -2
  94. package/dist/term.mjs +3 -3
  95. package/dist/test.d.mts +673 -0
  96. package/dist/test.d.mts.map +1 -0
  97. package/dist/test.mjs +2829 -0
  98. package/dist/test.mjs.map +1 -0
  99. package/dist/theme.d.mts +2 -2
  100. package/dist/theme.d.mts.map +1 -1
  101. package/dist/theme.mjs +3 -3
  102. package/dist/{types-Bx-XZNbE.d.mts → types-D_E0FbqA.d.mts} +1 -1
  103. package/dist/{types-Bx-XZNbE.d.mts.map → types-D_E0FbqA.d.mts.map} +1 -1
  104. package/dist/ui/animation.d.mts +2 -2
  105. package/dist/ui/animation.mjs +1 -1
  106. package/dist/ui/ansi.d.mts +1 -1
  107. package/dist/ui/ansi.mjs +1 -1
  108. package/dist/ui/cli.d.mts +3 -3
  109. package/dist/ui/cli.mjs +5 -5
  110. package/dist/ui/image.d.mts +1 -1
  111. package/dist/ui/image.mjs +1 -1
  112. package/dist/ui/progress.d.mts +4 -4
  113. package/dist/ui/progress.mjs +4 -4
  114. package/dist/ui/react.d.mts +1 -1
  115. package/dist/ui/react.mjs +2 -2
  116. package/dist/ui/react.mjs.map +1 -1
  117. package/dist/ui/recording-chrome-react.d.mts +1 -1
  118. package/dist/ui/recording-chrome-react.mjs +2 -2
  119. package/dist/ui/recording-chrome.d.mts +1 -1
  120. package/dist/ui/recording-chrome.mjs +1 -1
  121. package/dist/ui/utils.mjs +1 -1
  122. package/dist/ui/wrappers.d.mts +2 -2
  123. package/dist/ui/wrappers.mjs +1 -1
  124. package/dist/ui.d.mts +6 -6
  125. package/dist/ui.mjs +7 -7
  126. package/dist/{ansi-OYRLxxZQ.mjs → unicode-CxyxVGmB.mjs} +7317 -6909
  127. package/dist/unicode-CxyxVGmB.mjs.map +1 -0
  128. package/dist/{useLatest-DC8i7guK.d.mts → useLatest-UOvWy0ZW.d.mts} +2 -2
  129. package/dist/{useLatest-DC8i7guK.d.mts.map → useLatest-UOvWy0ZW.d.mts.map} +1 -1
  130. package/dist/{useLayout-BKsQl2Or.mjs → useLayout-D5cE8OCI.mjs} +4 -4
  131. package/dist/{useLayout-BKsQl2Or.mjs.map → useLayout-D5cE8OCI.mjs.map} +1 -1
  132. package/dist/{with-text-input-DG4f7JII.d.mts → with-text-input-DLoSCBrm.d.mts} +3 -3
  133. package/dist/{with-text-input-DG4f7JII.d.mts.map → with-text-input-DLoSCBrm.d.mts.map} +1 -1
  134. package/dist/wrap-measurer-registration-IV2HtcCd.d.mts +3106 -0
  135. package/dist/wrap-measurer-registration-IV2HtcCd.d.mts.map +1 -0
  136. package/dist/{wrapper-D7gNSsgf.mjs → wrapper-CD29afOs.mjs} +17 -37
  137. package/dist/wrapper-CD29afOs.mjs.map +1 -0
  138. package/dist/{wrappers-CypAzrMO.mjs → wrappers-CInBJm44.mjs} +6 -6
  139. package/dist/{wrappers-CypAzrMO.mjs.map → wrappers-CInBJm44.mjs.map} +1 -1
  140. package/dist/{yoga-adapter-SsEIqMc1.mjs → yoga-adapter-9uab5Qn-.mjs} +2 -2
  141. package/dist/{yoga-adapter-SsEIqMc1.mjs.map → yoga-adapter-9uab5Qn-.mjs.map} +1 -1
  142. package/dist/yoga-adapter-yqTl1aXK.mjs +2 -0
  143. package/package.json +20 -15
  144. package/dist/Text-BRf-59j2.mjs.map +0 -1
  145. package/dist/ag-BeFC4S2N.mjs.map +0 -1
  146. package/dist/ansi-OYRLxxZQ.mjs.map +0 -1
  147. package/dist/bound-term-BumfuXXW.d.mts.map +0 -1
  148. package/dist/cli-DG6zsfsS.mjs +0 -4
  149. package/dist/devtools-nmPUmYU_.mjs +0 -2
  150. package/dist/image-1nRyKa60.mjs.map +0 -1
  151. package/dist/index-2E1jYgak.d.mts.map +0 -1
  152. package/dist/index-DnuadDNL.d.mts.map +0 -1
  153. package/dist/layout-signals-Dch2EiCy.mjs.map +0 -1
  154. package/dist/mouse-events-hnbJZRwK.mjs.map +0 -1
  155. package/dist/node-BiFu8I9Y.mjs.map +0 -1
  156. package/dist/reconciler-NBDSEm8k.mjs.map +0 -1
  157. package/dist/render-string-B4h4SmK7.mjs.map +0 -1
  158. package/dist/src-B9S_woYc.mjs.map +0 -1
  159. package/dist/src-CbhWmUnF.mjs.map +0 -1
  160. package/dist/src-Oe6x5PrS.mjs.map +0 -1
  161. package/dist/src-WeA_J4BV.mjs.map +0 -1
  162. package/dist/ui/display.d.mts +0 -35
  163. package/dist/ui/display.d.mts.map +0 -1
  164. package/dist/ui/display.mjs +0 -123
  165. package/dist/ui/display.mjs.map +0 -1
  166. package/dist/ui/input.d.mts +0 -184
  167. package/dist/ui/input.d.mts.map +0 -1
  168. package/dist/ui/input.mjs +0 -283
  169. package/dist/ui/input.mjs.map +0 -1
  170. package/dist/wrapper-D7gNSsgf.mjs.map +0 -1
  171. package/dist/yoga-adapter-1ex8r0ws.mjs +0 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unicode-CxyxVGmB.mjs","names":["log","splitRawInput","ESC","CSI","_enableMouse","_disableMouse","KittyFlags","_enableKittyKeyboard","_disableKittyKeyboard","BEL","ST","log","log","loadTermless","log","DEBUG_OUTPUT","reusableCellStyle","styleTransition","wrapTextSizing","log","cellVisualEquals","ESC","BEL","CSI","ESC","ST","Buffer","log","safeJsonStringify","log","enableBracketedPaste","disableBracketedPaste","disableKittyKeyboard","enableKittyKeyboard","enableMouse","disableMouse","stripAnsi","_createTerm"],"sources":["../packages/ag/src/keys.ts","../packages/ag-term/src/buffer.ts","../packages/ag-term/src/output.ts","../packages/ag-term/src/text-sizing.ts","../packages/ag-term/src/strict-mode.ts","../packages/ag-term/src/strict-divergence.ts","../packages/ag-term/src/strict-terminal-backends.ts","../packages/ag-term/src/cursor-diagnostics.ts","../packages/ag-term/src/errors.ts","../packages/ag-term/src/pipeline/output-verify.ts","../packages/ag-term/src/pipeline/diff-buffers.ts","../packages/ag-term/src/pipeline/output-density.ts","../packages/ag-term/src/pipeline/output-modes.ts","../packages/ag-term/src/pipeline/output-phase.ts","../packages/ag-term/src/mouse.ts","../packages/ag-term/src/bracketed-paste.ts","../packages/ag-term/src/clipboard.ts","../packages/ag-term/src/focus-reporting.ts","../packages/ag-term/src/ansi/notification.ts","../packages/ag-term/src/runtime/input-owner.ts","../packages/ag-term/src/runtime/devices/console-router.ts","../packages/ag-term/src/runtime/devices/output.ts","../packages/ag-term/src/runtime/devices/size.ts","../packages/ag-term/src/runtime/devices/modes.ts","../packages/ag-term/src/runtime/devices/signals.ts","../packages/ag-term/src/runtime/devices/console.ts","../packages/ag-term/src/runtime/term-internal.ts","../packages/ag-term/src/ansi/term.ts","../packages/ag-term/src/ansi/advanced-clipboard.ts","../packages/ag-term/src/ansi/index.ts","../packages/ag-term/src/unicode.ts"],"sourcesContent":["/**\n * Keyboard Constants and Utilities\n *\n * Single source of truth for all key parsing, mapping, and matching in silvery.\n *\n * ## Two-Layer Input Architecture\n *\n * parseKey() returns `[input, key]` where these serve DIFFERENT purposes:\n *\n * - `input` is **normalized for physical keybinding matching**. Shifted punctuation is\n * decomposed: '#' becomes input='3' with key.shift=true, so keybindings\n * like 'shift+3' can match. Uppercase letters retain their casing and set shift.\n *\n * - `key.text` is the **actual typed character** (pre-normalization). For text\n * insertion or literal-character hotkeys, use it instead of normalized `input`.\n * `key.text ?? input` ensures Shift+3 inserts '#', opt+e inserts '´', and IME\n * output inserts the composed string.\n *\n * Rule: physical keybindings use `input` plus modifiers. Literal-character hotkeys\n * and text insertion use `key.text`; matchHotkey() handles both representations.\n * Never reconstruct characters from key codes — trust what the terminal sent.\n *\n * - KEY_MAP: Playwright key names -> ANSI sequences (for sending input)\n * - CODE_TO_KEY: ANSI escape suffixes -> key names (for parsing input)\n * - Key interface: structured key object with boolean flags\n * - parseKeypress(): raw terminal input -> ParsedKeypress\n * - parseKey(): raw terminal input -> [input, Key]\n * - keyToAnsi(): Playwright key string -> ANSI sequence\n * - keyToName(): Key object -> named key string\n * - keyToModifiers(): Key object -> modifier flags\n * - parseHotkey(): \"ctrl+shift+a\" -> ParsedHotkey\n * - matchHotkey(): match ParsedHotkey against Key\n *\n * @example\n * ```tsx\n * import { keyToAnsi } from '@silvery/test'\n *\n * // Convert key names to ANSI\n * keyToAnsi('Enter') // '\\r'\n * keyToAnsi('ArrowUp') // '\\x1b[A'\n * keyToAnsi('Control+c') // '\\x03'\n * keyToAnsi('a') // 'a'\n * ```\n */\n\nimport { createLogger } from \"loggily\"\n\nconst log = createLogger(\"silvery:keys\")\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Key object describing which special keys/modifiers were pressed.\n */\nexport interface Key {\n /** Up arrow key was pressed */\n upArrow: boolean\n /** Down arrow key was pressed */\n downArrow: boolean\n /** Left arrow key was pressed */\n leftArrow: boolean\n /** Right arrow key was pressed */\n rightArrow: boolean\n /** Page Down key was pressed */\n pageDown: boolean\n /** Page Up key was pressed */\n pageUp: boolean\n /** Home key was pressed */\n home: boolean\n /** End key was pressed */\n end: boolean\n /** Return (Enter) key was pressed */\n return: boolean\n /** Escape key was pressed */\n escape: boolean\n /** Ctrl key was pressed */\n ctrl: boolean\n /** Shift key was pressed */\n shift: boolean\n /** Tab key was pressed */\n tab: boolean\n /** Backspace key was pressed */\n backspace: boolean\n /** Delete key was pressed */\n delete: boolean\n /** Meta key (Alt/Option on macOS, Alt on other platforms) was pressed */\n meta: boolean\n /** Super key (Cmd on macOS, Win on Windows) was pressed. Requires Kitty protocol. */\n super: boolean\n /** Hyper key was pressed. Requires Kitty protocol. */\n hyper: boolean\n /** CapsLock is active. Requires Kitty protocol. */\n capsLock: boolean\n /** NumLock is active. Requires Kitty protocol. */\n numLock: boolean\n /** Kitty event type. Only set with Kitty flag 2 (report events). */\n eventType?: \"press\" | \"repeat\" | \"release\"\n /** The actual text character typed (pre-normalization). For text insertion,\n * use this instead of the normalized `input` which maps shifted chars to base keys. */\n text?: string\n /** True when the key is a modifier pressed alone (Shift, Ctrl, Alt, Super, Hyper, Meta).\n * Set during parsing for Kitty protocol modifier-only events. */\n isModifierOnly?: boolean\n /**\n * Internal Kitty key name (e.g. \"leftsuper\", \"rightcontrol\",\n * \"leftshift\") for modifier-only events. Surfaced so the modifier-state\n * tracker (`useModifierKeys`) can drop the matching flag on release —\n * Kitty release events keep the modifier bit set, so the bit alone\n * doesn't say which key was lifted.\n *\n * Optional + only set for modifier-only events to avoid bloating the\n * public surface for non-modifier keys (use the existing flag fields\n * `upArrow`, `escape`, etc. for those). Bead:\n * @km/silvery/keydown-keyup-test-primitives.\n */\n modifierName?: string\n}\n\n/**\n * Input handler callback type.\n * Return 'exit' to exit the app.\n */\nexport type InputHandler = (input: string, key: Key) => void | \"exit\"\n\n/**\n * Detect modifier-only key events (Cmd, Shift, Ctrl, Alt pressed alone).\n * With Kitty REPORT_ALL_KEYS, these fire as key events with empty input\n * and no actionable key flags — only modifier flags are set.\n *\n * The flag is set during parsing by `parseKey()`. This function checks it.\n * Shared by useInput (filters these out) and create-app (filters for app handlers).\n */\nexport function isModifierOnlyEvent(_input: string, key: Key): boolean {\n return key.isModifierOnly === true\n}\n\n/**\n * Parsed hotkey from a string like \"ctrl+shift+a\" or \"Control+ArrowUp\".\n */\nexport interface ParsedHotkey {\n key: string\n ctrl: boolean\n meta: boolean\n shift: boolean\n alt: boolean\n super: boolean\n hyper: boolean\n}\n\n// ============================================================================\n// Key -> ANSI Mapping (for sending input)\n// ============================================================================\n\n/**\n * Playwright-compatible key names -> ANSI sequences.\n * Keys that are modifier-only (Control, Shift, etc.) are null.\n */\nconst KEY_MAP: Record<string, string | null> = {\n // Navigation (Playwright names)\n ArrowUp: \"\\x1b[A\",\n ArrowDown: \"\\x1b[B\",\n ArrowLeft: \"\\x1b[D\",\n ArrowRight: \"\\x1b[C\",\n Home: \"\\x1b[H\",\n End: \"\\x1b[F\",\n PageUp: \"\\x1b[5~\",\n PageDown: \"\\x1b[6~\",\n\n // Editing\n Enter: \"\\r\",\n Tab: \"\\t\",\n Backspace: \"\\x7f\",\n Delete: \"\\x1b[3~\",\n Escape: \"\\x1b\",\n Space: \" \",\n\n // Modifiers (prefix only, not standalone sequences)\n Control: null,\n Shift: null,\n Alt: null,\n Meta: null,\n Super: null,\n Hyper: null,\n}\n\nconst MODIFIER_ALIASES: Record<string, string> = {\n ctrl: \"Control\",\n control: \"Control\",\n \"⌃\": \"Control\",\n shift: \"Shift\",\n \"⇧\": \"Shift\",\n alt: \"Alt\",\n meta: \"Meta\",\n opt: \"Alt\",\n option: \"Alt\",\n \"⌥\": \"Alt\",\n cmd: \"Super\",\n command: \"Super\",\n super: \"Super\",\n \"⌘\": \"Super\",\n hyper: \"Hyper\",\n \"✦\": \"Hyper\",\n}\n\n/** Modifier symbols that can be used as prefixes without + separator (e.g. ⌘J, ⌃⇧J) */\nconst MODIFIER_SYMBOLS = new Set([\"⌃\", \"⇧\", \"⌥\", \"⌘\", \"✦\"])\n\nfunction normalizeModifier(mod: string): string {\n return MODIFIER_ALIASES[mod.toLowerCase()] ?? mod\n}\n\n/**\n * Convert Playwright-style key string to ANSI sequence.\n *\n * Supports:\n * - Single characters: 'a', 'A', '1', etc.\n * - Named keys: 'Enter', 'ArrowUp', 'Escape', etc.\n * - Modifier combos: 'Control+c', 'Shift+Tab', 'Control+Shift+a'\n * - Lowercase modifier aliases: 'ctrl+c', 'shift+Tab', 'alt+x'\n *\n * @example\n * ```tsx\n * keyToAnsi('Enter') // '\\r'\n * keyToAnsi('ArrowUp') // '\\x1b[A'\n * keyToAnsi('Control+c') // '\\x03'\n * keyToAnsi('j') // 'j'\n * ```\n */\nexport function keyToAnsi(key: string): string {\n // Split on + for combos: 'Control+Shift+a' -> ['Control', 'Shift', 'a']\n const parts = key.split(\"+\")\n const mainKey = parts.pop()!\n // Normalize modifier aliases: ctrl->Control, shift->Shift, alt->Alt, meta->Meta\n const modifiers = parts.map(normalizeModifier)\n\n // Super/Hyper modifiers require Kitty keyboard protocol encoding\n // (standard ANSI cannot represent Cmd/Super)\n if (modifiers.includes(\"Super\") || modifiers.includes(\"Hyper\")) {\n return keyToKittyAnsi(key)\n }\n\n // Single char without modifiers\n if (!modifiers.length && mainKey.length === 1) {\n return mainKey\n }\n\n // Ctrl+letter -> control code (ASCII 1-26)\n if (modifiers.includes(\"Control\") && mainKey.length === 1) {\n const code = mainKey.toLowerCase().charCodeAt(0) - 96\n if (code >= 1 && code <= 26) return String.fromCharCode(code)\n }\n\n // Ctrl+/ -> \\x1f (Unit Separator, standard terminal convention)\n if (modifiers.includes(\"Control\") && mainKey === \"/\") {\n return \"\\x1f\"\n }\n\n // Ctrl+Enter -> \\n (legacy terminal: \\r = Enter, \\n = Ctrl+Enter/Ctrl+J)\n if (modifiers.includes(\"Control\") && mainKey === \"Enter\") {\n return \"\\n\"\n }\n\n // Alt+key -> ESC prefix (standard terminal convention)\n // Alt/Meta/Option keys send ESC followed by the key\n if ((modifiers.includes(\"Alt\") || modifiers.includes(\"Meta\")) && mainKey.length === 1) {\n return `\\x1b${mainKey}`\n }\n\n // Shift+Tab -> backtab (universally \\x1b[Z across all terminal emulators)\n if (modifiers.includes(\"Shift\") && mainKey === \"Tab\") {\n return \"\\x1b[Z\"\n }\n\n // Shift+key for legacy ANSI encoding (no CSI sequence available)\n // Legacy terminals send the shifted character directly: Shift+a → 'A', Shift+/ → '?'\n if (modifiers.length === 1 && modifiers[0] === \"Shift\" && mainKey.length === 1) {\n // Shift+letter → uppercase\n if (mainKey >= \"a\" && mainKey <= \"z\") return mainKey.toUpperCase()\n // Shift+punct → shifted character (US QWERTY)\n const shifted = BASE_TO_SHIFTED_PUNCT[mainKey]\n if (shifted) return shifted\n }\n\n // Modified arrow/function keys -> xterm-style CSI 1;modifier sequences\n // E.g. Shift+ArrowUp -> \\x1b[1;2A, Ctrl+ArrowDown -> \\x1b[1;5B\n const ARROW_SUFFIX: Record<string, string> = {\n ArrowUp: \"A\",\n ArrowDown: \"B\",\n ArrowRight: \"C\",\n ArrowLeft: \"D\",\n Home: \"H\",\n End: \"F\",\n }\n if (modifiers.length > 0 && mainKey in ARROW_SUFFIX) {\n let mod = 1\n if (modifiers.includes(\"Shift\")) mod += 1\n if (modifiers.includes(\"Alt\") || modifiers.includes(\"Meta\")) mod += 2\n if (modifiers.includes(\"Control\")) mod += 4\n if (modifiers.includes(\"Super\")) mod += 8\n if (modifiers.includes(\"Hyper\")) mod += 16\n return `\\x1b[1;${mod}${ARROW_SUFFIX[mainKey]}`\n }\n\n // Modified ~-terminated nav keys (PageUp/PageDown/Insert/Delete) ->\n // xterm-style CSI number;modifier ~ sequences. Without this, the bare\n // \"\\x1b[5~\" / \"\\x1b[6~\" sequences from KEY_MAP carry no Shift modifier\n // and parseKeypress decodes them as `pageUp:true, shift:false` —\n // breaking app bindings like silvercode's Shift+PageUp transcript\n // scroll, which only fires when `key.shift` is true.\n // Bead: @km/silvery/keyToAnsi-shift-pageup-pagedown-encoding.\n const TILDE_KEY_NUMBER: Record<string, number> = {\n PageUp: 5,\n PageDown: 6,\n Insert: 2,\n Delete: 3,\n }\n if (modifiers.length > 0 && mainKey in TILDE_KEY_NUMBER) {\n let mod = 1\n if (modifiers.includes(\"Shift\")) mod += 1\n if (modifiers.includes(\"Alt\") || modifiers.includes(\"Meta\")) mod += 2\n if (modifiers.includes(\"Control\")) mod += 4\n if (modifiers.includes(\"Super\")) mod += 8\n if (modifiers.includes(\"Hyper\")) mod += 16\n return `\\x1b[${TILDE_KEY_NUMBER[mainKey]};${mod}~`\n }\n\n // Look up base key in map\n const base = KEY_MAP[mainKey]\n if (base !== undefined && base !== null) return base\n\n // Fallback: return as-is (single char or unknown key)\n return mainKey\n}\n\n// ============================================================================\n// ANSI -> Key Mapping (for parsing input)\n// ============================================================================\n\n/**\n * ANSI escape code suffix -> key name mapping.\n * Used by useInput to parse incoming key sequences.\n *\n * The key is the escape sequence suffix (after ESC or ESC[).\n * Multiple terminal emulators may use different sequences for the same key.\n */\nexport const CODE_TO_KEY: Record<string, string> = {\n // Arrow keys (xterm ESC [ letter)\n \"[A\": \"up\",\n \"[B\": \"down\",\n \"[C\": \"right\",\n \"[D\": \"left\",\n \"[E\": \"clear\",\n \"[F\": \"end\",\n \"[H\": \"home\",\n\n // Arrow keys (xterm/gnome ESC O letter)\n OA: \"up\",\n OB: \"down\",\n OC: \"right\",\n OD: \"left\",\n OE: \"clear\",\n OF: \"end\",\n OH: \"home\",\n\n // Function keys (xterm/gnome ESC O letter)\n OP: \"f1\",\n OQ: \"f2\",\n OR: \"f3\",\n OS: \"f4\",\n\n // Function keys (xterm/rxvt ESC [ number ~)\n \"[11~\": \"f1\",\n \"[12~\": \"f2\",\n \"[13~\": \"f3\",\n \"[14~\": \"f4\",\n \"[15~\": \"f5\",\n \"[17~\": \"f6\",\n \"[18~\": \"f7\",\n \"[19~\": \"f8\",\n \"[20~\": \"f9\",\n \"[21~\": \"f10\",\n \"[23~\": \"f11\",\n \"[24~\": \"f12\",\n\n // Function keys (Cygwin/libuv)\n \"[[A\": \"f1\",\n \"[[B\": \"f2\",\n \"[[C\": \"f3\",\n \"[[D\": \"f4\",\n \"[[E\": \"f5\",\n\n // Navigation keys (xterm/rxvt ESC [ number ~)\n \"[1~\": \"home\",\n \"[2~\": \"insert\",\n \"[3~\": \"delete\",\n \"[4~\": \"end\",\n \"[5~\": \"pageup\",\n \"[6~\": \"pagedown\",\n\n // Navigation keys (putty)\n \"[[5~\": \"pageup\",\n \"[[6~\": \"pagedown\",\n\n // Navigation keys (rxvt)\n \"[7~\": \"home\",\n \"[8~\": \"end\",\n\n // Arrow keys with shift (rxvt lowercase)\n \"[a\": \"up\",\n \"[b\": \"down\",\n \"[c\": \"right\",\n \"[d\": \"left\",\n \"[e\": \"clear\",\n\n // Navigation keys with shift (rxvt $)\n \"[2$\": \"insert\",\n \"[3$\": \"delete\",\n \"[5$\": \"pageup\",\n \"[6$\": \"pagedown\",\n \"[7$\": \"home\",\n \"[8$\": \"end\",\n\n // Arrow keys with ctrl (rxvt O lowercase)\n Oa: \"up\",\n Ob: \"down\",\n Oc: \"right\",\n Od: \"left\",\n Oe: \"clear\",\n\n // Navigation keys with ctrl (rxvt ^)\n \"[2^\": \"insert\",\n \"[3^\": \"delete\",\n \"[5^\": \"pageup\",\n \"[6^\": \"pagedown\",\n \"[7^\": \"home\",\n \"[8^\": \"end\",\n\n // Shift+Tab\n \"[Z\": \"tab\",\n}\n\n// ============================================================================\n// Key Parsing Constants\n// ============================================================================\n\nconst NON_ALPHANUMERIC_KEYS = [\n ...Object.values(CODE_TO_KEY),\n \"backspace\",\n \"tab\",\n \"delete\",\n // Note: 'return', 'enter', 'escape', and 'space' are intentionally NOT included.\n // Users may need the raw character as input ('\\r' for return, '\\x1b' for escape).\n // Use key.return / key.escape boolean flags to detect these keys.\n]\n\nconst SHIFT_CODES = new Set([\n \"[a\",\n \"[b\",\n \"[c\",\n \"[d\",\n \"[e\",\n \"[2$\",\n \"[3$\",\n \"[5$\",\n \"[6$\",\n \"[7$\",\n \"[8$\",\n \"[Z\",\n])\n\nconst CTRL_CODES = new Set([\"Oa\", \"Ob\", \"Oc\", \"Od\", \"Oe\", \"[2^\", \"[3^\", \"[5^\", \"[6^\", \"[7^\", \"[8^\"])\n\nconst META_KEY_CODE_RE = /^(?:\\x1b)([\\x20-\\x7e])$/\nconst FN_KEY_RE = /^(?:\\x1b+)(O|N|\\[|\\[\\[)(?:(\\d+)(?:;(\\d+))?([~^$])|(?:1;)?(\\d+)?([a-zA-Z]))/\n\n/**\n * US QWERTY shifted punctuation → base key map.\n *\n * Legacy terminals send the result character (e.g. `?`) without a shift modifier flag.\n * Kitty protocol sends the base codepoint + shift modifier. This map lets us normalize\n * legacy input so that keybindings like `shift-/` match on both protocols.\n */\nconst SHIFTED_PUNCT_MAP: Record<string, string> = {\n \"!\": \"1\",\n \"@\": \"2\",\n \"#\": \"3\",\n $: \"4\",\n \"%\": \"5\",\n \"^\": \"6\",\n \"&\": \"7\",\n \"*\": \"8\",\n \"(\": \"9\",\n \")\": \"0\",\n _: \"-\",\n \"+\": \"=\",\n \"~\": \"`\",\n \"{\": \"[\",\n \"}\": \"]\",\n \"|\": \"\\\\\",\n \":\": \";\",\n '\"': \"'\",\n \"<\": \",\",\n \">\": \".\",\n \"?\": \"/\",\n}\n\n/** Reverse of SHIFTED_PUNCT_MAP: base key → shifted character (US QWERTY) */\nconst BASE_TO_SHIFTED_PUNCT: Record<string, string> = Object.fromEntries(\n Object.entries(SHIFTED_PUNCT_MAP).map(([shifted, base]) => [base, shifted]),\n)\n\n// ============================================================================\n// Kitty Keyboard Protocol\n// ============================================================================\n\n/**\n * Matches Kitty keyboard protocol sequences:\n * CSI codepoint[:shifted[:base]][;modifiers[:event_type][;text_codepoints]] u\n *\n * Groups:\n * 1: codepoint\n * 2: shifted_codepoint (optional)\n * 3: base_layout_key (optional)\n * 4: modifiers (optional, defaults to 1)\n * 5: event_type (optional)\n * 6: text_codepoints (colon-separated, optional — requires REPORT_TEXT flag)\n */\nconst KITTY_RE = /^\\x1b\\[(\\d+)(?::(\\d+))?(?::(\\d+))?(?:;(\\d+)(?::(\\d+))?(?:;([\\d:]+))?)?u$/\n\n/** Matches xterm modifyOtherKeys format: CSI 27 ; modifier ; keycode ~ */\nconst MODIFY_OTHER_KEYS_RE = /^\\x1b\\[27;(\\d+);(\\d+)~$/\n\n/**\n * Kitty-enhanced special key sequences:\n * CSI number ; modifiers : eventType {letter|~}\n * These are legacy CSI sequences enhanced with the :eventType field.\n * Examples: \\x1b[1;1:1A (up arrow press), \\x1b[3;1:3~ (delete release)\n */\nconst KITTY_SPECIAL_RE = /^\\x1b\\[(\\d+);(\\d+):(\\d+)([A-Za-z~])$/\n\n/** Letter-terminated special key names (CSI 1 ; mods letter) */\nconst KITTY_SPECIAL_LETTER_KEYS: Record<string, string> = {\n A: \"up\",\n B: \"down\",\n C: \"right\",\n D: \"left\",\n E: \"clear\",\n F: \"end\",\n H: \"home\",\n P: \"f1\",\n Q: \"f2\",\n R: \"f3\",\n S: \"f4\",\n}\n\n/** Number-terminated special key names (CSI number ; mods ~) */\nconst KITTY_SPECIAL_NUMBER_KEYS: Record<number, string> = {\n 2: \"insert\",\n 3: \"delete\",\n 5: \"pageup\",\n 6: \"pagedown\",\n 7: \"home\",\n 8: \"end\",\n 11: \"f1\",\n 12: \"f2\",\n 13: \"f3\",\n 14: \"f4\",\n 15: \"f5\",\n 17: \"f6\",\n 18: \"f7\",\n 19: \"f8\",\n 20: \"f9\",\n 21: \"f10\",\n 23: \"f11\",\n 24: \"f12\",\n}\n\n/** Valid Unicode codepoint range, excluding surrogates */\nfunction isValidCodepoint(cp: number): boolean {\n return cp >= 0 && cp <= 0x10_ffff && !(cp >= 0xd8_00 && cp <= 0xdf_ff)\n}\n\n/** Safely convert codepoint to string, returning '?' for invalid values */\nfunction safeFromCodePoint(cp: number): string {\n return isValidCodepoint(cp) ? String.fromCodePoint(cp) : \"?\"\n}\n\n/** Maps Kitty codepoints to key names for non-printable/functional keys */\nconst KITTY_CODEPOINT_MAP: Record<number, string> = {\n // Standard control keys\n // Note: Kitty protocol sends Backspace as codepoint 127 (CSI 127 u).\n // Codepoint 8 (BS) is kept as a fallback. Delete uses legacy CSI 3 ~ format\n // (handled by KITTY_SPECIAL_NUMBER_KEYS), not the CSI u format.\n 8: \"backspace\",\n 9: \"tab\",\n 13: \"return\",\n 27: \"escape\",\n 127: \"backspace\",\n // Function keys F13-F35 (F1-F12 use legacy CSI sequences in Kitty mode)\n 57376: \"f13\",\n 57377: \"f14\",\n 57378: \"f15\",\n 57379: \"f16\",\n 57380: \"f17\",\n 57381: \"f18\",\n 57382: \"f19\",\n 57383: \"f20\",\n 57384: \"f21\",\n 57385: \"f22\",\n 57386: \"f23\",\n 57387: \"f24\",\n 57388: \"f25\",\n 57389: \"f26\",\n 57390: \"f27\",\n 57391: \"f28\",\n 57392: \"f29\",\n 57393: \"f30\",\n 57394: \"f31\",\n 57395: \"f32\",\n 57396: \"f33\",\n 57397: \"f34\",\n 57398: \"f35\",\n // Lock/misc keys\n 57358: \"capslock\",\n 57359: \"scrolllock\",\n 57360: \"numlock\",\n 57361: \"printscreen\",\n 57362: \"pause\",\n 57363: \"menu\",\n // Keypad keys\n 57399: \"kp0\",\n 57400: \"kp1\",\n 57401: \"kp2\",\n 57402: \"kp3\",\n 57403: \"kp4\",\n 57404: \"kp5\",\n 57405: \"kp6\",\n 57406: \"kp7\",\n 57407: \"kp8\",\n 57408: \"kp9\",\n 57409: \"kpdecimal\",\n 57410: \"kpdivide\",\n 57411: \"kpmultiply\",\n 57412: \"kpsubtract\",\n 57413: \"kpadd\",\n 57414: \"kpenter\",\n 57415: \"kpequal\",\n 57416: \"kpseparator\",\n 57417: \"kpleft\",\n 57418: \"kpright\",\n 57419: \"kpup\",\n 57420: \"kpdown\",\n 57421: \"kppageup\",\n 57422: \"kppagedown\",\n 57423: \"kphome\",\n 57424: \"kpend\",\n 57425: \"kpinsert\",\n 57426: \"kpdelete\",\n 57427: \"kpbegin\",\n // Media keys\n 57428: \"mediaplay\",\n 57429: \"mediapause\",\n 57430: \"mediaplaypause\",\n 57431: \"mediareverse\",\n 57432: \"mediastop\",\n 57433: \"mediafastforward\",\n 57434: \"mediarewind\",\n 57435: \"mediatracknext\",\n 57436: \"mediatrackprevious\",\n 57437: \"mediarecord\",\n 57438: \"lowervolume\",\n 57439: \"raisevolume\",\n 57440: \"mutevolume\",\n // Modifier-only keys\n 57441: \"leftshift\",\n 57442: \"leftcontrol\",\n 57443: \"leftalt\",\n 57444: \"leftsuper\",\n 57445: \"lefthyper\",\n 57446: \"leftmeta\",\n 57447: \"rightshift\",\n 57448: \"rightcontrol\",\n 57449: \"rightalt\",\n 57450: \"rightsuper\",\n 57451: \"righthyper\",\n 57452: \"rightmeta\",\n 57453: \"isoLevel3Shift\",\n 57454: \"isoLevel5Shift\",\n}\n\n/** Key names that represent modifier-only events (Kitty codepoints 57441–57454). */\nconst MODIFIER_ONLY_NAMES = new Set([\n \"leftshift\",\n \"leftcontrol\",\n \"leftalt\",\n \"leftsuper\",\n \"lefthyper\",\n \"leftmeta\",\n \"rightshift\",\n \"rightcontrol\",\n \"rightalt\",\n \"rightsuper\",\n \"righthyper\",\n \"rightmeta\",\n \"isoLevel3Shift\",\n \"isoLevel5Shift\",\n])\n\n/** Lookup a Kitty codepoint to a key name */\nfunction kittyCodepointToName(cp: number): string | undefined {\n return KITTY_CODEPOINT_MAP[cp]\n}\n\n/** Convert numeric Kitty event type (1/2/3) to string. */\nfunction numericToEventType(n: number): \"press\" | \"repeat\" | \"release\" | undefined {\n if (n === 1) return \"press\"\n if (n === 2) return \"repeat\"\n if (n === 3) return \"release\"\n return undefined\n}\n\n// ============================================================================\n// Key Parsing\n// ============================================================================\n\nexport interface ParsedKeypress {\n name: string\n ctrl: boolean\n meta: boolean\n shift: boolean\n option: boolean\n super: boolean\n hyper: boolean\n /** Kitty event type. Only set with Kitty flag 2 (report events). */\n eventType?: \"press\" | \"repeat\" | \"release\"\n /** The character when Shift is held. From Kitty shifted_codepoint. */\n shiftedKey?: string\n /** The key on a standard US layout (for non-Latin keyboards). From Kitty base_layout_key. */\n baseLayoutKey?: string\n /** CapsLock is active. Kitty modifier bit 6. */\n capsLock?: boolean\n /** NumLock is active. Kitty modifier bit 7. */\n numLock?: boolean\n /** Decoded text from Kitty REPORT_TEXT mode. */\n associatedText?: string\n sequence: string\n /** Raw input string, identical to sequence for most keys. */\n raw?: string\n code?: string\n /** Whether this key was parsed from the Kitty keyboard protocol. */\n isKittyProtocol?: boolean\n /**\n * Whether this key represents printable text input.\n * When false, the key is a control/function/modifier key that should not\n * produce text input (e.g., arrows, function keys, capslock, media keys).\n * Only set by the kitty protocol parser.\n */\n isPrintable?: boolean\n /**\n * Text associated with the key.\n * For printable kitty keys, defaults to the character from the codepoint.\n * When REPORT_TEXT flag is active, contains the decoded text-as-codepoints.\n */\n text?: string\n}\n\n/**\n * Parse a raw input sequence into a structured keypress object.\n * Accepts string or Buffer (Buffer support for stdin compatibility).\n */\nexport function parseKeypress(s: string | Buffer): ParsedKeypress {\n let input: string\n\n if (typeof Buffer !== \"undefined\" && Buffer.isBuffer(s)) {\n if (s[0] !== undefined && s[0]! > 127 && s[1] === undefined) {\n const buf = Buffer.from(s)\n buf[0]! -= 128\n input = `\\x1b${buf.toString()}`\n } else {\n input = s.toString()\n }\n } else {\n input = typeof s === \"string\" ? (s ?? \"\") : String(s)\n }\n\n const key: ParsedKeypress = {\n name: \"\",\n ctrl: false,\n meta: false,\n shift: false,\n option: false,\n super: false,\n hyper: false,\n sequence: input,\n }\n\n if (input === \"\\r\") {\n key.name = \"return\"\n } else if (input === \"\\n\") {\n // In legacy terminal mode, Enter sends \\r. The only way to get \\n is\n // Ctrl+Enter (or Ctrl+J, same byte). Treat it as ctrl+return so\n // TextArea's submitKey=\"ctrl+enter\" works without Kitty protocol.\n key.name = \"return\"\n key.ctrl = true\n } else if (input === \"\\t\") {\n key.name = \"tab\"\n } else if (input === \"\\b\" || input === \"\\x1b\\b\") {\n key.name = \"backspace\"\n key.meta = input.charAt(0) === \"\\x1b\"\n } else if (input === \"\\x7f\" || input === \"\\x1b\\x7f\") {\n // Modern terminals send \\x7f for Backspace key (not \\x08).\n // The actual Delete key sends \\x1b[3~ (handled by CODE_TO_KEY).\n key.name = \"backspace\"\n key.meta = input.charAt(0) === \"\\x1b\"\n } else if (input === \"\\x1b\\r\") {\n // Meta + Return (Alt+Enter / Option+Return on macOS)\n key.name = \"return\"\n key.meta = true\n } else if (input === \"\\x1b\" || input === \"\\x1b\\x1b\") {\n key.name = \"escape\"\n key.meta = input.length === 2\n } else if (input === \" \" || input === \"\\x1b \") {\n key.name = \"space\"\n key.meta = input.length === 2\n } else if (input.length === 1 && input <= \"\\x1a\") {\n // ctrl+letter\n key.name = String.fromCharCode(input.charCodeAt(0) + \"a\".charCodeAt(0) - 1)\n key.ctrl = true\n } else if (input === \"\\x1f\") {\n // Ctrl+/ sends 0x1F (Unit Separator) in terminals\n key.name = \"/\"\n key.ctrl = true\n } else if (input.length === 1 && input >= \"0\" && input <= \"9\") {\n key.name = \"number\"\n } else if (input.length === 1 && input >= \"a\" && input <= \"z\") {\n key.name = input\n } else if (input.length === 1 && input >= \"A\" && input <= \"Z\") {\n key.name = input.toLowerCase()\n key.shift = true\n } else {\n // Try Kitty keyboard protocol first (CSI codepoint ; modifiers u)\n // Must be checked before FN_KEY_RE because 'u' matches [a-zA-Z]\n const kittyParts = KITTY_RE.exec(input)\n // Kitty-enhanced special keys: CSI number ; modifiers : eventType {letter|~}\n const kittySpecialParts = !kittyParts && KITTY_SPECIAL_RE.exec(input)\n // xterm modifyOtherKeys format: CSI 27 ; modifier ; keycode ~\n // Sent by Ghostty, xterm, and others for modified keys like Ctrl+Enter\n const modifyOtherKeysParts =\n !kittyParts && !kittySpecialParts && MODIFY_OTHER_KEYS_RE.exec(input)\n\n if (kittySpecialParts) {\n // Kitty-enhanced special key: CSI number ; modifiers : eventType {letter|~}\n const number = Number(kittySpecialParts[1])\n const modifier = Math.max(0, Number(kittySpecialParts[2]) - 1)\n const eventType = Number(kittySpecialParts[3])\n const terminator = kittySpecialParts[4]!\n\n const name =\n terminator === \"~\"\n ? KITTY_SPECIAL_NUMBER_KEYS[number]\n : KITTY_SPECIAL_LETTER_KEYS[terminator]\n\n key.isKittyProtocol = true\n key.isPrintable = false\n key.raw = input\n key.name = name ?? \"\"\n\n key.shift = !!(modifier & 1)\n key.option = !!(modifier & 2) // alt\n key.ctrl = !!(modifier & 4)\n key.super = !!(modifier & 8)\n key.hyper = !!(modifier & 16)\n key.meta = !!(modifier & 32)\n key.capsLock = !!(modifier & 64)\n key.numLock = !!(modifier & 128)\n\n const eventTypeStr = numericToEventType(eventType)\n if (eventTypeStr) {\n key.eventType = eventTypeStr\n }\n } else if (kittyParts || modifyOtherKeysParts) {\n let codepoint: number\n let modifier: number\n if (kittyParts) {\n codepoint = Number(kittyParts[1])\n modifier = Math.max(0, Number(kittyParts[4] || 1) - 1)\n } else {\n const mokParts = modifyOtherKeysParts as RegExpExecArray\n modifier = Math.max(0, Number(mokParts[1]) - 1)\n codepoint = Number(mokParts[2])\n }\n\n // Mark as kitty protocol\n if (kittyParts) {\n key.isKittyProtocol = true\n key.raw = input\n\n // Handle invalid codepoints (above U+10FFFF or surrogates)\n if (!isValidCodepoint(codepoint)) {\n key.name = \"\"\n key.isPrintable = false\n return key\n }\n }\n\n key.shift = !!(modifier & 1)\n key.option = !!(modifier & 2) // alt (in kitty protocol, bit 2 = alt/option)\n key.ctrl = !!(modifier & 4)\n key.super = !!(modifier & 8) // super (Cmd on macOS)\n key.hyper = !!(modifier & 16) // hyper\n key.meta = !!(modifier & 32) // meta (kitty distinguishes meta from alt)\n key.capsLock = !!(modifier & 64)\n key.numLock = !!(modifier & 128)\n\n // Event type from Kitty protocol (group 5): 1=press, 2=repeat, 3=release\n if (kittyParts?.[5]) {\n const et = numericToEventType(Number(kittyParts[5]))\n if (et) key.eventType = et\n }\n\n // Shifted codepoint (group 2)\n if (kittyParts?.[2]) {\n key.shiftedKey = String.fromCodePoint(Number(kittyParts[2]))\n }\n\n // Base layout key (group 3)\n if (kittyParts?.[3]) {\n key.baseLayoutKey = String.fromCodePoint(Number(kittyParts[3]))\n }\n\n // Text-as-codepoints (group 6) — requires REPORT_TEXT flag\n let textFromProtocol: string | undefined\n if (kittyParts?.[6]) {\n textFromProtocol = kittyParts[6]\n .split(\":\")\n .map((cp) => safeFromCodePoint(Number(cp)))\n .join(\"\")\n key.associatedText = textFromProtocol\n key.text = textFromProtocol\n }\n\n // Map codepoint to key name and determine printability\n if (codepoint === 32) {\n key.name = \"space\"\n key.isPrintable = true\n } else if (codepoint === 13) {\n key.name = \"return\"\n key.isPrintable = true\n } else {\n const mapped = kittyCodepointToName(codepoint)\n if (mapped) {\n key.name = mapped\n key.isPrintable = false\n } else if (codepoint >= 1 && codepoint <= 26) {\n // Ctrl+letter comes as codepoint 1-26\n key.name = String.fromCodePoint(codepoint + 96) // 'a' is 97\n key.isPrintable = false\n } else if (codepoint >= 32 && codepoint <= 126) {\n // Printable ASCII\n key.name = String.fromCharCode(codepoint).toLowerCase()\n if (codepoint >= 65 && codepoint <= 90) {\n key.shift = true\n key.name = String.fromCharCode(codepoint + 32)\n }\n key.isPrintable = true\n } else if (isValidCodepoint(codepoint)) {\n key.name = safeFromCodePoint(codepoint)\n key.isPrintable = true\n } else {\n key.name = \"\"\n key.isPrintable = false\n }\n }\n\n // Default text to the character from the codepoint when not explicitly\n // provided by the protocol, so keys like space and return produce their\n // expected text input (' ' and '\\r' respectively).\n // With REPORT_ALL_KEYS, Shift+key sends base codepoint + shift modifier\n // plus shifted_codepoint. Use it for correct text (Shift+1 → '!').\n if (kittyParts && key.isPrintable && !textFromProtocol) {\n if (key.shift && key.shiftedKey) {\n key.text = key.shiftedKey // Shift+1→'!', Shift+;→':'\n } else if (key.shift && codepoint >= 97 && codepoint <= 122) {\n key.text = String.fromCharCode(codepoint - 32) // 'a'→'A'\n } else if (key.shift && codepoint >= 65 && codepoint <= 90) {\n key.text = String.fromCharCode(codepoint) // Already uppercase — 'A' stays 'A'\n } else if (\n key.shift &&\n key.isPrintable &&\n !key.shiftedKey &&\n codepoint >= 32 &&\n codepoint <= 126\n ) {\n // Kitty protocol with Shift held on a non-letter key but no shifted_codepoint.\n // The terminal is using DISAMBIGUATE flag only (missing REPORT_ALL_KEYS flag 8).\n //\n // What goes wrong: key.text will be the base character ('1' instead of '!',\n // ';' instead of ':', '=' instead of '+', etc.). This means:\n // - Hotkey bindings like \"!\" won't match when the user types Shift+1\n // - TextInput will insert '1' instead of '!' when the user types Shift+1\n // - Any shifted punctuation produces the wrong character\n //\n // Fix: enable REPORT_ALL_KEYS when requesting the Kitty keyboard protocol.\n // Silvery defaults to this (flag 11 = DISAMBIGUATE|REPORT_EVENTS|REPORT_ALL_KEYS).\n // If you see this warning, your app or framework is overriding the default flags.\n //\n // See: https://terminfo.dev/extensions/kitty-keyboard-report-all-keys\n // https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement\n // Best-effort recovery: use US QWERTY shifted punct map as fallback.\n // Not perfect for non-US layouts, but better than inserting '1' for '#'.\n const baseChar = safeFromCodePoint(codepoint)\n const shifted = BASE_TO_SHIFTED_PUNCT[baseChar]\n key.text = shifted ?? baseChar\n } else {\n key.text = safeFromCodePoint(codepoint)\n }\n }\n } else if (KITTY_RE.test(input)) {\n // Matched kitty pattern but was rejected (e.g., invalid codepoint in the\n // parseKittyKeypress path above returned early). Return safe empty keypress.\n key.isKittyProtocol = true\n key.isPrintable = false\n key.raw = input\n return key\n } else {\n let parts = META_KEY_CODE_RE.exec(input)\n if (input === \"\\x1b[\") {\n // Ink compatibility: when a pending ESC[ prefix is flushed as\n // literal input, surface it as \"[\" rather than Alt+[.\n key.name = \"[\"\n } else if (parts) {\n key.meta = true\n key.shift = /^[A-Z]$/.test(parts[1] ?? \"\")\n } else {\n parts = FN_KEY_RE.exec(input)\n if (parts) {\n const segs = input.split(\"\")\n if (segs[0] === \"\\u001b\" && segs[1] === \"\\u001b\") {\n key.option = true\n }\n\n // Reassemble key code\n const code = [parts[1], parts[2], parts[4], parts[6]].filter(Boolean).join(\"\")\n const modifier = (Number(parts[3] || parts[5] || 1) - 1) as number\n\n key.ctrl = !!(modifier & 4)\n key.meta = !!(modifier & 2) // alt\n key.super = !!(modifier & 8) // super (Cmd on macOS)\n key.hyper = !!(modifier & 16) // hyper\n key.shift = !!(modifier & 1)\n key.capsLock = !!(modifier & 64)\n key.numLock = !!(modifier & 128)\n key.code = code\n key.name = CODE_TO_KEY[code] ?? \"\"\n key.shift = SHIFT_CODES.has(code) || key.shift\n key.ctrl = CTRL_CODES.has(code) || key.ctrl\n }\n }\n }\n }\n\n return key\n}\n\n/**\n * Parse raw terminal input into a Key object and cleaned input string.\n *\n * @param rawInput Raw terminal input (string or Buffer)\n * @returns Tuple of [cleanedInput, Key]\n */\nexport function parseKey(rawInput: string | Buffer): [string, Key] {\n const keypress = parseKeypress(rawInput)\n\n const key: Key = {\n upArrow: keypress.name === \"up\",\n downArrow: keypress.name === \"down\",\n leftArrow: keypress.name === \"left\",\n rightArrow: keypress.name === \"right\",\n pageDown: keypress.name === \"pagedown\",\n pageUp: keypress.name === \"pageup\",\n home: keypress.name === \"home\",\n end: keypress.name === \"end\",\n return: keypress.name === \"return\",\n escape: keypress.name === \"escape\",\n ctrl: keypress.ctrl,\n shift: keypress.shift,\n tab: keypress.name === \"tab\",\n backspace: keypress.name === \"backspace\",\n delete: keypress.name === \"delete\",\n meta: keypress.name !== \"escape\" && (keypress.meta || keypress.option),\n super: keypress.super,\n hyper: keypress.hyper,\n capsLock: keypress.capsLock ?? false,\n numLock: keypress.numLock ?? false,\n eventType: keypress.eventType,\n }\n\n let input: string\n\n if (keypress.isKittyProtocol) {\n // Kitty protocol: use base key name for keybinding resolution.\n // key.text (set by parseKeypress) has the shifted character for text insertion,\n // but input must be the base key so keybindings like \"shift-/\" match correctly.\n //\n // Exception: shifted letters get uppercase input to match legacy terminal behavior\n // (legacy terminals send \"G\" for Shift+g). This matters because many components\n // check `input === \"G\"` directly. The km command system normalizes via keyToString\n // so uppercase vs lowercase doesn't affect keybinding resolution for letters.\n if (keypress.isPrintable) {\n if (\n keypress.shift &&\n keypress.name.length === 1 &&\n keypress.name >= \"a\" &&\n keypress.name <= \"z\"\n ) {\n input = keypress.name.toUpperCase()\n } else if (keypress.name === \"space\") {\n input = \" \"\n } else if (keypress.name === \"return\") {\n input = \"\\r\"\n } else {\n input = keypress.name\n }\n } else if (keypress.ctrl && keypress.name.length === 1) {\n // Ctrl+letter via codepoint 1-26: provide the letter name so handlers\n // (e.g., exitOnCtrlC checking input === 'c' && key.ctrl) still work.\n input = keypress.name\n } else {\n input = \"\"\n }\n } else {\n input = keypress.ctrl ? keypress.name : keypress.sequence\n\n if (NON_ALPHANUMERIC_KEYS.includes(keypress.name)) {\n input = \"\"\n }\n\n // Strip meta prefix if remaining\n if (input.startsWith(\"\\u001b\")) {\n input = input.slice(1)\n }\n\n // Filter out escape sequence fragments that leak through\n // e.g., \"[2~\" from Insert key, \"[A\" from arrows when not fully parsed\n // Single \"[\" and \"]\" are allowed — they're valid key bindings\n if (\n (input.startsWith(\"[\") && input.length > 1) ||\n (input.startsWith(\"O\") && input.length > 1)\n ) {\n // For Kitty-encoded keys (Super/Hyper modifiers), preserve the key name\n // since the raw sequence was CSI codepoint;modifiers u\n if (keypress.super || keypress.hyper) {\n input = keypress.name\n } else {\n input = \"\"\n }\n }\n }\n\n // Preserve the actual typed character for text insertion.\n // For Kitty: keypress.text has the shifted char ('?' for Shift+/), input has the base key ('/').\n // For legacy: input has the shifted char ('#' for Shift+3), which gets normalized below.\n if (keypress.isKittyProtocol) {\n key.text = keypress.text ?? input\n } else if (input.length >= 1) {\n key.text = input\n }\n\n // Legacy terminals: normalize shifted punctuation → base key + shift\n // Kitty protocol already provides base codepoint + shift modifier\n if (!keypress.isKittyProtocol && input.length === 1 && !key.shift) {\n const base = SHIFTED_PUNCT_MAP[input]\n if (base) {\n input = base\n key.shift = true\n }\n }\n\n // Detect shift for uppercase letters\n if (input.length === 1 && typeof input[0] === \"string\" && /[A-Z]/.test(input[0])) {\n key.shift = true\n }\n\n // Flag modifier-only events (Kitty protocol sends Shift/Ctrl/Alt/Super/Hyper/Meta as keys)\n if (input === \"\" && MODIFIER_ONLY_NAMES.has(keypress.name)) {\n key.isModifierOnly = true\n // Surface the underlying Kitty name so consumers can tell WHICH\n // modifier was pressed/released — the modifier byte bits alone\n // don't disambiguate (release events keep the bit set). Bead:\n // @km/silvery/keydown-keyup-test-primitives.\n key.modifierName = keypress.name\n }\n\n // Trace every parsed key — enable with DEBUG=silvery:keys. The raw\n // sequence + parsed name/modifiers lets you pinpoint why a keystroke\n // isn't firing (terminal not in Kitty mode, modifier not parsed, etc.).\n log.debug?.(\"parseKey\", {\n raw: typeof rawInput === \"string\" ? rawInput : rawInput.toString(\"hex\"),\n input,\n name: keypress.name,\n shift: key.shift,\n ctrl: key.ctrl,\n meta: key.meta,\n super: key.super,\n tab: key.tab,\n kitty: keypress.isKittyProtocol ?? false,\n })\n\n return [input, key]\n}\n\n/**\n * Create an empty Key object (all fields false).\n */\nexport function emptyKey(): Key {\n return {\n upArrow: false,\n downArrow: false,\n leftArrow: false,\n rightArrow: false,\n pageDown: false,\n pageUp: false,\n home: false,\n end: false,\n return: false,\n escape: false,\n ctrl: false,\n shift: false,\n tab: false,\n backspace: false,\n delete: false,\n meta: false,\n super: false,\n hyper: false,\n capsLock: false,\n numLock: false,\n }\n}\n\n// ============================================================================\n// Key Utility Functions\n// ============================================================================\n\n/**\n * Convert a Key object to a named key string.\n *\n * Returns the Playwright-compatible name for special keys (ArrowUp, Enter, etc.)\n * or \"\" if no special key is pressed.\n */\nexport function keyToName(key: Key): string {\n if (key.upArrow) return \"ArrowUp\"\n if (key.downArrow) return \"ArrowDown\"\n if (key.leftArrow) return \"ArrowLeft\"\n if (key.rightArrow) return \"ArrowRight\"\n if (key.return) return \"Enter\"\n if (key.escape) return \"Escape\"\n if (key.backspace) return \"Backspace\"\n if (key.delete) return \"Delete\"\n if (key.tab) return \"Tab\"\n if (key.pageUp) return \"PageUp\"\n if (key.pageDown) return \"PageDown\"\n if (key.home) return \"Home\"\n if (key.end) return \"End\"\n return \"\"\n}\n\n/**\n * Extract modifier flags from a Key object.\n * `alt` is always false (terminals cannot distinguish alt from meta).\n */\nexport function keyToModifiers(key: Key): {\n ctrl: boolean\n meta: boolean\n shift: boolean\n alt: boolean\n super: boolean\n hyper: boolean\n} {\n return {\n ctrl: !!key.ctrl,\n meta: !!key.meta,\n shift: !!key.shift,\n alt: false,\n super: !!key.super,\n hyper: !!key.hyper,\n }\n}\n\n/**\n * Parse a hotkey string into base key and modifiers.\n *\n * Supports Playwright-style (\"Control+c\", \"Shift+ArrowUp\") and\n * lowercase aliases (\"ctrl+c\", \"shift+tab\", \"cmd+a\").\n *\n * @example\n * ```tsx\n * parseHotkey('j') // { key: 'j', ctrl: false, meta: false, shift: false, alt: false }\n * parseHotkey('Control+c') // { key: 'c', ctrl: true, ... }\n * parseHotkey('Shift+ArrowUp') // { key: 'ArrowUp', shift: true, ... }\n * parseHotkey('⌘j') // { key: 'j', super: true, ... } (macOS symbol prefix)\n * parseHotkey('⌃⇧a') // { key: 'a', ctrl: true, shift: true, ... }\n * ```\n */\nexport function parseHotkey(keyStr: string): ParsedHotkey {\n // Support macOS symbol prefix format: ⌘J, ⌃⇧J, ✦⌘J\n let remaining = keyStr\n const symbolMods = new Set<string>()\n for (const char of remaining) {\n if (MODIFIER_SYMBOLS.has(char)) {\n symbolMods.add(char)\n } else {\n break\n }\n }\n\n if (symbolMods.size > 0) {\n remaining = remaining.slice(symbolMods.size)\n if (remaining.startsWith(\"+\")) remaining = remaining.slice(1)\n }\n\n const parts = remaining.split(\"+\")\n const key = parts.pop() || keyStr\n const modifiers = new Set([...parts.map((p) => p.toLowerCase()), ...symbolMods])\n\n return {\n key,\n ctrl: modifiers.has(\"control\") || modifiers.has(\"ctrl\") || modifiers.has(\"⌃\"),\n meta:\n modifiers.has(\"meta\") ||\n modifiers.has(\"alt\") ||\n modifiers.has(\"opt\") ||\n modifiers.has(\"option\") ||\n modifiers.has(\"⌥\"),\n shift: modifiers.has(\"shift\") || modifiers.has(\"⇧\"),\n alt: false, // alt and meta are indistinguishable in terminals; use meta\n super:\n modifiers.has(\"super\") ||\n modifiers.has(\"cmd\") ||\n modifiers.has(\"command\") ||\n modifiers.has(\"⌘\"),\n hyper: modifiers.has(\"hyper\") || modifiers.has(\"✦\"),\n }\n}\n\n/**\n * Match a parsed hotkey against a Key object and input string.\n *\n * @param hotkey Parsed hotkey to match\n * @param key Key object from input event\n * @param input Optional input string (for matching character keys)\n * @returns true if the hotkey matches the key event\n */\nexport function matchHotkey(hotkey: ParsedHotkey, key: Key, input?: string): boolean {\n // Check modifiers\n if (!!hotkey.ctrl !== !!key.ctrl) return false\n if (!!hotkey.meta !== !!key.meta) return false\n if (!!hotkey.super !== !!key.super) return false\n if (!!hotkey.hyper !== !!key.hyper) return false\n if (!!hotkey.alt !== false) return false // terminals can't distinguish alt from meta\n\n // For single-character hotkeys without explicit Shift (e.g., \"!\", \"J\", \"@\"),\n // skip the shift check — match against the input string directly.\n // The character itself encodes shift intent regardless of keyboard layout.\n // This is layout-independent: French \"!\" (unshifted) and US \"!\" (Shift+1) both match.\n const isCharacterHotkey = hotkey.key.length === 1 && !hotkey.shift\n if (!isCharacterHotkey && !!hotkey.shift !== !!key.shift) return false\n\n // Check key name against Key boolean fields\n const name = keyToName(key)\n if (name && name === hotkey.key) return true\n\n // Check against input string (base key for keybinding resolution)\n if (input !== undefined && input === hotkey.key) return true\n\n // Check against key.text (actual typed character for text insertion).\n // Kitty protocol: input is the base key (\"1\") but key.text is the shifted char (\"!\").\n // Character hotkeys like \"!\" should match against key.text so they work across protocols.\n if (key.text !== undefined && key.text === hotkey.key) return true\n\n return false\n}\n\n// ============================================================================\n// Kitty Protocol Output\n// ============================================================================\n\n/** Reverse map: key name → Kitty codepoint */\nconst NAME_TO_KITTY_CODEPOINT: Record<string, number> = {}\nfor (const [cp, name] of Object.entries(KITTY_CODEPOINT_MAP)) {\n NAME_TO_KITTY_CODEPOINT[name] = Number(cp)\n}\n\n// Bare modifier-name aliases — lookup defaults to the LEFT variant so\n// `keyToKittyAnsi(\"Super\")` produces the same sequence as\n// `keyToKittyAnsi(\"leftsuper\")` (left-cmd press). Tests asserting on a\n// specific physical key (right-shift, right-super, …) keep using the\n// `left*`/`right*` names directly. Bead:\n// @km/silvery/keydown-keyup-test-primitives.\nNAME_TO_KITTY_CODEPOINT[\"shift\"] = 57441\nNAME_TO_KITTY_CODEPOINT[\"control\"] = 57442\nNAME_TO_KITTY_CODEPOINT[\"alt\"] = 57443\nNAME_TO_KITTY_CODEPOINT[\"super\"] = 57444\nNAME_TO_KITTY_CODEPOINT[\"hyper\"] = 57445\nNAME_TO_KITTY_CODEPOINT[\"meta\"] = 57446\n// Aliases for the canonical modifier names that the alias table maps to.\n// `cmd`/`command`/`super` all normalize to \"Super\"; `keyToKittyAnsi` does\n// `mainKey.toLowerCase()` then this lookup, so we cover the lowered form.\n\n/**\n * Implicit modifier bits for modifier-only keys.\n * When a modifier key is pressed by itself, real terminals include the\n * corresponding modifier bit in the CSI u sequence. E.g., pressing Cmd alone\n * sends `\\x1b[57444;9u` (codepoint 57444, modifier 9 = super(8) + 1).\n * This map ensures keyToKittyAnsi produces the same encoding.\n */\nconst MODIFIER_KEY_IMPLICIT_BITS: Record<string, number> = {\n leftshift: 1,\n rightshift: 1,\n leftcontrol: 4,\n rightcontrol: 4,\n leftalt: 2,\n rightalt: 2,\n leftsuper: 8,\n rightsuper: 8,\n lefthyper: 16,\n righthyper: 16,\n leftmeta: 32,\n rightmeta: 32,\n // Bare modifier-name aliases — same implicit bits as the LEFT variant\n // so `keyToKittyAnsi(\"Super\")` includes the super(8) bit by itself.\n // Bead: @km/silvery/keydown-keyup-test-primitives.\n shift: 1,\n control: 4,\n alt: 2,\n super: 8,\n hyper: 16,\n meta: 32,\n}\n\n/** Playwright-style key name → CSI u codepoint for keys using CSI u format */\nconst PLAYWRIGHT_TO_KITTY_CSI_U: Record<string, number> = {\n Enter: 13,\n Escape: 27,\n Backspace: 127,\n Tab: 9,\n Space: 32,\n}\n\n/** Playwright-style key name → Kitty enhanced special key suffix (letter-terminated) */\nconst PLAYWRIGHT_TO_KITTY_SPECIAL_LETTER: Record<string, string> = {\n ArrowUp: \"A\",\n ArrowDown: \"B\",\n ArrowRight: \"C\",\n ArrowLeft: \"D\",\n Home: \"H\",\n End: \"F\",\n F1: \"P\",\n F2: \"Q\",\n F3: \"R\",\n F4: \"S\",\n}\n\n/** Playwright-style key name → Kitty enhanced special key number (tilde-terminated) */\nconst PLAYWRIGHT_TO_KITTY_SPECIAL_TILDE: Record<string, number> = {\n Insert: 2,\n Delete: 3,\n PageUp: 5,\n PageDown: 6,\n F5: 15,\n F6: 17,\n F7: 18,\n F8: 19,\n F9: 20,\n F10: 21,\n F11: 23,\n F12: 24,\n}\n\n/**\n * Convert a Playwright-style key string to a Kitty keyboard protocol ANSI sequence.\n *\n * Uses the appropriate Kitty format for each key type:\n * - Regular keys: CSI codepoint ; modifiers u\n * - Arrow/nav keys: CSI 1 ; modifiers letter (enhanced special key format)\n * - Tilde keys: CSI number ; modifiers ~ (enhanced special key format)\n *\n * @example\n * ```tsx\n * keyToKittyAnsi('a') // '\\x1b[97u' (no modifiers → bare)\n * keyToKittyAnsi('Enter') // '\\x1b[13u'\n * keyToKittyAnsi('Control+c') // '\\x1b[99;5u' (ctrl = 4, modifier = 5)\n * keyToKittyAnsi('Shift+Enter') // '\\x1b[13;2u' (shift = 1, modifier = 2)\n * keyToKittyAnsi('ArrowUp') // '\\x1b[1;1A' (enhanced special key)\n * ```\n */\nexport function keyToKittyAnsi(key: string): string {\n const parts = key.split(\"+\")\n const mainKey = parts.pop()!\n const modifiers = parts.map(normalizeModifier)\n\n // Calculate modifier bitfield\n let mod = 0\n if (modifiers.includes(\"Shift\")) mod |= 1\n if (modifiers.includes(\"Alt\") || modifiers.includes(\"Meta\")) mod |= 2\n if (modifiers.includes(\"Control\")) mod |= 4\n if (modifiers.includes(\"Super\")) mod |= 8\n if (modifiers.includes(\"Hyper\")) mod |= 16\n\n // Check for letter-terminated special keys (arrow keys, home, end, F1-F4)\n const specialLetter = PLAYWRIGHT_TO_KITTY_SPECIAL_LETTER[mainKey]\n if (specialLetter) {\n return `\\x1b[1;${mod + 1}${specialLetter}`\n }\n\n // Check for tilde-terminated special keys (insert, delete, pageup, F5-F12)\n const specialNumber = PLAYWRIGHT_TO_KITTY_SPECIAL_TILDE[mainKey]\n if (specialNumber !== undefined) {\n return `\\x1b[${specialNumber};${mod + 1}~`\n }\n\n // Check CSI u format keys\n const csiUCodepoint = PLAYWRIGHT_TO_KITTY_CSI_U[mainKey]\n if (csiUCodepoint !== undefined) {\n if (mod > 0) {\n return `\\x1b[${csiUCodepoint};${mod + 1}u`\n }\n return `\\x1b[${csiUCodepoint}u`\n }\n\n // Single character — use Unicode codepoint in CSI u format\n if (mainKey.length === 1) {\n const codepoint = mainKey.charCodeAt(0)\n if (mod > 0) {\n // Include shifted_codepoint for Shift+key so parseKey can recover the actual character.\n // Format: CSI codepoint:shifted_codepoint;modifiers u\n if ((mod & 1) !== 0) {\n // Shift is held — check if there's a shifted character to include\n const shifted = BASE_TO_SHIFTED_PUNCT[mainKey]\n if (shifted) {\n return `\\x1b[${codepoint}:${shifted.charCodeAt(0)};${mod + 1}u`\n }\n // Shift+letter: include uppercase codepoint\n if (codepoint >= 97 && codepoint <= 122) {\n return `\\x1b[${codepoint}:${codepoint - 32};${mod + 1}u`\n }\n }\n return `\\x1b[${codepoint};${mod + 1}u`\n }\n return `\\x1b[${codepoint}u`\n }\n\n // Try lowercase as direct kitty name (e.g., \"return\", \"escape\")\n const cp = NAME_TO_KITTY_CODEPOINT[mainKey.toLowerCase()]\n if (cp !== undefined) {\n // Modifier-only keys: include the implicit modifier bit.\n // When the key IS a modifier (leftsuper, leftshift, etc.), real terminals\n // include the corresponding modifier bit in the sequence. E.g., pressing\n // Cmd sends codepoint 57444 with super bit (8), so modifier = 9.\n const implicitMod = MODIFIER_KEY_IMPLICIT_BITS[mainKey.toLowerCase()]\n if (implicitMod !== undefined) {\n mod |= implicitMod\n }\n if (mod > 0) {\n return `\\x1b[${cp};${mod + 1}u`\n }\n return `\\x1b[${cp}u`\n }\n\n // Fallback: return as-is (not a kitty key)\n return keyToAnsi(key)\n}\n\n// ============================================================================\n// Raw Input Splitting\n// ============================================================================\n\n/** Grapheme segmenter for splitting non-escape text into visual characters */\nconst graphemeSegmenter = new Intl.Segmenter(\"en\", { granularity: \"grapheme\" })\n\n/**\n * Split raw terminal input into individual keypresses.\n *\n * When stdin.read() returns multiple characters buffered together (e.g., rapid\n * typing, paste, or auto-repeat during heavy renders), this tokenizer splits\n * them into individual keypresses so each can be parsed and handled separately.\n *\n * Uses grapheme segmentation for non-escape text, so emoji with variation\n * selectors (❤️), ZWJ sequences (👨‍👩‍👧‍👦), and combining marks stay intact.\n *\n * Handles:\n * - CSI sequences: ESC [ ... (arrow keys, function keys, Kitty protocol)\n * - SS3 sequences: ESC O + letter\n * - Meta sequences: ESC + single char\n * - Double ESC\n * - Grapheme clusters (emoji, combining marks, CJK)\n */\nexport function* splitRawInput(data: string): Generator<string> {\n // Single character fast path (most common case in real terminal I/O)\n if (data.length <= 1) {\n if (data.length === 1) yield data\n return\n }\n\n let i = 0\n let textStart = -1 // start of accumulated non-escape text\n\n while (i < data.length) {\n if (data.charCodeAt(i) === 0x1b) {\n // Flush accumulated text before this escape sequence\n if (textStart >= 0) {\n yield* splitNonEscapeText(data.slice(textStart, i))\n textStart = -1\n }\n\n // ESC — start of escape sequence\n if (i + 1 >= data.length) {\n // Bare ESC at end of chunk\n yield \"\\x1b\"\n i++\n continue\n }\n\n const next = data.charCodeAt(i + 1)\n\n if (next === 0x5b) {\n // CSI sequence: ESC [ params final-byte\n // Final byte is in range 0x40-0x7E (@A-Z[\\]^_`a-z{|}~)\n let j = i + 2\n while (j < data.length) {\n const c = data.charCodeAt(j)\n if (c >= 0x40 && c <= 0x7e) {\n j++ // include the final byte\n break\n }\n j++\n }\n yield data.slice(i, j)\n i = j\n } else if (next === 0x4f) {\n // SS3 sequence: ESC O + one letter\n const end = Math.min(i + 3, data.length)\n yield data.slice(i, end)\n i = end\n } else if (next === 0x1b) {\n // Double ESC: meta + escape, OR meta + CSI/SS3 sequence\n // Check if a control sequence follows the double ESC\n if (i + 2 < data.length) {\n const third = data.charCodeAt(i + 2)\n if (third === 0x5b) {\n // Meta + CSI: ESC ESC [ params final-byte (e.g., meta+arrow)\n let j = i + 3\n while (j < data.length) {\n const c = data.charCodeAt(j)\n if (c >= 0x40 && c <= 0x7e) {\n j++ // include the final byte\n break\n }\n j++\n }\n yield data.slice(i, j)\n i = j\n } else if (third === 0x4f) {\n // Meta + SS3: ESC ESC O letter\n const end = Math.min(i + 4, data.length)\n yield data.slice(i, end)\n i = end\n } else {\n // Plain double ESC (meta+escape)\n yield \"\\x1b\\x1b\"\n i += 2\n }\n } else {\n // Double ESC at end of chunk\n yield \"\\x1b\\x1b\"\n i += 2\n }\n } else {\n // Meta + single char (Alt+key)\n yield data.slice(i, i + 2)\n i += 2\n }\n } else {\n // Non-escape: accumulate into text run\n if (textStart < 0) textStart = i\n i++\n }\n }\n\n // Flush final text run\n if (textStart >= 0) {\n yield* splitNonEscapeText(data.slice(textStart))\n }\n}\n\n/**\n * Split non-escape text, keeping most characters together as one chunk.\n *\n * Only delete (0x7F) and backspace (0x08) are split out individually because\n * they can arrive as rapid repeats in a single stdin chunk. Other control\n * characters like \\r and \\t may legitimately appear inside pasted text and\n * should NOT be split out (matching Ink's input-parser behavior).\n */\nfunction* splitNonEscapeText(text: string): Generator<string> {\n let segmentStart = 0\n for (let i = 0; i < text.length; i++) {\n const ch = text.charCodeAt(i)\n if (ch === 0x7f || ch === 0x08) {\n // Flush text before this delete/backspace\n if (i > segmentStart) {\n yield text.slice(segmentStart, i)\n }\n yield text[i]!\n segmentStart = i + 1\n }\n }\n // Flush remaining text\n if (segmentStart < text.length) {\n yield text.slice(segmentStart)\n }\n}\n\n/** Split a non-escape text run into grapheme clusters */\nfunction* splitGraphemes(text: string): Generator<string> {\n for (const { segment } of graphemeSegmenter.segment(text)) {\n yield segment\n }\n}\n","/**\n * Terminal buffer implementation for Silvery.\n *\n * Uses packed Uint32Array for efficient cell metadata storage,\n * with separate string array for character storage (needed for\n * multi-byte Unicode graphemes and combining characters).\n */\n\nimport { bgFromRgb, fgFromRgb, nearestAnsi16, rgbToAnsi256, type ColorLevel } from \"@silvery/ansi\"\nimport { fgColorCode, bgColorCode } from \"./ansi/sgr-codes\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Underline style variants (SGR 4:x codes).\n * - false: no underline\n * - 'single': standard underline (SGR 4 or 4:1)\n * - 'double': double underline (SGR 4:2)\n * - 'curly': curly/wavy underline (SGR 4:3)\n * - 'dotted': dotted underline (SGR 4:4)\n * - 'dashed': dashed underline (SGR 4:5)\n */\nexport type UnderlineStyle = false | \"single\" | \"double\" | \"curly\" | \"dotted\" | \"dashed\"\n\n/**\n * Text attributes that can be applied to a cell.\n */\nexport interface CellAttrs {\n bold?: boolean\n dim?: boolean\n italic?: boolean\n /** Simple underline flag (for backwards compatibility) */\n underline?: boolean\n /**\n * Underline style: 'single' | 'double' | 'curly' | 'dotted' | 'dashed'.\n * When set, takes precedence over the underline boolean.\n */\n underlineStyle?: UnderlineStyle\n /**\n * Overline (SGR 53/55). A line ABOVE the character cell, independent of\n * underline. Used for top-edge indicators where underline would read as\n * \"this row is underlined content\" instead of \"you're at the top\".\n */\n overline?: boolean\n blink?: boolean\n inverse?: boolean\n hidden?: boolean\n strikethrough?: boolean\n}\n\n/**\n * Color representation.\n * - number: 256-color index (0-255) — silvery's compact indexed form\n * - RGB object: true color; may carry `index` to preserve palette provenance\n * (identity-preserving color — the shape the terminal-flow engine produces).\n * When `index` is a valid 0-255 slot it is honored ahead of r/g/b, so the\n * color packs and re-emits as indexed SGR rather than a truecolor bake.\n * - null: default/inherit\n * - DEFAULT_BG: terminal's default background (SGR 49), opaque but uses terminal's own bg color\n */\nexport type Color = number | { r: number; g: number; b: number; index?: number } | null\n\n/**\n * Sentinel color representing the terminal's default background (SGR 49).\n * Unlike `null` (transparent/inherit), this actively fills cells with the\n * terminal's configured background, making the element opaque.\n */\nexport const DEFAULT_BG: Color = Object.freeze({ r: -1, g: -1, b: -1 })\n\n/** Check if a color is the default bg sentinel. */\nexport function isDefaultBg(color: Color): boolean {\n return color !== null && typeof color === \"object\" && color.r === -1\n}\n\n/**\n * A single cell in the terminal buffer.\n */\nexport interface Cell {\n /** The character/grapheme in this cell */\n char: string\n /** Foreground color */\n fg: Color\n /** Background color */\n bg: Color\n /**\n * Underline color (independent of fg).\n * Uses SGR 58. If null, underline uses fg color.\n */\n underlineColor: Color\n /** Text attributes */\n attrs: CellAttrs\n /** True if this is a wide character (CJK, emoji, etc.) */\n wide: boolean\n /** True if this is the continuation cell after a wide character */\n continuation: boolean\n /**\n * OSC 8 hyperlink URL.\n * When set, the cell is part of a clickable hyperlink in supporting terminals.\n */\n hyperlink?: string\n}\n\n/**\n * Partial cell update accepted by buffer write APIs.\n *\n * `selectable` is render metadata, not a visual cell property: it controls the\n * SELECTABLE_FLAG bit used by app-level semantic text selection. It is carried\n * on write payloads so selectability is explicit per mutation rather than an\n * ambient buffer mode.\n */\nexport type CellPatch = Partial<Cell> & { selectable?: boolean }\n\n/**\n * Style information for a cell (excludes char and position flags).\n */\nexport interface Style {\n fg: Color\n bg: Color\n /**\n * Underline color (independent of fg).\n * Uses SGR 58. If null, underline uses fg color.\n */\n underlineColor?: Color\n attrs: CellAttrs\n /**\n * OSC 8 hyperlink URL.\n * When set, the cell is part of a clickable hyperlink in supporting terminals.\n */\n hyperlink?: string\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n// Bit packing layout for cell metadata in Uint32Array:\n// [0-7]: foreground color index (8 bits)\n// [8-15]: background color index (8 bits)\n// [16-23]: attributes (8 bits): bold, dim, italic, blink, inverse, hidden, strikethrough, overline\n// [24-26]: underline style (3 bits): 0=none, 1=single, 2=double, 3=curly, 4=dotted, 5=dashed\n// [27-31]: flags (5 bits): wide, continuation, true_color_fg, true_color_bg + 1 spare\n\n// Attribute bit positions (within bits 16-23)\nconst ATTR_BOLD = 1 << 16\nconst ATTR_DIM = 1 << 17\nconst ATTR_ITALIC = 1 << 18\nconst ATTR_BLINK = 1 << 19\nconst ATTR_INVERSE = 1 << 20\nconst ATTR_HIDDEN = 1 << 21\nconst ATTR_STRIKETHROUGH = 1 << 22\nconst ATTR_OVERLINE = 1 << 23\n\n// Underline style (3 bits in positions 24-26)\n// 0 = no underline, 1 = single, 2 = double, 3 = curly, 4 = dotted, 5 = dashed\nconst UNDERLINE_STYLE_SHIFT = 24\nconst UNDERLINE_STYLE_MASK = 0x7 << UNDERLINE_STYLE_SHIFT // 3 bits\n\n// Flag bit positions (in bits 27-31)\nconst WIDE_FLAG = 1 << 27\nconst CONTINUATION_FLAG = 1 << 28\nconst TRUE_COLOR_FG_FLAG = 1 << 29\nconst TRUE_COLOR_BG_FLAG = 1 << 30\n/**\n * Selection mask flag — bit 31 in the packed cell metadata.\n * Set during render phase based on resolved `userSelect` prop.\n * Read during selection to determine which cells participate.\n *\n * Uses `>>> 0` to avoid JS signed 32-bit integer issues.\n */\nexport const SELECTABLE_FLAG = 0x80000000 >>> 0\n\n/**\n * Check if a cell is selectable (SELECTABLE_FLAG is set in packed metadata).\n */\nexport function isCellSelectable(packed: number): boolean {\n return ((packed >>> 0) & SELECTABLE_FLAG) !== 0\n}\n\n/**\n * Set the SELECTABLE_FLAG on a packed cell value.\n */\nexport function setSelectableFlag(packed: number): number {\n return (packed | SELECTABLE_FLAG) >>> 0\n}\n\n/**\n * Clear the SELECTABLE_FLAG on a packed cell value.\n */\nexport function clearSelectableFlag(packed: number): number {\n return (packed & ~SELECTABLE_FLAG) >>> 0\n}\n\n/**\n * Packed attribute bits that make a space character visually meaningful.\n * Inverse makes spaces visible (block of color), underline draws a line under spaces,\n * strikethrough draws a line through spaces, overline draws a line above spaces.\n * Other attrs (bold, dim, italic) don't visually affect space characters.\n */\nexport const VISIBLE_SPACE_ATTR_MASK =\n ATTR_INVERSE | ATTR_STRIKETHROUGH | ATTR_OVERLINE | UNDERLINE_STYLE_MASK\n\n// Default empty cell\nconst EMPTY_CELL: Cell = {\n char: \" \",\n fg: null,\n bg: null,\n underlineColor: null,\n attrs: {},\n wide: false,\n continuation: false,\n hyperlink: undefined,\n}\n\n/** Frozen empty attrs object, shared across zero-allocation reads for OOB cells */\nconst EMPTY_ATTRS: CellAttrs = Object.freeze({})\n\n// ============================================================================\n// Row Metadata\n// ============================================================================\n\n/**\n * Per-row metadata for text extraction correctness.\n * Maintained by the render phase during text rendering.\n */\nexport interface RowMetadata {\n /** True if this row continues on the next row (soft wrap, not hard break) */\n softWrapped: boolean\n /** Rightmost column with non-space content (for trailing space trimming) */\n lastContentCol: number\n /**\n * Only meaningful when `softWrapped` is true. Records whether the soft-wrap\n * break to the next row consumed a whitespace character (word wrap) versus a\n * forced mid-word break (a token longer than the line, or `wrap=\"hard\"`).\n *\n * `trim`-mode rendering (the default) strips the breaking space from BOTH\n * rows, so copy extraction must reinsert exactly one separator when rejoining\n * the visual rows into their logical line: a single space when\n * `wrapJoinSpace` is true, nothing when it is false/undefined. Without this,\n * \"alpha beta gamma\"+\"delta\" rejoins as \"alpha beta gammadelta\" (lost space)\n * and \"verylong\"+\"word\" would gain a spurious space.\n */\n wrapJoinSpace?: boolean\n}\n\n// ============================================================================\n// Packing/Unpacking Helpers\n// ============================================================================\n\n/**\n * Map UnderlineStyle to numeric value for bit packing.\n */\nfunction underlineStyleToNumber(style: UnderlineStyle | undefined): number {\n switch (style) {\n case false:\n return 0\n case \"single\":\n return 1\n case \"double\":\n return 2\n case \"curly\":\n return 3\n case \"dotted\":\n return 4\n case \"dashed\":\n return 5\n default:\n return 0 // undefined or unknown = no underline\n }\n}\n\n/**\n * Map numeric value back to UnderlineStyle.\n */\nfunction numberToUnderlineStyle(n: number): UnderlineStyle | undefined {\n switch (n) {\n case 0:\n return undefined // No underline\n case 1:\n return \"single\"\n case 2:\n return \"double\"\n case 3:\n return \"curly\"\n case 4:\n return \"dotted\"\n case 5:\n return \"dashed\"\n default:\n return undefined\n }\n}\n\n/**\n * Convert CellAttrs to bits for packing (used internally by packCell).\n * Note: This packs into the full 32-bit word, not just the attrs byte.\n */\nexport function attrsToNumber(attrs: CellAttrs): number {\n let n = 0\n if (attrs.bold) n |= ATTR_BOLD\n if (attrs.dim) n |= ATTR_DIM\n if (attrs.italic) n |= ATTR_ITALIC\n if (attrs.blink) n |= ATTR_BLINK\n if (attrs.inverse) n |= ATTR_INVERSE\n if (attrs.hidden) n |= ATTR_HIDDEN\n if (attrs.strikethrough) n |= ATTR_STRIKETHROUGH\n if (attrs.overline) n |= ATTR_OVERLINE\n\n // Pack underline style (3 bits)\n // If underlineStyle is set, use it. Otherwise, check underline boolean.\n const ulStyle = attrs.underlineStyle ?? (attrs.underline ? \"single\" : undefined)\n n |= underlineStyleToNumber(ulStyle) << UNDERLINE_STYLE_SHIFT\n\n return n\n}\n\n/**\n * Convert a number back to CellAttrs.\n */\nexport function numberToAttrs(n: number): CellAttrs {\n const attrs: CellAttrs = {}\n if (n & ATTR_BOLD) attrs.bold = true\n if (n & ATTR_DIM) attrs.dim = true\n if (n & ATTR_ITALIC) attrs.italic = true\n if (n & ATTR_BLINK) attrs.blink = true\n if (n & ATTR_INVERSE) attrs.inverse = true\n if (n & ATTR_HIDDEN) attrs.hidden = true\n if (n & ATTR_STRIKETHROUGH) attrs.strikethrough = true\n if (n & ATTR_OVERLINE) attrs.overline = true\n\n // Unpack underline style\n const ulStyleNum = (n & UNDERLINE_STYLE_MASK) >> UNDERLINE_STYLE_SHIFT\n const ulStyle = numberToUnderlineStyle(ulStyleNum)\n if (ulStyle) {\n attrs.underlineStyle = ulStyle\n attrs.underline = true\n }\n\n return attrs\n}\n\n/**\n * The 256-color palette slot for a color, or `undefined` when the color is a\n * genuine truecolor (RGB with no palette provenance) or null.\n *\n * A bare `number` IS its own palette slot. An RGB object may carry `index` to\n * preserve the origin slot (identity-preserving color); when present and valid\n * it wins over r/g/b, so an engine-produced indexed color survives the pack as\n * indexed rather than baking to truecolor.\n */\nfunction paletteIndex(color: Color): number | undefined {\n if (color === null) return undefined\n const idx = typeof color === \"number\" ? color : color.index\n return typeof idx === \"number\" && idx >= 0 && idx <= 255 ? idx : undefined\n}\n\n/**\n * Convert a color to an index value for packing.\n * Returns 0 for null (default), or (index + 1) for 256-color.\n * This +1 offset allows distinguishing null from black (color index 0).\n * True color is handled separately via flags and auxiliary storage.\n */\nfunction colorToIndex(color: Color): number {\n const slot = paletteIndex(color)\n if (slot === undefined) return 0 // null or true color (handled via flag)\n return (slot & 0xff) + 1 // +1 to distinguish from null\n}\n\n/**\n * Check if a color is true color (RGB with no palette provenance).\n *\n * An RGB object carrying a valid `index` is NOT true color — it packs into the\n * index slot and re-emits as indexed SGR. Only palette-less RGB reaches the\n * truecolor flag + auxiliary color storage.\n */\nfunction isTrueColor(color: Color): color is { r: number; g: number; b: number } {\n return color !== null && typeof color === \"object\" && paletteIndex(color) === undefined\n}\n\n/**\n * Pack cell metadata into a 32-bit number.\n */\nexport function packCell(cell: Cell): number {\n let packed = 0\n\n // Foreground color index (bits 0-7)\n packed |= colorToIndex(cell.fg) & 0xff\n\n // Background color index (bits 8-15)\n packed |= (colorToIndex(cell.bg) & 0xff) << 8\n\n // Attributes (bits 16-22) and underline style (bits 24-26)\n // attrsToNumber returns bits already in their final positions\n packed |= attrsToNumber(cell.attrs)\n\n // Flags (bits 27-30)\n if (cell.wide) packed |= WIDE_FLAG\n if (cell.continuation) packed |= CONTINUATION_FLAG\n if (isTrueColor(cell.fg)) packed |= TRUE_COLOR_FG_FLAG\n if (isTrueColor(cell.bg)) packed |= TRUE_COLOR_BG_FLAG\n\n return packed\n}\n\n/**\n * Unpack foreground color index from packed value.\n */\nfunction unpackFgIndex(packed: number): number {\n return packed & 0xff\n}\n\n/**\n * Unpack background color index from packed value.\n */\nfunction unpackBgIndex(packed: number): number {\n return (packed >> 8) & 0xff\n}\n\n/**\n * Unpack attributes from packed value.\n * Extracts both the boolean attrs (bits 16-22) and underline style (bits 24-26).\n */\nfunction unpackAttrs(packed: number): CellAttrs {\n // numberToAttrs expects the full packed value with attrs in bits 16-22\n // and underline style in bits 24-26\n return numberToAttrs(packed)\n}\n\n/**\n * Check if wide flag is set.\n */\nfunction unpackWide(packed: number): boolean {\n return (packed & WIDE_FLAG) !== 0\n}\n\n/**\n * Check if continuation flag is set.\n */\nfunction unpackContinuation(packed: number): boolean {\n return (packed & CONTINUATION_FLAG) !== 0\n}\n\n/**\n * Check if true color foreground flag is set.\n */\nfunction unpackTrueColorFg(packed: number): boolean {\n return (packed & TRUE_COLOR_FG_FLAG) !== 0\n}\n\n/**\n * Check if true color background flag is set.\n */\nfunction unpackTrueColorBg(packed: number): boolean {\n return (packed & TRUE_COLOR_BG_FLAG) !== 0\n}\n\n// ============================================================================\n// TerminalBuffer Class\n// ============================================================================\n\n/**\n * Efficient terminal cell buffer.\n *\n * Uses packed Uint32Array for cell metadata and separate string array\n * for characters. This allows efficient diffing while supporting\n * full Unicode grapheme clusters.\n */\nexport class TerminalBuffer {\n /** Packed cell metadata */\n private cells: Uint32Array\n /** Character storage (one per cell, may be multi-byte grapheme) */\n private chars: string[]\n /** True color foreground storage (only for cells with true color fg) */\n private fgColors: Map<number, { r: number; g: number; b: number }>\n /** True color background storage (only for cells with true color bg) */\n private bgColors: Map<number, { r: number; g: number; b: number }>\n /** Underline color storage (independent of fg, for SGR 58) */\n private underlineColors: Map<number, Color>\n /** OSC 8 hyperlink URL storage (only for cells that are part of a hyperlink) */\n private hyperlinks: Map<number, string>\n /**\n * Per-row dirty tracking for diff optimization.\n * When set, diffBuffers() can skip clean rows entirely.\n * 0 = clean (unchanged since last resetDirtyRows), 1 = dirty (modified).\n */\n private _dirtyRows: Uint8Array\n /** Bounding box: first dirty row (inclusive). -1 when no rows are dirty. */\n private _minDirtyRow: number\n /** Bounding box: last dirty row (inclusive). -1 when no rows are dirty. */\n private _maxDirtyRow: number\n /**\n * Per-row metadata for text extraction (soft wrap, last content column).\n * Set by the render phase, read by extractText.\n */\n private _rowMetadata: RowMetadata[]\n readonly width: number\n readonly height: number\n\n constructor(width: number, height: number) {\n this.width = width\n this.height = height\n const size = width * height\n this.cells = new Uint32Array(size)\n this.chars = new Array<string>(size).fill(\" \")\n this.fgColors = new Map()\n this.bgColors = new Map()\n this.underlineColors = new Map()\n this.hyperlinks = new Map()\n // All rows start dirty (fresh buffer needs full diff on first comparison)\n this._dirtyRows = new Uint8Array(height).fill(1)\n this._minDirtyRow = 0\n this._maxDirtyRow = height - 1\n // Row metadata: default to not soft-wrapped, no content\n this._rowMetadata = Array.from({ length: height }, () => ({\n softWrapped: false,\n lastContentCol: -1,\n }))\n }\n\n /**\n * Get the index for a cell position.\n */\n private index(x: number, y: number): number {\n return y * this.width + x\n }\n\n /**\n * Check if coordinates are within bounds.\n */\n inBounds(x: number, y: number): boolean {\n return x >= 0 && x < this.width && y >= 0 && y < this.height\n }\n\n /**\n * Get a cell at the given position.\n */\n getCell(x: number, y: number): Cell {\n if (!this.inBounds(x, y)) {\n return { ...EMPTY_CELL }\n }\n\n const idx = this.index(x, y)\n const packed = this.cells[idx]\n const char = this.chars[idx]\n\n // Determine foreground color\n // Color indices are stored with +1 offset (0=null, 1=black, 2=red, etc.)\n let fg: Color = null\n if (unpackTrueColorFg(packed!)) {\n fg = this.fgColors.get(idx) ?? null\n } else {\n const fgIndex = unpackFgIndex(packed!)\n fg = fgIndex > 0 ? fgIndex - 1 : null // -1 to restore actual color index\n }\n\n // Determine background color\n let bg: Color = null\n if (unpackTrueColorBg(packed!)) {\n bg = this.bgColors.get(idx) ?? null\n } else {\n const bgIndex = unpackBgIndex(packed!)\n bg = bgIndex > 0 ? bgIndex - 1 : null // -1 to restore actual color index\n }\n\n const hyperlink = this.hyperlinks.get(idx)\n return {\n char: char!,\n fg,\n bg,\n underlineColor: this.underlineColors.get(idx) ?? null,\n attrs: unpackAttrs(packed!),\n wide: unpackWide(packed!),\n continuation: unpackContinuation(packed!),\n ...(hyperlink !== undefined ? { hyperlink } : {}),\n }\n }\n\n // --------------------------------------------------------------------------\n // Zero-allocation cell accessors for hot paths\n // --------------------------------------------------------------------------\n\n /**\n * Count cells that are not default-empty.\n *\n * A cell is \"painted\" iff any of these differs from default-empty:\n * - char !== \" \"\n * - packed metadata is non-zero (any fg, bg, attr, wide/cont/truecolor flag)\n *\n * Used by the degenerate-frame canary in `render()` (renderer.ts) and by\n * test helpers that want to assert a fixture renders meaningful content.\n *\n * O(W*H) — single pass over the packed Uint32Array + chars array.\n */\n countPaintedCells(): number {\n let n = 0\n const len = this.cells.length\n for (let i = 0; i < len; i++) {\n if (this.cells[i]! !== 0 || this.chars[i]! !== \" \") n++\n }\n return n\n }\n\n /**\n * Get just the character at a cell position (no object allocation).\n * Returns \" \" for out-of-bounds positions.\n */\n getCellChar(x: number, y: number): string {\n if (!this.inBounds(x, y)) return \" \"\n return this.chars[this.index(x, y)]!\n }\n\n /**\n * Get just the background color at a cell position (no object allocation).\n * Returns null for out-of-bounds positions.\n */\n getCellBg(x: number, y: number): Color {\n if (!this.inBounds(x, y)) return null\n const idx = this.index(x, y)\n const packed = this.cells[idx]!\n if (unpackTrueColorBg(packed)) {\n return this.bgColors.get(idx) ?? null\n }\n const bgIndex = unpackBgIndex(packed)\n return bgIndex > 0 ? bgIndex - 1 : null\n }\n\n /**\n * Get just the foreground color at a cell position (no object allocation).\n * Returns null for out-of-bounds positions.\n */\n getCellFg(x: number, y: number): Color {\n if (!this.inBounds(x, y)) return null\n const idx = this.index(x, y)\n const packed = this.cells[idx]!\n if (unpackTrueColorFg(packed)) {\n return this.fgColors.get(idx) ?? null\n }\n const fgIndex = unpackFgIndex(packed)\n return fgIndex > 0 ? fgIndex - 1 : null\n }\n\n /**\n * Get the raw packed metadata at a cell position (no unpackAttrs allocation).\n * Returns 0 for out-of-bounds positions. The packed value contains color\n * indices, attr bits, underline style, and flags in a single Uint32.\n */\n getCellAttrs(x: number, y: number): number {\n if (!this.inBounds(x, y)) return 0\n return this.cells[this.index(x, y)]!\n }\n\n /**\n * Check if a cell is a wide character (no object allocation).\n * Returns false for out-of-bounds positions.\n */\n isCellWide(x: number, y: number): boolean {\n if (!this.inBounds(x, y)) return false\n return unpackWide(this.cells[this.index(x, y)]!)\n }\n\n /**\n * Check if a cell is a continuation of a wide character (no object allocation).\n * Returns false for out-of-bounds positions.\n */\n isCellContinuation(x: number, y: number): boolean {\n if (!this.inBounds(x, y)) return false\n return unpackContinuation(this.cells[this.index(x, y)]!)\n }\n\n /**\n * Check if a cell is selectable (SELECTABLE_FLAG is set, no object allocation).\n * Returns false for out-of-bounds positions.\n */\n isCellSelectable(x: number, y: number): boolean {\n if (!this.inBounds(x, y)) return false\n return ((this.cells[this.index(x, y)]! >>> 0) & SELECTABLE_FLAG) !== 0\n }\n\n // --------------------------------------------------------------------------\n // Row Metadata (for text extraction)\n // --------------------------------------------------------------------------\n\n /**\n * Set metadata for a row (soft wrap, last content column).\n * Called by the render phase during text rendering.\n */\n setRowMeta(row: number, meta: Partial<RowMetadata>): void {\n if (row < 0 || row >= this.height) return\n const existing = this._rowMetadata[row]!\n if (meta.softWrapped !== undefined) existing.softWrapped = meta.softWrapped\n if (meta.lastContentCol !== undefined) existing.lastContentCol = meta.lastContentCol\n if (meta.wrapJoinSpace !== undefined) existing.wrapJoinSpace = meta.wrapJoinSpace\n }\n\n /**\n * Get metadata for a row. Returns default values for out-of-bounds rows.\n */\n getRowMeta(row: number): RowMetadata {\n if (row < 0 || row >= this.height) return { softWrapped: false, lastContentCol: -1 }\n return this._rowMetadata[row]!\n }\n\n /**\n * Get the full row metadata array (for bulk access during text extraction).\n */\n getRowMetadataArray(): readonly RowMetadata[] {\n return this._rowMetadata\n }\n\n /**\n * Read cell data into a caller-provided Cell object (zero-allocation).\n * For hot loops that need the full Cell, reuse a single object:\n *\n * const cell = createMutableCell()\n * for (...) { buffer.readCellInto(x, y, cell) }\n *\n * Returns the same `out` object for chaining convenience.\n */\n readCellInto(x: number, y: number, out: Cell): Cell {\n if (!this.inBounds(x, y)) {\n out.char = \" \"\n out.fg = null\n out.bg = null\n out.underlineColor = null\n out.attrs = EMPTY_ATTRS\n out.wide = false\n out.continuation = false\n out.hyperlink = undefined\n return out\n }\n\n const idx = this.index(x, y)\n const packed = this.cells[idx]!\n\n out.char = this.chars[idx]!\n\n // Foreground color\n if (unpackTrueColorFg(packed)) {\n out.fg = this.fgColors.get(idx) ?? null\n } else {\n const fgIndex = unpackFgIndex(packed)\n out.fg = fgIndex > 0 ? fgIndex - 1 : null\n }\n\n // Background color\n if (unpackTrueColorBg(packed)) {\n out.bg = this.bgColors.get(idx) ?? null\n } else {\n const bgIndex = unpackBgIndex(packed)\n out.bg = bgIndex > 0 ? bgIndex - 1 : null\n }\n\n out.underlineColor = this.underlineColors.get(idx) ?? null\n\n // Unpack attrs inline to avoid allocating a new CellAttrs object.\n // We reuse the existing out.attrs object when possible.\n const attrs = out.attrs === EMPTY_ATTRS ? ((out.attrs = {}), out.attrs) : out.attrs\n attrs.bold = (packed & ATTR_BOLD) !== 0 ? true : undefined\n attrs.dim = (packed & ATTR_DIM) !== 0 ? true : undefined\n attrs.italic = (packed & ATTR_ITALIC) !== 0 ? true : undefined\n attrs.blink = (packed & ATTR_BLINK) !== 0 ? true : undefined\n attrs.inverse = (packed & ATTR_INVERSE) !== 0 ? true : undefined\n attrs.hidden = (packed & ATTR_HIDDEN) !== 0 ? true : undefined\n attrs.strikethrough = (packed & ATTR_STRIKETHROUGH) !== 0 ? true : undefined\n attrs.overline = (packed & ATTR_OVERLINE) !== 0 ? true : undefined\n\n const ulStyleNum = (packed & UNDERLINE_STYLE_MASK) >> UNDERLINE_STYLE_SHIFT\n const ulStyle = numberToUnderlineStyle(ulStyleNum)\n if (ulStyle) {\n attrs.underlineStyle = ulStyle\n attrs.underline = true\n } else {\n attrs.underlineStyle = undefined\n attrs.underline = undefined\n }\n\n out.wide = (packed & WIDE_FLAG) !== 0\n out.continuation = (packed & CONTINUATION_FLAG) !== 0\n out.hyperlink = this.hyperlinks.get(idx)\n\n return out\n }\n\n /**\n * Set a cell at the given position.\n *\n * Optimized: resolves defaults and packs metadata inline to avoid\n * allocating an intermediate Cell object.\n */\n setCell(x: number, y: number, cell: CellPatch): void {\n if (!this.inBounds(x, y)) {\n return\n }\n\n // Write trap for SILVERY_STRICT mismatch diagnosis\n const trap = (globalThis as any).__silvery_write_trap\n if (x === trap?.x && y === trap.y) {\n const char = cell.char ?? \" \"\n const stack = new Error().stack?.split(\"\\n\").slice(1, 6).join(\"\\n\") ?? \"\"\n trap.log.push(\n ` char=\"${char}\" fg=${cell.fg ?? \"null\"} bg=${cell.bg ?? \"null\"} dim=${cell.attrs?.dim} ul=${cell.attrs?.underline}\\n${stack}`,\n )\n }\n\n this._dirtyRows[y] = 1\n if (this._minDirtyRow === -1 || y < this._minDirtyRow) this._minDirtyRow = y\n if (y > this._maxDirtyRow) this._maxDirtyRow = y\n\n const idx = this.index(x, y)\n\n // Wide character consistency: when overwriting a continuation cell (second\n // half of a wide char), the leading main cell at x-1 must be replaced with\n // a space (matching Ink's overlay boundary clearing — see ink/output.ts).\n // Otherwise the buffer would have a wide-char glyph with no continuation,\n // and the terminal would render a half-visible wide character past the\n // overlay. When overwriting a wide char's main cell with a non-wide char,\n // clear the continuation cell at x+1 the same way.\n if (!(cell.continuation ?? false)) {\n const prevPacked = this.cells[idx]\n if (prevPacked !== undefined && (prevPacked & CONTINUATION_FLAG) !== 0 && x > 0) {\n // Overwriting a continuation cell — replace the leading wide-char cell\n // at x-1 with a space and clear its wide flag. This matches Ink's\n // pre-write boundary cleanup (ink/output.ts: currentLine[offsetX-1] = spaceCell).\n const prevIdx = idx - 1\n this.cells[prevIdx] = this.cells[prevIdx]! & ~WIDE_FLAG\n this.chars[prevIdx] = \" \"\n }\n }\n if (!(cell.wide ?? false)) {\n const prevPacked = this.cells[idx]\n if (prevPacked !== undefined && (prevPacked & WIDE_FLAG) !== 0 && x + 1 < this.width) {\n // Overwriting a wide cell with a non-wide char — clear continuation at x+1\n const nextIdx = idx + 1\n this.cells[nextIdx] = this.cells[nextIdx]! & ~CONTINUATION_FLAG\n this.chars[nextIdx] = \" \"\n }\n }\n\n // Resolve properties with defaults (no intermediate object)\n const char = cell.char ?? \" \"\n const fg = cell.fg ?? null\n const bg = cell.bg ?? null\n const underlineColor = cell.underlineColor ?? null\n const attrs = cell.attrs ?? EMPTY_ATTRS\n const wide = cell.wide ?? false\n const continuation = cell.continuation ?? false\n\n // Store character\n this.chars[idx] = char\n\n // Handle true color storage\n if (isTrueColor(fg)) {\n this.fgColors.set(idx, fg)\n } else {\n this.fgColors.delete(idx)\n }\n\n if (isTrueColor(bg)) {\n this.bgColors.set(idx, bg)\n } else {\n this.bgColors.delete(idx)\n }\n\n // Handle underline color storage\n if (underlineColor !== null) {\n this.underlineColors.set(idx, underlineColor)\n } else {\n this.underlineColors.delete(idx)\n }\n\n // Handle hyperlink storage\n const hyperlink = cell.hyperlink\n if (hyperlink !== undefined && hyperlink !== \"\") {\n this.hyperlinks.set(idx, hyperlink)\n } else {\n this.hyperlinks.delete(idx)\n }\n\n // Pack metadata inline (avoids packCell's fullCell parameter overhead)\n let packed = 0\n packed |= colorToIndex(fg) & 0xff\n packed |= (colorToIndex(bg) & 0xff) << 8\n packed |= attrsToNumber(attrs)\n if (wide) packed |= WIDE_FLAG\n if (continuation) packed |= CONTINUATION_FLAG\n if (isTrueColor(fg)) packed |= TRUE_COLOR_FG_FLAG\n if (isTrueColor(bg)) packed |= TRUE_COLOR_BG_FLAG\n if (cell.selectable) packed = (packed | SELECTABLE_FLAG) >>> 0\n this.cells[idx] = packed\n }\n\n /**\n * Fill a region with a cell.\n *\n * Optimized: packs cell metadata once and assigns directly to arrays,\n * avoiding O(width*height) intermediate object allocations from setCell().\n */\n fill(x: number, y: number, width: number, height: number, cell: CellPatch): void {\n const endX = Math.min(x + width, this.width)\n const endY = Math.min(y + height, this.height)\n const startX = Math.max(0, x)\n const startY = Math.max(0, y)\n\n if (startX >= endX || startY >= endY) return\n\n // Write trap for SILVERY_STRICT mismatch diagnosis (fill path)\n const trap = (globalThis as any).__silvery_write_trap\n if (trap && trap.x >= startX && trap.x < endX && trap.y >= startY && trap.y < endY) {\n const stack = new Error().stack?.split(\"\\n\").slice(1, 6).join(\"\\n\") ?? \"\"\n const ch = cell.char ?? \" \"\n trap.log.push(\n ` FILL char=\"${ch}\" fg=${cell.fg ?? \"null\"} bg=${cell.bg ?? \"null\"} rect=(${x},${y},${width},${height})\\n${stack}`,\n )\n }\n\n // Resolve cell properties once (instead of per-cell in setCell)\n const char = cell.char ?? \" \"\n const fg = cell.fg ?? null\n const bg = cell.bg ?? null\n const underlineColor = cell.underlineColor ?? null\n const attrs = cell.attrs ?? {}\n const wide = cell.wide ?? false\n const continuation = cell.continuation ?? false\n\n // Pack metadata once for the entire fill region\n const fullCell: Cell = {\n char,\n fg,\n bg,\n underlineColor,\n attrs,\n wide,\n continuation,\n }\n let packed = packCell(fullCell)\n if (cell.selectable) packed = (packed | SELECTABLE_FLAG) >>> 0\n\n // Determine true color values once\n const hasTrueColorFg = isTrueColor(fg)\n const hasTrueColorBg = isTrueColor(bg)\n const trueColorFg = hasTrueColorFg ? (fg as { r: number; g: number; b: number }) : null\n const trueColorBg = hasTrueColorBg ? (bg as { r: number; g: number; b: number }) : null\n const hasUnderlineColor = underlineColor !== null\n const hyperlink = cell.hyperlink\n const hasHyperlink = hyperlink !== undefined && hyperlink !== \"\"\n\n // Mark affected rows dirty + update bounding box\n for (let cy = startY; cy < endY; cy++) {\n this._dirtyRows[cy] = 1\n }\n if (startY < endY) {\n if (this._minDirtyRow === -1 || startY < this._minDirtyRow) this._minDirtyRow = startY\n if (endY - 1 > this._maxDirtyRow) this._maxDirtyRow = endY - 1\n }\n\n // Determine which Map operations are actually needed.\n // Skip delete() calls when the map is already empty (common case: no true colors).\n const needFgDelete = !hasTrueColorFg && this.fgColors.size > 0\n const needBgDelete = !hasTrueColorBg && this.bgColors.size > 0\n const needUlDelete = !hasUnderlineColor && this.underlineColors.size > 0\n const needHlDelete = !hasHyperlink && this.hyperlinks.size > 0\n\n for (let cy = startY; cy < endY; cy++) {\n const rowBase = cy * this.width\n for (let cx = startX; cx < endX; cx++) {\n const idx = rowBase + cx\n\n // Direct array assignment (no setCell overhead)\n this.cells[idx] = packed\n this.chars[idx] = char\n\n // Handle true color maps — skip delete when map is empty\n if (hasTrueColorFg) {\n this.fgColors.set(idx, trueColorFg!)\n } else if (needFgDelete) {\n this.fgColors.delete(idx)\n }\n\n if (hasTrueColorBg) {\n this.bgColors.set(idx, trueColorBg!)\n } else if (needBgDelete) {\n this.bgColors.delete(idx)\n }\n\n if (hasUnderlineColor) {\n this.underlineColors.set(idx, underlineColor)\n } else if (needUlDelete) {\n this.underlineColors.delete(idx)\n }\n\n if (hasHyperlink) {\n this.hyperlinks.set(idx, hyperlink!)\n } else if (needHlDelete) {\n this.hyperlinks.delete(idx)\n }\n }\n }\n }\n\n /**\n * Restyle a rectangular region — update fg, bg, and attrs on existing cells\n * without changing character content (char, wide, continuation, hyperlink).\n *\n * This is the style-only fast path: when only visual props changed (color,\n * bold, dim, inverse, etc.) but text content and layout are identical, we\n * can skip text collection/formatting and just update the style metadata.\n *\n * @param x Left column (inclusive)\n * @param y Top row (inclusive)\n * @param width Region width\n * @param height Region height\n * @param style New style to apply (fg, bg, attrs, underlineColor)\n */\n restyleRegion(x: number, y: number, width: number, height: number, style: Style): void {\n const endX = Math.min(x + width, this.width)\n const endY = Math.min(y + height, this.height)\n const startX = Math.max(0, x)\n const startY = Math.max(0, y)\n\n if (startX >= endX || startY >= endY) return\n\n // Pre-compute style-related packed bits\n const fgIndex = colorToIndex(style.fg) & 0xff\n const bgIndex = (colorToIndex(style.bg) & 0xff) << 8\n const attrBits = attrsToNumber(style.attrs)\n const hasTrueColorFg = isTrueColor(style.fg)\n const hasTrueColorBg = isTrueColor(style.bg)\n const trueColorFgFlag = hasTrueColorFg ? TRUE_COLOR_FG_FLAG : 0\n const trueColorBgFlag = hasTrueColorBg ? TRUE_COLOR_BG_FLAG : 0\n const trueColorFg = hasTrueColorFg ? (style.fg as { r: number; g: number; b: number }) : null\n const trueColorBg = hasTrueColorBg ? (style.bg as { r: number; g: number; b: number }) : null\n const underlineColor = style.underlineColor ?? null\n const hasUnderlineColor = underlineColor !== null\n\n // Style bits to apply (everything except char position flags)\n const styleBits = fgIndex | bgIndex | attrBits | trueColorFgFlag | trueColorBgFlag\n\n // Mask to preserve: wide, continuation, selectable\n // Clear: fg index (0-7), bg index (8-15), attrs (16-26), true color flags (29-30)\n const PRESERVE_MASK = WIDE_FLAG | CONTINUATION_FLAG | SELECTABLE_FLAG\n const needFgDelete = !hasTrueColorFg && this.fgColors.size > 0\n const needBgDelete = !hasTrueColorBg && this.bgColors.size > 0\n const needUlDelete = !hasUnderlineColor && this.underlineColors.size > 0\n\n // Mark affected rows dirty\n for (let cy = startY; cy < endY; cy++) {\n this._dirtyRows[cy] = 1\n }\n if (this._minDirtyRow === -1 || startY < this._minDirtyRow) this._minDirtyRow = startY\n if (endY - 1 > this._maxDirtyRow) this._maxDirtyRow = endY - 1\n\n for (let cy = startY; cy < endY; cy++) {\n const rowBase = cy * this.width\n for (let cx = startX; cx < endX; cx++) {\n const idx = rowBase + cx\n\n // Preserve char position flags, replace style bits\n const oldPacked = this.cells[idx]!\n this.cells[idx] = ((oldPacked & PRESERVE_MASK) >>> 0) | styleBits\n\n // Handle true color maps\n if (hasTrueColorFg) {\n this.fgColors.set(idx, trueColorFg!)\n } else if (needFgDelete) {\n this.fgColors.delete(idx)\n }\n\n if (hasTrueColorBg) {\n this.bgColors.set(idx, trueColorBg!)\n } else if (needBgDelete) {\n this.bgColors.delete(idx)\n }\n\n if (hasUnderlineColor) {\n this.underlineColors.set(idx, underlineColor)\n } else if (needUlDelete) {\n this.underlineColors.delete(idx)\n }\n }\n }\n }\n\n /**\n * Fill only the background color of a region — update bg on existing cells\n * without changing character content, foreground, or attributes.\n *\n * Unlike fill() which writes space characters, this preserves existing chars.\n * Used by the style-only fast path: when a Box's backgroundColor changes but\n * children are unchanged, fillBg() paints the new bg without destroying child\n * chars. Clean children can then be skipped (their chars are correct from the\n * clone, their bg was updated by fillBg).\n *\n * @param x Left column (inclusive)\n * @param y Top row (inclusive)\n * @param width Region width\n * @param height Region height\n * @param bg New background color\n */\n fillBg(x: number, y: number, width: number, height: number, bg: Color): void {\n const endX = Math.min(x + width, this.width)\n const endY = Math.min(y + height, this.height)\n const startX = Math.max(0, x)\n const startY = Math.max(0, y)\n\n if (startX >= endX || startY >= endY) return\n\n const bgIndex = (colorToIndex(bg) & 0xff) << 8\n const hasTrueColorBg = isTrueColor(bg)\n const trueColorBg = hasTrueColorBg ? (bg as { r: number; g: number; b: number }) : null\n const needBgDelete = !hasTrueColorBg && this.bgColors.size > 0\n\n // Mask to clear: bg index (bits 8-15) and TRUE_COLOR_BG_FLAG (bit 30)\n const BG_CLEAR_MASK = ~((0xff << 8) | TRUE_COLOR_BG_FLAG)\n const trueColorBgFlag = hasTrueColorBg ? TRUE_COLOR_BG_FLAG : 0\n\n // Mark affected rows dirty\n for (let cy = startY; cy < endY; cy++) {\n this._dirtyRows[cy] = 1\n }\n if (startY < endY) {\n if (this._minDirtyRow === -1 || startY < this._minDirtyRow) this._minDirtyRow = startY\n if (endY - 1 > this._maxDirtyRow) this._maxDirtyRow = endY - 1\n }\n\n for (let cy = startY; cy < endY; cy++) {\n const rowBase = cy * this.width\n for (let cx = startX; cx < endX; cx++) {\n const idx = rowBase + cx\n\n // Clear old bg bits, set new bg bits (preserve everything else)\n const oldPacked = this.cells[idx]!\n this.cells[idx] = ((oldPacked & BG_CLEAR_MASK) >>> 0) | bgIndex | trueColorBgFlag\n\n // Handle true color bg map\n if (hasTrueColorBg) {\n this.bgColors.set(idx, trueColorBg!)\n } else if (needBgDelete) {\n this.bgColors.delete(idx)\n }\n }\n }\n }\n\n /**\n * OR-combine SGR attribute bits into every cell in a rectangular region —\n * WITHOUT modifying glyphs, fg, bg, wide flags, selectable flag, or any\n * other per-cell state.\n *\n * This is the transparent-overlay primitive. It lets a Box (or any caller)\n * layer an underline / strikethrough / bold / etc. onto existing content\n * without overwriting what's underneath.\n *\n * Semantics:\n * - `attrs.underlineStyle` (or `attrs.underline: true` via attrsToNumber)\n * REPLACES any existing underline style on each cell — 3-bit field, one\n * value wins. This matches CSS `text-decoration` overlay semantics where\n * a decoration container sets the decoration.\n * - All other attr bits (bold/dim/italic/blink/inverse/hidden/strikethrough)\n * OR-in — existing attrs are preserved, new attrs add to them.\n * - `underlineColor` is applied only when explicitly provided; otherwise\n * each cell keeps its existing underline color.\n *\n * Use cases:\n * - Overscroll indicator: `<Box underline=\"single\" position=\"absolute\" />`\n * overlays an underline on the last row without touching the text.\n * - Error squiggly across a paragraph: `<Box underline=\"curly\">`.\n * - Heading visual emphasis: `<Box underline=\"double\">`.\n *\n * @param x Left column (inclusive)\n * @param y Top row (inclusive)\n * @param width Region width\n * @param height Region height\n * @param attrs Attributes to merge onto every cell\n * @param underlineColor Optional underline color — when provided, replaces the\n * existing underlineColor on each cell. `null` clears it. `undefined`\n * leaves it untouched.\n */\n mergeAttrsInRect(\n x: number,\n y: number,\n width: number,\n height: number,\n attrs: CellAttrs,\n underlineColor?: Color,\n ): void {\n const endX = Math.min(x + width, this.width)\n const endY = Math.min(y + height, this.height)\n const startX = Math.max(0, x)\n const startY = Math.max(0, y)\n\n if (startX >= endX || startY >= endY) return\n\n const attrBits = attrsToNumber(attrs)\n if (attrBits === 0 && underlineColor === undefined) return\n\n // Separate the underline-style bits (bits 24-26) from the OR-in bits\n // (bits 16-22 — bold, dim, italic, blink, inverse, hidden, strikethrough).\n // Underline REPLACES rather than OR-combines so callers can request a\n // specific style without accumulating with existing ones.\n const UL_MASK = UNDERLINE_STYLE_MASK\n const orInBits = attrBits & ~UL_MASK\n const newUlBits = attrBits & UL_MASK\n const setUnderline = newUlBits !== 0\n const setUnderlineColor = underlineColor !== undefined\n\n // Mark affected rows dirty\n for (let cy = startY; cy < endY; cy++) {\n this._dirtyRows[cy] = 1\n }\n if (this._minDirtyRow === -1 || startY < this._minDirtyRow) this._minDirtyRow = startY\n if (endY - 1 > this._maxDirtyRow) this._maxDirtyRow = endY - 1\n\n const hasTrueColorUl = setUnderlineColor && isTrueColor(underlineColor)\n const trueColorUl = hasTrueColorUl\n ? (underlineColor as { r: number; g: number; b: number })\n : null\n\n for (let cy = startY; cy < endY; cy++) {\n const rowBase = cy * this.width\n for (let cx = startX; cx < endX; cx++) {\n const idx = rowBase + cx\n const oldPacked = this.cells[idx]!\n\n let newPacked = (oldPacked | orInBits) >>> 0\n if (setUnderline) {\n // Clear old underline bits, set new ones\n newPacked = (((newPacked & ~UL_MASK) >>> 0) | newUlBits) >>> 0\n }\n this.cells[idx] = newPacked\n\n if (setUnderlineColor) {\n if (underlineColor === null) {\n this.underlineColors.delete(idx)\n } else if (hasTrueColorUl) {\n this.underlineColors.set(idx, trueColorUl!)\n } else {\n // 256-color / palette index\n this.underlineColors.set(idx, underlineColor!)\n }\n }\n }\n }\n }\n\n /**\n * Clear the buffer (fill with empty cells).\n */\n clear(): void {\n this.cells.fill(0)\n this.chars.fill(\" \")\n this.fgColors.clear()\n this.bgColors.clear()\n this.underlineColors.clear()\n this.hyperlinks.clear()\n this._dirtyRows.fill(1)\n this._minDirtyRow = 0\n this._maxDirtyRow = this.height - 1\n }\n\n /**\n * Copy a region from another buffer.\n */\n copyFrom(\n source: TerminalBuffer,\n srcX: number,\n srcY: number,\n destX: number,\n destY: number,\n width: number,\n height: number,\n ): void {\n const cell = createMutableCell()\n for (let dy = 0; dy < height; dy++) {\n const dstY = destY + dy\n if (dstY >= 0 && dstY < this.height) {\n this._dirtyRows[dstY] = 1\n if (this._minDirtyRow === -1 || dstY < this._minDirtyRow) this._minDirtyRow = dstY\n if (dstY > this._maxDirtyRow) this._maxDirtyRow = dstY\n }\n for (let dx = 0; dx < width; dx++) {\n const sx = srcX + dx\n const sy = srcY + dy\n const dX = destX + dx\n\n if (source.inBounds(sx, sy) && this.inBounds(dX, dstY)) {\n source.readCellInto(sx, sy, cell)\n this.setCell(dX, dstY, { ...cell, selectable: source.isCellSelectable(sx, sy) })\n }\n }\n }\n }\n\n /**\n * Shift content within a rectangular region vertically by `delta` rows.\n * Positive delta = shift content UP (scroll down), negative = shift DOWN (scroll up).\n * Exposed rows (at the bottom for positive delta, top for negative) are filled\n * with the given background cell.\n *\n * Uses Uint32Array.copyWithin for the packed cells (native memcpy) and\n * Array splice for the character array.\n */\n scrollRegion(\n x: number,\n y: number,\n regionWidth: number,\n regionHeight: number,\n delta: number,\n clearCell: CellPatch = {},\n ): void {\n if (delta === 0 || regionHeight <= 0 || regionWidth <= 0) return\n\n const startX = Math.max(0, x)\n const endX = Math.min(x + regionWidth, this.width)\n const startY = Math.max(0, y)\n const endY = Math.min(y + regionHeight, this.height)\n const clampedWidth = endX - startX\n const clampedHeight = endY - startY\n\n if (clampedWidth <= 0 || clampedHeight <= 0) return\n\n // Mark all rows in the scroll region dirty + update bounding box\n for (let r = startY; r < endY; r++) {\n this._dirtyRows[r] = 1\n }\n if (this._minDirtyRow === -1 || startY < this._minDirtyRow) this._minDirtyRow = startY\n if (endY - 1 > this._maxDirtyRow) this._maxDirtyRow = endY - 1\n\n if (Math.abs(delta) >= clampedHeight) {\n // Scroll amount exceeds region — just clear everything\n this.fill(startX, startY, clampedWidth, clampedHeight, {\n char: clearCell.char ?? \" \",\n bg: clearCell.bg ?? null,\n selectable: clearCell.selectable,\n })\n return\n }\n\n const absDelta = Math.abs(delta)\n const w = this.width\n\n if (delta > 0) {\n // Shift content UP: copy rows [startY + delta .. endY) to [startY .. endY - delta)\n for (let row = startY; row < endY - absDelta; row++) {\n const dstBase = row * w\n const srcBase = (row + absDelta) * w\n // Copy cells and chars for the region columns\n this.cells.copyWithin(dstBase + startX, srcBase + startX, srcBase + endX)\n for (let cx = startX; cx < endX; cx++) {\n this.chars[dstBase + cx] = this.chars[srcBase + cx]!\n // Move true color maps\n const srcIdx = srcBase + cx\n const dstIdx = dstBase + cx\n const fgc = this.fgColors.get(srcIdx)\n if (fgc) {\n this.fgColors.set(dstIdx, fgc)\n this.fgColors.delete(srcIdx)\n } else {\n this.fgColors.delete(dstIdx)\n }\n const bgc = this.bgColors.get(srcIdx)\n if (bgc) {\n this.bgColors.set(dstIdx, bgc)\n this.bgColors.delete(srcIdx)\n } else {\n this.bgColors.delete(dstIdx)\n }\n const ulc = this.underlineColors.get(srcIdx)\n if (ulc) {\n this.underlineColors.set(dstIdx, ulc)\n this.underlineColors.delete(srcIdx)\n } else {\n this.underlineColors.delete(dstIdx)\n }\n const hl = this.hyperlinks.get(srcIdx)\n if (hl) {\n this.hyperlinks.set(dstIdx, hl)\n this.hyperlinks.delete(srcIdx)\n } else {\n this.hyperlinks.delete(dstIdx)\n }\n }\n }\n // Clear exposed rows at bottom\n this.fill(startX, endY - absDelta, clampedWidth, absDelta, {\n char: clearCell.char ?? \" \",\n bg: clearCell.bg ?? null,\n selectable: clearCell.selectable,\n })\n } else {\n // Shift content DOWN: copy rows [startY .. endY - absDelta) to [startY + absDelta .. endY)\n for (let row = endY - 1; row >= startY + absDelta; row--) {\n const dstBase = row * w\n const srcBase = (row - absDelta) * w\n this.cells.copyWithin(dstBase + startX, srcBase + startX, srcBase + endX)\n for (let cx = startX; cx < endX; cx++) {\n this.chars[dstBase + cx] = this.chars[srcBase + cx]!\n const srcIdx = srcBase + cx\n const dstIdx = dstBase + cx\n const fgc = this.fgColors.get(srcIdx)\n if (fgc) {\n this.fgColors.set(dstIdx, fgc)\n this.fgColors.delete(srcIdx)\n } else {\n this.fgColors.delete(dstIdx)\n }\n const bgc = this.bgColors.get(srcIdx)\n if (bgc) {\n this.bgColors.set(dstIdx, bgc)\n this.bgColors.delete(srcIdx)\n } else {\n this.bgColors.delete(dstIdx)\n }\n const ulc = this.underlineColors.get(srcIdx)\n if (ulc) {\n this.underlineColors.set(dstIdx, ulc)\n this.underlineColors.delete(srcIdx)\n } else {\n this.underlineColors.delete(dstIdx)\n }\n const hl = this.hyperlinks.get(srcIdx)\n if (hl) {\n this.hyperlinks.set(dstIdx, hl)\n this.hyperlinks.delete(srcIdx)\n } else {\n this.hyperlinks.delete(dstIdx)\n }\n }\n }\n // Clear exposed rows at top\n this.fill(startX, startY, clampedWidth, absDelta, {\n char: clearCell.char ?? \" \",\n bg: clearCell.bg ?? null,\n selectable: clearCell.selectable,\n })\n }\n }\n\n /**\n * Clone this buffer.\n */\n clone(): TerminalBuffer {\n const copy = new TerminalBuffer(this.width, this.height)\n copy.cells.set(this.cells)\n copy.chars = [...this.chars]\n copy.fgColors = new Map(this.fgColors)\n copy.bgColors = new Map(this.bgColors)\n copy.underlineColors = new Map(this.underlineColors)\n copy.hyperlinks = new Map(this.hyperlinks)\n // Clone starts with all rows CLEAN. The render phase will mark only\n // the rows it modifies as dirty. diffBuffers() then skips clean rows,\n // which are guaranteed identical to the prev buffer (since this is a clone).\n copy._dirtyRows.fill(0)\n copy._minDirtyRow = -1\n copy._maxDirtyRow = -1\n // Deep-copy row metadata\n copy._rowMetadata = this._rowMetadata.map((m) => ({ ...m }))\n // Phase 2 Step 5 of paint-clear-invariant L5: outline snapshots no\n // longer live on `TerminalBuffer`. Cross-frame outline state is now\n // owned by `createAg` via `RenderPostState` (see render-post-state.ts).\n // The buffer is now a pure cell store with no across-frame coupling\n // beyond the cells themselves.\n return copy\n }\n\n /**\n * Check if a row has been modified since the last resetDirtyRows() call.\n * Used by diffBuffers() to skip unchanged rows.\n */\n isRowDirty(y: number): boolean {\n if (y < 0 || y >= this.height) return false\n return this._dirtyRows[y] !== 0\n }\n\n /** First dirty row (inclusive), or -1 if no rows are dirty. */\n get minDirtyRow(): number {\n return this._minDirtyRow\n }\n\n /** Last dirty row (inclusive), or -1 if no rows are dirty. */\n get maxDirtyRow(): number {\n return this._maxDirtyRow\n }\n\n /**\n * Reset all dirty row flags to clean.\n * Call after diffing to prepare for the next frame's modifications.\n */\n resetDirtyRows(): void {\n this._dirtyRows.fill(0)\n this._minDirtyRow = -1\n this._maxDirtyRow = -1\n }\n\n /**\n * Mark all rows as dirty.\n * Used when the buffer's dirty rows may not cover all changes relative\n * to a different prev buffer (e.g., after multiple doRender calls where\n * the runtime's prevBuffer skipped intermediate buffers).\n */\n markAllRowsDirty(): void {\n this._dirtyRows.fill(1)\n this._minDirtyRow = 0\n this._maxDirtyRow = this.height - 1\n }\n\n /**\n * Mark a single row as dirty (no-op if out of bounds).\n *\n * Used by the managed-caret overlay-clear path: when a composited caret is\n * suppressed or moves off a row whose CONTENT is otherwise static (and thus\n * clean in this incremental buffer), the prior caret's row must be made dirty\n * so `diffBuffers` re-scans it and clears the stale `inverse` overlay cell.\n * Without this, the dirty-row gate (`diffBuffers`: `if (!next.isRowDirty(y))\n * continue`) skips the row and the prior frame's reverse-video block strands\n * on screen — the @km/code/v0.2/19702 cursor-above-composer signature.\n */\n markRowDirty(y: number): void {\n if (y < 0 || y >= this.height) return\n this._dirtyRows[y] = 1\n if (this._minDirtyRow === -1 || y < this._minDirtyRow) this._minDirtyRow = y\n if (y > this._maxDirtyRow) this._maxDirtyRow = y\n }\n\n /**\n * Check if two cells at given positions are equal.\n * Used for diffing.\n */\n cellEquals(x: number, y: number, other: TerminalBuffer): boolean {\n if (!this.inBounds(x, y) || !other.inBounds(x, y)) {\n return false\n }\n\n const idx = this.index(x, y)\n const otherIdx = other.index(x, y)\n\n // Quick check: packed metadata must match (mask out SELECTABLE_FLAG — it's\n // selection metadata, not visual output, and must not trigger output diffs)\n const mask = ~SELECTABLE_FLAG\n if ((this.cells[idx]! & mask) !== (other.cells[otherIdx]! & mask)) {\n return false\n }\n\n // Character must match\n if (this.chars[idx] !== other.chars[otherIdx]) {\n return false\n }\n\n // If true color flags are set, check the color values\n const packed = this.cells[idx]!\n if (unpackTrueColorFg(packed)) {\n const a = this.fgColors.get(idx)\n const b = other.fgColors.get(otherIdx)\n if (!colorEquals(a, b)) return false\n }\n if (unpackTrueColorBg(packed!)) {\n const a = this.bgColors.get(idx)\n const b = other.bgColors.get(otherIdx)\n if (!colorEquals(a, b)) return false\n }\n\n // Check underline colors\n const ulA = this.underlineColors.get(idx) ?? null\n const ulB = other.underlineColors.get(otherIdx) ?? null\n if (!colorEquals(ulA, ulB)) return false\n\n // Check hyperlinks\n const hlA = this.hyperlinks.get(idx)\n const hlB = other.hyperlinks.get(otherIdx)\n if (hlA !== hlB) return false\n\n return true\n }\n\n /**\n * Fast check: are all packed metadata values identical for a row?\n * This is a bulk pre-check before per-cell comparison. If metadata differs,\n * we still need per-cell diffing. If metadata matches, we only need to\n * check chars, true color maps, underline colors, and hyperlinks.\n * Returns true if all packed 32-bit values in the row are identical.\n */\n rowMetadataEquals(y: number, other: TerminalBuffer): boolean {\n if (y < 0 || y >= this.height || y >= other.height) return false\n const start = y * this.width\n const otherStart = y * other.width\n const w = Math.min(this.width, other.width)\n // Mask out SELECTABLE_FLAG — it's selection metadata, not visual output\n const mask = ~SELECTABLE_FLAG\n for (let i = 0; i < w; i++) {\n if ((this.cells[start + i]! & mask) !== (other.cells[otherStart + i]! & mask)) return false\n }\n return true\n }\n\n /**\n * Fast check: are all characters identical for a row?\n * Companion to rowMetadataEquals for a two-phase row comparison.\n */\n rowCharsEquals(y: number, other: TerminalBuffer): boolean {\n if (y < 0 || y >= this.height || y >= other.height) return false\n const start = y * this.width\n const otherStart = y * other.width\n const w = Math.min(this.width, other.width)\n for (let i = 0; i < w; i++) {\n if (this.chars[start + i] !== other.chars[otherStart + i]) return false\n }\n return true\n }\n\n /**\n * Check Map-based extras for a row: true color fg/bg, underline colors, hyperlinks.\n * Must be called AFTER rowMetadataEquals confirms packed metadata matches.\n * Only checks cells that have true color flags set (the Maps are only populated\n * for those cells). Also checks underline colors and hyperlinks for all cells.\n */\n rowExtrasEquals(y: number, other: TerminalBuffer): boolean {\n if (y < 0 || y >= this.height || y >= other.height) return false\n const start = y * this.width\n const w = Math.min(this.width, other.width)\n const otherStart = y * other.width\n for (let i = 0; i < w; i++) {\n const idx = start + i\n const otherIdx = otherStart + i\n const packed = this.cells[idx]!\n\n // Check true color fg values\n if ((packed & TRUE_COLOR_FG_FLAG) !== 0) {\n const a = this.fgColors.get(idx)\n const b = other.fgColors.get(otherIdx)\n if (!colorEquals(a, b)) return false\n }\n\n // Check true color bg values\n if ((packed & TRUE_COLOR_BG_FLAG) !== 0) {\n const a = this.bgColors.get(idx)\n const b = other.bgColors.get(otherIdx)\n if (!colorEquals(a, b)) return false\n }\n\n // Check underline colors\n const ulA = this.underlineColors.get(idx) ?? null\n const ulB = other.underlineColors.get(otherIdx) ?? null\n if (!colorEquals(ulA, ulB)) return false\n\n // Check hyperlinks\n const hlA = this.hyperlinks.get(idx)\n const hlB = other.hyperlinks.get(otherIdx)\n if (hlA !== hlB) return false\n }\n return true\n }\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Compare two colors for equality.\n */\nexport function colorEquals(a: Color | undefined, b: Color | undefined): boolean {\n if (a === b) return true\n if (a === null || a === undefined) return b === null || b === undefined\n if (b === null || b === undefined) return false\n if (typeof a === \"number\") return a === b\n if (typeof b === \"number\") return false\n return a.r === b.r && a.g === b.g && a.b === b.b\n}\n\n/**\n * Compare two cells for equality.\n */\nexport function cellEquals(a: Cell, b: Cell): boolean {\n return (\n a.char === b.char &&\n colorEquals(a.fg, b.fg) &&\n colorEquals(a.bg, b.bg) &&\n colorEquals(a.underlineColor, b.underlineColor) &&\n a.wide === b.wide &&\n a.continuation === b.continuation &&\n attrsEquals(a.attrs, b.attrs) &&\n (a.hyperlink ?? undefined) === (b.hyperlink ?? undefined)\n )\n}\n\n/**\n * Compare two CellAttrs for equality.\n */\nexport function attrsEquals(a: CellAttrs, b: CellAttrs): boolean {\n return (\n Boolean(a.bold) === Boolean(b.bold) &&\n Boolean(a.dim) === Boolean(b.dim) &&\n Boolean(a.italic) === Boolean(b.italic) &&\n Boolean(a.underline) === Boolean(b.underline) &&\n (a.underlineStyle ?? false) === (b.underlineStyle ?? false) &&\n Boolean(a.overline) === Boolean(b.overline) &&\n Boolean(a.blink) === Boolean(b.blink) &&\n Boolean(a.inverse) === Boolean(b.inverse) &&\n Boolean(a.hidden) === Boolean(b.hidden) &&\n Boolean(a.strikethrough) === Boolean(b.strikethrough)\n )\n}\n\n/**\n * Compare two styles for equality.\n */\nexport function styleEquals(a: Style | null, b: Style | null): boolean {\n if (a === b) return true\n if (!a || !b) return false\n return (\n colorEquals(a.fg, b.fg) &&\n colorEquals(a.bg, b.bg) &&\n colorEquals(a.underlineColor, b.underlineColor) &&\n attrsEquals(a.attrs, b.attrs) &&\n (a.hyperlink ?? undefined) === (b.hyperlink ?? undefined)\n )\n}\n\n/**\n * Create a mutable Cell object for use with readCellInto().\n * The returned object is reusable -- readCellInto() overwrites all fields.\n */\nexport function createMutableCell(): Cell {\n return {\n char: \" \",\n fg: null,\n bg: null,\n underlineColor: null,\n attrs: {},\n wide: false,\n continuation: false,\n hyperlink: undefined,\n }\n}\n\n/**\n * Create a buffer initialized with a specific character.\n */\nexport function createBuffer(width: number, height: number, char = \" \"): TerminalBuffer {\n const buffer = new TerminalBuffer(width, height)\n if (char !== \" \") {\n buffer.fill(0, 0, width, height, { char })\n }\n return buffer\n}\n\n// ============================================================================\n// Buffer Conversion Utilities\n// ============================================================================\n\n/**\n * Convert a terminal buffer to plain text (no ANSI codes).\n * Useful for snapshot testing and text-based assertions.\n *\n * @param buffer The buffer to convert\n * @param options.trimTrailingWhitespace Remove trailing spaces from each line (default: true)\n * @param options.trimEmptyLines Remove trailing empty lines (default: true)\n * @returns Plain text representation of the buffer\n */\nexport function bufferToText(\n buffer: TerminalBuffer,\n options: {\n trimTrailingWhitespace?: boolean\n trimEmptyLines?: boolean\n } = {},\n): string {\n const { trimTrailingWhitespace = true, trimEmptyLines = true } = options\n\n const lines: string[] = []\n\n for (let y = 0; y < buffer.height; y++) {\n let line = \"\"\n // Track string offset for each column (needed because continuation cells\n // are skipped, making string length != column count for wide chars)\n let strOffset = 0\n let contentEdgeStrOffset = 0\n const contentEdge = trimTrailingWhitespace ? getContentEdge(buffer, y) : 0\n for (let x = 0; x < buffer.width; x++) {\n // Use zero-allocation accessors instead of getCell()\n if (buffer.isCellContinuation(x, y)) continue\n line += buffer.getCellChar(x, y)\n strOffset++\n // Track the string offset corresponding to the content edge column\n if (x < contentEdge) {\n contentEdgeStrOffset = strOffset\n }\n }\n if (trimTrailingWhitespace) {\n // Smart trim: use content edge to preserve styled trailing spaces\n // while removing unstyled buffer padding.\n const trimmed = line.trimEnd()\n line =\n trimmed.length >= contentEdgeStrOffset ? trimmed : line.substring(0, contentEdgeStrOffset)\n }\n lines.push(line)\n }\n\n let result = lines.join(\"\\n\")\n if (trimEmptyLines) {\n // Remove trailing empty lines without stripping spaces from last content line\n while (lines.length > 0 && lines[lines.length - 1]!.length === 0) {\n lines.pop()\n }\n result = lines.join(\"\\n\")\n }\n return result\n}\n\n/**\n * Find the rightmost column with non-default cell content on a row.\n * A default cell has packed metadata === 0 (no fg, bg, attrs) AND char === ' '.\n * Returns the column count (1-indexed), so the content edge for trimming.\n */\nfunction getContentEdge(buffer: TerminalBuffer, y: number): number {\n // Mask out structural flags (wide, continuation) that don't indicate actual content.\n // Also mask SELECTABLE_FLAG (bit 31) — it's selection metadata, not styling.\n // True-color flags DO indicate styled content (they mean fg/bg is set in Maps).\n const FLAG_MASK = ~(WIDE_FLAG | CONTINUATION_FLAG | SELECTABLE_FLAG)\n for (let x = buffer.width - 1; x >= 0; x--) {\n // Skip continuation cells (trailing half of wide chars) — the main cell covers them\n if (buffer.isCellContinuation(x, y)) continue\n // Check if cell has any actual styling (fg, bg, text attrs) after masking structural flags\n const attrs = buffer.getCellAttrs(x, y) & FLAG_MASK\n if (attrs !== 0) return x + 1\n // Check if cell has a non-space character\n if (buffer.getCellChar(x, y) !== \" \") return x + 1\n }\n return 0\n}\n\n/**\n * Convert a terminal buffer to styled ANSI text.\n * Unlike bufferToAnsi, this doesn't include cursor control sequences,\n * making it suitable for displaying in terminals or saving to files.\n *\n * @param buffer The buffer to convert\n * @param options.trimTrailingWhitespace Remove trailing spaces from each line (default: true)\n * @param options.trimEmptyLines Remove trailing empty lines (default: true)\n * @returns ANSI-styled text (no cursor control)\n */\nexport function bufferToStyledText(\n buffer: TerminalBuffer,\n options: {\n trimTrailingWhitespace?: boolean\n trimEmptyLines?: boolean\n colorLevel?: ColorLevel\n } = {},\n): string {\n const { trimTrailingWhitespace = true, trimEmptyLines = true, colorLevel = \"truecolor\" } = options\n\n const lines: string[] = []\n let currentStyle: Style | null = null\n let currentHyperlink: string | undefined\n\n for (let y = 0; y < buffer.height; y++) {\n let line = \"\"\n\n for (let x = 0; x < buffer.width; x++) {\n // getCell allocates a fresh object each call, which is fine here since\n // bufferToStyledText is a utility function, not a hot render path.\n const cell = buffer.getCell(x, y)\n // Skip continuation cells (part of wide character)\n if (cell.continuation) continue\n\n // Check if hyperlink changed (OSC 8 is separate from SGR)\n const cellHyperlink = cell.hyperlink\n if (cellHyperlink !== currentHyperlink) {\n if (currentHyperlink) {\n // Close previous hyperlink using the same format as the open\n line += emitHyperlinkClose(currentHyperlink)\n }\n if (cellHyperlink) {\n line += emitHyperlinkOpen(cellHyperlink)\n }\n currentHyperlink = cellHyperlink\n }\n\n // Check if style changed — emit minimal transition (chalk-compatible)\n const cellStyle: Style = {\n fg: cell.fg,\n bg: cell.bg,\n underlineColor: cell.underlineColor,\n attrs: cell.attrs,\n }\n if (!styleEquals(currentStyle, cellStyle)) {\n line += styleTransitionCodes(currentStyle, cellStyle, colorLevel)\n currentStyle = cellStyle\n }\n\n line += cell.char\n }\n\n // Close any open hyperlink at end of line\n if (currentHyperlink) {\n line += emitHyperlinkClose(currentHyperlink)\n currentHyperlink = undefined\n }\n\n // Reset style at end of line using per-attribute resets (chalk-compatible)\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n line += styleResetCodes(currentStyle)\n currentStyle = null\n }\n\n if (trimTrailingWhitespace) {\n // Need to be careful not to strip ANSI codes\n // Only trim actual whitespace at the end\n line = trimTrailingWhitespacePreservingAnsi(line)\n }\n lines.push(line)\n }\n\n // Final per-attribute reset (chalk-compatible)\n let result = lines.join(\"\\n\")\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n result += styleResetCodes(currentStyle)\n }\n\n if (trimEmptyLines) {\n // Remove empty lines at the end (but preserve ANSI resets)\n result = result.replace(/\\n+$/, \"\")\n }\n\n return result\n}\n\n// ============================================================================\n// Hyperlink Format Helpers\n// ============================================================================\n\n/**\n * Decode hyperlink format metadata from URL prefix.\n * parseAnsiText encodes the original OSC format (C1 vs ESC, BEL vs ST)\n * as a prefix: \\x01<tag>\\x02<url>\n *\n * Tags:\n * c1b = C1 OSC (\\x9d) + BEL (\\x07) terminator\n * c1s = C1 OSC (\\x9d) + ST (\\x1b\\\\) terminator\n * e7b = ESC OSC (\\x1b]) + BEL (\\x07) terminator\n * (no prefix) = ESC OSC + ST (default)\n */\nfunction decodeHyperlinkFormat(encoded: string): {\n url: string\n oscIntro: string\n oscClose: string\n closeIntro: string\n closeTerminator: string\n} {\n if (encoded.charCodeAt(0) === 1) {\n const sepIdx = encoded.indexOf(\"\\x02\")\n if (sepIdx > 0) {\n const tag = encoded.slice(1, sepIdx)\n const url = encoded.slice(sepIdx + 1)\n if (tag === \"c1b\") {\n return {\n url,\n oscIntro: \"\\x9d\",\n oscClose: \"\\x9d\",\n closeIntro: \"\\x9d\",\n closeTerminator: \"\\x07\",\n }\n }\n if (tag === \"c1s\") {\n return {\n url,\n oscIntro: \"\\x9d\",\n oscClose: \"\\x9d\",\n closeIntro: \"\\x9d\",\n closeTerminator: \"\\x1b\\\\\",\n }\n }\n if (tag === \"e7b\") {\n return {\n url,\n oscIntro: \"\\x1b]\",\n oscClose: \"\\x1b]\",\n closeIntro: \"\\x1b]\",\n closeTerminator: \"\\x07\",\n }\n }\n }\n }\n // Default: ESC OSC + ST\n return {\n url: encoded,\n oscIntro: \"\\x1b]\",\n oscClose: \"\\x1b]\",\n closeIntro: \"\\x1b]\",\n closeTerminator: \"\\x1b\\\\\",\n }\n}\n\n/** Emit OSC 8 hyperlink open sequence, respecting format metadata in URL. */\nfunction emitHyperlinkOpen(encoded: string): string {\n const fmt = decodeHyperlinkFormat(encoded)\n return `${fmt.oscIntro}8;;${fmt.url}${fmt.closeTerminator}`\n}\n\n/** Emit OSC 8 hyperlink close sequence, respecting format metadata in URL. */\nfunction emitHyperlinkClose(encoded: string): string {\n const fmt = decodeHyperlinkFormat(encoded)\n return `${fmt.closeIntro}8;;${fmt.closeTerminator}`\n}\n\n// ============================================================================\n// xterm-256 Color Palette\n// ============================================================================\n\n/** Standard xterm-256 color palette as hex strings. */\nconst XTERM_256_PALETTE: string[] = (() => {\n const palette: string[] = new Array(256)\n\n // Colors 0-7: standard colors\n const standard = [\n \"#000000\",\n \"#cd0000\",\n \"#00cd00\",\n \"#cdcd00\",\n \"#0000ee\",\n \"#cd00cd\",\n \"#00cdcd\",\n \"#e5e5e5\",\n ]\n // Colors 8-15: bright colors\n const bright = [\n \"#7f7f7f\",\n \"#ff0000\",\n \"#00ff00\",\n \"#ffff00\",\n \"#5c5cff\",\n \"#ff00ff\",\n \"#00ffff\",\n \"#ffffff\",\n ]\n for (let i = 0; i < 8; i++) {\n palette[i] = standard[i]!\n palette[i + 8] = bright[i]!\n }\n\n // Colors 16-231: 6x6x6 RGB cube\n const cubeValues = [0, 95, 135, 175, 215, 255]\n for (let i = 0; i < 216; i++) {\n const r = cubeValues[Math.floor(i / 36)]!\n const g = cubeValues[Math.floor((i % 36) / 6)]!\n const b = cubeValues[i % 6]!\n palette[16 + i] =\n \"#\" +\n r.toString(16).padStart(2, \"0\") +\n g.toString(16).padStart(2, \"0\") +\n b.toString(16).padStart(2, \"0\")\n }\n\n // Colors 232-255: grayscale ramp\n for (let i = 0; i < 24; i++) {\n const v = 8 + i * 10\n const hex = v.toString(16).padStart(2, \"0\")\n palette[232 + i] = \"#\" + hex + hex + hex\n }\n\n return palette\n})()\n\n/**\n * Convert a Color value to a CSS color string.\n * Returns null for default/inherit colors.\n */\nfunction colorToCSS(color: Color): string | null {\n if (color === null) return null\n if (typeof color === \"number\") {\n return XTERM_256_PALETTE[color] ?? null\n }\n // DEFAULT_BG sentinel → no CSS color (use inherited/default)\n if (color.r === -1) return null\n return `rgb(${color.r},${color.g},${color.b})`\n}\n\n// ============================================================================\n// Buffer to HTML Conversion\n// ============================================================================\n\n/**\n * Convert a terminal buffer to a full HTML document.\n * Suitable for rendering as a screenshot via headless browser.\n *\n * @param buffer The buffer to convert\n * @param options.fontFamily CSS font-family (default: 'JetBrains Mono, Menlo, monospace')\n * @param options.fontSize CSS font-size in px (default: 14)\n * @param options.theme Color scheme (default: 'dark')\n * @returns Complete HTML document string\n */\nexport function bufferToHTML(\n buffer: TerminalBuffer,\n options: {\n fontFamily?: string\n fontSize?: number\n theme?: \"dark\" | \"light\"\n } = {},\n): string {\n const { fontFamily = \"JetBrains Mono, Menlo, monospace\", fontSize = 14, theme = \"dark\" } = options\n\n const defaultFg = theme === \"dark\" ? \"#d4d4d4\" : \"#1e1e1e\"\n const defaultBg = theme === \"dark\" ? \"#1e1e1e\" : \"#ffffff\"\n\n const htmlLines: string[] = []\n\n for (let y = 0; y < buffer.height; y++) {\n let lineHTML = \"\"\n let currentStyle: Style | null = null\n let spanOpen = false\n let linkOpen = false\n let currentHyperlink: string | undefined\n\n for (let x = 0; x < buffer.width; x++) {\n const cell = buffer.getCell(x, y)\n if (cell.continuation) continue\n\n // Handle hyperlink transitions\n const cellHyperlink = cell.hyperlink\n if (cellHyperlink !== currentHyperlink) {\n if (linkOpen) {\n if (spanOpen) {\n lineHTML += \"</span>\"\n spanOpen = false\n }\n lineHTML += \"</a>\"\n linkOpen = false\n }\n if (cellHyperlink) {\n lineHTML += `<a href=\"${escapeHTML(cellHyperlink)}\">`\n linkOpen = true\n }\n currentHyperlink = cellHyperlink\n }\n\n const cellStyle: Style = {\n fg: cell.fg,\n bg: cell.bg,\n underlineColor: cell.underlineColor,\n attrs: cell.attrs,\n }\n\n if (!styleEquals(currentStyle, cellStyle)) {\n if (spanOpen) {\n lineHTML += \"</span>\"\n spanOpen = false\n }\n const css = styleToCSSProperties(cellStyle, defaultFg, defaultBg)\n if (css) {\n lineHTML += `<span style=\"${css}\">`\n spanOpen = true\n }\n currentStyle = cellStyle\n }\n\n lineHTML += escapeHTML(cell.char)\n }\n\n if (spanOpen) {\n lineHTML += \"</span>\"\n }\n if (linkOpen) {\n lineHTML += \"</a>\"\n currentHyperlink = undefined\n }\n\n htmlLines.push(`<div>${lineHTML}</div>`)\n }\n\n return `<!DOCTYPE html>\n<html>\n<head><meta charset=\"utf-8\"></head>\n<body style=\"margin:0;padding:0;background:${defaultBg};color:${defaultFg};font-family:${fontFamily};font-size:${fontSize}px;line-height:1.2;white-space:pre\">\n${htmlLines.join(\"\\n\")}\n</body>\n</html>`\n}\n\n/**\n * Convert a Style to CSS inline style properties.\n * Returns null if the style is entirely default.\n */\nfunction styleToCSSProperties(style: Style, defaultFg: string, defaultBg: string): string | null {\n const parts: string[] = []\n\n // Handle inverse: swap fg/bg\n let fgColor: string | null\n let bgColor: string | null\n if (style.attrs.inverse) {\n fgColor = colorToCSS(style.bg) ?? defaultBg\n bgColor = colorToCSS(style.fg) ?? defaultFg\n } else {\n fgColor = colorToCSS(style.fg)\n bgColor = colorToCSS(style.bg)\n }\n\n if (fgColor) parts.push(`color:${fgColor}`)\n if (bgColor) parts.push(`background:${bgColor}`)\n if (style.attrs.bold) parts.push(\"font-weight:bold\")\n if (style.attrs.dim) parts.push(\"opacity:0.5\")\n if (style.attrs.italic) parts.push(\"font-style:italic\")\n if (style.attrs.hidden) parts.push(\"visibility:hidden\")\n\n // Text decoration: underline and/or strikethrough\n const decorations: string[] = []\n const underlineStyle = style.attrs.underlineStyle\n if (typeof underlineStyle === \"string\") {\n const cssStyleMap: Record<string, string> = {\n single: \"solid\",\n double: \"double\",\n curly: \"wavy\",\n dotted: \"dotted\",\n dashed: \"dashed\",\n }\n decorations.push(\"underline\")\n const cssStyle = cssStyleMap[underlineStyle]\n if (cssStyle) parts.push(`text-decoration-style:${cssStyle}`)\n const ulColor = colorToCSS(style.underlineColor ?? null)\n if (ulColor) parts.push(`text-decoration-color:${ulColor}`)\n } else if (style.attrs.underline) {\n decorations.push(\"underline\")\n }\n if (style.attrs.strikethrough) decorations.push(\"line-through\")\n if (decorations.length > 0) parts.push(`text-decoration:${decorations.join(\" \")}`)\n\n return parts.length > 0 ? parts.join(\";\") : null\n}\n\n/** Escape special HTML characters. */\nfunction escapeHTML(str: string): string {\n if (str === \" \" || str.length === 0) return str\n return str\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n}\n\n/**\n * Check if any text attributes are active.\n */\nexport function hasActiveAttrs(attrs: CellAttrs): boolean {\n return !!(\n attrs.bold ||\n attrs.dim ||\n attrs.italic ||\n attrs.underline ||\n attrs.underlineStyle ||\n attrs.overline ||\n attrs.blink ||\n attrs.inverse ||\n attrs.hidden ||\n attrs.strikethrough\n )\n}\n\n/**\n * Convert style to ANSI escape sequence.\n *\n * Uses SGR 7 for inverse so terminals correctly swap fg/bg\n * (including default terminal colors that have no explicit ANSI code).\n */\n// =============================================================================\n// Color code helpers (imported from ansi/sgr-codes.ts)\n// =============================================================================\n\n/**\n * Convert style to ANSI escape sequence (chalk-compatible format).\n *\n * Emits only non-default attributes with no reset prefix. Called when there\n * is no previous style context (first cell), so the terminal is already in\n * reset state. Each attribute gets its own \\x1b[Xm sequence.\n */\nfunction serializedPaletteIndex(color: Exclude<Color, null>): number | undefined {\n if (typeof color === \"number\") return color\n return color.index !== undefined && color.index >= 0 && color.index <= 255\n ? color.index\n : undefined\n}\n\nfunction rgbForColor(color: Exclude<Color, null>): { r: number; g: number; b: number } {\n return typeof color === \"number\" ? ansi256ToRgb(color) : color\n}\n\nfunction foregroundCode(color: Exclude<Color, null>, colorLevel: ColorLevel): string | undefined {\n if (colorLevel === \"mono\") return undefined\n if (colorLevel === \"truecolor\") return fgColorCode(color)\n const index = serializedPaletteIndex(color)\n if (colorLevel === \"256\" && index !== undefined) return `38;5;${index}`\n const { r, g, b } = rgbForColor(color)\n return fgFromRgb(r, g, b, colorLevel)\n}\n\nfunction backgroundCode(color: Exclude<Color, null>, colorLevel: ColorLevel): string | undefined {\n if (colorLevel === \"mono\") return undefined\n if (colorLevel === \"truecolor\") return bgColorCode(color)\n const index = serializedPaletteIndex(color)\n if (colorLevel === \"256\" && index !== undefined) return `48;5;${index}`\n const { r, g, b } = rgbForColor(color)\n return bgFromRgb(r, g, b, colorLevel)\n}\n\nfunction underlineColorCode(\n color: Exclude<Color, null>,\n colorLevel: ColorLevel,\n): string | undefined {\n if (colorLevel === \"mono\") return undefined\n const index = serializedPaletteIndex(color)\n if (colorLevel === \"truecolor\") {\n if (index !== undefined) return `58;5;${index}`\n const { r, g, b } = rgbForColor(color)\n return `58;2;${r};${g};${b}`\n }\n if (colorLevel === \"256\") {\n if (index !== undefined) return `58;5;${index}`\n const { r, g, b } = rgbForColor(color)\n return `58;5;${rgbToAnsi256(r, g, b)}`\n }\n const { r, g, b } = rgbForColor(color)\n return `58;5;${nearestAnsi16(r, g, b)}`\n}\n\nfunction colorTransitionCode(\n oldColor: Color | undefined,\n newColor: Color | undefined,\n resetCode: string,\n colorLevel: ColorLevel,\n serialize: (color: Exclude<Color, null>, level: ColorLevel) => string | undefined,\n): string {\n if (colorEquals(oldColor, newColor)) return \"\"\n if (newColor == null) return colorLevel === \"mono\" ? \"\" : resetCode\n const code = serialize(newColor, colorLevel)\n return code ? `\\x1b[${code}m` : \"\"\n}\n\nexport function styleToAnsiCodes(style: Style, colorLevel: ColorLevel = \"truecolor\"): string {\n const fg = style.fg\n const bg = style.bg\n\n let result = \"\"\n\n // Foreground color\n if (fg !== null) {\n const code = foregroundCode(fg, colorLevel)\n if (code) result += `\\x1b[${code}m`\n }\n\n // Background color (DEFAULT_BG sentinel = terminal default, skip)\n if (bg !== null && !isDefaultBg(bg)) {\n const code = backgroundCode(bg, colorLevel)\n if (code) result += `\\x1b[${code}m`\n }\n\n // Attributes\n if (style.attrs.bold) result += \"\\x1b[1m\"\n if (style.attrs.dim) result += \"\\x1b[2m\"\n if (style.attrs.italic) result += \"\\x1b[3m\"\n\n // Underline: use SGR 4:x if style specified, otherwise simple SGR 4\n const underlineStyle = style.attrs.underlineStyle\n if (typeof underlineStyle === \"string\") {\n const styleMap: Record<string, number> = {\n single: 1,\n double: 2,\n curly: 3,\n dotted: 4,\n dashed: 5,\n }\n const subparam = styleMap[underlineStyle]\n if (subparam !== undefined && subparam !== 0) {\n result += `\\x1b[4:${subparam}m`\n }\n } else if (style.attrs.underline) {\n result += \"\\x1b[4m\" // Simple underline\n }\n\n // Use SGR 7 for inverse — lets the terminal correctly swap fg/bg\n if (style.attrs.inverse) result += \"\\x1b[7m\"\n if (style.attrs.strikethrough) result += \"\\x1b[9m\"\n if (style.attrs.overline) result += \"\\x1b[53m\"\n\n // Underline color (SGR 58)\n if (style.underlineColor !== null && style.underlineColor !== undefined) {\n const code = underlineColorCode(style.underlineColor, colorLevel)\n if (code) result += `\\x1b[${code}m`\n }\n\n return result\n}\n\n/**\n * Compute the minimal SGR transition between two styles (chalk-compatible).\n *\n * When oldStyle is null (first cell or after reset), falls through to\n * full generation via styleToAnsiCodes. Otherwise, diffs attribute\n * by attribute and emits only changed SGR codes as individual \\x1b[Xm sequences.\n */\nexport function styleTransitionCodes(\n oldStyle: Style | null,\n newStyle: Style,\n colorLevel: ColorLevel = \"truecolor\",\n): string {\n // First cell or after reset — full generation\n if (!oldStyle) return styleToAnsiCodes(newStyle, colorLevel)\n\n // Same style — nothing to emit\n if (styleEquals(oldStyle, newStyle)) return \"\"\n\n let result = \"\"\n const oa = oldStyle.attrs\n const na = newStyle.attrs\n\n // Bold and dim share SGR 22 as their off-code\n const boldChanged = Boolean(oa.bold) !== Boolean(na.bold)\n const dimChanged = Boolean(oa.dim) !== Boolean(na.dim)\n if (boldChanged || dimChanged) {\n const boldOff = boldChanged && !na.bold\n const dimOff = dimChanged && !na.dim\n if (boldOff || dimOff) {\n result += \"\\x1b[22m\"\n if (na.bold) result += \"\\x1b[1m\"\n if (na.dim) result += \"\\x1b[2m\"\n } else {\n if (boldChanged && na.bold) result += \"\\x1b[1m\"\n if (dimChanged && na.dim) result += \"\\x1b[2m\"\n }\n }\n if (Boolean(oa.italic) !== Boolean(na.italic)) {\n result += na.italic ? \"\\x1b[3m\" : \"\\x1b[23m\"\n }\n\n // Underline\n const oldUl = Boolean(oa.underline)\n const newUl = Boolean(na.underline)\n const oldUlStyle = oa.underlineStyle ?? false\n const newUlStyle = na.underlineStyle ?? false\n if (oldUl !== newUl || oldUlStyle !== newUlStyle) {\n if (typeof na.underlineStyle === \"string\") {\n const styleMap: Record<string, number> = {\n single: 1,\n double: 2,\n curly: 3,\n dotted: 4,\n dashed: 5,\n }\n const sub = styleMap[na.underlineStyle]\n if (sub !== undefined && sub !== 0) {\n result += `\\x1b[4:${sub}m`\n } else if (newUl) {\n result += \"\\x1b[4m\"\n } else {\n result += \"\\x1b[24m\"\n }\n } else if (newUl) {\n result += \"\\x1b[4m\"\n } else {\n result += \"\\x1b[24m\"\n }\n }\n\n if (Boolean(oa.inverse) !== Boolean(na.inverse)) {\n result += na.inverse ? \"\\x1b[7m\" : \"\\x1b[27m\"\n }\n if (Boolean(oa.strikethrough) !== Boolean(na.strikethrough)) {\n result += na.strikethrough ? \"\\x1b[9m\" : \"\\x1b[29m\"\n }\n // Overline (SGR 53/55)\n if (Boolean(oa.overline) !== Boolean(na.overline)) {\n result += na.overline ? \"\\x1b[53m\" : \"\\x1b[55m\"\n }\n\n result += colorTransitionCode(oldStyle.fg, newStyle.fg, \"\\x1b[39m\", colorLevel, foregroundCode)\n result += colorTransitionCode(oldStyle.bg, newStyle.bg, \"\\x1b[49m\", colorLevel, backgroundCode)\n result += colorTransitionCode(\n oldStyle.underlineColor,\n newStyle.underlineColor,\n \"\\x1b[59m\",\n colorLevel,\n underlineColorCode,\n )\n\n return result\n}\n\n/**\n * Emit per-attribute reset codes for all active attributes in a style.\n * Returns empty string if no attributes are active.\n * Uses individual \\x1b[Xm sequences to match chalk's format.\n */\nexport function styleResetCodes(style: Style): string {\n let result = \"\"\n // Attributes (order: underline, bold/dim, italic, strikethrough, inverse — matches chalk close order)\n if (style.attrs.underline || style.attrs.underlineStyle) result += \"\\x1b[24m\"\n if (style.attrs.bold || style.attrs.dim) result += \"\\x1b[22m\"\n if (style.attrs.italic) result += \"\\x1b[23m\"\n if (style.attrs.strikethrough) result += \"\\x1b[29m\"\n if (style.attrs.inverse) result += \"\\x1b[27m\"\n if (style.attrs.overline) result += \"\\x1b[55m\"\n // Colors\n if (style.bg !== null && !isDefaultBg(style.bg)) result += \"\\x1b[49m\"\n if (style.fg !== null) result += \"\\x1b[39m\"\n // Underline color\n if (style.underlineColor !== null && style.underlineColor !== undefined) result += \"\\x1b[59m\"\n return result\n}\n\n/**\n * Trim trailing whitespace from a string while preserving ANSI codes.\n */\nexport function trimTrailingWhitespacePreservingAnsi(str: string): string {\n // Find the last non-whitespace character or ANSI escape\n let lastContentIndex = -1\n let i = 0\n\n while (i < str.length) {\n if (str[i] === \"\\x1b\") {\n // Check for OSC sequence (ESC ] ... ST or BEL)\n if (str[i + 1] === \"]\") {\n // Find the terminator: ST (\\x1b\\\\) or BEL (\\x07)\n let end = -1\n for (let j = i + 2; j < str.length; j++) {\n if (str[j] === \"\\x07\") {\n end = j\n break\n }\n if (str[j] === \"\\x1b\" && str[j + 1] === \"\\\\\") {\n end = j + 1\n break\n }\n }\n if (end !== -1) {\n lastContentIndex = end\n i = end + 1\n continue\n }\n }\n // Found SGR escape - skip the entire sequence\n const end = str.indexOf(\"m\", i)\n if (end !== -1) {\n lastContentIndex = end\n i = end + 1\n continue\n }\n }\n if (str[i] !== \" \" && str[i] !== \"\\t\") {\n lastContentIndex = i\n }\n i++\n }\n\n return str.slice(0, lastContentIndex + 1)\n}\n\n// ============================================================================\n// TextFrame — Immutable Snapshot\n// ============================================================================\n\nimport type { TextFrame, FrameCell, RGB } from \"@silvery/ag/text-frame\"\n\n/**\n * Standard ANSI 256-color palette → RGB lookup.\n * Colors 0-15: standard + bright colors (approximate).\n * Colors 16-231: 6×6×6 color cube.\n * Colors 232-255: grayscale ramp.\n *\n * Exported for the backdrop-fade pipeline pass, which needs to resolve\n * palette-indexed cells to RGB before blending via `@silvery/color`.\n */\nexport function ansi256ToRgb(idx: number): RGB {\n if (idx < 16) {\n // Standard 16 colors (same values used by xterm)\n const table: [number, number, number][] = [\n [0, 0, 0],\n [128, 0, 0],\n [0, 128, 0],\n [128, 128, 0],\n [0, 0, 128],\n [128, 0, 128],\n [0, 128, 128],\n [192, 192, 192],\n [128, 128, 128],\n [255, 0, 0],\n [0, 255, 0],\n [255, 255, 0],\n [0, 0, 255],\n [255, 0, 255],\n [0, 255, 255],\n [255, 255, 255],\n ]\n const [r, g, b] = table[idx]!\n return { r, g, b }\n }\n if (idx < 232) {\n // 6×6×6 color cube (indices 16-231)\n const i = idx - 16\n const r = Math.floor(i / 36)\n const g = Math.floor((i % 36) / 6)\n const b = i % 6\n return {\n r: r ? r * 40 + 55 : 0,\n g: g ? g * 40 + 55 : 0,\n b: b ? b * 40 + 55 : 0,\n }\n }\n // Grayscale ramp (indices 232-255)\n const v = (idx - 232) * 10 + 8\n return { r: v, g: v, b: v }\n}\n\n/**\n * Resolve a buffer Color (number | RGB | null) to FrameCell RGB (RGB | null).\n *\n * Palette provenance is preserved: an indexed color resolves to its baked RGB\n * PLUS the originating `index`, so identity-aware readers (differs, comparators,\n * re-emitters) can round-trip it back to indexed SGR while painters still read\n * r/g/b unconditionally.\n */\nfunction resolveColor(color: Color): RGB | null {\n if (color === null) return null\n if (typeof color === \"number\") return { ...ansi256ToRgb(color), index: color }\n // RGB object — check for DEFAULT_BG sentinel\n if (color.r === -1) return null\n return color\n}\n\n/** Convert a buffer Cell to an immutable FrameCell with resolved RGB colors. */\nexport function cellToFrameCell(c: Cell): FrameCell {\n const ulStyle =\n c.attrs.underlineStyle ?? (c.attrs.underline ? (\"single\" as const) : (false as const))\n return {\n char: c.char,\n fg: resolveColor(c.fg),\n bg: resolveColor(c.bg),\n bold: c.attrs.bold ?? false,\n dim: c.attrs.dim ?? false,\n italic: c.attrs.italic ?? false,\n underline: ulStyle,\n underlineColor: resolveColor(c.underlineColor ?? null),\n overline: c.attrs.overline ?? false,\n strikethrough: c.attrs.strikethrough ?? false,\n inverse: c.attrs.inverse ?? false,\n blink: c.attrs.blink ?? false,\n hidden: c.attrs.hidden ?? false,\n wide: c.wide,\n continuation: c.continuation,\n hyperlink: c.hyperlink ?? null,\n }\n}\n\n/** Empty FrameCell returned for out-of-bounds access. */\nexport const EMPTY_FRAME_CELL: FrameCell = Object.freeze({\n char: \" \",\n fg: null,\n bg: null,\n bold: false,\n dim: false,\n italic: false,\n underline: false as const,\n underlineColor: null,\n overline: false,\n strikethrough: false,\n inverse: false,\n blink: false,\n hidden: false,\n wide: false,\n continuation: false,\n hyperlink: null,\n})\n\n/**\n * Create an immutable TextFrame snapshot from a TerminalBuffer.\n *\n * The snapshot is detached from the source buffer — mutations to the buffer\n * after this call do not affect the frame. Text and ANSI are lazily computed\n * on first access.\n *\n * This is the primary public read API for rendered output. Internal pipeline\n * code (diff-buffers, render phases) continues to use TerminalBuffer directly.\n */\nexport function createTextFrame(buffer: TerminalBuffer): TextFrame {\n const width = buffer.width\n const height = buffer.height\n\n // Snapshot: lazy clone — only on first text/ansi/cell access. Hot test\n // paths create frames they never read, so eager cloning charges every\n // caller for an 80K-cell buffer copy that's discarded immediately.\n // Callers who need true construction-time detachment (i.e. the frame\n // must be safe across buffer mutations between createTextFrame() and\n // first read) should request an eager/COW snapshot via a dedicated path\n // instead of relying on createTextFrame()'s default.\n let _snapshot: TerminalBuffer | undefined\n function getSnapshot(): TerminalBuffer {\n if (!_snapshot) _snapshot = buffer.clone()\n return _snapshot\n }\n\n // Cell data: lazy — only built on first cell() access.\n // At 400x200, eager construction creates 80,000 Cell objects (~10ms).\n // Most frames only access .text or .ansi, never individual cells.\n let _cellData: Cell[] | undefined\n function getCellData(): Cell[] {\n if (!_cellData) {\n const snap = getSnapshot()\n _cellData = new Array(width * height)\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n _cellData[y * width + x] = snap.getCell(x, y)\n }\n }\n }\n return _cellData\n }\n\n // Lazy caches\n let _text: string | undefined\n let _ansi: string | undefined\n let _lines: string[] | undefined\n\n const frame: TextFrame = {\n width,\n height,\n\n get text(): string {\n if (_text === undefined) _text = bufferToText(getSnapshot())\n return _text\n },\n\n get ansi(): string {\n if (_ansi === undefined) _ansi = bufferToStyledText(getSnapshot())\n return _ansi\n },\n\n get lines(): string[] {\n if (_lines === undefined) _lines = frame.text.split(\"\\n\")\n return _lines\n },\n\n cell(col: number, row: number): FrameCell {\n if (col < 0 || col >= width || row < 0 || row >= height) {\n return EMPTY_FRAME_CELL\n }\n return cellToFrameCell(getCellData()[row * width + col]!)\n },\n\n containsText(text: string): boolean {\n return frame.text.includes(text)\n },\n }\n\n return frame\n}\n","/**\n * ANSI output constants and terminal control utilities.\n *\n * NOTE: Buffer rendering is handled by pipeline/output-phase.ts.\n * This file contains only terminal control sequences and constants.\n */\n\nimport { hostname } from \"node:os\"\nimport {\n enableMouse as _enableMouse,\n disableMouse as _disableMouse,\n enableKittyKeyboard as _enableKittyKeyboard,\n disableKittyKeyboard as _disableKittyKeyboard,\n} from \"@silvery/ansi\"\n\n// ============================================================================\n// ANSI Escape Codes\n// ============================================================================\n\nconst ESC = \"\\x1b\"\nconst CSI = `${ESC}[`\n\n// Cursor control\nconst CURSOR_HIDE = `${CSI}?25l`\nconst CURSOR_SHOW = `${CSI}?25h`\nconst CURSOR_HOME = `${CSI}H`\n\n// Synchronized Update Mode (DEC private mode 2026)\n// Tells the terminal to batch output and paint atomically, preventing tearing.\n// Supported by: Ghostty, Kitty, WezTerm, iTerm2, Foot, Alacritty 0.14+, tmux 3.2+\n// Terminals that don't support it safely ignore these sequences.\nconst SYNC_BEGIN = `${CSI}?2026h`\nconst SYNC_END = `${CSI}?2026l`\n\n// Style reset\nconst RESET = `${CSI}0m`\n\n// SGR (Select Graphic Rendition) codes\nconst SGR = {\n // Attributes\n bold: 1,\n dim: 2,\n italic: 3,\n underline: 4,\n blink: 5,\n inverse: 7,\n hidden: 8,\n strikethrough: 9,\n\n // Attribute resets\n boldOff: 22, // Also resets dim\n italicOff: 23,\n underlineOff: 24,\n blinkOff: 25,\n inverseOff: 27,\n hiddenOff: 28,\n strikethroughOff: 29,\n\n // Colors (foreground)\n fgDefault: 39,\n fgBlack: 30,\n fgRed: 31,\n fgGreen: 32,\n fgYellow: 33,\n fgBlue: 34,\n fgMagenta: 35,\n fgCyan: 36,\n fgWhite: 37,\n fgBrightBlack: 90,\n fgBrightRed: 91,\n fgBrightGreen: 92,\n fgBrightYellow: 93,\n fgBrightBlue: 94,\n fgBrightMagenta: 95,\n fgBrightCyan: 96,\n fgBrightWhite: 97,\n\n // Colors (background)\n bgDefault: 49,\n bgBlack: 40,\n bgRed: 41,\n bgGreen: 42,\n bgYellow: 43,\n bgBlue: 44,\n bgMagenta: 45,\n bgCyan: 46,\n bgWhite: 47,\n bgBrightBlack: 100,\n bgBrightRed: 101,\n bgBrightGreen: 102,\n bgBrightYellow: 103,\n bgBrightBlue: 104,\n bgBrightMagenta: 105,\n bgBrightCyan: 106,\n bgBrightWhite: 107,\n} as const\n\n// ============================================================================\n// Cursor Movement\n// ============================================================================\n\n/**\n * Generate ANSI sequence to move cursor to position.\n * Terminal positions are 1-indexed.\n */\nfunction moveCursor(x: number, y: number): string {\n return `${CSI}${y + 1};${x + 1}H`\n}\n\n/**\n * Generate ANSI sequence to move cursor up N lines.\n */\nfunction cursorUp(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}A`\n return `${CSI}${n}A`\n}\n\n/**\n * Generate ANSI sequence to move cursor down N lines.\n */\nfunction cursorDown(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}B`\n return `${CSI}${n}B`\n}\n\n/**\n * Generate ANSI sequence to move cursor right N columns.\n */\nfunction cursorRight(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}C`\n return `${CSI}${n}C`\n}\n\n/**\n * Generate ANSI sequence to move cursor left N columns.\n */\nfunction cursorLeft(n: number): string {\n if (n <= 0) return \"\"\n if (n === 1) return `${CSI}D`\n return `${CSI}${n}D`\n}\n\n/**\n * Generate ANSI sequence to move cursor to column.\n */\nfunction cursorToColumn(x: number): string {\n return `${CSI}${x + 1}G`\n}\n\n// ============================================================================\n// Cursor Shape (DECSCUSR)\n// ============================================================================\n\n/**\n * Terminal cursor shape. Combined with blink parameter in setCursorStyle().\n */\nexport type CursorShape = \"block\" | \"underline\" | \"bar\"\n\nconst CURSOR_SHAPE_CODES: Record<CursorShape, { blink: number; steady: number }> = {\n block: { blink: 1, steady: 2 },\n underline: { blink: 3, steady: 4 },\n bar: { blink: 5, steady: 6 },\n}\n\n/**\n * Set the terminal cursor shape via DECSCUSR (CSI Ps SP q).\n *\n * Supported by: xterm, Ghostty, Kitty, WezTerm, iTerm2, Alacritty, foot.\n * Terminals that don't support it safely ignore the sequence.\n *\n * @param shape - \"block\", \"underline\", or \"bar\"\n * @param blink - Whether the cursor should blink (default: false)\n */\nexport function setCursorStyle(shape: CursorShape, blink = false): string {\n const code = blink ? CURSOR_SHAPE_CODES[shape].blink : CURSOR_SHAPE_CODES[shape].steady\n return `${CSI}${code} q`\n}\n\n/**\n * Reset the terminal cursor style to the terminal's default (DECSCUSR 0).\n */\nexport function resetCursorStyle(): string {\n return `${CSI}0 q`\n}\n\n// ============================================================================\n// Terminal Control\n// ============================================================================\n\n/**\n * Enter alternate screen buffer, clear screen, and hide cursor.\n * Cursor is hidden by default - applications must explicitly show it for text input.\n *\n * The clear screen (\\x1b[2J) and cursor home (\\x1b[H) are essential after entering\n * the alternate buffer to ensure a clean slate. Without this, the terminal may have\n * leftover content from previous sessions that causes rendering artifacts like\n * content appearing at wrong Y positions (bug km-x7ih).\n */\nexport function enterAlternateScreen(): string {\n return `${CSI}?1049h${CSI}2J${CURSOR_HOME}${CURSOR_HIDE}`\n}\n\n/**\n * Leave alternate screen buffer and restore cursor.\n * Includes SYNC_END as a safety belt — ensures synchronized update mode is\n * reset even if the process was interrupted mid-render. Sending SYNC_END\n * when not in sync mode is a harmless no-op.\n */\nexport function leaveAlternateScreen(): string {\n return `${SYNC_END}${CURSOR_SHOW}${CSI}?1049l`\n}\n\n// Re-export from @silvery/ansi — canonical implementations\nexport const enableMouse = _enableMouse\nexport const disableMouse = _disableMouse\n\n/**\n * Kitty keyboard protocol flags (bitfield).\n *\n * | Flag | Bit | Description |\n * | ---- | --- | ---------------------------------------------- |\n * | 1 | 0 | Disambiguate escape codes |\n * | 2 | 1 | Report event types (press/repeat/release) |\n * | 4 | 2 | Report alternate keys |\n * | 8 | 3 | Report all keys as escape codes |\n * | 16 | 4 | Report associated text |\n */\nexport const KittyFlags = {\n DISAMBIGUATE: 1,\n REPORT_EVENTS: 2,\n REPORT_ALTERNATE: 4,\n REPORT_ALL_KEYS: 8,\n REPORT_TEXT: 16,\n} as const\n\n/**\n * Enable Kitty keyboard protocol (push mode).\n * Sends CSI > flags u to opt into the specified modes.\n * Default flags=11 (DISAMBIGUATE | REPORT_EVENTS | REPORT_ALL_KEYS) —\n * enables modifier-only key reporting needed for useModifierKeys() Cmd tracking.\n * Supported: Ghostty, Kitty, WezTerm, foot. Ignored by unsupported terminals.\n *\n * @param flags Bitfield of KittyFlags (default: DISAMBIGUATE)\n */\nexport const enableKittyKeyboard = _enableKittyKeyboard\nexport function queryKittyKeyboard(): string {\n return `${CSI}?u`\n}\nexport const disableKittyKeyboard = _disableKittyKeyboard\n\n// ============================================================================\n// Bell / OSC String Terminator\n// ============================================================================\n\n/** BEL character — audible bell and legacy OSC string terminator. */\nexport const BEL = \"\\x07\"\n\n// ============================================================================\n// Window Title (OSC 0/2)\n// ============================================================================\n\n/**\n * Set the terminal window title using OSC 2 (window title only).\n * Does not affect icon title (tab name in some terminals).\n * Widely supported: xterm, Ghostty, iTerm2, Kitty, WezTerm, Alacritty, foot.\n */\nexport function setWindowTitle(stdout: NodeJS.WriteStream, title: string): void {\n stdout.write(`${ESC}]2;${title}${BEL}`)\n}\n\n/**\n * Set both the window title and icon title using OSC 0.\n * Some terminals treat OSC 0 as equivalent to OSC 2; others also change the\n * dock/taskbar icon name.\n */\nexport function setWindowAndIconTitle(stdout: NodeJS.WriteStream, title: string): void {\n stdout.write(`${ESC}]0;${title}${BEL}`)\n}\n\n/**\n * Reset the terminal window title by sending an empty OSC 2 sequence.\n * The terminal typically reverts to its default title (shell command, etc.).\n */\nexport function resetWindowTitle(stdout: NodeJS.WriteStream): void {\n stdout.write(`${ESC}]2;${BEL}`)\n}\n\n// ============================================================================\n// Directory Reporting\n// ============================================================================\n\n/** Report current working directory to the terminal via OSC 7.\n * Used by terminals (iTerm2, Ghostty, WezTerm) for tab/split directory inheritance.\n */\nexport function reportDirectory(stdout: NodeJS.WriteStream, path: string): void {\n // OSC 7 format: ESC ] 7 ; file://hostname/path BEL\n const host = hostname()\n const encoded = encodeURI(path).replace(/#/g, \"%23\")\n stdout.write(`${ESC}]7;file://${host}${encoded}${BEL}`)\n}\n\n// ============================================================================\n// Mouse Cursor Shape (OSC 22)\n// ============================================================================\n\n/**\n * Mouse cursor shape names for OSC 22.\n *\n * Uses X11/CSS cursor names. Supported by: Ghostty, Kitty (>=0.33), foot,\n * WezTerm (partial). Terminals that don't support OSC 22 safely ignore it.\n */\nexport type MouseCursorShape =\n | \"default\"\n | \"text\"\n | \"pointer\"\n | \"crosshair\"\n | \"move\"\n | \"not-allowed\"\n | \"wait\"\n | \"help\"\n | \"grab\"\n | \"grabbing\"\n | \"col-resize\"\n | \"row-resize\"\n | \"ew-resize\"\n | \"ns-resize\"\n\n/**\n * Generate OSC 22 sequence to set the mouse cursor shape.\n *\n * @param shape - X11/CSS cursor name\n * @returns ANSI escape sequence string\n */\nexport function setMouseCursorShape(shape: MouseCursorShape): string {\n return `${ESC}]22;${shape}${BEL}`\n}\n\n/**\n * Generate OSC 22 sequence to reset mouse cursor to default.\n *\n * @returns ANSI escape sequence string\n */\nexport function resetMouseCursorShape(): string {\n return `${ESC}]22;default${BEL}`\n}\n\n// ============================================================================\n// Export Constants\n// ============================================================================\n\nexport const ANSI = {\n ESC,\n CSI,\n CURSOR_HIDE,\n CURSOR_SHOW,\n CURSOR_HOME,\n SYNC_BEGIN,\n SYNC_END,\n RESET,\n SGR,\n moveCursor,\n cursorUp,\n cursorDown,\n cursorLeft,\n cursorRight,\n cursorToColumn,\n} as const\n","/**\n * Text Sizing Protocol (OSC 66) -- Kitty v0.40+\n *\n * Lets the app specify how many cells a character should occupy.\n * This solves the measurement/rendering mismatch for Private Use Area (PUA)\n * characters (nerdfont icons, powerline symbols) that `string-width` reports\n * as 1-cell but terminals render as 2-cell.\n *\n * When OSC 66 is used with w=2, both the app's layout engine and the terminal\n * agree on the character width, eliminating truncation and misalignment.\n *\n * Protocol format:\n * ESC ] 66 ; w=<width> ; <text> BEL\n *\n * Post km-silvery.unicode-plateau Phase 2 (2026-04-23): this module reads\n * ZERO environment variables. Capability detection lives entirely in\n * `@silvery/ansi`'s `createTerminalProfile`. Consumers pass `TerminalCaps`\n * (or an explicit fingerprint string for the probe cache) so the module is\n * pure w.r.t. environment and browser/canvas targets aren't broken.\n *\n * @see https://sw.kovidgoyal.net/kitty/text-sizing-protocol/\n */\n\nimport type { TerminalCaps } from \"@silvery/ansi\"\n\nconst OSC = \"\\x1b]\"\nconst ST = \"\\x07\" // BEL terminator (more compatible than ESC \\)\n\n/**\n * Wrap text in an OSC 66 sequence that tells the terminal to render it\n * in exactly `width` cells.\n */\nexport function textSized(text: string, width: number): string {\n return `${OSC}66;w=${width};${text}${ST}`\n}\n\n// ============================================================================\n// Text Scale (font size multiplier) — OSC 66 s= parameter\n// ============================================================================\n\n/**\n * Generate an OSC 66 escape sequence to set the text scale (font size).\n *\n * The scale is a floating-point multiplier:\n * - 2.0 = double size (headings)\n * - 1.0 = normal\n * - 0.5 = half size (small print, annotations)\n *\n * The scale applies to all subsequent text until reset or changed.\n * Only supported in Kitty v0.40+.\n *\n * @param scale - Font size multiplier (e.g. 2.0 for double, 0.5 for half)\n */\nexport function textScaled(scale: number): string {\n return `${OSC}66;s=${scale}${ST}`\n}\n\n/**\n * Generate an OSC 66 escape sequence to reset text scale to default (1.0).\n */\nexport function resetTextScale(): string {\n return `${OSC}66;s=1${ST}`\n}\n\n/**\n * Check if a code point is in the Private Use Area (PUA).\n * Covers BMP PUA (U+E000-U+F8FF) and Supplementary PUA-A/B.\n */\nexport function isPrivateUseArea(cp: number): boolean {\n return (\n (cp >= 0xe000 && cp <= 0xf8ff) || // BMP PUA\n (cp >= 0xf0000 && cp <= 0xffffd) || // Supplementary PUA-A\n (cp >= 0x100000 && cp <= 0x10fffd) // Supplementary PUA-B\n )\n}\n\n// ============================================================================\n// Probe fingerprint + result cache\n// ============================================================================\n\n/**\n * Structural subset of the terminal profile's emulator the fingerprint helper\n * needs. Accepts any object with `program` + `version` strings (typically\n * `profile.emulator` or `term.emulator`).\n */\nexport interface FingerprintEmulator {\n readonly program: string\n readonly version: string\n}\n\n/**\n * Build a terminal fingerprint for cache keying. Combines `program` +\n * `version` from the supplied emulator to uniquely identify the terminal\n * type. Different versions may add/remove OSC 66 support, so version is part\n * of the key.\n *\n * Post unicode-plateau Phase 2: the emulator argument is required — the\n * legacy env-reading variant is gone. Callers building fingerprints from a\n * one-shot probe can use `createTerminalProfile().emulator` upstream.\n */\nexport function getTerminalFingerprint(emulator: FingerprintEmulator): string {\n const program = emulator.program || \"unknown\"\n const version = emulator.version || \"unknown\"\n return `${program}@${version}`\n}\n\n/** Result of text sizing probe */\nexport interface TextSizingProbeResult {\n supported: boolean\n widthOnly: boolean\n}\n\n/**\n * Cache of probe results by terminal fingerprint.\n * Persists across app instances in the same process so the probe\n * only runs once per terminal type.\n */\nconst probeCache = new Map<string, TextSizingProbeResult>()\n\n/**\n * Get a cached probe result for the given fingerprint, if available.\n */\nexport function getCachedProbeResult(fingerprint: string): TextSizingProbeResult | undefined {\n return probeCache.get(fingerprint)\n}\n\n/**\n * Store a probe result in the cache for the given fingerprint.\n */\nexport function setCachedProbeResult(fingerprint: string, result: TextSizingProbeResult): void {\n probeCache.set(fingerprint, result)\n}\n\n/**\n * Clear the probe cache. Useful for testing.\n */\nexport function clearProbeCache(): void {\n probeCache.clear()\n}\n\n// ============================================================================\n// Async probe\n// ============================================================================\n\n/**\n * Detect terminal support for the text sizing protocol.\n * Uses cursor position reports (CPR) to check if OSC 66 advances the cursor\n * by the specified width.\n *\n * Results are cached by fingerprint so the probe only runs once per terminal\n * type per process.\n *\n * @param write - Writer function (TUI output)\n * @param read - Reader function (CPR response source)\n * @param fingerprint - Cache key. Derived via {@link getTerminalFingerprint}\n * from `TerminalCaps`; callers inside a running session typically compute\n * it once from `term.profile.caps`.\n * @param timeout - Per-probe timeout in ms (default 1000)\n * @returns Object with `supported` and `widthOnly` flags:\n * - supported=true, widthOnly=false: full support (scale + width)\n * - supported=true, widthOnly=true: width mode only\n * - supported=false: no support\n */\nexport async function detectTextSizingSupport(\n write: (data: string) => void,\n read: () => Promise<string>,\n fingerprint: string,\n timeout = 1000,\n): Promise<TextSizingProbeResult> {\n // Check cache first\n const cached = getCachedProbeResult(fingerprint)\n if (cached !== undefined) return cached\n\n // Detection sequence:\n // 1. CR to column 0\n // 2. OSC 66 w=2 with a space character\n // 3. Request CPR (cursor position report)\n // If cursor is at column 3 (1-indexed), w=2 worked\n const testSequence = \"\\r\" + textSized(\" \", 2) + \"\\x1b[6n\" + \"\\r\\x1b[K\"\n write(testSequence)\n\n try {\n const response = await Promise.race([\n read(),\n new Promise<string>((_resolve, reject) =>\n setTimeout(() => reject(new Error(\"timeout\")), timeout),\n ),\n ])\n\n // Parse CPR response: ESC [ row ; col R\n const match = response.match(/\\x1b\\[(\\d+);(\\d+)R/)\n if (match) {\n const col = Number(match[2])\n // Column 3 means the space occupied 2 cells (col is 1-indexed, started at 1)\n if (col === 3) {\n const result: TextSizingProbeResult = { supported: true, widthOnly: false }\n setCachedProbeResult(fingerprint, result)\n return result\n }\n }\n\n const result: TextSizingProbeResult = { supported: false, widthOnly: false }\n setCachedProbeResult(fingerprint, result)\n return result\n } catch {\n const result: TextSizingProbeResult = { supported: false, widthOnly: false }\n setCachedProbeResult(fingerprint, result)\n return result\n }\n}\n","/**\n * SILVERY_STRICT — single canonical strictness gate.\n *\n * Contract: the `SILVERY_STRICT` env var is the only knob for runtime\n * verification. It accepts a comma-separated list where each entry is\n * either a numeric tier (1/2/3, ascending strictness) or a check slug\n * (e.g. \"canary\", \"residue\", \"incremental\"). Empty / \"0\" / unset is off.\n *\n * SILVERY_STRICT=1 // all canonical checks at tier 1\n * SILVERY_STRICT=2 // tier 1 + every-action checks\n * SILVERY_STRICT=canary // only the degenerate-frame canary\n * SILVERY_STRICT=residue,1 // tier 1 + explicit residue (redundant; same as 1)\n * SILVERY_STRICT=1,!canary // tier 1 minus the canary (per-check skip)\n *\n * The \"do NOT add new SILVERY_* enable env vars\" rule lives here as code:\n * every check threads through `isStrictEnabled(slug, minTier)`. New checks\n * pick a slug + a tier and inherit the umbrella behavior — `bun run\n * test:fast` (which sets `SILVERY_STRICT=1` by default) gets every new\n * check without anyone touching env config.\n *\n * Slug + min-tier constants live BESIDE each check (the codebase convention),\n * never in a central registry here. Examples: `RESIDUE_STRICT_SLUG` in\n * pipeline/strict-residue.ts; `LAYOUT_FLAG_STRICT_SLUG` (tier 1),\n * `LAYOUT_OVERFLOW_STRICT_SLUG` (tier 2), and `SCROLL_INVARIANTS_STRICT_SLUG`\n * (tier 2) in pipeline/layout-phase.ts. Because they all route through\n * `isStrictEnabled`, a compound spec (`incremental,2`) throws every tier-2\n * check and a `!slug` suffix (`2,!layout-overflow`) disables exactly one.\n */\n\nexport interface StrictModeQuery {\n /** Slug name for this check (e.g. \"canary\", \"residue\"). */\n slug: string\n /** Minimum numeric tier at which this check is implicitly enabled. */\n minTier?: number\n}\n\ninterface ParsedStrict {\n enabledTiers: ReadonlySet<number>\n enabledSlugs: ReadonlySet<string>\n disabledSlugs: ReadonlySet<string>\n raw: string | undefined\n}\n\nlet cachedRaw: string | undefined = undefined\nlet cachedParsed: ParsedStrict | null = null\n\nfunction parseStrict(raw: string | undefined): ParsedStrict {\n if (!raw || raw === \"0\") {\n return {\n enabledTiers: new Set(),\n enabledSlugs: new Set(),\n disabledSlugs: new Set(),\n raw,\n }\n }\n const tiers = new Set<number>()\n const slugs = new Set<string>()\n const disabled = new Set<string>()\n for (const part of raw.split(\",\")) {\n const trimmed = part.trim()\n if (!trimmed) continue\n if (trimmed.startsWith(\"!\")) {\n disabled.add(trimmed.slice(1))\n continue\n }\n const n = Number(trimmed)\n if (Number.isFinite(n) && n >= 1) {\n tiers.add(n)\n continue\n }\n slugs.add(trimmed)\n }\n return { enabledTiers: tiers, enabledSlugs: slugs, disabledSlugs: disabled, raw }\n}\n\nfunction getParsed(): ParsedStrict {\n const raw = process.env.SILVERY_STRICT\n if (raw !== cachedRaw || cachedParsed === null) {\n cachedRaw = raw\n cachedParsed = parseStrict(raw)\n }\n return cachedParsed\n}\n\n/**\n * Returns true if `slug` should fire under the current `SILVERY_STRICT`.\n *\n * - `!slug` always wins (per-check skip)\n * - explicit slug match wins\n * - any tier >= minTier (default 1) implies the check\n */\nexport function isStrictEnabled(slug: string, minTier: number = 1): boolean {\n const p = getParsed()\n if (p.disabledSlugs.has(slug)) return false\n if (p.enabledSlugs.has(slug)) return true\n for (const tier of p.enabledTiers) {\n if (tier >= minTier) return true\n }\n return false\n}\n\n/** True if SILVERY_STRICT is set to anything that turns checking on. */\nexport function isStrictAnyEnabled(): boolean {\n const p = getParsed()\n return p.enabledTiers.size > 0 || p.enabledSlugs.size > 0\n}\n\n/** Reset the cache — for tests that mutate process.env mid-run. */\nexport function resetStrictCache(): void {\n cachedRaw = undefined\n cachedParsed = null\n}\n","/**\n * STRICT divergence — the blank-screen class-killer (19604 focus-blank).\n *\n * Bead: @km/code/v0.2/19604-focus-blank (#undead #P0).\n *\n * The 19604 signature is brutally specific: silvery emits a FULL content\n * frame (bytes_out shows a real frame — painted cells, ANSI, the works)\n * while the TERMINAL SCREEN IS BLANK (cmux `read-screen` reports\n * nonspace=0). The render pipeline and the emulator disagree: our buffer\n * says \"thousands of cells painted\", the screen says \"all spaces\". A\n * swallowed focus-restore repaint, a degenerate-resize heal that never\n * lands, a cursor-positioning desync that parks every write off-screen —\n * any of them produces this exact divergence, and ALL of them are silent\n * today (the frame \"succeeds\" from silvery's point of view).\n *\n * This check makes that class LOUD. After a frame's cumulative ANSI has\n * been fed into an in-process emulator (the SILVERY_STRICT_TERMINAL\n * readback path), compare:\n *\n * painted := buffer.countPaintedCells() (what we MEANT to draw)\n * onScreen := emulator non-space cell count (what the terminal SHOWS)\n *\n * If `painted` is a real content frame (>= CONTENT_THRESHOLD, not the\n * near-empty splash/spinner) but `onScreen === 0`, the frame is a 19604\n * blank-screen divergence — throw.\n *\n * ── Why this lives in ag-term/src/ and not pipeline/ ──\n * Like strict-cls.ts, this is a pure assertion helper. It consumes a\n * painted-cell count and an emulator readback and decides via the\n * SILVERY_STRICT contract. It has no coupling to render-phase /\n * layout-phase internals. The ONE call site (output-phase.ts, right\n * after `verifyTerminalEquivalence` feeds the frame into the emulator)\n * passes both numbers in — this module never reaches into the pipeline.\n *\n * ── Strictness: tier 2 (paranoid) ──\n * `SILVERY_STRICT=2` (or `=divergence`, or `=3`) fires the throw;\n * `SILVERY_STRICT=1` does NOT — it emits a `silvery:divergence` debug\n * line instead (mirrors the degenerate-frame canary's tier-2 throw-vs-\n * debug shape in renderer.ts). The default tier-1 `bun run test:fast`\n * pass stays green; only the paranoid tier asserts. `console.warn` is\n * deliberately NOT used — km's vitest setup treats any `console.warn`\n * as a hard test failure.\n *\n * ── No-op in production by construction ──\n * The check only has a meaningful emulator readback when an in-process\n * emulator exists, i.e. under `SILVERY_STRICT_TERMINAL=xterm|ghostty`.\n * Live Ghostty / a real TTY has NO in-process emulator — the call site\n * is inside the `tvState.terminal || tvState.ghosttyTerminal` block, so\n * production live terminals never enter it and can never throw. The\n * `emulatorReadback === null` guard is a second belt: a null readback is\n * a structural no-op regardless of tier.\n */\n\nimport { isStrictEnabled } from \"./strict-mode\"\nimport { createLogger } from \"loggily\"\n\nconst log = createLogger(\"silvery:divergence\")\n\n/** SILVERY_STRICT slug for the render/emulator divergence check. Tier 2 by design. */\nexport const DIVERGENCE_STRICT_SLUG = \"divergence\"\nexport const DIVERGENCE_STRICT_MIN_TIER = 2\n\n/**\n * Minimum painted-cell count for the check to engage. Below this, the\n * buffer is a unit-test fixture or a legitimately near-empty frame\n * (startup splash, lone spinner, cleared screen) where \"few painted\n * cells, zero on screen\" is not a bug. Matches the degenerate-frame\n * canary's CANARY_MIN_BUFFER_CELLS spirit (≈80×50) — a real app frame\n * paints far more than this; a blank-but-content-frame divergence at\n * 19604 scale paints thousands.\n */\nexport const DIVERGENCE_MIN_PAINTED_CELLS = 4000\n\n/** Returns true when SILVERY_STRICT=divergence (or =2 / =3 / etc.) is enabled. */\nexport function isDivergenceStrictEnabled(): boolean {\n return isStrictEnabled(DIVERGENCE_STRICT_SLUG, DIVERGENCE_STRICT_MIN_TIER)\n}\n\n/**\n * Thrown when a frame had real painted content in silvery's buffer but\n * the in-process emulator readback is all-spaces (zero non-space cells).\n * This is the 19604 blank-screen signature: a full frame emitted to a\n * blank screen.\n *\n * `.paintedCells` / `.onScreenCells` are exposed for programmatic\n * inspection; `.message` names 19604 and both counts.\n */\nexport class RenderEmulatorDivergenceError extends Error {\n readonly paintedCells: number\n readonly onScreenCells: number\n\n constructor(paintedCells: number, onScreenCells: number, backend: string, frameCount: number) {\n super(\n `[SILVERY_STRICT=divergence] render/emulator divergence (19604 blank-screen signature): ` +\n `frame ${frameCount} painted ${paintedCells} content cells but the ${backend} emulator ` +\n `readback is all-spaces (${onScreenCells} non-space cells). silvery emitted a full ` +\n `content frame while the terminal screen is blank — the exact @km/code/v0.2/19604-focus-blank ` +\n `failure mode (a swallowed/mis-positioned repaint that never lands on screen). ` +\n `Per-test opt-out: SILVERY_STRICT=2,!divergence.`,\n )\n this.name = \"RenderEmulatorDivergenceError\"\n this.paintedCells = paintedCells\n this.onScreenCells = onScreenCells\n }\n}\n\n/** Object with `countPaintedCells()` — structural so tests can pass a stub. */\nexport interface PaintedCellSource {\n countPaintedCells(): number\n}\n\n/**\n * The emulator readback for one in-process terminal backend.\n *\n * `nonSpaceCells` is the number of non-whitespace cells currently on the\n * emulator screen (e.g. `terminal.getText()` with whitespace stripped, or\n * a per-cell `char.trim() !== \"\"` count). `backendName` is for the error\n * message (\"xterm\" / \"ghostty\").\n */\nexport interface EmulatorReadback {\n nonSpaceCells: number\n backendName: string\n}\n\n/**\n * Throws `RenderEmulatorDivergenceError` if the frame painted a real\n * content buffer but the emulator readback shows an all-spaces screen,\n * AND `SILVERY_STRICT=divergence` (or a stricter tier) is enabled.\n * Otherwise a no-op.\n *\n * Gates (all must pass to engage):\n * 1. `isDivergenceStrictEnabled()` — slug/tier ≥ 2.\n * 2. `emulatorReadback !== null` — an in-process emulator exists\n * (no-op for live TTYs, which have none).\n * 3. `buffer.countPaintedCells() >= DIVERGENCE_MIN_PAINTED_CELLS` —\n * this is a real content frame, not a near-empty / unit-fixture one.\n *\n * Divergence condition: the gated content frame's emulator readback has\n * ZERO non-space cells.\n *\n * Tier handling mirrors the degenerate-frame canary: explicit slug\n * \"divergence\" or tier ≥ 2 throws; a lower tier that somehow reaches\n * here (it cannot via `isDivergenceStrictEnabled`, but the debug branch\n * is kept for parity / future tier-1 promotion) emits a debug line.\n *\n * @param buffer painted-cell source (the just-rendered `next` buffer)\n * @param emulatorReadback the in-process emulator readback, or `null` when none exists\n * @param frameCount frame index for the diagnostic message\n */\nexport function checkRenderEmulatorDivergence(\n buffer: PaintedCellSource,\n emulatorReadback: EmulatorReadback | null,\n frameCount: number = 0,\n): void {\n // Gate 2: no in-process emulator → structural no-op (live TTY path).\n if (emulatorReadback === null) return\n // Gate 1 (cheap-after-2): only engage when the slug/tier is on. Checked\n // after the null guard so the production path short-circuits first.\n if (!isStrictEnabled(DIVERGENCE_STRICT_SLUG, 1)) return\n\n const painted = buffer.countPaintedCells()\n // Gate 3: real content frame only.\n if (painted < DIVERGENCE_MIN_PAINTED_CELLS) return\n\n // The divergence: real content painted, nothing on screen.\n if (emulatorReadback.nonSpaceCells !== 0) return\n\n if (isDivergenceStrictEnabled()) {\n throw new RenderEmulatorDivergenceError(\n painted,\n emulatorReadback.nonSpaceCells,\n emulatorReadback.backendName,\n frameCount,\n )\n } else {\n // Tier-1 (slug not at tier 2): observe, don't block. Parity with the\n // canary so a future tier-1 promotion is a one-line change.\n log.debug?.(\n `silvery: render/emulator divergence (19604) — frame ${frameCount} painted ${painted} ` +\n `cells, ${emulatorReadback.backendName} emulator shows 0 non-space cells. ` +\n `Raise to SILVERY_STRICT=2 (or =divergence) to make this throw.`,\n )\n }\n}\n\n/**\n * Count non-space cells in an emulator's full-screen text readback.\n *\n * Helper for the call site: termless `Terminal.getText()` returns the\n * entire screen as a newline-joined string; non-whitespace length is the\n * \"is anything visible\" signal that matches cmux `read-screen` nonspace.\n */\nexport function countNonSpaceInText(screenText: string): number {\n let n = 0\n for (const ch of screenText) {\n // Newlines separate rows in getText(); spaces are blank cells. Any\n // other code point is a visible glyph.\n if (ch !== \" \" && ch !== \"\\n\" && ch !== \"\\r\" && ch !== \"\\t\") n++\n }\n return n\n}\n","/**\n * ESM-graph-coherent loader for the OPTIONAL @termless/* emulator backends used\n * by SILVERY_STRICT_TERMINAL (output-verify.ts) and cursor verification\n * (cursor-diagnostics.ts).\n *\n * ── Why this module exists ────────────────────────────────────────────────\n *\n * NOT `createRequire`: `createRequire(import.meta.url)(\"@termless/ghostty\")`\n * re-introduces the CommonJS loader inside ESM. Under vitest's module runner the\n * loaded module ESCAPES the ESM graph and gets its OWN instance. @termless/ghostty\n * holds a module-level `sharedGhostty` WASM singleton (set by `initGhostty()`); a\n * second instance splits it, so the verifier's `createGhosttyBackend()` observes\n * `sharedGhostty = null` even after the ESM instance was initialized — it then\n * diverges from (or errors against) the ghostty terminal every other consumer\n * shares. That is the 2026-07-02 Ghostty-WASM incident: the contract-test culprit\n * was fixed in silvery 4641f71b; output-verify.ts + cursor-diagnostics.ts were the\n * remaining production-side createRequire instances. Routing every load through\n * this module puts the verifier on the SAME instance as createTermless /\n * createGhosttyBackend / the tests' own `import … from \"@termless/ghostty\"`.\n *\n * NOT a static `import`: @termless/* are OPTIONAL peers of the published silvery\n * umbrella. A static import evaluates them at module load for every consumer and\n * breaks installs that don't have the peers. `await import()` inside a preload\n * only resolves when strict verification is actually requested.\n *\n * ── The sync/async bridge ─────────────────────────────────────────────────\n *\n * The load is async (`await import()`); the consumers (the output phase, cursor\n * diagnostics) run in the SYNCHRONOUS render pipeline and cannot await a dynamic\n * import mid-frame. We bridge with an async PRELOAD into a module cache plus a\n * sync GET that throws LOUD on a cache miss (no silent fallback — a missing\n * backend must be diagnosable, not swallowed). Preload is awaited at the two\n * async boundaries that precede any strict frame:\n * - `@silvery/test`'s top-level await (test path — the backends are already\n * statically imported there, so the preload is a free re-resolve).\n * - the live `run()` setup (the SILVERY_STRICT_TERMINAL / cursor debugging path).\n *\n * Bead: createrequire-ban (wave 3 — the incident siblings).\n */\n\ninterface BackendCache {\n core: typeof import(\"@termless/core\") | null\n xterm: typeof import(\"@termless/xtermjs\") | null\n ghostty: typeof import(\"@termless/ghostty\") | null\n}\n\n// The cache lives on globalThis under a Symbol.for key, NOT in a module-level\n// `let`. Under vitest's module runner this file can be instantiated more than\n// once — @silvery/test imports it by package specifier\n// (`@silvery/ag-term/strict-terminal-backends`) while output-verify.ts /\n// cursor-diagnostics.ts import it by relative path (`../strict-terminal-backends`),\n// and those two specifiers resolve to DISTINCT module instances. A module-level\n// cache would then split: @silvery/test's top-level preload populates one\n// instance, the synchronous verifier reads the other (empty) instance and\n// fail-louds. `Symbol.for` is a cross-instance/cross-realm global registry, so\n// every instance shares ONE cache object — the same singleton-coherence property\n// this module exists to guarantee for @termless itself.\nconst CACHE_KEY = Symbol.for(\"@silvery/ag-term:strict-terminal-backends\")\n\nfunction backendCache(): BackendCache {\n const store = globalThis as typeof globalThis & { [CACHE_KEY]?: BackendCache }\n return (store[CACHE_KEY] ??= { core: null, xterm: null, ghostty: null })\n}\n\nexport interface StrictTerminalPreloadOptions {\n /**\n * Also load the `@termless/ghostty` module (required before any\n * SILVERY_STRICT_TERMINAL=ghostty frame). Defaults to false so callers that\n * only need the xterm/core verifier don't pull the ghostty module.\n */\n ghostty?: boolean\n /**\n * Also `await initGhostty()` so the WASM singleton is ready for the\n * SYNCHRONOUS `createGhosttyBackend()` the verifier calls mid-frame. The\n * `@silvery/test` top-level preload passes `false` — the ghostty MODULE is\n * loaded (so the sync accessor resolves) but WASM init stays deferred to the\n * ghostty test's own `await initGhostty()` (or the live run() path), which\n * keeps non-ghostty test suites from paying the WASM load. Defaults to\n * whatever `ghostty` is.\n */\n initGhosttyWasm?: boolean\n}\n\n/**\n * Load the requested @termless backends into the module cache via the ESM\n * graph. Idempotent — safe to call from every async setup path. Awaiting this\n * is what makes the subsequent synchronous `getTermless*()` reads succeed.\n */\nexport async function preloadStrictTerminalBackends(\n opts: StrictTerminalPreloadOptions = {},\n): Promise<void> {\n const wantGhostty = opts.ghostty ?? false\n const wantWasm = opts.initGhosttyWasm ?? wantGhostty\n const cache = backendCache()\n\n // core + xtermjs are always needed by both verifiers (the fresh-render\n // comparison terminal is xterm even in ghostty mode).\n cache.core ??= await import(\"@termless/core\")\n cache.xterm ??= await import(\"@termless/xtermjs\")\n\n if (wantGhostty) {\n cache.ghostty ??= await import(\"@termless/ghostty\")\n if (wantWasm) await cache.ghostty.initGhostty()\n }\n}\n\nfunction missing(specifier: string, extra = \"\"): never {\n throw new Error(\n `[silvery] @termless backend \"${specifier}\" was consumed before it was preloaded. ` +\n `The SILVERY_STRICT terminal/cursor verifier runs in the synchronous output phase ` +\n `and cannot await a dynamic import mid-frame, so the backend must be loaded first via ` +\n `\\`await preloadStrictTerminalBackends()\\`. Tests get this for free through @silvery/test's ` +\n `top-level preload; a live \\`run()\\` under SILVERY_STRICT_TERMINAL/cursor awaits it during ` +\n `setup. This is loaded via the ESM graph (never the CommonJS require shim) so the verifier ` +\n `shares the single @termless module instance — the 2026-07-02 Ghostty-WASM singleton-split fix.${extra}`,\n )\n}\n\n/** Sync accessor for `@termless/core`. Throws loud if not preloaded. */\nexport function getTermlessCore(): typeof import(\"@termless/core\") {\n return backendCache().core ?? missing(\"@termless/core\")\n}\n\n/** Sync accessor for `@termless/xtermjs`. Throws loud if not preloaded. */\nexport function getTermlessXterm(): typeof import(\"@termless/xtermjs\") {\n return backendCache().xterm ?? missing(\"@termless/xtermjs\")\n}\n\n/** Sync accessor for `@termless/ghostty`. Throws loud if not preloaded. */\nexport function getTermlessGhostty(): typeof import(\"@termless/ghostty\") {\n return (\n backendCache().ghostty ??\n missing(\n \"@termless/ghostty\",\n \" Pass `{ ghostty: true }` to preloadStrictTerminalBackends() (SILVERY_STRICT_TERMINAL=ghostty).\",\n )\n )\n}\n\n/**\n * Test-only reset of the backend cache. Lets a test assert the fail-loud path\n * and re-exercise the preload. NOT for production use.\n * @internal\n */\nexport function _resetStrictTerminalBackendsForTesting(): void {\n const cache = backendCache()\n cache.core = null\n cache.xterm = null\n cache.ghostty = null\n}\n","import { isStrictEnabled } from \"./strict-mode\"\nimport { getTermlessCore, getTermlessXterm } from \"./strict-terminal-backends\"\nimport { createLogger } from \"loggily\"\n\nconst log = createLogger(\"silvery:cursor\")\n\nexport const CURSOR_STRICT_SLUG = \"cursor\"\nexport const CURSOR_POSITION_STRICT_SLUG = \"cursor-position\"\nexport const CURSOR_STRICT_MIN_TIER = 2\n\nexport interface OutputCursorTarget {\n x: number\n y: number\n visible: boolean\n shape?: string\n}\n\nexport interface OutputCursorTerminalState {\n x: number\n y: number\n visible: boolean | null\n style: string | null\n}\n\nexport interface OutputCursorBounds {\n x: number\n y: number\n width: number\n height: number\n}\n\nexport interface OutputCompositorCaret {\n x: number\n y: number\n visible: boolean\n style: string\n}\n\n/**\n * The previous frame's presentation buffer bundled with the composited caret\n * that was painted into it.\n *\n * These two values MUST move together: the buffer is the diff baseline the next\n * frame renders against, and `caret` is where (or whether) this frame painted\n * the managed inverse-caret overlay. The next frame's overlay-clear\n * (`clearPriorCaretOverlay` in `managed-caret.ts`) reads `caret` to decide which\n * row to re-mark dirty so a moved/removed caret's stale `inverse` cell is\n * cleared. Storing a new buffer without the matching caret (or vice versa)\n * reintroduces the @km/code/v0.2/19702 stranded-caret residue.\n *\n * Bundling them into one struct makes that desync impossible by construction —\n * every assignment site sets both fields in a single object literal, so there is\n * no way to advance the buffer baseline without also advancing the caret it was\n * composited with. Generic over the buffer type because the scheduler tracks a\n * raw `TerminalBuffer` while the runtime tracks its wrapped `Buffer`; both hold\n * the same caret type.\n */\nexport interface PrevPresentation<TBuffer> {\n /** The presentation buffer the terminal showed this frame (the next diff baseline). */\n buffer: TBuffer\n /** The composited caret painted into `buffer`, or null if none was painted. */\n caret: OutputCompositorCaret | null\n}\n\nexport interface OutputCursorDiagnostics {\n reason: string\n mode: \"fullscreen\" | \"inline\"\n backend: \"xterm\"\n width: number\n height: number\n termRows?: number\n outputBytes: number\n semanticCursor: OutputCursorTarget | null\n target: OutputCursorTarget | null\n hardwareParking: OutputCursorTarget | null\n hardwareVisibility: boolean | null\n compositorCaret: OutputCompositorCaret | null\n promptBounds: OutputCursorBounds | null\n composerBounds: OutputCursorBounds | null\n /**\n * Whether the VISIBLE composited caret landed inside the editable's content\n * rect (`composerBounds`). The @km/code/v0.2/19702 stranded-caret invariant:\n * a Silvery-owned composited caret several rows ABOVE the composer (on a\n * transcript/chrome row) reads as `false` here. `null` when not checkable —\n * no composited caret, or no known composer bounds (island / legacy cursor,\n * where the caret's owner is a different node than `composerBounds`).\n */\n compositorCaretInComposerBounds: boolean | null\n finalCursorEscape: \"show\" | \"hide\" | null\n expectedTerminal: OutputCursorTarget | null\n terminal: OutputCursorTerminalState | null\n}\n\nexport class OutputCursorMismatchError extends Error {\n readonly diagnostics: OutputCursorDiagnostics\n\n constructor(message: string, diagnostics: OutputCursorDiagnostics) {\n super(message)\n this.name = \"OutputCursorMismatchError\"\n this.diagnostics = diagnostics\n }\n}\n\nlet lastOutputCursorDiagnostics: OutputCursorDiagnostics | null = null\n\nfunction isCursorStrictDisabled(): boolean {\n const raw = process.env.SILVERY_STRICT\n if (!raw) return false\n for (const part of raw.split(\",\")) {\n const slug = part.trim()\n if (slug === `!${CURSOR_STRICT_SLUG}` || slug === `!${CURSOR_POSITION_STRICT_SLUG}`) {\n return true\n }\n }\n return false\n}\n\nexport function isCursorStrictEnabled(): boolean {\n if (isCursorStrictDisabled()) return false\n return (\n isStrictEnabled(CURSOR_STRICT_SLUG, CURSOR_STRICT_MIN_TIER) ||\n isStrictEnabled(CURSOR_POSITION_STRICT_SLUG, CURSOR_STRICT_MIN_TIER)\n )\n}\n\nexport function getLastOutputCursorDiagnostics(): OutputCursorDiagnostics | null {\n return lastOutputCursorDiagnostics\n ? {\n ...lastOutputCursorDiagnostics,\n target: lastOutputCursorDiagnostics.target\n ? { ...lastOutputCursorDiagnostics.target }\n : null,\n expectedTerminal: lastOutputCursorDiagnostics.expectedTerminal\n ? { ...lastOutputCursorDiagnostics.expectedTerminal }\n : null,\n semanticCursor: lastOutputCursorDiagnostics.semanticCursor\n ? { ...lastOutputCursorDiagnostics.semanticCursor }\n : null,\n hardwareParking: lastOutputCursorDiagnostics.hardwareParking\n ? { ...lastOutputCursorDiagnostics.hardwareParking }\n : null,\n compositorCaret: lastOutputCursorDiagnostics.compositorCaret\n ? { ...lastOutputCursorDiagnostics.compositorCaret }\n : null,\n promptBounds: lastOutputCursorDiagnostics.promptBounds\n ? { ...lastOutputCursorDiagnostics.promptBounds }\n : null,\n composerBounds: lastOutputCursorDiagnostics.composerBounds\n ? { ...lastOutputCursorDiagnostics.composerBounds }\n : null,\n terminal: lastOutputCursorDiagnostics.terminal\n ? { ...lastOutputCursorDiagnostics.terminal }\n : null,\n }\n : null\n}\n\nexport function clearLastOutputCursorDiagnostics(): void {\n lastOutputCursorDiagnostics = null\n}\n\nfunction loadTermless(): {\n createTerminal: typeof import(\"@termless/core\").createTerminal\n createXtermBackend: typeof import(\"@termless/xtermjs\").createXtermBackend\n} {\n // ESM-graph load (never createRequire) so the cursor replay terminal shares\n // the single @termless instance — see strict-terminal-backends.ts for the\n // Ghostty-WASM singleton-split rationale. Preloaded by @silvery/test / run().\n const core = getTermlessCore()\n const xtermjs = getTermlessXterm()\n return { createTerminal: core.createTerminal, createXtermBackend: xtermjs.createXtermBackend }\n}\n\nfunction replayCursor(output: string, cols: number, rows: number): OutputCursorTerminalState {\n const { createTerminal, createXtermBackend } = loadTermless()\n const terminal = createTerminal({ backend: createXtermBackend(), cols, rows })\n try {\n terminal.feed(output)\n const cursor = terminal.getCursor()\n return {\n x: cursor.x,\n y: cursor.y,\n visible: cursor.visible,\n style: cursor.style,\n }\n } finally {\n void terminal.close()\n }\n}\n\nfunction formatTarget(target: OutputCursorTarget | null): string {\n if (!target) return \"hidden\"\n return `x=${target.x} y=${target.y} visible=${target.visible}`\n}\n\nfunction assertCursorMatches(diagnostics: OutputCursorDiagnostics): void {\n const expected = diagnostics.expectedTerminal\n const actual = diagnostics.terminal\n if (!actual) return\n\n if (!expected) {\n if (actual.visible === false || diagnostics.finalCursorEscape === \"hide\") return\n const message =\n `SILVERY_STRICT=cursor expected hidden terminal cursor after ${diagnostics.reason}; ` +\n `actual x=${actual.x} y=${actual.y} visible=${actual.visible}`\n throw new OutputCursorMismatchError(message, diagnostics)\n }\n\n if (!expected.visible) {\n if (\n (actual.visible === false ||\n actual.visible === null ||\n diagnostics.finalCursorEscape === \"hide\") &&\n actual.x === expected.x &&\n actual.y === expected.y\n ) {\n return\n }\n const message =\n `SILVERY_STRICT=cursor expected hidden terminal cursor at ` +\n `${formatTarget(expected)} after ${diagnostics.reason}; ` +\n `actual x=${actual.x} y=${actual.y} visible=${actual.visible}`\n throw new OutputCursorMismatchError(message, diagnostics)\n }\n\n if (actual.x !== expected.x || actual.y !== expected.y || actual.visible === false) {\n const message =\n `SILVERY_STRICT=cursor terminal cursor mismatch after ${diagnostics.reason}: ` +\n `expected ${formatTarget(expected)} actual x=${actual.x} y=${actual.y} ` +\n `visible=${actual.visible}`\n throw new OutputCursorMismatchError(message, diagnostics)\n }\n}\n\n/**\n * The @km/code/v0.2/19702 composited-caret-bounds invariant: a VISIBLE\n * composited caret must land inside the editable's content rect. Returns:\n * - `true` — caret is within `bounds`.\n * - `false` — caret is OUTSIDE `bounds` (the stranded-caret artifact: an\n * inverse cell painted on a transcript/chrome row above/below the composer).\n * - `null` — not checkable (no visible composited caret, or no known\n * composer bounds — e.g. a `cursorActive` island / legacy cursor whose\n * owning node differs from `bounds`).\n *\n * Rows are half-open `[y, y+height)`. The end-of-line virtual column (where the\n * next character would be typed) legitimately sits one past the content width,\n * so the x upper bound is inclusive.\n */\nfunction caretInComposerBounds(\n caret: OutputCompositorCaret | null,\n bounds: OutputCursorBounds | null,\n): boolean | null {\n if (!caret || caret.visible === false || !bounds) return null\n const inX = caret.x >= bounds.x && caret.x <= bounds.x + bounds.width\n const inY = caret.y >= bounds.y && caret.y < bounds.y + bounds.height\n return inX && inY\n}\n\nexport function recordOutputCursorDiagnostics(opts: {\n reason: string\n mode: \"fullscreen\" | \"inline\"\n width: number\n height: number\n termRows?: number\n output: string\n target: OutputCursorTarget | null\n expectedTerminal?: OutputCursorTarget | null\n compositorCaret?: OutputCompositorCaret | null\n promptBounds?: OutputCursorBounds | null\n composerBounds?: OutputCursorBounds | null\n}): void {\n if (!isCursorStrictEnabled()) return\n\n const terminalRows = opts.termRows ?? opts.height\n const terminal = replayCursor(opts.output, opts.width, terminalRows)\n const finalCursorEscape = lastCursorVisibilityEscape(opts.output)\n const expectedTerminal =\n opts.expectedTerminal === undefined\n ? opts.target\n ? { ...opts.target }\n : null\n : opts.expectedTerminal\n ? { ...opts.expectedTerminal }\n : null\n const diagnostics: OutputCursorDiagnostics = {\n reason: opts.reason,\n mode: opts.mode,\n backend: \"xterm\",\n width: opts.width,\n height: opts.height,\n termRows: opts.termRows,\n outputBytes: Buffer.byteLength(opts.output),\n semanticCursor: opts.target ? { ...opts.target } : null,\n target: opts.target ? { ...opts.target } : null,\n hardwareParking: expectedTerminal ? { ...expectedTerminal } : null,\n hardwareVisibility:\n finalCursorEscape === \"hide\" ? false : finalCursorEscape === \"show\" ? true : terminal.visible,\n compositorCaret: opts.compositorCaret ? { ...opts.compositorCaret } : null,\n promptBounds: opts.promptBounds ? { ...opts.promptBounds } : null,\n composerBounds: opts.composerBounds ? { ...opts.composerBounds } : null,\n compositorCaretInComposerBounds: caretInComposerBounds(\n opts.compositorCaret ?? null,\n opts.composerBounds ?? null,\n ),\n finalCursorEscape,\n expectedTerminal,\n terminal,\n }\n lastOutputCursorDiagnostics = diagnostics\n\n // NO SILENT ERRORS: a composited caret outside its composer bounds is the\n // @km/code/v0.2/19702 stranded-caret artifact. Surface it LOUDLY rather than\n // silently rendering an inverse cell on a transcript/chrome row. Recorded\n // (not thrown) because an island host-caret legitimately composites outside\n // the separate composer's bounds and the payload does not yet carry the\n // caret's owning-node rect to disambiguate; upgrade to a throw scoped to\n // `focused-declarative` provenance once the provenance walk returns\n // `{node, rect, provenance}` (the noted 19702 follow-up).\n if (diagnostics.compositorCaretInComposerBounds === false) {\n log.warn?.(\n `cursor ${opts.reason}: composited caret OUT OF composer bounds ` +\n `(@km/code/v0.2/19702 stranded-caret) — ` +\n `caret=${formatCompositorCaret(diagnostics.compositorCaret)} ` +\n `composer=${formatBounds(diagnostics.composerBounds)}`,\n )\n }\n\n log.debug?.(\n `cursor ${opts.reason}: cursor:row=${terminal.y},col=${terminal.x} ` +\n `target=${formatTarget(diagnostics.target)} ` +\n `expected=${formatTarget(diagnostics.expectedTerminal)} ` +\n `terminal=x=${terminal.x} y=${terminal.y} visible=${terminal.visible} ` +\n `compositor=${formatCompositorCaret(diagnostics.compositorCaret)} ` +\n `prompt=${formatBounds(diagnostics.promptBounds)} ` +\n `composer=${formatBounds(diagnostics.composerBounds)} ` +\n `final=${diagnostics.finalCursorEscape ?? \"none\"}`,\n )\n\n assertCursorMatches(diagnostics)\n}\n\nfunction lastCursorVisibilityEscape(output: string): \"show\" | \"hide\" | null {\n let last: \"show\" | \"hide\" | null = null\n const re = /\\x1b\\[\\?25([hl])/g\n let match: RegExpExecArray | null\n while ((match = re.exec(output))) {\n last = match[1] === \"h\" ? \"show\" : \"hide\"\n }\n return last\n}\n\nfunction formatCompositorCaret(caret: OutputCompositorCaret | null): string {\n if (!caret) return \"none\"\n return `x=${caret.x} y=${caret.y} visible=${caret.visible} style=${caret.style}`\n}\n\nfunction formatBounds(bounds: OutputCursorBounds | null): string {\n if (!bounds) return \"none\"\n return `x=${bounds.x} y=${bounds.y} w=${bounds.width} h=${bounds.height}`\n}\n","/**\n * Error types for silvery terminal rendering.\n *\n * Separated from scheduler.ts to allow React-free barrel imports.\n * Keep this file React-free — only plain types allowed.\n */\n\nimport type { RenderPhaseStats } from \"./pipeline/types\"\n\n/** Structured mismatch data attached to the error (mirrors MismatchDebugContext shape) */\nexport interface MismatchErrorData {\n /** Render-phase instrumentation snapshot (nodes visited/rendered/skipped, per-flag breakdown) */\n renderPhaseStats?: RenderPhaseStats\n /** Debug context for the mismatched cell (from debug-mismatch.ts) */\n mismatchContext?: unknown\n}\n\n/**\n * Error thrown when SILVERY_STRICT detects a mismatch.\n * This error should NOT be caught by general error handlers - it indicates\n * a bug in incremental rendering that needs to be fixed.\n *\n * When SILVERY_STRICT fires, the error automatically includes:\n * - Render-phase instrumentation (nodes visited/rendered/skipped, per-flag breakdown)\n * - Cell attribution (which node owns the mismatched cell, dirty flags, scroll context)\n */\nexport class IncrementalRenderMismatchError extends Error {\n /** Render-phase instrumentation snapshot */\n renderPhaseStats?: RenderPhaseStats\n /** Debug context for the mismatched cell */\n mismatchContext?: unknown\n\n constructor(message: string, data?: MismatchErrorData) {\n super(message)\n this.name = \"IncrementalRenderMismatchError\"\n this.renderPhaseStats = data?.renderPhaseStats\n this.mismatchContext = data?.mismatchContext\n }\n}\n","/**\n * STRICT/Verification code extracted from output-phase.ts.\n *\n * Contains: replayAnsiWithStyles (vt100 backend), captureStrictFailureArtifacts,\n * verifyOutputEquivalence, verifyAccumulatedOutput, verifyTerminalEquivalence,\n * compareTerminals, and all supporting types/helpers.\n *\n * These functions are only called when SILVERY_STRICT, SILVERY_STRICT_ACCUMULATE,\n * or SILVERY_STRICT_TERMINAL are enabled. Production builds can tree-shake this\n * entire module.\n */\n\nimport type { TerminalBuffer } from \"../buffer\"\nimport { IncrementalRenderMismatchError } from \"../errors\"\nimport { getTermlessCore, getTermlessGhostty, getTermlessXterm } from \"../strict-terminal-backends\"\nimport { graphemeWidth, isTextPresentationEmoji } from \"../unicode\"\nimport { createLogger } from \"loggily\"\nimport type { OutputContext } from \"./output-phase\"\n\nconst log = createLogger(\"silvery:output\")\n\nconst _env =\n typeof process !== \"undefined\" ? process.env : ({} as Record<string, string | undefined>)\nconst DEBUG_OUTPUT = !!_env.SILVERY_DEBUG_OUTPUT\n\n// ============================================================================\n// Types for avoiding circular dependencies\n// ============================================================================\n\n/** Function signature for bufferToAnsi — passed from output-phase to avoid circular import. */\nexport type BufferToAnsiFn = (\n buffer: TerminalBuffer,\n ctx: OutputContext,\n maxRows?: number,\n) => string\n\n/** Function signature for outputGraphemeWidth. */\nexport type OutputGraphemeWidthFn = (g: string, ctx: OutputContext) => number\n\n/** Function signature for outputTextSizingEnabled. */\nexport type OutputTextSizingEnabledFn = (ctx: OutputContext) => boolean\n\n/** Per-instance state for SILVERY_STRICT_ACCUMULATE verification. */\nexport interface AccumulateState {\n accumulatedAnsi: string\n accumulateWidth: number\n accumulateHeight: number\n accumulateFrameCount: number\n}\n\ntype VerificationTerminal = ReturnType<typeof import(\"@termless/core\").createTerminal>\n\n/** Per-instance state for SILVERY_STRICT_TERMINAL verification.\n * Holds persistent terminal(s) that accumulate incremental ANSI output\n * across frames, enabling comparison against a fresh render in an independent emulator. */\nexport interface TerminalVerifyState {\n /** The persistent xterm.js terminal accumulating incremental output */\n terminal: VerificationTerminal | null\n /** Optional persistent Ghostty terminal for cross-backend verification */\n ghosttyTerminal: VerificationTerminal | null\n /** Width of the terminal */\n width: number\n /** Height of the terminal */\n height: number\n /** Frame count for diagnostics */\n frameCount: number\n /** Which emulator backends to verify (xterm, ghostty — vt100 handled separately) */\n backends: Array<\"xterm\" | \"ghostty\">\n /** Whether the vt100 (replayAnsiWithStyles) backend is enabled */\n hasVt100: boolean\n}\n\n// ============================================================================\n// Strict environment helpers\n// ============================================================================\n\n/** Parse SILVERY_STRICT_TERMINAL into a list of backends.\n *\n * Accepts `all` (= `vt100,xterm,ghostty`) or a comma-separated list of\n * backend names (e.g., `vt100,xterm`).\n *\n * The `vt100` backend uses the internal `replayAnsiWithStyles` parser (stateless).\n * `xterm` and `ghostty` use persistent terminal emulators (stateful).\n */\nexport function strictTerminalBackends(): Array<\"vt100\" | \"xterm\" | \"ghostty\"> {\n const val = (typeof process !== \"undefined\" ? (process.env.SILVERY_STRICT_TERMINAL ?? \"\") : \"\")\n .toLowerCase()\n .trim()\n if (!val) return []\n if (val === \"all\") return [\"vt100\", \"xterm\", \"ghostty\"]\n // Comma-separated list\n const backends = val\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean)\n const valid = new Set([\"vt100\", \"xterm\", \"ghostty\"])\n for (const b of backends) {\n if (!valid.has(b)) {\n log.warn?.(`SILVERY_STRICT_TERMINAL: unknown backend '${b}', ignoring`)\n }\n }\n return backends.filter((b) => valid.has(b)) as Array<\"vt100\" | \"xterm\" | \"ghostty\">\n}\n\n/** Create fresh terminal verify state. */\nexport function createTerminalVerifyState(): TerminalVerifyState {\n const allBackends = strictTerminalBackends()\n return {\n terminal: null,\n ghosttyTerminal: null,\n width: 0,\n height: 0,\n frameCount: 0,\n backends: allBackends.filter((b) => b !== \"vt100\") as Array<\"xterm\" | \"ghostty\">,\n hasVt100: allBackends.includes(\"vt100\"),\n }\n}\n\n// ============================================================================\n// Style-Aware ANSI Replay\n// ============================================================================\n\n/** SGR state tracked during ANSI replay. */\ninterface SgrState {\n fg: number | { r: number; g: number; b: number } | null\n bg: number | { r: number; g: number; b: number } | null\n bold: boolean\n dim: boolean\n italic: boolean\n underline: boolean\n blink: boolean\n inverse: boolean\n hidden: boolean\n strikethrough: boolean\n}\n\n/** A cell in the style-aware virtual terminal. */\nexport interface StyledCell {\n char: string\n fg: number | { r: number; g: number; b: number } | null\n bg: number | { r: number; g: number; b: number } | null\n bold: boolean\n dim: boolean\n italic: boolean\n underline: boolean\n blink: boolean\n inverse: boolean\n hidden: boolean\n strikethrough: boolean\n}\n\nfunction createDefaultSgr(): SgrState {\n return {\n fg: null,\n bg: null,\n bold: false,\n dim: false,\n italic: false,\n underline: false,\n blink: false,\n inverse: false,\n hidden: false,\n strikethrough: false,\n }\n}\n\nfunction createDefaultStyledCell(): StyledCell {\n return {\n char: \" \",\n fg: null,\n bg: null,\n bold: false,\n dim: false,\n italic: false,\n underline: false,\n blink: false,\n inverse: false,\n hidden: false,\n strikethrough: false,\n }\n}\n\nfunction cloneStyledCell(cell: StyledCell): StyledCell {\n return { ...cell }\n}\n\n/**\n * Apply SGR parameters to the current state.\n * Handles all SGR codes used by styleTransition().\n */\nfunction applySgrParams(params: string, sgr: SgrState): void {\n if (params === \"\" || params === \"0\") {\n // Reset\n sgr.fg = null\n sgr.bg = null\n sgr.bold = false\n sgr.dim = false\n sgr.italic = false\n sgr.underline = false\n sgr.blink = false\n sgr.inverse = false\n sgr.hidden = false\n sgr.strikethrough = false\n return\n }\n\n const parts = params.split(\";\")\n let i = 0\n while (i < parts.length) {\n const code = parts[i]!\n // Handle subparameters (e.g., \"4:3\" for curly underline)\n const colonIdx = code.indexOf(\":\")\n if (colonIdx >= 0) {\n const mainCode = parseInt(code.substring(0, colonIdx))\n if (mainCode === 4) {\n // Underline style subparameter\n const sub = parseInt(code.substring(colonIdx + 1))\n sgr.underline = sub > 0\n }\n // 58:5:N or 58:2:r:g:b — underline color, skip (we don't track underline color in styled cells)\n i++\n continue\n }\n\n const n = parseInt(code)\n if (n === 0) {\n sgr.fg = null\n sgr.bg = null\n sgr.bold = false\n sgr.dim = false\n sgr.italic = false\n sgr.underline = false\n sgr.blink = false\n sgr.inverse = false\n sgr.hidden = false\n sgr.strikethrough = false\n } else if (n === 1) {\n sgr.bold = true\n } else if (n === 2) {\n sgr.dim = true\n } else if (n === 3) {\n sgr.italic = true\n } else if (n === 4) {\n sgr.underline = true\n } else if (n === 5 || n === 6) {\n sgr.blink = true\n } else if (n === 7) {\n sgr.inverse = true\n } else if (n === 8) {\n sgr.hidden = true\n } else if (n === 9) {\n sgr.strikethrough = true\n } else if (n === 21) {\n // Double underline — we simplify to underline=true\n sgr.underline = true\n } else if (n === 22) {\n sgr.bold = false\n sgr.dim = false\n } else if (n === 23) {\n sgr.italic = false\n } else if (n === 24) {\n sgr.underline = false\n } else if (n === 25) {\n sgr.blink = false\n } else if (n === 27) {\n sgr.inverse = false\n } else if (n === 28) {\n sgr.hidden = false\n } else if (n === 29) {\n sgr.strikethrough = false\n } else if (n >= 30 && n <= 37) {\n sgr.fg = n - 30\n } else if (n === 38) {\n // Extended fg color\n if (parts[i + 1] === \"5\") {\n sgr.fg = parseInt(parts[i + 2]!)\n i += 2\n } else if (parts[i + 1] === \"2\") {\n sgr.fg = {\n r: parseInt(parts[i + 2]!),\n g: parseInt(parts[i + 3]!),\n b: parseInt(parts[i + 4]!),\n }\n i += 4\n }\n } else if (n === 39) {\n sgr.fg = null\n } else if (n >= 40 && n <= 47) {\n sgr.bg = n - 40\n } else if (n === 48) {\n // Extended bg color\n if (parts[i + 1] === \"5\") {\n sgr.bg = parseInt(parts[i + 2]!)\n i += 2\n } else if (parts[i + 1] === \"2\") {\n sgr.bg = {\n r: parseInt(parts[i + 2]!),\n g: parseInt(parts[i + 3]!),\n b: parseInt(parts[i + 4]!),\n }\n i += 4\n }\n } else if (n === 49) {\n sgr.bg = null\n } else if (n === 58) {\n // Underline color — semicolon-form (legacy): \"58;5;N\" (256-color) or\n // \"58;2;r;g;b\" (RGB). The colon-form (\"58:5:N\", \"58:2:r:g:b\") is handled\n // by the colon-detection branch above. We don't track underline color in\n // styled cells, but we MUST consume the sub-parameters here — otherwise\n // the next param (\"2\" or \"5\") will be re-interpreted as standalone SGR\n // 2 (dim) or 5 (blink), corrupting the cell style. The bug surfaced as\n // STRICT_OUTPUT mismatches with `dim: true vs false` at border cells\n // following links/tag refs that emit `58;2;r;g;b` underline color.\n if (parts[i + 1] === \"5\") {\n i += 2\n } else if (parts[i + 1] === \"2\") {\n i += 4\n }\n } else if (n === 59) {\n // Default underline color — no sub-params, nothing to track.\n } else if (n >= 90 && n <= 97) {\n // Bright fg\n sgr.fg = n - 90 + 8\n } else if (n >= 100 && n <= 107) {\n // Bright bg\n sgr.bg = n - 100 + 8\n }\n i++\n }\n}\n\n/**\n * Replay ANSI output into a virtual terminal grid with style tracking.\n *\n * Returns a 2D array [y][x] of StyledCells. This replays the same ANSI\n * escape sequences that outputPhase() produces (CUP, SGR, EL, text) and\n * tracks the resulting character + style at each position.\n *\n * Used by SILVERY_STRICT_TERMINAL (vt100 backend) to verify that incremental\n * output produces the same visual + style result as a fresh full render.\n *\n * @deprecated Use `SILVERY_STRICT_TERMINAL=vt100` instead. This function is now\n * the internal implementation of the `vt100` backend for STRICT_TERMINAL verification.\n * Direct usage is discouraged — prefer the unified STRICT_TERMINAL env var interface.\n *\n * Public-export removal tracked: km-silvery.unexport-replay-ansi-with-styles\n * (function remains internal to output-verify.ts; only the public re-export is removed)\n */\nexport function replayAnsiWithStyles(\n width: number,\n height: number,\n ansi: string,\n ctx: OutputContext = _defaultCtxForReplay,\n): StyledCell[][] {\n const screen: StyledCell[][] = Array.from({ length: height }, () =>\n Array.from({ length: width }, () => createDefaultStyledCell()),\n )\n let cx = 0\n let cy = 0\n let scrollTop = 0\n let scrollBottom = height - 1\n let pendingWrap = false // VT100 pending wrap flag\n const sgr = createDefaultSgr()\n let i = 0\n\n while (i < ansi.length) {\n if (ansi[i] === \"\\x1b\") {\n if (ansi[i + 1] === \"[\") {\n i += 2\n let params = \"\"\n while (\n i < ansi.length &&\n ((ansi[i]! >= \"0\" && ansi[i]! <= \"9\") ||\n ansi[i] === \";\" ||\n ansi[i] === \"?\" ||\n ansi[i] === \":\")\n ) {\n params += ansi[i]\n i++\n }\n const cmd = ansi[i]\n i++\n if (cmd === \"H\") {\n // CUP clears pending wrap\n pendingWrap = false\n if (params === \"\") {\n cx = 0\n cy = 0\n } else {\n const cmdParts = params.split(\";\")\n // Clamp to screen bounds (real terminals clamp CUP to valid range)\n cy = Math.min(height - 1, Math.max(0, (parseInt(cmdParts[0]!) || 1) - 1))\n cx = Math.min(width - 1, Math.max(0, (parseInt(cmdParts[1]!) || 1) - 1))\n }\n } else if (cmd === \"K\") {\n // Erase to end of line — fills with current bg (or default)\n if (cy >= height) continue // out of bounds — skip\n for (let x = cx; x < width; x++) {\n const cell = screen[cy]![x]!\n cell.char = \" \"\n cell.fg = null\n cell.bg = sgr.bg\n cell.bold = false\n cell.dim = false\n cell.italic = false\n cell.underline = false\n cell.blink = false\n cell.inverse = false\n cell.hidden = false\n cell.strikethrough = false\n }\n } else if (cmd === \"J\") {\n // Erase from cursor to end of screen\n // Clear from cursor to end of current line\n if (cy < height) {\n for (let x = cx; x < width; x++) {\n const cell = screen[cy]![x]!\n cell.char = \" \"\n cell.fg = null\n cell.bg = sgr.bg\n cell.bold = false\n cell.dim = false\n cell.italic = false\n cell.underline = false\n cell.blink = false\n cell.inverse = false\n cell.hidden = false\n cell.strikethrough = false\n }\n }\n // Clear all subsequent lines\n for (let y = cy + 1; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const cell = screen[y]![x]!\n cell.char = \" \"\n cell.fg = null\n cell.bg = sgr.bg\n cell.bold = false\n cell.dim = false\n cell.italic = false\n cell.underline = false\n cell.blink = false\n cell.inverse = false\n cell.hidden = false\n cell.strikethrough = false\n }\n }\n } else if (cmd === \"m\") {\n applySgrParams(params, sgr)\n } else if (cmd === \"C\") {\n // Cursor forward — clears pending wrap\n pendingWrap = false\n const n = parseInt(params) || 1\n cx = Math.min(width - 1, cx + n)\n } else if (cmd === \"A\") {\n // Cursor up — clears pending wrap\n pendingWrap = false\n const n = parseInt(params) || 1\n cy = Math.max(0, cy - n)\n } else if (cmd === \"B\") {\n // Cursor down — clears pending wrap\n pendingWrap = false\n const n = parseInt(params) || 1\n cy = Math.min(height - 1, cy + n)\n } else if (cmd === \"r\") {\n // DECSTBM scrolling region (1-based inclusive). Empty params reset.\n pendingWrap = false\n if (params === \"\") {\n scrollTop = 0\n scrollBottom = height - 1\n } else {\n const [topParam, bottomParam] = params.split(\";\")\n const top = Math.max(0, (parseInt(topParam ?? \"1\") || 1) - 1)\n const bottom = Math.min(\n height - 1,\n (parseInt(bottomParam ?? String(height)) || height) - 1,\n )\n if (top < bottom) {\n scrollTop = top\n scrollBottom = bottom\n }\n }\n cx = 0\n cy = 0\n } else if (cmd === \"S\" || cmd === \"T\") {\n // SU / SD: scroll the active region up/down by N rows.\n pendingWrap = false\n const n = Math.min(parseInt(params) || 1, scrollBottom - scrollTop + 1)\n if (cmd === \"S\") {\n for (let y = scrollTop; y <= scrollBottom - n; y++) {\n screen[y] = screen[y + n]!.map(cloneStyledCell)\n }\n for (let y = scrollBottom - n + 1; y <= scrollBottom; y++) {\n screen[y] = Array.from({ length: width }, () => createDefaultStyledCell())\n }\n } else {\n for (let y = scrollBottom; y >= scrollTop + n; y--) {\n screen[y] = screen[y - n]!.map(cloneStyledCell)\n }\n for (let y = scrollTop; y < scrollTop + n; y++) {\n screen[y] = Array.from({ length: width }, () => createDefaultStyledCell())\n }\n }\n } else if (cmd === \"l\" || cmd === \"h\") {\n // Private mode set/reset (e.g., ?25l = hide cursor, ?25h = show cursor)\n // Skip — we don't model cursor visibility\n }\n continue\n } else if (ansi[i + 1] === \"]\") {\n // OSC sequence — skip until ST (BEL or ESC\\\\)\n i += 2\n while (i < ansi.length) {\n if (ansi[i] === \"\\x07\") {\n i++\n break\n }\n if (ansi[i] === \"\\x1b\" && ansi[i + 1] === \"\\\\\") {\n i += 2\n break\n }\n i++\n }\n continue\n }\n // Unknown escape — skip the ESC and next char\n i += 2\n continue\n }\n\n if (ansi[i] === \"\\r\") {\n // CR clears pending wrap, moves to column 0\n pendingWrap = false\n cx = 0\n i++\n continue\n }\n\n if (ansi[i] === \"\\n\") {\n // LF clears pending wrap and moves down one row\n pendingWrap = false\n cy++\n if (cy >= height) {\n // Scroll: shift all rows up by 1\n const first = screen.shift()!\n // Reset the row being recycled\n for (const cell of first) {\n cell.char = \" \"\n cell.fg = null\n cell.bg = null\n cell.bold = false\n cell.dim = false\n cell.italic = false\n cell.underline = false\n cell.blink = false\n cell.inverse = false\n cell.hidden = false\n cell.strikethrough = false\n }\n screen.push(first)\n cy = height - 1\n }\n i++\n continue\n }\n\n // Regular character — collect full grapheme cluster.\n //\n // A grapheme cluster may span multiple UTF-16 code units:\n // - Surrogate pairs for astral codepoints (e.g., emoji U+1F600)\n // - ZWJ sequences (e.g., 🏃‍♂️ = U+1F3C3 U+200D U+2642 U+FE0F)\n // - Variation selectors (U+FE0E text, U+FE0F emoji presentation)\n // - Combining marks, skin tone modifiers, regional indicators\n //\n // Strategy: extract the first codepoint (handling surrogates), then\n // greedily absorb subsequent zero-width joiners, variation selectors,\n // combining marks, and their targets into a single grapheme string.\n // This matches terminal behavior where the entire cluster occupies\n // the column count of the base character.\n let grapheme = \"\"\n let advance = 0\n // First-codepoint kind tracking — required for UAX #29 GB12/GB13\n // (regional indicators only pair with another RI; they MUST NOT extend\n // a non-RI base character). Also used to gate variation-selector and\n // skin-tone modifier absorption to graphemes that started with a\n // codepoint capable of taking that modifier — otherwise these can\n // glue onto e.g. an ASCII space and form a 3-column \"space + flag\"\n // cluster, which then writes 2 cells over the wrong columns and\n // leaves stale prev pixels at cx+1 and cx+2.\n let firstCpIsRegionalIndicator = false\n let firstCpIsEmojiBase = false\n\n // Extract the initial codepoint\n let firstCp: number\n const code0 = ansi.charCodeAt(i)\n if (code0 >= 0xd800 && code0 <= 0xdbff && i + 1 < ansi.length) {\n const low = ansi.charCodeAt(i + 1)\n if (low >= 0xdc00 && low <= 0xdfff) {\n grapheme = ansi[i]! + ansi[i + 1]!\n advance = 2\n firstCp = ((code0 - 0xd800) << 10) + (low - 0xdc00) + 0x10000\n } else {\n grapheme = ansi[i]!\n advance = 1\n firstCp = code0\n }\n } else {\n grapheme = ansi[i]!\n advance = 1\n firstCp = code0\n }\n firstCpIsRegionalIndicator = firstCp >= 0x1f1e6 && firstCp <= 0x1f1ff\n // Conservative emoji-base set: emoji-presentation codepoints plus\n // text-presentation emoji such as ↔, ™, and ❤️ bases. Skin-tone /\n // VS-16 only meaningfully extend these.\n if (firstCp >= 0x1f000) {\n firstCpIsEmojiBase = true\n } else if (firstCp >= 0x2600 && firstCp <= 0x27bf) {\n // Misc Symbols + Dingbats (☀, ✈, etc.)\n firstCpIsEmojiBase = true\n } else if (isTextPresentationEmoji(grapheme)) {\n firstCpIsEmojiBase = true\n }\n\n // Absorb combining/extending codepoints that follow:\n // ZWJ (U+200D), variation selectors (U+FE0E-FE0F), combining marks\n // (U+0300-036F, U+20D0-20FF, U+1AB0-1AFF, U+FE20-FE2F), skin tone\n // modifiers (U+1F3FB-1F3FF), regional indicators (U+1F1E6-1F1FF —\n // GATED: only when the first codepoint is itself an RI, and at most\n // ONE more RI per UAX #29 GB12/GB13), enclosing keycap (U+20E3), and\n // tag sequences (U+E0020-E007F).\n {\n let j = i + advance\n let absorbedRiCount = 0\n while (j < ansi.length) {\n const c = ansi.charCodeAt(j)\n // Decode the codepoint at j (handle surrogates)\n let cp: number\n let cpLen: number\n if (c >= 0xd800 && c <= 0xdbff && j + 1 < ansi.length) {\n const lo = ansi.charCodeAt(j + 1)\n if (lo >= 0xdc00 && lo <= 0xdfff) {\n cp = ((c - 0xd800) << 10) + (lo - 0xdc00) + 0x10000\n cpLen = 2\n } else {\n break\n }\n } else {\n cp = c\n cpLen = 1\n }\n\n // Truly-zero-width modifiers — always safe to absorb. These do\n // not start new graphemes regardless of base.\n const isCombiningMark =\n (cp >= 0x0300 && cp <= 0x036f) || // Combining diacritical marks\n (cp >= 0x20d0 && cp <= 0x20ff) || // Combining marks for symbols\n (cp >= 0x1ab0 && cp <= 0x1aff) || // Combining diacritical extended\n (cp >= 0xfe20 && cp <= 0xfe2f) || // Combining half marks\n cp === 0x20e3 // Combining enclosing keycap\n if (cp === 0x200d || isCombiningMark) {\n grapheme += ansi.slice(j, j + cpLen)\n advance += cpLen\n j += cpLen\n continue\n }\n\n // Variation selectors and skin-tone modifiers only extend an\n // emoji-capable base. Without this gate, U+FE0F glues onto\n // arbitrary preceding ASCII and inflates grapheme width.\n if (cp === 0xfe0e || cp === 0xfe0f) {\n if (firstCpIsEmojiBase) {\n grapheme += ansi.slice(j, j + cpLen)\n advance += cpLen\n j += cpLen\n continue\n }\n break\n }\n if (cp >= 0x1f3fb && cp <= 0x1f3ff) {\n if (firstCpIsEmojiBase) {\n grapheme += ansi.slice(j, j + cpLen)\n advance += cpLen\n j += cpLen\n continue\n }\n break\n }\n\n // Tag sequences — only after an emoji base.\n if ((cp >= 0xe0020 && cp <= 0xe007f) || cp === 0xe0001) {\n if (firstCpIsEmojiBase) {\n grapheme += ansi.slice(j, j + cpLen)\n advance += cpLen\n j += cpLen\n continue\n }\n break\n }\n\n // Regional indicators — UAX #29 GB12/GB13. An RI may extend a\n // grapheme ONLY when the grapheme started with an RI AND no\n // additional RI has been absorbed yet (a flag is exactly two\n // RIs; a third RI begins a new flag). This was the source of the\n // \"🇺🇸 written at col N renders at col N-1\" bug: previously, any\n // RI absorbed onto whatever preceded it (e.g., ' ' + RI + RI →\n // grapheme \" 🇺🇸\" of width 3, smearing one column to the left).\n if (cp >= 0x1f1e6 && cp <= 0x1f1ff) {\n if (firstCpIsRegionalIndicator && absorbedRiCount === 0) {\n grapheme += ansi.slice(j, j + cpLen)\n advance += cpLen\n j += cpLen\n absorbedRiCount++\n continue\n }\n break\n }\n\n // After ZWJ, absorb the next codepoint (the ZWJ target)\n const prevCp =\n grapheme.length >= 2\n ? grapheme.charCodeAt(grapheme.length - 2) === 0x200d\n ? 0x200d\n : grapheme.charCodeAt(grapheme.length - 1)\n : grapheme.charCodeAt(grapheme.length - 1)\n if (prevCp === 0x200d && cp > 0x20) {\n // ZWJ target: absorb any codepoint after ZWJ\n grapheme += ansi.slice(j, j + cpLen)\n advance += cpLen\n j += cpLen\n continue\n }\n\n break\n }\n }\n\n const charWidth = graphemeWidth(grapheme)\n\n if (cy < height && cx < width) {\n // Resolve pending wrap before writing\n if (pendingWrap) {\n pendingWrap = false\n cx = 0\n cy++\n if (cy >= height) {\n // Scroll: shift all rows up by 1\n const first = screen.shift()!\n for (const cell of first) {\n cell.char = \" \"\n cell.fg = null\n cell.bg = null\n cell.bold = false\n cell.dim = false\n cell.italic = false\n cell.underline = false\n cell.blink = false\n cell.inverse = false\n cell.hidden = false\n cell.strikethrough = false\n }\n screen.push(first)\n cy = height - 1\n }\n }\n if (cy < height && cx < width) {\n const cell = screen[cy]![cx]!\n cell.char = grapheme\n cell.fg = sgr.fg\n cell.bg = sgr.bg\n cell.bold = sgr.bold\n cell.dim = sgr.dim\n cell.italic = sgr.italic\n cell.underline = sgr.underline\n cell.blink = sgr.blink\n cell.inverse = sgr.inverse\n cell.hidden = sgr.hidden\n cell.strikethrough = sgr.strikethrough\n\n // Wide character: clear the continuation cell at cx+1 (terminals\n // implicitly overwrite the second column when writing a wide char)\n if (charWidth === 2 && cx + 1 < width) {\n const cont = screen[cy]![cx + 1]!\n cont.char = \" \"\n cont.fg = sgr.fg\n cont.bg = sgr.bg\n cont.bold = sgr.bold\n cont.dim = sgr.dim\n cont.italic = sgr.italic\n cont.underline = sgr.underline\n cont.blink = sgr.blink\n cont.inverse = sgr.inverse\n cont.hidden = sgr.hidden\n cont.strikethrough = sgr.strikethrough\n }\n\n cx += charWidth\n if (cx >= width) {\n // Enter pending wrap state — cursor stays at last column\n cx = width - 1\n pendingWrap = true\n }\n }\n }\n i += advance\n }\n return screen\n}\n\n// Default context for replayAnsiWithStyles when called without ctx (by tests).\nconst _defaultCtxForReplay: OutputContext = {\n caps: {\n // Full per-style support — tests render at truecolor with all styles\n // available. Narrower caps are exercised via createOutputPhase({...}).\n underlineStyles: [\"single\", \"double\", \"curly\", \"dotted\", \"dashed\"],\n underlineColor: true,\n overline: true,\n colorLevel: \"truecolor\",\n },\n measurer: null,\n sgrCache: new Map(),\n transitionCache: new Map(),\n mode: \"fullscreen\",\n termRows: undefined,\n}\n\n/** Format a color value for display. */\nexport function formatColor(c: number | { r: number; g: number; b: number } | null): string {\n if (c === null) return \"default\"\n if (typeof c === \"number\") return `${c}`\n return `rgb(${c.r},${c.g},${c.b})`\n}\n\n// =============================================================================\n// STRICT failure artifact capture\n// =============================================================================\n\n/**\n * Capture debug artifacts to disk when a STRICT verification fails.\n * Saves prev/next buffer snapshots, ANSI sequences, terminal size, and test context\n * to /tmp/silvery-strict-failure-<timestamp>/.\n *\n * Returns the artifact directory path (included in the error message).\n */\nexport function captureStrictFailureArtifacts(opts: {\n source: string\n errorMessage: string\n prev?: TerminalBuffer | null\n next?: TerminalBuffer | null\n incrOutput?: string\n freshOutput?: string\n ctx?: OutputContext\n frameCount?: number\n /** Optional bufferToAnsi function for generating fresh-prev.ansi. */\n renderFull?: BufferToAnsiFn\n}): string {\n try {\n // sync-node-builtin: multi-target lazy guard via getBuiltinModule (ESM-only wave-3).\n // captureStrictFailureArtifacts is a synchronous diagnostic (returns the artifact\n // dir for the thrown error message), so it can't await import(); getBuiltinModule\n // keeps node:fs/node:path out of browser/canvas bundles while staying sync.\n const fs = process.getBuiltinModule(\"node:fs\")\n const path = process.getBuiltinModule(\"node:path\")\n const timestamp = Date.now()\n const dir = `/tmp/silvery-strict-failure-${timestamp}`\n fs.mkdirSync(dir, { recursive: true })\n\n // Metadata\n const meta: Record<string, unknown> = {\n source: opts.source,\n timestamp: new Date().toISOString(),\n frameCount: opts.frameCount,\n prevSize: opts.prev ? { width: opts.prev.width, height: opts.prev.height } : null,\n nextSize: opts.next ? { width: opts.next.width, height: opts.next.height } : null,\n incrOutputLength: opts.incrOutput?.length,\n freshOutputLength: opts.freshOutput?.length,\n testName: (globalThis as any).__vitest_worker__?.current?.name as string | undefined,\n }\n fs.writeFileSync(path.join(dir, \"meta.json\"), JSON.stringify(meta, null, 2))\n\n // Error message\n fs.writeFileSync(path.join(dir, \"error.txt\"), opts.errorMessage)\n\n // ANSI sequences\n if (opts.incrOutput) {\n fs.writeFileSync(path.join(dir, \"incremental.ansi\"), opts.incrOutput)\n }\n if (opts.freshOutput) {\n fs.writeFileSync(path.join(dir, \"fresh.ansi\"), opts.freshOutput)\n }\n\n // Buffer snapshots (text representation)\n if (opts.prev) {\n const rows: string[] = []\n for (let y = 0; y < opts.prev.height; y++) {\n let row = \"\"\n for (let x = 0; x < opts.prev.width; x++) {\n const cell = opts.prev.getCell(x, y)\n row += cell.char || \" \"\n }\n rows.push(row.trimEnd())\n }\n fs.writeFileSync(path.join(dir, \"prev-buffer.txt\"), rows.join(\"\\n\"))\n }\n\n if (opts.next) {\n const rows: string[] = []\n for (let y = 0; y < opts.next.height; y++) {\n let row = \"\"\n for (let x = 0; x < opts.next.width; x++) {\n const cell = opts.next.getCell(x, y)\n row += cell.char || \" \"\n }\n rows.push(row.trimEnd())\n }\n fs.writeFileSync(path.join(dir, \"next-buffer.txt\"), rows.join(\"\\n\"))\n }\n\n // Fresh prev ANSI (for replay)\n if (opts.prev && opts.ctx && opts.renderFull) {\n const freshPrev = opts.renderFull(opts.prev, opts.ctx)\n fs.writeFileSync(path.join(dir, \"fresh-prev.ansi\"), freshPrev)\n }\n\n return dir\n } catch {\n return \"(artifact capture failed)\"\n }\n}\n\n// =============================================================================\n// Output Equivalence Verification (vt100 backend)\n// =============================================================================\n\n/**\n * Verify that applying changesToAnsi output to a previous terminal state\n * produces the same visible characters AND styles as a fresh render of the\n * next buffer. Throws on mismatch.\n *\n * @param prev Previous terminal buffer\n * @param next Next terminal buffer\n * @param incrOutput Incremental ANSI output from changesToAnsi\n * @param ctx Output context\n * @param renderFull Function to convert a buffer to full ANSI output\n * @param graphemeWidthFn Function to get grapheme width\n * @param textSizingEnabledFn Function to check if text sizing is enabled\n */\nexport function verifyOutputEquivalence(\n prev: TerminalBuffer,\n next: TerminalBuffer,\n incrOutput: string,\n ctx: OutputContext,\n renderFull: BufferToAnsiFn,\n graphemeWidthFn: OutputGraphemeWidthFn,\n textSizingEnabledFn: OutputTextSizingEnabledFn,\n): void {\n const w = Math.max(prev.width, next.width)\n // VT height must accommodate the larger buffer to prevent scrolling artifacts\n // when prev is taller than next (e.g., items removed from a scrollback list).\n // We only compare up to next.height rows — excess rows should be cleared.\n const vtHeight = Math.max(prev.height, next.height)\n // DEBUG: log buffer dimensions\n if (DEBUG_OUTPUT) {\n log.error?.(\n `[VERIFY] prev=${prev.width}x${prev.height} next=${next.width}x${next.height} vtSize=${w}x${vtHeight}`,\n )\n }\n // Replay: fresh prev render + incremental diff applied on top\n const freshPrev = renderFull(prev, ctx)\n if (DEBUG_OUTPUT) {\n log.error?.(`[VERIFY] freshPrev len=${freshPrev.length} incrOutput len=${incrOutput.length}`)\n // Show incrOutput as escaped string\n const escaped = incrOutput.replace(/\\x1b/g, \"\\\\e\").replace(/\\r/g, \"\\\\r\").replace(/\\n/g, \"\\\\n\")\n log.error?.(`[VERIFY] incrOutput: ${escaped.slice(0, 500)}`)\n }\n const screenIncr = replayAnsiWithStyles(w, vtHeight, freshPrev + incrOutput, ctx)\n // Replay: fresh render of next buffer\n const freshNext = renderFull(next, ctx)\n const screenFresh = replayAnsiWithStyles(w, vtHeight, freshNext, ctx)\n\n const _dumpRowWideCells = (buf: TerminalBuffer, row: number): string => {\n const parts: string[] = []\n for (let cx = 0; cx < buf.width; cx++) {\n const c = buf.getCell(cx, row)\n const cp = c.char\n ? [...c.char]\n .map(\n (ch) => \"U+\" + (ch.codePointAt(0) ?? 0).toString(16).toUpperCase().padStart(4, \"0\"),\n )\n .join(\",\")\n : \"empty\"\n if (c.wide) parts.push(`W@${cx}:${cp}(gw=${graphemeWidthFn(c.char, ctx)})`)\n if (c.continuation) parts.push(`C@${cx}`)\n // Flag cells where written char width differs from buffer expectation\n const charToWrite = c.char || \" \"\n const vtWidth = graphemeWidthFn(charToWrite, ctx)\n const bufWidth = c.wide ? 2 : 1\n if (!c.continuation && vtWidth !== bufWidth) {\n parts.push(\n `MISMATCH@${cx}:${cp}(vtW=${vtWidth},bufW=${bufWidth},tse=${textSizingEnabledFn(ctx)})`,\n )\n }\n }\n return parts.join(\" \")\n }\n\n // Compare character by character AND style by style.\n // Use vtHeight (not compareHeight) to catch stale rows after height shrink.\n // When prev.height > next.height, stale rows beyond next.height must be\n // verified as cleared — otherwise incremental output silently diverges.\n for (let y = 0; y < vtHeight; y++) {\n for (let x = 0; x < w; x++) {\n const incr = screenIncr[y]![x]!\n const fresh = screenFresh[y]![x]!\n\n // Check character\n if (incr.char !== fresh.char) {\n // Build context: show the row from both renders\n const incrRow = screenIncr[y]!.map((c) => c.char).join(\"\")\n const freshRow = screenFresh[y]!.map((c) => c.char).join(\"\")\n // Also show the prev buffer row for diagnosis\n const prevRow = screenIncr[y]!.map((_, cx) => {\n const prevCell = prev.getCell(cx, y)\n return prevCell.char\n }).join(\"\")\n // Show what changesToAnsi tried to write at this position\n const nextCell = next.getCell(x, y)\n const prevCell = prev.getCell(x, y)\n // Show detailed column-by-column comparison around the mismatch\n const contextStart = Math.max(0, x - 5)\n const contextEnd = Math.min(w, x + 10)\n const colDetails: string[] = []\n for (let cx = contextStart; cx < contextEnd; cx++) {\n const ic = screenIncr[y]![cx]!\n const fc = screenFresh[y]![cx]!\n const pc = prev.getCell(cx, y)\n const nc = next.getCell(cx, y)\n const marker = cx === x ? \" <<<\" : ic.char !== fc.char ? \" !!!\" : \"\"\n colDetails.push(\n ` col ${cx}: prev='${pc.char}'(w=${pc.wide},c=${pc.continuation}) next='${nc.char}' incr='${ic.char}' fresh='${fc.char}' wide=${nc.wide} cont=${nc.continuation}${marker}`,\n )\n }\n const msg =\n `STRICT_OUTPUT char mismatch at (${x},${y}): ` +\n `incremental='${incr.char}' fresh='${fresh.char}'\\n` +\n ` prev buffer cell: char='${prevCell.char}' bg=${prevCell.bg} wide=${prevCell.wide} cont=${prevCell.continuation}\\n` +\n ` next buffer cell: char='${nextCell.char}' bg=${nextCell.bg} wide=${nextCell.wide} cont=${nextCell.continuation}\\n` +\n ` incr row: ${incrRow}\\n` +\n ` fresh row: ${freshRow}\\n` +\n ` prev row: ${prevRow}\\n` +\n `Wide/cont cells on row ${y} (next buffer): ${_dumpRowWideCells(next, y)}\\n` +\n `Wide/cont cells on row ${y} (prev buffer): ${_dumpRowWideCells(prev, y)}\\n` +\n `Column detail around mismatch:\\n${colDetails.join(\"\\n\")}`\n const artifactDir = captureStrictFailureArtifacts({\n source: \"STRICT_OUTPUT\",\n errorMessage: msg,\n prev,\n next,\n incrOutput,\n freshOutput: freshNext,\n ctx,\n renderFull,\n })\n const fullMsg = `${msg}\\n Artifacts: ${artifactDir}`\n log.error?.(fullMsg)\n throw new IncrementalRenderMismatchError(fullMsg)\n }\n\n // Check styles\n const diffs: string[] = []\n if (!sgrColorEquals(incr.fg, fresh.fg))\n diffs.push(`fg: ${formatColor(incr.fg)} vs ${formatColor(fresh.fg)}`)\n if (!sgrColorEquals(incr.bg, fresh.bg))\n diffs.push(`bg: ${formatColor(incr.bg)} vs ${formatColor(fresh.bg)}`)\n if (incr.bold !== fresh.bold) diffs.push(`bold: ${incr.bold} vs ${fresh.bold}`)\n if (incr.dim !== fresh.dim) diffs.push(`dim: ${incr.dim} vs ${fresh.dim}`)\n if (incr.italic !== fresh.italic) diffs.push(`italic: ${incr.italic} vs ${fresh.italic}`)\n if (incr.underline !== fresh.underline)\n diffs.push(`underline: ${incr.underline} vs ${fresh.underline}`)\n if (incr.inverse !== fresh.inverse) diffs.push(`inverse: ${incr.inverse} vs ${fresh.inverse}`)\n if (incr.strikethrough !== fresh.strikethrough)\n diffs.push(`strikethrough: ${incr.strikethrough} vs ${fresh.strikethrough}`)\n\n if (diffs.length > 0) {\n const msg =\n `STRICT_OUTPUT style mismatch at (${x},${y}) char='${incr.char}': ` +\n diffs.join(\", \") +\n `\\n incremental: fg=${formatColor(incr.fg)} bg=${formatColor(incr.bg)} bold=${incr.bold} dim=${incr.dim}` +\n `\\n fresh: fg=${formatColor(fresh.fg)} bg=${formatColor(fresh.bg)} bold=${fresh.bold} dim=${fresh.dim}`\n const artifactDir2 = captureStrictFailureArtifacts({\n source: \"STRICT_OUTPUT\",\n errorMessage: msg,\n prev,\n next,\n incrOutput,\n freshOutput: freshNext,\n ctx,\n renderFull,\n })\n throw new IncrementalRenderMismatchError(`${msg}\\n Artifacts: ${artifactDir2}`)\n }\n }\n }\n}\n\n// =============================================================================\n// Accumulated Output Verification\n// =============================================================================\n\n/**\n * Verify that the accumulated output from all frames produces the same\n * terminal state as a fresh render of the current buffer.\n * Catches compounding errors across multiple render frames.\n */\nexport function verifyAccumulatedOutput(\n currentBuffer: TerminalBuffer,\n ctx: OutputContext,\n accState: AccumulateState,\n renderFull: BufferToAnsiFn,\n): void {\n const w = accState.accumulateWidth\n const h = accState.accumulateHeight\n // Replay all accumulated output (first render + all incremental updates)\n const screenAccumulated = replayAnsiWithStyles(w, h, accState.accumulatedAnsi, ctx)\n // Replay fresh render of current buffer\n const freshOutput = renderFull(currentBuffer, ctx)\n const screenFresh = replayAnsiWithStyles(w, h, freshOutput, ctx)\n\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < w; x++) {\n const accum = screenAccumulated[y]![x]!\n const fresh = screenFresh[y]![x]!\n\n if (accum.char !== fresh.char) {\n const msg =\n `SILVERY_STRICT_ACCUMULATE char mismatch at (${x},${y}) after ${accState.accumulateFrameCount} frames: ` +\n `accumulated='${accum.char}' fresh='${fresh.char}'`\n const dir = captureStrictFailureArtifacts({\n source: \"STRICT_ACCUMULATE\",\n errorMessage: msg,\n next: currentBuffer,\n incrOutput: accState.accumulatedAnsi,\n freshOutput,\n ctx,\n frameCount: accState.accumulateFrameCount,\n renderFull,\n })\n log.error?.(`${msg}\\n Artifacts: ${dir}`)\n throw new IncrementalRenderMismatchError(`${msg}\\n Artifacts: ${dir}`)\n }\n\n const diffs: string[] = []\n if (!sgrColorEquals(accum.fg, fresh.fg))\n diffs.push(`fg: ${formatColor(accum.fg)} vs ${formatColor(fresh.fg)}`)\n if (!sgrColorEquals(accum.bg, fresh.bg))\n diffs.push(`bg: ${formatColor(accum.bg)} vs ${formatColor(fresh.bg)}`)\n if (accum.bold !== fresh.bold) diffs.push(`bold: ${accum.bold} vs ${fresh.bold}`)\n if (accum.dim !== fresh.dim) diffs.push(`dim: ${accum.dim} vs ${fresh.dim}`)\n if (accum.italic !== fresh.italic) diffs.push(`italic: ${accum.italic} vs ${fresh.italic}`)\n if (accum.underline !== fresh.underline)\n diffs.push(`underline: ${accum.underline} vs ${fresh.underline}`)\n if (accum.inverse !== fresh.inverse)\n diffs.push(`inverse: ${accum.inverse} vs ${fresh.inverse}`)\n if (accum.strikethrough !== fresh.strikethrough)\n diffs.push(`strikethrough: ${accum.strikethrough} vs ${fresh.strikethrough}`)\n\n if (diffs.length > 0) {\n const msg =\n `SILVERY_STRICT_ACCUMULATE style mismatch at (${x},${y}) char='${accum.char}' after ${accState.accumulateFrameCount} frames: ` +\n diffs.join(\", \")\n const dir2 = captureStrictFailureArtifacts({\n source: \"STRICT_ACCUMULATE\",\n errorMessage: msg,\n next: currentBuffer,\n freshOutput,\n ctx,\n frameCount: accState.accumulateFrameCount,\n renderFull,\n })\n log.error?.(`${msg}\\n Artifacts: ${dir2}`)\n throw new IncrementalRenderMismatchError(`${msg}\\n Artifacts: ${dir2}`)\n }\n }\n }\n}\n\n// =============================================================================\n// SILVERY_STRICT_TERMINAL: Independent xterm.js emulator verification\n// =============================================================================\n\nfunction loadTermless(): {\n createTerminal: typeof import(\"@termless/core\").createTerminal\n createXtermBackend: typeof import(\"@termless/xtermjs\").createXtermBackend\n} {\n // ESM-graph load (never createRequire) so this verifier shares the single\n // @termless instance with createTermless / createGhosttyBackend / the tests'\n // own imports — the 2026-07-02 Ghostty-WASM singleton-split fix. Preloaded by\n // @silvery/test's top-level await / run()'s setup; see strict-terminal-backends.ts.\n const core = getTermlessCore()\n const xtermjs = getTermlessXterm()\n return { createTerminal: core.createTerminal, createXtermBackend: xtermjs.createXtermBackend }\n}\n\nfunction loadGhosttyBackend(): typeof import(\"@termless/ghostty\").createGhosttyBackend {\n // WASM readiness (initGhostty) is a PRELOAD precondition — the ghostty tests\n // await initGhostty() in setup and the live run() path preloads with WASM, so\n // by the time this synchronous mid-frame call runs the shared sharedGhostty is\n // populated. No fire-and-forget init here (that was the null-WASM race window).\n return getTermlessGhostty().createGhosttyBackend\n}\n\n/**\n * Initialize the terminal verify state: create persistent terminal emulators\n * and feed the initial full render ANSI output.\n */\nexport function initTerminalVerifyState(\n state: TerminalVerifyState,\n width: number,\n height: number,\n initialAnsi: string,\n): void {\n // Close any existing terminals from a previous run\n if (state.terminal) void state.terminal.close()\n if (state.ghosttyTerminal) void state.ghosttyTerminal.close()\n\n // Create xterm.js terminal if requested\n if (state.backends.includes(\"xterm\")) {\n const { createTerminal, createXtermBackend } = loadTermless()\n state.terminal = createTerminal({ backend: createXtermBackend(), cols: width, rows: height })\n state.terminal.feed(initialAnsi)\n } else {\n state.terminal = null\n }\n\n // Create Ghostty terminal if requested\n if (state.backends.includes(\"ghostty\")) {\n const { createTerminal } = loadTermless()\n const createGhostty = loadGhosttyBackend()\n state.ghosttyTerminal = createTerminal({ backend: createGhostty(), cols: width, rows: height })\n state.ghosttyTerminal.feed(initialAnsi)\n } else {\n state.ghosttyTerminal = null\n }\n\n state.width = width\n state.height = height\n state.frameCount = 0\n}\n\n/**\n * Verify that the cumulative incremental ANSI output (fed through a persistent\n * xterm.js terminal) produces the same visible state as a fresh full render\n * (fed through a second, throwaway xterm.js terminal).\n *\n * This is the ONE invariant that catches both OSC 66 and buffer overflow bugs:\n * \"For a given terminal capability profile and viewport size, the actual\n * terminal state after incremental rendering must equal the actual terminal\n * state after a fresh full redraw\" — checked in an independent emulator.\n */\nexport function verifyTerminalEquivalence(\n state: TerminalVerifyState,\n incrOutput: string,\n nextBuffer: TerminalBuffer,\n ctx: OutputContext,\n renderFull: BufferToAnsiFn,\n): void {\n // Buffer dimensions may change between frames (test renderers use content-sized\n // buffers). When dimensions change, the persistent terminal can't be meaningfully\n // compared — CUP commands and scrolling behave differently at different sizes.\n // Reinitialize the persistent terminal with a fresh render at the new dimensions.\n if (nextBuffer.width !== state.width || nextBuffer.height !== state.height) {\n const freshAnsi = renderFull(nextBuffer, ctx)\n initTerminalVerifyState(state, nextBuffer.width, nextBuffer.height, freshAnsi)\n state.frameCount++\n return\n }\n\n const freshAnsi = renderFull(nextBuffer, ctx)\n\n // Verify xterm.js terminal\n if (state.terminal) {\n state.terminal.feed(incrOutput)\n const { createTerminal, createXtermBackend } = loadTermless()\n const freshTerm = createTerminal({\n backend: createXtermBackend(),\n cols: state.width,\n rows: state.height,\n })\n freshTerm.feed(freshAnsi)\n try {\n compareTerminals(state.terminal, freshTerm, state, \"xterm\")\n } catch (e) {\n if (e instanceof IncrementalRenderMismatchError) {\n const dir = captureStrictFailureArtifacts({\n source: \"STRICT_TERMINAL[xterm]\",\n errorMessage: e.message,\n next: nextBuffer,\n incrOutput,\n freshOutput: freshAnsi,\n ctx,\n frameCount: state.frameCount,\n renderFull,\n })\n throw new IncrementalRenderMismatchError(`${e.message}\\n Artifacts: ${dir}`)\n }\n throw e\n } finally {\n void freshTerm.close()\n }\n }\n\n // Verify Ghostty terminal\n if (state.ghosttyTerminal) {\n state.ghosttyTerminal.feed(incrOutput)\n const { createTerminal } = loadTermless()\n const createGhostty = loadGhosttyBackend()\n const freshTerm = createTerminal({\n backend: createGhostty(),\n cols: state.width,\n rows: state.height,\n })\n freshTerm.feed(freshAnsi)\n try {\n compareTerminals(state.ghosttyTerminal, freshTerm, state, \"ghostty\")\n } catch (e) {\n if (e instanceof IncrementalRenderMismatchError) {\n const dir = captureStrictFailureArtifacts({\n source: \"STRICT_TERMINAL[ghostty]\",\n errorMessage: e.message,\n next: nextBuffer,\n incrOutput,\n freshOutput: freshAnsi,\n ctx,\n frameCount: state.frameCount,\n renderFull,\n })\n throw new IncrementalRenderMismatchError(`${e.message}\\n Artifacts: ${dir}`)\n }\n throw e\n } finally {\n void freshTerm.close()\n }\n }\n}\n\n/** Compare two terminal states cell-by-cell. Throws IncrementalRenderMismatchError on divergence. */\nfunction compareTerminals(\n incrTerm: import(\"@termless/core\").Terminal,\n freshTerm: import(\"@termless/core\").Terminal,\n state: TerminalVerifyState,\n backendName: string,\n): void {\n const w = state.width\n const h = state.height\n const prefix = `SILVERY_STRICT_TERMINAL[${backendName}]`\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < w; x++) {\n const incrCell = incrTerm.getCell(y, x)\n const freshCell = freshTerm.getCell(y, x)\n const incrChar = incrCell.char || \" \"\n const freshChar = freshCell.char || \" \"\n\n if (incrChar !== freshChar) {\n const incrRow = Array.from(\n { length: w },\n (_, cx) => incrTerm.getCell(y, cx).char || \" \",\n ).join(\"\")\n const freshRow = Array.from(\n { length: w },\n (_, cx) => freshTerm.getCell(y, cx).char || \" \",\n ).join(\"\")\n const msg =\n `${prefix} char mismatch at (${x},${y}) frame ${state.frameCount}: ` +\n `incremental='${incrChar}' fresh='${freshChar}'\\n` +\n ` incr row: ${incrRow.trimEnd()}\\n` +\n ` fresh row: ${freshRow.trimEnd()}`\n log.error?.(msg)\n throw new IncrementalRenderMismatchError(msg)\n }\n\n if (!rgbEquals(incrCell.fg, freshCell.fg)) {\n const msg =\n `${prefix} fg color mismatch at (${x},${y}) char='${incrChar}' frame ${state.frameCount}: ` +\n `incremental=${formatRgb(incrCell.fg)} fresh=${formatRgb(freshCell.fg)}`\n log.error?.(msg)\n throw new IncrementalRenderMismatchError(msg)\n }\n\n if (!rgbEquals(incrCell.bg, freshCell.bg)) {\n const msg =\n `${prefix} bg color mismatch at (${x},${y}) char='${incrChar}' frame ${state.frameCount}: ` +\n `incremental=${formatRgb(incrCell.bg)} fresh=${formatRgb(freshCell.bg)}`\n log.error?.(msg)\n throw new IncrementalRenderMismatchError(msg)\n }\n\n const attrDiffs: string[] = []\n if (incrCell.bold !== freshCell.bold)\n attrDiffs.push(`bold: ${incrCell.bold} vs ${freshCell.bold}`)\n if (incrCell.dim !== freshCell.dim) attrDiffs.push(`dim: ${incrCell.dim} vs ${freshCell.dim}`)\n if (incrCell.italic !== freshCell.italic)\n attrDiffs.push(`italic: ${incrCell.italic} vs ${freshCell.italic}`)\n if (incrCell.inverse !== freshCell.inverse)\n attrDiffs.push(`inverse: ${incrCell.inverse} vs ${freshCell.inverse}`)\n if (incrCell.strikethrough !== freshCell.strikethrough)\n attrDiffs.push(`strikethrough: ${incrCell.strikethrough} vs ${freshCell.strikethrough}`)\n\n if (attrDiffs.length > 0) {\n const msg =\n `${prefix} attr mismatch at (${x},${y}) char='${incrChar}' frame ${state.frameCount}: ` +\n attrDiffs.join(\", \")\n log.error?.(msg)\n throw new IncrementalRenderMismatchError(msg)\n }\n }\n }\n}\n\n/** Compare two RGB color values (from termless Cell). */\nfunction rgbEquals(\n a: { r: number; g: number; b: number } | null,\n b: { r: number; g: number; b: number } | null,\n): boolean {\n if (a === b) return true\n if (a === null || b === null) return false\n return a.r === b.r && a.g === b.g && a.b === b.b\n}\n\n/** Format an RGB value for diagnostic messages. */\nfunction formatRgb(c: { r: number; g: number; b: number } | null): string {\n if (c === null) return \"null\"\n return `rgb(${c.r},${c.g},${c.b})`\n}\n\n/** Compare two SGR color values. */\nexport function sgrColorEquals(\n a: number | { r: number; g: number; b: number } | null,\n b: number | { r: number; g: number; b: number } | null,\n): boolean {\n if (a === b) return true\n if (a === null || b === null) return false\n if (typeof a === \"number\" || typeof b === \"number\") return a === b\n return a.r === b.r && a.g === b.g && a.b === b.b\n}\n","/**\n * Buffer diffing for the output phase.\n *\n * Compares two TerminalBuffers and returns a list of changed cells via a\n * pre-allocated pool (zero per-frame allocation). Handles dimension changes\n * (growth and shrink) and wide→narrow character transitions.\n */\n\nimport type { TerminalBuffer } from \"../buffer\"\nimport type { CellChange } from \"./types\"\n\n// ============================================================================\n// Pre-allocated diff pool\n// ============================================================================\n\n/**\n * Create a fresh CellChange with empty cell data.\n * Used to populate the pre-allocated pool.\n */\nfunction createEmptyCellChange(): CellChange {\n return {\n x: 0,\n y: 0,\n cell: {\n char: \" \",\n fg: null,\n bg: null,\n underlineColor: null,\n attrs: {},\n wide: false,\n continuation: false,\n hyperlink: undefined,\n },\n }\n}\n\n/** Pre-allocated pool of CellChange objects, reused across frames. */\nconst diffPool: CellChange[] = []\n\n/** Current pool capacity. */\nlet diffPoolCapacity = 0\n\n/**\n * Ensure the diff pool has at least `capacity` entries.\n * Grows the pool if needed; never shrinks.\n */\nfunction ensureDiffPoolCapacity(capacity: number): void {\n if (capacity <= diffPoolCapacity) return\n for (let i = diffPoolCapacity; i < capacity; i++) {\n diffPool.push(createEmptyCellChange())\n }\n diffPoolCapacity = capacity\n}\n\n/**\n * Write cell data from a buffer into a pre-allocated CellChange entry.\n * Uses readCellInto for zero-allocation reads.\n */\nfunction writeCellChange(change: CellChange, x: number, y: number, buffer: TerminalBuffer): void {\n change.x = x\n change.y = y\n buffer.readCellInto(x, y, change.cell)\n}\n\n/**\n * Write empty cell data into a pre-allocated CellChange entry.\n * Used for shrink regions where cells need to be cleared.\n */\nfunction writeEmptyCellChange(change: CellChange, x: number, y: number): void {\n change.x = x\n change.y = y\n const cell = change.cell\n cell.char = \" \"\n cell.fg = null\n cell.bg = null\n cell.underlineColor = null\n // Reset attrs fields\n const attrs = cell.attrs\n attrs.bold = undefined\n attrs.dim = undefined\n attrs.italic = undefined\n attrs.underline = undefined\n attrs.underlineStyle = undefined\n attrs.blink = undefined\n attrs.inverse = undefined\n attrs.hidden = undefined\n attrs.strikethrough = undefined\n attrs.overline = undefined\n cell.wide = false\n cell.continuation = false\n cell.hyperlink = undefined\n}\n\n/**\n * Diff result: pool reference + count (avoids per-frame array allocation).\n */\nexport interface DiffResult {\n pool: CellChange[]\n count: number\n}\n\n/** Reusable diff result object (avoids allocating a new one per frame). */\nconst diffResult: DiffResult = { pool: diffPool, count: 0 }\n\n/**\n * Diff two buffers and return changes via pre-allocated pool.\n *\n * Optimization: Uses a pre-allocated pool of CellChange objects to avoid\n * allocating new objects per changed cell. Uses readCellInto for\n * zero-allocation cell reads. The pool grows as needed but is reused\n * between frames. Returns a pool+count pair instead of slicing the array.\n */\nexport function diffBuffers(prev: TerminalBuffer, next: TerminalBuffer): DiffResult {\n // Ensure pool is large enough for worst case (all cells changed).\n // Wide→narrow transitions emit an extra change for the continuation cell,\n // so worst case is 1.5x (every other cell could be a wide→narrow transition).\n const cells = Math.max(prev.width, next.width) * Math.max(prev.height, next.height)\n const maxChanges = cells + (cells >> 1) // 1.5x\n ensureDiffPoolCapacity(maxChanges)\n\n let changeCount = 0\n\n // Dimension mismatch means we need to re-render everything visible\n const height = Math.min(prev.height, next.height)\n const width = Math.min(prev.width, next.width)\n\n // Use dirty row bounding box to narrow the scan range.\n // If no rows are dirty, minDirtyRow is -1 and the loop body is skipped.\n const startRow = next.minDirtyRow === -1 ? 0 : next.minDirtyRow\n const endRow = next.maxDirtyRow === -1 ? -1 : Math.min(next.maxDirtyRow, height - 1)\n\n for (let y = startRow; y <= endRow; y++) {\n // Skip individual clean rows within the bounding box\n if (!next.isRowDirty(y)) continue\n\n // Fast row-level pre-check: if all packed metadata, chars, AND Map-based\n // extras (true colors, underline colors, hyperlinks) match, skip per-cell\n // comparison entirely. This catches rows marked dirty by fill() or\n // scrollRegion() that didn't actually change content.\n // NOTE: rowExtrasEquals is essential — rowMetadataEquals only checks packed\n // flags (e.g., \"has true color fg\"), not the actual RGB values in the Maps.\n if (\n next.rowMetadataEquals(y, prev) &&\n next.rowCharsEquals(y, prev) &&\n next.rowExtrasEquals(y, prev)\n )\n continue\n\n for (let x = 0; x < width; x++) {\n // Use buffer's optimized cellEquals which compares packed metadata first\n if (!next.cellEquals(x, y, prev)) {\n writeCellChange(diffPool[changeCount]!, x, y, next)\n changeCount++\n\n // Wide char transition: when prev had a wide char and next doesn't,\n // we must also emit the continuation position (x+1) as a change.\n // The terminal's state at x+1 contains the second half of the wide\n // char, but the buffer may show x+1 as \"unchanged\" (both prev and\n // next are ' '). Without this explicit change, changesToAnsi skips\n // x+1 and the terminal retains the wide char remnant, causing\n // cursor drift.\n //\n // Skip when the next column already triggers its own change (next[x+1]\n // is itself a wide char or a continuation cell that differs from prev):\n // the normal scan at x+1 will emit it. Pushing here too would produce a\n // duplicate change at the same (x+1, y) position. changesToAnsi sorts\n // by position and processes both entries, emitting the wide char twice\n // (regression: km-silvery.wide-char-incr-render — wide char at column\n // N in prev frame, shifted to column N+1 in next frame produced\n // '🇯🇵🇯🇵' instead of ' 🇯🇵').\n if (\n x + 1 < width &&\n prev.isCellWide(x, y) &&\n !next.isCellWide(x, y) &&\n !next.isCellWide(x + 1, y) &&\n !next.isCellContinuation(x + 1, y)\n ) {\n writeCellChange(diffPool[changeCount]!, x + 1, y, next)\n changeCount++\n }\n }\n }\n }\n\n // Handle size growth: add all cells in new areas.\n // Width growth covers the right strip (x >= prev.width) for ALL rows.\n // Height growth covers the bottom strip (y >= prev.height) but only up to\n // prev.width to avoid double-counting the corner with width growth.\n const widthGrew = next.width > prev.width\n if (widthGrew) {\n for (let y = 0; y < next.height; y++) {\n for (let x = prev.width; x < next.width; x++) {\n writeCellChange(diffPool[changeCount]!, x, y, next)\n changeCount++\n }\n }\n }\n if (next.height > prev.height) {\n // When width also grew, only iterate x=0..prev.width (the rest was\n // already covered by width growth above). Otherwise iterate full width.\n const xEnd = widthGrew ? prev.width : next.width\n for (let y = prev.height; y < next.height; y++) {\n for (let x = 0; x < xEnd; x++) {\n writeCellChange(diffPool[changeCount]!, x, y, next)\n changeCount++\n }\n }\n }\n\n // Handle size shrink: clear cells in old-but-not-new areas.\n // Width shrink covers x >= next.width for the shared height.\n // Height shrink covers y >= next.height but only up to next.width when\n // width also shrank, to avoid double-counting the corner.\n const widthShrank = prev.width > next.width\n if (widthShrank) {\n for (let y = 0; y < height; y++) {\n for (let x = next.width; x < prev.width; x++) {\n writeEmptyCellChange(diffPool[changeCount]!, x, y)\n changeCount++\n }\n }\n }\n if (prev.height > next.height) {\n // When width also shrank, the corner (x >= next.width, y >= next.height)\n // was NOT covered by width shrink (which only iterates y < height =\n // min(prev.height, next.height) = next.height). So iterate full prev.width.\n for (let y = next.height; y < prev.height; y++) {\n for (let x = 0; x < prev.width; x++) {\n writeEmptyCellChange(diffPool[changeCount]!, x, y)\n changeCount++\n }\n }\n }\n\n if (changeCount > maxChanges) {\n throw new Error(\n `diffBuffers: changeCount ${changeCount} exceeds pool capacity ${maxChanges} ` +\n `(prev ${prev.width}x${prev.height}, next ${next.width}x${next.height})`,\n )\n }\n\n diffResult.pool = diffPool\n diffResult.count = changeCount\n return diffResult\n}\n","/**\n * Hybrid output emission — density analysis.\n *\n * Given a sorted dirty-cell pool, compute per-row density summaries and pick\n * the cheapest emission mode (whole-row / run-length / scatter).\n *\n * Tracking: km-silvery.known-limits.hybrid-output\n *\n * Phase 2 implementation. Phase 1 (scaffold) shipped earlier; Phase 3 will\n * wire these into output-phase.ts behind the SILVERY_HYBRID_OUTPUT feature\n * flag. Until then, this module is reachable only via direct unit tests.\n */\n\nimport type { CellChange } from \"./types\"\n\n/** Discriminator for the three emission modes defined in the design doc. */\nexport type EmissionMode = \"whole-row\" | \"run-length\" | \"scatter\"\n\n/**\n * A maximal contiguous run of dirty columns on a single row.\n *\n * `start` and `end` are inclusive column indices in the destination buffer.\n * A single-cell run has `start === end`.\n *\n * Wide characters whose main cell falls inside `[start, end]` are emitted as\n * a single unit by the run-length emitter; the continuation cell is widened\n * into the run automatically by the analyzer.\n */\nexport interface DirtyRunSpan {\n start: number\n end: number\n}\n\n/**\n * Per-row dirty summary produced by `analyzeRowDensity`. One instance per\n * row that has any dirty cells. Values are valid only until the next call\n * to `analyzeRowDensity` (the summary pool is reused across frames).\n */\nexport interface DirtyRowSummary {\n /** Row index in the destination buffer. */\n y: number\n /** Number of dirty cells on the row (wide-char continuations deduped). */\n dirty: number\n /** Leftmost dirty column on the row. */\n minX: number\n /** Rightmost dirty column on the row (inclusive). */\n maxX: number\n /** Number of maximal contiguous runs. */\n runCount: number\n /** Maximal contiguous dirty runs, ordered by `start`. */\n runs: DirtyRunSpan[]\n /** Inclusive pool index of the first cell change on this row. */\n poolStart: number\n /** Exclusive pool index of the last cell change on this row. */\n poolEnd: number\n}\n\n/**\n * Result of density analysis: a flat array of per-row summaries ordered by\n * ascending `y`. The array is module-scoped and reused across frames — do\n * not retain references beyond the current emission pass.\n */\nexport interface DensityAnalysis {\n /** One summary per dirty row. Reused across frames. */\n rows: readonly DirtyRowSummary[]\n /** Number of valid entries in `rows`. */\n rowCount: number\n}\n\n// ============================================================================\n// Module-scoped pools (zero per-frame allocation in steady state)\n// ============================================================================\n\n/** Per-row summaries — grow lazily. */\nconst summaryPool: DirtyRowSummary[] = []\n\n/**\n * Per-row run arrays. Each row owns its own `runs` array (sliced into via\n * length manipulation). The arrays themselves grow lazily; we never shrink\n * them so the JIT can keep monomorphic shapes.\n */\nfunction getOrCreateSummary(index: number): DirtyRowSummary {\n let summary = summaryPool[index]\n if (!summary) {\n summary = {\n y: -1,\n dirty: 0,\n minX: -1,\n maxX: -1,\n runCount: 0,\n runs: [],\n poolStart: -1,\n poolEnd: -1,\n }\n summaryPool[index] = summary\n }\n return summary\n}\n\n/** Cached result object — returned from every call. */\nconst cachedAnalysis: { rows: DirtyRowSummary[]; rowCount: number } = {\n rows: summaryPool,\n rowCount: 0,\n}\n\n// ============================================================================\n// Cost estimator constants — see design doc §4 of\n// hub/silvery/design/v05-layout/hybrid-output.md.\n//\n// These are byte-cost estimates for the emission paths:\n//\n// scatter: dirty * PER_CELL_SCATTER\n// runs: runCount * RUN_PREAMBLE + dirty * PER_CELL_IN_RUN\n// whole: ROW_PREAMBLE + width * PER_CELL_IN_ROW\n//\n// The scatter per-cell cost includes a cursor jump (CUP ≈ 6-8 bytes) plus a\n// short SGR transition plus the char itself, amortized to 12 bytes/cell.\n//\n// Run preamble = 1 CUP (~6 bytes) + 1 SGR transition (~4 bytes) per run\n// boundary, totaling ~10 bytes/run. Within a run the cursor auto-advances,\n// so the per-cell cost drops to ~2 bytes (char only, occasional SGR amortized).\n//\n// Whole-row preamble = 1 CUP to (y, 0) ≈ 8 bytes (slightly more than a same-\n// row CUF because it also resets the column). Per-cell-in-row mirrors the\n// run path since the cursor auto-advances and SGRs amortize across the row.\n// ============================================================================\n\n/** Per-cell amortized cost in scatter mode (CUP + SGR + char). */\nconst PER_CELL_SCATTER = 12\n/** Cost of one cursor jump + SGR transition per run preamble. */\nconst RUN_PREAMBLE = 10\n/** Per-cell cost inside a run (relies on auto-advance). */\nconst PER_CELL_IN_RUN = 2\n/** Cost of one absolute CUP to (y, 0) per whole-row preamble. */\nconst ROW_PREAMBLE = 8\n/** Per-cell cost inside a whole-row emission. */\nconst PER_CELL_IN_ROW = 2\n\n/**\n * Analyze a sorted dirty-cell pool and produce per-row summaries.\n *\n * Preconditions:\n * - `pool` is sorted by `(y, x)` ascending. The caller\n * (`sortPoolByPosition` in output-phase.ts) guarantees this.\n * - `count` is the number of valid entries in `pool`. Entries beyond\n * `count` may contain stale data and must be ignored.\n * - `width` is the destination buffer width in columns.\n *\n * Postconditions:\n * - Returns a `DensityAnalysis` with one summary per distinct `y`.\n * - Wide-char continuation cells are deduped from `dirty` but their columns\n * are still represented inside runs so the emitter can widen spans to\n * cover both halves of a wide char that straddles a run boundary.\n * - Zero per-frame allocation: summaries and runs come from module-scoped\n * pools that grow lazily.\n */\nexport function analyzeRowDensity(\n pool: readonly CellChange[],\n count: number,\n _width: number,\n): DensityAnalysis {\n cachedAnalysis.rowCount = 0\n\n if (count === 0) {\n return cachedAnalysis as DensityAnalysis\n }\n\n let rowIdx = 0\n let i = 0\n\n while (i < count) {\n const rowY = pool[i]!.y\n const summary = getOrCreateSummary(rowIdx)\n summary.y = rowY\n summary.dirty = 0\n summary.minX = pool[i]!.x\n summary.maxX = pool[i]!.x\n summary.runCount = 0\n summary.poolStart = i\n\n // Walk this row's contiguous slice in the sorted pool, building runs.\n let runStart = -1\n let runEnd = -1\n\n while (i < count && pool[i]!.y === rowY) {\n const change = pool[i]!\n const x = change.x\n const isContinuation = change.cell.continuation === true\n const isWide = change.cell.wide === true\n\n if (!isContinuation) {\n summary.dirty++\n }\n if (x < summary.minX) summary.minX = x\n // Track maxX as the rightmost column actually touched (continuations\n // count too, since the emitter must cover them).\n const rightCol = isWide ? x + 1 : x\n if (rightCol > summary.maxX) summary.maxX = rightCol\n\n // Run building. A new cell extends the current run when it is\n // adjacent to (or overlapping with) the previous run's end.\n // Wide-char continuations don't appear separately if their main cell\n // is in the pool (they share the same x+1 column the wide flag\n // already widened); for orphan continuations we still want a span\n // covering [x-1, x] so the emitter can re-emit the main cell.\n const cellStart = isContinuation ? Math.max(0, x - 1) : x\n const cellEnd = isContinuation ? x : isWide ? x + 1 : x\n\n if (runStart === -1) {\n runStart = cellStart\n runEnd = cellEnd\n } else if (cellStart <= runEnd + 1) {\n // Extend current run (adjacent or overlapping).\n if (cellEnd > runEnd) runEnd = cellEnd\n } else {\n // Close current run, open new one.\n appendRun(summary, runStart, runEnd)\n runStart = cellStart\n runEnd = cellEnd\n }\n\n i++\n }\n\n if (runStart !== -1) {\n appendRun(summary, runStart, runEnd)\n }\n\n summary.poolEnd = i\n rowIdx++\n }\n\n cachedAnalysis.rowCount = rowIdx\n return cachedAnalysis as DensityAnalysis\n}\n\n/** Append a run to a summary's run pool, growing the array lazily. */\nfunction appendRun(summary: DirtyRowSummary, start: number, end: number): void {\n const idx = summary.runCount\n const existing = summary.runs[idx]\n if (existing) {\n existing.start = start\n existing.end = end\n } else {\n summary.runs[idx] = { start, end }\n }\n summary.runCount++\n}\n\n/**\n * Pick the cheapest emission mode for a single row, given its density\n * summary and the destination buffer width.\n *\n * Uses the estimator in §4 of the design doc:\n *\n * scatterCost = dirty * PER_CELL_SCATTER\n * runCost = runCount * RUN_PREAMBLE + dirty * PER_CELL_IN_RUN\n * wholeCost = ROW_PREAMBLE + width * PER_CELL_IN_ROW\n *\n * Fast paths (no estimator):\n * - `dirty <= 2` → scatter\n * - `dirty * 2 >= width` → whole-row\n * - `runCount === 1` → run-length\n */\nexport function pickEmissionMode(row: DirtyRowSummary, width: number): EmissionMode {\n const { dirty, runCount } = row\n\n // Fast paths\n if (dirty <= 2) return \"scatter\"\n if (dirty * 2 >= width) return \"whole-row\"\n if (runCount === 1) return \"run-length\"\n\n // Estimator\n const scatterCost = dirty * PER_CELL_SCATTER\n const runCost = runCount * RUN_PREAMBLE + dirty * PER_CELL_IN_RUN\n const wholeCost = ROW_PREAMBLE + width * PER_CELL_IN_ROW\n\n // Pick the minimum. Ties favor run-length (least over-emission risk),\n // then whole-row (most predictable cursor state), then scatter.\n if (runCost <= scatterCost && runCost <= wholeCost) return \"run-length\"\n if (wholeCost <= scatterCost) return \"whole-row\"\n return \"scatter\"\n}\n","/**\n * Hybrid output emission — per-mode emitters.\n *\n * Three emission modes, picked per-row by `pickEmissionMode` in\n * `output-density.ts`. All three mutate a shared `OutputEmitState` so that\n * cursor and style state carries across rows (enabling the `\\r\\n` shortcut\n * and avoiding redundant SGR transitions).\n *\n * Tracking: km-silvery.known-limits.hybrid-output\n *\n * Phase 2 implementation. Phase 3 will wire these into output-phase.ts\n * behind the SILVERY_HYBRID_OUTPUT feature flag. Until then, the new\n * emitters are reachable only via direct unit tests; legacy `changesToAnsi`\n * is unchanged.\n */\n\nimport {\n type Style,\n type TerminalBuffer,\n createMutableCell,\n hasActiveAttrs,\n styleEquals,\n} from \"../buffer\"\nimport type { DirtyRowSummary } from \"./output-density\"\nimport type { CellChange } from \"./types\"\nimport {\n type OutputContext,\n _internalStyleTransition as styleTransition,\n _internalWrapTextSizing as wrapTextSizing,\n} from \"./output-phase\"\n\n/**\n * Mutable state threaded through each per-row emitter so that cross-row\n * cursor and style transitions are as cheap as the current `changesToAnsi`\n * single-pass implementation.\n *\n * Zero-allocation discipline: this is reused across rows and across frames.\n * The `OutputEmitState` lives in the `createOutputPhase` closure alongside\n * `InlineCursorState`.\n */\nexport interface OutputEmitState {\n /** Accumulated ANSI output for the current frame. */\n output: string\n /** Current terminal cursor column, render-relative. -1 means uninitialized. */\n cursorX: number\n /** Current terminal cursor row, render-relative. -1 means uninitialized. */\n cursorY: number\n /** Previous emitted row (for cross-row shortcut detection). */\n prevY: number\n /** Last emitted cell column (wide-char continuation tracking). */\n lastEmittedX: number\n /** Last emitted cell row (wide-char continuation tracking). */\n lastEmittedY: number\n /** Currently active SGR style, or null if reset. */\n currentStyle: Style | null\n /** Currently active OSC 8 hyperlink, or undefined if none. */\n currentHyperlink: string | undefined\n /** Inline mode adjustments: subtract from y to get render-relative row. */\n startLine: number\n /** True if emitting in inline mode (relative cursor positioning). */\n isInline: boolean\n}\n\n/** Create a fresh `OutputEmitState`. Reused across rows by emitters. */\nexport function createOutputEmitState(opts?: {\n startLine?: number\n isInline?: boolean\n}): OutputEmitState {\n return {\n output: \"\",\n cursorX: -1,\n cursorY: -1,\n prevY: -1,\n lastEmittedX: -1,\n lastEmittedY: -1,\n currentStyle: null,\n currentHyperlink: undefined,\n startLine: opts?.startLine ?? 0,\n isInline: opts?.isInline ?? false,\n }\n}\n\n// ============================================================================\n// Shared per-cell emission helper\n// ============================================================================\n\n/**\n * Pre-allocated style scratch object — reused across all emitter calls.\n * Per-frame emit state, not per-call.\n */\nconst reusableCellStyle: Style = {\n fg: null,\n bg: null,\n underlineColor: null,\n attrs: {},\n}\n\n/** Pre-allocated cell scratch for buffer reads. */\nconst reusableCell = createMutableCell()\n\n/**\n * Move the cursor to `(renderY, x)`, choosing the cheapest escape sequence.\n * Mutates `state.output`, `state.cursorX`, `state.cursorY`.\n *\n * - First positioning (`cursorY === -1`): fullscreen uses absolute CUP,\n * inline uses `\\r` + CUF.\n * - Same-row forward jump: CUF (resetting bg first to prevent bleed).\n * - Next-row col-0: `\\r\\n` (resetting style first).\n * - Same-column down N rows: `\\r\\x1b[NB` or `\\r\\n` for N=1.\n * - Inline arbitrary positioning: relative `\\x1b[NA/B` + `\\r` + CUF.\n * - Fullscreen arbitrary: absolute CUP `\\x1b[Y;XH`.\n */\nfunction moveCursorTo(state: OutputEmitState, x: number, renderY: number): void {\n const { cursorX, cursorY, isInline } = state\n if (renderY === cursorY && x === cursorX) return\n\n // \\r\\n shortcut: next row at column 0.\n if (cursorY >= 0 && renderY === cursorY + 1 && x === 0) {\n if (\n state.currentStyle &&\n (state.currentStyle.bg !== null || hasActiveAttrs(state.currentStyle.attrs))\n ) {\n state.output += \"\\x1b[0m\"\n state.currentStyle = null\n }\n state.output += \"\\r\\n\"\n } else if (cursorY >= 0 && renderY === cursorY && x > cursorX) {\n // Same row forward.\n if (state.currentStyle && state.currentStyle.bg !== null) {\n state.output += \"\\x1b[0m\"\n state.currentStyle = null\n }\n const dx = x - cursorX\n state.output += dx === 1 ? \"\\x1b[C\" : `\\x1b[${dx}C`\n } else if (cursorY >= 0 && renderY > cursorY && x === 0) {\n // Same column 0 down N rows.\n if (\n state.currentStyle &&\n (state.currentStyle.bg !== null || hasActiveAttrs(state.currentStyle.attrs))\n ) {\n state.output += \"\\x1b[0m\"\n state.currentStyle = null\n }\n const dy = renderY - cursorY\n state.output += dy === 1 ? \"\\r\\n\" : `\\r\\x1b[${dy}B`\n } else if (isInline) {\n if (\n state.currentStyle &&\n (state.currentStyle.bg !== null || hasActiveAttrs(state.currentStyle.attrs))\n ) {\n state.output += \"\\x1b[0m\"\n state.currentStyle = null\n }\n const fromRow = cursorY >= 0 ? cursorY : 0\n if (renderY > fromRow) {\n state.output += `\\x1b[${renderY - fromRow}B\\r`\n } else if (renderY < fromRow) {\n state.output += `\\x1b[${fromRow - renderY}A\\r`\n } else {\n state.output += \"\\r\"\n }\n if (x > 0) state.output += x === 1 ? \"\\x1b[C\" : `\\x1b[${x}C`\n } else {\n state.output += `\\x1b[${renderY + 1};${x + 1}H`\n }\n\n state.cursorX = x\n state.cursorY = renderY\n}\n\n/**\n * Emit a single cell at the current cursor position.\n * Caller is responsible for moving the cursor first via `moveCursorTo`.\n *\n * Handles: hyperlink open/close, style transition, char write, wide-char\n * cursor resync. Mutates `state.output`, `state.currentStyle`,\n * `state.currentHyperlink`, `state.cursorX`, `state.lastEmittedX/Y`.\n */\nfunction emitCellAtCursor(\n state: OutputEmitState,\n x: number,\n renderY: number,\n cell: {\n char: string\n fg: Style[\"fg\"]\n bg: Style[\"bg\"]\n underlineColor: Style[\"underlineColor\"]\n attrs: Style[\"attrs\"]\n wide: boolean\n hyperlink?: string\n },\n ctx: OutputContext,\n): void {\n // Hyperlink transition.\n if (cell.hyperlink !== state.currentHyperlink) {\n if (state.currentHyperlink) {\n state.output += \"\\x1b]8;;\\x1b\\\\\"\n }\n if (cell.hyperlink) {\n state.output += `\\x1b]8;;${cell.hyperlink}\\x1b\\\\`\n }\n state.currentHyperlink = cell.hyperlink\n }\n\n // Style transition.\n reusableCellStyle.fg = cell.fg\n reusableCellStyle.bg = cell.bg\n reusableCellStyle.underlineColor = cell.underlineColor\n reusableCellStyle.attrs = cell.attrs\n if (!styleEquals(state.currentStyle, reusableCellStyle)) {\n const prev = state.currentStyle\n const snapshot: Style = {\n fg: cell.fg,\n bg: cell.bg,\n underlineColor: cell.underlineColor,\n attrs: { ...cell.attrs },\n }\n state.output += styleTransition(prev, snapshot, ctx)\n state.currentStyle = snapshot\n }\n\n // Char (empty → space so terminal advances cursor).\n const char = cell.char || \" \"\n state.output += wrapTextSizing(char, cell.wide, ctx)\n\n state.cursorX = x + (cell.wide ? 2 : 1)\n state.cursorY = renderY\n state.lastEmittedX = x\n state.lastEmittedY = renderY - state.startLine // store buffer-relative y\n\n // Wide-char cursor resync (terminal-bug workaround for flag emoji etc.).\n if (cell.wide) {\n if (state.isInline) {\n if (state.currentStyle && state.currentStyle.bg !== null) {\n state.output += \"\\x1b[0m\"\n state.currentStyle = null\n }\n state.output += \"\\r\"\n if (state.cursorX > 0) {\n state.output += state.cursorX === 1 ? \"\\x1b[C\" : `\\x1b[${state.cursorX}C`\n }\n } else {\n state.output += `\\x1b[${state.cursorY + 1};${state.cursorX + 1}H`\n }\n }\n}\n\n/**\n * Reset row-level state (close hyperlinks, reset bg) when transitioning to\n * a new row. Called at the start of each emitter and at the end of\n * whole-row emissions to prevent right-margin bleed.\n */\nfunction endOfRowReset(state: OutputEmitState): void {\n if (state.currentHyperlink) {\n state.output += \"\\x1b]8;;\\x1b\\\\\"\n state.currentHyperlink = undefined\n }\n if (\n state.currentStyle &&\n (state.currentStyle.bg !== null || hasActiveAttrs(state.currentStyle.attrs))\n ) {\n state.output += \"\\x1b[0m\"\n state.currentStyle = null\n }\n}\n\n// ============================================================================\n// Mode A — whole-row emission\n// ============================================================================\n\n/**\n * Emit an entire row's worth of cells unconditionally, matching the\n * per-cell inner loop of `bufferToAnsi`. Used when `pickEmissionMode`\n * returns `\"whole-row\"`.\n *\n * Contract:\n * - Writes every cell in `[0, buffer.width)` on row `summary.y`.\n * - Starts with an absolute cursor jump to `(summary.y, 0)`.\n * - Mutates `state` to reflect the final cursor position at\n * `(summary.y, buffer.width)` (pending-wrap state).\n * - Appends to `state.output`.\n */\nexport function emitWholeRow(\n summary: DirtyRowSummary,\n buffer: TerminalBuffer,\n ctx: OutputContext,\n state: OutputEmitState,\n): void {\n const y = summary.y\n const renderY = state.isInline ? y - state.startLine : y\n\n // Close hyperlink on row change.\n if (y !== state.prevY && state.currentHyperlink) {\n state.output += \"\\x1b]8;;\\x1b\\\\\"\n state.currentHyperlink = undefined\n }\n state.prevY = y\n\n // Position cursor at start of row.\n moveCursorTo(state, 0, renderY)\n\n for (let x = 0; x < buffer.width; x++) {\n buffer.readCellInto(x, y, reusableCell)\n emitCellAtCursor(\n state,\n x,\n renderY,\n {\n char: reusableCell.char,\n fg: reusableCell.fg,\n bg: reusableCell.bg,\n underlineColor: reusableCell.underlineColor,\n attrs: reusableCell.attrs,\n wide: reusableCell.wide,\n hyperlink: reusableCell.hyperlink,\n },\n ctx,\n )\n if (reusableCell.wide) {\n x++ // skip continuation column\n }\n }\n\n endOfRowReset(state)\n state.cursorX = buffer.width\n state.cursorY = renderY\n state.lastEmittedX = buffer.width - 1\n state.lastEmittedY = y\n}\n\n// ============================================================================\n// Mode B — run-length emission\n// ============================================================================\n\n/**\n * Emit the maximal contiguous runs of dirty cells on a row. Used when\n * `pickEmissionMode` returns `\"run-length\"`.\n *\n * Contract:\n * - Iterates `summary.runs` in order. For each run, moves the cursor to\n * `(summary.y, run.start)` then emits cells `[run.start, run.end]`.\n * - Relies on cursor auto-advance within a run — no per-cell CUF.\n * - Mutates `state.cursorX` / `state.cursorY` to reflect the final position\n * of the last run on the row.\n * - Appends to `state.output`.\n *\n * Run bounds are trusted: `analyzeRowDensity` widens runs to cover both\n * halves of straddling wide chars.\n */\nexport function emitRuns(\n summary: DirtyRowSummary,\n pool: readonly CellChange[],\n buffer: TerminalBuffer,\n ctx: OutputContext,\n state: OutputEmitState,\n): void {\n const y = summary.y\n const renderY = state.isInline ? y - state.startLine : y\n\n if (y !== state.prevY && state.currentHyperlink) {\n state.output += \"\\x1b]8;;\\x1b\\\\\"\n state.currentHyperlink = undefined\n }\n state.prevY = y\n\n // Build a quick lookup for cells whose changes are in the pool, keyed\n // by x. For positions inside the run that are NOT in the pool (e.g., a\n // run widened to cover a wide-char half), read from the buffer.\n // Linear scan over the pool slice — the slice is small (one row).\n for (let r = 0; r < summary.runCount; r++) {\n const run = summary.runs[r]!\n moveCursorTo(state, run.start, renderY)\n\n let x = run.start\n while (x <= run.end) {\n // Locate the change in the pool slice for column x, if any.\n let change: CellChange | undefined\n for (let i = summary.poolStart; i < summary.poolEnd; i++) {\n const c = pool[i]!\n if (c.y !== y) break // pool may have other rows after — but slice limited\n if (c.x === x) {\n change = c\n break\n }\n }\n\n if (change && !change.cell.continuation) {\n emitCellAtCursor(\n state,\n x,\n renderY,\n {\n char: change.cell.char,\n fg: change.cell.fg,\n bg: change.cell.bg,\n underlineColor: change.cell.underlineColor,\n attrs: change.cell.attrs,\n wide: change.cell.wide,\n hyperlink: change.cell.hyperlink,\n },\n ctx,\n )\n x += change.cell.wide ? 2 : 1\n } else {\n // No change at this column (or only a continuation cell touched).\n // Read from buffer to repaint the cell. This covers wide-char\n // widening and gap-filling within a run.\n buffer.readCellInto(x, y, reusableCell)\n if (reusableCell.continuation && x > 0) {\n // Step back to main cell.\n x -= 1\n buffer.readCellInto(x, y, reusableCell)\n if (reusableCell.continuation) {\n // Should not happen with valid buffers — bail.\n x += 2\n continue\n }\n }\n emitCellAtCursor(\n state,\n x,\n renderY,\n {\n char: reusableCell.char,\n fg: reusableCell.fg,\n bg: reusableCell.bg,\n underlineColor: reusableCell.underlineColor,\n attrs: reusableCell.attrs,\n wide: reusableCell.wide,\n hyperlink: reusableCell.hyperlink,\n },\n ctx,\n )\n x += reusableCell.wide ? 2 : 1\n }\n }\n }\n}\n\n// ============================================================================\n// Mode C — scatter emission\n// ============================================================================\n\n/**\n * Emit dirty cells one by one with explicit cursor jumps between them.\n * Used when `pickEmissionMode` returns `\"scatter\"`. This is the current\n * behavior of `changesToAnsi`, refactored to take `OutputEmitState` and a\n * per-row pool slice.\n *\n * Contract:\n * - Iterates `pool[summary.poolStart..summary.poolEnd)` in order.\n * - Emits each cell with an explicit cursor jump if the cursor is not\n * already at the target position.\n * - Handles wide-char continuation cells by looking up the main cell from\n * the buffer (orphan handling, see `changesToAnsi`).\n * - Mutates `state` to reflect the final cursor position after the last\n * emitted cell.\n */\nexport function emitScatter(\n summary: DirtyRowSummary,\n pool: readonly CellChange[],\n buffer: TerminalBuffer,\n ctx: OutputContext,\n state: OutputEmitState,\n): void {\n const y = summary.y\n const renderY = state.isInline ? y - state.startLine : y\n\n if (y !== state.prevY && state.currentHyperlink) {\n state.output += \"\\x1b]8;;\\x1b\\\\\"\n state.currentHyperlink = undefined\n }\n state.prevY = y\n\n for (let i = summary.poolStart; i < summary.poolEnd; i++) {\n const change = pool[i]!\n let x = change.x\n let cell: typeof change.cell = change.cell\n\n if (cell.continuation) {\n // Main cell already emitted in this pass — skip.\n if (state.lastEmittedX === x - 1 && state.lastEmittedY === y) continue\n\n // Orphan continuation — repaint main cell from buffer.\n if (x > 0) {\n x = x - 1\n buffer.readCellInto(x, y, reusableCell)\n if (reusableCell.continuation || !reusableCell.wide) continue\n cell = reusableCell\n } else {\n continue\n }\n }\n\n moveCursorTo(state, x, renderY)\n emitCellAtCursor(\n state,\n x,\n renderY,\n {\n char: cell.char,\n fg: cell.fg,\n bg: cell.bg,\n underlineColor: cell.underlineColor,\n attrs: cell.attrs,\n wide: cell.wide,\n hyperlink: cell.hyperlink,\n },\n ctx,\n )\n // emitCellAtCursor stores buffer-relative y, but for scatter we want\n // the actual y for cross-cell continuation tracking.\n state.lastEmittedY = y\n }\n}\n","/**\n * Phase 4: Output Phase\n *\n * Diff two buffers and produce minimal ANSI output.\n *\n * Debug: Set SILVERY_DEBUG_OUTPUT=1 to log diff changes and ANSI sequences.\n */\n\nimport {\n type Style,\n type TerminalBuffer,\n type UnderlineStyle,\n VISIBLE_SPACE_ATTR_MASK,\n cellEquals as cellVisualEquals,\n colorEquals,\n createMutableCell,\n hasActiveAttrs,\n isDefaultBg,\n styleEquals,\n} from \"../buffer\"\nimport { fgColorCode, bgColorCode } from \"../ansi/sgr-codes\"\nimport { resetCursorStyle, setCursorStyle } from \"../output\"\nimport type { CursorState } from \"@silvery/ag-react/hooks/useCursor\"\nimport { textSized } from \"../text-sizing\"\nimport { graphemeWidth, isTextSizingEnabled } from \"../unicode\"\nimport type { CellChange } from \"./types\"\nimport { checkRenderEmulatorDivergence, countNonSpaceInText } from \"../strict-divergence\"\nimport {\n clearLastOutputCursorDiagnostics,\n getLastOutputCursorDiagnostics,\n recordOutputCursorDiagnostics,\n} from \"../cursor-diagnostics\"\nimport { createLogger } from \"loggily\"\nimport {\n replayAnsiWithStyles,\n captureStrictFailureArtifacts as _captureArtifacts,\n verifyOutputEquivalence as _verifyOutputEquivalence,\n verifyAccumulatedOutput as _verifyAccumulatedOutput,\n verifyTerminalEquivalence as _verifyTerminalEquivalence,\n initTerminalVerifyState,\n createTerminalVerifyState,\n sgrColorEquals,\n formatColor,\n type AccumulateState,\n type TerminalVerifyState,\n} from \"./output-verify\"\n\nconst log = createLogger(\"silvery:output\")\n\nconst _env =\n typeof process !== \"undefined\" ? process.env : ({} as Record<string, string | undefined>)\nconst DEBUG_OUTPUT = !!_env.SILVERY_DEBUG_OUTPUT\nconst FULL_RENDER = !!_env.SILVERY_FULL_RENDER\nconst DEBUG_CAPTURE = !!_env.SILVERY_DEBUG_CAPTURE\nconst CAPTURE_RAW = !!_env.SILVERY_CAPTURE_RAW\n/**\n * Phase 3 hybrid-output feature flag. When set (`SILVERY_HYBRID_OUTPUT=1`)\n * and the render path can call into the new emitters (fullscreen mode +\n * buffer available), `changesToAnsi` dispatches per-row through\n * `analyzeRowDensity` + `pickEmissionMode` and the three mode-specific\n * emitters in `output-modes.ts`. Off by default during the soak period —\n * see `hub/silvery/design/v05-layout/hybrid-output.md` §8 (rollout plan).\n *\n * Read at call time (not module-load time) so integration tests can toggle\n * the flag without forcing module re-import. The cost is one `process.env`\n * lookup per frame, dominated by the surrounding diff/emit work.\n *\n * Inline mode is intentionally excluded in this phase — see design doc R4.\n */\nfunction isHybridOutputEnabled(): boolean {\n return (typeof process !== \"undefined\" ? process.env.SILVERY_HYBRID_OUTPUT : undefined) === \"1\"\n}\nlet _debugFrameCount = 0\nlet _captureRawFrameCount = 0\n\nexport interface OutputPhaseDiagnostics {\n reason:\n | \"first-render\"\n | \"inline-first-render\"\n | \"inline-resize-incremental\"\n | \"inline-incremental\"\n | \"forced-full-render\"\n | \"dimension-mismatch\"\n | \"zero-diff\"\n | \"diff\"\n | \"native-scroll-diff\"\n mode: \"fullscreen\" | \"inline\"\n width: number\n height: number\n prevWidth: number\n prevHeight: number\n changedCells?: number\n rawChangedCells?: number\n dirtyRows?: number\n}\n\nlet lastOutputPhaseDiagnostics: OutputPhaseDiagnostics | null = null\n\nexport function getLastOutputPhaseDiagnostics(): OutputPhaseDiagnostics | null {\n return lastOutputPhaseDiagnostics ? { ...lastOutputPhaseDiagnostics } : null\n}\n\nexport function clearLastOutputPhaseDiagnostics(): void {\n lastOutputPhaseDiagnostics = null\n}\n\nexport { getLastOutputCursorDiagnostics, clearLastOutputCursorDiagnostics }\n\nfunction setLastOutputPhaseDiagnostics(diagnostics: OutputPhaseDiagnostics): void {\n lastOutputPhaseDiagnostics = diagnostics\n}\n\n// Re-export verification types only. `replayAnsiWithStyles` is internal —\n// tests that need it import from `./output-verify` directly. Bead:\n// km-silvery.unexport-replay-ansi-with-styles.\nexport type { StyledCell } from \"./output-verify\"\n// Wrap captureStrictFailureArtifacts to bind bufferToAnsi (avoids circular dep in output-verify.ts)\nexport function captureStrictFailureArtifacts(\n opts: Parameters<typeof _captureArtifacts>[0],\n): string {\n return _captureArtifacts({ ...opts, renderFull: opts.renderFull ?? bufferToAnsi })\n}\n\n// ============================================================================\n// Terminal Capability Flags (suppress unsupported SGR codes)\n// ============================================================================\n\nimport type { TerminalCaps } from \"../terminal-caps\"\n\n// ============================================================================\n// Output Phase Factory (per-term instance, no globals)\n// ============================================================================\n\n/**\n * Output-phase capabilities type.\n *\n * Post km-silvery.text-box-attr-props (2026-04-23):\n * `underlineStyles` is now the per-style set (not just a boolean), so the\n * output phase can downgrade individual styles — e.g. `\"curly\"` requested on\n * a terminal that only supports `\"single\"` emits plain `SGR 4` instead of\n * the unsupported `4:3`.\n *\n * Accepts either form at construction for backwards compatibility:\n * - `boolean` — back-compat shorthand. `true` = all styles supported\n * (modern terminals); `false` = only plain `SGR 4` works.\n * - `readonly UnderlineStyle[]` — per-style granular set, matches\n * `TerminalCaps.underlineStyles` directly.\n *\n * Internally the output phase always sees the array form (resolved by the\n * ctx factory). Consumers check per-style via the `supportsUnderlineStyle`\n * helper, or check \"any extended support\" via `caps.underlineStyles.length > 0`.\n */\nexport type OutputUnderlineStylesCap = boolean | readonly UnderlineStyle[]\n\n/** All five spelled-out underline styles — the full modern-terminal set. */\nconst ALL_EXTENDED_UNDERLINE_STYLES: readonly UnderlineStyle[] = [\n \"single\",\n \"double\",\n \"curly\",\n \"dotted\",\n \"dashed\",\n]\n\n/**\n * Normalize a (boolean | array) underline-styles cap to the array form used\n * by the output context.\n */\nexport function resolveUnderlineStylesCap(\n cap: OutputUnderlineStylesCap | undefined,\n): readonly UnderlineStyle[] {\n if (cap === undefined) return ALL_EXTENDED_UNDERLINE_STYLES\n if (typeof cap === \"boolean\") {\n return cap ? ALL_EXTENDED_UNDERLINE_STYLES : []\n }\n return cap\n}\n\n/**\n * Check whether a requested underline style is in the resolved cap set.\n * `\"single\"` is always supported (it's the base SGR 4), so this returns true\n * for `\"single\"` regardless of the array contents.\n */\nfunction supportsUnderlineStyle(\n styles: readonly UnderlineStyle[],\n requested: UnderlineStyle | undefined,\n): boolean {\n if (!requested) return false\n if (requested === \"single\") return true\n return styles.includes(requested)\n}\n\nexport interface OutputCaps {\n /**\n * Per-style underline support. Array form so the output phase can\n * downgrade individual unsupported styles to plain SGR 4 (single).\n * Empty array = plain SGR 4 only.\n */\n readonly underlineStyles: readonly UnderlineStyle[]\n readonly underlineColor: boolean\n /**\n * SGR 53 (overline) support. When false, the output phase skips\n * emitting SGR 53/55 — the cell attr is still packed/diffed, but no\n * escape sequence reaches the terminal. Defaults to true for modern\n * terminals (Ghostty, iTerm2, xterm with extended attrs enabled).\n */\n readonly overline: boolean\n readonly colorLevel: TerminalCaps[\"colorLevel\"]\n}\n\n// ============================================================================\n// Output Context (per-instance state, replaces module-level globals)\n// ============================================================================\n\n/**\n * Per-instance output context containing terminal capabilities, measurer,\n * caches, and per-frame viewport state. Threaded through internal functions\n * to eliminate module-level mutable state and prevent \"forgot to thread\n * parameter\" failures. Caches are per-context because SGR output depends on caps.\n *\n * Per-frame fields (mode, termRows) are set by the caller before each frame:\n * - createOutputPhase() closure sets them in scopedOutputPhase()\n * - bare outputPhase() sets them from its parameters\n */\nexport interface OutputContext {\n readonly caps: OutputCaps\n readonly measurer: OutputMeasurer | null\n readonly sgrCache: Map<string, string>\n readonly transitionCache: Map<string, string>\n /** Render mode for the current frame. Set per-frame by the output phase entry point. */\n mode: \"fullscreen\" | \"inline\"\n /** Terminal height in rows for the current frame. Caps output to prevent\n * scrollback corruption when content exceeds terminal height. Set per-frame. */\n termRows: number | undefined\n}\n\n/** Default context used by bare outputPhase() calls (full capability support, no measurer). */\nconst defaultContext: OutputContext = {\n caps: {\n underlineStyles: ALL_EXTENDED_UNDERLINE_STYLES,\n underlineColor: true,\n overline: true,\n colorLevel: \"truecolor\",\n },\n measurer: null,\n sgrCache: new Map(),\n transitionCache: new Map(),\n mode: \"fullscreen\",\n termRows: undefined,\n}\n\ninterface NativeScrollPlan {\n top: number\n height: number\n delta: number\n afterScroll: TerminalBuffer\n}\n\nconst nativeScrollPrevCell = createMutableCell()\nconst nativeScrollNextCell = createMutableCell()\n\nfunction renderedCellCount(buffer: TerminalBuffer, termRows?: number): number {\n const rows = termRows == null ? buffer.height : Math.min(buffer.height, termRows)\n return buffer.width * Math.max(0, rows)\n}\n\nfunction changedRowCount(pool: readonly CellChange[], count: number): number {\n if (count <= 0) return 0\n let rows: Set<number> | null = null\n let firstRow = pool[0]?.y ?? -1\n for (let i = 1; i < count; i++) {\n const y = pool[i]!.y\n if (y === firstRow) continue\n if (!rows) rows = new Set([firstRow])\n rows.add(y)\n }\n return rows ? rows.size : 1\n}\n\nfunction rowsEqualWithOffset(\n prev: TerminalBuffer,\n next: TerminalBuffer,\n nextY: number,\n prevY: number,\n): boolean {\n if (nextY < 0 || nextY >= next.height || prevY < 0 || prevY >= prev.height) return false\n for (let x = 0; x < next.width; x++) {\n prev.readCellInto(x, prevY, nativeScrollPrevCell)\n next.readCellInto(x, nextY, nativeScrollNextCell)\n if (!cellVisualEquals(nativeScrollPrevCell, nativeScrollNextCell)) {\n return false\n }\n }\n return true\n}\n\nfunction detectNativeScrollPlan(\n prev: TerminalBuffer,\n next: TerminalBuffer,\n): NativeScrollPlan | null {\n if (prev.width !== next.width || prev.height !== next.height) return null\n if (next.minDirtyRow < 0 || next.maxDirtyRow < next.minDirtyRow) return null\n const top = next.minDirtyRow\n const bottom = next.maxDirtyRow\n const height = bottom - top + 1\n if (height < 4 || next.width < 4) return null\n\n let best: { delta: number; matchedRows: number } | null = null\n for (let absDelta = 1; absDelta < height; absDelta++) {\n let upMatches = true\n for (let y = top; y <= bottom - absDelta; y++) {\n if (!rowsEqualWithOffset(prev, next, y, y + absDelta)) {\n upMatches = false\n break\n }\n }\n if (upMatches) {\n const matchedRows = height - absDelta\n if (!best || matchedRows > best.matchedRows) best = { delta: absDelta, matchedRows }\n }\n\n let downMatches = true\n for (let y = top + absDelta; y <= bottom; y++) {\n if (!rowsEqualWithOffset(prev, next, y, y - absDelta)) {\n downMatches = false\n break\n }\n }\n if (downMatches) {\n const matchedRows = height - absDelta\n if (!best || matchedRows > best.matchedRows) best = { delta: -absDelta, matchedRows }\n }\n }\n\n if (!best || best.matchedRows < Math.max(3, Math.floor(height * 0.5))) return null\n const afterScroll = prev.clone()\n afterScroll.scrollRegion(0, top, next.width, height, best.delta)\n return { top, height, delta: best.delta, afterScroll }\n}\n\nfunction nativeScrollPrefix(plan: NativeScrollPlan): string {\n const top = plan.top + 1\n const bottom = plan.top + plan.height\n const amount = Math.abs(plan.delta)\n const command = plan.delta > 0 ? \"S\" : \"T\"\n return `\\x1b[${top};${bottom}r\\x1b[${top};1H\\x1b[${amount}${command}\\x1b[r`\n}\n\n/** Output phase function signature. */\nexport interface OutputPhaseFn {\n (\n prev: TerminalBuffer | null,\n next: TerminalBuffer,\n mode?: \"fullscreen\" | \"inline\",\n scrollbackOffset?: number,\n termRows?: number,\n cursorPos?: CursorState | null,\n ): string\n /** Reset inline cursor state. Used by useScrollback to clear cursor tracking on resize. */\n resetInlineState?: () => void\n /** Get the current inline cursor row (relative to render region start). -1 if unknown. */\n getInlineCursorRow?: () => number\n /** Promote frozen content to scrollback. Called by useScrollback to queue\n * frozen content for the next render — the output phase writes frozen + live\n * content in a single target.write() to avoid flicker. */\n promoteScrollback?: (frozenContent: string, frozenLineCount: number) => void\n}\n\n// ============================================================================\n// Output Phase Measurer (module-local, avoids dual-module-loading issues)\n// ============================================================================\n// bun can load the same .ts file via symlink + real path as separate module\n// instances. This means `_scopedMeasurer` set by `runWithMeasurer()` in\n// pipeline/index.ts's instance of unicode.ts is invisible to output-phase.ts's\n// instance. We avoid this by closing over the measurer in createOutputPhase()\n// and setting a module-local variable that all output-phase functions read.\n\nexport interface OutputMeasurer {\n graphemeWidth(grapheme: string): number\n readonly textSizing: boolean\n}\n\n/** Get grapheme width using the output context measurer (falls back to unicode.ts import). */\nfunction outputGraphemeWidth(g: string, ctx: OutputContext): number {\n return ctx.measurer ? ctx.measurer.graphemeWidth(g) : graphemeWidth(g)\n}\n\n/** Check if text sizing is enabled using the output context measurer. */\nfunction outputTextSizingEnabled(ctx: OutputContext): boolean {\n return ctx.measurer ? ctx.measurer.textSizing : isTextSizingEnabled()\n}\n\n/**\n * Create a scoped output phase that uses specific terminal capabilities.\n *\n * @param caps - Terminal capabilities for SGR code generation. Accepts\n * `underlineStyles` as either a boolean (back-compat) or the per-style\n * array form; see {@link OutputUnderlineStylesCap}.\n * @param measurer - Width measurer for graphemeWidth/textSizing (avoids dual-module-loading issues)\n */\nexport function createOutputPhase(\n caps: {\n underlineStyles?: OutputUnderlineStylesCap\n underlineColor?: boolean\n overline?: boolean\n colorLevel?: TerminalCaps[\"colorLevel\"]\n },\n measurer?: OutputMeasurer,\n): OutputPhaseFn {\n // Instance-scoped context — caps, measurer, and caches are all per-instance.\n // mode and termRows are set per-frame in scopedOutputPhase() below.\n // No module-level globals are read or modified.\n const ctx: OutputContext = {\n caps: {\n underlineStyles: resolveUnderlineStylesCap(caps.underlineStyles),\n underlineColor: caps.underlineColor ?? true,\n overline: caps.overline ?? true,\n colorLevel: caps.colorLevel ?? \"truecolor\",\n },\n measurer: measurer ?? null,\n sgrCache: new Map(),\n transitionCache: new Map(),\n mode: \"fullscreen\",\n termRows: undefined,\n }\n // Instance-scoped inline cursor state — persists across frames for incremental rendering.\n // Each createOutputPhase() call gets its own state, eliminating module-level globals.\n const inlineState = createInlineCursorState()\n\n // Instance-scoped accumulated ANSI state for SILVERY_STRICT_ACCUMULATE verification.\n // Tracks per-instance verification state rather than using module-level globals.\n const accState = {\n accumulatedAnsi: \"\",\n accumulateWidth: 0,\n accumulateHeight: 0,\n accumulateFrameCount: 0,\n }\n\n // Instance-scoped terminal verify state for SILVERY_STRICT_TERMINAL verification.\n const tvState = createTerminalVerifyState()\n\n // Pending scrollback promotion — queued by useScrollback, consumed by the next render.\n let pendingPromotion: { frozenContent: string; frozenLineCount: number } | null = null\n\n const fn: OutputPhaseFn = function scopedOutputPhase(\n prev: TerminalBuffer | null,\n next: TerminalBuffer,\n mode: \"fullscreen\" | \"inline\" = \"fullscreen\",\n scrollbackOffset = 0,\n termRows?: number,\n cursorPos?: CursorState | null,\n ): string {\n // Set per-frame viewport state on the context — internal functions read from ctx\n // instead of accepting mode/termRows as separate parameters.\n ctx.mode = mode\n ctx.termRows = termRows\n // Handle scrollback promotion: write frozen content + live content in one pass.\n if (pendingPromotion && mode === \"inline\") {\n const promo = pendingPromotion\n pendingPromotion = null\n return handleScrollbackPromotion(\n inlineState,\n promo.frozenContent,\n promo.frozenLineCount,\n next,\n cursorPos,\n ctx,\n tvState,\n accState,\n )\n }\n return outputPhase(\n prev,\n next,\n mode,\n scrollbackOffset,\n termRows,\n cursorPos,\n inlineState,\n ctx,\n accState,\n tvState,\n )\n }\n\n fn.resetInlineState = () => {\n Object.assign(inlineState, createInlineCursorState())\n inlineState.forceFirstRender = true\n // Clear any queued promotion — the resize handler re-emits all frozen items\n // directly, so any pending promotion from the freeze effect is redundant.\n // Without this, freeze+resize in the same frame causes duplicate frozen content.\n pendingPromotion = null\n }\n\n fn.getInlineCursorRow = () => inlineState.prevCursorRow\n\n fn.promoteScrollback = (frozenContent: string, frozenLineCount: number) => {\n if (pendingPromotion) {\n pendingPromotion.frozenContent += frozenContent\n pendingPromotion.frozenLineCount += frozenLineCount\n } else {\n pendingPromotion = { frozenContent, frozenLineCount }\n }\n }\n\n return fn\n}\n\n/**\n * Handle scrollback promotion: write frozen content + live content in a single output string.\n *\n * Instead of useScrollback writing directly to stdout (which blanks the screen and\n * causes flicker), this function builds one output string that:\n * 1. Moves cursor to the render region start (no clearing)\n * 2. Writes frozen content (each line overwrites in-place via \\x1b[K])\n * 3. Writes live content via bufferToAnsi (also with per-line \\x1b[K])\n * 4. Erases any leftover lines from the previous frame\n * 5. Positions the hardware cursor\n *\n * Result: a single target.write() with no blanking — no flicker.\n */\nfunction handleScrollbackPromotion(\n state: InlineCursorState,\n frozenContent: string,\n frozenLineCount: number,\n next: TerminalBuffer,\n cursorPos: CursorState | null | undefined,\n ctx: OutputContext,\n tvState?: TerminalVerifyState,\n accState?: AccumulateState,\n): string {\n const { termRows } = ctx\n // 1. Move cursor to render region start\n let output = \"\"\n if (state.prevCursorRow > 0) {\n output += `\\x1b[${state.prevCursorRow}A`\n }\n output += \"\\r\" // column 0, NO \\x1b[J clear\n\n // 2. Write frozen content (overwrites old content in-place, OSC markers included)\n output += frozenContent\n\n // 3. Write live content via bufferToAnsi (each line has \\x1b[K — no blanking)\n const nextContentLines = findLastContentLine(next) + 1\n const maxOutputLines = termRows != null ? Math.min(nextContentLines, termRows) : nextContentLines\n output += bufferToAnsi(next, ctx, maxOutputLines)\n\n // Total lines on-screen: frozen + live. The terminal may scroll if this exceeds\n // termRows, naturally pushing frozen lines into scrollback. No padding needed —\n // we track ALL on-screen lines so the next render can overwrite them cleanly.\n const totalOnScreen = frozenLineCount + maxOutputLines\n\n // 4. Erase leftover lines at bottom (if content shrank)\n const oldTotalLines = state.prevOutputLines\n const nextLastLine = totalOnScreen - 1\n const terminalScroll = termRows != null ? Math.max(0, totalOnScreen - termRows) : 0\n const lastOccupied = Math.max(oldTotalLines - 1 - terminalScroll, 0)\n if (lastOccupied > nextLastLine) {\n for (let y = nextLastLine + 1; y <= lastOccupied; y++) {\n output += \"\\n\\r\\x1b[K\"\n }\n const up = lastOccupied - nextLastLine\n if (up > 0) output += `\\x1b[${up}A`\n }\n\n // 5. Cursor suffix (hardware cursor positioning)\n // Cursor is at the end of live content (row totalOnScreen - 1 relative to\n // render region start). inlineCursorSuffix moves it to the useCursor position\n // within the live content area.\n output += inlineCursorSuffix(cursorPos ?? null, next, ctx)\n\n // 6. Update tracking for subsequent incremental renders.\n // Track cursor position and output lines relative to the LIVE content only.\n // Frozen content has been written as raw ANSI above the live content and is\n // now \"owned\" by the terminal — it stays on screen until natural scrolling\n // pushes it into terminal scrollback. The next render only needs to cursor-up\n // to the start of the live content area, not past the frozen content.\n //\n // This is critical for real terminals where pre-existing content (shell prompt,\n // direnv output) sits above the app. If prevCursorRow included frozenLineCount,\n // the cursor-up would overshoot into the shell prompt area, clearing it.\n let startLine = 0\n if (termRows != null && nextContentLines > termRows) startLine = nextContentLines - termRows\n state.prevBuffer = next\n\n // Cursor row within the LIVE content area only (not including frozen lines).\n // inlineCursorSuffix already positioned the cursor within the live content.\n if (cursorPos?.visible) {\n const visibleRow = cursorPos.y - startLine\n state.prevCursorRow =\n visibleRow >= 0 && visibleRow < maxOutputLines ? visibleRow : maxOutputLines - 1\n } else {\n state.prevCursorRow = maxOutputLines - 1\n }\n state.prevOutputLines = maxOutputLines\n\n // Realign STRICT verification state. Scrollback promotion writes frozen\n // content + live content as a single output, fundamentally remapping the\n // render region — re-initialize verification with a fresh fullscreen\n // rendering of `next` so subsequent incremental frames have a consistent\n // baseline. (Frozen content above the live render region is \"owned by the\n // terminal\" and not part of the buffer-equivalence invariant.)\n if (tvState || accState) {\n if ((tvState && tvState.backends.length > 0) || (accState && isStrictAccumulate())) {\n const savedMode = ctx.mode\n ctx.mode = \"fullscreen\"\n const fsFresh = bufferToAnsi(next, ctx)\n if (accState && isStrictAccumulate()) {\n accState.accumulatedAnsi = fsFresh\n accState.accumulateWidth = next.width\n accState.accumulateHeight = next.height\n accState.accumulateFrameCount = 0\n }\n if (tvState && tvState.backends.length > 0) {\n initTerminalVerifyState(tvState, next.width, next.height, fsFresh)\n }\n ctx.mode = savedMode\n }\n }\n\n return output\n}\n\n// These use getters so they can be set after module load (e.g., in test files).\n// SILVERY_STRICT enables buffer + output checks (per-frame), including vt100 output verification.\n// SILVERY_STRICT_ACCUMULATE is separate — it replays ALL frames (O(N²)) and is opt-in only.\n// Note: \"0\" and \"false\" are treated as disabled, consistent with renderer.ts strictMode check.\nfunction isStrictOutput(): boolean {\n if (typeof process === \"undefined\") return false\n const val = process.env.SILVERY_STRICT\n return !!val && val !== \"0\" && val !== \"false\"\n}\nfunction isStrictAccumulate(): boolean {\n if (typeof process === \"undefined\") return false\n const val = process.env.SILVERY_STRICT_ACCUMULATE\n return !!val && val !== \"0\" && val !== \"false\"\n}\n\n/** Default accumulate state used by bare outputPhase() calls. */\nconst defaultAccState: AccumulateState = {\n accumulatedAnsi: \"\",\n accumulateWidth: 0,\n accumulateHeight: 0,\n accumulateFrameCount: 0,\n}\n\n/** Default terminal verify state used by bare outputPhase() calls. */\nconst defaultTerminalVerifyState = createTerminalVerifyState()\n\n// ============================================================================\n// Inline Mode: Inter-frame Cursor Tracking (instance-scoped)\n// ============================================================================\n\n/**\n * Mutable state for inline mode inter-frame cursor tracking.\n * Captured in the createOutputPhase() closure — no module-level globals.\n */\ninterface InlineCursorState {\n /** Row within render region after last inline frame's cursor suffix. -1 = unknown. */\n prevCursorRow: number\n /** Total output lines rendered in last frame. Used to clear old content on resize. */\n prevOutputLines: number\n /** Previous frame's buffer — used for incremental rendering when runtime invalidates (resize). */\n prevBuffer: TerminalBuffer | null\n /** When true, the next inline render treats prev as null (first-render path).\n * Set by resetInlineState() after useScrollback clears and re-emits frozen items. */\n forceFirstRender: boolean\n}\n\n/** Create fresh inline cursor state (unknown position → first call falls back to full render). */\nfunction createInlineCursorState(): InlineCursorState {\n return {\n prevCursorRow: -1,\n prevOutputLines: 0,\n prevBuffer: null,\n forceFirstRender: false,\n }\n}\n\n/**\n * Update cursor tracking after an inline render frame.\n * Records where the terminal cursor ends up after inlineCursorSuffix().\n */\nfunction updateInlineCursorRow(\n state: InlineCursorState,\n cursorPos: CursorState | null | undefined,\n maxOutputLines: number,\n startLine: number,\n): void {\n if (cursorPos?.visible) {\n const visibleRow = cursorPos.y - startLine\n state.prevCursorRow =\n visibleRow >= 0 && visibleRow < maxOutputLines ? visibleRow : maxOutputLines - 1\n } else {\n // Cursor hidden: cursor stays at end of last content line\n state.prevCursorRow = maxOutputLines - 1\n }\n state.prevOutputLines = maxOutputLines\n}\n\n/**\n * Wrap a cell character in OSC 66 if text sizing is enabled and the cell is\n * wide. OSC 66 tells the terminal to render the character in exactly `width`\n * cells, matching the layout engine's measurement.\n *\n * Previously this only wrapped specific categories (PUA, text-presentation\n * emoji, flag emoji) — a whack-a-mole approach that missed new categories\n * as Unicode evolved. Now wraps ALL wide chars unconditionally: if the buffer\n * says width 2, the terminal is told width 2. CJK chars don't strictly need\n * it (terminals agree on their width), but the ~8-byte overhead per wide char\n * is negligible and eliminates any future width disagreement.\n */\nfunction wrapTextSizing(char: string, wide: boolean, ctx: OutputContext): string {\n if (!wide || !outputTextSizingEnabled(ctx)) return char\n return textSized(char, 2)\n}\n\n/**\n * Internal: exported for use by output-modes.ts (hybrid output emitters).\n * Not part of the public API — do not use outside the pipeline.\n */\nexport const _internalWrapTextSizing = wrapTextSizing\n\n// ============================================================================\n// Style Interning + SGR Cache\n// ============================================================================\n\n// SGR caches are now per-OutputContext (see OutputContext interface).\n// This is correct because SGR output depends on caps (underlineStyles,\n// underlineColor), so caches populated under one caps configuration\n// would produce wrong results under a different one.\n\n/**\n * Serialize a Style into a cache key string.\n * Fast path: most styles are simple (256-color or null fg/bg, no true color).\n */\nfunction styleToKey(style: Style): string {\n const fg = style.fg\n const bg = style.bg\n const attrs = style.attrs\n\n // Fast path: common case of simple colors + few attrs\n let key = \"\"\n\n // fg\n if (fg === null) {\n key = \"n\"\n } else if (typeof fg === \"number\") {\n key = `${fg}`\n } else {\n key = `r${fg.r},${fg.g},${fg.b}`\n }\n\n key += \"|\"\n\n // bg\n if (bg === null) {\n key += \"n\"\n } else if (typeof bg === \"number\") {\n key += `${bg}`\n } else {\n key += `r${bg.r},${bg.g},${bg.b}`\n }\n\n // attrs packed as bitmask for speed\n let attrBits = 0\n if (attrs.bold) attrBits |= 1\n if (attrs.dim) attrBits |= 2\n if (attrs.italic) attrBits |= 4\n if (attrs.underline) attrBits |= 8\n if (attrs.inverse) attrBits |= 16\n if (attrs.strikethrough) attrBits |= 32\n if (attrs.blink) attrBits |= 64\n if (attrs.hidden) attrBits |= 128\n if (attrs.overline) attrBits |= 256\n\n key += `|${attrBits}`\n\n // Underline style (rare)\n if (attrs.underlineStyle) {\n key += `|u${attrs.underlineStyle}`\n }\n\n // Underline color (rare)\n const ul = style.underlineColor\n if (ul !== null && ul !== undefined) {\n if (typeof ul === \"number\") {\n key += `|l${ul}`\n } else {\n key += `|lr${ul.r},${ul.g},${ul.b}`\n }\n }\n\n // Hyperlink URL (rare)\n if (style.hyperlink) {\n key += `|h${style.hyperlink}`\n }\n\n return key\n}\n\n/**\n * Get the SGR escape string for a style, using the intern cache.\n * Cache hit: O(1) Map lookup + key serialization.\n * Cache miss: builds the SGR string and caches it.\n */\nfunction cachedStyleToAnsi(style: Style, ctx: OutputContext): string {\n const key = styleToKey(style)\n let sgr = ctx.sgrCache.get(key)\n if (sgr !== undefined) return sgr\n sgr = styleToAnsi(style, ctx)\n ctx.sgrCache.set(key, sgr)\n if (ctx.sgrCache.size > 1000) ctx.sgrCache.clear()\n return sgr\n}\n\n/**\n * Compute the minimal SGR transition between two styles.\n *\n * When oldStyle is null (first cell or after reset), falls through to\n * full SGR generation via cachedStyleToAnsi. Otherwise, diffs attribute\n * by attribute and emits only changed SGR codes. Caches the result for\n * each (oldKey, newKey) pair.\n */\nfunction styleTransition(oldStyle: Style | null, newStyle: Style, ctx: OutputContext): string {\n // First cell or after reset — full generation\n if (!oldStyle) return cachedStyleToAnsi(newStyle, ctx)\n\n // Same style — nothing to emit\n if (styleEquals(oldStyle, newStyle)) return \"\"\n\n // Check transition cache\n const oldKey = styleToKey(oldStyle)\n const newKey = styleToKey(newStyle)\n const cacheKey = `${oldKey}\\x00${newKey}`\n const cached = ctx.transitionCache.get(cacheKey)\n if (cached !== undefined) return cached\n\n // Build minimal diff\n const codes: string[] = []\n\n // Check attributes that can only be \"turned off\" via reset or specific off-codes.\n // If an attribute was on and is now off, we need either the off-code or a full reset.\n const oa = oldStyle.attrs\n const na = newStyle.attrs\n\n // Bold and dim share SGR 22 as their off-code, so handle them together\n // to avoid emitting duplicate codes.\n const boldChanged = Boolean(oa.bold) !== Boolean(na.bold)\n const dimChanged = Boolean(oa.dim) !== Boolean(na.dim)\n if (boldChanged || dimChanged) {\n const boldOff = boldChanged && !na.bold\n const dimOff = dimChanged && !na.dim\n if (boldOff || dimOff) {\n // SGR 22 resets both bold and dim\n codes.push(\"22\")\n // Re-enable whichever should stay on\n if (na.bold) codes.push(\"1\")\n if (na.dim) codes.push(\"2\")\n } else {\n // Only turning attributes on\n if (boldChanged && na.bold) codes.push(\"1\")\n if (dimChanged && na.dim) codes.push(\"2\")\n }\n }\n if (Boolean(oa.italic) !== Boolean(na.italic)) {\n codes.push(na.italic ? \"3\" : \"23\")\n }\n\n // Underline: compare both underline flag and underlineStyle. Per-style\n // capability downgrade — unsupported styles fall back to plain SGR 4\n // (single) instead of silently emitting unsupported 4:x sequences.\n const oldUl = Boolean(oa.underline)\n const newUl = Boolean(na.underline)\n const oldUlStyle = oa.underlineStyle ?? false\n const newUlStyle = na.underlineStyle ?? false\n if (oldUl !== newUl || oldUlStyle !== newUlStyle) {\n const effectiveStyle = supportsUnderlineStyle(ctx.caps.underlineStyles, na.underlineStyle)\n ? na.underlineStyle\n : newUl || na.underlineStyle\n ? \"single\"\n : undefined\n const sgrSub = underlineStyleToSgr(effectiveStyle)\n if (sgrSub !== null && sgrSub !== 0 && sgrSub !== 1) {\n codes.push(`4:${sgrSub}`)\n } else if (newUl || effectiveStyle) {\n codes.push(\"4\")\n } else {\n codes.push(\"24\")\n }\n }\n\n if (Boolean(oa.inverse) !== Boolean(na.inverse)) {\n codes.push(na.inverse ? \"7\" : \"27\")\n }\n if (Boolean(oa.hidden) !== Boolean(na.hidden)) {\n codes.push(na.hidden ? \"8\" : \"28\")\n }\n if (Boolean(oa.strikethrough) !== Boolean(na.strikethrough)) {\n codes.push(na.strikethrough ? \"9\" : \"29\")\n }\n // Overline (SGR 53/55). Skip emit when terminal doesn't advertise support —\n // the cell attr is still packed and diffed, but we suppress the escape bytes\n // so older terminals don't render the literal `[53m` sequence as text.\n if (ctx.caps.overline && Boolean(oa.overline) !== Boolean(na.overline)) {\n codes.push(na.overline ? \"53\" : \"55\")\n }\n if (Boolean(oa.blink) !== Boolean(na.blink)) {\n codes.push(na.blink ? \"5\" : \"25\")\n }\n\n // Foreground color — stripped at monochrome tier (hierarchy via attrs)\n if (!colorEquals(oldStyle.fg, newStyle.fg)) {\n if (newStyle.fg === null || ctx.caps.colorLevel === \"mono\") {\n codes.push(\"39\")\n } else {\n codes.push(fgColorCode(newStyle.fg))\n }\n }\n\n // Background color — stripped at monochrome tier (hierarchy via attrs)\n if (!colorEquals(oldStyle.bg, newStyle.bg)) {\n if (newStyle.bg === null || ctx.caps.colorLevel === \"mono\") {\n codes.push(\"49\")\n } else {\n codes.push(bgColorCode(newStyle.bg))\n }\n }\n\n // Underline color (SGR 58/59) — skip for terminals that don't support it\n if (ctx.caps.underlineColor && !colorEquals(oldStyle.underlineColor, newStyle.underlineColor)) {\n if (newStyle.underlineColor === null || newStyle.underlineColor === undefined) {\n // SGR 59 resets underline color\n codes.push(\"59\")\n } else if (typeof newStyle.underlineColor === \"number\") {\n codes.push(`58;5;${newStyle.underlineColor}`)\n } else {\n codes.push(\n `58;2;${newStyle.underlineColor.r};${newStyle.underlineColor.g};${newStyle.underlineColor.b}`,\n )\n }\n }\n\n // Hyperlink (OSC 8) is handled separately in the render loops, not here.\n\n let result: string\n if (codes.length === 0) {\n // Styles differ but no SGR codes emitted (e.g., hyperlink-only change).\n // Fall back to full generation to be safe.\n result = cachedStyleToAnsi(newStyle, ctx)\n } else {\n result = `\\x1b[${codes.join(\";\")}m`\n }\n\n ctx.transitionCache.set(cacheKey, result)\n if (ctx.transitionCache.size > 1000) ctx.transitionCache.clear()\n return result\n}\n\n/**\n * Internal: exported for use by output-modes.ts (hybrid output emitters).\n * Not part of the public API — do not use outside the pipeline.\n */\nexport const _internalStyleTransition = styleTransition\n\n/**\n * Map underline style to SGR 4:x subparameter.\n */\nfunction underlineStyleToSgr(style: UnderlineStyle | undefined): number | null {\n switch (style) {\n case false:\n return 0 // SGR 4:0 = no underline\n case \"single\":\n return 1 // SGR 4:1 = single underline\n case \"double\":\n return 2 // SGR 4:2 = double underline\n case \"curly\":\n return 3 // SGR 4:3 = curly underline\n case \"dotted\":\n return 4 // SGR 4:4 = dotted underline\n case \"dashed\":\n return 5 // SGR 4:5 = dashed underline\n default:\n return null // Use simple SGR 4 or no underline\n }\n}\n\n/**\n * Diff two buffers and produce minimal ANSI output.\n *\n * @param prev Previous buffer (null on first render)\n * @param next Current buffer\n * @param mode Render mode: fullscreen or inline\n * @param scrollbackOffset Lines written to stdout between renders (inline mode)\n * @param termRows Terminal height in rows (inline mode) — caps output to prevent\n * scrollback corruption when content exceeds terminal height\n * @returns ANSI escape sequence string\n */\nexport function outputPhase(\n prev: TerminalBuffer | null,\n next: TerminalBuffer,\n mode: \"fullscreen\" | \"inline\" = \"fullscreen\",\n scrollbackOffset = 0,\n termRows?: number,\n cursorPos?: CursorState | null,\n _inlineState?: InlineCursorState,\n _ctx?: OutputContext,\n _accState?: AccumulateState,\n _tvState?: TerminalVerifyState,\n): string {\n // Bare outputPhase() calls use a fresh cursor state each time.\n // prevCursorRow = -1 means incremental rendering always falls back to full render.\n // Instance-scoped state (via createOutputPhase) enables incremental across frames.\n const inlineState = _inlineState ?? createInlineCursorState()\n const ctx = _ctx ?? defaultContext\n const accState = _accState ?? defaultAccState\n\n // Set per-frame viewport state on the context — internal functions read from ctx.\n // For bare outputPhase() calls (no createOutputPhase), this updates defaultContext\n // each call. For scoped calls, ctx.mode/termRows were already set by the closure.\n ctx.mode = mode\n ctx.termRows = termRows\n const tvState = _tvState ?? defaultTerminalVerifyState\n\n // After resetInlineState (e.g., useScrollback cleared and re-emitted frozen items),\n // treat the next render as a first render. The cursor is at a known position\n // (right after the re-emitted frozen items) and prev is stale.\n if (mode === \"inline\" && inlineState.forceFirstRender) {\n inlineState.forceFirstRender = false\n prev = null // may already be null (runtime.invalidate on resize), consume flag regardless\n }\n\n // First render: output entire buffer\n if (!prev) {\n // Accumulate timing for full-render path\n const fullRenderAcc = (globalThis as any).__silvery_bench_output_detail\n if (fullRenderAcc) {\n fullRenderAcc.fullRenderCalls = (fullRenderAcc.fullRenderCalls ?? 0) + 1\n fullRenderAcc.fullRenderCells =\n (fullRenderAcc.fullRenderCells ?? 0) + next.width * next.height\n }\n // Inline mode resize optimization: if the runtime invalidated prevBuffer (resize)\n // but we have a stored buffer with matching dimensions, use incremental rendering\n // instead of clear+full render. This avoids wiping content when the buffer is unchanged\n // (e.g., content narrower than both old and new terminal widths).\n if (mode === \"inline\" && inlineState.prevBuffer && inlineState.prevCursorRow >= 0) {\n const stored = inlineState.prevBuffer\n if (stored.width === next.width && stored.height === next.height) {\n // Dimensions match — use incremental rendering (skip clear entirely)\n inlineState.prevBuffer = next\n setLastOutputPhaseDiagnostics({\n reason: \"inline-resize-incremental\",\n mode,\n width: next.width,\n height: next.height,\n prevWidth: stored.width,\n prevHeight: stored.height,\n })\n return inlineIncrementalRender(\n inlineState,\n stored,\n next,\n scrollbackOffset,\n cursorPos,\n ctx,\n tvState,\n accState,\n )\n }\n }\n\n // Cap output to terminal height to prevent scroll desync.\n // In inline mode: prevents scrollback corruption (cursor-up clamped at row 0).\n // In fullscreen mode: prevents terminal scroll that desynchronizes prevBuffer\n // from actual terminal state, causing ghost pixels on subsequent incremental renders.\n const tFullStart = performance.now()\n const firstOutput = bufferToAnsi(next, ctx, termRows)\n const tFullEnd = performance.now()\n if (fullRenderAcc) {\n fullRenderAcc.fullRenderMs = (fullRenderAcc.fullRenderMs ?? 0) + (tFullEnd - tFullStart)\n fullRenderAcc.fullRenderBytes = (fullRenderAcc.fullRenderBytes ?? 0) + firstOutput.length\n }\n // For inline first render, append cursor positioning and initialize tracking\n if (mode === \"inline\") {\n const firstContentLines = findLastContentLine(next) + 1\n const firstMaxOutput =\n termRows != null ? Math.min(firstContentLines, termRows) : firstContentLines\n let firstStartLine = 0\n if (termRows != null && firstContentLines > termRows)\n firstStartLine = firstContentLines - termRows\n\n // Resize: clear the entire visible screen and re-render.\n // Terminal reflow is unpredictable — lines wrap differently based on content,\n // unicode, wrap points. Rather than guessing cursor-up distances, overshoot:\n // ESC[nA is clamped at row 0 of the visible screen (can't touch scrollback),\n // so using termRows is safe. Frozen scrollback content above is preserved.\n let prefix = \"\"\n if (inlineState.prevCursorRow >= 0) {\n const clearDistance =\n termRows ?? Math.max(inlineState.prevCursorRow, inlineState.prevOutputLines - 1)\n if (clearDistance > 0) {\n prefix += `\\x1b[${clearDistance}A`\n }\n prefix += \"\\r\\x1b[J\" // column 0, clear from cursor to end of screen\n }\n\n // Initialize STRICT verification states using a FULLSCREEN-mode rendering\n // of the buffer. The persistent emulators (xterm/ghostty) and accumulated\n // ANSI tracker only verify buffer-diff correctness — they don't need to\n // mirror the terminal's actual cursor-relative state. Using fullscreen\n // output (absolute CUP positioning) gives a canonical reference frame\n // unaffected by inline cursor management (relative positioning,\n // scrollback promotion, prevCursorRow tracking). See the matching block\n // in inlineIncrementalRender for the per-frame counterpart.\n if (isStrictAccumulate() || tvState.backends.length > 0) {\n const savedMode = ctx.mode\n ctx.mode = \"fullscreen\"\n const fsFirstOutput = bufferToAnsi(next, ctx)\n if (isStrictAccumulate()) {\n accState.accumulatedAnsi = fsFirstOutput\n accState.accumulateWidth = next.width\n accState.accumulateHeight = next.height\n accState.accumulateFrameCount = 0\n }\n if (tvState.backends.length > 0) {\n initTerminalVerifyState(tvState, next.width, next.height, fsFirstOutput)\n }\n ctx.mode = savedMode\n }\n\n inlineState.prevBuffer = next\n updateInlineCursorRow(inlineState, cursorPos, firstMaxOutput, firstStartLine)\n setLastOutputPhaseDiagnostics({\n reason: \"inline-first-render\",\n mode,\n width: next.width,\n height: next.height,\n prevWidth: 0,\n prevHeight: 0,\n changedCells: renderedCellCount(next, termRows),\n rawChangedCells: renderedCellCount(next, termRows),\n dirtyRows: Math.min(next.height, termRows ?? next.height),\n })\n const firstCursorSuffix = inlineCursorSuffix(cursorPos ?? null, next, ctx)\n const output = prefix + firstOutput + firstCursorSuffix\n const target = cursorPos\n ? { x: cursorPos.x, y: cursorPos.y, visible: cursorPos.visible, shape: cursorPos.shape }\n : null\n const expectedTerminal =\n cursorPos && cursorPos.visible\n ? {\n x: cursorPos.x,\n y: cursorPos.y - firstStartLine,\n visible: true,\n shape: cursorPos.shape,\n }\n : target\n recordOutputCursorDiagnostics({\n reason: \"inline-first-render\",\n mode,\n width: next.width,\n height: next.height,\n termRows,\n output,\n target,\n expectedTerminal,\n })\n return output\n }\n if (isStrictAccumulate()) {\n accState.accumulatedAnsi = firstOutput\n accState.accumulateWidth = next.width\n accState.accumulateHeight = next.height\n accState.accumulateFrameCount = 0\n }\n if (tvState.backends.length > 0) {\n initTerminalVerifyState(tvState, next.width, next.height, firstOutput)\n }\n if (CAPTURE_RAW) {\n try {\n // sync-node-builtin: multi-target lazy guard via getBuiltinModule (ESM-only wave-3).\n const fs = process.getBuiltinModule(\"node:fs\")\n _captureRawFrameCount = 0\n // Write initial render with frame separator\n fs.writeFileSync(\"/tmp/silvery-raw.ansi\", firstOutput)\n fs.writeFileSync(\n \"/tmp/silvery-raw-frames.jsonl\",\n JSON.stringify({\n frame: 0,\n type: \"full\",\n bytes: firstOutput.length,\n width: next.width,\n height: next.height,\n }) + \"\\n\",\n )\n } catch {}\n }\n setLastOutputPhaseDiagnostics({\n reason: \"first-render\",\n mode,\n width: next.width,\n height: next.height,\n prevWidth: 0,\n prevHeight: 0,\n changedCells: renderedCellCount(next, termRows),\n rawChangedCells: renderedCellCount(next, termRows),\n dirtyRows: Math.min(next.height, termRows ?? next.height),\n })\n return firstOutput\n }\n\n // Inline mode: use incremental rendering when safe, fall back to full render.\n if (mode === \"inline\") {\n inlineState.prevBuffer = next\n setLastOutputPhaseDiagnostics({\n reason: \"inline-incremental\",\n mode,\n width: next.width,\n height: next.height,\n prevWidth: prev.width,\n prevHeight: prev.height,\n })\n return inlineIncrementalRender(\n inlineState,\n prev,\n next,\n scrollbackOffset,\n cursorPos,\n ctx,\n tvState,\n accState,\n )\n }\n\n // SILVERY_FULL_RENDER: bypass incremental diff, always render full buffer.\n // Use to diagnose garbled rendering — if FULL_RENDER fixes it, the bug\n // is in changesToAnsi (diff → ANSI serialization).\n if (FULL_RENDER) {\n setLastOutputPhaseDiagnostics({\n reason: \"forced-full-render\",\n mode,\n width: next.width,\n height: next.height,\n prevWidth: prev.width,\n prevHeight: prev.height,\n changedCells: renderedCellCount(next, termRows),\n rawChangedCells: renderedCellCount(next, termRows),\n dirtyRows: Math.min(next.height, termRows ?? next.height),\n })\n return bufferToAnsi(next, ctx, termRows)\n }\n\n // Dimension change: fall back to full render.\n // When prev and next buffers have different dimensions (e.g., terminal resize\n // without prevBuffer invalidation, or test renderer dimension changes),\n // incremental diff produces CUP sequences targeting positions beyond the\n // current terminal bounds. Terminals clamp out-of-bounds CUP to the last\n // valid row/column, causing stale cells from the diff's shrink-region clears\n // to overwrite valid content at the terminal edge.\n if (prev.width !== next.width || prev.height !== next.height) {\n setLastOutputPhaseDiagnostics({\n reason: \"dimension-mismatch\",\n mode,\n width: next.width,\n height: next.height,\n prevWidth: prev.width,\n prevHeight: prev.height,\n changedCells: renderedCellCount(next, termRows),\n rawChangedCells: renderedCellCount(next, termRows),\n dirtyRows: Math.min(next.height, termRows ?? next.height),\n })\n return bufferToAnsi(next, ctx, termRows)\n }\n\n // Fullscreen mode: diff and emit only changes\n const tDiff0 = performance.now()\n const { pool, count: rawCount } = diffBuffers(prev, next)\n const tDiff1 = performance.now()\n\n // Filter out changes beyond terminal height to prevent CUP targeting rows\n // past the terminal, which causes scrolling and prevBuffer desync.\n let count = rawCount\n if (termRows != null) {\n let writeIdx = 0\n for (let i = 0; i < rawCount; i++) {\n if (pool[i]!.y < termRows) {\n pool[writeIdx++] = pool[i]!\n }\n }\n count = writeIdx\n }\n\n if (DEBUG_OUTPUT) {\n log.error?.(\n `diffBuffers: ${count} changes${rawCount !== count ? ` (${rawCount - count} clamped beyond termRows)` : \"\"}`,\n )\n const debugLimit = Math.min(count, 10)\n for (let i = 0; i < debugLimit; i++) {\n const change = pool[i]!\n log.error?.(` (${change.x},${change.y}): \"${change.cell.char}\"`)\n }\n if (count > 10) {\n log.error?.(` ... and ${count - 10} more`)\n }\n }\n\n if (count === 0) {\n // Accumulate timing even for zero-change frames\n const acc = (globalThis as any).__silvery_bench_output_detail\n if (acc) {\n acc.diffMs += tDiff1 - tDiff0\n acc.calls += 1\n }\n setLastOutputPhaseDiagnostics({\n reason: \"zero-diff\",\n mode,\n width: next.width,\n height: next.height,\n prevWidth: prev.width,\n prevHeight: prev.height,\n changedCells: 0,\n rawChangedCells: rawCount,\n dirtyRows: 0,\n })\n return \"\" // No changes\n }\n\n // Wide characters are handled atomically in changesToAnsi():\n // - Wide char main cells emit the character and advance cursor by 2\n // - Continuation cells are skipped (handled with their main cell)\n // - Orphaned continuation cells (main cell unchanged) trigger a\n // re-emit of the main cell from the buffer\n const tAnsi0 = performance.now()\n const nativeScrollPlan = ctx.mode === \"fullscreen\" ? detectNativeScrollPlan(prev, next) : null\n let incrOutput: string\n let diagnosticReason: OutputPhaseDiagnostics[\"reason\"] = \"diff\"\n let diagnosticChangedCells = count\n let diagnosticRawChangedCells = rawCount\n let diagnosticDirtyRows = changedRowCount(pool, count)\n if (nativeScrollPlan) {\n const { pool: nativePool, count: nativeCount } = diffBuffers(nativeScrollPlan.afterScroll, next)\n let filteredNativeCount = nativeCount\n if (termRows != null) {\n let writeIdx = 0\n for (let i = 0; i < nativeCount; i++) {\n if (nativePool[i]!.y < termRows) {\n nativePool[writeIdx++] = nativePool[i]!\n }\n }\n filteredNativeCount = writeIdx\n }\n incrOutput =\n nativeScrollPrefix(nativeScrollPlan) +\n (filteredNativeCount === 0\n ? \"\"\n : changesToAnsi(nativePool, filteredNativeCount, ctx, next, 0, Infinity, prev).output)\n diagnosticReason = \"native-scroll-diff\"\n diagnosticChangedCells = filteredNativeCount\n diagnosticRawChangedCells = nativeCount\n diagnosticDirtyRows = changedRowCount(nativePool, filteredNativeCount)\n } else {\n incrOutput = changesToAnsi(pool, count, ctx, next, 0, Infinity, prev).output\n }\n const tAnsi1 = performance.now()\n\n // Accumulate output-phase sub-timing for benchmarks\n const outputDetailAcc = (globalThis as any).__silvery_bench_output_detail\n if (outputDetailAcc) {\n outputDetailAcc.diffMs += tDiff1 - tDiff0\n outputDetailAcc.ansiMs += tAnsi1 - tAnsi0\n outputDetailAcc.calls += 1\n outputDetailAcc.totalChanges += count\n // Count dirty rows\n let dirtyRowCount = 0\n const minRow = next.minDirtyRow\n const maxRow = next.maxDirtyRow\n if (minRow >= 0) {\n for (let r = minRow; r <= maxRow; r++) {\n if (next.isRowDirty(r)) dirtyRowCount++\n }\n }\n outputDetailAcc.dirtyRows += dirtyRowCount\n outputDetailAcc.outputBytes += incrOutput.length\n }\n\n // Log output sizes when debug or strict-accumulate is enabled\n if (DEBUG_OUTPUT || isStrictAccumulate()) {\n const bytes = Buffer.byteLength(incrOutput)\n try {\n // sync-node-builtin: multi-target lazy guard via getBuiltinModule (ESM-only wave-3).\n const fs = process.getBuiltinModule(\"node:fs\")\n fs.appendFileSync(\n \"/tmp/silvery-sizes.log\",\n `changesToAnsi: ${count} changes, ${bytes} bytes\\n`,\n )\n } catch {}\n }\n\n // Debug capture: write both incremental and fresh ANSI to files for comparison.\n if (DEBUG_CAPTURE) {\n _debugFrameCount++\n try {\n // sync-node-builtin: multi-target lazy guard via getBuiltinModule (ESM-only wave-3).\n const fs = process.getBuiltinModule(\"node:fs\")\n const freshOutput = bufferToAnsi(next, ctx)\n const freshPrev = prev ? bufferToAnsi(prev, ctx) : \"\"\n // Replay incremental on top of fresh prev\n const w = Math.max(prev?.width ?? next.width, next.width)\n const h = Math.max(prev?.height ?? next.height, next.height)\n const screenIncr = replayAnsiWithStyles(w, h, freshPrev + incrOutput, ctx)\n const screenFresh = replayAnsiWithStyles(w, h, freshOutput, ctx)\n // Find first mismatch\n let mismatchInfo = \"\"\n for (let y = 0; y < h && !mismatchInfo; y++) {\n for (let x = 0; x < w && !mismatchInfo; x++) {\n const ic = screenIncr[y]?.[x]\n const fc = screenFresh[y]?.[x]\n if (\n ic &&\n fc &&\n (ic.char !== fc.char || !sgrColorEquals(ic.fg, fc.fg) || !sgrColorEquals(ic.bg, fc.bg))\n ) {\n mismatchInfo = `MISMATCH at (${x},${y}): incr='${ic.char}' fresh='${fc.char}' incrFg=${formatColor(ic.fg)} freshFg=${formatColor(fc.fg)} incrBg=${formatColor(ic.bg)} freshBg=${formatColor(fc.bg)}`\n // Show row context\n const incrRow = screenIncr[y]!.map((c) => c.char).join(\"\")\n const freshRow = screenFresh[y]!.map((c) => c.char).join(\"\")\n mismatchInfo += `\\n incr row ${y}: ${incrRow.slice(Math.max(0, x - 20), x + 40)}\\n fresh row ${y}: ${freshRow.slice(Math.max(0, x - 20), x + 40)}`\n }\n }\n }\n const status = mismatchInfo || \"MATCH\"\n fs.appendFileSync(\n \"/tmp/silvery-capture.log\",\n `Frame ${_debugFrameCount}: ${count} changes, ${status}\\n`,\n )\n if (mismatchInfo) {\n fs.writeFileSync(`/tmp/silvery-incr-${_debugFrameCount}.ansi`, freshPrev + incrOutput)\n fs.writeFileSync(`/tmp/silvery-fresh-${_debugFrameCount}.ansi`, freshOutput)\n fs.appendFileSync(\n \"/tmp/silvery-capture.log\",\n ` Saved ANSI files: /tmp/silvery-incr-${_debugFrameCount}.ansi and /tmp/silvery-fresh-${_debugFrameCount}.ansi\\n`,\n )\n }\n } catch (e) {\n try {\n // sync-node-builtin: multi-target lazy guard via getBuiltinModule (ESM-only wave-3).\n process\n .getBuiltinModule(\"node:fs\")\n .appendFileSync(\"/tmp/silvery-capture.log\", `Frame ${_debugFrameCount}: ERROR ${e}\\n`)\n } catch {}\n }\n }\n\n // vt100 output verification: verify that the incremental ANSI output produces\n // the same visible terminal state as a fresh render. Uses the internal\n // replayAnsiWithStyles parser (stateless). Enabled by SILVERY_STRICT or\n // SILVERY_STRICT_TERMINAL containing \"vt100\".\n if (isStrictOutput() || tvState.hasVt100) {\n _verifyOutputEquivalence(\n prev,\n next,\n incrOutput,\n ctx,\n bufferToAnsi,\n outputGraphemeWidth,\n outputTextSizingEnabled,\n )\n }\n\n // SILVERY_STRICT_ACCUMULATE: verify that the accumulated output from ALL frames\n // produces the same terminal state as a fresh render of the current buffer.\n // Catches compounding errors that per-frame verification misses.\n if (isStrictAccumulate()) {\n accState.accumulatedAnsi += incrOutput\n accState.accumulateFrameCount++\n _verifyAccumulatedOutput(next, ctx, accState, bufferToAnsi)\n }\n\n // SILVERY_STRICT_TERMINAL (xterm/ghostty backends): verify via independent\n // terminal emulators that the cumulative incremental ANSI output produces the\n // same terminal state as a fresh full render. Unlike the vt100 backend (which\n // uses replayAnsiWithStyles — the same ANSI parser as the output generator),\n // these feed output through real terminal emulators, catching bugs where our\n // parser and generator agree but a real terminal disagrees (e.g., OSC 66, wide\n // char cursor drift, buffer overflow scrolling).\n if (tvState.backends.length > 0 && (tvState.terminal || tvState.ghosttyTerminal)) {\n tvState.frameCount++\n _verifyTerminalEquivalence(tvState, incrOutput, next, ctx, bufferToAnsi)\n\n // ───────────────────────────────────────────────────────────────────────\n // Render/emulator divergence canary (SILVERY_STRICT slug: \"divergence\")\n // ───────────────────────────────────────────────────────────────────────\n //\n // The frame's cumulative ANSI has now been fed into the in-process\n // emulator (above). Read it back: if silvery painted a real content\n // frame (`next.countPaintedCells()`) but the emulator screen is\n // all-spaces, that's the 19604 blank-screen signature — a full frame\n // emitted to a blank screen. Tier 2 throws; tier 1 logs. NO-OP for\n // live TTYs (they never enter this in-process-emulator block).\n // Bead: @km/code/v0.2/19604-focus-blank.\n const divergenceTerm = tvState.terminal ?? tvState.ghosttyTerminal\n if (divergenceTerm) {\n checkRenderEmulatorDivergence(\n next,\n {\n nonSpaceCells: countNonSpaceInText(divergenceTerm.getText()),\n backendName: tvState.terminal ? \"xterm\" : \"ghostty\",\n },\n tvState.frameCount,\n )\n }\n }\n\n if (CAPTURE_RAW) {\n try {\n // sync-node-builtin: multi-target lazy guard via getBuiltinModule (ESM-only wave-3).\n const fs = process.getBuiltinModule(\"node:fs\")\n _captureRawFrameCount++\n // Append output to cumulative ANSI file\n fs.appendFileSync(\"/tmp/silvery-raw.ansi\", incrOutput)\n // Also save the fresh render of this frame for comparison\n const freshOutput = bufferToAnsi(next, ctx)\n fs.writeFileSync(`/tmp/silvery-raw-fresh-${_captureRawFrameCount}.ansi`, freshOutput)\n fs.appendFileSync(\n \"/tmp/silvery-raw-frames.jsonl\",\n JSON.stringify({\n frame: _captureRawFrameCount,\n type: \"incremental\",\n changes: count,\n bytes: incrOutput.length,\n width: next.width,\n height: next.height,\n }) + \"\\n\",\n )\n } catch {}\n }\n\n setLastOutputPhaseDiagnostics({\n reason: diagnosticReason,\n mode,\n width: next.width,\n height: next.height,\n prevWidth: prev.width,\n prevHeight: prev.height,\n changedCells: diagnosticChangedCells,\n rawChangedCells: diagnosticRawChangedCells,\n dirtyRows: diagnosticDirtyRows,\n })\n return incrOutput\n}\n\n/**\n * Check if a line has any non-space content or styling.\n * A row with only spaces but with background color or other styling\n * (bold, inverse, underline, etc.) is visually meaningful.\n */\nfunction lineHasContent(buffer: TerminalBuffer, y: number): boolean {\n for (let x = 0; x < buffer.width; x++) {\n const ch = buffer.getCellChar(x, y)\n if (ch !== \" \" && ch !== \"\") return true\n // Styled blank cells are visually meaningful:\n // - background color (colored spacer rows)\n // - inverse (visible block of fg color)\n // - underline (visible line under space)\n // - strikethrough (visible line through space)\n const bg = buffer.getCellBg(x, y)\n if (bg !== null) return true\n if (buffer.getCellAttrs(x, y) & VISIBLE_SPACE_ATTR_MASK) return true\n }\n return false\n}\n\n/**\n * Find the last line with content in the buffer.\n */\nfunction findLastContentLine(buffer: TerminalBuffer): number {\n for (let y = buffer.height - 1; y >= 0; y--) {\n if (lineHasContent(buffer, y)) {\n return y\n }\n }\n return 0 // At least render first line\n}\n\n/**\n * Compute the ANSI suffix that positions the real terminal cursor for inline mode.\n *\n * After inline rendering, the terminal cursor sits at the end of the last\n * content line. If a component used useCursor(), we move the cursor to\n * that position (relative to the rendered output). Otherwise we just show\n * the cursor at its current position.\n *\n * @param cursorPos The cursor state from useCursor() (or null if none)\n * @param buffer The rendered buffer\n * @param ctx Output context (termRows read from ctx.termRows)\n */\nfunction inlineCursorSuffix(\n cursorPos: CursorState | null | undefined,\n buffer: TerminalBuffer,\n ctx: OutputContext,\n): string {\n const { termRows } = ctx\n if (!cursorPos) {\n // No active cursor — hide it\n return \"\\x1b[?25l\"\n }\n\n // Determine the visible row range (same logic as bufferToAnsi for inline)\n const lastContentLine = findLastContentLine(buffer)\n const maxLine = lastContentLine\n let startLine = 0\n const maxOutputLines =\n termRows != null ? Math.min(lastContentLine + 1, termRows) : lastContentLine + 1\n if (termRows != null && maxLine >= termRows) {\n startLine = maxLine - termRows + 1\n }\n\n // Convert absolute buffer cursor position to visible row index\n const visibleRow = cursorPos.y - startLine\n if (visibleRow < 0 || visibleRow >= maxOutputLines) {\n // Cursor is outside the visible area (scrolled off) — hide it\n return \"\\x1b[?25l\"\n }\n\n // After rendering, the terminal cursor is at the end of the last output line.\n // The last output line is at visible row (maxOutputLines - 1).\n const currentRow = maxOutputLines - 1\n const rowDelta = currentRow - visibleRow\n\n let suffix = \"\"\n // Move up to the correct row\n if (rowDelta > 0) {\n suffix += `\\x1b[${rowDelta}A`\n }\n // Move to column 0, then right to the correct column\n suffix += \"\\r\"\n if (cursorPos.x > 0) {\n suffix += `\\x1b[${cursorPos.x}C`\n }\n if (!cursorPos.visible) {\n suffix += \"\\x1b[?25l\"\n return suffix\n }\n // Show cursor with the requested shape. Fullscreen mode emits the same\n // DECSCUSR sequence in scheduler/runtime; inline mode needs to do it here\n // because inline cursor positioning is owned by output-phase.\n suffix += cursorPos.shape ? setCursorStyle(cursorPos.shape) : resetCursorStyle()\n suffix += \"\\x1b[?25h\"\n return suffix\n}\n\n/**\n * Incremental rendering for inline mode.\n *\n * When conditions are safe (no external writes, dimensions unchanged),\n * diffs prev/next buffers and emits only changed cells using relative\n * cursor positioning. Falls back to full render otherwise.\n *\n * This reduces output from ~5,848 bytes (full re-render at 50 items)\n * to ~50-100 bytes per keystroke, matching fullscreen efficiency.\n */\nfunction inlineIncrementalRender(\n state: InlineCursorState,\n prev: TerminalBuffer,\n next: TerminalBuffer,\n scrollbackOffset: number,\n cursorPos?: CursorState | null,\n ctx: OutputContext = defaultContext,\n tvState?: TerminalVerifyState,\n accState?: AccumulateState,\n): string {\n const { termRows } = ctx\n // Guard: fall back to full render for complex cases\n if (\n scrollbackOffset > 0 ||\n prev.width !== next.width ||\n prev.height !== next.height ||\n state.prevCursorRow < 0\n ) {\n return inlineFullRender(state, prev, next, scrollbackOffset, cursorPos, ctx, tvState, accState)\n }\n\n const nextContentLines = findLastContentLine(next) + 1\n const prevContentLines = findLastContentLine(prev) + 1\n\n // Compute visible ranges for both prev and next content\n const prevMaxOutputLines =\n termRows != null ? Math.min(prevContentLines, termRows) : prevContentLines\n const maxOutputLines = termRows != null ? Math.min(nextContentLines, termRows) : nextContentLines\n let prevStartLine = 0\n if (termRows != null && prevContentLines > termRows) {\n prevStartLine = prevContentLines - termRows\n }\n let startLine = 0\n if (termRows != null && nextContentLines > termRows) {\n startLine = nextContentLines - termRows\n }\n\n // When the visible window shifts (content exceeds termRows and startLine changes),\n // the entire visible region is different — fall back to full render.\n if (startLine !== prevStartLine) {\n return inlineFullRender(state, prev, next, scrollbackOffset, cursorPos, ctx, tvState, accState)\n }\n\n // Diff buffers\n const { pool, count } = diffBuffers(prev, next)\n if (count === 0 && nextContentLines === prevContentLines) {\n // No buffer changes, but cursor position may have changed.\n // Emit cursor suffix to update the terminal cursor.\n const suffix = inlineCursorSuffix(cursorPos ?? null, next, ctx)\n updateInlineCursorRow(state, cursorPos, maxOutputLines, startLine)\n return suffix\n }\n\n // Move cursor from tracked row to row 0 of render region\n let output = \"\"\n if (state.prevCursorRow > 0) {\n output += `\\x1b[${state.prevCursorRow}A`\n }\n output += \"\\r\"\n output += \"\\x1b[?25l\" // hide cursor during update\n\n // Emit changes with relative positioning.\n // Use the larger of prev/next output lines so changesToAnsi processes all\n // visible cells including rows that shrank (need clearing) or grew (need writing).\n const effectiveOutputLines = Math.max(prevMaxOutputLines, maxOutputLines)\n const changes = changesToAnsi(pool, count, ctx, next, startLine, effectiveOutputLines, prev)\n output += changes.output\n\n // After changesToAnsi, cursor is at changes.finalY (render-relative).\n // We need to position cursor at the effective bottom row, then handle\n // growth/shrinkage, and end at (maxOutputLines - 1) for inlineCursorSuffix.\n const finalY = changes.finalY\n const prevBottomRow = prevMaxOutputLines - 1\n const bottomRow = maxOutputLines - 1\n\n if (maxOutputLines > prevMaxOutputLines) {\n // Content grew: rows beyond the previous bottom don't exist on the terminal.\n // CUD (\\x1b[nB) is clamped at the terminal edge and won't create new lines.\n // Use \\r\\n for each new row to ensure the terminal extends naturally.\n //\n // changesToAnsi may have already moved past prevBottomRow using \\r\\n\n // (creating new terminal lines in the process). Only add \\r\\n for\n // rows beyond what changesToAnsi already reached.\n const fromRow = finalY >= 0 ? finalY : 0\n if (fromRow >= bottomRow) {\n // changesToAnsi already reached or passed the new bottom — nothing to do\n } else if (fromRow >= prevBottomRow) {\n // Cursor already past old bottom (changesToAnsi extended the terminal).\n // Only need \\r\\n for remaining rows.\n const remainingRows = bottomRow - fromRow\n for (let i = 0; i < remainingRows; i++) {\n output += \"\\r\\n\"\n }\n } else {\n // Cursor is still within the old content area.\n // First, move to the old bottom row using CUD (safe, rows exist on terminal)\n if (fromRow < prevBottomRow) {\n const dy = prevBottomRow - fromRow\n output += dy === 1 ? \"\\r\\n\" : `\\r\\x1b[${dy}B`\n }\n // Then extend to new bottom with \\r\\n for each new row\n const newRows = bottomRow - prevBottomRow\n for (let i = 0; i < newRows; i++) {\n output += \"\\r\\n\"\n }\n }\n } else if (maxOutputLines < prevMaxOutputLines) {\n // Content shrank: erase orphan lines below the new content.\n // changesToAnsi already wrote empty cells to clear the old content,\n // but we need \\x1b[K to clear any residual characters.\n // The cursor is at finalY after changesToAnsi — move to the new bottom first.\n const fromRow = finalY >= 0 ? finalY : 0\n if (fromRow < bottomRow) {\n const dy = bottomRow - fromRow\n output += dy === 1 ? \"\\r\\n\" : `\\r\\x1b[${dy}B`\n } else if (fromRow > bottomRow) {\n // Cursor is past the new bottom (was erasing orphan rows) — move back up\n output += `\\x1b[${fromRow - bottomRow}A`\n }\n // Now at new bottom row (bottomRow). Erase orphan lines below by moving\n // down one line at a time, erasing each. This leaves cursor at the last\n // orphan row (prevMaxOutputLines - 1).\n const orphanCount = prevMaxOutputLines - maxOutputLines\n for (let y = 0; y < orphanCount; y++) {\n output += \"\\n\\r\\x1b[K\"\n }\n // Move back up from last orphan row to new bottom row\n if (orphanCount > 0) output += `\\x1b[${orphanCount}A`\n } else {\n // Same height: move to bottom row if not already there\n if (finalY >= 0 && finalY < bottomRow) {\n const dy = bottomRow - finalY\n output += dy === 1 ? \"\\r\\n\" : `\\r\\x1b[${dy}B`\n }\n }\n\n output += inlineCursorSuffix(cursorPos ?? null, next, ctx)\n\n // STRICT verification for inline incremental renders.\n //\n // Inline mode uses relative cursor positioning (CUU/CUD/CR) instead of\n // absolute CUP. The inline `output` we just built is what the real terminal\n // sees, but it can't be fed directly to verifyOutputEquivalence (which\n // replays freshPrev + incrOutput — the cursor position after freshPrev\n // differs from the tracked prevCursorRow), nor to a persistent xterm/ghostty\n // terminal initialized in fullscreen mode (cursor positioning would diverge\n // from frame 1).\n //\n // The strategy: re-diff prev → next in FULLSCREEN mode and verify with that\n // canonical output. This is sound because all four verifiers ask the same\n // invariant — \"does applying the diff produce the same buffer state as a\n // fresh full render?\" — which is independent of cursor positioning. The\n // matching first-render block in outputPhase initializes tvState/accState\n // with a fullscreen-mode bufferToAnsi() so the persistent state is\n // canonical, fullscreen-style, across the whole session.\n //\n // Coverage: this catches buffer-diff regressions (changesToAnsi correctness,\n // OSC 66 / wide-char drift, SGR transitions) at the same fidelity as\n // fullscreen mode. It does NOT catch inline-specific cursor-positioning\n // bugs (relative CUU/CUD math, scrollback promotion, useCursor placement)\n // — those are covered by tests/inline-mode.test.ts and the rest of the\n // inline test suite, not by replay-equivalence STRICT checks.\n const needsFsDiff =\n isStrictOutput() ||\n tvState?.hasVt100 ||\n (tvState && tvState.backends.length > 0 && (tvState.terminal || tvState.ghosttyTerminal)) ||\n (accState && isStrictAccumulate())\n if (needsFsDiff) {\n const savedMode = ctx.mode\n ctx.mode = \"fullscreen\"\n const fsIncrOutput = changesToAnsi(pool, count, ctx, next, 0, Infinity, prev).output\n if (isStrictOutput() || tvState?.hasVt100) {\n _verifyOutputEquivalence(\n prev,\n next,\n fsIncrOutput,\n ctx,\n bufferToAnsi,\n outputGraphemeWidth,\n outputTextSizingEnabled,\n )\n }\n if (accState && isStrictAccumulate()) {\n accState.accumulatedAnsi += fsIncrOutput\n accState.accumulateFrameCount++\n _verifyAccumulatedOutput(next, ctx, accState, bufferToAnsi)\n }\n if (tvState && tvState.backends.length > 0 && (tvState.terminal || tvState.ghosttyTerminal)) {\n tvState.frameCount++\n _verifyTerminalEquivalence(tvState, fsIncrOutput, next, ctx, bufferToAnsi)\n\n // Render/emulator divergence canary (inline path) — same 19604\n // class-killer as the fullscreen path. See the matching block in\n // outputPhase(). NO-OP for live TTYs. Bead: @km/code/v0.2/19604-focus-blank.\n const divergenceTerm = tvState.terminal ?? tvState.ghosttyTerminal\n if (divergenceTerm) {\n checkRenderEmulatorDivergence(\n next,\n {\n nonSpaceCells: countNonSpaceInText(divergenceTerm.getText()),\n backendName: tvState.terminal ? \"xterm\" : \"ghostty\",\n },\n tvState.frameCount,\n )\n }\n }\n ctx.mode = savedMode\n }\n\n // Update tracking\n updateInlineCursorRow(state, cursorPos, maxOutputLines, startLine)\n\n return output\n}\n\n/**\n * Full re-render for inline mode.\n *\n * Moves cursor to the start of the render region, writes the entire\n * buffer fresh, and erases any leftover lines from the previous render.\n *\n * When content exceeds terminal height, output is capped to termRows lines.\n * Lines beyond the terminal can't be managed (cursor-up is clamped at row 0),\n * so we truncate to prevent scrollback corruption.\n */\nfunction inlineFullRender(\n state: InlineCursorState,\n prev: TerminalBuffer,\n next: TerminalBuffer,\n scrollbackOffset: number,\n cursorPos?: CursorState | null,\n ctx: OutputContext = defaultContext,\n tvState?: TerminalVerifyState,\n accState?: AccumulateState,\n): string {\n const { termRows } = ctx\n const nextContentLines = findLastContentLine(next) + 1\n\n // Use tracked state from the previous frame for cursor position and output height.\n // state.prevCursorRow tracks where the terminal cursor actually is (row within render\n // region), and state.prevOutputLines tracks how many lines the previous frame rendered.\n // Re-deriving from the prev buffer can disagree (e.g., when cursor was positioned at a\n // visible row via useCursor, not at the bottom), causing the cursor-up to overshoot\n // and leaving orphan lines below the content (\"inline-bleed\").\n // Fall back to prev-buffer derivation only when cursor tracking is uninitialized.\n let prevOutputLines: number\n let cursorRowInRegion: number\n if (state.prevCursorRow >= 0) {\n prevOutputLines = state.prevOutputLines\n cursorRowInRegion = state.prevCursorRow\n } else {\n const prevContentLines = findLastContentLine(prev) + 1\n prevOutputLines = termRows != null ? Math.min(prevContentLines, termRows) : prevContentLines\n cursorRowInRegion = prevOutputLines - 1\n }\n\n // How far the cursor is below the start of the render region:\n // tracked cursor row + any lines written to stdout between renders.\n // Cap to termRows-1: terminal clamps cursor-up at row 0.\n const rawCursorOffset = cursorRowInRegion + scrollbackOffset\n const cursorOffset =\n termRows != null && !isStrictOutput()\n ? Math.min(rawCursorOffset, termRows - 1)\n : rawCursorOffset\n\n // Cap output at terminal height to prevent scrollback corruption.\n // Content taller than the terminal pushes lines into scrollback where\n // they can never be overwritten (cursor-up is clamped at terminal row 0).\n const maxOutputLines = termRows != null ? Math.min(nextContentLines, termRows) : nextContentLines\n\n // Quick check: if nothing changed and no scrollback displacement, skip\n if (scrollbackOffset === 0) {\n const { count } = diffBuffers(prev, next)\n if (count === 0) return \"\"\n }\n\n // Move cursor up to the start of the render region\n let prefix = \"\"\n if (cursorOffset > 0) {\n prefix = `\\x1b[${cursorOffset}A\\r`\n }\n // bufferToAnsi handles: hide cursor, render content lines with\n // \\x1b[K (clear to EOL) on each line, and reset style at end.\n let output = prefix + bufferToAnsi(next, ctx, maxOutputLines)\n\n // Erase leftover lines if visible area shrank.\n // Account for terminal scroll: when useScrollback writes frozen items and the\n // cursor overflows the terminal, the terminal scrolls up. The scroll amount\n // equals how far rawCursorOffset exceeds termRows-1 (the terminal's last row).\n // That many old render lines were pushed into scrollback and no longer need\n // erasing. The remaining visible old render lines end at lastOccupiedLine.\n // Lines beyond that contain frozen items — those must NOT be erased.\n // Note: computed from terminal geometry, independent of strict-output cursor bypass.\n const terminalScroll = termRows != null ? Math.max(0, rawCursorOffset - (termRows - 1)) : 0\n const lastOccupiedLine = Math.max(prevOutputLines - 1 - terminalScroll, 0)\n const nextLastLine = maxOutputLines - 1\n if (lastOccupiedLine > nextLastLine) {\n for (let y = nextLastLine + 1; y <= lastOccupiedLine; y++) {\n output += \"\\n\\r\\x1b[K\"\n }\n const up = lastOccupiedLine - nextLastLine\n if (up > 0) output += `\\x1b[${up}A`\n }\n\n // Position the real terminal cursor and show it.\n // If a component called useCursor(), place the cursor there.\n // Otherwise, just show it at the current position (end of content).\n output += inlineCursorSuffix(cursorPos ?? null, next, ctx)\n\n // Update cursor tracking for incremental rendering on next frame\n let startLine = 0\n if (termRows != null && nextContentLines > termRows) startLine = nextContentLines - termRows\n updateInlineCursorRow(state, cursorPos, maxOutputLines, startLine)\n\n // Realign STRICT verification state. Inline full-render falls back from\n // incremental for reasons that invalidate the persistent buffer/emulator\n // state (dimension change, scrollback offset, uninitialized cursor). The\n // simplest correct response is to re-initialize the verification state\n // with a fullscreen rendering of `next` so subsequent incremental frames\n // resume comparison against a consistent baseline. See the matching\n // first-render block in outputPhase for the canonical-fullscreen rationale.\n if (tvState || accState) {\n if ((tvState && tvState.backends.length > 0) || (accState && isStrictAccumulate())) {\n const savedMode = ctx.mode\n ctx.mode = \"fullscreen\"\n const fsFresh = bufferToAnsi(next, ctx)\n if (accState && isStrictAccumulate()) {\n accState.accumulatedAnsi = fsFresh\n accState.accumulateWidth = next.width\n accState.accumulateHeight = next.height\n accState.accumulateFrameCount = 0\n }\n if (tvState && tvState.backends.length > 0) {\n initTerminalVerifyState(tvState, next.width, next.height, fsFresh)\n }\n ctx.mode = savedMode\n }\n }\n\n return output\n}\n\n/**\n * Convert entire buffer to ANSI string.\n *\n * Mode is read from ctx.mode. maxRows is an explicit parameter because callers\n * sometimes pass a pre-computed value (e.g., maxOutputLines) rather than raw\n * ctx.termRows.\n *\n * @param maxRows Optional cap on number of rows to output (inline mode).\n * When content exceeds terminal height, this prevents scrollback corruption.\n */\nfunction bufferToAnsi(\n buffer: TerminalBuffer,\n ctx: OutputContext = defaultContext,\n maxRows?: number,\n): string {\n const { mode } = ctx\n let output = \"\"\n let currentStyle: Style | null = null\n let currentHyperlink: string | undefined\n\n // Cap output to prevent rendering beyond terminal height.\n // Inline mode: render up to last content line; if taller than terminal, show bottom\n // (footer and latest content stay visible in scrollback).\n // Fullscreen mode: always start from top; cap at terminal height to prevent scroll\n // that desynchronizes prevBuffer from actual terminal state.\n let maxLine = mode === \"inline\" ? findLastContentLine(buffer) : buffer.height - 1\n let startLine = 0\n if (maxRows != null && maxLine >= maxRows) {\n if (mode === \"fullscreen\") {\n maxLine = maxRows - 1 // cap at terminal height, always from top\n } else {\n startLine = maxLine - maxRows + 1 // show bottom of content\n }\n }\n\n // Move cursor to start position based on mode\n if (mode === \"fullscreen\") {\n // Fullscreen: Move cursor to home position (top-left)\n output += \"\\x1b[H\"\n } else {\n // Inline: Hide cursor, start from current position\n output += \"\\x1b[?25l\"\n }\n\n // Reusable objects to avoid per-cell allocation in the inner loop\n const cell = createMutableCell()\n const cellStyle: Style = {\n fg: null,\n bg: null,\n underlineColor: null,\n attrs: {},\n }\n\n for (let y = startLine; y <= maxLine; y++) {\n // Move to start of line.\n // Fullscreen: CUP (absolute positioning) for rows after startLine.\n // After writing exactly `cols` chars, cursor enters pending-wrap state.\n // \\r goes to col 0 of the CURRENT row (doesn't resolve wrap in real\n // terminals), overwriting the row. CUP is unambiguous across all\n // terminals and our internal ANSI parser (replayAnsiWithStyles).\n // First row skips CUP — cursor is already at home via \\x1b[H.\n // Inline: \\r on EVERY row (including first) to ensure column 0.\n if (mode === \"inline\") {\n output += \"\\r\"\n } else if (y > startLine) {\n output += `\\x1b[${y + 1};1H`\n }\n\n // Render the line content\n for (let x = 0; x < buffer.width; x++) {\n buffer.readCellInto(x, y, cell)\n\n // No continuation skip here. Valid continuation cells are never reached\n // because `if (cell.wide) x++` below jumps past them. Orphaned\n // continuation cells (wide char overwritten by region clear) must NOT\n // be skipped — they write a space to keep the VT cursor in sync.\n\n // Handle OSC 8 hyperlink transitions (separate from SGR style)\n const cellHyperlink = cell.hyperlink\n if (cellHyperlink !== currentHyperlink) {\n if (currentHyperlink) {\n output += \"\\x1b]8;;\\x1b\\\\\" // Close previous hyperlink\n }\n if (cellHyperlink) {\n output += `\\x1b]8;;${cellHyperlink}\\x1b\\\\` // Open new hyperlink\n }\n currentHyperlink = cellHyperlink\n }\n\n // Build style from cell and check if changed.\n // readCellInto mutates cell.attrs in place, so we must snapshot attrs\n // only when the style actually changes (which is rare -- most adjacent\n // cells share the same style). This avoids per-cell object allocation.\n cellStyle.fg = cell.fg\n cellStyle.bg = cell.bg\n cellStyle.underlineColor = cell.underlineColor\n cellStyle.attrs = cell.attrs\n if (!styleEquals(currentStyle, cellStyle)) {\n // Snapshot: copy attrs so currentStyle isn't invalidated by next readCellInto\n const saved: Style = {\n fg: cell.fg,\n bg: cell.bg,\n underlineColor: cell.underlineColor,\n attrs: { ...cell.attrs },\n }\n output += styleTransition(currentStyle, saved, ctx)\n currentStyle = saved\n }\n\n // Write character — empty-string chars are treated as space to ensure\n // the terminal cursor advances (an empty string writes nothing, causing\n // all subsequent characters on the row to shift left by one column).\n const char = cell.char || \" \"\n output += wrapTextSizing(char, cell.wide, ctx)\n\n // Wide characters occupy 2 columns in the terminal. Skip the next cell\n // position since the terminal cursor already advanced by 2. We skip\n // unconditionally (not relying on the next cell's continuation flag)\n // because the buffer may have a corrupted continuation cell — e.g., when\n // an adjacent container's region clear overwrites the continuation.\n // Without this, the non-continuation cell at x+1 would also be written,\n // causing every subsequent character on the row to shift right by 1.\n if (cell.wide) {\n x++\n // Cursor re-sync: some terminals treat multi-codepoint wide chars\n // (flag emoji like 🇨🇦) as two width-1 chars instead of one width-2\n // char. This causes the terminal cursor to be 1 column behind where\n // we expect. Emit an explicit cursor position to re-sync, mirroring\n // the re-sync in changesToAnsi. Cost: ~8 bytes per wide char; wide\n // chars are rare so overhead is negligible.\n // After x++, x points to the continuation cell. The next character\n // to write is at x+1 (after the loop increment), so position the\n // cursor at 0-indexed column x+1 = 1-indexed column x+2.\n if (mode === \"fullscreen\") {\n output += `\\x1b[${y - startLine + 1};${x + 2}H`\n } else {\n // Inline: \\r resets to column 0, CUF moves to expected position.\n // Reset bg first to prevent bleed across traversed cells.\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n const nextCol = x + 1\n output += \"\\r\"\n if (nextCol > 0) output += nextCol === 1 ? \"\\x1b[C\" : `\\x1b[${nextCol}C`\n }\n }\n }\n\n // Close any open hyperlink at end of row\n if (currentHyperlink) {\n output += \"\\x1b]8;;\\x1b\\\\\"\n currentHyperlink = undefined\n }\n\n // Reset style before newline to prevent background color from\n // bleeding into the next line via the terminal's right margin fill.\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n // Note: \\x1b[K (Erase to End of Line) is intentionally omitted.\n // bufferToAnsi writes every cell in the row (buffer.width == terminal cols),\n // so there is nothing beyond the cursor to erase. More importantly, after\n // writing the last column the cursor enters pending-wrap state, and EL\n // behavior in pending-wrap is terminal-dependent — some terminals resolve\n // the wrap first, causing the next line's content to shift down by one row.\n\n // Move to next line (except for last line)\n if (y < maxLine) {\n if (mode === \"inline\") {\n // In inline mode, use \\r\\n to cancel DECAWM pending-wrap state.\n // When the line fills exactly `cols` characters, the cursor enters\n // pending-wrap. A bare \\n in that state causes a double line advance\n // in some terminals (Ghostty, iTerm2). The \\r first moves to column 0\n // (canceling pending-wrap), then \\n advances one row cleanly.\n output += \"\\r\\n\"\n }\n // Fullscreen: no \\n needed — CUP at the start of the next row handles\n // positioning. Bare \\n in pending-wrap state is terminal-dependent\n // (some terminals double-advance, potentially causing scroll at the\n // bottom of the alternate screen).\n }\n }\n\n // Close any open hyperlink at end\n if (currentHyperlink) {\n output += \"\\x1b]8;;\\x1b\\\\\"\n }\n\n // Reset style at end\n output += \"\\x1b[0m\"\n\n return output\n}\n\n// ============================================================================\n// Buffer diffing (extracted to diff-buffers.ts)\n// ============================================================================\n\nimport { diffBuffers } from \"./diff-buffers\"\nimport { analyzeRowDensity, pickEmissionMode } from \"./output-density\"\nimport {\n type OutputEmitState,\n createOutputEmitState,\n emitRuns,\n emitScatter,\n emitWholeRow,\n} from \"./output-modes\"\n\n/** Result from changesToAnsi: ANSI output string and final cursor position. */\ninterface ChangesResult {\n output: string\n /** Final render-relative cursor Y after emitting changes (-1 if no changes emitted). */\n finalY: number\n}\n\n/** Pre-allocated style object reused across changesToAnsi calls. */\nconst reusableCellStyle: Style = {\n fg: null,\n bg: null,\n underlineColor: null,\n attrs: {},\n}\n\n/**\n * Pre-allocated cell for looking up wide char main cells from the buffer\n * when an orphaned continuation cell is encountered in changesToAnsi.\n */\nconst wideCharLookupCell = createMutableCell()\n\n/**\n * Sort a sub-range of the pool by position for optimal cursor movement.\n * Uses a simple in-place sort on pool[0..count).\n */\nfunction sortPoolByPosition(pool: CellChange[], count: number): void {\n // Insertion sort is efficient for the typical case (mostly sorted or small count)\n for (let i = 1; i < count; i++) {\n const item = pool[i]!\n const iy = item.y\n const ix = item.x\n let j = i - 1\n while (j >= 0 && (pool[j]!.y > iy || (pool[j]!.y === iy && pool[j]!.x > ix))) {\n pool[j + 1] = pool[j]!\n j--\n }\n pool[j + 1] = item\n }\n}\n\n// ============================================================================\n// Phase 3 — hybrid output dispatch (SILVERY_HYBRID_OUTPUT=1)\n//\n// Module-scoped emit state, reused across frames so cross-row cursor and\n// style transitions stay zero-allocation. Only one fullscreen renderer reads\n// from this at a time, mirroring the existing module-level `denseRowCell` /\n// `wideCharLookupCell` discipline. Inline mode is intentionally not wired —\n// it has its own per-frame cursor arithmetic.\n// ============================================================================\n\nconst hybridEmitState: OutputEmitState = createOutputEmitState({ isInline: false })\n\n/**\n * Per-row analyze→pick→emit dispatch for fullscreen incremental renders.\n *\n * Pre-conditions:\n * - `pool` is sorted by `(y, x)` (the caller `changesToAnsi` already sorted).\n * - `buffer` is the current `next` buffer; the emitters use it to read main\n * cells for orphan continuations and to fill whole-row spans.\n * - `ctx.mode === \"fullscreen\"`.\n *\n * Outputs the same terminal-state-equivalent ANSI as the legacy emitter; the\n * STRICT verifier compares cell grids, not bytes (see design doc §6).\n */\nfunction hybridChangesToAnsi(\n pool: CellChange[],\n count: number,\n ctx: OutputContext,\n buffer: TerminalBuffer,\n): ChangesResult {\n // Reset emit state. Pool is reused across frames; we never carry cursor\n // tracking from a previous render because `changesToAnsi` is only entered\n // from `outputPhase` after the cursor is implicitly known to be at an\n // arbitrary terminal position. Each render emits its own absolute CUP for\n // the first cell, just like the legacy path's `cursorY === -1` branch.\n hybridEmitState.output = \"\"\n hybridEmitState.cursorX = -1\n hybridEmitState.cursorY = -1\n hybridEmitState.prevY = -1\n hybridEmitState.lastEmittedX = -1\n hybridEmitState.lastEmittedY = -1\n hybridEmitState.currentStyle = null\n hybridEmitState.currentHyperlink = undefined\n hybridEmitState.startLine = 0\n hybridEmitState.isInline = false\n\n const analysis = analyzeRowDensity(pool, count, buffer.width)\n\n // Telemetry — design doc §11 open question 5. Mode counts surface via\n // SILVERY_INSTRUMENT=1 so we can tune the estimator from real workloads.\n const acc = (globalThis as any).__silvery_bench_output_detail\n if (acc) {\n acc.hybridFrames = (acc.hybridFrames ?? 0) + 1\n acc.hybridRows = (acc.hybridRows ?? 0) + analysis.rowCount\n acc.modeCounts = acc.modeCounts ?? { wholeRow: 0, runLength: 0, scatter: 0 }\n }\n\n for (let r = 0; r < analysis.rowCount; r++) {\n const summary = analysis.rows[r]!\n const mode = pickEmissionMode(summary, buffer.width)\n\n if (acc) {\n if (mode === \"whole-row\") acc.modeCounts.wholeRow++\n else if (mode === \"run-length\") acc.modeCounts.runLength++\n else acc.modeCounts.scatter++\n }\n\n switch (mode) {\n case \"whole-row\":\n emitWholeRow(summary, buffer, ctx, hybridEmitState)\n break\n case \"run-length\":\n emitRuns(summary, pool, buffer, ctx, hybridEmitState)\n break\n case \"scatter\":\n emitScatter(summary, pool, buffer, ctx, hybridEmitState)\n break\n }\n }\n\n // Final teardown — match the legacy path's end-of-stream behavior:\n // close any open hyperlink and reset SGR so the terminal returns to a\n // known-clean state.\n if (hybridEmitState.currentHyperlink) {\n hybridEmitState.output += \"\\x1b]8;;\\x1b\\\\\"\n hybridEmitState.currentHyperlink = undefined\n }\n if (hybridEmitState.currentStyle) {\n hybridEmitState.output += \"\\x1b[0m\"\n hybridEmitState.currentStyle = null\n }\n\n return { output: hybridEmitState.output, finalY: hybridEmitState.cursorY }\n}\n\n/**\n * Convert cell changes to optimized ANSI output.\n *\n * Wide characters are handled atomically: the main cell (wide:true) and its\n * continuation cell are treated as a single unit. When the main cell is in\n * the pool, it's emitted and the cursor advances by 2. When only the\n * continuation cell changed (e.g., bg color), the main cell is read from\n * the buffer and emitted to cover both columns.\n *\n * @param pool Pre-allocated pool of CellChange objects\n * @param count Number of valid entries in the pool\n * @param ctx Output context (mode read from ctx.mode)\n * @param buffer The current buffer, used to look up main cells for orphaned\n * continuation cells (optional for backward compatibility)\n * @param startLine For inline mode: first visible buffer row (for termRows capping)\n * @param maxOutputLines For inline mode: number of visible rows\n */\nfunction changesToAnsi(\n pool: CellChange[],\n count: number,\n ctx: OutputContext = defaultContext,\n buffer?: TerminalBuffer,\n startLine = 0,\n maxOutputLines = Infinity,\n prevBuffer?: TerminalBuffer,\n): ChangesResult {\n const { mode } = ctx\n if (count === 0) return { output: \"\", finalY: -1 }\n\n // Sort by position for optimal cursor movement (in-place, no allocation)\n sortPoolByPosition(pool, count)\n\n // ========================================================================\n // Phase 3 hybrid output dispatch (SILVERY_HYBRID_OUTPUT=1).\n //\n // When the feature flag is on AND the render path supplies enough state\n // (fullscreen mode + buffer present), delegate to the per-row\n // analyze→pick→emit pipeline in output-density.ts + output-modes.ts.\n // Falls back to the legacy in-line implementation otherwise.\n //\n // Inline mode is excluded: the inline path uses relative cursor arithmetic\n // (\\x1b[nA/nB + \\r) and a per-frame `startLine` window that the new\n // emitters don't yet model. See design doc R4 (rollout plan §8).\n // ========================================================================\n if (mode === \"fullscreen\" && buffer && isHybridOutputEnabled()) {\n return hybridChangesToAnsi(pool, count, ctx, buffer)\n }\n\n // ========================================================================\n // Hybrid emission: analyze per-row density and choose strategy per row.\n //\n // Dense rows (>50% of cells changed): emit the entire row from the buffer\n // with a single cursor positioning — avoids per-cell CUP overhead.\n //\n // Sparse rows: cell-by-cell emission (current behavior) — optimal for\n // isolated changes since it skips unchanged cells entirely.\n //\n // The threshold is checked only when the buffer is available (incremental\n // renders always pass it; bare changesToAnsi calls may not).\n // ========================================================================\n\n // Build a set of dense rows by scanning the sorted pool.\n // A row is dense if its changed cell count exceeds 50% of the buffer width.\n // We collect row boundaries (start/end indices in the pool) for dense rows\n // so we can skip their individual changes in the main loop.\n const denseRows = new Set<number>()\n const bufWidth = buffer?.width ?? 0\n const densityThreshold = bufWidth > 0 ? bufWidth * 0.5 : Infinity\n\n if (buffer && bufWidth > 0) {\n let rowStart = 0\n while (rowStart < count) {\n const rowY = pool[rowStart]!.y\n let rowEnd = rowStart + 1\n while (rowEnd < count && pool[rowEnd]!.y === rowY) rowEnd++\n const rowChanges = rowEnd - rowStart\n if (rowChanges >= densityThreshold) {\n denseRows.add(rowY)\n }\n rowStart = rowEnd\n }\n }\n\n let output = \"\"\n let currentStyle: Style | null = null\n let currentHyperlink: string | undefined\n const isInline = mode === \"inline\"\n const endLine = startLine + maxOutputLines // exclusive upper bound for inline filtering\n let finalY = -1\n let cursorX = -1\n let cursorY = -1\n let prevY = -1\n // Track the last emitted cell position to detect when a continuation\n // cell's main cell was already emitted in this pass.\n let lastEmittedX = -1\n let lastEmittedY = -1\n\n // Pre-allocated cell for full-row reads (dense row emission).\n // Separate from wideCharLookupCell to avoid aliasing issues.\n const denseRowCell = createMutableCell()\n\n for (let i = 0; i < count; i++) {\n const change = pool[i]!\n let x = change.x\n const y = change.y\n let cell = change.cell\n\n // In inline mode, skip changes outside the visible range\n if (isInline && (y < startLine || y >= endLine)) continue\n\n // ====================================================================\n // Dense row: emit the full row from the buffer.\n // Skip all pool entries for this row — we read directly from the buffer.\n // ====================================================================\n if (buffer && denseRows.has(y)) {\n // Skip remaining pool entries for this row\n while (i + 1 < count && pool[i + 1]!.y === y) i++\n\n const renderY = isInline ? y - startLine : y\n\n // Close hyperlink on row change\n if (y !== prevY && currentHyperlink) {\n output += \"\\x1b]8;;\\x1b\\\\\"\n currentHyperlink = undefined\n }\n prevY = y\n\n // Position cursor at start of row (column 0)\n if (renderY !== cursorY || cursorX !== 0) {\n // Reset style before cursor movement to prevent bg bleed\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n\n if (cursorY >= 0 && renderY === cursorY + 1) {\n output += \"\\r\\n\"\n } else if (cursorY >= 0 && renderY > cursorY) {\n const dy = renderY - cursorY\n output += dy === 1 ? \"\\r\\n\" : `\\r\\x1b[${dy}B`\n } else if (isInline) {\n const fromRow = cursorY >= 0 ? cursorY : 0\n if (renderY > fromRow) {\n output += `\\x1b[${renderY - fromRow}B\\r`\n } else if (renderY < fromRow) {\n output += `\\x1b[${fromRow - renderY}A\\r`\n } else {\n output += \"\\r\"\n }\n } else {\n output += `\\x1b[${renderY + 1};1H`\n }\n }\n\n // Emit the entire row from the buffer (like bufferToAnsi per-row logic)\n for (let bx = 0; bx < bufWidth; bx++) {\n buffer.readCellInto(bx, y, denseRowCell)\n\n // Handle hyperlink transitions\n const cellHyperlink = denseRowCell.hyperlink\n if (cellHyperlink !== currentHyperlink) {\n if (currentHyperlink) output += \"\\x1b]8;;\\x1b\\\\\"\n if (cellHyperlink) output += `\\x1b]8;;${cellHyperlink}\\x1b\\\\`\n currentHyperlink = cellHyperlink\n }\n\n // Style transition\n reusableCellStyle.fg = denseRowCell.fg\n reusableCellStyle.bg = denseRowCell.bg\n reusableCellStyle.underlineColor = denseRowCell.underlineColor\n reusableCellStyle.attrs = denseRowCell.attrs\n if (!styleEquals(currentStyle, reusableCellStyle)) {\n const prevStyle = currentStyle\n currentStyle = {\n fg: denseRowCell.fg,\n bg: denseRowCell.bg,\n underlineColor: denseRowCell.underlineColor,\n attrs: { ...denseRowCell.attrs },\n }\n output += styleTransition(prevStyle, currentStyle, ctx)\n }\n\n // Write character\n const char = denseRowCell.char || \" \"\n output += wrapTextSizing(char, denseRowCell.wide, ctx)\n\n // Skip continuation cell for wide chars\n if (denseRowCell.wide) {\n bx++\n // Cursor re-sync for wide chars (same logic as bufferToAnsi)\n if (isInline) {\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n const nextCol = bx + 1\n output += \"\\r\"\n if (nextCol > 0) output += nextCol === 1 ? \"\\x1b[C\" : `\\x1b[${nextCol}C`\n } else {\n output += `\\x1b[${renderY + 1};${bx + 2}H`\n }\n }\n }\n\n // Close hyperlink at end of row\n if (currentHyperlink) {\n output += \"\\x1b]8;;\\x1b\\\\\"\n currentHyperlink = undefined\n }\n\n // Reset style at end of row to prevent bg bleed into next row\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n\n cursorX = bufWidth\n cursorY = renderY\n lastEmittedX = bufWidth - 1\n lastEmittedY = y\n continue\n }\n\n // ====================================================================\n // Sparse cell: standard cell-by-cell emission\n // ====================================================================\n\n // Handle continuation cells: these are the second column of a wide\n // character. If their main cell (x-1) was already emitted in this\n // pass, skip. Otherwise, look up and emit the main cell from the\n // buffer so the wide char covers both columns.\n if (cell.continuation) {\n // Main cell was already emitted — skip. Do not write into the\n // continuation column after emitting the wide character: xterm-compatible\n // backends treat that as editing the wide cell and clear the glyph.\n // The wide-char CUP resync below keeps the cursor model aligned.\n if (lastEmittedX === x - 1 && lastEmittedY === y) {\n continue\n }\n\n // Orphaned continuation cell: main cell didn't change but this\n // cell's style did. Read the main cell from the buffer and emit it.\n if (buffer && x > 0) {\n x = x - 1\n buffer.readCellInto(x, y, wideCharLookupCell)\n cell = wideCharLookupCell\n // If the looked-up cell is itself a continuation (shouldn't happen\n // with valid buffers) or not wide, fall back to skipping\n if (cell.continuation || !cell.wide) continue\n } else {\n continue\n }\n }\n\n // For inline mode, use render-region-relative row indices\n const renderY = isInline ? y - startLine : y\n\n // Close hyperlink on row change (hyperlinks must not span across rows)\n if (y !== prevY && currentHyperlink) {\n output += \"\\x1b]8;;\\x1b\\\\\"\n currentHyperlink = undefined\n }\n prevY = y\n\n // Move cursor if needed (cursor must be exactly at target position)\n if (renderY !== cursorY || x !== cursorX) {\n // Use \\r\\n optimization only if cursor is initialized AND we're moving\n // to the next line at column 0. Don't use it when cursorY is -1\n // (uninitialized) because that would incorrectly emit a newline at start.\n // Bug km-x7ih: This was causing the first row to appear at the bottom.\n if (cursorY >= 0 && renderY === cursorY + 1 && x === 0) {\n // Next line at column 0, use newline (more efficient)\n // Reset style before newline to prevent background color bleeding\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n output += \"\\r\\n\"\n } else if (cursorY >= 0 && renderY === cursorY && x > cursorX) {\n // Same row, forward: use CUF (Cursor Forward) for small jumps.\n // Reset bg before CUF to prevent background color bleeding into\n // skipped cells. Some terminals (e.g., Ghostty) may apply the\n // current bg to cells traversed by CUF, causing visual artifacts\n // when bg transitions from undefined→color (km-tui.col-header-dup).\n if (currentStyle && currentStyle.bg !== null) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n const dx = x - cursorX\n output += dx === 1 ? \"\\x1b[C\" : `\\x1b[${dx}C`\n } else if (cursorY >= 0 && renderY > cursorY && x === 0) {\n // Same column (0), down N rows: use \\r + CUD\n const dy = renderY - cursorY\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n output += dy === 1 ? \"\\r\\n\" : `\\r\\x1b[${dy}B`\n } else if (isInline) {\n // Inline mode: relative positioning (no absolute row numbers)\n if (currentStyle && (currentStyle.bg !== null || hasActiveAttrs(currentStyle.attrs))) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n // When cursorY === -1 (first change in incremental render),\n // the cursor is at row 0 (set by inlineIncrementalRender prefix).\n const fromRow = cursorY >= 0 ? cursorY : 0\n if (renderY > fromRow) {\n output += `\\x1b[${renderY - fromRow}B\\r`\n } else if (renderY < fromRow) {\n output += `\\x1b[${fromRow - renderY}A\\r`\n } else {\n output += \"\\r\"\n }\n if (x > 0) output += x === 1 ? \"\\x1b[C\" : `\\x1b[${x}C`\n } else {\n // Fullscreen: absolute position (1-indexed)\n output += `\\x1b[${renderY + 1};${x + 1}H`\n }\n }\n\n // Handle OSC 8 hyperlink transitions (separate from SGR style)\n const cellHyperlink = cell.hyperlink\n if (cellHyperlink !== currentHyperlink) {\n if (currentHyperlink) {\n output += \"\\x1b]8;;\\x1b\\\\\" // Close previous hyperlink\n }\n if (cellHyperlink) {\n output += `\\x1b]8;;${cellHyperlink}\\x1b\\\\` // Open new hyperlink\n }\n currentHyperlink = cellHyperlink\n }\n\n // Update style if changed (reuse pre-allocated style object)\n reusableCellStyle.fg = cell.fg\n reusableCellStyle.bg = cell.bg\n reusableCellStyle.underlineColor = cell.underlineColor\n reusableCellStyle.attrs = cell.attrs\n if (!styleEquals(currentStyle, reusableCellStyle)) {\n // Snapshot: copy attrs so currentStyle isn't invalidated by next iteration\n const prevStyle = currentStyle\n currentStyle = {\n fg: cell.fg,\n bg: cell.bg,\n underlineColor: cell.underlineColor,\n attrs: { ...cell.attrs },\n }\n output += styleTransition(prevStyle, currentStyle, ctx)\n }\n\n // Write character — empty-string chars are treated as space to ensure\n // the terminal cursor advances and cursorX tracking stays correct.\n const char = cell.char || \" \"\n output += wrapTextSizing(char, cell.wide, ctx)\n cursorX = x + (cell.wide ? 2 : 1)\n cursorY = renderY\n lastEmittedX = x\n lastEmittedY = y\n\n // Wide char cursor re-sync: terminals may advance the cursor by 1\n // instead of 2 for certain emoji (flag sequences, text-presentation\n // emoji without OSC 66 support). In bufferToAnsi (full render) this\n // only causes a consistent per-row shift since every cell is written\n // sequentially. But in changesToAnsi, contiguous runs rely on cursor\n // auto-advance, so a width mismatch causes progressive drift —\n // characters appear at wrong positions, mixing old and new content.\n // Fix: emit an explicit cursor position after each wide char to\n // re-sync the terminal cursor with our tracking. Cost: ~8 bytes per\n // wide char (CUP in fullscreen, \\r+CUF in inline). Wide chars are\n // rare so the overhead is negligible.\n if (cell.wide) {\n if (isInline) {\n // Inline: \\r resets to column 0, CUF moves to expected position.\n // Reset bg first to prevent bleed across traversed cells.\n if (currentStyle && currentStyle.bg !== null) {\n output += \"\\x1b[0m\"\n currentStyle = null\n }\n output += \"\\r\"\n if (cursorX > 0) output += cursorX === 1 ? \"\\x1b[C\" : `\\x1b[${cursorX}C`\n } else {\n // Fullscreen: CUP (absolute position) — no style reset needed.\n output += `\\x1b[${cursorY + 1};${cursorX + 1}H`\n }\n }\n }\n\n finalY = cursorY\n\n // Close any open hyperlink\n if (currentHyperlink) {\n output += \"\\x1b]8;;\\x1b\\\\\"\n }\n\n // Reset style at end\n if (currentStyle) {\n output += \"\\x1b[0m\"\n }\n\n return { output, finalY }\n}\n\n// =============================================================================\n// Color code helpers (imported from ansi/sgr-codes.ts)\n// =============================================================================\n\n/**\n * Convert style to ANSI escape sequence (chalk-compatible format).\n *\n * Emits only non-default attributes with no reset prefix. Called when there\n * is no previous style context (first cell or after all attributes are off),\n * so the terminal is already in reset state.\n *\n * Uses native 4-bit codes for basic colors (0-7), 256-color for extended,\n * and true-color for RGB. Each attribute gets its own \\x1b[Xm sequence to\n * match chalk's output format.\n *\n * Emits SGR codes including:\n * - Basic colors (30-37, 40-47)\n * - 256-color (38;5;N, 48;5;N)\n * - True color (38;2;r;g;b, 48;2;r;g;b)\n * - Underline styles (4:x where x = 0-5)\n * - Underline color (58;5;N or 58;2;r;g;b)\n * - Inverse uses SGR 7 so terminals swap fg/bg correctly (including default colors)\n */\nfunction styleToAnsi(style: Style, ctx: OutputContext = defaultContext): string {\n const fg = style.fg\n const bg = style.bg\n const monoTier = ctx.caps.colorLevel === \"mono\"\n\n // Collect all SGR codes into one combined sequence: \\x1b[code1;code2;...m\n // This is more spec-compliant and produces fewer bytes than separate sequences.\n const codes: string[] = []\n\n // Foreground color — stripped at monochrome tier (hierarchy via attrs)\n if (fg !== null && !monoTier) {\n codes.push(fgColorCode(fg))\n }\n\n // Background color (DEFAULT_BG sentinel = terminal default, skip) — stripped at mono tier\n if (bg !== null && !isDefaultBg(bg) && !monoTier) {\n codes.push(bgColorCode(bg))\n }\n\n // Attributes\n if (style.attrs.bold) codes.push(\"1\")\n if (style.attrs.dim) codes.push(\"2\")\n if (style.attrs.italic) codes.push(\"3\")\n\n // Underline: per-style capability downgrade. Requested style falls back to\n // plain SGR 4 (single) when the terminal doesn't support that specific\n // extended variant.\n const requestedUl = style.attrs.underlineStyle\n if (requestedUl || style.attrs.underline) {\n const effectiveStyle = supportsUnderlineStyle(ctx.caps.underlineStyles, requestedUl)\n ? requestedUl\n : \"single\"\n const sgrSubparam = underlineStyleToSgr(effectiveStyle)\n if (sgrSubparam !== null && sgrSubparam !== 0 && sgrSubparam !== 1) {\n codes.push(`4:${sgrSubparam}`)\n } else {\n codes.push(\"4\")\n }\n }\n\n // Use SGR 7 for inverse — lets the terminal correctly swap fg/bg\n // (including default terminal colors that have no explicit ANSI code)\n if (style.attrs.blink) codes.push(\"5\")\n if (style.attrs.inverse) codes.push(\"7\")\n if (style.attrs.hidden) codes.push(\"8\")\n if (style.attrs.strikethrough) codes.push(\"9\")\n if (style.attrs.overline && ctx.caps.overline) codes.push(\"53\")\n\n // Append underline color if specified (SGR 58) — skip for limited terminals\n if (\n ctx.caps.underlineColor &&\n style.underlineColor !== null &&\n style.underlineColor !== undefined\n ) {\n if (typeof style.underlineColor === \"number\") {\n codes.push(`58;5;${style.underlineColor}`)\n } else {\n codes.push(\n `58;2;${style.underlineColor.r};${style.underlineColor.g};${style.underlineColor.b}`,\n )\n }\n }\n\n if (codes.length === 0) return \"\"\n return `\\x1b[${codes.join(\";\")}m`\n}\n\n// =============================================================================\n// STRICT/Verification code lives in output-verify.ts (~990 LOC extracted).\n// =============================================================================\n","/**\n * SGR mouse event parsing (mode 1006) and SGR-Pixels parsing (mode 1016).\n *\n * SGR format: CSI < button;x;y M (press) or CSI < button;x;y m (release)\n *\n * Button encoding:\n * - Bits 0-1: 0=left, 1=middle, 2=right, 3=release (X10 only, not SGR)\n * - Bit 2 (+4): Shift held\n * - Bit 3 (+8): Meta/Alt held\n * - Bit 4 (+16): Ctrl held\n * - Bit 5 (+32): Motion event (mouse moved while button held)\n * - Bits 6-7: 64=wheel-up, 65=wheel-down, 66=wheel-left, 67=wheel-right\n */\n\n/**\n * Parsed mouse event from SGR mouse protocol.\n */\nexport interface ParsedMouse {\n /** Mouse button: 0=left, 1=middle, 2=right */\n button: number\n /**\n * Silvery layout X coordinate, in terminal cells.\n * Integer in SGR 1006 mode; fractional when parsed from SGR-Pixels 1016.\n */\n x: number\n /**\n * Silvery layout Y coordinate, in terminal cells.\n * Integer in SGR 1006 mode; fractional when parsed from SGR-Pixels 1016.\n */\n y: number\n /** Physical pixel X coordinate, present only for SGR-Pixels 1016. */\n clientX?: number\n /** Physical pixel Y coordinate, present only for SGR-Pixels 1016. */\n clientY?: number\n /** Coordinate mode used by the parser. */\n coordinateMode: \"cell\" | \"pixel\"\n /** Event action */\n action: \"down\" | \"up\" | \"move\" | \"wheel\"\n /**\n * Vertical wheel delta (deltaY): -1 for wheel-up, +1 for wheel-down, 0 for a\n * pure-horizontal wheel. DOM-style sign convention (down is positive).\n */\n delta?: number\n /**\n * Horizontal wheel delta (deltaX): -1 for wheel-left, +1 for wheel-right, 0\n * for a pure-vertical wheel. DOM-style sign convention (right is positive).\n * SGR buttons 66 (left) / 67 (right) decode here; consumers that only read\n * `delta`/`deltaY` are unaffected.\n */\n deltaX?: number\n /** Shift was held */\n shift: boolean\n /** Alt/Meta was held */\n meta: boolean\n /** Ctrl was held */\n ctrl: boolean\n /** Monotonic timestamp when the terminal input chunk was received. */\n receivedAt?: number\n /** Monotonic id shared by events parsed from the same terminal input chunk. */\n inputBatchId?: number\n}\n\nconst SGR_MOUSE_RE = /^\\x1b\\[<(\\d+);(\\d+);(\\d+)([Mm])$/\n\nexport interface ParseMouseOptions {\n coordinateMode?: \"cell\" | \"pixel\"\n cellSize?: { width: number; height: number }\n}\n\n/**\n * Parse an SGR mouse sequence.\n *\n * Return semantics (see ProtocolError in @silvery/ansi for the full contract):\n * - `null` — input does not match the SGR mouse shape `CSI < B;X;Y [Mm]`.\n * No \"committed but malformed\" branch exists here: either the full SGR\n * shape matches (parse succeeds) or it doesn't (null = next-parser-please).\n *\n * The bead 15127 audit listed this parser for review, but the regex-based\n * shape match means there's no place where the parser commits to \"this is\n * a mouse event\" and then fails on body validation — both happen at the\n * same point. Loud-error tightening here would require a stricter\n * sub-grammar (e.g. validating button-code ranges), tracked separately.\n *\n * @returns ParsedMouse or null if not a valid mouse sequence\n */\nexport function parseMouseSequence(input: string, options?: ParseMouseOptions): ParsedMouse | null {\n const m = SGR_MOUSE_RE.exec(input)\n if (!m) return null\n\n const raw = parseInt(m[1]!)\n const rawX = parseInt(m[2]!) - 1 // 1-indexed → 0-indexed\n const rawY = parseInt(m[3]!) - 1\n const coordinateMode = options?.coordinateMode ?? \"cell\"\n const cellWidth = Math.max(1, options?.cellSize?.width ?? 1)\n const cellHeight = Math.max(1, options?.cellSize?.height ?? 1)\n const x = coordinateMode === \"pixel\" ? rawX / cellWidth : rawX\n const y = coordinateMode === \"pixel\" ? rawY / cellHeight : rawY\n const clientX = coordinateMode === \"pixel\" ? rawX : undefined\n const clientY = coordinateMode === \"pixel\" ? rawY : undefined\n const terminator = m[4]!\n\n const shift = !!(raw & 4)\n const meta = !!(raw & 8)\n const ctrl = !!(raw & 16)\n const motion = !!(raw & 32)\n const isWheel = !!(raw & 64)\n\n if (isWheel) {\n // Bits 0-1 of a wheel button: 0=up, 1=down, 2=left, 3=right (X11 buttons\n // 4/5/6/7 → SGR 64/65/66/67). Up/down move the vertical axis (deltaY),\n // left/right the horizontal axis (deltaX); a wheel tick is single-axis.\n const wheelButton = raw & 3\n const horizontal = wheelButton >= 2\n const deltaY = horizontal ? 0 : wheelButton === 0 ? -1 : 1\n const deltaX = horizontal ? (wheelButton === 2 ? -1 : 1) : 0\n return {\n button: 0,\n x,\n y,\n ...(clientX === undefined ? {} : { clientX }),\n ...(clientY === undefined ? {} : { clientY }),\n coordinateMode,\n action: \"wheel\",\n delta: deltaY,\n deltaX,\n shift,\n meta,\n ctrl,\n }\n }\n\n const button = raw & 3\n const action = motion ? \"move\" : terminator === \"M\" ? \"down\" : \"up\"\n return {\n button,\n x,\n y,\n ...(clientX === undefined ? {} : { clientX }),\n ...(clientY === undefined ? {} : { clientY }),\n coordinateMode,\n action,\n shift,\n meta,\n ctrl,\n }\n}\n\nconst SGR_MOUSE_TEST_RE = /^\\x1b\\[<\\d+;\\d+;\\d+[Mm]$/\n\n/** Check if a raw input string is a mouse sequence */\nexport function isMouseSequence(input: string): boolean {\n return SGR_MOUSE_TEST_RE.test(input)\n}\n","/**\n * Bracketed Paste Mode\n *\n * Enables bracketed paste so the terminal wraps pasted text with markers.\n * This lets the app distinguish pasted text from typed input and receive\n * it as a single event rather than individual keystrokes.\n *\n * Protocol: DEC private mode 2004\n * - Enable: CSI ? 2004 h\n * - Disable: CSI ? 2004 l\n * - Paste start marker: CSI 200 ~\n * - Paste end marker: CSI 201 ~\n *\n * Supported by: Ghostty, Kitty, WezTerm, iTerm2, Alacritty, xterm, tmux, foot\n */\n\nimport { ProtocolError } from \"@silvery/ansi\"\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Escape sequence that marks the beginning of pasted text */\nexport const PASTE_START = \"\\x1b[200~\"\n\n/** Escape sequence that marks the end of pasted text */\nexport const PASTE_END = \"\\x1b[201~\"\n\n// ============================================================================\n// Protocol Control\n// ============================================================================\n\n/**\n * Enable bracketed paste mode.\n * Writes CSI ? 2004 h to the output stream.\n */\nexport function enableBracketedPaste(stdout: NodeJS.WriteStream): void {\n stdout.write(\"\\x1b[?2004h\")\n}\n\n/**\n * Disable bracketed paste mode.\n * Writes CSI ? 2004 l to the output stream.\n */\nexport function disableBracketedPaste(stdout: NodeJS.WriteStream): void {\n stdout.write(\"\\x1b[?2004l\")\n}\n\n// ============================================================================\n// Parsing\n// ============================================================================\n\n/** Result of parsing a bracketed paste sequence */\nexport interface BracketedPasteResult {\n type: \"paste\"\n content: string\n}\n\n/** @internal Parsed protocol envelope with its exact consumed byte span. */\nexport interface BracketedPasteEnvelope {\n result: BracketedPasteResult\n start: number\n end: number\n}\n\n/** @internal Use when a stream owner must preserve bytes around the paste. */\nexport function parseBracketedPasteEnvelope(input: string): BracketedPasteEnvelope | null {\n const startIdx = input.indexOf(PASTE_START)\n if (startIdx === -1) return null\n\n const contentStart = startIdx + PASTE_START.length\n const endIdx = input.indexOf(PASTE_END, contentStart)\n if (endIdx === -1) {\n // PASTE_START found but no PASTE_END — protocol violation OR mid-stream\n // split. Throwing surfaces it; the dispatch boundary may catch and buffer.\n throw new ProtocolError({\n parser: \"parseBracketedPaste\",\n input,\n reason: \"PASTE_START present but no PASTE_END terminator in chunk\",\n })\n }\n\n return {\n result: {\n type: \"paste\",\n content: input.slice(contentStart, endIdx),\n },\n start: startIdx,\n end: endIdx + PASTE_END.length,\n }\n}\n\n/**\n * Detect and extract bracketed paste content from raw terminal input.\n *\n * Return semantics (see {@link ProtocolError} for the full contract):\n * - `null` — input contains no PASTE_START marker (this is not bracketed\n * paste input). Discriminator-chain \"next parser please\" signal.\n * - `throw ProtocolError` — input HAS PASTE_START (we committed to\n * bracketed paste) but no PASTE_END follows. This indicates either a\n * stream-split paste (caller should buffer and retry on the next chunk)\n * or a protocol violation. Loud failure surfaces the gap; the dispatch\n * layer catches and decides whether to buffer or log.\n */\nexport function parseBracketedPaste(input: string): BracketedPasteResult | null {\n return parseBracketedPasteEnvelope(input)?.result ?? null\n}\n\n// ============================================================================\n// Paste Event\n// ============================================================================\n\n/**\n * Structured paste event with source tracking and optional rich data.\n *\n * Fired when text is pasted into the application, either from the system\n * clipboard (via bracketed paste) or from the internal clipboard store.\n */\nexport interface PasteEvent {\n /** The pasted plain text */\n text: string\n /** Where the paste came from */\n source: \"bracketed\" | \"internal\"\n /** Rich clipboard data, if available (internal paste only) */\n structured?: import(\"./clipboard\").ClipboardData\n}\n\n/**\n * Create a PasteEvent from a bracketed paste result.\n */\nexport function createBracketedPasteEvent(result: BracketedPasteResult): PasteEvent {\n return {\n text: result.content,\n source: \"bracketed\",\n }\n}\n\n/**\n * Create a PasteEvent from internal clipboard data.\n */\nexport function createInternalPasteEvent(data: import(\"./clipboard\").ClipboardData): PasteEvent {\n return {\n text: data.text,\n source: \"internal\",\n structured: data,\n }\n}\n","/**\n * Clipboard Backend Abstraction\n *\n * Pluggable clipboard system with support for multiple backends.\n * The default backend uses OSC 52 for terminal clipboard access.\n *\n * Architecture:\n * - ClipboardBackend: interface for clipboard read/write\n * - ClipboardData: multi-format clipboard content (text, markdown, html, internal)\n * - createOsc52Backend: OSC 52 terminal clipboard (default)\n * - createInternalClipboardBackend: in-memory store for rich app-internal paste\n * - createCompositeClipboard: fan-out writes to multiple backends\n *\n * OSC 52 Protocol:\n * - Copy: ESC ] 52 ; c ; <base64> BEL\n * - Query: ESC ] 52 ; c ; ? BEL\n * - Response: ESC ] 52 ; c ; <base64> BEL (or ST terminator)\n *\n * Supported by: Ghostty, Kitty, WezTerm, iTerm2, xterm, foot, tmux\n */\n\nimport { ProtocolError } from \"@silvery/ansi\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Multi-format clipboard content.\n *\n * Plain text is always present. Optional rich formats allow applications\n * to provide structured data for within-app paste without losing it\n * through the plain-text-only system clipboard.\n */\nexport interface ClipboardData {\n /** Plain text content (always present) */\n text: string\n /** Markdown representation */\n markdown?: string\n /** HTML representation */\n html?: string\n /** App-specific structured data (e.g., node tree for structured paste) */\n internal?: unknown\n}\n\n/**\n * Clipboard backend capabilities.\n *\n * `text` is always true — every backend supports plain text.\n * Rich format support is backend-dependent.\n */\nexport interface ClipboardCapabilities {\n readonly text: true\n readonly html?: boolean\n readonly markdown?: boolean\n readonly internal?: boolean\n}\n\n/**\n * Pluggable clipboard backend.\n *\n * Backends handle the transport of clipboard data to/from the system\n * or an in-memory store. The framework writes ClipboardData; the backend\n * decides what formats it can actually carry.\n */\nexport interface ClipboardBackend {\n /** Write clipboard data. Backends may ignore formats they don't support. */\n write(data: ClipboardData): void | Promise<void>\n /** Read clipboard contents as plain text. Not all backends support read. */\n read?(): Promise<string>\n /** What formats this backend supports */\n readonly capabilities: ClipboardCapabilities\n}\n\n// ============================================================================\n// Writable interface (avoid coupling to Node.js WriteStream)\n// ============================================================================\n\n/** Minimal writable interface for clipboard output */\ninterface Writable {\n write(data: string): boolean | void\n}\n\n// ============================================================================\n// OSC 52 Constants\n// ============================================================================\n\nconst ESC = \"\\x1b\"\nconst BEL = \"\\x07\"\n\n/** OSC 52 response prefix */\nconst OSC52_PREFIX = `${ESC}]52;c;`\n\n/**\n * Upper bound on the decoded size of an OSC 52 clipboard payload.\n *\n * OSC 52 responses are attacker-influenceable: a hostile program on the far end\n * of an SSH session, or a malicious terminal emulator, can emit an arbitrary\n * base64 blob that arrives on stdin and is decoded here (see the\n * `input-owner.ts` clipboard-response path that fires it as a paste). Node's\n * base64 decoder never throws and imposes no size limit, so without a cap a\n * single response could force an arbitrarily large allocation and then be fed\n * into a focused guest as pasted input. Real clipboards are small — terminals\n * themselves cap OSC 52 payloads around ~100 KB (see the `createOsc52Backend`\n * quirks note) — so 1 MiB sits comfortably above any legitimate clipboard while\n * keeping the decode bounded.\n */\nexport const MAX_OSC52_PAYLOAD_BYTES = 1024 * 1024\n\n/**\n * Base64 encodes 3 bytes as 4 chars, so the encoded form of the byte cap is\n * `ceil(bytes / 3) * 4`. We compare the base64 length against this to reject\n * oversize payloads before decoding (never allocating the decoded buffer).\n */\nconst MAX_OSC52_BASE64_LENGTH = Math.ceil(MAX_OSC52_PAYLOAD_BYTES / 3) * 4\n\n/**\n * Standard base64 alphabet with optional `=` padding. Node's decoder silently\n * drops out-of-alphabet bytes instead of failing, so we validate shape first\n * and reject anything that isn't well-formed base64 rather than best-effort\n * decoding garbage into a paste.\n */\nconst OSC52_BASE64_SHAPE = /^[A-Za-z0-9+/]*={0,2}$/\n\n// ============================================================================\n// OSC 52 Backend\n// ============================================================================\n\n/**\n * Create an OSC 52 clipboard backend.\n *\n * Writes plain text to the system clipboard via the terminal's OSC 52 support.\n * Works across SSH sessions. Rich formats (markdown, html, internal) are\n * silently ignored — OSC 52 only carries plain text.\n *\n * Quirks:\n * - Some terminals limit payload size (~100KB)\n * - tmux requires `set -g set-clipboard on`\n * - Some terminals only support BEL terminator (not ST)\n */\nexport function createOsc52Backend(stdout: Writable): ClipboardBackend {\n return {\n write(data: ClipboardData): void {\n const base64 = Buffer.from(data.text, \"utf-8\").toString(\"base64\")\n stdout.write(`${ESC}]52;c;${base64}${BEL}`)\n },\n\n async read(): Promise<string> {\n // OSC 52 read requires async response parsing from stdin.\n // The query is sent here; the caller must parse the response\n // from the terminal input stream using parseClipboardResponse().\n stdout.write(`${ESC}]52;c;?${BEL}`)\n // Note: actual response arrives asynchronously via stdin.\n // This is a limitation of the terminal protocol — true async\n // read requires coordination with the input parser.\n return \"\"\n },\n\n capabilities: { text: true },\n }\n}\n\n// ============================================================================\n// Internal Clipboard Backend\n// ============================================================================\n\n/**\n * In-memory clipboard store for within-app paste.\n *\n * Stores the full ClipboardData including rich formats that OSC 52 can't carry.\n * Used alongside OSC 52 so plain text goes to the system clipboard while\n * rich data is available for internal paste operations.\n */\nexport function createInternalClipboardBackend(): ClipboardBackend & {\n /** Get the stored clipboard data, or null if empty */\n getData(): ClipboardData | null\n /** Get the timestamp of the last write */\n getTimestamp(): number\n} {\n let stored: ClipboardData | null = null\n let timestamp = 0\n\n return {\n write(data: ClipboardData): void {\n stored = { ...data }\n timestamp = Date.now()\n },\n\n async read(): Promise<string> {\n return stored?.text ?? \"\"\n },\n\n getData(): ClipboardData | null {\n return stored ? { ...stored } : null\n },\n\n getTimestamp(): number {\n return timestamp\n },\n\n capabilities: { text: true, html: true, markdown: true, internal: true },\n }\n}\n\n// ============================================================================\n// Composite Clipboard\n// ============================================================================\n\n/**\n * Create a composite clipboard that writes to multiple backends.\n *\n * Writes fan out to all backends. Reads come from the first backend\n * that supports read (in order). This lets you do OSC 52 + internal\n * store simultaneously: plain text goes to system clipboard, rich\n * data stays in memory for structured paste.\n */\nexport function createCompositeClipboard(...backends: ClipboardBackend[]): ClipboardBackend {\n return {\n write(data: ClipboardData): void | Promise<void> {\n const promises: Promise<void>[] = []\n for (const backend of backends) {\n const result = backend.write(data)\n if (result instanceof Promise) {\n promises.push(result)\n }\n }\n if (promises.length > 0) {\n return Promise.all(promises).then(() => undefined)\n }\n },\n\n async read(): Promise<string> {\n for (const backend of backends) {\n if (backend.read) {\n const text = await backend.read()\n if (text) return text\n }\n }\n return \"\"\n },\n\n capabilities: {\n text: true,\n html: backends.some((b) => b.capabilities.html) || undefined,\n markdown: backends.some((b) => b.capabilities.markdown) || undefined,\n internal: backends.some((b) => b.capabilities.internal) || undefined,\n },\n }\n}\n\n// ============================================================================\n// Response Parsing\n// ============================================================================\n\n/** @internal Parsed OSC 52 response with its exact consumed byte span. */\nexport interface ClipboardResponseEnvelope {\n text: string\n start: number\n end: number\n}\n\n/** @internal Use when a stream owner must preserve bytes around the response. */\nexport function parseClipboardResponseEnvelope(input: string): ClipboardResponseEnvelope | null {\n const prefixIdx = input.indexOf(OSC52_PREFIX)\n if (prefixIdx === -1) return null\n\n const contentStart = prefixIdx + OSC52_PREFIX.length\n\n // Reject the query marker — it's not a response (but it IS valid OSC 52\n // input, so silent skip is correct, not a ProtocolError).\n if (input[contentStart] === \"?\") return null\n\n // Find terminator: BEL (\\x07) or ST (ESC \\)\n let terminatorLength = BEL.length\n let contentEnd = input.indexOf(BEL, contentStart)\n if (contentEnd === -1) {\n const stringTerminator = `${ESC}\\\\`\n contentEnd = input.indexOf(stringTerminator, contentStart)\n terminatorLength = stringTerminator.length\n }\n if (contentEnd === -1) {\n // We committed to OSC 52 (prefix matched) — missing terminator is a\n // protocol violation, not a \"next parser please\" condition.\n throw new ProtocolError({\n parser: \"parseClipboardResponse\",\n input,\n reason: \"missing terminator (expected BEL or ST after OSC 52 base64 payload)\",\n })\n }\n\n // OSC 52 responses are attacker-influenceable and Node's base64 decoder is\n // lenient (never throws, silently drops invalid bytes) and unbounded. Reject\n // oversize or malformed payloads LOUDLY instead of decoding garbage into a\n // paste (NO SILENT ERRORS). The dispatch caller catches ProtocolError and\n // drops the paste with a debug-log breadcrumb. The length check runs on the\n // index delta so we never slice a giant substring just to measure it.\n if (contentEnd - contentStart > MAX_OSC52_BASE64_LENGTH) {\n throw new ProtocolError({\n parser: \"parseClipboardResponse\",\n input,\n reason: `OSC 52 base64 payload exceeds ${MAX_OSC52_PAYLOAD_BYTES}-byte cap (got ${contentEnd - contentStart} base64 chars)`,\n })\n }\n\n const base64 = input.slice(contentStart, contentEnd)\n if (!OSC52_BASE64_SHAPE.test(base64)) {\n throw new ProtocolError({\n parser: \"parseClipboardResponse\",\n input,\n reason: \"OSC 52 payload is not valid base64\",\n })\n }\n\n return {\n text: Buffer.from(base64, \"base64\").toString(\"utf-8\"),\n start: prefixIdx,\n end: contentEnd + terminatorLength,\n }\n}\n\n/**\n * Parse an OSC 52 clipboard response and decode the base64 content.\n *\n * Return semantics (see {@link ProtocolError} for the full contract):\n * - `null` — input is NOT an OSC 52 clipboard response (no prefix, or a\n * query marker `?` rather than a response). Callers in a discriminator\n * chain treat this as \"next parser please.\"\n * - `throw ProtocolError` — input HAS the OSC 52 prefix (we committed to\n * this protocol) but is malformed (e.g., missing terminator). Loud\n * failure is required by bead 15127 acceptance line 22.\n *\n * Handles both BEL (\\x07) and ST (ESC \\) terminators.\n */\nexport function parseClipboardResponse(input: string): string | null {\n return parseClipboardResponseEnvelope(input)?.text ?? null\n}\n","/**\n * Focus Reporting (CSI ?1004h)\n *\n * Enables/disables terminal focus-in/focus-out event reporting.\n * When enabled, the terminal sends CSI I on focus-in and CSI O on focus-out.\n *\n * Protocol:\n * - Enable: CSI ? 1004 h\n * - Disable: CSI ? 1004 l\n * - Focus In: CSI I (\\x1b[I)\n * - Focus Out: CSI O (\\x1b[O)\n *\n * Supported by: xterm (v282+), Ghostty, Kitty, WezTerm, iTerm2, foot, VTE\n */\n\nconst CSI = \"\\x1b[\"\n\n/**\n * Enable terminal focus reporting.\n * After enabling, the terminal will send CSI I / CSI O sequences\n * when the terminal window gains or loses focus.\n */\nexport function enableFocusReporting(write: (data: string) => void): void {\n write(`${CSI}?1004h`)\n}\n\n/**\n * Disable terminal focus reporting.\n */\nexport function disableFocusReporting(write: (data: string) => void): void {\n write(`${CSI}?1004l`)\n}\n\n/**\n * Parse a focus event from terminal input.\n *\n * Return semantics (see ProtocolError in @silvery/ansi for the full contract):\n * - `null` — input contains neither `CSI I` nor `CSI O` markers (not a\n * focus event). Discriminator-chain \"next parser please\" signal.\n *\n * The bead 15127 audit listed this parser for review, but there is no\n * \"committed but malformed\" branch — the marker either appears in the\n * input or it doesn't. No ProtocolError throw needed.\n *\n * @param input Raw terminal input string\n * @returns Parsed focus event, or null if not a focus sequence\n */\nexport function parseFocusEvent(input: string): { type: \"focus-in\" | \"focus-out\" } | null {\n if (input.includes(`${CSI}I`)) {\n return { type: \"focus-in\" }\n }\n if (input.includes(`${CSI}O`)) {\n return { type: \"focus-out\" }\n }\n return null\n}\n","/** Terminal-owned desktop notification emission. */\n\nimport { Buffer } from \"node:buffer\"\nimport type { TerminalCaps, TerminalNotificationProtocol } from \"@silvery/ansi\"\n\nconst ESC = \"\\x1b\"\nconst BEL = \"\\x07\"\nconst ST = `${ESC}\\\\`\nconst KITTY_ENCODED_CHUNK_SIZE = 4096\n\n/** Internal marker for synthetic streams that cannot deliver notifications. */\nexport const NOTIFICATION_WRITER_AVAILABLE: unique symbol = Symbol(\n \"silvery.notification-writer-available\",\n)\n\nexport type NotificationUrgency = \"low\" | \"normal\" | \"critical\"\n\n/** Target-neutral action shown alongside a desktop notification. */\nexport interface NotificationAction {\n /** Stable application-owned id returned by activation events. */\n readonly id: string\n readonly label: string\n}\n\n/** Protocol-neutral notification data accepted by a Term. */\nexport interface NotificationRequest {\n /** Stable identifier used by protocols that support replacement. */\n readonly id?: string\n readonly title?: string\n readonly body: string\n readonly urgency?: NotificationUrgency\n /** Optional actions. The terminal target supports these only through OSC 99. */\n readonly actions?: readonly NotificationAction[]\n /** Ask the target to report a click on the notification itself. */\n readonly reportActivation?: boolean\n}\n\nexport type NotificationUnsupportedReason =\n | \"notifications\"\n | \"notification-actions\"\n | \"notification-activation\"\n\n/** Target-neutral delivery result shared by terminal and future DOM targets. */\nexport type NotificationDelivery =\n | { readonly status: \"emitted\" }\n | {\n readonly status: \"unsupported\"\n readonly reason: NotificationUnsupportedReason\n }\n\n/** Terminal refinement that preserves exact protocol evidence for Term callers. */\nexport type TerminalNotificationDelivery =\n | { readonly status: \"emitted\"; readonly protocol: TerminalNotificationProtocol }\n | { readonly status: \"unsupported\"; readonly reason: NotificationUnsupportedReason }\n\n/** Target-neutral activation projected from the target's native reply. */\nexport type NotificationActivation =\n | { readonly id: string; readonly kind: \"notification\" }\n | { readonly id: string; readonly kind: \"action\"; readonly actionId: string }\n\n/** Cross-target notification capability. A future DOM target implements this same shape. */\nexport interface NotificationTarget {\n notify(request: NotificationRequest): NotificationDelivery\n onNotificationActivation(handler: (event: NotificationActivation) => void): () => void\n}\n\n/** OSC 99 reply after protocol parsing, before domain action-id projection. */\nexport interface TerminalNotificationActivation {\n readonly id: string\n readonly button?: number\n}\n\nexport type NotificationReplyEnvelope =\n | {\n readonly status: \"complete\"\n readonly start: number\n readonly end: number\n readonly activation: TerminalNotificationActivation | null\n }\n | { readonly status: \"incomplete\"; readonly start: number }\n\ninterface NotificationController extends NotificationTarget, Disposable {\n notify(request: NotificationRequest): TerminalNotificationDelivery\n dispose(): void\n}\n\ntype SubscribeToActivationReplies = (\n handler: (reply: TerminalNotificationActivation) => void,\n) => (() => void) | undefined\n\n/**\n * Bind notification emission to a Term's proven capability and owned writer.\n * Internal: callers use the single public `term.notify(request)` entry.\n */\nexport function createNotificationTarget(\n caps: Pick<TerminalCaps, \"notifications\">,\n write?: (data: string) => void,\n subscribeToActivationReplies?: SubscribeToActivationReplies,\n): NotificationController {\n const activationHandlers = new Set<(event: NotificationActivation) => void>()\n const pendingActivations = new Map<string, readonly string[]>()\n let unsubscribeFromReplies: (() => void) | undefined\n let disposed = false\n\n function receiveActivation(reply: TerminalNotificationActivation): void {\n if (disposed) return\n const actionIds = pendingActivations.get(reply.id)\n if (actionIds === undefined) return\n\n const actionId = reply.button === undefined ? undefined : actionIds[reply.button - 1]\n const event: NotificationActivation | undefined =\n reply.button === undefined\n ? { id: reply.id, kind: \"notification\" }\n : actionId === undefined\n ? undefined\n : { id: reply.id, kind: \"action\", actionId }\n if (event === undefined) return\n pendingActivations.delete(reply.id)\n for (const handler of activationHandlers) handler(event)\n }\n\n function ensureReplySubscription(): boolean {\n if (unsubscribeFromReplies !== undefined) return true\n if (subscribeToActivationReplies === undefined) return false\n unsubscribeFromReplies = subscribeToActivationReplies(receiveActivation)\n return unsubscribeFromReplies !== undefined\n }\n\n function notify(request: NotificationRequest): TerminalNotificationDelivery {\n const protocol = caps.notifications\n if (protocol === false || write === undefined) {\n return { status: \"unsupported\", reason: \"notifications\" }\n }\n\n const actions = request.actions ?? []\n const wantsActivation = request.reportActivation === true || actions.length > 0\n if (actions.length > 0 && protocol !== \"osc99\") {\n return { status: \"unsupported\", reason: \"notification-actions\" }\n }\n if (request.reportActivation === true && protocol !== \"osc99\") {\n return { status: \"unsupported\", reason: \"notification-activation\" }\n }\n if (wantsActivation) {\n if (request.id === undefined) {\n throw new TypeError(\"Notification activation replies require a notification id\")\n }\n validateActions(actions)\n kittyIdentifier(request.id)\n if (!ensureReplySubscription()) {\n return { status: \"unsupported\", reason: \"notification-activation\" }\n }\n }\n\n const sequence =\n protocol === \"osc99\"\n ? encodeOsc99(request)\n : protocol === \"osc777\"\n ? encodeOsc777(request)\n : encodeOsc9(request)\n const activationId = protocol === \"osc99\" ? request.id : undefined\n const previousActionIds =\n activationId === undefined ? undefined : pendingActivations.get(activationId)\n if (activationId !== undefined) {\n setPendingActivation(\n activationId,\n wantsActivation ? actions.map((action) => action.id) : undefined,\n )\n }\n try {\n write(sequence)\n } catch (error) {\n if (activationId !== undefined) setPendingActivation(activationId, previousActionIds)\n throw error\n }\n return { status: \"emitted\", protocol }\n }\n\n function setPendingActivation(id: string, actionIds: readonly string[] | undefined): void {\n if (actionIds === undefined) pendingActivations.delete(id)\n else pendingActivations.set(id, actionIds)\n }\n\n function onNotificationActivation(handler: (event: NotificationActivation) => void): () => void {\n if (disposed) return () => {}\n activationHandlers.add(handler)\n return () => activationHandlers.delete(handler)\n }\n\n function dispose(): void {\n if (disposed) return\n disposed = true\n unsubscribeFromReplies?.()\n unsubscribeFromReplies = undefined\n pendingActivations.clear()\n activationHandlers.clear()\n }\n\n return {\n notify,\n onNotificationActivation,\n dispose,\n [Symbol.dispose]: dispose,\n }\n}\n\nconst KITTY_URGENCY: Record<NotificationUrgency, 0 | 1 | 2> = {\n low: 0,\n normal: 1,\n critical: 2,\n}\n\nfunction legacyText(text: string, delimiterSafe = false): string {\n const withoutControls = Array.from(text, (character) => {\n const code = character.charCodeAt(0)\n return code <= 0x1f || (code >= 0x7f && code <= 0x9f) ? \" \" : character\n }).join(\"\")\n const withoutDelimiters = delimiterSafe ? withoutControls.replaceAll(\";\", \",\") : withoutControls\n return withoutDelimiters.replace(/\\s+/g, \" \").trim()\n}\n\nfunction encodeOsc9(request: NotificationRequest): string {\n return `${ESC}]9;${legacyText(request.body)}${BEL}`\n}\n\nfunction encodeOsc777(request: NotificationRequest): string {\n const title = legacyText(request.title ?? \"\", true)\n const body = legacyText(request.body, true)\n return `${ESC}]777;notify;${title};${body}${BEL}`\n}\n\nfunction kittyIdentifier(id: string | undefined): string | undefined {\n if (id === undefined) return undefined\n if (id === \"0\" || !/^[A-Za-z0-9_.+-]+$/.test(id)) {\n throw new TypeError(\n \"OSC 99 notification ids may contain only A-Z, a-z, 0-9, _, -, +, and .; id 0 is reserved\",\n )\n }\n return id\n}\n\nfunction validateActions(actions: readonly NotificationAction[]): void {\n const ids = new Set<string>()\n for (const action of actions) {\n if (action.id.length === 0) throw new TypeError(\"Notification action ids must not be empty\")\n if (ids.has(action.id)) {\n throw new TypeError(`Notification action ids must be unique: ${action.id}`)\n }\n if (action.label.length === 0) {\n throw new TypeError(`Notification action labels must not be empty: ${action.id}`)\n }\n if (action.label.includes(\"\\u2028\")) {\n throw new TypeError(`Notification action labels must not contain U+2028: ${action.id}`)\n }\n ids.add(action.id)\n }\n}\n\nfunction encodedChunks(text: string): readonly string[] {\n const encoded = Buffer.from(text, \"utf8\").toString(\"base64\")\n if (encoded.length === 0) return [\"\"]\n const chunks: string[] = []\n for (let offset = 0; offset < encoded.length; offset += KITTY_ENCODED_CHUNK_SIZE) {\n chunks.push(encoded.slice(offset, offset + KITTY_ENCODED_CHUNK_SIZE))\n }\n return chunks\n}\n\nfunction encodeKittyPayload(\n request: NotificationRequest,\n payload: \"title\" | \"body\" | \"buttons\",\n text: string,\n doneAfterPayload: boolean,\n): string {\n const id = kittyIdentifier(request.id)\n const chunks = encodedChunks(text)\n if (chunks.length > 1 && id === undefined) {\n throw new TypeError(\"Chunked OSC 99 notifications require a request id\")\n }\n\n const reportActivation = request.reportActivation === true || (request.actions?.length ?? 0) > 0\n\n return chunks\n .map((chunk, index) => {\n const done = doneAfterPayload && index === chunks.length - 1\n const metadata = [\n id === undefined ? undefined : `i=${id}`,\n reportActivation ? \"a=report\" : undefined,\n `d=${done ? 1 : 0}`,\n \"e=1\",\n `p=${payload}`,\n request.urgency === undefined ? undefined : `u=${KITTY_URGENCY[request.urgency]}`,\n ]\n .filter((part): part is string => part !== undefined)\n .join(\":\")\n return `${ESC}]99;${metadata};${chunk}${ST}`\n })\n .join(\"\")\n}\n\nfunction encodeOsc99(request: NotificationRequest): string {\n const title =\n request.title === undefined ? \"\" : encodeKittyPayload(request, \"title\", request.title, false)\n const buttons =\n request.actions === undefined || request.actions.length === 0\n ? \"\"\n : encodeKittyPayload(\n request,\n \"buttons\",\n request.actions.map((action) => action.label).join(\"\\u2028\"),\n false,\n )\n return title + buttons + encodeKittyPayload(request, \"body\", request.body, true)\n}\n\n/** Parse one OSC 99 reply envelope without leaking terminal button indexes to callers. */\nexport function parseNotificationReplyEnvelope(input: string): NotificationReplyEnvelope | null {\n const prefix = `${ESC}]99;`\n const start = input.indexOf(prefix)\n if (start < 0) return null\n const terminator = input.indexOf(ST, start + prefix.length)\n if (terminator < 0) return { status: \"incomplete\", start }\n\n const end = terminator + ST.length\n const content = input.slice(start + prefix.length, terminator)\n const match = /^i=([A-Za-z0-9_.+-]+);([1-9][0-9]*)?$/.exec(content)\n const id = match?.[1]\n const button = match?.[2]\n return {\n status: \"complete\",\n start,\n end,\n activation:\n id === undefined\n ? null\n : {\n id,\n ...(button === undefined ? {} : { button: Number(button) }),\n },\n }\n}\n","/**\n * Input owner — mediates ALL stdin raw-mode + data access within a silvery\n * session. Mirrors `Output` (`./devices/output.ts`) for stdout and\n * `forwardConsole` in loggily's worker.ts for cross-process output: one owner\n * per resource, tenants issue capability requests.\n *\n * ## Why\n *\n * `process.stdin` is a global, multi-tenant resource. The historical pattern —\n * each probe captures `wasRaw = stdin.isRaw` on entry and restores it in a\n * `finally` — races silently under async. When the tenants overlap (e.g.\n * `probeColors` invoked from a React `useEffect` during `term-provider.events()`\n * startup), the last `finally` to run wins, silently disabling raw mode and\n * killing the host TUI's input. See silvery commits `2d9ab59f` + `cea0460b`\n * for tenant-side patches that make each probe *individually* race-safe via\n * the `didSetRaw + listenerCount > 0` guard. Those patches stopped the bleed\n * but the ownership vacuum remained.\n *\n * ## The shape\n *\n * One `stdin.on(\"data\", …)` listener for the owner's lifetime. Incoming\n * chunks drain through two phases:\n *\n * 1. **Probes** (registration-ordered) — `probe(opts)` callers consume\n * response bytes via a custom parser. First match wins, consumed bytes\n * are spliced out of the shared buffer.\n * 2. **Typed event parser** — whatever probes don't consume is parsed into\n * key/mouse/paste/focus events and fanned out to `onKey/onMouse/onPaste/onFocus`\n * subscribers. The parser handles bracketed paste, mouse sequences, focus\n * events, CSI/SS3 sequences, and cross-chunk incomplete CSI buffering.\n *\n * Raw mode and bracketed paste are set ONCE at construction (when `modes`\n * is provided) and restored ONCE at dispose. The owner never toggles these\n * mid-session. Tenants use `probe()` for one-shot OSC queries or subscribe\n * to the typed event handlers; neither touches termios directly.\n *\n * ## Relation to Output\n *\n * The owner is agnostic to whether Output is activated. If it is, the caller\n * passes a write function that routes through `output.write`; if not, a bare\n * `stdout.write` is fine. The owner's concern is stdin.\n */\n\nimport { isProtocolError } from \"@silvery/ansi\"\nimport { createLogger } from \"loggily\"\nimport { type Key, parseKey } from \"./keys\"\nimport {\n isMouseSequence,\n parseMouseSequence,\n type ParseMouseOptions,\n type ParsedMouse,\n} from \"../mouse\"\nimport { parseBracketedPasteEnvelope } from \"../bracketed-paste\"\nimport { parseClipboardResponseEnvelope } from \"../clipboard\"\nimport { parseFocusEvent } from \"../focus-reporting\"\nimport {\n parseNotificationReplyEnvelope,\n type TerminalNotificationActivation,\n} from \"../ansi/notification\"\nimport type { Modes } from \"./devices/modes\"\n\nconst BRACKETED_PASTE_ON = \"\\x1b[?2004h\"\nconst BRACKETED_PASTE_OFF = \"\\x1b[?2004l\"\nconst ESC_DISAMBIGUATION_MS = 25\nconst SET_MOUSE_OPTIONS = Symbol(\"silvery.input.setMouseOptions\")\n\nconst log = createLogger(\"silvery:input-owner\")\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Structured key event — input string + parsed Key metadata. */\nexport interface KeyEvent {\n input: string\n key: Key\n}\n\n/** Structured paste event — the text that was pasted (without markers). */\nexport interface PasteEvent {\n text: string\n}\n\n/** Structured focus event — whether the terminal gained or lost focus. */\nexport interface FocusEvent {\n focused: boolean\n}\n\nexport interface InputOwner extends Disposable {\n /**\n * Write a query to stdout, accumulate stdin response bytes, run `parse`\n * against the accumulated buffer on each chunk. Resolves with the first\n * non-null parse result; resolves with `null` if `timeoutMs` elapses first.\n *\n * Consumed bytes (`consumed` from the parse result) are spliced out of the\n * shared buffer. Bytes before/after the consumed region remain available\n * to subsequent probes and/or the event parser.\n */\n probe<T>(opts: {\n /** Bytes to write to stdout. May be \"\" for pure-listen probes. */\n query: string\n /**\n * Run on the accumulated buffer each time new bytes arrive.\n * Return `null` when the buffer doesn't contain a parseable response yet;\n * return `{ result, consumed }` to resolve the probe with `result` and\n * splice `consumed` bytes out of the buffer.\n *\n * NOTE: `consumed` need not equal the full buffer length; probes may\n * consume a prefix or a middle slice. The owner splices the FIRST\n * `consumed` bytes from the buffer — parsers that match a non-prefix\n * region should locate + return the exact consumed prefix length.\n */\n parse: (acc: string) => { result: T; consumed: number } | null\n /** Maximum wait in ms before resolving with `null`. */\n timeoutMs: number\n }): Promise<T | null>\n\n /**\n * Subscribe to parsed key events (press, repeat, release — handler filters\n * as needed). Returns an unsubscribe function.\n */\n onKey(handler: (event: KeyEvent) => void): () => void\n\n /**\n * Subscribe to parsed mouse events (SGR-encoded button + motion). Returns\n * an unsubscribe function.\n */\n onMouse(handler: (event: ParsedMouse) => void): () => void\n\n /**\n * Subscribe to bracketed-paste events. The `text` field holds the pasted\n * content with markers stripped. Returns an unsubscribe function.\n */\n onPaste(handler: (event: PasteEvent) => void): () => void\n\n /**\n * Subscribe to focus-in / focus-out events (CSI I / CSI O). Returns an\n * unsubscribe function.\n */\n onFocus(handler: (event: FocusEvent) => void): () => void\n\n /** Subscribe to parsed OSC 99 activation replies. */\n onNotificationActivationReply(\n handler: (event: TerminalNotificationActivation) => void,\n ): () => void\n\n /**\n * Inject raw terminal input through the canonical parser. Emulator-backed\n * Terms use this instead of maintaining a second protocol parser.\n */\n sendRaw(data: string | Buffer): void\n\n /**\n * Inject a synthetic key event. Used by emulator-backed terms\n * (`createTerm({ cols, rows, emulator })`) and test helpers to fan out to\n * the same subscribers as real stdin parsing would.\n */\n sendKey(event: KeyEvent): void\n\n /**\n * Inject a synthetic mouse event (same rationale as sendKey).\n */\n sendMouse(event: ParsedMouse): void\n\n /**\n * Inject a synthetic paste event (same rationale as sendKey).\n */\n sendPaste(event: PasteEvent): void\n\n /**\n * Inject a synthetic focus event (same rationale as sendKey).\n */\n sendFocus(event: FocusEvent): void\n\n /** True once construction succeeded and dispose() hasn't run. */\n readonly active: boolean\n /** Number of probes successfully resolved (result, not null) since activation. */\n readonly resolvedCount: number\n /** Number of probes that timed out since activation. */\n readonly timedOutCount: number\n\n dispose(): void\n [Symbol.dispose](): void\n}\n\nexport interface InputOwnerOptions {\n /**\n * Alternate writer for outgoing query bytes (e.g. `output.write`). Defaults\n * to `stdout.write.bind(stdout)`.\n */\n writeStdout?: (data: string) => boolean | void\n /**\n * When true, `dispose()` does NOT drop raw mode. The listener is still\n * removed and pending probes still resolve with null, but raw mode stays\n * set for a caller-managed handoff.\n *\n * Silvery's runtime does not use this escape hatch: probes and normal input\n * share one owner instead. Removing a listener while raw mode remains set\n * creates an ownerless interval unless the caller supplies stronger atomic\n * handoff machinery than Node streams provide.\n */\n retainRawModeOnDispose?: boolean\n /**\n * Shared Modes owner (from Term). When provided, the input owner drives\n * `stdin.setRawMode` + bracketed paste through `modes.rawMode(true/false)`\n * + `modes.bracketedPaste(true/false)` so there is exactly one writer.\n * Fallback to direct stdin calls + no bracketed-paste toggle when absent\n * keeps the standalone/tests path working without a full Term.\n */\n modes?: Modes\n /**\n * Enable bracketed paste at construction. Defaults to true when `modes`\n * is provided and the owner is TTY-backed. Set to false for unit tests\n * that don't want any protocol bytes written to stdout.\n */\n enableBracketedPaste?: boolean\n /**\n * Mouse coordinate parser options. Use this when the terminal has been put\n * into SGR-Pixels mode 1016 and cell metrics are known.\n */\n mouse?: ParseMouseOptions\n}\n\ninterface ProbeEntry {\n parse: (acc: string) => { result: unknown; consumed: number } | null\n resolve: (value: unknown) => void\n timer: ReturnType<typeof setTimeout>\n settled: boolean\n}\n\ninterface ConfigurableInputOwner extends InputOwner {\n [SET_MOUSE_OPTIONS](options: ParseMouseOptions | undefined): void\n}\n\n/** Configure startup-discovered mouse metrics without widening Term.input's public API. */\nexport function setInputOwnerMouseOptions(\n input: InputOwner,\n options: ParseMouseOptions | undefined,\n): void {\n const configure = (input as ConfigurableInputOwner)[SET_MOUSE_OPTIONS]\n if (!configure) throw new Error(\"InputOwner does not support negotiated mouse options\")\n configure(options)\n}\n\n// ============================================================================\n// Input Splitting (moved from term-provider.ts)\n// ============================================================================\n\n/**\n * Result of splitting raw input — includes parsed sequences and any trailing\n * incomplete CSI sequence that needs cross-chunk buffering.\n */\ninterface SplitResult {\n sequences: string[]\n incomplete: string | null\n}\n\n/**\n * Split a raw stdin chunk into individual key sequences.\n *\n * When the OS buffers key repeat events, stdin delivers multiple keystrokes\n * in a single read (e.g., \"jjjjj\" for held 'j'). `parseKey` expects one\n * keystroke at a time, so we split first.\n *\n * When a CSI sequence (ESC [ ...) ends at the chunk boundary without a\n * terminator, it is returned as `incomplete` so the caller can buffer it\n * and prepend to the next chunk. This handles SGR mouse sequences that\n * split across stdin data events (e.g., '\\x1b[<0;58;8' + 'M').\n */\nfunction splitRawInput(raw: string): SplitResult {\n const sequences: string[] = []\n let i = 0\n while (i < raw.length) {\n if (raw[i] === \"\\x1b\") {\n // Escape sequence\n if (i + 1 >= raw.length) {\n // Bare ESC at the chunk boundary may be either the Escape key or the\n // prefix of a CSI/SS3/meta sequence arriving in the next read.\n return { sequences, incomplete: raw.slice(i) }\n }\n if (raw[i + 1] === \"[\") {\n // CSI sequence: ESC [ ... <letter or ~>\n let j = i + 2\n while (j < raw.length && !isCSITerminator(raw[j]!)) j++\n if (j < raw.length) {\n j++ // include terminator\n sequences.push(raw.slice(i, j))\n i = j\n } else {\n // Incomplete CSI — hit end of chunk without finding terminator.\n return { sequences, incomplete: raw.slice(i) }\n }\n } else if (raw[i + 1] === \"O\") {\n // SS3 sequence: ESC O <letter>\n const end = Math.min(i + 3, raw.length)\n sequences.push(raw.slice(i, end))\n i = end\n } else if (raw[i + 1] === \"\\x1b\") {\n // Double ESC: meta + escape, OR meta + CSI/SS3 sequence\n if (i + 2 < raw.length && raw[i + 2] === \"[\") {\n let j = i + 3\n while (j < raw.length && !isCSITerminator(raw[j]!)) j++\n if (j < raw.length) {\n j++\n sequences.push(raw.slice(i, j))\n i = j\n } else {\n return { sequences, incomplete: raw.slice(i) }\n }\n } else if (i + 2 < raw.length && raw[i + 2] === \"O\") {\n const end = Math.min(i + 4, raw.length)\n sequences.push(raw.slice(i, end))\n i = end\n } else {\n sequences.push(\"\\x1b\\x1b\")\n i += 2\n }\n } else {\n // Meta key: ESC + char\n sequences.push(raw.slice(i, i + 2))\n i += 2\n }\n } else {\n // Single byte (printable char, ctrl code, etc.)\n sequences.push(raw[i]!)\n i++\n }\n }\n return { sequences, incomplete: null }\n}\n\nfunction isCSITerminator(ch: string): boolean {\n return (ch >= \"A\" && ch <= \"Z\") || (ch >= \"a\" && ch <= \"z\") || ch === \"~\"\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\nexport function createInputOwner(\n stdin: NodeJS.ReadStream,\n stdout: NodeJS.WriteStream,\n options: InputOwnerOptions = {},\n): InputOwner {\n const writeStdout = options.writeStdout ?? ((data: string) => stdout.write(data))\n\n // Termios setup — ONCE. When non-TTY, we become a no-op owner (probes time\n // out, event subscribers never fire); stdin is left untouched. We still\n // install the structure against an in-memory buffer so callers don't need\n // to branch.\n const isTTY = Boolean(stdin.isTTY)\n const injectedModes = options.modes\n let rawWasSet = false\n let bracketedPasteWasSet = false\n if (isTTY) {\n try {\n // Capture the prior state so dispose() decides whether to restore or\n // no-op. In the canonical TUI lifecycle the owner is the FIRST\n // raw-mode setter of the session; `wasRaw` should be false. We record\n // it defensively so an owner constructed inside an already-raw session\n // (e.g. nested run()) doesn't flip raw=false on dispose and kill the\n // outer owner's input.\n const wasRaw = stdin.isRaw\n if (!wasRaw) {\n // Single writer: drive through Modes when provided. Fallback to\n // direct stdin call when constructed without a Modes owner\n // (standalone / test path).\n if (injectedModes) injectedModes.rawMode(true)\n else stdin.setRawMode(true)\n rawWasSet = true\n }\n stdin.resume()\n stdin.setEncoding(\"utf8\")\n\n // Bracketed paste — default-on for any TTY-backed owner. Route through\n // Modes when provided (single-writer invariant); otherwise write the\n // protocol bytes directly through `writeStdout` so standalone/test\n // paths still get paste detection. Opt-out via `enableBracketedPaste:\n // false` for unit tests that don't want protocol bytes written.\n const shouldEnablePaste = options.enableBracketedPaste ?? true\n if (shouldEnablePaste) {\n if (injectedModes) injectedModes.bracketedPaste(true)\n else writeStdout(BRACKETED_PASTE_ON)\n bracketedPasteWasSet = true\n }\n } catch (err) {\n log?.warn?.(`termios setup failed: ${String(err)}`)\n }\n }\n\n // Per-owner state.\n let buffer = \"\"\n let incompleteSequence: string | null = null\n let incompletePaste: string | null = null\n let incompleteNotification: string | null = null\n let incompleteSequenceTimer: ReturnType<typeof setTimeout> | null = null\n let incompleteSequenceImmediate: ReturnType<typeof setImmediate> | null = null\n const probes: ProbeEntry[] = []\n const keyHandlers = new Set<(e: KeyEvent) => void>()\n const mouseHandlers = new Set<(e: ParsedMouse) => void>()\n const pasteHandlers = new Set<(e: PasteEvent) => void>()\n const focusHandlers = new Set<(e: FocusEvent) => void>()\n const notificationActivationHandlers = new Set<(e: TerminalNotificationActivation) => void>()\n let mouseOptions = options.mouse\n let resolvedCount = 0\n let timedOutCount = 0\n let disposed = false\n let inputBatchSeq = 0\n\n // Fire an event to every handler in a set, catching throws so one broken\n // subscriber doesn't prevent others from seeing the event.\n function fire<T>(handlers: Set<(e: T) => void>, event: T): void {\n for (const handler of handlers) {\n try {\n handler(event)\n } catch (err) {\n log?.warn?.(`handler threw: ${String(err)}`)\n }\n }\n }\n\n /**\n * Parse one CSI/SS3/meta/control/printable sequence into the right event\n * type and fire it. Order: focus → mouse → key (catch-all).\n */\n function dispatchSequence(raw: string, receivedAt?: number, inputBatchId?: number): void {\n const focus = parseFocusEvent(raw)\n if (focus) {\n fire(focusHandlers, { focused: focus.type === \"focus-in\" })\n return\n }\n if (isMouseSequence(raw)) {\n const mouse = parseMouseSequence(raw, mouseOptions)\n if (mouse) {\n const event = { ...mouse, receivedAt, inputBatchId }\n // Action-specific debug — silvery:input-owner only logs the\n // less-frequent wheel and click actions to avoid drowning move\n // streams. Use `silvery:input-owner` namespace for live capture.\n // Bead: @km/code/trackpad-wheel-not-scrolling.\n if (mouse.action === \"wheel\" || mouse.action === \"down\" || mouse.action === \"up\") {\n log?.debug?.(\n `parsed mouse: action=${mouse.action} button=${mouse.button} x=${mouse.x} y=${mouse.y} delta=${mouse.delta ?? 0} bytes=${JSON.stringify(raw)}`,\n )\n }\n fire(mouseHandlers, event)\n return\n }\n // Mouse sequence detected but failed to parse — log raw bytes so\n // we can spot terminal-specific formats the parser doesn't handle.\n log?.warn?.(`mouse sequence failed to parse: ${JSON.stringify(raw)}`)\n }\n const [input, key] = parseKey(raw)\n fire(keyHandlers, { input, key })\n }\n\n function clearIncompleteTimer(): void {\n if (incompleteSequenceTimer !== null) {\n clearTimeout(incompleteSequenceTimer)\n incompleteSequenceTimer = null\n }\n if (incompleteSequenceImmediate !== null) {\n clearImmediate(incompleteSequenceImmediate)\n incompleteSequenceImmediate = null\n }\n }\n\n function scheduleIncompleteFlush(receivedAt?: number, inputBatchId?: number): void {\n if (incompleteSequence !== \"\\x1b\") return\n clearIncompleteTimer()\n incompleteSequenceTimer = setTimeout(() => {\n incompleteSequenceTimer = null\n // Yield through the check phase before committing standalone Escape.\n // If the event loop was busy while the timeout expired, already-ready\n // stdin tail bytes get one poll phase to arrive and reassemble first.\n incompleteSequenceImmediate = setImmediate(() => {\n incompleteSequenceImmediate = null\n if (disposed || incompleteSequence !== \"\\x1b\") return\n incompleteSequence = null\n dispatchSequence(\"\\x1b\", receivedAt, inputBatchId)\n })\n }, ESC_DISAMBIGUATION_MS)\n }\n\n function dispatchRawChunk(chunk: string, receivedAt?: number, inputBatchId?: number): void {\n if (chunk.length === 0) return\n const { sequences, incomplete } = splitRawInput(chunk)\n incompleteSequence = incomplete\n scheduleIncompleteFlush(receivedAt, inputBatchId)\n for (const raw of sequences) dispatchSequence(raw, receivedAt, inputBatchId)\n }\n\n // Drain the current buffer against probes (in registration order). Anything\n // probes don't consume flows into the event parser, which fires typed\n // handlers for each parsed sequence.\n function drain(receivedAt?: number, inputBatchId?: number): void {\n if (disposed) return\n\n // Loop because one probe resolving may leave bytes that unblock the next.\n let progress = true\n while (progress && probes.length > 0 && buffer.length > 0) {\n progress = false\n for (let i = 0; i < probes.length; i++) {\n const entry = probes[i]!\n if (entry.settled) continue\n let parsed: { result: unknown; consumed: number } | null\n try {\n parsed = entry.parse(buffer)\n } catch (err) {\n log?.warn?.(`probe parse threw: ${String(err)}`)\n entry.settled = true\n clearTimeout(entry.timer)\n entry.resolve(null)\n progress = true\n break\n }\n if (parsed !== null) {\n const consumed = Math.max(0, Math.min(parsed.consumed, buffer.length))\n buffer = buffer.slice(consumed)\n entry.settled = true\n clearTimeout(entry.timer)\n resolvedCount++\n entry.resolve(parsed.result)\n progress = true\n break\n }\n }\n for (let i = probes.length - 1; i >= 0; i--) {\n if (probes[i]!.settled) probes.splice(i, 1)\n }\n }\n\n // Parser phase: run leftover bytes through the typed event parser.\n if (buffer.length === 0) return\n\n // Prepend any buffered incomplete escape/control sequence from a prior\n // chunk so split SGR mouse sequences (e.g. '\\x1b[<0;58;8' + 'M')\n // reassemble.\n let chunk = buffer\n buffer = \"\"\n if (incompleteSequence !== null) {\n clearIncompleteTimer()\n chunk = incompleteSequence + chunk\n incompleteSequence = null\n }\n if (incompletePaste !== null) {\n chunk = incompletePaste + chunk\n incompletePaste = null\n }\n if (incompleteNotification !== null) {\n chunk = incompleteNotification + chunk\n incompleteNotification = null\n }\n\n // Bracketed paste is detected before splitting into individual keys —\n // paste content is one logical event, not a stream of keystrokes.\n //\n // The parser may throw ProtocolError when PASTE_START is found but no\n // PASTE_END follows in this chunk. That commonly indicates a stream-\n // split paste (the rest arrives in the next TTY read), so we buffer and\n // retry before splitting into key events. This preserves paste atomicity\n // while still emitting a debug-log breadcrumb so chronic protocol-format\n // problems become visible. Bead reference:\n // @km/silvery/15127-custom-protocol-implementation/protocol-loud-errors.\n let remaining = chunk\n while (remaining.length > 0) {\n let pasteEnvelope: ReturnType<typeof parseBracketedPasteEnvelope> = null\n try {\n pasteEnvelope = parseBracketedPasteEnvelope(remaining)\n } catch (err) {\n if (isProtocolError(err)) {\n incompletePaste = remaining\n log?.debug?.(\n `bracketed paste parser buffered incomplete input: ${err.reason} (parser=${err.parser}, len=${err.inputLength})`,\n )\n return\n } else {\n log?.warn?.(`bracketed paste parser threw: ${String(err)}`)\n }\n }\n\n let clipboardEnvelope: ReturnType<typeof parseClipboardResponseEnvelope> = null\n try {\n clipboardEnvelope = parseClipboardResponseEnvelope(remaining)\n } catch (err) {\n if (isProtocolError(err)) {\n log?.debug?.(\n `clipboard parser flagged malformed input: ${err.reason} (parser=${err.parser}, len=${err.inputLength})`,\n )\n } else {\n log?.warn?.(`clipboard parser threw: ${String(err)}`)\n }\n }\n\n const notificationEnvelope = parseNotificationReplyEnvelope(remaining)\n const notificationIsFirst =\n notificationEnvelope !== null &&\n (!pasteEnvelope || notificationEnvelope.start <= pasteEnvelope.start) &&\n (!clipboardEnvelope || notificationEnvelope.start <= clipboardEnvelope.start)\n\n if (notificationIsFirst) {\n dispatchRawChunk(remaining.slice(0, notificationEnvelope.start), receivedAt, inputBatchId)\n if (notificationEnvelope.status === \"incomplete\") {\n incompleteNotification = remaining.slice(notificationEnvelope.start)\n return\n }\n if (notificationEnvelope.activation !== null) {\n fire(notificationActivationHandlers, notificationEnvelope.activation)\n }\n remaining = remaining.slice(notificationEnvelope.end)\n continue\n }\n\n if (pasteEnvelope && (!clipboardEnvelope || pasteEnvelope.start <= clipboardEnvelope.start)) {\n dispatchRawChunk(remaining.slice(0, pasteEnvelope.start), receivedAt, inputBatchId)\n fire(pasteHandlers, { text: pasteEnvelope.result.content })\n remaining = remaining.slice(pasteEnvelope.end)\n continue\n }\n\n if (clipboardEnvelope) {\n dispatchRawChunk(remaining.slice(0, clipboardEnvelope.start), receivedAt, inputBatchId)\n fire(pasteHandlers, { text: clipboardEnvelope.text })\n remaining = remaining.slice(clipboardEnvelope.end)\n continue\n }\n\n dispatchRawChunk(remaining, receivedAt, inputBatchId)\n return\n }\n }\n\n // Single stdin listener — the whole reason this file exists. No other\n // code in the session should call stdin.on(\"data\", …) or stdin.setRawMode.\n function sendRaw(chunk: string | Buffer): void {\n if (disposed) return\n const receivedAt = performance.now()\n const inputBatchId = ++inputBatchSeq\n buffer += typeof chunk === \"string\" ? chunk : chunk.toString(\"utf8\")\n drain(receivedAt, inputBatchId)\n }\n const onChunk = (chunk: string | Buffer) => sendRaw(chunk)\n if (isTTY) stdin.on(\"data\", onChunk)\n\n function probe<T>(opts: {\n query: string\n parse: (acc: string) => { result: T; consumed: number } | null\n timeoutMs: number\n }): Promise<T | null> {\n if (disposed) return Promise.resolve(null)\n if (!isTTY) {\n // Non-TTY owners still accept probes — they just time out.\n return new Promise((resolve) => setTimeout(() => resolve(null), opts.timeoutMs))\n }\n\n return new Promise<T | null>((resolve) => {\n let settled = false\n const entry: ProbeEntry = {\n parse: opts.parse as (acc: string) => { result: unknown; consumed: number } | null,\n resolve: (value) => {\n if (settled) return\n settled = true\n resolve(value as T | null)\n },\n timer: setTimeout(() => {\n if (entry.settled) return\n entry.settled = true\n const idx = probes.indexOf(entry)\n if (idx >= 0) probes.splice(idx, 1)\n timedOutCount++\n entry.resolve(null)\n }, opts.timeoutMs),\n settled: false,\n }\n probes.push(entry)\n\n // Write the query AFTER registering. Terminal responses typically\n // arrive async, but a mocked terminal may respond synchronously inside\n // the write — we need the probe registered first so the response\n // doesn't fall through to the event parser.\n if (opts.query.length > 0) {\n try {\n writeStdout(opts.query)\n } catch (err) {\n log?.warn?.(`probe query write failed: ${String(err)}`)\n clearTimeout(entry.timer)\n entry.settled = true\n const idx = probes.indexOf(entry)\n if (idx >= 0) probes.splice(idx, 1)\n entry.resolve(null)\n return\n }\n }\n\n // Drain eagerly so a probe registered against already-buffered bytes\n // resolves immediately.\n if (buffer.length > 0) drain()\n })\n }\n\n function onKey(handler: (e: KeyEvent) => void): () => void {\n keyHandlers.add(handler)\n return () => {\n keyHandlers.delete(handler)\n }\n }\n\n function onMouse(handler: (e: ParsedMouse) => void): () => void {\n mouseHandlers.add(handler)\n return () => {\n mouseHandlers.delete(handler)\n }\n }\n\n function onPaste(handler: (e: PasteEvent) => void): () => void {\n pasteHandlers.add(handler)\n return () => {\n pasteHandlers.delete(handler)\n }\n }\n\n function onFocus(handler: (e: FocusEvent) => void): () => void {\n focusHandlers.add(handler)\n return () => {\n focusHandlers.delete(handler)\n }\n }\n\n function onNotificationActivationReply(\n handler: (e: TerminalNotificationActivation) => void,\n ): () => void {\n notificationActivationHandlers.add(handler)\n return () => {\n notificationActivationHandlers.delete(handler)\n }\n }\n\n function sendKey(event: KeyEvent): void {\n if (disposed) return\n fire(keyHandlers, event)\n }\n\n function sendMouse(event: ParsedMouse): void {\n if (disposed) return\n fire(mouseHandlers, event)\n }\n\n function sendPaste(event: PasteEvent): void {\n if (disposed) return\n fire(pasteHandlers, event)\n }\n\n function sendFocus(event: FocusEvent): void {\n if (disposed) return\n fire(focusHandlers, event)\n }\n\n function setMouseOptions(next: ParseMouseOptions | undefined): void {\n if (disposed) return\n mouseOptions = next\n }\n\n function dispose(): void {\n if (disposed) return\n disposed = true\n\n // Resolve pending probes with null so awaiting callers don't hang.\n for (const entry of probes) {\n if (entry.settled) continue\n entry.settled = true\n clearTimeout(entry.timer)\n try {\n entry.resolve(null)\n } catch {\n // downstream already handled\n }\n }\n probes.length = 0\n keyHandlers.clear()\n mouseHandlers.clear()\n pasteHandlers.clear()\n focusHandlers.clear()\n notificationActivationHandlers.clear()\n clearIncompleteTimer()\n buffer = \"\"\n incompleteSequence = null\n incompletePaste = null\n incompleteNotification = null\n\n if (isTTY) {\n try {\n stdin.off(\"data\", onChunk)\n } catch {\n // listener already removed\n }\n if (!options.retainRawModeOnDispose) {\n try {\n // Disable bracketed paste FIRST (before raw-mode restore) so the\n // enable/disable pair nests inside the raw-mode lifetime.\n if (bracketedPasteWasSet) {\n if (injectedModes) injectedModes.bracketedPaste(false)\n else writeStdout(BRACKETED_PASTE_OFF)\n }\n } catch {\n // stdin may already be closed\n }\n try {\n if (rawWasSet) {\n if (injectedModes) injectedModes.rawMode(false)\n else stdin.setRawMode(false)\n }\n } catch {\n // stdin may already be closed\n }\n try {\n stdin.pause()\n } catch {\n // stdin may already be closed\n }\n }\n }\n\n log?.debug?.(`disposed (resolved=${resolvedCount}, timedOut=${timedOutCount})`)\n }\n\n const owner: InputOwner = {\n probe,\n onKey,\n onMouse,\n onPaste,\n onFocus,\n onNotificationActivationReply,\n sendRaw,\n sendKey,\n sendMouse,\n sendPaste,\n sendFocus,\n get active() {\n return !disposed\n },\n get resolvedCount() {\n return resolvedCount\n },\n get timedOutCount() {\n return timedOutCount\n },\n dispose,\n [Symbol.dispose]: dispose,\n }\n Object.defineProperty(owner, SET_MOUSE_OPTIONS, { value: setMouseOptions })\n return owner\n}\n","/**\n * ConsoleRouter — single patcher for `console.log/info/warn/error/debug`.\n *\n * Before this owner existed, `Console` and `Output` independently monkey-\n * patched `console.*` — the last patcher to activate stole the wrappers from\n * the earlier one. Pro review 2026-04-22 P0-3 flagged this; the structural\n * fix is exactly one patcher that both owners register policies against.\n *\n * The router owns the five `target[method] = ...` installs and the matching\n * restore. Console and Output call `registerTap` / `registerSink` to declare\n * intent; the router composes them in a single wrapper per method.\n *\n * ### Invocation order per `console.*` call\n *\n * 1. All registered **taps** fire in registration order.\n * Taps are pure observers — they see the call but cannot change it.\n * Used by `term.console` to record entries for later replay.\n *\n * 2. The **sink policy** decides forwarding:\n * - `suppress: true` → drop the call (no original forward, no redirect).\n * - `redirectFd` → write a formatted single-line copy to that fd\n * (canonical use: redirect stderr to DEBUG_LOG so\n * foreign console.warn doesn't corrupt the alt screen).\n * - neither → forward to the ORIGINAL console method captured\n * before any patching.\n * Only the most recently registered sink is consulted (last-one-wins).\n * Unregistering a sink reverts to the prior sink (stack discipline) —\n * or, if no sink is registered, the call is forwarded.\n *\n * ### Lifecycle\n *\n * Router construction is free. First `registerTap` or `registerSink` call\n * installs all five wrappers and captures originals. Last unregister (both\n * tap set empty AND sink stack empty) restores the originals and clears\n * the wrappers. Dispose is an explicit reset.\n */\n\nimport type { ConsoleMethod } from \"../../ansi/types\"\nimport { writeSync } from \"node:fs\"\n\nconst METHODS: ConsoleMethod[] = [\"log\", \"info\", \"warn\", \"error\", \"debug\"]\n\n/** Shape a tap handler receives on every console.* call. */\nexport interface ConsoleCall {\n method: ConsoleMethod\n args: unknown[]\n}\n\n/** Sink policy registered by a consumer (e.g. Output's alt-screen guard). */\nexport interface ConsoleSinkPolicy {\n /**\n * If true, the call is dropped (no forward to original, no redirect).\n * Default: false.\n */\n suppress?: boolean\n /**\n * If set, a single-line formatted copy of the call is written via\n * `fs.writeSync(redirectFd, …)`. Typical use: redirect console.* to\n * DEBUG_LOG during alt-screen. Mutually exclusive with `suppress: true`\n * at the semantic level (if both are set, `suppress` wins).\n */\n redirectFd?: number | null\n}\n\n/**\n * Public shape of a ConsoleRouter.\n */\nexport interface ConsoleRouter extends Disposable {\n /**\n * Register a tap: called on every console.* call in registration order,\n * before sink policy. Tap handlers are pure observers. Returns an\n * unregister function.\n */\n registerTap(handler: (call: ConsoleCall) => void): () => void\n\n /**\n * Register a sink policy. The most recently registered sink is the\n * active one (stack semantics). Unregister pops the stack back to the\n * prior sink. Returns an unregister function.\n */\n registerSink(policy: ConsoleSinkPolicy): () => void\n\n /** True while at least one tap OR sink is registered (i.e. wrappers installed). */\n readonly active: boolean\n\n /** Dispose — restore originals, clear taps + sinks. Idempotent. */\n dispose(): void\n [Symbol.dispose](): void\n}\n\n/**\n * Format a console call into a single line for DEBUG_LOG redirect.\n * Shape matches Console.replay's format: args joined with space, newline.\n */\nfunction formatLine(call: ConsoleCall): string {\n return (\n call.args\n .map((a) =>\n typeof a === \"string\"\n ? a\n : a instanceof Error\n ? `${a.name}: ${a.message}`\n : safeJsonStringify(a),\n )\n .join(\" \") + \"\\n\"\n )\n}\n\nfunction safeJsonStringify(value: unknown): string {\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n}\n\n/**\n * Create a ConsoleRouter patching the given console global (defaults to\n * `globalThis.console`). Construction is cheap: no patching happens until\n * the first `registerTap` / `registerSink` call. Every subsequent register\n * joins the existing wrappers; unregister that drops the last registrant\n * restores the originals.\n */\nexport function createConsoleRouter(\n target: globalThis.Console = globalThis.console,\n): ConsoleRouter {\n const taps = new Set<(call: ConsoleCall) => void>()\n const sinkStack: ConsoleSinkPolicy[] = []\n const originals = new Map<ConsoleMethod, globalThis.Console[ConsoleMethod]>()\n let installed = false\n let disposed = false\n\n function currentSink(): ConsoleSinkPolicy | null {\n return sinkStack.length > 0 ? sinkStack[sinkStack.length - 1]! : null\n }\n\n function install() {\n if (installed || disposed) return\n installed = true\n for (const method of METHODS) {\n originals.set(method, target[method])\n const original = originals.get(method)!\n target[method] = ((...args: unknown[]) => {\n const call: ConsoleCall = { method, args }\n\n // 1. Fan out to taps. Isolated in try/catch — a broken tap must not\n // derail forwarding or other taps. (Matches the signals-owner\n // isolation pattern.)\n for (const tap of taps) {\n try {\n tap(call)\n } catch {\n /* isolate */\n }\n }\n\n // 2. Apply sink policy.\n const sink = currentSink()\n if (sink?.suppress) return\n if (sink?.redirectFd != null) {\n try {\n writeSync(sink.redirectFd, formatLine(call))\n } catch {\n /* fd may be closed */\n }\n return\n }\n // 3. No sink → forward to the captured original.\n ;(original as (...a: unknown[]) => void).call(target, ...args)\n }) as globalThis.Console[ConsoleMethod]\n }\n }\n\n function uninstall() {\n if (!installed) return\n for (const method of METHODS) {\n const original = originals.get(method)\n if (original) target[method] = original\n }\n originals.clear()\n installed = false\n }\n\n function maybeUninstall() {\n if (taps.size === 0 && sinkStack.length === 0) uninstall()\n }\n\n function registerTap(handler: (call: ConsoleCall) => void): () => void {\n if (disposed) return () => {}\n taps.add(handler)\n install()\n return () => {\n taps.delete(handler)\n maybeUninstall()\n }\n }\n\n function registerSink(policy: ConsoleSinkPolicy): () => void {\n if (disposed) return () => {}\n sinkStack.push(policy)\n install()\n return () => {\n const idx = sinkStack.lastIndexOf(policy)\n if (idx >= 0) sinkStack.splice(idx, 1)\n maybeUninstall()\n }\n }\n\n function dispose() {\n if (disposed) return\n disposed = true\n uninstall()\n taps.clear()\n sinkStack.length = 0\n }\n\n return {\n registerTap,\n registerSink,\n get active() {\n return installed\n },\n dispose,\n [Symbol.dispose]: dispose,\n }\n}\n","/**\n * Output — single-owner stdout/stderr/console mediator for a silvery session.\n *\n * Mirrors `InputOwner` (`../input-owner.ts`) for the write side: one owner per\n * Term, stable across the session. When active, intercepts process.stdout,\n * process.stderr, and console.* so only the render pipeline (via `output.write`)\n * reaches the terminal. Non-silvery writes are suppressed (stdout) or redirected\n * to `options.stderrLog`/`process.env.DEBUG_LOG` (stderr/console).\n *\n * ## Lifecycle\n *\n * Constructed once per Term, initially deactivated. `activate()` installs the\n * intercepts; `deactivate()` restores originals; `dispose()` does final cleanup\n * (closes stderr fd, flushes buffered stderr). The activate/deactivate cycle is\n * used by the runtime to temporarily pass writes through during pause/resume\n * (console mode, log dump).\n *\n * ## Relation to InputOwner\n *\n * InputOwner is constructed once at term creation, activated immediately (it\n * needs raw mode + stdin data listener to mediate probes). Output is\n * constructed deactivated because installing intercepts before protocol setup\n * (alt screen, kitty keyboard) would suppress the setup sequences themselves.\n * The runtime calls `activate()` after protocol setup completes.\n */\n\nimport { openSync, writeSync, closeSync } from \"node:fs\"\nimport { createLogger } from \"loggily\"\nimport { signal, type ReadSignal } from \"@silvery/signals\"\nimport { createConsoleRouter, type ConsoleRouter } from \"./console-router\"\n\nconst log = createLogger(\"silvery:guard\")\n\nexport interface Output extends Disposable {\n /** Write data to stdout. When active, bypasses the intercept (silvery's render\n * pipeline writes go through here). When inactive, forwards to the raw\n * stdout.write. */\n write(data: string | Uint8Array): boolean\n /**\n * Whether intercepts are currently installed — a `ReadSignal<boolean>`.\n * Call `output.active()` to read; subscribe with\n * `effect(() => output.active())`. The owner writes it internally from\n * `activate()` / `deactivate()`.\n */\n readonly active: ReadSignal<boolean>\n /** Activate intercepts: installs stdout/stderr/console patches. Idempotent —\n * no-op if already active. Options override those passed at construction. */\n activate(options?: OutputOptions): void\n /** Deactivate intercepts: restores original stdout/stderr/console methods.\n * Idempotent. Closes stderr log fd if open. */\n deactivate(): void\n /** Number of stdout writes suppressed since construction (cumulative across\n * activate/deactivate cycles). Plain getter — changes on every write, not\n * worth the reactive cost. */\n readonly suppressedCount: number\n /** Number of stderr writes redirected since construction (cumulative across\n * activate/deactivate cycles). Plain getter — changes on every write. */\n readonly redirectedCount: number\n /** Final cleanup: deactivates + any teardown. Idempotent. */\n dispose(): void\n [Symbol.dispose](): void\n}\n\nexport interface OutputOptions {\n /** File path to redirect stderr to (default: process.env.DEBUG_LOG) */\n stderrLog?: string\n /** If true, buffer stderr and flush on deactivate instead of redirecting to file */\n bufferStderr?: boolean\n}\n\n/**\n * Create an Output owner. Starts deactivated — call `activate()` to install\n * intercepts. Call `dispose()` for final cleanup.\n *\n * A `ConsoleRouter` may be passed to route `console.*` redirect policy\n * through the shared patcher (so Console's tap and Output's sink layer\n * deterministically). When omitted, Output patches `console.*` via its own\n * private router — the behaviour is identical, but Console's tap cannot\n * share the patch site.\n */\nexport function createOutput(defaultOptions?: OutputOptions, router?: ConsoleRouter): Output {\n let disposed = false\n // Reactive `active` — internal writes from activate/deactivate only.\n // Exposed read-only via the `active` ReadSignal on the public Output shape.\n const _active = signal<boolean>(false)\n\n // Cumulative stats across activate/deactivate cycles\n let suppressedCount = 0\n let redirectedCount = 0\n\n // Saved originals — captured at activation time, restored at deactivation\n let savedStdoutWrite: typeof process.stdout.write | null = null\n let savedStderrWrite: typeof process.stderr.write | null = null\n let origStdoutWrite: ((chunk: unknown, ...args: unknown[]) => boolean) | null = null\n let origStderrWrite: ((chunk: unknown, ...args: unknown[]) => boolean) | null = null\n\n // Stderr redirection state (re-created on activate, torn down on deactivate)\n let stderrFd: number | null = null\n let stderrBuffer: string[] = []\n let bufferStderr = false\n\n // Router is the canonical patcher for console.*. `unregisterSink` holds\n // the disposer for the sink policy we registered in activate().\n const ownsRouter = !router\n // console-router.ts imports nothing from output.ts, so the historical cycle\n // that motivated a lazy require no longer exists — a static import is safe and\n // ESM-legal (createConsoleRouter is only called here at factory time).\n const _router: ConsoleRouter = router ?? createConsoleRouter()\n let unregisterSink: (() => void) | null = null\n\n // Route flag — when true, stdout.write(…) inside the intercept forwards to the\n // original (silvery's own write() path toggles this briefly).\n let silveryWriting = false\n\n function activate(options?: OutputOptions): void {\n if (disposed) return\n if (_active()) return\n _active(true)\n\n const opts = { ...defaultOptions, ...options }\n bufferStderr = !!opts.bufferStderr\n\n savedStdoutWrite = process.stdout.write\n savedStderrWrite = process.stderr.write\n origStdoutWrite = savedStdoutWrite.bind(process.stdout) as typeof origStdoutWrite\n origStderrWrite = savedStderrWrite.bind(process.stderr) as typeof origStderrWrite\n\n const stderrLog = opts.stderrLog ?? process.env.DEBUG_LOG\n if (stderrLog) {\n try {\n stderrFd = openSync(stderrLog, \"a\")\n } catch {\n // If we can't open the log file, fall back to suppression\n }\n }\n\n process.stdout.write = function (chunk: unknown, ...args: unknown[]): boolean {\n if (silveryWriting) {\n return origStdoutWrite!(chunk, ...args)\n }\n // Non-silvery stdout write -- suppress in alt screen\n suppressedCount++\n const preview = typeof chunk === \"string\" ? chunk.slice(0, 60) : \"<binary>\"\n log?.debug?.(`suppressed stdout write (${suppressedCount}): ${JSON.stringify(preview)}`)\n return true\n } as typeof process.stdout.write\n\n process.stderr.write = function (chunk: unknown, ..._args: unknown[]): boolean {\n const str = typeof chunk === \"string\" ? chunk : String(chunk)\n redirectedCount++\n if (stderrFd !== null) {\n try {\n writeSync(stderrFd, str)\n } catch {\n // File may have been closed externally\n }\n return true\n }\n if (bufferStderr) {\n stderrBuffer.push(str)\n return true\n }\n return true\n } as typeof process.stderr.write\n\n // Intercept console methods via the shared ConsoleRouter — the router\n // owns the five `target[method] = wrapper` installs and composes Output's\n // redirect policy with whatever Console has registered as a tap. Using\n // the router (instead of patching console.* directly here) is what makes\n // the Console tap + Output sink coexist regardless of activation order\n // (Pro review 2026-04-22 P0-3 structural fix).\n unregisterSink = _router.registerSink({\n // If we have a fd to redirect to, drive the router's writeSync path.\n // Otherwise we'd want to buffer/drop — the router currently supports\n // suppress + redirect, so we emulate \"buffer\" by intercepting in our\n // own observer-style sink extension (via a tap that appends to the\n // stderrBuffer). Keep the canonical flow simple: fd-redirect is the\n // production case; buffering is the test case.\n redirectFd: stderrFd,\n })\n if (bufferStderr && stderrFd === null) {\n // Test/buffer mode: register a second sink that captures console.* into\n // stderrBuffer. The last-registered sink wins; register AFTER the\n // redirect sink so this one is active. Buffering also counts toward\n // redirectedCount so consumers see an accurate tally.\n unregisterSink?.()\n unregisterSink = _router.registerSink({\n // The router's sink currently has suppress/redirect semantics only;\n // buffering requires the caller to also register a tap for side effects.\n // We use suppress + a tap below.\n suppress: true,\n })\n // Tap: every call gets appended to stderrBuffer + counted.\n const unregisterBufferTap = _router.registerTap((call) => {\n const str = call.args.map((a) => (typeof a === \"string\" ? a : String(a))).join(\" \") + \"\\n\"\n redirectedCount++\n stderrBuffer.push(str)\n })\n const prevUnregister = unregisterSink\n unregisterSink = () => {\n unregisterBufferTap()\n prevUnregister()\n }\n } else {\n // Count fd-redirected writes by registering a tap alongside the redirect\n // sink. The tap fires BEFORE the sink runs writeSync, so count is in sync.\n const unregisterCountTap = _router.registerTap(() => {\n redirectedCount++\n })\n const prevUnregister = unregisterSink\n unregisterSink = () => {\n unregisterCountTap()\n prevUnregister!()\n }\n }\n\n log?.info?.(\"activated\" + (stderrLog ? ` (stderr -> ${stderrLog})` : \" (stderr suppressed)\"))\n }\n\n function deactivate(): void {\n if (!_active()) return\n _active(false)\n\n if (savedStdoutWrite) process.stdout.write = savedStdoutWrite\n if (savedStderrWrite) process.stderr.write = savedStderrWrite\n // Unregister the sink + redirect tap on the ConsoleRouter. The router\n // itself stays wired (Console may still be tapping) — this just pops\n // Output's policies off.\n unregisterSink?.()\n unregisterSink = null\n\n log?.info?.(\n `deactivated (suppressed ${suppressedCount} stdout, redirected ${redirectedCount} stderr)`,\n )\n\n // Flush buffered stderr through the original. Headed with a separator\n // so the operator can distinguish replayed log output from anything\n // that ran AFTER the alt-screen restored. Suppressed when there's\n // nothing to replay (no header on a clean run).\n if (origStderrWrite && stderrBuffer.length > 0) {\n origStderrWrite(\n `\\n— silvery: replaying ${stderrBuffer.length} captured stderr/console line(s) —\\n`,\n )\n for (const line of stderrBuffer) {\n origStderrWrite(line)\n }\n }\n stderrBuffer = []\n\n if (stderrFd !== null) {\n try {\n closeSync(stderrFd)\n } catch {\n // Already closed\n }\n stderrFd = null\n }\n\n savedStdoutWrite = null\n savedStderrWrite = null\n origStdoutWrite = null\n origStderrWrite = null\n }\n\n function dispose(): void {\n if (disposed) return\n disposed = true\n deactivate()\n if (ownsRouter) _router.dispose()\n }\n\n return {\n write(data) {\n if (_active() && origStdoutWrite) {\n silveryWriting = true\n try {\n return origStdoutWrite(data)\n } finally {\n silveryWriting = false\n }\n }\n // Not active — forward straight to the current stdout.write (whatever it\n // is now). Caller is responsible for any additional routing.\n return process.stdout.write(data as string | Uint8Array)\n },\n active: _active as ReadSignal<boolean>,\n activate,\n deactivate,\n get suppressedCount() {\n return suppressedCount\n },\n get redirectedCount() {\n return redirectedCount\n },\n dispose,\n [Symbol.dispose]: dispose,\n }\n}\n","/**\n * term.size — single source of truth for terminal dimensions, exposed as\n * reactive ReadSignals.\n *\n * Wraps the WriteStream's columns/rows behind an alien-signals signal so every\n * reader sees the same value and can participate in the reactive graph\n * (computed / effect). The signals are read-only from the outside — writes\n * happen internally on `resize` events (after coalescing).\n *\n * Resize coalescing: multiplexers (tmux, cmux, Ghostty tabs) can fire\n * multiple SIGWINCH bursts in rapid succession as the PTY re-syncs. Without\n * coalescing, each event triggers a full re-layout at an intermediate size,\n * producing visible multi-phase layout shift. The owner uses a **trailing-edge\n * debounce**: every incoming SIGWINCH resets a 200 ms timer, and the snapshot\n * publishes only after `coalesceMs` of silence. A four-event burst at ~80 ms\n * intervals (the observed cmux pattern) collapses to one published snapshot\n * carrying the final geometry. Same-size resize notifications still publish a\n * fresh snapshot: cmux/focus restores can corrupt terminal screen state\n * without changing cols/rows, and fullscreen runtimes need the event so they\n * can repaint/clear. Discrete resizes spaced further apart than `coalesceMs`\n * pass through individually. The cost is a small per-resize latency — fine for\n * \"settle after the workspace switch\" but perceptible for a continuous\n * drag-resize; consumers that want zero-latency live preview pass\n * `coalesceMs: 0`.\n *\n * ## API shape\n *\n * ```ts\n * size.cols() // current width\n * size.rows() // current height\n * size.snapshot() // { cols, rows }\n * effect(() => { … use size.cols() … }) // subscribe reactively\n * ```\n *\n * Ownership: one `Size` per terminal session. Constructed by `createTerm()`\n * (Node terminal) or the emulator path (termless). Readers never touch\n * `stdout.columns` / `stdout.rows` directly.\n */\n\nimport { signal, type ReadSignal } from \"@silvery/signals\"\nimport { appendFileSync } from \"node:fs\"\n\n// Optional instrumentation gated on DEBUG_LOG env var. Pattern mirrors\n// scheduler.ts:768 (existing silvery convention). Logs install + every\n// resize event + every publish so SIGWINCH plumbing is observable in\n// the live binary. Zero overhead when DEBUG_LOG is unset.\nfunction logSize(message: string): void {\n const path = process.env.DEBUG_LOG\n if (!path) return\n if (process.env.DEBUG && !/silvery(:size|:\\*|^)/.test(process.env.DEBUG)) return\n try {\n appendFileSync(path, `[silvery:size ${new Date().toISOString()}] ${message}\\n`)\n } catch {\n // Ignore — instrumentation must never crash the runtime.\n }\n}\n\n/** Snapshot of terminal dimensions. */\nexport interface SizeSnapshot {\n readonly cols: number\n readonly rows: number\n}\n\n// ---------------------------------------------------------------------------\n// Stream-shared listener (refcounted)\n//\n// Multiple `createSize(stream)` calls on the SAME WriteStream share ONE\n// underlying `stream.on(\"resize\", …)` attachment. Without sharing, every\n// `createTerm(process.stdout)` call site (km-cli has many module-level\n// singletons — `program.ts`, `index.ts`, every command module) would add\n// its own resize listener; Node's default 10-listener cap fires\n// `MaxListenersExceededWarning: 11 resize listeners added to [WriteStream]`\n// at startup. See bead `@km/silvery/resize-listener-leak` (P2).\n//\n// The shared registration installs lazily on first read of any public\n// signal (preserves the lazy-install contract — cold Terms that never read\n// `size.*` still pay zero listeners — see size.test.ts §\"lazy install\").\n// Each `createSize(stream)` instance gets its own private fanout slot, so\n// per-instance state (the trailing-edge debounce timer, the alien-signals\n// `_snapshot`, dispose semantics) is unchanged. The refcount tracks the\n// number of installed instances; when the last instance disposes, the\n// underlying `stream.on(\"resize\")` attachment is removed.\n//\n// Keyed off the stream identity, not just `process.stdout`, so test mocks\n// (each test gets its own `EventEmitter` \"stdout\") share within a test but\n// stay isolated across tests. WeakMap keeps GC-friendliness for synthetic\n// streams.\n// ---------------------------------------------------------------------------\n\ninterface SharedResizeEntry {\n /** Number of `createSize(stream)` instances currently subscribed. */\n count: number\n /** Per-instance fan-out — invoked on every stream \"resize\" event. */\n readonly fanout: Set<() => void>\n /** The single function attached to `stream.on(\"resize\", …)`. */\n readonly handler: () => void\n}\n\nconst sharedResizeRegistry: WeakMap<NodeJS.WriteStream, SharedResizeEntry> = new WeakMap()\n\n/**\n * Subscribe `onResize` to the stream's resize event via the shared\n * (refcounted) listener. Returns a `detach()` that removes this subscriber\n * and tears down the underlying `stream.on(...)` when the last subscriber\n * detaches.\n *\n * Calling this is what bumps the refcount — `createSize` defers the call\n * until first read so the lazy-install contract is preserved.\n */\nfunction subscribeShared(stream: NodeJS.WriteStream, onResize: () => void): () => void {\n let entry = sharedResizeRegistry.get(stream)\n if (!entry) {\n const fanout = new Set<() => void>()\n const handler = () => {\n // Iterate a snapshot — a subscriber's onResize may detach mid-fire\n // (e.g. a Size disposes inside its own resize handler).\n for (const cb of [...fanout]) {\n try {\n cb()\n } catch {\n // Per-instance handlers must not crash the shared dispatch loop.\n // Errors surface through the instance's own pathway (signal\n // updates are catch-free at the consumer level).\n }\n }\n }\n stream.on(\"resize\", handler)\n entry = { count: 0, fanout, handler }\n sharedResizeRegistry.set(stream, entry)\n }\n entry.fanout.add(onResize)\n entry.count += 1\n\n let detached = false\n return () => {\n if (detached) return\n detached = true\n const e = sharedResizeRegistry.get(stream)\n if (!e) return\n e.fanout.delete(onResize)\n e.count -= 1\n if (e.count === 0) {\n stream.off(\"resize\", e.handler)\n sharedResizeRegistry.delete(stream)\n }\n }\n}\n\n/**\n * Test/diagnostic helper — returns the current refcount for a stream's\n * shared resize listener. Zero if no Size is currently subscribed (whether\n * because none was constructed, or because every Size has disposed). Used\n * by the SILVERY_STRICT canary that bounds `process.stdout` resize listener\n * count after app teardown.\n *\n * @internal\n */\nexport function _sharedResizeRefcount(stream: NodeJS.WriteStream): number {\n return sharedResizeRegistry.get(stream)?.count ?? 0\n}\n\n/**\n * Terminal size sub-owner.\n *\n * `cols`, `rows`, and `snapshot` are alien-signals `ReadSignal`s — call them\n * as functions to read the current value and, inside an `effect`, to\n * subscribe to changes. The first read (inside or outside an effect)\n * installs the lazy `stdout.on(\"resize\")` listener.\n *\n * ```ts\n * // Read once\n * const { cols, rows } = term.size.snapshot()\n *\n * // Subscribe to changes\n * effect(() => {\n * layout(term.size.cols(), term.size.rows()) // re-runs on every resize\n * })\n *\n * // React\n * const cols = useSignal(term.size.cols)\n * ```\n */\nexport interface Size extends Disposable {\n /** Current terminal width in columns. */\n readonly cols: ReadSignal<number>\n\n /** Current terminal height in rows. */\n readonly rows: ReadSignal<number>\n\n /** Current dimensions as a plain snapshot. */\n readonly snapshot: ReadSignal<SizeSnapshot>\n}\n\n/**\n * Trailing-edge debounce window. Long enough to absorb a multiplexer SIGWINCH\n * burst (cmux fires 4–6 events at ~80 ms intervals across ~300 ms during a\n * workspace switch); short enough to feel like settle, not lag, for a discrete\n * resize. Tests can shorten this via `coalesceMs`; consumers that want every\n * resize event published synchronously pass `coalesceMs: 0`.\n */\nconst RESIZE_COALESCE_MS = 200\nconst DEFAULT_COLS = 80\nconst DEFAULT_ROWS = 24\n\n/**\n * Options for `createSize`.\n */\nexport interface CreateSizeOptions {\n /** Override initial cols (default: `stdout.columns || 80`). */\n cols?: number\n /** Override initial rows (default: `stdout.rows || 24`). */\n rows?: number\n /**\n * Trailing-edge debounce window in ms. Defaults to 200. Every incoming\n * `resize` event resets the timer; the snapshot publishes only after\n * `coalesceMs` of silence. Set to 0 to disable coalescing entirely (each\n * `resize` publishes synchronously) — useful in test scenarios where the\n * harness already controls timing.\n */\n coalesceMs?: number\n}\n\nfunction validDimension(value: number | undefined, fallback: number): number {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0 ? value : fallback\n}\n\nfunction isValidDimension(value: number | undefined): boolean {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0\n}\n\n/**\n * Create a `Size` owner from a WriteStream. Subscribes to the stream's\n * `resize` event, coalesces bursts, and publishes the final geometry to\n * subscribers via alien-signals.\n *\n * Idempotent dispose: removes the resize listener and clears any pending\n * coalesce timer. Subsequent reads still work — the last known cols/rows\n * remain in the signal.\n */\nexport function createSize(stdout: NodeJS.WriteStream, options: CreateSizeOptions = {}): Size {\n const coalesceMs = options.coalesceMs ?? RESIZE_COALESCE_MS\n // When callers override dims explicitly (tests, headless-emulator setup),\n // their values are authoritative — we don't sync from stdout on install.\n // When neither is overridden we treat stdout as truth and re-read on first\n // access so any resize between construction and first read isn't lost.\n const dimsOverridden = options.cols !== undefined || options.rows !== undefined\n const initialCols =\n options.cols !== undefined ? options.cols : validDimension(stdout.columns, DEFAULT_COLS)\n const initialRows =\n options.rows !== undefined ? options.rows : validDimension(stdout.rows, DEFAULT_ROWS)\n\n // Writable signal owned privately — readers see it only through the\n // ReadSignal-shaped exports below. Consumers subscribe via\n // `effect(() => size.cols())` (or React's `useSignal(size.cols)`).\n // Seed snapshot is frozen so external casts (`as any`) can't corrupt owner\n // state silently. Every subsequent publish freezes a fresh object.\n const _snapshot = signal<SizeSnapshot>(Object.freeze({ cols: initialCols, rows: initialRows }))\n\n let disposed = false\n let coalesceTimer: ReturnType<typeof setTimeout> | null = null\n let installed = false\n let detachShared: (() => void) | null = null\n\n /**\n * Publish a new snapshot when dims changed, or when a valid same-size\n * `resize` event needs to wake resize subscribers.\n */\n const publish = (cols: number, rows: number, opts: { force?: boolean } = {}) => {\n const prev = _snapshot()\n if (!opts.force && prev.cols === cols && prev.rows === rows) {\n logSize(`publish skipped — no change (cols=${cols} rows=${rows})`)\n return\n }\n const suffix = prev.cols === cols && prev.rows === rows ? \" (same-size resize)\" : \"\"\n logSize(`publish: ${prev.cols}x${prev.rows} → ${cols}x${rows}${suffix}`)\n _snapshot(Object.freeze({ cols, rows }))\n }\n\n const flush = () => {\n coalesceTimer = null\n if (disposed) return\n const prev = _snapshot()\n const rawCols = stdout.columns\n const rawRows = stdout.rows\n const liveCols = validDimension(rawCols, prev.cols)\n const liveRows = validDimension(rawRows, prev.rows)\n const degenerate = !(isValidDimension(rawCols) && isValidDimension(rawRows))\n logSize(\n `flush: stdout=${liveCols}x${liveRows} prev=${prev.cols}x${prev.rows} degenerate=${degenerate}`,\n )\n // Always force the heal republish on a coalesced flush. A resize\n // notification — the only thing that schedules this flush — means the\n // terminal may have been corrupted: a real resize, a same-size cmux/focus\n // restore (the documented heal at the top of this file), OR a degenerate\n // (0×0) hidden-pane frame delivered during a workspace switch. In every\n // case the fullscreen runtime is owed a repaint over whatever the\n // multiplexer left on screen. Degenerate reads are already floored to\n // last-good dims by `validDimension` above, so the renderer never sees 0×0;\n // forcing the publish only restores the heal that the prior `force:\n // validResize` SWALLOWED whenever the flush-time read was degenerate — the\n // swallowed heal is the @km/code/v0.2/19604-focus-blank zero-cell blank\n // (live cmux read-screen nonspace=0 on an otherwise-live pane). The\n // trailing-edge debounce still collapses the burst to one publish, so this\n // adds no repaints beyond the one same-size heal a valid restore already\n // delivers.\n publish(liveCols, liveRows, { force: true })\n }\n\n const onResize = () => {\n logSize(\n `onResize fired: stdout=${stdout.columns}x${stdout.rows} disposed=${disposed} coalesceMs=${coalesceMs}`,\n )\n if (disposed) return\n if (coalesceMs === 0) {\n flush()\n return\n }\n // Trailing-edge debounce: every event resets the pending timer so a burst\n // of SIGWINCHs settles to a single publish carrying the *final* geometry.\n // Replaces the prior first-edge-then-flush design which let bursts wider\n // than the coalesce window leak every intermediate value (cmux's ~80 ms\n // inter-event spacing escaped the 16 ms window and produced 4–6 publishes\n // per workspace switch).\n if (coalesceTimer !== null) clearTimeout(coalesceTimer)\n coalesceTimer = setTimeout(flush, coalesceMs)\n }\n\n // Lazy install — the resize listener is attached on first read of any\n // public ReadSignal. Consumers that never read (e.g. style-only\n // createTerm() usages from chalk-compat call sites in km-tui/text/*)\n // pay zero listeners. Prevents the MaxListenersExceededWarning that\n // surfaced when every createTerm() eagerly wired one.\n //\n // Install-time resync: if the terminal resized between construction and\n // this first read (no listener attached yet → missed event), re-read the\n // live stdout dims and publish if they differ from the seed. Fixes the\n // \"first read returns stale value forever\" bug flagged by Pro review.\n //\n // Skipped when the caller explicitly overrode dims — those options are\n // authoritative (simulated sizes for tests, emulator setup).\n const ensureInstalled = () => {\n if (installed || disposed) return\n installed = true\n logSize(\n `ensureInstalled: attaching stdout.on(\"resize\") listener (shared) — ` +\n `dimsOverridden=${dimsOverridden} initial=${initialCols}x${initialRows} ` +\n `live=${stdout.columns}x${stdout.rows}`,\n )\n // Shared, refcounted attachment — multiple Sizes on the same stream\n // share ONE underlying `stream.on(\"resize\")` listener. See\n // `subscribeShared` block at top of this file.\n detachShared = subscribeShared(stdout, onResize)\n if (!dimsOverridden) {\n const prev = _snapshot()\n publish(validDimension(stdout.columns, prev.cols), validDimension(stdout.rows, prev.rows))\n }\n }\n\n // Plain arrow functions — the derivation (`.cols` / `.rows` field access)\n // is O(1) and `computed()`'s memoization doesn't earn its keep here.\n const cols: ReadSignal<number> = () => {\n ensureInstalled()\n return _snapshot().cols\n }\n const rows: ReadSignal<number> = () => {\n ensureInstalled()\n return _snapshot().rows\n }\n const snapshot: ReadSignal<SizeSnapshot> = () => {\n ensureInstalled()\n return _snapshot()\n }\n\n return {\n cols,\n rows,\n snapshot,\n [Symbol.dispose]() {\n if (disposed) return\n disposed = true\n if (installed) {\n // Detach our subscriber from the shared registration. The\n // underlying `stream.on(\"resize\")` is removed only when the last\n // Size detaches; refcount drops to 0 → handler off.\n detachShared?.()\n detachShared = null\n installed = false\n }\n if (coalesceTimer !== null) {\n clearTimeout(coalesceTimer)\n coalesceTimer = null\n }\n },\n }\n}\n\n/**\n * Create a fixed-dimensions `Size` for headless / emulator terminals.\n *\n * No stream is observed. `update(cols, rows)` is the only way dimensions\n * change; the emulator-backed term calls it on explicit resize.\n */\nexport function createFixedSize(initial: SizeSnapshot): Size & {\n update(cols: number, rows: number): void\n} {\n const _snapshot = signal<SizeSnapshot>(Object.freeze({ cols: initial.cols, rows: initial.rows }))\n let disposed = false\n\n const cols: ReadSignal<number> = () => _snapshot().cols\n const rows: ReadSignal<number> = () => _snapshot().rows\n const snapshot: ReadSignal<SizeSnapshot> = () => _snapshot()\n\n return {\n cols,\n rows,\n snapshot,\n update(nextCols: number, nextRows: number) {\n if (disposed) return\n // Floor degenerate (<=0 / non-finite) dimensions to the last known good\n // value, matching createSize's validDimension contract. A 0×0 (or\n // negative) frame — e.g. a hidden pane's GUI frame delivered by a tiling\n // multiplexer, or a test-harness resize — must never reach the renderer\n // as a real layout dimension: the degenerate frame corrupts the\n // renderer's dimension bookkeeping (_lastLayoutDims) and consumes the\n // clearNextFullscreenRender flag, leaving the pane blank after the next\n // real-size resize. createSize never publishes <=0; createFixedSize must\n // not either. @km/code/v0.2/19604-focus-blank.\n const prev = _snapshot()\n _snapshot(\n Object.freeze({\n cols: validDimension(nextCols, prev.cols),\n rows: validDimension(nextRows, prev.rows),\n }),\n )\n },\n [Symbol.dispose]() {\n if (disposed) return\n disposed = true\n },\n }\n}\n","/**\n * term.modes — single owner for terminal protocol modes, exposed as signals.\n *\n * Consolidates the previously-scattered enable/disable calls for:\n * - raw mode (stdin termios)\n * - alternate screen buffer (DEC private mode 1049)\n * - bracketed paste (DEC private mode 2004)\n * - Kitty keyboard protocol (CSI > flags u / CSI < u)\n * - mouse tracking (modes 1003 + 1006, optional 1016 SGR-Pixels)\n * - focus reporting (DEC private mode 1004)\n *\n * ## Why\n *\n * Terminal protocol modes are *shared global state*. The historical pattern —\n * every subsystem (probe, runtime, provider) calls `enableX()` / `disableX()`\n * independently — produces the same race class that killed raw mode in the\n * 2026-04-22 `wasRaw` incident: multi-tenant toggling of global termios/\n * terminal state across async boundaries.\n *\n * ## API shape: signals\n *\n * Each mode is a callable alien-signals `Signal<T>`:\n *\n * - `modes.altScreen()` — read current value\n * - `modes.altScreen(true)` — write; internal effect emits ANSI on change\n * - `effect(() => modes.altScreen())` — subscribe to changes\n *\n * Idempotence is automatic: alien-signals doesn't fire dependents when the\n * new value equals the prior value, so `modes.altScreen(true)` twice produces\n * one ANSI write.\n *\n * ## Ownership contract\n *\n * One `Modes` instance per Term. Construction is cheap — no ANSI is emitted\n * until the first write to a signal. Callers write modes ONCE at session\n * start and restore them ONCE on dispose. Mid-session re-toggling is\n * permitted (e.g. suspend/resume flows need to drop protocols before SIGTSTP),\n * but MUST go through the owner so state stays consistent.\n *\n * Standalone CLI flows (prompts, pickers) outside a Term session construct\n * their own short-lived `createModes()`. Raw mode is arbitrated across ALL\n * owners on one stdin via a per-stream reference count (see below), so a\n * transient owner's dispose never cooks a stream another owner still holds.\n *\n * Signals reflect the last value *this owner* wrote. They are the app's\n * source of truth — the terminal has no query-for-current-mode protocol for\n * most of these.\n *\n * ## Dispose\n *\n * Restores every mode this owner activated (ignores modes the owner never\n * touched — setting `rawMode=false` when we never enabled raw would be wrong\n * on a shared stdin). Dispose writes `false` to each ever-activated signal;\n * the internal effects emit the disable ANSI naturally. Idempotent.\n *\n * Bead: km-silvery.term-sub-owners (Phase 4) + km-silvery.modes-as-signals.\n */\n\nimport { signal, effect, type Signal } from \"@silvery/signals\"\n\nimport {\n enableMouse,\n disableMouse,\n enableKittyKeyboard,\n disableKittyKeyboard,\n enableBracketedPaste,\n disableBracketedPaste,\n} from \"@silvery/ansi\"\n\nconst CSI = \"\\x1b[\"\n\n/** DEC private mode 1004: focus-in / focus-out reporting. */\nconst ENABLE_FOCUS_REPORTING = `${CSI}?1004h`\nconst DISABLE_FOCUS_REPORTING = `${CSI}?1004l`\n\n/** DEC private mode 1049: alternate screen buffer (save + switch). */\nconst ENTER_ALT_SCREEN = `${CSI}?1049h`\nconst LEAVE_ALT_SCREEN = `${CSI}?1049l`\n\n/** DEC private mode 1007: alternate-scroll (wheel → cursor keys on alt screen). */\nconst DISABLE_ALTERNATE_SCROLL = `${CSI}?1007l`\n\n/**\n * Kitty keyboard protocol flags (bitfield).\n *\n * | Flag | Bit | Description |\n * | ---- | --- | ----------------------------------------- |\n * | 1 | 0 | Disambiguate escape codes |\n * | 2 | 1 | Report event types (press/repeat/release) |\n * | 4 | 2 | Report alternate keys |\n * | 8 | 3 | Report all keys as escape codes |\n * | 16 | 4 | Report associated text |\n */\nexport const KittyFlags = {\n DISAMBIGUATE: 1,\n REPORT_EVENTS: 2,\n REPORT_ALTERNATE: 4,\n REPORT_ALL_KEYS: 8,\n REPORT_TEXT: 16,\n} as const\n\n/**\n * Mode names that can be passed to `modes.enable(...)`. These cover every\n * toggleable mode on the `Modes` owner. `kittyKeyboard` accepts a bitfield\n * rather than `true` and is handled via the per-mode signal directly (see\n * the `kittyKeyboard` signal below) — it's intentionally excluded from\n * `enable()` because the \"on\" value is not a single fixed boolean.\n */\nexport type ModeName = \"rawMode\" | \"altScreen\" | \"bracketedPaste\" | \"mouse\" | \"focusReporting\"\n\nexport type MouseTrackingMode = boolean | \"pixel\"\n\n/**\n * Terminal protocol modes sub-owner.\n *\n * Each property is a callable alien-signals `Signal`:\n * - read: `modes.altScreen()` → `boolean`\n * - write: `modes.altScreen(true)` — internal effect emits ANSI on change\n * - subscribe: `effect(() => modes.altScreen())`\n *\n * `dispose()` writes `false` to every signal that was ever activated, which\n * drives the same effects to emit the disable ANSI. Mode signals that were\n * never touched stay `false` — no ANSI is emitted for them, matching the\n * shared-stdin safety contract.\n *\n * For scope-style ownership (`scope.use(modes.enable(\"altScreen\"))`), use\n * `enable()` — it returns a `Disposable` that restores the mode to whatever\n * it was before the call, independent of the owner's full `dispose()`.\n */\nexport interface Modes extends Disposable {\n /**\n * stdin raw mode.\n *\n * wasRaw note: prefer a single `modes.rawMode(true)` at session start; do\n * not capture-and-restore around async work. See\n * `vendor/silvery/CLAUDE.md` \"Anti-pattern: wasRaw\".\n */\n readonly rawMode: Signal<boolean>\n\n /** Alternate screen buffer (DEC 1049). */\n readonly altScreen: Signal<boolean>\n\n /** Bracketed paste (DEC 2004). */\n readonly bracketedPaste: Signal<boolean>\n\n /**\n * Kitty keyboard protocol flags. Bitfield (see `KittyFlags`) to enable,\n * `false` to disable. A change from one non-false bitfield to another\n * emits a fresh `CSI > flags u` write.\n */\n readonly kittyKeyboard: Signal<number | false>\n\n /** SGR mouse tracking (xterm modes 1003 + 1006, or 1003 + 1006 + 1016). */\n readonly mouse: Signal<MouseTrackingMode>\n\n /** Focus-in / focus-out reporting (DEC 1004). */\n readonly focusReporting: Signal<boolean>\n\n /**\n * Disable DEC private mode 1007 (alternate-scroll) with `CSI ?1007l`.\n *\n * Alternate-scroll makes the terminal translate wheel events into cursor\n * (arrow) keys while the alternate screen is active and mouse tracking is\n * OFF. For a silvery app that deliberately turned mouse tracking off, those\n * synthesized arrows move the cursor/selection on every wheel tick — the\n * recurring \"wheel jumps the cursor\" bug. Disabling 1007 turns the wheel\n * into a benign no-op instead.\n *\n * One-way by design, unlike the callable-signal modes above: we emit\n * `?1007l` at most once and never re-enable on dispose. 1007's prior state\n * is unknowable (we never query it), so a blind `?1007h` on teardown could\n * wrongly enable a mode the user had off; and it is moot once we leave the\n * alt screen, which is where 1007 has any effect. Idempotent across calls.\n */\n disableAlternateScroll(): void\n\n /**\n * Enable a mode and return a `Disposable` that restores the mode to its\n * prior value on disposal. Complements the per-mode signals for callers\n * that want scope-style ownership:\n *\n * ```ts\n * scope.use(term.modes.enable(\"altScreen\"))\n * // …later, when the scope disposes, altScreen flips back to its\n * // pre-enable value.\n * ```\n *\n * Idempotent across repeated `enable(name)` calls (alien-signals equality\n * short-circuits same-value writes). Disposing the returned handle twice\n * is a no-op. The kitty keyboard bitfield is intentionally not covered —\n * write `modes.kittyKeyboard(flags)` directly for that shape.\n */\n enable(name: ModeName): Disposable\n}\n\n/**\n * Options for `createModes()`.\n *\n * The owner needs:\n * - a write function for ANSI sequences (routes through Output if activated,\n * else bare `stdout.write`)\n * - the stdin stream (for `rawMode` — termios toggle, not ANSI)\n */\nexport interface CreateModesOptions {\n /** Write raw ANSI bytes to stdout. */\n write: (data: string) => void\n /** stdin stream — used only for raw-mode termios toggles. */\n stdin: NodeJS.ReadStream\n}\n\n// ---------------------------------------------------------------------------\n// Cross-owner raw-mode arbitration\n//\n// Termios raw mode is process-global per stream, but more than one Modes\n// owner can legitimately coexist on one stdin — a long-lived session owner\n// plus a transient CLI prompt (withSelect / withTextInput), or nested\n// sessions in tests. A plain per-owner toggle lets the transient owner's\n// dispose cook the stream out from under the still-active owner — the\n// 2026-04-22 wasRaw race, one level up (owner-vs-owner instead of\n// tenant-vs-tenant). Arbitrate with a per-stream reference count: only the\n// 0→1 acquire and the 1→0 release touch termios.\n// ---------------------------------------------------------------------------\nconst rawModeHolds = new WeakMap<NodeJS.ReadStream, number>()\n\nfunction acquireRawMode(stdin: NodeJS.ReadStream): void {\n const holds = rawModeHolds.get(stdin) ?? 0\n rawModeHolds.set(stdin, holds + 1)\n if (holds === 0 && stdin.isTTY) {\n try {\n stdin.setRawMode(true)\n } catch {\n // stdin may be closed mid-call — ignore, hold count still tracked\n }\n }\n}\n\nfunction releaseRawMode(stdin: NodeJS.ReadStream): void {\n const holds = rawModeHolds.get(stdin) ?? 0\n // Unreachable from Modes instances (each releases at most the one hold it\n // acquired, guarded by its holdsRaw flag) — kept as a defensive floor so a\n // future bug cannot wedge the count negative and swallow later acquires.\n if (holds <= 0) return\n rawModeHolds.set(stdin, holds - 1)\n if (holds === 1 && stdin.isTTY) {\n try {\n stdin.setRawMode(false)\n } catch {\n // stdin may be closed mid-call — ignore, hold count still tracked\n }\n }\n}\n\n/**\n * Create a `Modes` sub-owner. Does not emit any ANSI at construction — all\n * sequences are written lazily on the first change to a mode signal.\n */\nexport function createModes(opts: CreateModesOptions): Modes {\n const { write, stdin } = opts\n\n const rawMode = signal<boolean>(false)\n const altScreen = signal<boolean>(false)\n const bracketedPaste = signal<boolean>(false)\n const kittyKeyboard = signal<number | false>(false)\n const mouse = signal<MouseTrackingMode>(false)\n const focusReporting = signal<boolean>(false)\n\n // Track which modes this owner ever activated. Dispose only restores those,\n // matching the pre-signals behaviour — we must not emit a disable sequence\n // for a mode a neighbouring owner intentionally set up.\n let touchedRawMode = false\n let touchedAltScreen = false\n let touchedBracketedPaste = false\n let touchedKittyKeyboard = false\n let touchedMouse = false\n let touchedFocusReporting = false\n // Alternate-scroll (1007) is one-way: we only ever disable it, and never\n // restore it on dispose (see `disableAlternateScroll` docs). This flag makes\n // the emission idempotent — a second call is a no-op.\n let touchedAlternateScroll = false\n\n let disposed = false\n let disposing = false\n\n // Each mode has a dedicated effect: when the signal changes, emit the\n // matching enable/disable ANSI. Same-value writes don't fire dependents\n // (alien-signals equality check), so idempotence is automatic.\n //\n // Each effect reads its signal once, which seeds the dependency. The first\n // firing is the \"seed\" read (value=false) — we skip emitting ANSI there so\n // construction stays free, matching the previous lazy-first-write contract.\n // This owner contributes at most ONE hold to the per-stream refcount,\n // tracked by holdsRaw — repeated `rawMode(true)` writes are same-value\n // no-ops at the signal layer anyway, and dispose/idempotence guarantees\n // the release runs at most once.\n let holdsRaw = false\n let rawSeeded = false\n const stopRawEffect = effect(() => {\n const on = rawMode()\n if (!rawSeeded) {\n rawSeeded = true\n return\n }\n if (disposed && !disposing) return\n if (on) touchedRawMode = true\n // Route through the cross-owner refcount — never toggle termios\n // directly. Overlapping owners on one stdin compose: only the first\n // acquire and the last release reach `stdin.setRawMode`.\n if (on && !holdsRaw) {\n holdsRaw = true\n acquireRawMode(stdin)\n } else if (!on && holdsRaw) {\n holdsRaw = false\n releaseRawMode(stdin)\n }\n })\n\n let altSeeded = false\n const stopAltEffect = effect(() => {\n const on = altScreen()\n if (!altSeeded) {\n altSeeded = true\n return\n }\n if (disposed && !disposing) return\n if (on) touchedAltScreen = true\n try {\n write(on ? ENTER_ALT_SCREEN : LEAVE_ALT_SCREEN)\n } catch {\n // Terminal may already be gone (SSH disconnect, etc.)\n }\n })\n\n let pasteSeeded = false\n const stopPasteEffect = effect(() => {\n const on = bracketedPaste()\n if (!pasteSeeded) {\n pasteSeeded = true\n return\n }\n if (disposed && !disposing) return\n if (on) touchedBracketedPaste = true\n try {\n write(on ? enableBracketedPaste() : disableBracketedPaste())\n } catch {\n // Terminal may already be gone\n }\n })\n\n let kittySeeded = false\n const stopKittyEffect = effect(() => {\n const flags = kittyKeyboard()\n if (!kittySeeded) {\n kittySeeded = true\n return\n }\n if (disposed && !disposing) return\n if (flags !== false) touchedKittyKeyboard = true\n try {\n write(flags === false ? disableKittyKeyboard() : enableKittyKeyboard(flags))\n } catch {\n // Terminal may already be gone\n }\n })\n\n let mouseSeeded = false\n const stopMouseEffect = effect(() => {\n const on = mouse()\n if (!mouseSeeded) {\n mouseSeeded = true\n return\n }\n if (disposed && !disposing) return\n if (on) touchedMouse = true\n try {\n write(on ? enableMouse({ pixels: on === \"pixel\" }) : disableMouse())\n } catch {\n // Terminal may already be gone\n }\n })\n\n let focusSeeded = false\n const stopFocusEffect = effect(() => {\n const on = focusReporting()\n if (!focusSeeded) {\n focusSeeded = true\n return\n }\n if (disposed && !disposing) return\n if (on) touchedFocusReporting = true\n try {\n write(on ? ENABLE_FOCUS_REPORTING : DISABLE_FOCUS_REPORTING)\n } catch {\n // Terminal may already be gone\n }\n })\n\n const dispose = () => {\n if (disposed) return\n disposed = true\n disposing = true\n\n // Restore ONLY what this owner activated. Order matters: drop protocols\n // first (so the terminal stops sending their events), then leave the alt\n // screen, then drop raw. Mirrors the order of `restoreTerminalState()`\n // in terminal-lifecycle.ts.\n //\n // We flip each signal back to its inactive value; the per-mode effect\n // emits the disable ANSI as a side-effect. Effects that were never\n // activated stay at `false` (no value change → no emission).\n if (touchedFocusReporting) focusReporting(false)\n if (touchedMouse) mouse(false)\n if (touchedKittyKeyboard) kittyKeyboard(false)\n if (touchedBracketedPaste) bracketedPaste(false)\n if (touchedAltScreen) altScreen(false)\n if (touchedRawMode) rawMode(false)\n\n disposing = false\n\n // Tear down the effects now that the restore writes have fired. Any\n // further writes to the signals after this point update the values but\n // do not emit ANSI (effects are stopped, and the `disposed && !disposing`\n // guard blocks the edge case where tearDown order races).\n stopRawEffect()\n stopAltEffect()\n stopPasteEffect()\n stopKittyEffect()\n stopMouseEffect()\n stopFocusEffect()\n }\n\n function disableAlternateScroll(): void {\n if (touchedAlternateScroll) return\n touchedAlternateScroll = true\n // Note: no `touched`-based restore. Unlike the callable-signal modes, we\n // do NOT flip this back on dispose — 1007's prior state is unknowable and\n // it is moot once the alt screen is gone. Disable-and-leave, by design.\n if (disposed && !disposing) return\n try {\n write(DISABLE_ALTERNATE_SCROLL)\n } catch {\n // Terminal may already be gone (SSH disconnect, etc.)\n }\n }\n\n function enable(name: ModeName): Disposable {\n const sig =\n name === \"rawMode\"\n ? rawMode\n : name === \"altScreen\"\n ? altScreen\n : name === \"bracketedPaste\"\n ? bracketedPaste\n : name === \"mouse\"\n ? mouse\n : focusReporting\n const prior = sig()\n sig(true)\n let disposed = false\n return {\n [Symbol.dispose]() {\n if (disposed) return\n disposed = true\n sig(prior)\n },\n }\n }\n\n return {\n rawMode,\n altScreen,\n bracketedPaste,\n kittyKeyboard,\n mouse,\n focusReporting,\n disableAlternateScroll,\n enable,\n [Symbol.dispose]: dispose,\n }\n}\n","/**\n * term.signals — single SignalScope per Term lifetime with topologically-ordered\n * teardown.\n *\n * ## Why\n *\n * The 2026-04-22 shared-global audit found 78 `process.on(\"SIGINT\" | \"SIGTERM\" |\n * \"SIGTSTP\" | \"SIGWINCH\" | \"exit\" | \"beforeExit\" | …)` registrations across km\n * + silvery with no documented cleanup order. Handlers fire in registration\n * order. If an earlier handler crashes, the Node.js default behaviour may skip\n * later handlers or the process may exit before their cleanup runs — the same\n * class of resource-leak race that the `wasRaw` finding exposed for raw mode.\n *\n * Signals is the same META-fix as Output / Modes / Input: one owner per Term\n * mediates every registration for a given signal. On dispose (or on signal\n * delivery) handlers fire in priority / dependency order, each wrapped in\n * try/catch so a single failing handler doesn't block the rest.\n *\n * ## API shape\n *\n * const unregister = term.signals.on(\"SIGINT\", () => closeDb(), {\n * priority: 10, // lower = runs first\n * before: [\"flush-logs\"], // or explicit dep graph\n * after: [\"save-state\"],\n * name: \"close-db\", // optional handle for before/after\n * })\n * term.signals.dispose() // cascades from term.dispose()\n *\n * `priority` is a simple sort key (number, default 0). `before` / `after`\n * reference handler `name`s. `dispose()` runs every registered handler in\n * topological order, catches errors, and is idempotent.\n *\n * The return value of `on()` is both callable (`unregister()`) and\n * `Disposable` (`using sub = term.signals.on(...)` and\n * `scope.use(term.signals.on(...))`). All three forms unregister the handler\n * without firing it.\n *\n * ## Not covered by this owner\n *\n * - `apps/km-tui/src/state/raw-signals.ts::restoreTerminal` stays as the\n * emergency last-ditch crash handler (runs on `uncaughtException`). It\n * must run even if the Term (and its Signals) has already been disposed.\n *\n * Bead: km-silvery.term-sub-owners (Phase 6).\n */\n\n/** Process signal / lifecycle event the owner understands. */\nexport type SignalName =\n | NodeJS.Signals\n | \"exit\"\n | \"beforeExit\"\n | \"uncaughtException\"\n | \"unhandledRejection\"\n\n/** Options per registration. */\nexport interface SignalOnOptions {\n /**\n * Sort key — lower runs first on dispose / signal delivery. Default 0.\n *\n * Rule of thumb:\n * - 0–9: app-level cleanup (close DB, cancel pending work)\n * - 10–19: runtime cleanup (stop schedulers, drain queues)\n * - 20–29: terminal cleanup (restore modes, leave alt screen)\n * - 30+: emergency / last-ditch\n *\n * `before` / `after` take precedence — priority is only a tiebreaker.\n */\n priority?: number\n\n /**\n * Handler name — required if you want `before`/`after` to reference this\n * handler. If omitted, a unique id is generated.\n */\n name?: string\n\n /** Handler names that must run AFTER this one. */\n before?: string[]\n\n /** Handler names that must run BEFORE this one. */\n after?: string[]\n\n /**\n * If `true`, the handler also runs on `dispose()` (before any process-level\n * signal handler is detached). Default: true — dispose is the primary\n * teardown path.\n */\n onDispose?: boolean\n\n /**\n * If `true`, the handler runs when the named signal is delivered to the\n * process (via `process.on(signal, …)`). Default: true.\n */\n onSignal?: boolean\n}\n\n/**\n * Unregister function returned by `signals.on(...)`. Callable as a plain\n * function (backward-compat: `unregister()`) and also implements both\n * `Symbol.dispose` and `Symbol.asyncDispose` so it works with sync `using`,\n * async `await using`, and `Scope` (which is `AsyncDisposableStack`-based\n * and only accepts values with `Symbol.asyncDispose`). All three forms\n * remove the handler from the owner's registry without firing it.\n */\nexport type SignalUnregister = (() => void) & Disposable & AsyncDisposable\n\n/**\n * Signals sub-owner.\n *\n * One per Term. Mediates every `process.on(signalName, …)` registration for\n * the Term's lifetime, running handlers in priority/dependency order on\n * signal delivery or on `dispose()`.\n */\nexport interface Signals extends Disposable {\n /**\n * Register a handler for a process signal or lifecycle event. Returns a\n * `SignalUnregister` — a callable `Disposable`. Either `unregister()` or\n * `unregister[Symbol.dispose]()` (implicitly via `using` / `scope.use`)\n * removes the handler from this owner's registry (does not fire the handler).\n *\n * The first registration for a given signal installs a shared\n * `process.on(signal, …)` listener; subsequent registrations reuse it.\n * `dispose()` removes the shared listener.\n */\n on(\n signal: SignalName,\n handler: () => void | Promise<void>,\n opts?: SignalOnOptions,\n ): SignalUnregister\n\n /**\n * Synchronous teardown. Runs every registered handler (with `onDispose:\n * true`, the default) in priority/dependency order, catching errors so one\n * failing handler can't block the rest. Idempotent.\n *\n * Handlers that return Promises are awaited best-effort via a microtask —\n * but `dispose()` itself is synchronous because it runs under `using` /\n * Symbol.dispose semantics, and on signal delivery the process may exit\n * before any async work resolves.\n */\n dispose(): void\n\n /** True after `dispose()` / `Symbol.dispose` has run. */\n readonly isDisposed: boolean\n\n /** Number of live registrations across all signals. */\n readonly size: number\n}\n\ninterface Entry {\n id: string\n name: string\n signal: SignalName\n handler: () => void | Promise<void>\n priority: number\n before: string[]\n after: string[]\n onDispose: boolean\n onSignal: boolean\n}\n\n/**\n * Options for `createSignals`.\n */\nexport interface CreateSignalsOptions {\n /**\n * Override the process-level event source. Tests pass a fake `process`\n * to drive signal delivery without touching real signals.\n * Defaults to Node's `process` global.\n */\n process?: NodeJS.Process\n\n /**\n * Error hook — called for every handler that throws. Default: swallow.\n * Tests use this to assert isolation semantics.\n */\n onError?: (error: unknown, entry: { name: string; signal: SignalName }) => void\n}\n\n/**\n * Create a `Signals` sub-owner. Installs no process-level listeners until the\n * first `on()` call; removes them on `dispose()`.\n */\nexport function createSignals(opts: CreateSignalsOptions = {}): Signals {\n const proc = opts.process ?? process\n const onError = opts.onError\n\n let disposed = false\n let nextId = 0\n\n /** All registrations, regardless of signal. */\n const entries = new Map<string, Entry>()\n\n /** Reverse index: every live handler name → its entry id. Used to reject\n * duplicate names in `on()`, which would otherwise produce an ambiguous\n * topological order when two handlers both claim the same `name` as a\n * `before`/`after` key. */\n const byName = new Map<string, string>()\n\n /** Shared `process.on(signal, …)` listeners, one per signal. */\n const installed = new Map<SignalName, () => void>()\n\n function makeId(): string {\n return `signals-${++nextId}`\n }\n\n /**\n * Wrap an unregister function so it's both callable and `Disposable`. This\n * keeps every existing `const off = signals.on(...); off()` caller working\n * while also enabling `using sub = signals.on(...)` and\n * `scope.use(signals.on(...))`.\n */\n function makeUnregister(fn: () => void): SignalUnregister {\n // Attach both Symbol.dispose (sync `using`) and Symbol.asyncDispose\n // (`await using` / `Scope.use(...)` — `AsyncDisposableStack` only\n // accepts values with Symbol.asyncDispose). The unregister itself is\n // synchronous; the async variant just resolves immediately.\n return Object.assign(fn, {\n [Symbol.dispose]() {\n fn()\n },\n [Symbol.asyncDispose]() {\n fn()\n return Promise.resolve()\n },\n })\n }\n\n function entriesFor(signal: SignalName): Entry[] {\n const out: Entry[] = []\n for (const e of entries.values()) {\n if (e.signal === signal) out.push(e)\n }\n return out\n }\n\n /**\n * Topological sort of entries respecting `before`/`after` deps, with\n * `priority` as the tiebreaker. Returns entries in the order they should\n * run. Cycles fall back to priority order (cycle edges silently dropped).\n */\n function ordered(subset: Entry[]): Entry[] {\n // Map name → entry for dep resolution.\n const byName = new Map<string, Entry>()\n for (const e of subset) byName.set(e.name, e)\n\n // Adjacency: edge from A → B means \"A must run before B\".\n const outgoing = new Map<string, Set<string>>()\n const incoming = new Map<string, number>()\n for (const e of subset) {\n outgoing.set(e.name, new Set())\n incoming.set(e.name, 0)\n }\n for (const e of subset) {\n // `before: [\"X\"]` — this entry runs before X → edge this → X\n for (const target of e.before) {\n if (!byName.has(target)) continue\n const set = outgoing.get(e.name)!\n if (!set.has(target)) {\n set.add(target)\n incoming.set(target, (incoming.get(target) ?? 0) + 1)\n }\n }\n // `after: [\"Y\"]` — this entry runs after Y → edge Y → this\n for (const source of e.after) {\n if (!byName.has(source)) continue\n const set = outgoing.get(source)!\n if (!set.has(e.name)) {\n set.add(e.name)\n incoming.set(e.name, (incoming.get(e.name) ?? 0) + 1)\n }\n }\n }\n\n // Kahn's algorithm, tiebroken by (priority, insertion order via id).\n const byKey = (a: Entry, b: Entry) => {\n if (a.priority !== b.priority) return a.priority - b.priority\n return a.id < b.id ? -1 : a.id > b.id ? 1 : 0\n }\n const ready: Entry[] = []\n for (const e of subset) {\n if ((incoming.get(e.name) ?? 0) === 0) ready.push(e)\n }\n ready.sort(byKey)\n\n const result: Entry[] = []\n const visited = new Set<string>()\n while (ready.length > 0) {\n const next = ready.shift()!\n if (visited.has(next.name)) continue\n visited.add(next.name)\n result.push(next)\n for (const downstream of outgoing.get(next.name) ?? []) {\n const count = (incoming.get(downstream) ?? 0) - 1\n incoming.set(downstream, count)\n if (count === 0) {\n const entry = byName.get(downstream)\n if (entry && !visited.has(entry.name)) {\n ready.push(entry)\n ready.sort(byKey)\n }\n }\n }\n }\n\n // Cycle fallback — any entry not yet visited gets appended in priority\n // order. Silently tolerates cyclic deps (they're a bug, but we prefer a\n // best-effort teardown over throwing during a crash handler).\n const remaining = subset.filter((e) => !visited.has(e.name)).sort(byKey)\n result.push(...remaining)\n return result\n }\n\n function runHandlers(list: Entry[]): void {\n for (const entry of list) {\n try {\n const maybe = entry.handler()\n // Async handlers are awaited best-effort — we can't block a sync\n // dispose. Any rejection after this point is caught below; we don't\n // want to hang the crash path on a slow Promise.\n if (maybe && typeof (maybe as Promise<unknown>).then === \"function\") {\n ;(maybe as Promise<unknown>).catch((err) => {\n if (onError) onError(err, { name: entry.name, signal: entry.signal })\n })\n }\n } catch (err) {\n if (onError) onError(err, { name: entry.name, signal: entry.signal })\n }\n }\n }\n\n function installIfNeeded(signal: SignalName): void {\n if (installed.has(signal)) return\n const listener = () => {\n if (disposed) return\n const list = ordered(entriesFor(signal).filter((e) => e.onSignal))\n runHandlers(list)\n }\n proc.on(signal, listener)\n installed.set(signal, listener)\n }\n\n function uninstall(signal: SignalName): void {\n const listener = installed.get(signal)\n if (!listener) return\n try {\n proc.off(signal, listener)\n } catch {\n // process may be exiting\n }\n installed.delete(signal)\n }\n\n function on(\n signal: SignalName,\n handler: () => void | Promise<void>,\n options: SignalOnOptions = {},\n ): SignalUnregister {\n if (disposed) {\n // After dispose, registrations are a no-op — callers that keep a stale\n // owner reference shouldn't accidentally re-install listeners.\n return makeUnregister(() => {})\n }\n const id = makeId()\n const name = options.name ?? id\n if (byName.has(name)) {\n throw new Error(\n `term.signals.on: handler name ${JSON.stringify(name)} is already registered. ` +\n `Names must be unique across the Signals owner so that before/after refs ` +\n `resolve unambiguously. Unregister the previous handler first, or pick a ` +\n `different name.`,\n )\n }\n const entry: Entry = {\n id,\n name,\n signal,\n handler,\n priority: options.priority ?? 0,\n before: options.before ?? [],\n after: options.after ?? [],\n onDispose: options.onDispose !== false,\n onSignal: options.onSignal !== false,\n }\n entries.set(id, entry)\n byName.set(name, id)\n if (entry.onSignal) installIfNeeded(signal)\n\n return makeUnregister(() => {\n const existing = entries.get(id)\n if (!existing) return\n entries.delete(id)\n byName.delete(existing.name)\n // If this was the last registration for the signal AND no other entries\n // need it for onSignal, drop the shared listener. Rare path — usually\n // dispose() handles the whole batch.\n if (!entriesFor(signal).some((e) => e.onSignal)) {\n uninstall(signal)\n }\n })\n }\n\n function dispose(): void {\n if (disposed) return\n disposed = true\n\n // Run dispose handlers once, across ALL signals, in a single unified\n // topological order. Using one global pass means cross-signal deps\n // (e.g. \"restore terminal after close DB\") work naturally; callers\n // don't have to remember which signal their handler was registered on.\n const disposalSet = [...entries.values()].filter((e) => e.onDispose)\n const sorted = ordered(disposalSet)\n runHandlers(sorted)\n\n // Remove every process-level listener we installed.\n for (const signal of [...installed.keys()]) {\n uninstall(signal)\n }\n\n entries.clear()\n byName.clear()\n }\n\n return {\n on,\n dispose,\n get isDisposed() {\n return disposed\n },\n get size() {\n return entries.size\n },\n [Symbol.dispose]: dispose,\n }\n}\n","/**\n * Console — single-owner console.* interceptor for a silvery session.\n *\n * Captures `console.log/info/warn/error/debug` during alt-screen rendering so\n * stray log output doesn't corrupt the TUI display. Entries are buffered and\n * can be replayed to the normal streams on exit (so the operator sees what\n * would have been printed) or rendered live inside the app via the `<Console>`\n * component (which reads `subscribe` + `getSnapshot`).\n *\n * Mirrors the other sub-owners in shape: constructed cheaply at term creation,\n * does nothing until `capture()` is called, `Symbol.dispose` is idempotent.\n *\n * ## Lifecycle\n *\n * One Console per Term. Capture is opt-in (`term.console.capture({suppress:true})`)\n * because hoisting it unconditionally at term creation would silently swallow\n * any log output from the caller's own setup code. `restore()` undoes the\n * patch; `dispose()` restores + clears subscribers.\n *\n * ## Relation to Output\n *\n * `Output` patches `process.stdout.write` / `process.stderr.write` / `console.*`\n * during alt-screen to suppress foreign writes and redirect stderr to\n * `DEBUG_LOG`. `Console` patches `console.*` alone to *capture* entries for\n * display + replay. They are complementary:\n *\n * - Output's console patch is a sink (write to DEBUG_LOG or drop).\n * - Console's patch is a tap (record for later use AND optionally forward).\n *\n * **Call order: Output first, then Console.** Last patch wins, so whichever\n * owner wraps `console.log` last is the one user calls hit first. With Output\n * first and Console second, `console.log(x)` hits Console's tap, Console\n * records the entry, and (unless `suppress: true`) forwards to its captured\n * \"original\" — which is Output's redirect wrapper. Net effect: tap fires for\n * every call, and Output's DEBUG_LOG redirect still applies to non-silvery\n * writes. Reversing the order (Console first, Output second) is the 2026-04-22\n * pro-review bug: Output overwrites Console's wrapper and the tap never fires.\n *\n * `restore()` in the reverse order — Console first, then Output — to unwind\n * the layering symmetrically.\n */\n\nimport { signal, type ReadSignal } from \"@silvery/signals\"\n\nimport type { ConsoleEntry, ConsoleMethod } from \"../../ansi/types\"\nimport { createConsoleRouter, type ConsoleRouter } from \"./console-router\"\n\n/**\n * Aggregate counts of captured console output by severity.\n */\nexport interface ConsoleStats {\n total: number\n errors: number\n warnings: number\n}\n\n/**\n * Options for `console.capture()`.\n */\nexport interface ConsoleCaptureOptions {\n /**\n * Suppress forwarding to the original console methods.\n * Use in TUI / alt-screen mode where the raw output would corrupt the display.\n * Default: false.\n */\n suppress?: boolean\n /**\n * Store full entries in memory (default: true).\n * Set false for count-only mode — `getSnapshot()` returns empty, but\n * `getStats()` still tracks counts. Avoids unbounded memory growth for\n * long-running sessions where you only care about warning/error badges.\n */\n capture?: boolean\n}\n\n/**\n * Console — single-owner console.* capture + replay for a silvery session.\n *\n * Constructed lazily by `createTerm()` (no patching until `capture()`).\n * `subscribe` + `getSnapshot` are shaped for React's `useSyncExternalStore` —\n * each change produces a new array reference.\n */\nexport interface Console extends Disposable {\n /**\n * Start patching `console.log/info/warn/error/debug`. Idempotent — calling\n * while already capturing is a no-op (options are ignored on re-entry;\n * `restore()` then `capture()` again to change behaviour).\n */\n capture(options?: ConsoleCaptureOptions): void\n\n /**\n * Restore original console methods. Idempotent. Subscribers survive; you can\n * `capture()` again without re-subscribing. `dispose()` is the terminal\n * variant that also clears subscribers.\n */\n restore(): void\n\n /**\n * Whether `capture()` is currently active — a `ReadSignal<boolean>`.\n * Call `console.capturing()` to read; subscribe via\n * `effect(() => console.capturing())`. The owner writes it internally from\n * `capture()` / `restore()`.\n */\n readonly capturing: ReadSignal<boolean>\n\n /**\n * Notification signal — increments by 1 per captured entry (stats.total).\n * Cheap: no array copy, no object allocation. Consumers subscribe via\n * `effect(() => console.count())` and pull the full list lazily (via\n * `entriesSnapshot()`) only when they need it — typically on a debounce\n * flush in a React hook. Replaces the per-entry frozen-slice publish that\n * degraded to O(n²) for long sessions (Pro review 2026-04-22 P1-9).\n */\n readonly count: ReadSignal<number>\n\n /**\n * Return a frozen snapshot of captured entries at this moment. Slices on\n * demand — callers pay O(n) only when they actually need the list, not\n * on every log. Returns an empty frozen array when `capture=false` was\n * passed.\n */\n entriesSnapshot(): readonly ConsoleEntry[]\n\n /** Aggregate counts. Tracked even when `capture=false`. */\n getStats(): ConsoleStats\n\n /**\n * Replay captured entries to explicit streams (typically `process.stdout` +\n * `process.stderr` after exiting alt-screen). Entries whose stream was\n * `'stderr'` go to the stderr stream; the rest go to stdout. Does not clear\n * entries — call this alongside `dispose()` at TUI exit.\n */\n replay(stdout: NodeJS.WriteStream, stderr: NodeJS.WriteStream): void\n\n dispose(): void\n [Symbol.dispose](): void\n}\n\nconst STDERR_METHODS = new Set<ConsoleMethod>([\"error\", \"warn\"])\nconst EMPTY_ENTRIES: readonly ConsoleEntry[] = Object.freeze([])\n\n/**\n * Create a Console owner backed by a ConsoleRouter. Starts in the restored\n * (non-capturing) state — call `capture()` to register the tap and (when\n * `suppress: true`) also push a suppress sink policy on the router.\n *\n * When no router is provided, Console constructs a private one patched\n * against the given `target` console global. Production Term factories\n * should pass a shared router so Console's tap and Output's sink layer\n * deterministically via a single patch site.\n */\nexport function createConsole(\n target: globalThis.Console = globalThis.console,\n router?: ConsoleRouter,\n): Console {\n let disposed = false\n // Reactive `capturing` — written only by capture()/restore(), read by the\n // public `capturing` ReadSignal.\n const _capturing = signal<boolean>(false)\n let captureEntries = true\n\n // Authoritative ConsoleEntry[] — kept mutable; snapshots are frozen on\n // demand via entriesSnapshot(). Replaces the per-log frozen-slice publish\n // that degraded to O(n²) over long sessions (Pro review P1-9).\n const buffer: ConsoleEntry[] = []\n // Notification signal — increments on every captured entry. Cheap: no\n // array copy. Consumers subscribe via effect(() => console.count()),\n // then pull the buffer lazily with entriesSnapshot().\n const _count = signal<number>(0)\n const stats: ConsoleStats = { total: 0, errors: 0, warnings: 0 }\n\n const ownsRouter = !router\n const _router: ConsoleRouter = router ?? createConsoleRouter(target)\n\n let unregisterTap: (() => void) | null = null\n let unregisterSink: (() => void) | null = null\n\n function capture(options?: ConsoleCaptureOptions): void {\n if (disposed) return\n if (_capturing()) return\n _capturing(true)\n captureEntries = options?.capture ?? true\n // Tap records each call into the entry buffer + stats.\n unregisterTap = _router.registerTap((call) => {\n stats.total++\n if (call.method === \"error\") stats.errors++\n else if (call.method === \"warn\") stats.warnings++\n if (captureEntries) {\n buffer.push({\n method: call.method,\n args: call.args,\n stream: STDERR_METHODS.has(call.method) ? \"stderr\" : \"stdout\",\n })\n }\n // Always advance count so non-capturing consumers (count-only mode)\n // still get a reactive heartbeat for badges / activity indicators.\n _count(stats.total)\n })\n // `suppress: true` asks the router to drop forwarding entirely.\n // Without it, the router forwards to the active sink (Output's redirect)\n // or, if no sink is registered, to the original method.\n if (options?.suppress) {\n unregisterSink = _router.registerSink({ suppress: true })\n }\n }\n\n function restore(): void {\n if (!_capturing()) return\n _capturing(false)\n unregisterTap?.()\n unregisterTap = null\n unregisterSink?.()\n unregisterSink = null\n }\n\n function replay(stdout: NodeJS.WriteStream, stderr: NodeJS.WriteStream): void {\n for (const entry of buffer) {\n const stream = entry.stream === \"stderr\" ? stderr : stdout\n const line =\n entry.args\n .map((a) =>\n typeof a === \"string\"\n ? a\n : a instanceof Error\n ? `${a.name}: ${a.message}`\n : safeJsonStringify(a),\n )\n .join(\" \") + \"\\n\"\n stream.write(line)\n }\n }\n\n function dispose(): void {\n if (disposed) return\n disposed = true\n restore()\n if (ownsRouter) _router.dispose()\n }\n\n return {\n capture,\n restore,\n capturing: _capturing as ReadSignal<boolean>,\n count: _count as ReadSignal<number>,\n entriesSnapshot(): readonly ConsoleEntry[] {\n if (!captureEntries) return EMPTY_ENTRIES\n return Object.freeze(buffer.slice())\n },\n getStats() {\n return { ...stats }\n },\n replay,\n dispose,\n [Symbol.dispose]: dispose,\n }\n}\n\nfunction safeJsonStringify(value: unknown): string {\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n}\n","/**\n * Internal accessor for raw stdin/stdout on a Term.\n *\n * The public `Term` interface (`../ansi/term.ts`) deliberately omits\n * `stdin` / `stdout`: direct stream access is the leak vector that produced\n * the `wasRaw` race class. Sub-owners (`term.input`, `term.output`,\n * `term.modes`, etc.) are the supported surface for every consumer.\n *\n * At runtime, the raw streams are stored on the term's underlying object\n * under two non-enumerable `Symbol` keys so external `as any` casts cannot\n * reach them — only code that imports `STDIN_SYMBOL` / `STDOUT_SYMBOL` from\n * this module can read them, and the module's import is restricted to\n * silvery's own runtime/ and ansi/ directories by the ownership lint\n * (`packages/km-infra/scripts/check-stdin-ownership.sh` in km).\n *\n * BUT: silvery's own `run()` adapter still has to thread raw streams into the\n * legacy `createApp.run()` option bag (`stdin: ReadStream`, `stdout: WriteStream`)\n * for the emulator + real-terminal paths. Until createApp grows a Term-aware\n * overload, this internal accessor is the single legitimate bridge.\n */\n\nimport type { Term } from \"../ansi/term\"\n\n/** Private symbol key for the raw stdin stream on a term's underlying object. */\nexport const STDIN_SYMBOL: unique symbol = Symbol.for(\"silvery.internal.stdin\")\n/** Private symbol key for the raw stdout stream on a term's underlying object. */\nexport const STDOUT_SYMBOL: unique symbol = Symbol.for(\"silvery.internal.stdout\")\n\n/**\n * Internal shape — silvery runtime adapters only. Every Term factory in\n * `term.ts` (createNodeTerm, createHeadlessTerm, createBackendTerm) attaches\n * these symbol-keyed fields on the underlying termBase object; they're\n * absent from the public `Term` interface AND from enumerable property\n * inspection so user code can't reach them via `(term as any).stdin`.\n */\nexport interface TermInternalStreams {\n readonly stdin: NodeJS.ReadStream\n readonly stdout: NodeJS.WriteStream\n}\n\n/**\n * Read the raw stdin/stdout streams a Term wraps. ONLY for silvery runtime\n * adapters that bridge to legacy stream-based APIs (createApp.run()'s option\n * bag). User code MUST go through sub-owners — see the Term interface.\n *\n * Each factory attaches the streams under `STDIN_SYMBOL` / `STDOUT_SYMBOL`\n * as non-enumerable own properties on the underlying object.\n */\nexport function getInternalStreams(term: Term): TermInternalStreams {\n const obj = term as unknown as Record<symbol, unknown>\n return {\n stdin: obj[STDIN_SYMBOL] as NodeJS.ReadStream,\n stdout: obj[STDOUT_SYMBOL] as NodeJS.WriteStream,\n }\n}\n","/**\n * Term interface and createTerm() factory.\n *\n * Term is the central abstraction for terminal interaction:\n * - Caps + emulator identity: term.caps, term.emulator\n * - Dimensions: cols, rows (shorthand for term.size.cols() / .rows())\n * - I/O: write(), writeLine(), notify()\n * - Sub-owners: input, output, modes, size, signals, console\n * - Styling: Chainable styles via Proxy (term.bold.red('text'))\n * - Lifecycle: Disposable pattern via Symbol.dispose\n *\n * Post km-silvery.plateau-naming-polish (2026-04-23): `term.identity` became\n * `term.emulator` (matches TERM_PROGRAM provenance) and `term.heuristics` was\n * absorbed into `term.caps` with a `maybe` prefix per field\n * (`caps.maybeDarkBackground` etc.).\n *\n * Post km-silvery.caps-restructure (Phase 7, 2026-04-23): the legacy\n * `hasCursor()`/`hasInput()`/`hasColor()`/`hasUnicode()` methods are gone.\n * Callers read `term.caps.cursor`, `term.caps.input`, `term.caps.colorLevel`,\n * and `term.caps.unicode` directly — one source of truth.\n *\n * @example\n * ```ts\n * // Styling\n * const term = createTerm()\n * console.log(term.bold.red('error'))\n *\n * // Full terminal app\n * using term = createTerm()\n * await run(<App />, term)\n * ```\n */\n\nimport {\n createMixedStyle,\n createStyle,\n createTerminalProfile,\n detectColorFromEnv,\n type Style,\n type TerminalProfile,\n type TerminalEmulator,\n} from \"@silvery/ansi\"\nimport { createTerminal } from \"@termless/core\"\nimport type {\n ColorLevel,\n CreateTermOptions,\n TermEmulator,\n TermEmulatorBackend,\n TermScreen,\n TerminalCaps,\n} from \"./types\"\nimport type { TerminalBuffer } from \"../buffer\"\nimport { createTextFrame } from \"../buffer\"\nimport type { TextFrame } from \"@silvery/ag/text-frame\"\nimport { outputPhase } from \"../pipeline/output-phase\"\nimport { defaultCaps } from \"./detection\"\nimport { createInputOwner, type InputOwner as Input } from \"../runtime/input-owner\"\nexport type { Input }\nimport { createOutput, type Output } from \"../runtime/devices/output\"\nexport type { Output }\nimport { createFixedSize, createSize, type Size, type SizeSnapshot } from \"../runtime/devices/size\"\nexport type { Size, SizeSnapshot }\nimport { createModes, type Modes } from \"../runtime/devices/modes\"\nexport type { Modes }\nimport { createSignals, type Signals } from \"../runtime/devices/signals\"\nexport type { Signals }\nimport {\n createConsole,\n type Console as DeviceConsole,\n type ConsoleCaptureOptions,\n type ConsoleStats,\n} from \"../runtime/devices/console\"\nimport { createConsoleRouter } from \"../runtime/devices/console-router\"\nexport type { DeviceConsole as Console, ConsoleCaptureOptions, ConsoleStats }\nimport { STDIN_SYMBOL, STDOUT_SYMBOL } from \"../runtime/term-internal\"\nimport {\n createNotificationTarget,\n NOTIFICATION_WRITER_AVAILABLE,\n type NotificationActivation,\n type NotificationRequest,\n type NotificationTarget,\n type TerminalNotificationDelivery,\n} from \"./notification\"\n\nexport type { OutputOptions } from \"../runtime/devices/output\"\n\n// =============================================================================\n// ANSI Utilities\n// =============================================================================\n\n/**\n * ANSI escape code pattern for stripping.\n */\nconst ANSI_REGEX =\n /\\x1b\\[[0-9;:]*m|\\x9b[0-9;:]*m|\\x1b\\]8;;[^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)|\\x9d8;;[^\\x07\\x1b\\x9c]*(?:\\x07|\\x1b\\\\|\\x9c)/g\n\n/**\n * Strip all ANSI escape codes from a string.\n */\nfunction stripAnsi(text: string): string {\n return text.replace(ANSI_REGEX, \"\")\n}\n\n// =============================================================================\n// Style Chain Types\n// =============================================================================\n\n/**\n * All chalk style method names that can be chained.\n */\ntype ChalkStyleName =\n // Modifiers\n | \"reset\"\n | \"bold\"\n | \"dim\"\n | \"italic\"\n | \"underline\"\n | \"overline\"\n | \"inverse\"\n | \"hidden\"\n | \"strikethrough\"\n | \"visible\"\n // Foreground colors\n | \"black\"\n | \"red\"\n | \"green\"\n | \"yellow\"\n | \"blue\"\n | \"magenta\"\n | \"cyan\"\n | \"white\"\n | \"gray\"\n | \"grey\"\n | \"blackBright\"\n | \"redBright\"\n | \"greenBright\"\n | \"yellowBright\"\n | \"blueBright\"\n | \"magentaBright\"\n | \"cyanBright\"\n | \"whiteBright\"\n // Background colors\n | \"bgBlack\"\n | \"bgRed\"\n | \"bgGreen\"\n | \"bgYellow\"\n | \"bgBlue\"\n | \"bgMagenta\"\n | \"bgCyan\"\n | \"bgWhite\"\n | \"bgGray\"\n | \"bgGrey\"\n | \"bgBlackBright\"\n | \"bgRedBright\"\n | \"bgGreenBright\"\n | \"bgYellowBright\"\n | \"bgBlueBright\"\n | \"bgMagentaBright\"\n | \"bgCyanBright\"\n | \"bgWhiteBright\"\n\n/**\n * StyleChain provides chainable styling methods.\n * Each property returns a new chain, and the chain is callable.\n */\nexport type StyleChain = {\n /**\n * Apply styles to text.\n */\n (text: string): string\n (template: TemplateStringsArray, ...values: unknown[]): string\n\n /**\n * RGB foreground color.\n */\n rgb(r: number, g: number, b: number): StyleChain\n\n /**\n * Hex foreground color.\n */\n hex(color: string): StyleChain\n\n /**\n * 256-color foreground.\n */\n ansi256(code: number): StyleChain\n\n /**\n * RGB background color.\n */\n bgRgb(r: number, g: number, b: number): StyleChain\n\n /**\n * Hex background color.\n */\n bgHex(color: string): StyleChain\n\n /**\n * 256-color background.\n */\n bgAnsi256(code: number): StyleChain\n} & {\n /**\n * Chainable style properties.\n */\n readonly [K in ChalkStyleName]: StyleChain\n} & {\n // Extended underline terminators (Phase 6 of the unicode plateau,\n // 2026-04-23). These replaced the bare `curlyUnderline()` /\n // `styledUnderline()` etc. exports in `@silvery/ansi` — caps flow\n // through `createStyle(caps)` at Term construction instead of per-call.\n // Not chainable; take text as the final argument. Fall back to standard\n // SGR 4 when the requested style isn't in `term.caps.underlineStyles`\n // (post Phase 7 the field is an array, not a boolean). Declared in a separate\n // intersection member because TypeScript forbids mixing mapped types\n // with method signatures in the same object.\n curlyUnderline(text: string): string\n dottedUnderline(text: string): string\n dashedUnderline(text: string): string\n doubleUnderline(text: string): string\n underlineColor(r: number, g: number, b: number, text: string): string\n styledUnderline(\n name: import(\"./types.ts\").UnderlineStyle,\n rgb: import(\"./types.ts\").RGB,\n text: string,\n ): string\n}\n\n// =============================================================================\n// Term Interface\n// =============================================================================\n\n/**\n * Term — the central abstraction for terminal interaction.\n *\n * Term is both a styling helper (chainable ANSI via Proxy) and the umbrella\n * for typed sub-owners (input / output / modes / size / signals / console).\n * Pass it to `run()` or `createApp()`.\n *\n * Provides:\n * - Capability detection (cached on creation)\n * - Dimensions (shorthand getters over `term.size`)\n * - I/O (write, writeLine + the per-resource sub-owners)\n * - Sub-owners: input (stdin/probes/events), output (stdout guard),\n * modes (raw/alt-screen/paste/kitty/mouse/focus), size (dims + resize),\n * signals (process signal scope), console (console.* capture)\n * - Styling (chainable via Proxy)\n * - Disposable lifecycle\n *\n * @example\n * ```ts\n * using term = createTerm()\n * await run(<App />, term)\n * ```\n */\nexport interface Term extends Disposable, StyleChain, NotificationTarget {\n // -------------------------------------------------------------------------\n // Capability + identity surface (post km-silvery.caps-restructure Phase 7)\n //\n // The legacy hasCursor()/hasInput()/hasColor()/hasUnicode() methods are\n // deleted — they duplicated fields already on `caps`. Callers read:\n // term.caps.cursor\n // term.caps.input\n // term.caps.colorLevel\n // term.caps.unicode\n // -------------------------------------------------------------------------\n\n /**\n * Terminal capabilities profile.\n *\n * Always populated — every Term constructor commits to a full TerminalCaps.\n * Node-backed Terms with TTY stdin detect from the environment; non-TTY\n * Node terms, headless Terms, and emulator-backed Terms use sensible\n * deterministic defaults (`defaultCaps()` — truecolor, unicode, no kitty\n * keyboard). Override via `createTerm({ caps: { … } })`.\n *\n * Post km-silvery.terminal-profile-plateau Phase 2 this is non-optional —\n * callers no longer need `term.caps ?? detectTerminalCaps()` guards.\n *\n * Equivalent to `term.profile.caps`. The two views are guaranteed identical\n * — every Term constructor seeds `profile` from `caps` (or vice versa) via\n * `createTerminalProfile({ caps })` so there's only one source of truth.\n */\n readonly caps: TerminalCaps\n\n /**\n * Fully-resolved {@link TerminalProfile} for this Term — `caps`, `colorLevel`,\n * `colorForced`, and `colorProvenance` bundled into the single value\n * downstream consumers should pass through the pipeline.\n *\n * Every Term variant owns its profile. Node-backed Terms build it from the\n * TTY/env detection that populated `caps`; headless and emulator-backed\n * Terms build it from their deterministic caps. The profile's\n * `colorProvenance` is always `\"caller-caps\"` (and `colorForced` is `false`)\n * when the Term constructed it from `caps` — Term construction is not an\n * opportunity for env precedence (that happens at `run()` / `createApp()`\n * where `colorLevel` / `NO_COLOR` are applied).\n *\n * Prefer this over `term.caps` when calling downstream pipeline entry\n * points that accept a profile. run.tsx's Term branch reads it directly\n * instead of rebuilding via `createTerminalProfile({ caps: term.caps })`\n * — one detection, one profile, no double-pass.\n *\n * Post km-silvery.plateau-term-owns-profile (H15 of the /big review\n * 2026-04-23).\n */\n readonly profile: TerminalProfile\n\n /**\n * Environment identity — `program`, `version`, `TERM`. Convenience mirror\n * of `profile.emulator`. Callers that only need \"who is the terminal?\"\n * (diagnostics, probe-cache keys) read this instead of sitting on the full\n * protocol-flags surface of {@link caps}.\n *\n * Post km-silvery.plateau-naming-polish (2026-04-23): renamed from\n * `term.identity`; `TerminalIdentity` → `TerminalEmulator`.\n */\n readonly emulator: TerminalEmulator\n\n // -------------------------------------------------------------------------\n // Dimensions\n // -------------------------------------------------------------------------\n\n /**\n * Terminal width in columns.\n * Undefined if not a TTY or dimensions unavailable.\n */\n readonly cols: number | undefined\n\n /**\n * Terminal height in rows.\n * Undefined if not a TTY or dimensions unavailable.\n */\n readonly rows: number | undefined\n\n // -------------------------------------------------------------------------\n // Sub-owners replace direct stdin/stdout (Phase 8 of km-silvery.term-sub-owners)\n //\n // The public Term interface no longer exposes raw NodeJS.ReadStream /\n // WriteStream. Direct access was the leak vector that produced the\n // 2026-04-22 wasRaw race class. Use the typed sub-owners instead:\n // - term.input — reads / probes (was: term.stdin.on('data', …))\n // - term.output — writes (was: term.stdout.write(…))\n // - term.modes — raw mode + protocol toggles (was: term.stdin.setRawMode)\n // - term.size — cols/rows + resize subscription\n // - term.signals — process signal scope\n // - term.console — patched console capture\n //\n // Silvery's own runtime (run.tsx adapters) still needs the raw streams to\n // bridge to legacy createApp.run() options — those access them via the\n // package-private accessor `getInternalStreams(term)` from `./term-internal`.\n // -------------------------------------------------------------------------\n\n // -------------------------------------------------------------------------\n // Sub-owners (the typed I/O surface — see km-silvery.term-sub-owners)\n //\n // Each sub-owner owns one class of shared global I/O state for the Term's\n // lifetime. Race-free by construction: only one writer per resource.\n // -------------------------------------------------------------------------\n\n /**\n * Input owner — mediates ALL stdin reads + raw-mode + data subscription.\n * Use `term.input.probe(…)` for terminal queries (color, cursor, kitty, etc.)\n * and its typed key/mouse/paste/focus subscriptions for normal input.\n *\n * Replaces direct `process.stdin.setRawMode` / `stdin.on('data', …)` —\n * those patterns race under async (the 2026-04-22 wasRaw class).\n *\n * Lazily constructed on first access for Node-backed Terms.\n * Undefined for headless Terms (no stdin to own).\n */\n readonly input: Input | undefined\n\n /**\n * Output owner — single-owner stdout/stderr/console mediator.\n * Use `term.output.write(…)` for render-pipeline output.\n *\n * When activated (after protocol setup), intercepts `process.stdout` /\n * `process.stderr` / `console.*` so only silvery's render pipeline reaches\n * the terminal. Non-silvery writes are suppressed (stdout) or redirected to\n * `DEBUG_LOG` / buffered (stderr). One stable owner per Term, toggled via\n * `activate()` / `deactivate()` for pause/resume cycles.\n *\n * Lazily constructed on first access for Node-backed Terms.\n * Undefined for headless and emulator-backed Terms (no real stdout to own).\n */\n readonly output: Output | undefined\n\n /**\n * Size owner — single source of truth for terminal dimensions, exposed as\n * alien-signals `ReadSignal`s.\n *\n * `term.size.cols()` / `term.size.rows()` / `term.size.snapshot()` read the\n * current value and, inside `computed` / `effect`, subscribe to changes.\n * The first read installs the stdout `resize` listener; SIGWINCH bursts\n * coalesce through the Size owner's 200ms trailing debounce.\n *\n * Replaces direct `process.stdout.columns` / `stdout.rows` reads — those\n * return stale snapshots under concurrent resize and scatter coalescing\n * logic across every consumer.\n *\n * `term.cols` / `term.rows` remain as shorthand getters that delegate to\n * this owner; they are slated for removal alongside `term.stdin/stdout` in\n * Phase 8.\n */\n readonly size: Size\n\n /**\n * Modes owner — single authority for terminal protocol modes, exposed as\n * alien-signals `Signal<T>`s.\n *\n * Each mode is a callable signal: `term.modes.rawMode`, `altScreen`,\n * `bracketedPaste`, `kittyKeyboard` (`number | false`), `mouse`,\n * `focusReporting`. Read via `modes.altScreen()`, write via\n * `modes.altScreen(true)`, subscribe via `effect(() => modes.altScreen())`.\n * Same-value writes don't re-emit ANSI (alien-signals equality). `dispose`\n * restores exactly what this owner activated.\n *\n * Replaces the scattered `enableMouse()` / `enableKittyKeyboard()` /\n * `enableBracketedPaste()` / `enableFocusReporting()` call sites that\n * previously toggled terminal state from every subsystem. Those shared\n * globals are the same leak vector that produced the 2026-04-22 wasRaw\n * race class — concentrating them behind one owner makes them race-free.\n */\n readonly modes: Modes\n\n /**\n * Signals owner — single coordinator for every process-signal handler\n * bound to this Term's lifetime.\n *\n * `term.signals.on(\"SIGINT\", handler, { priority, before, after, name })`\n * registers a teardown handler. One shared `process.on(signal, …)` listener\n * is installed per signal, regardless of how many handlers the owner\n * manages. On `dispose()` (called from `term[Symbol.dispose]`), every\n * handler runs in priority / dependency order, each wrapped in try/catch\n * so one failure doesn't block the rest.\n *\n * Replaces ad-hoc `process.on(\"SIGINT\", …)` / `process.once(\"SIGTERM\", …)`\n * call sites scattered across runtime + apps. The 2026-04-22 shared-global\n * audit found 78 such sites with no documented cleanup order — late\n * handlers could crash while earlier handlers' resources leaked. The owner\n * gives every Term exactly one entry-point to the signal graph.\n *\n * Present on every Term variant — even headless / emulator-backed — since\n * signal handling is cross-cutting and benefits from consistent teardown\n * semantics in tests as well as production.\n */\n readonly signals: Signals\n\n /**\n * Console owner — single-owner console.* interceptor for the Term's lifetime.\n *\n * Starts inert. Call `term.console.capture({suppress:true})` once the alt\n * screen is active to route `console.log/info/warn/error/debug` into a\n * buffer instead of the screen; then `term.console.replay(stdout, stderr)`\n * on exit to re-emit captured entries to the normal streams. React apps\n * read via `subscribe` + `getSnapshot` (see `<Console>` + `useConsole`).\n *\n * Replaces the standalone console-patching helper — same implementation,\n * Term-owned lifecycle. Undefined for Terms that don't own a real console\n * (headless dims + emulator-backed), which never render through the global\n * terminal and therefore have nothing to corrupt.\n */\n readonly console: DeviceConsole | undefined\n\n // -------------------------------------------------------------------------\n // I/O Methods\n // -------------------------------------------------------------------------\n\n /**\n * Write string to stdout.\n */\n write(str: string): void\n\n /** Emit a desktop notification through the capability-proven protocol. */\n notify(request: NotificationRequest): TerminalNotificationDelivery\n\n /** Subscribe to target-neutral notification and action activations. */\n onNotificationActivation(handler: (event: NotificationActivation) => void): () => void\n\n /**\n * Write string followed by newline to stdout.\n */\n writeLine(str: string): void\n\n // -------------------------------------------------------------------------\n // Utilities\n // -------------------------------------------------------------------------\n\n /**\n * Strip ANSI escape codes from string.\n */\n stripAnsi(str: string): string\n\n // -------------------------------------------------------------------------\n // Terminal Emulator (present when created with a termless backend)\n // -------------------------------------------------------------------------\n\n /**\n * Visible screen region. Only available when created with a terminal backend.\n * Provides getText(), getLines(), containsText() for assertions.\n */\n readonly screen?: TermScreen\n\n /**\n * Scrollback region. Only available when created with a terminal backend.\n * Provides getText(), getLines(), containsText() for assertions.\n */\n readonly scrollback?: TermScreen\n\n /**\n * Cell-level access for the visible screen — row-first order.\n * Returns resolved RGB colors, attributes, wide-char info.\n * Only available on emulator-backed terms (createTermless).\n */\n cell?(\n row: number,\n col: number,\n ): { readonly fg: unknown; readonly bg: unknown; readonly char: string }\n\n /**\n * Row-level access for the visible screen.\n * Only available on emulator-backed terms (createTermless).\n */\n row?(n: number): {\n getText(): string\n cell(col: number): { readonly fg: unknown; readonly bg: unknown; readonly char: string }\n }\n\n /**\n * Resize the terminal emulator. Only available when created with a terminal backend.\n * Resizes the underlying emulator and triggers a re-render in the app.\n */\n resize?(cols: number, rows: number): void\n\n // -------------------------------------------------------------------------\n // Paint (rendered buffer → output)\n // -------------------------------------------------------------------------\n\n /**\n * Paint a rendered buffer to produce ANSI output.\n * Diffs buffer against prev (fresh render if prev is null).\n * Updates term.frame with an immutable TextFrame snapshot.\n * Returns the ANSI output string.\n * For emulator backends, also feeds the output to the emulator.\n * For headless terms, returns empty string.\n */\n paint?(buffer: TerminalBuffer, prev: TerminalBuffer | null): string\n\n /**\n * Last painted TextFrame. Set after each paint() call.\n * Immutable snapshot with cell-level access and resolved RGB colors.\n */\n readonly frame?: TextFrame\n}\n\n// =============================================================================\n// Shared Helpers\n// =============================================================================\n\n/** Mock stdout that feeds a terminal emulator and supports resize events. */\nfunction createEmulatorStdout(feed: (data: string) => void, cols: number, rows: number) {\n const resizeListeners = new Set<() => void>()\n const stdout: { columns: number; rows: number } & Record<string, unknown> = {\n write: (data: string | Uint8Array) => {\n feed(typeof data === \"string\" ? data : new TextDecoder().decode(data))\n return true\n },\n on: (event: string, handler: () => void) => {\n if (event === \"resize\") resizeListeners.add(handler)\n },\n off: (event: string, handler: () => void) => {\n if (event === \"resize\") resizeListeners.delete(handler)\n },\n isTTY: true,\n columns: cols,\n rows: rows,\n }\n return {\n stdout: stdout as unknown as NodeJS.WriteStream,\n resizeListeners,\n updateDims(c: number, r: number) {\n stdout.columns = c\n stdout.rows = r\n },\n }\n}\n\n/**\n * Finalize termBase into a Term: add frame getter, delegate emulator properties,\n * wrap with style chain Proxy.\n */\nfunction finalizeTerm(\n style: Style,\n termBase: Record<string, unknown>,\n frame: { get: () => TextFrame | undefined },\n opts?: {\n defineProperties?: Record<string, PropertyDescriptor>\n delegateFrom?: object\n },\n): Term {\n Object.defineProperty(termBase, \"frame\", { get: frame.get, enumerable: true })\n if (opts?.defineProperties) Object.defineProperties(termBase, opts.defineProperties)\n if (opts?.delegateFrom) {\n for (const key of Object.keys(opts.delegateFrom)) {\n if (key in termBase) continue\n const val = (opts.delegateFrom as any)[key]\n Object.defineProperty(\n termBase,\n key,\n typeof val === \"function\"\n ? { value: (...args: unknown[]) => (opts.delegateFrom as any)[key](...args) }\n : { get: () => (opts.delegateFrom as any)[key] },\n )\n }\n }\n return createMixedStyle(style, termBase) as unknown as Term\n}\n\n// =============================================================================\n// createTerm Factory\n// =============================================================================\n\n/**\n * Create a Term instance.\n *\n * Factory overloads:\n * - `createTerm()` — Node.js terminal (auto-detect from process.stdin/stdout)\n * - `createTerm({ stdout, stdin, ... })` — Node.js with custom streams/overrides\n * - `createTerm({ cols, rows })` — Headless for testing (no I/O, fixed dims)\n * - `createTerm(backend, { cols, rows })` — Terminal emulator backend (termless) for testing\n * - `createTerm(emulator)` — Pre-created termless Terminal\n *\n * Detection results are cached at creation time for consistency.\n *\n * @example\n * ```ts\n * // Full terminal app\n * using term = createTerm()\n * await run(<App />, term)\n *\n * // Headless for testing\n * const term = createTerm({ cols: 80, rows: 24 })\n *\n * // Terminal emulator (termless) for full ANSI testing\n * using term = createTerm(createXtermBackend(), { cols: 80, rows: 24 })\n * await run(<App />, term)\n * expect(term.screen).toContainText(\"Hello\")\n *\n * // Custom streams\n * const term = createTerm({ stdout: customStream })\n * ```\n */\nexport function createTerm(options?: CreateTermOptions): Term\nexport function createTerm(dims: { cols: number; rows: number; caps?: Partial<TerminalCaps> }): Term\nexport function createTerm(\n backend: TermEmulatorBackend,\n dims: { cols: number; rows: number; caps?: Partial<TerminalCaps> },\n): Term\nexport function createTerm(emulator: TermEmulator, opts?: { caps?: Partial<TerminalCaps> }): Term\nexport function createTerm(\n first?:\n | CreateTermOptions\n | { cols: number; rows: number; caps?: Partial<TerminalCaps> }\n | TermEmulator\n | TermEmulatorBackend,\n second?:\n | { cols: number; rows: number; caps?: Partial<TerminalCaps> }\n | { caps?: Partial<TerminalCaps> },\n): Term {\n // Two-arg: createTerm(backend, { cols, rows, caps? }) — raw backend + dims\n if (second && first && isTermBackend(first)) {\n const dims = second as { cols: number; rows: number; caps?: Partial<TerminalCaps> }\n const createTermlessTerminal = createTerminal as unknown as (opts: {\n backend: TermEmulatorBackend\n cols: number\n rows: number\n }) => TermEmulator\n const emulator = createTermlessTerminal({\n backend: first as TermEmulatorBackend,\n cols: dims.cols,\n rows: dims.rows,\n })\n return createBackendTerm(emulator, dims.caps)\n }\n // Detect terminal emulator (termless Terminal): has feed + screen\n if (first && isTermEmulator(first)) {\n const opts = second as { caps?: Partial<TerminalCaps> } | undefined\n return createBackendTerm(first as TermEmulator, opts?.caps)\n }\n // Detect headless dims: has cols + rows but no stdout/stdin/color/caps\n if (first && isHeadlessDims(first)) {\n const dims = first as { cols: number; rows: number; caps?: Partial<TerminalCaps> }\n return createHeadlessTerm({ cols: dims.cols, rows: dims.rows }, dims.caps)\n }\n return createNodeTerm((first as CreateTermOptions) ?? {})\n}\n\n/** Detect terminal emulator (termless Terminal): has feed() + screen */\nfunction isTermEmulator(obj: unknown): obj is TermEmulator {\n if (typeof obj !== \"object\" || obj === null) return false\n const o = obj as Record<string, unknown>\n return typeof o.feed === \"function\" && typeof o.screen === \"object\" && o.screen !== null\n}\n\n/** Detect terminal emulator backend (termless TerminalBackend): has init() + name */\nfunction isTermBackend(obj: unknown): obj is TermEmulatorBackend {\n if (typeof obj !== \"object\" || obj === null) return false\n const o = obj as Record<string, unknown>\n return (\n typeof o.init === \"function\" && typeof o.name === \"string\" && typeof o.destroy === \"function\"\n )\n}\n\n/** Detect headless dims: has cols and rows numbers, no stdout */\nfunction isHeadlessDims(obj: unknown): boolean {\n if (typeof obj !== \"object\" || obj === null) return false\n const o = obj as Record<string, unknown>\n return (\n typeof o.cols === \"number\" && typeof o.rows === \"number\" && !(\"stdout\" in o) && !(\"stdin\" in o)\n )\n}\n\n/**\n * Create a Node.js terminal with full Provider capabilities.\n */\nfunction createNodeTerm(options: CreateTermOptions): Term {\n const stdout = options.stdout ?? process.stdout\n const stdin = options.stdin ?? process.stdin\n\n // Fully-resolved TerminalProfile — the single value that flows through\n // run() / createApp() for this Term's lifetime. Post km-silvery.plateau-\n // delete-legacy-shims (H6): this one call replaces the former\n // `detectColor(stdout)` + `detectTerminalCaps()` pair.\n //\n // Term-construction precedence (differs from profile-factory defaults):\n // - `options.caps` present → caps base + `colorLevel` = caps.colorLevel.\n // Treat explicit caps as a hard override that wins over env — a test\n // harness or adapter passing `caps: { colorLevel: \"truecolor\" }` wants\n // that tier regardless of what FORCE_COLOR / NO_COLOR say. This matches\n // the legacy `detectTerminalCaps()` path which short-circuited on\n // explicit caps.\n // - `options.colorLevel` (`ColorLevel | null`) → thread through\n // profile `colorLevel` so it participates in the normal precedence chain.\n // - non-TTY Node terms → `defaultCaps()` base, but the COLOR TIER still\n // resolves through the env+stdout chain (`detectColorFromEnv`): mono\n // when stdout is piped/redirected (no-color.org), FORCE_COLOR opts back\n // in at the factory's env rung. The deterministic base may not claim a\n // tier the attached stream can't justify — `defaultCaps()` hardcodes\n // `truecolor`, which made every scripted/piped `km` invocation emit\n // ANSI to non-TTY stdout (contract: create-term-defaults, non-TTY\n // streams block). Matches the headless-mono defaults contract.\n // - TTY Node terms → full env-based auto-detection.\n const profileCapsBase: Partial<TerminalCaps> | undefined = options.caps\n ? options.caps\n : stdin.isTTY\n ? undefined // let profile factory run full env detection\n : {\n ...defaultCaps(), // non-TTY: deterministic caps, skip env caps probe\n colorLevel: detectColorFromEnv(process.env as Record<string, string | undefined>, stdout),\n }\n const explicitColor =\n options.colorLevel === undefined ? undefined : (options.colorLevel ?? \"mono\")\n // Explicit caps override the env chain — tests and adapters that pass\n // `caps: { colorLevel: ... }` rely on the tier they specified. Promoting\n // the caps tier into `colorLevel` mostly re-establishes that, but the\n // profile factory still lets NO_COLOR / FORCE_COLOR win over overrides\n // (that's the documented chain). So when caps is explicit, we build the\n // profile with `env: {}` to neutralize env precedence at the factory\n // level — the caller's caps are authoritative for the Term's lifetime.\n const baseProfile: TerminalProfile = options.caps\n ? createTerminalProfile({\n env: {},\n stdout,\n stdin,\n caps: options.caps,\n colorLevel: explicitColor ?? options.caps.colorLevel,\n })\n : createTerminalProfile({\n stdout,\n stdin,\n caps: profileCapsBase,\n colorLevel: explicitColor,\n })\n\n // Apply legacy `options.cursor` / `options.unicode` overrides to caps so\n // the Term's profile is the one source of truth. Without this the old\n // `hasCursor()` / `hasUnicode()` values would disagree with `caps.cursor`\n // / `caps.unicode` once those methods are gone.\n const profile: TerminalProfile =\n options.cursor !== undefined || options.unicode !== undefined\n ? {\n ...baseProfile,\n caps: {\n ...baseProfile.caps,\n cursor: options.cursor ?? baseProfile.caps.cursor,\n unicode: options.unicode ?? baseProfile.caps.unicode,\n },\n }\n : baseProfile\n\n const detectedCaps: TerminalCaps = profile.caps\n const cachedColor: ColorLevel = profile.colorLevel\n\n // Create style instance with appropriate color level + caps. Caps drive\n // the extended-underline methods on Style (Phase 6 of the unicode\n // plateau, 2026-04-23) — `term.curlyUnderline(\"err\")` respects the same\n // `term.caps.underlineStyles` gate the retired bare `curlyUnderline()`\n // export used. Post Phase 7: `underlineStyles` is an array; style.ts\n // projects that to a boolean \"any extended style supported\" for the\n // legacy style caps shape.\n const styleInstance = createStyle({\n level: cachedColor,\n caps: {\n underlineStyles: profile.caps.underlineStyles.length > 0,\n underlineColor: profile.caps.underlineColor,\n },\n })\n\n // Size owner — single source of truth for cols/rows. Subscribes to stdout's\n // `resize` event with trailing-edge coalescing so burst SIGWINCH from tmux/cmux/\n // Ghostty tab switches collapses to one notification. Constructed eagerly\n // so term.size and term.cols/rows are valid for any consumer.\n //\n // `resizeCoalesceMs` lets test / emulator paths opt out of debouncing — they\n // drive resize explicitly via `term.resize(...)` (no SIGWINCH bursts) and\n // want the snapshot to update synchronously so layout reflows within the\n // test's settle window. See `km-silvery.termless-resize-reflow-4-fails`.\n // See km-silvery.term-sub-owners Phase 5.\n const size =\n options.resizeCoalesceMs !== undefined\n ? createSize(stdout, { coalesceMs: options.resizeCoalesceMs })\n : createSize(stdout)\n\n // Force-install the SIGWINCH listener even if no React subscriber ever\n // reads `size.cols()` / `size.rows()` / `size.snapshot()`.\n //\n // `createSize()` installs the `stdout.on(\"resize\")` listener lazily on\n // first signal read (avoids piling listeners onto cold Terms during\n // startup orchestration — see size.ts:`ensureInstalled`). silvercode's\n // React tree drives layout from `Box.onLayout` rather than `term.size.*`,\n // so without this read the listener stays dormant and SIGWINCH events\n // are dropped — the live binary never repaints on terminal resize.\n //\n // Calling `snapshot()` once here triggers `ensureInstalled` exactly once\n // for every live Node Term, regardless of whether downstream code\n // subscribes to the signal. Tests / emulator paths use `createFixedSize`\n // which has no listener, so this read is a no-op there.\n size.snapshot()\n\n let _frame: TextFrame | undefined\n\n // Sub-owner storage declared up-front so `ownedWrite` below can reference\n // `_output` in its closure. `_input` is constructed eagerly for TTY-backed\n // terms (single stdin owner for the session's lifetime); `_output` is\n // populated lazily by `getOutput()`.\n let _output: Output | null = null\n\n // Writer router: every silvery-owned ANSI write (modes toggles, term.write,\n // probe queries via InputOwner) flows through this single function. When\n // `Output` is active, foreign `stdout.write` is patched into a\n // suppress/redirect sink, so owned writes MUST go through `output.write(...)`\n // which bypasses the sink. When `Output` is inactive, the raw `stdout.write`\n // is safe. Without this, any mode toggle or probe query after\n // `output.activate()` goes to the patched `process.stdout.write` and can\n // be dropped. Pro-review finding, 2026-04-22.\n const ownedWrite = (s: string): boolean => {\n const out = _output\n if (out && out.active()) return out.write(s)\n return stdout.write(s)\n }\n const notificationWrite =\n (stdout as { [NOTIFICATION_WRITER_AVAILABLE]?: boolean })[NOTIFICATION_WRITER_AVAILABLE] ===\n false\n ? undefined\n : ownedWrite\n\n // Modes owner — single authority for terminal protocol modes (raw, alt-\n // screen, paste, kitty keyboard, mouse, focus reporting). Consolidates\n // the scattered enable*/disable* call sites. See Phase 4. Declared before\n // Input because Input routes raw-mode + bracketed-paste through modes.\n const modes = createModes({\n write: ownedWrite,\n stdin,\n })\n\n // Lazy Input — constructed on first access for TTY-backed Node terms.\n // Owns stdin's raw mode + data listener + bracketed-paste protocol for the\n // Term's lifetime once accessed. Lazy construction avoids piling up\n // listeners when cold Terms are constructed during startup orchestration\n // (km-cli's multi-step progress UI briefly instantiates several Terms\n // before the live TUI one — if Input were eager, each would attach an\n // 11th `data` listener on process.stdin). Non-TTY backed terms (tests,\n // piped stdin) get undefined; callers branch off `term.input` existence.\n // See km-silvery.term-sub-owners Phase 2 + km-silvery.input-structured-events.\n //\n // `options.input === false` is the explicit opt-out: the host process\n // owns stdin for its own purpose (e.g. piping to a child PTY) and silvery\n // must not race for it. The accessor permanently returns `undefined`,\n // so `term.input?.…` chains no-op and downstream code (probes, term\n // provider input subscription) skips the input pipeline by structural\n // check rather than a separate code path. See\n // `docs/design/terminal-component.md` § \"render({ input: false })\".\n const inputDisabled = options.input === false\n let _input: Input | null = null\n const getInput = (): Input | undefined => {\n if (inputDisabled) return undefined\n if (!stdin.isTTY) return undefined\n if (!_input) {\n _input = createInputOwner(stdin, stdout, {\n writeStdout: ownedWrite,\n modes,\n mouse: options.mouse,\n })\n }\n return _input\n }\n\n // Shared ConsoleRouter — the single patcher for console.*. Both Console\n // (tap) and Output (sink) register against it so activation order no\n // longer matters. Pro review 2026-04-22 P0-3 structural fix.\n const consoleRouter = createConsoleRouter(globalThis.console)\n\n // Lazy Output — constructed on first access. Owns stdout/stderr/console\n // intercepts for the Term's lifetime. Starts deactivated; caller activates\n // after protocol setup. See km-silvery.term-sub-owners Phase 3.\n // Only available when stdout is the real process.stdout (mocks + emulators\n // don't benefit from the global-patching guard).\n const getOutput = (): Output | undefined => {\n if (stdout !== process.stdout) return undefined\n if (!_output) {\n _output = createOutput(undefined, consoleRouter)\n }\n return _output\n }\n\n // Console owner — captures console.* during alt-screen rendering. Shares\n // the Term's ConsoleRouter so its tap lives on the same patch site as\n // Output's sink. See km-silvery.term-sub-owners Phase 7 + Phase C (Pro\n // review follow-up).\n const consoleOwner = createConsole(globalThis.console, consoleRouter)\n\n // Signals owner — coordinates process-signal handlers for this Term's\n // lifetime. No process-level listener is installed until the first `on()`\n // call. Replaces the 78+ scattered `process.on(SIG…, …)` sites found in the\n // 2026-04-22 shared-global audit. See Phase 6.\n const signals = createSignals()\n const notifications = createNotificationTarget(detectedCaps, notificationWrite, (handler) =>\n getInput()?.onNotificationActivationReply(handler),\n )\n\n const termBase = {\n caps: detectedCaps,\n profile,\n emulator: profile.emulator,\n size,\n modes,\n signals,\n console: consoleOwner,\n write: (str: string) => {\n ownedWrite(str)\n },\n notify: notifications.notify,\n onNotificationActivation: notifications.onNotificationActivation,\n writeLine: (str: string) => {\n ownedWrite(str + \"\\n\")\n },\n stripAnsi,\n paint: (buffer: TerminalBuffer, prev: TerminalBuffer | null): string => {\n const output = outputPhase(prev, buffer)\n _frame = createTextFrame(buffer)\n return output\n },\n [Symbol.dispose]: () => {\n // Run signal teardown FIRST so handlers can still touch sub-owners\n // (input/output/modes) while they're alive. After this, we drop each\n // owner in reverse-construction order.\n signals.dispose()\n notifications.dispose()\n if (_input) _input[Symbol.dispose]()\n if (_output) _output[Symbol.dispose]()\n consoleOwner[Symbol.dispose]()\n // Dispose the shared router AFTER both Console and Output have been\n // torn down (above) so no latent taps/sinks reference a disposed\n // router.\n consoleRouter.dispose()\n modes[Symbol.dispose]()\n size[Symbol.dispose]()\n },\n }\n // Raw streams live under private Symbol keys — invisible to Object.keys\n // and unreachable via `(term as any).stdin`. Only `getInternalStreams()`\n // from runtime/term-internal.ts can read them, and that module's import\n // is restricted to silvery's runtime/ + ansi/ by the ownership lint.\n Object.defineProperty(termBase, STDIN_SYMBOL, {\n value: stdin,\n enumerable: false,\n writable: false,\n configurable: false,\n })\n Object.defineProperty(termBase, STDOUT_SYMBOL, {\n value: stdout,\n enumerable: false,\n writable: false,\n configurable: false,\n })\n\n return finalizeTerm(\n styleInstance,\n termBase,\n { get: () => _frame },\n {\n defineProperties: {\n // cols / rows are shorthand for term.size.cols() / .rows(). Non-TTY\n // streams have no reliable dims — surface undefined then.\n cols: { get: () => (stdout.isTTY ? size.cols() : undefined), enumerable: true },\n rows: { get: () => (stdout.isTTY ? size.rows() : undefined), enumerable: true },\n input: { get: () => getInput(), enumerable: true },\n output: { get: () => getOutput(), enumerable: true },\n },\n },\n )\n}\n\n/** Stand-in ReadStream for Modes on headless / emulator-backed Terms.\n *\n * Not a TTY, so `setRawMode` is a no-op on the underlying stream. The Modes\n * owner tracks intent (e.g. `isRawMode`) but emits no ANSI and touches no\n * real termios — correct for testing / emulator backends.\n */\nconst HEADLESS_STDIN: NodeJS.ReadStream = {\n isTTY: false,\n setRawMode() {\n return this as NodeJS.ReadStream\n },\n} as unknown as NodeJS.ReadStream\n\n/** Create a headless terminal for testing — no I/O, fixed dimensions. */\nfunction createHeadlessTerm(\n dims: { cols: number; rows: number },\n capsOverride?: Partial<TerminalCaps>,\n): Term {\n let disposed = false\n let _frame: TextFrame | undefined\n const size = createFixedSize(dims)\n // Modes owner for the headless term: writes go to a sink, stdin is\n // non-TTY so raw-mode toggles are tracked but never applied. Keeps the\n // `term.modes` surface uniform across Term variants.\n const modes = createModes({ write: () => {}, stdin: HEADLESS_STDIN })\n // Signals owner — handlers register lazily; dispose removes them. Same\n // shape as Node-backed Term so test code that mounts a Signals consumer\n // works in both contexts.\n const signals = createSignals()\n\n // Headless Terms are declaratively mono/no-input but still publish a full\n // TerminalCaps so downstream code (pipeline, measurer) gets the guaranteed\n // shape `Term.caps: TerminalCaps` promises. Default to `mono` colorLevel;\n // callers wanting a richer test surface can pass\n // `{ caps: { colorLevel: 'truecolor' } }` through createTermless.\n const headlessCaps: TerminalCaps = {\n ...defaultCaps(),\n colorLevel: \"mono\",\n unicode: false,\n mouse: false,\n bracketedPaste: false,\n ...capsOverride,\n }\n\n // Fully-resolved profile — identical caps, `source: \"caller-caps\"`. See the\n // Node-backed path for the H15 rationale.\n const profile: TerminalProfile = createTerminalProfile({\n env: {},\n stdin: undefined,\n stdout: { isTTY: false },\n caps: headlessCaps,\n })\n const notifications = createNotificationTarget(profile.caps)\n\n const termBase = {\n caps: profile.caps,\n profile,\n emulator: profile.emulator,\n size,\n modes,\n signals,\n console: undefined as DeviceConsole | undefined,\n write: () => {},\n notify: notifications.notify,\n onNotificationActivation: notifications.onNotificationActivation,\n writeLine: () => {},\n stripAnsi,\n paint: (_buffer: TerminalBuffer, _prev: TerminalBuffer | null): string => {\n _frame = createTextFrame(_buffer)\n return \"\"\n },\n [Symbol.dispose]: () => {\n if (disposed) return\n disposed = true\n notifications.dispose()\n signals[Symbol.dispose]()\n modes[Symbol.dispose]()\n size[Symbol.dispose]()\n },\n }\n // Headless exposes process.stdin/stdout under the same symbol-keyed slots\n // so getInternalStreams() returns stable refs across Term variants.\n Object.defineProperty(termBase, STDIN_SYMBOL, {\n value: process.stdin,\n enumerable: false,\n writable: false,\n configurable: false,\n })\n Object.defineProperty(termBase, STDOUT_SYMBOL, {\n value: process.stdout,\n enumerable: false,\n writable: false,\n configurable: false,\n })\n\n return finalizeTerm(\n // Headless: no color, no extended underline. Project the array form of\n // caps.underlineStyles to the boolean the Style caps shape expects.\n createStyle({\n level: null,\n caps: {\n underlineStyles: profile.caps.underlineStyles.length > 0,\n underlineColor: profile.caps.underlineColor,\n },\n }),\n termBase,\n { get: () => _frame },\n {\n defineProperties: {\n cols: { get: () => size.cols(), enumerable: true },\n rows: { get: () => size.rows(), enumerable: true },\n // Headless Terms have no real stdin to own — sub-owner is undefined.\n input: { get: () => undefined, enumerable: true },\n // Headless Terms have no real stdout to own — sub-owner is undefined.\n output: { get: () => undefined, enumerable: true },\n },\n },\n )\n}\n\n/** Create a terminal backed by a termless emulator — real ANSI processing, screen/scrollback. */\nfunction createBackendTerm(emulator: TermEmulator, capsOverride?: Partial<TerminalCaps>): Term {\n const { stdout, resizeListeners, updateDims } = createEmulatorStdout(\n (s) => emulator.feed(s),\n emulator.cols,\n emulator.rows,\n )\n let _frame: TextFrame | undefined\n const size = createFixedSize({ cols: emulator.cols, rows: emulator.rows })\n // Modes owner for emulator-backed terms: ANSI mode sequences would be\n // interpreted as input if fed to the emulator, so the write function is a\n // sink. The owner still tracks state (`modes.mouse()`, etc.) for parity\n // with Node terms, but the emulator itself decides what to accept.\n const modes = createModes({ write: () => {}, stdin: HEADLESS_STDIN })\n // Input sub-owner — non-TTY, pure event-bus mode. `sendInput(data)` parses\n // ANSI bytes and fans out to onKey/onMouse/onPaste/onFocus subscribers so\n // emulator-backed Terms share the same consumer shape as Node-backed.\n const input = createInputOwner(HEADLESS_STDIN, stdout, { enableBracketedPaste: false })\n // Signals owner — emulator-backed terms share the host process so exit /\n // SIGINT handlers remain meaningful. Construction is free (no process\n // listeners until first on()), and the contract promises signals on every\n // Term.\n const signals = createSignals()\n\n // Emulator-backed terms (termless / xterm.js) render a known-capability\n // virtual terminal: truecolor, unicode, mouse, bracketed paste. These are\n // deterministic (no env sniffing), which is exactly what tests want.\n // `defaultCaps()` already matches this shape — keep it as the base so any\n // future default changes propagate without a second source of truth.\n //\n // Post km-silvery.unicode-plateau Phase 3/4 (2026-04-23): caps.cursor and\n // caps.input were added to TerminalCaps. `defaultCaps()` defaults them to\n // `false` (safe for headless / non-TTY). Emulator-backed terms are the\n // exception — they simulate a full TTY, so we override both to `true` to\n // match the hardcoded `hasCursor()` / `hasInput()` methods below. Without\n // this override, `term.caps.cursor not matching the hard-coded hasCursor=true on emulator Term (historic API — removed in Phase 7)` — a subtle\n // divergence any caps consumer would hit.\n const emulatorCaps: TerminalCaps = {\n ...defaultCaps(),\n cursor: true,\n input: true,\n ...capsOverride,\n }\n\n // Fully-resolved profile — same reasoning as the Node-backed path. Source\n // is always `\"caller-caps\"` because Term construction commits to the caps\n // before env-level overrides get a say.\n const profile: TerminalProfile = createTerminalProfile({\n env: {},\n stdin: undefined,\n stdout: { isTTY: false },\n caps: emulatorCaps,\n })\n const notifications = createNotificationTarget(\n profile.caps,\n (str) => emulator.feed(str),\n (handler) => input.onNotificationActivationReply(handler),\n )\n\n const termBase = {\n caps: profile.caps,\n profile,\n emulator: profile.emulator,\n size,\n modes,\n signals,\n console: undefined as DeviceConsole | undefined,\n write: (str: string) => emulator.feed(str),\n notify: notifications.notify,\n onNotificationActivation: notifications.onNotificationActivation,\n writeLine: (str: string) => emulator.feed(str + \"\\n\"),\n resize: (cols: number, rows: number) => {\n emulator.resize(cols, rows)\n updateDims(cols, rows)\n size.update(cols, rows)\n resizeListeners.forEach((l) => l())\n },\n sendInput: (data: string) => input.sendRaw(data),\n stripAnsi,\n paint: (buffer: TerminalBuffer, prev: TerminalBuffer | null): string => {\n const output = outputPhase(prev, buffer)\n if (output) emulator.feed(output)\n _frame = createTextFrame(buffer)\n return output\n },\n _emulator: emulator,\n [Symbol.dispose]: () => {\n notifications.dispose()\n input[Symbol.dispose]()\n signals.dispose()\n modes[Symbol.dispose]()\n size[Symbol.dispose]()\n emulator.close().catch(() => {})\n },\n }\n // Emulator-backed terms expose the mock stdout (feed-into-emulator) and\n // the host's real stdin under the symbol slots so getInternalStreams()\n // returns stable refs for run()'s legacy options-bag bridge.\n Object.defineProperty(termBase, STDIN_SYMBOL, {\n value: process.stdin,\n enumerable: false,\n writable: false,\n configurable: false,\n })\n Object.defineProperty(termBase, STDOUT_SYMBOL, {\n value: stdout,\n enumerable: false,\n writable: false,\n configurable: false,\n })\n\n return finalizeTerm(\n // Emulator-backed (termless / xterm.js): full truecolor + extended\n // underline — emulatorCaps defaults to the modern style set.\n createStyle({\n level: \"truecolor\",\n caps: {\n underlineStyles: profile.caps.underlineStyles.length > 0,\n underlineColor: profile.caps.underlineColor,\n },\n }),\n termBase,\n { get: () => _frame },\n {\n defineProperties: {\n cols: { get: () => size.cols(), enumerable: true },\n rows: { get: () => size.rows(), enumerable: true },\n screen: { get: () => emulator.screen, enumerable: true },\n scrollback: { get: () => emulator.scrollback, enumerable: true },\n // Emulator-backed Terms expose a non-TTY InputOwner as a pure event\n // bus: consumers subscribe via input.on*, and `term.sendInput(data)`\n // parses bytes into sendKey/sendMouse/sendPaste/sendFocus calls.\n input: { get: () => input, enumerable: true },\n // Termless-backed Terms write through emulator.feed, not process.stdout —\n // no Output guard needed.\n output: { get: () => undefined, enumerable: true },\n },\n delegateFrom: emulator,\n },\n )\n}\n","/**\n * Advanced Clipboard — OSC 5522 (Kitty clipboard protocol)\n *\n * Extends OSC 52 with MIME type support, large payload chunking,\n * and paste events. Falls back to OSC 52 when 5522 is not available.\n *\n * Protocol overview (from kitty docs):\n * - Write: ESC ] 5522 ; type=write ST → wdata chunks → empty wdata\n * - Read: ESC ] 5522 ; type=read ; <base64 mime list> ST\n * - Paste mode: CSI ? 5522 h (enable) / CSI ? 5522 l (disable)\n *\n * Metadata is colon-separated key=value pairs. Payloads are base64-encoded.\n * Data chunks are at most 4096 bytes before base64 encoding.\n *\n * Supported by: Kitty 0.28+. Ghostty is considering adoption.\n *\n * @see https://sw.kovidgoyal.net/kitty/clipboard/\n * @module\n */\n\nimport { isProtocolError, ProtocolError } from \"@silvery/ansi\"\nimport { createLogger } from \"loggily\"\n\nconst log = createLogger(\"silvery:advanced-clipboard\")\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst ESC = \"\\x1b\"\nconst ST = `${ESC}\\\\`\nconst OSC_5522 = `${ESC}]5522;`\n\n/** Default chunk size in bytes (before base64 encoding) */\nconst DEFAULT_CHUNK_SIZE = 4096\n\n/** Enable paste events mode */\nexport const ENABLE_PASTE_EVENTS = `${ESC}[?5522h`\n\n/** Disable paste events mode */\nexport const DISABLE_PASTE_EVENTS = `${ESC}[?5522l`\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** A single clipboard entry with MIME type and data. */\nexport interface ClipboardEntry {\n /** MIME type (e.g. \"text/plain\", \"text/html\", \"image/png\") */\n mime: string\n /** Content — string for text types, Uint8Array for binary */\n data: string | Uint8Array\n}\n\n/** Options for creating an advanced clipboard. */\nexport interface AdvancedClipboardOptions {\n /** Write escape sequences to the terminal */\n write: (data: string) => void\n /** Subscribe to terminal input data. Returns unsubscribe function. */\n onData: (handler: (data: string) => void) => () => void\n /** Max bytes per chunk before base64 encoding (default: 4096) */\n chunkSize?: number\n /** Whether the terminal supports OSC 5522 (default: false — uses OSC 52 fallback) */\n supported?: boolean\n}\n\n/** Advanced clipboard with MIME type support and paste events. */\nexport interface AdvancedClipboard {\n /** Copy entries with MIME type support */\n copy(entries: ClipboardEntry[]): void\n /** Copy plain text (convenience) */\n copyText(text: string): void\n /** Copy with HTML alternative */\n copyRich(text: string, html: string): void\n /** Subscribe to paste events. Returns unsubscribe function. */\n onPaste(handler: (entries: ClipboardEntry[]) => void): () => void\n /** Whether the terminal supports OSC 5522 */\n readonly supported: boolean\n /** Clean up subscriptions */\n dispose(): void\n}\n\n// ============================================================================\n// Encoding Helpers\n// ============================================================================\n\n/** Encode string as base64 */\nfunction toBase64(text: string): string {\n return Buffer.from(text, \"utf-8\").toString(\"base64\")\n}\n\n/** Encode bytes as base64 */\nfunction bytesToBase64(data: Uint8Array): string {\n return Buffer.from(data).toString(\"base64\")\n}\n\n/** Decode base64 to string */\nfunction fromBase64(base64: string): string {\n return Buffer.from(base64, \"base64\").toString(\"utf-8\")\n}\n\n/** Decode base64 to bytes */\nfunction base64ToBytes(base64: string): Uint8Array {\n return new Uint8Array(Buffer.from(base64, \"base64\"))\n}\n\n/** Convert entry data to a Buffer for chunking */\nfunction entryDataToBuffer(data: string | Uint8Array): Buffer {\n if (typeof data === \"string\") {\n return Buffer.from(data, \"utf-8\")\n }\n return Buffer.from(data)\n}\n\n// ============================================================================\n// Protocol Helpers\n// ============================================================================\n\n/**\n * Build an OSC 5522 sequence.\n *\n * @param metadata - colon-separated key=value pairs\n * @param payload - optional base64 payload\n */\nfunction osc5522(metadata: string, payload?: string): string {\n if (payload !== undefined) {\n return `${OSC_5522}${metadata};${payload}${ST}`\n }\n return `${OSC_5522}${metadata}${ST}`\n}\n\n/**\n * Generate chunked wdata sequences for a single MIME entry.\n *\n * Each chunk is at most chunkSize bytes before base64 encoding.\n * An empty wdata with just the mime signals end of data for that type.\n */\nfunction* generateChunks(entry: ClipboardEntry, chunkSize: number): Generator<string> {\n const mimeB64 = toBase64(entry.mime)\n const raw = entryDataToBuffer(entry.data)\n\n if (raw.length === 0) {\n // Even empty data sends one chunk then a terminator\n yield osc5522(`type=wdata:mime=${mimeB64}`, \"\")\n } else {\n for (let offset = 0; offset < raw.length; offset += chunkSize) {\n const chunk = raw.subarray(offset, offset + chunkSize)\n const b64 = bytesToBase64(new Uint8Array(chunk))\n yield osc5522(`type=wdata:mime=${mimeB64}`, b64)\n }\n }\n\n // Empty wdata signals end of this MIME type's data\n yield osc5522(\"type=wdata\")\n}\n\n// ============================================================================\n// OSC 52 Fallback\n// ============================================================================\n\n/**\n * Copy plain text via OSC 52 (fallback for terminals without 5522 support).\n */\nfunction osc52Copy(write: (data: string) => void, text: string): void {\n const base64 = toBase64(text)\n write(`${ESC}]52;c;${base64}\\x07`)\n}\n\n// ============================================================================\n// Response Parsing\n// ============================================================================\n\n/** Parsed OSC 5522 response from terminal */\ninterface ParsedResponse {\n type: string\n status?: string\n mime?: string\n payload?: string\n}\n\n/**\n * Parse an OSC 5522 response sequence.\n *\n * Format: ESC ] 5522 ; metadata ; payload ST\n * Metadata: colon-separated key=value pairs\n *\n * Return semantics (see {@link ProtocolError} for the full contract):\n * - `null` — input does NOT contain the OSC 5522 prefix (not for us).\n * - `throw ProtocolError` — prefix matched (we committed to OSC 5522)\n * but the response is missing its ST terminator. Loud failure makes\n * the gap visible to the dispatcher boundary.\n */\nexport function parseOsc5522Response(input: string): ParsedResponse | null {\n // Find OSC 5522 prefix\n const prefixIdx = input.indexOf(OSC_5522)\n if (prefixIdx === -1) return null\n\n const afterPrefix = prefixIdx + OSC_5522.length\n\n // Find ST terminator (ESC \\)\n const stIdx = input.indexOf(ST, afterPrefix)\n if (stIdx === -1) {\n throw new ProtocolError({\n parser: \"parseOsc5522Response\",\n input,\n reason: \"OSC 5522 prefix present but missing ST terminator\",\n })\n }\n\n const body = input.slice(afterPrefix, stIdx)\n\n // Split metadata and payload at first semicolon\n const semiIdx = body.indexOf(\";\")\n const metadataStr = semiIdx === -1 ? body : body.slice(0, semiIdx)\n const payload = semiIdx === -1 ? undefined : body.slice(semiIdx + 1)\n\n // Parse metadata key=value pairs\n const pairs = metadataStr.split(\":\")\n const result: ParsedResponse = { type: \"\" }\n\n for (const pair of pairs) {\n const eqIdx = pair.indexOf(\"=\")\n if (eqIdx === -1) continue\n const key = pair.slice(0, eqIdx)\n const value = pair.slice(eqIdx + 1)\n\n switch (key) {\n case \"type\":\n result.type = value\n break\n case \"status\":\n result.status = value\n break\n case \"mime\":\n result.mime = value\n break\n }\n }\n\n if (payload !== undefined) {\n result.payload = payload\n }\n\n return result\n}\n\n/**\n * Parse a paste event notification from terminal.\n *\n * When paste events mode is enabled (CSI ? 5522 h), the terminal\n * sends available MIME types when the user pastes.\n *\n * Format: ESC ] 5522 ; type=read:status=DATA:mime=<b64mime> ; <b64data> ST\n *\n * Return semantics (see {@link ProtocolError} for the full contract):\n * - `null` — not an OSC 5522 response, OR not a `type=read status=DATA`\n * message (e.g. it's a DONE notification, a write response, or some\n * other valid 5522 message that just isn't paste DATA). Discriminator\n * chain \"next-please.\"\n * - `throw ProtocolError` — DATA message identified (the dispatcher\n * committed to it being a paste data chunk) but the mime field or\n * payload is missing. Loud failure surfaces the malformed terminal\n * output to the dispatch boundary.\n *\n * Note: a `ProtocolError` from {@link parseOsc5522Response} also bubbles\n * through this function (we deliberately do not catch it here — let the\n * single dispatch boundary handle it consistently).\n */\nexport function parsePasteData(input: string): ClipboardEntry | null {\n const parsed = parseOsc5522Response(input)\n if (!parsed) return null\n if (parsed.type !== \"read\" || parsed.status !== \"DATA\") return null\n if (!parsed.mime || parsed.payload === undefined) {\n throw new ProtocolError({\n parser: \"parsePasteData\",\n input,\n reason: \"OSC 5522 type=read status=DATA missing required mime or payload field\",\n })\n }\n\n const mime = fromBase64(parsed.mime)\n const isText = mime.startsWith(\"text/\")\n\n if (isText) {\n return { mime, data: fromBase64(parsed.payload) }\n }\n return { mime, data: base64ToBytes(parsed.payload) }\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Create an advanced clipboard with OSC 5522 support.\n *\n * When `supported` is true, uses the full OSC 5522 protocol for\n * MIME-typed clipboard operations. When false (default), falls back\n * to OSC 52 for plain text only.\n *\n * @example\n * ```ts\n * const clipboard = createAdvancedClipboard({\n * write: (data) => process.stdout.write(data),\n * onData: (handler) => {\n * process.stdin.on(\"data\", (buf) => handler(buf.toString()))\n * return () => process.stdin.removeListener(\"data\", handler)\n * },\n * supported: true,\n * })\n *\n * // Copy with MIME types\n * clipboard.copy([\n * { mime: \"text/plain\", data: \"Hello\" },\n * { mime: \"text/html\", data: \"<b>Hello</b>\" },\n * ])\n *\n * // Subscribe to paste events\n * const unsub = clipboard.onPaste((entries) => {\n * for (const entry of entries) {\n * console.log(entry.mime, entry.data)\n * }\n * })\n *\n * clipboard.dispose()\n * ```\n */\nexport function createAdvancedClipboard(options: AdvancedClipboardOptions): AdvancedClipboard {\n const { write, onData, chunkSize = DEFAULT_CHUNK_SIZE, supported = false } = options\n\n const pasteHandlers = new Set<(entries: ClipboardEntry[]) => void>()\n let inputUnsub: (() => void) | undefined\n\n // Accumulate paste data entries from multiple DATA responses\n let pendingEntries: ClipboardEntry[] = []\n\n // Set up input listener for paste events when we have handlers\n function ensureInputListener(): void {\n if (inputUnsub) return\n\n inputUnsub = onData((data: string) => {\n if (pasteHandlers.size === 0) return\n\n // The parsers may throw ProtocolError on malformed OSC 5522 input\n // (prefix matched but missing ST terminator, or DATA message without\n // mime/payload). This is a dispatch boundary — log + continue, never\n // crash the app on protocol-format problems.\n // Bead: @km/silvery/15127-custom-protocol-implementation/protocol-loud-errors.\n let entry: ClipboardEntry | null = null\n try {\n entry = parsePasteData(data)\n } catch (err) {\n if (isProtocolError(err)) {\n log.debug?.(\n `parsePasteData flagged malformed OSC 5522 input: ${err.reason} (len=${err.inputLength})`,\n )\n } else {\n throw err\n }\n }\n if (entry) {\n pendingEntries.push(entry)\n }\n\n // Check for DONE signal — flush accumulated entries to handlers\n let parsed: ParsedResponse | null = null\n try {\n parsed = parseOsc5522Response(data)\n } catch (err) {\n if (isProtocolError(err)) {\n log.debug?.(\n `parseOsc5522Response flagged malformed input: ${err.reason} (len=${err.inputLength})`,\n )\n } else {\n throw err\n }\n }\n if (parsed?.type === \"read\" && parsed.status === \"DONE\") {\n if (pendingEntries.length > 0) {\n const entries = pendingEntries\n pendingEntries = []\n for (const handler of pasteHandlers) {\n handler(entries)\n }\n }\n }\n })\n }\n\n return {\n get supported(): boolean {\n return supported\n },\n\n copy(entries: ClipboardEntry[]): void {\n if (!supported) {\n // Fallback: find text/plain entry and use OSC 52\n const textEntry = entries.find((e) => e.mime === \"text/plain\")\n if (textEntry) {\n const text =\n typeof textEntry.data === \"string\"\n ? textEntry.data\n : fromBase64(bytesToBase64(textEntry.data))\n osc52Copy(write, text)\n }\n return\n }\n\n // OSC 5522 write protocol:\n // 1. Send write start\n write(osc5522(\"type=write\"))\n\n // 2. Send chunked data for each MIME entry\n for (const entry of entries) {\n for (const chunk of generateChunks(entry, chunkSize)) {\n write(chunk)\n }\n }\n },\n\n copyText(text: string): void {\n this.copy([{ mime: \"text/plain\", data: text }])\n },\n\n copyRich(text: string, html: string): void {\n this.copy([\n { mime: \"text/plain\", data: text },\n { mime: \"text/html\", data: html },\n ])\n },\n\n onPaste(handler: (entries: ClipboardEntry[]) => void): () => void {\n pasteHandlers.add(handler)\n ensureInputListener()\n\n return () => {\n pasteHandlers.delete(handler)\n if (pasteHandlers.size === 0 && inputUnsub) {\n inputUnsub()\n inputUnsub = undefined\n }\n }\n },\n\n dispose(): void {\n pasteHandlers.clear()\n pendingEntries = []\n if (inputUnsub) {\n inputUnsub()\n inputUnsub = undefined\n }\n },\n }\n}\n","/**\n * ANSI primitives — Term factory, styling, detection, underlines, hyperlinks.\n *\n * Pure ANSI utilities (detection, SGR codes, constants, types) live in\n * `@silvery/ansi` and are re-exported here for backwards compatibility.\n *\n * ```ts\n * import { createTerm } from '@silvery/ag-term'\n *\n * using term = createTerm()\n * term.red('error')\n * term.bold.green('success')\n *\n * term.console?.capture({ suppress: true })\n * term.console?.subscribe(() => {})\n * ```\n *\n * @module\n */\n\n// =============================================================================\n// Term API (NewWay)\n// =============================================================================\n\nexport { createTerm } from \"./term\"\nexport type { Term, StyleChain } from \"./term\"\n\nimport { createTerm as _createTerm } from \"./term\"\nimport type { Term } from \"./term\"\n\n/**\n * Default term instance for convenience, lazily initialized on first access.\n * This avoids running terminal detection (including spawnSync) at import time.\n * Use this for simple scripts. For apps, prefer createTerm() with `using`.\n *\n * @example\n * ```ts\n * import { term } from '@silvery/ag-term'\n *\n * console.log(term.green('success'))\n * if (term.caps.colorLevel) { ... }\n * ```\n */\nlet _lazyTerm: Term | undefined\nexport const term: Term = new Proxy({} as Term, {\n get(_target, prop, receiver) {\n if (!_lazyTerm) _lazyTerm = _createTerm()\n return Reflect.get(_lazyTerm, prop, receiver)\n },\n apply(_target, thisArg, args) {\n if (!_lazyTerm) _lazyTerm = _createTerm()\n return Reflect.apply(_lazyTerm as unknown as (...args: unknown[]) => unknown, thisArg, args)\n },\n has(_target, prop) {\n if (!_lazyTerm) _lazyTerm = _createTerm()\n return Reflect.has(_lazyTerm, prop)\n },\n})\n\nexport type { Output, OutputOptions } from \"../runtime/devices/output\"\nexport type { Console, ConsoleCaptureOptions, ConsoleStats } from \"../runtime/devices/console\"\nexport { createConsole } from \"../runtime/devices/console\"\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type {\n UnderlineStyle,\n RGB,\n ColorLevel,\n Color,\n AnsiColorName,\n StyleOptions,\n ConsoleMethod,\n ConsoleEntry,\n CreateTermOptions,\n TermEmulator,\n TermEmulatorBackend,\n TermScreen,\n} from \"./types\"\n\n// =============================================================================\n// Detection Functions\n// =============================================================================\n\n// Post km-silvery.plateau-delete-legacy-shims (H6): `detectColor` and\n// `detectTerminalCaps` deleted — use `createTerminalProfile()` (sync) or\n// `probeTerminalProfile()` (async with theme) instead.\nexport {\n defaultCaps,\n createTerminalProfile,\n probeTerminalProfile,\n createBgModeDetector,\n parseBgModeResponse,\n ENABLE_BG_MODE_REPORTING,\n DISABLE_BG_MODE_REPORTING,\n} from \"./detection\"\nexport type {\n TerminalCaps,\n TerminalProfile,\n ColorProvenance,\n TerminalProfileStdout,\n CreateTerminalProfileOptions,\n ProbeTerminalProfileOptions,\n BgModeDetector,\n BgModeDetectorOptions,\n BgMode,\n} from \"./detection\"\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\nexport { ANSI_REGEX, stripAnsi, displayLength } from \"./utils\"\n\n// =============================================================================\n// Underline — moved to Style methods in Phase 6 of the unicode plateau\n// (2026-04-23). Consumers use `term.curlyUnderline(x)` etc., or build a\n// headless style: `createStyle({ caps }).curlyUnderline(x)`.\n// =============================================================================\n\n// =============================================================================\n// Hyperlink Functions\n// =============================================================================\n\nexport { hyperlink } from \"./hyperlink\"\n\n// =============================================================================\n// ANSI Terminal Control Helpers\n// =============================================================================\n\nexport {\n enterAltScreen,\n leaveAltScreen,\n clearScreen,\n clearLine,\n cursorTo,\n cursorHome,\n cursorHide,\n cursorShow,\n cursorStyle,\n setTitle,\n enableMouse,\n disableMouse,\n enableBracketedPaste,\n disableBracketedPaste,\n enableSyncUpdate,\n disableSyncUpdate,\n setScrollRegion,\n resetScrollRegion,\n scrollUp,\n scrollDown,\n enableKittyKeyboard,\n disableKittyKeyboard,\n} from \"./ansi\"\n\n// =============================================================================\n// Background Override — Compose styled text inside Box with backgroundColor\n// =============================================================================\n\n/**\n * SGR code recognized by silvery to signal intentional bg override.\n * When text is wrapped with this, silvery won't warn/throw about chalk bg + silvery bg conflicts.\n * Exported for silvery to detect this marker in text content.\n */\nexport const BG_OVERRIDE_CODE = 9999\n\n/**\n * Compose styled text with an explicit background inside a Box that has its own\n * `backgroundColor`. This is the correct way to layer chalk/term background\n * colors on top of an silvery Box background.\n *\n * Without `bgOverride`, silvery throws (by default) when it detects both an ANSI\n * background in the text content AND a `backgroundColor` prop on an ancestor\n * Box, because the two conflict and produce visual artifacts (the ANSI bg\n * only covers the text, leaving gaps at line edges).\n *\n * `bgOverride` wraps the text with a private SGR marker that tells silvery\n * \"this background is intentional — don't throw.\" Use it when you need\n * pixel-precise background control within a styled container.\n *\n * @param text - Text containing ANSI background codes (e.g. from `chalk.bgBlack()`)\n * @returns The same text prefixed with a marker SGR code\n *\n * @example\n * ```tsx\n * import { bgOverride } from '@silvery/chalk'\n *\n * // Without bgOverride — silvery throws:\n * <Box backgroundColor=\"cyan\">\n * <Text>{chalk.bgBlack('text')}</Text> // Error!\n * </Box>\n *\n * // With bgOverride — explicitly allowed:\n * <Box backgroundColor=\"cyan\">\n * <Text>{bgOverride(chalk.bgBlack('text'))}</Text> // OK\n * </Box>\n *\n * // Also works with term styling:\n * <Box backgroundColor=\"$surface-bg\">\n * <Text>{bgOverride(term.bgRgb(30, 30, 30)('highlighted'))}</Text>\n * </Box>\n * ```\n */\nexport function bgOverride(text: string): string {\n return `\\x1b[${BG_OVERRIDE_CODE}m${text}`\n}\n\n// =============================================================================\n// Advanced Clipboard (OSC 5522)\n// =============================================================================\n\nexport {\n createAdvancedClipboard,\n parseOsc5522Response,\n parsePasteData,\n ENABLE_PASTE_EVENTS,\n DISABLE_PASTE_EVENTS,\n} from \"./advanced-clipboard\"\nexport type {\n AdvancedClipboard,\n AdvancedClipboardOptions,\n ClipboardEntry,\n} from \"./advanced-clipboard\"\n","/**\n * Unicode handling for Silvery.\n *\n * Uses Intl.Segmenter for proper grapheme cluster segmentation and\n * string-width for accurate terminal width calculation.\n *\n * Key concepts:\n * - Grapheme: A user-perceived character (may be multiple code points)\n * - Display width: How many terminal columns a character occupies (0, 1, or 2)\n * - Wide characters: CJK ideographs, emoji, etc. that take 2 columns\n * - Combining characters: Diacritics, emoji modifiers that take 0 columns\n */\n\nimport { BG_OVERRIDE_CODE } from \"./ansi/index\"\nimport sliceAnsi from \"slice-ansi\"\nimport stringWidth from \"string-width\"\nimport {\n type Cell,\n type Style,\n type TerminalBuffer,\n type UnderlineStyle,\n createMutableCell,\n styleToAnsiCodes,\n styleResetCodes,\n} from \"./buffer\"\nimport { isPrivateUseArea } from \"./text-sizing\"\n\n// Re-export for consumers of silvery\nexport { BG_OVERRIDE_CODE }\n\n// ============================================================================\n// Grapheme Segmentation\n// ============================================================================\n\n// Singleton Intl.Segmenter instance (stateless, reusable)\nconst segmenter = new Intl.Segmenter(undefined, { granularity: \"grapheme\" })\n\n// ============================================================================\n// Performance: LRU Cache for displayWidth\n// ============================================================================\n\n/**\n * Simple LRU cache for displayWidth results.\n * String width calculation is expensive (~8us for ASCII text),\n * but the same strings are often measured repeatedly.\n */\nclass DisplayWidthCache {\n private cache = new Map<string, number>()\n private maxSize: number\n\n constructor(maxSize = 1000) {\n this.maxSize = maxSize\n }\n\n get(text: string): number | undefined {\n const cached = this.cache.get(text)\n if (cached !== undefined) {\n // Move to end (most recently used)\n this.cache.delete(text)\n this.cache.set(text, cached)\n }\n return cached\n }\n\n set(text: string, width: number): void {\n // Evict oldest if at capacity\n if (this.cache.size >= this.maxSize) {\n const firstKey = this.cache.keys().next().value\n if (firstKey !== undefined) {\n this.cache.delete(firstKey)\n }\n }\n this.cache.set(text, width)\n }\n\n clear(): void {\n this.cache.clear()\n }\n}\n\n// Cache size: 10K entries should be enough for most TUI apps\n// Each entry is a string key + number value, ~100 bytes, so 10K = ~1MB\nconst displayWidthCache = new DisplayWidthCache(10000)\n\n// ============================================================================\n// Text Sizing Protocol (OSC 66) State\n// ============================================================================\n\n/**\n * Default text-presentation emoji width: true (modern terminal assumption).\n * Modern terminals (Ghostty, iTerm, Kitty) render text-presentation emoji\n * as 2-wide. Terminal.app renders them as 1-wide.\n */\nconst DEFAULT_TEXT_EMOJI_WIDE = true\n\n/**\n * Default text sizing mode: false.\n * When enabled via a Measurer, PUA characters are treated as 2-wide.\n */\nconst DEFAULT_TEXT_SIZING_ENABLED = false\n\n// ============================================================================\n// Scoped Measurer (pipeline execution context)\n// ============================================================================\n\n/**\n * Active measurer for the current pipeline execution.\n * Set by runWithMeasurer() during pipeline execution, restored after.\n * When null, module-level functions use the lazy default measurer.\n */\nlet _scopedMeasurer: Measurer | null = null\n\n/**\n * Run a function with a specific measurer as the active scope.\n * Module-level convenience functions (graphemeWidth, displayWidth, etc.)\n * will use this measurer instead of the lazy default for the duration.\n */\nexport function runWithMeasurer<T>(measurer: Measurer, fn: () => T): T {\n const prev = _scopedMeasurer\n _scopedMeasurer = measurer\n try {\n return fn()\n } finally {\n _scopedMeasurer = prev\n }\n}\n\n/**\n * Check if text sizing mode is currently enabled.\n * Returns the default (false) since globals have been removed.\n * Use measurer.textSizing for scoped queries.\n */\nexport function isTextSizingEnabled(): boolean {\n if (_scopedMeasurer) return _scopedMeasurer.textSizing\n return DEFAULT_TEXT_SIZING_ENABLED\n}\n\n// ============================================================================\n// Width Measurer (per-term instance, no globals)\n// ============================================================================\n\n/**\n * Width measurement functions scoped to specific terminal capabilities.\n * Created by createMeasurer() from TerminalCaps.\n */\nexport interface Measurer {\n readonly maybeWideEmojis: boolean\n readonly textSizing: boolean\n /** Height of one line in measurement units. Terminal: 1 (one cell row). Canvas pixel: e.g. 20. */\n readonly lineHeight: number\n displayWidth(text: string): number\n displayWidthAnsi(text: string): number\n graphemeWidth(grapheme: string): number\n wrapText(\n text: string,\n width: number,\n trim?: boolean,\n hard?: boolean,\n truncateAtomicOverflow?: boolean,\n ): string[]\n sliceByWidth(text: string, maxWidth: number): string\n sliceByWidthFromEnd(text: string, maxWidth: number): string\n}\n\n// eslint-disable-next-line no-control-regex -- OSC 8 is a terminal control sequence.\nconst OSC8_RE = /\\x1b\\]8;;([^\\x07\\x1b]*)(?:\\x07|\\x1b\\\\)/g\nconst OSC8_CLOSE = \"\\x1b]8;;\\x1b\\\\\"\n\nfunction stripOsc8ForSlice(text: string): string {\n return text.replace(OSC8_RE, \"\")\n}\n\ntype HyperlinkRange = Readonly<{ start: number; end: number; href: string }>\n\n/** Find hyperlink ranges in visible columns without asking slice-ansi to parse OSC. */\nfunction hyperlinkRanges(text: string, measure: (value: string) => number): HyperlinkRange[] {\n const ranges: HyperlinkRange[] = []\n let cursor = 0\n let column = 0\n let active: { href: string; start: number } | undefined\n\n for (const match of text.matchAll(OSC8_RE)) {\n const index = match.index\n column += measure(text.slice(cursor, index))\n if (active !== undefined) ranges.push({ ...active, end: column })\n const href = match[1]!\n active = href === \"\" ? undefined : { href, start: column }\n cursor = index + match[0].length\n }\n\n column += measure(text.slice(cursor))\n if (active !== undefined) ranges.push({ ...active, end: column })\n return ranges.filter((range) => range.end > range.start)\n}\n\n/** Slice ANSI text by columns while retaining OSC 8 on the visible linked runs. */\nfunction sliceAnsiWithHyperlinks(\n text: string,\n start: number,\n end: number | undefined,\n measure: (value: string) => number,\n): string {\n const cleaned = stripOsc8ForSlice(text)\n const from = Math.max(0, start)\n const to = Math.min(measure(cleaned), end ?? Number.POSITIVE_INFINITY)\n if (to <= from) return \"\"\n\n const ranges = hyperlinkRanges(text, measure)\n if (ranges.length === 0) return sliceAnsi(cleaned, from, to)\n\n const boundaries = new Set([from, to])\n for (const range of ranges) {\n if (range.end <= from || range.start >= to) continue\n boundaries.add(Math.max(from, range.start))\n boundaries.add(Math.min(to, range.end))\n }\n\n const points = [...boundaries].sort((left, right) => left - right)\n let result = \"\"\n for (let index = 0; index < points.length - 1; index++) {\n const left = points[index]!\n const right = points[index + 1]!\n const content = sliceAnsi(cleaned, left, right)\n const range = ranges.find((candidate) => candidate.start <= left && candidate.end > left)\n result += range === undefined ? content : `\\x1b]8;;${range.href}\\x1b\\\\${content}${OSC8_CLOSE}`\n }\n return result\n}\n\n/**\n * Create a width measurer scoped to terminal capabilities.\n * Each measurer has its own caches (no shared global state).\n */\nexport function createMeasurer(\n caps: { maybeWideEmojis?: boolean; textSizing?: boolean } = {},\n): Measurer {\n const maybeWideEmojis = caps.maybeWideEmojis ?? true\n const textSizing = caps.textSizing ?? false\n const cache = new DisplayWidthCache(10000)\n\n function measuredGraphemeWidth(grapheme: string): number {\n const width = stringWidth(grapheme)\n if (width !== 1) return width\n if (maybeWideEmojis && isTextPresentationEmoji(grapheme)) return 2\n if (textSizing) {\n const cp = grapheme.codePointAt(0)\n if (cp !== undefined && isPrivateUseArea(cp)) return 2\n }\n return width\n }\n\n function measuredDisplayWidth(text: string): number {\n const cached = cache.get(text)\n if (cached !== undefined) return cached\n\n let width: number\n const needsSlowPath =\n MAY_CONTAIN_TEXT_EMOJI.test(text) || (textSizing && MAY_CONTAIN_PUA.test(text))\n if (!needsSlowPath) {\n width = stringWidth(text)\n } else {\n const stripped = stripAnsi(text)\n width = 0\n for (const grapheme of splitGraphemes(stripped)) {\n width += measuredGraphemeWidth(grapheme)\n }\n }\n cache.set(text, width)\n return width\n }\n\n function measuredDisplayWidthAnsi(text: string): number {\n return measuredDisplayWidth(stripAnsi(text))\n }\n\n function measuredSliceByWidth(text: string, maxWidth: number): string {\n if (hasAnsi(text)) {\n return text.includes(\"\\x1b]8;;\")\n ? sliceAnsiWithHyperlinks(text, 0, maxWidth, measuredDisplayWidthAnsi)\n : sliceAnsi(text, 0, maxWidth)\n }\n let width = 0\n let result = \"\"\n const graphemes = splitGraphemes(text)\n for (const grapheme of graphemes) {\n const gWidth = measuredGraphemeWidth(grapheme)\n if (width + gWidth > maxWidth) break\n result += grapheme\n width += gWidth\n }\n return result\n }\n\n function measuredSliceByWidthFromEnd(text: string, maxWidth: number): string {\n const totalWidth = measuredDisplayWidthAnsi(text)\n if (totalWidth <= maxWidth) return text\n if (hasAnsi(text)) {\n const startIndex = totalWidth - maxWidth\n return text.includes(\"\\x1b]8;;\")\n ? sliceAnsiWithHyperlinks(text, startIndex, undefined, measuredDisplayWidthAnsi)\n : sliceAnsi(text, startIndex)\n }\n const graphemes = splitGraphemes(text)\n let width = 0\n let startIdx = graphemes.length\n for (let i = graphemes.length - 1; i >= 0; i--) {\n const gWidth = measuredGraphemeWidth(graphemes[i]!)\n if (width + gWidth > maxWidth) break\n width += gWidth\n startIdx = i\n }\n return graphemes.slice(startIdx).join(\"\")\n }\n\n function measuredWrapText(\n text: string,\n width: number,\n trim?: boolean,\n hard?: boolean,\n truncateAtomicOverflow?: boolean,\n ): string[] {\n return wrapTextWithMeasurer(\n text,\n width,\n measurer,\n trim ?? false,\n hard ?? false,\n true,\n truncateAtomicOverflow ?? false,\n )\n }\n\n const measurer: Measurer = {\n maybeWideEmojis,\n textSizing,\n lineHeight: 1,\n displayWidth: measuredDisplayWidth,\n displayWidthAnsi: measuredDisplayWidthAnsi,\n graphemeWidth: measuredGraphemeWidth,\n wrapText: measuredWrapText,\n sliceByWidth: measuredSliceByWidth,\n sliceByWidthFromEnd: measuredSliceByWidthFromEnd,\n }\n\n return measurer\n}\n\n// ============================================================================\n// Default Measurer (lazy singleton for module-level convenience functions)\n// ============================================================================\n\nlet _defaultMeasurer: Measurer | undefined\n\n/** Get the default measurer (lazy init, uses default caps). */\nfunction getDefaultMeasurer(): Measurer {\n if (!_defaultMeasurer) {\n _defaultMeasurer = createMeasurer()\n }\n return _defaultMeasurer\n}\n\n/**\n * Split a string into grapheme clusters.\n * Each grapheme is a user-perceived character that may consist of\n * multiple Unicode code points.\n *\n * Examples:\n * - \"cafe\\u0301\" (café with combining accent) -> [\"c\", \"a\", \"f\", \"e\\u0301\"]\n * - \"👨‍👩‍👧\" (family emoji) -> [\"👨‍👩‍👧\"]\n * - \"한국어\" -> [\"한\", \"국\", \"어\"]\n */\nexport function splitGraphemes(text: string): string[] {\n return [...segmenter.segment(text)].map((s) => s.segment)\n}\n\n/**\n * Count the number of graphemes in a string.\n */\nexport function graphemeCount(text: string): number {\n let count = 0\n for (const _ of segmenter.segment(text)) count++\n return count\n}\n\n// ============================================================================\n// Emoji Width Correction\n// ============================================================================\n\n/**\n * Regex for Extended_Pictographic characters that have default text presentation.\n * These characters are reported as width 1 by string-width (per Unicode EAW),\n * but most modern terminals render them as 2 columns wide using emoji glyphs.\n *\n * Specifically: Extended_Pictographic AND NOT Emoji_Presentation.\n * Examples: ⚠ (U+26A0), ☑ (U+2611), ✈ (U+2708), ❤ (U+2764)\n * Counter-examples: 📁 (U+1F4C1) has Emoji_Presentation so string-width is correct.\n *\n * Uses the RGI_Emoji regex with VS16 to detect characters that support\n * emoji presentation -- if char+VS16 is RGI emoji, the terminal likely\n * renders the bare char as 2-wide.\n */\nconst TEXT_PRESENTATION_EMOJI_REGEX = /^\\p{Extended_Pictographic}$/u\nconst EMOJI_PRESENTATION_REGEX = /^\\p{Emoji_Presentation}$/u\nconst RGI_EMOJI_REGEX = /^\\p{RGI_Emoji}$/v\n\n/**\n * Cache for isTextPresentationEmoji results.\n * Maps first code point to boolean.\n */\nconst textPresentationEmojiCache = new Map<number, boolean>()\n\n/**\n * Check if a grapheme is a text-presentation emoji that terminals render wide.\n *\n * Returns true for characters that are Extended_Pictographic, do NOT have\n * the Emoji_Presentation property, but become RGI emoji when followed by\n * VS16 (U+FE0F). These characters are rendered as 2 columns in most\n * modern terminals despite string-width reporting width 1.\n */\nexport function isTextPresentationEmoji(grapheme: string): boolean {\n const cp = grapheme.codePointAt(0)\n if (cp === undefined) return false\n\n // Multi-codepoint graphemes (with VS16, ZWJ, etc.) are already handled\n // correctly by string-width. Only check single-codepoint graphemes.\n //\n // Important: this gate must run before the codepoint cache. U+FE0E\n // text-presentation clusters such as \"⏸︎\" share the same first codepoint\n // as bare \"⏸\", but have different width semantics. Caching by base\n // codepoint for multi-codepoint clusters makes width depend on call order.\n const singleChar = String.fromCodePoint(cp)\n if (singleChar.length !== grapheme.length) {\n return false\n }\n\n // Check cache for single-codepoint graphemes only.\n const cached = textPresentationEmojiCache.get(cp)\n if (cached !== undefined) return cached\n\n // Must be Extended_Pictographic but NOT Emoji_Presentation\n const isExtPict = TEXT_PRESENTATION_EMOJI_REGEX.test(grapheme)\n const isEmojiPres = EMOJI_PRESENTATION_REGEX.test(grapheme)\n if (!isExtPict || isEmojiPres) {\n textPresentationEmojiCache.set(cp, false)\n return false\n }\n\n // Check if adding VS16 makes it an RGI emoji sequence\n const withVs16 = grapheme + \"\\uFE0F\"\n const result = RGI_EMOJI_REGEX.test(withVs16)\n textPresentationEmojiCache.set(cp, result)\n return result\n}\n\n// ============================================================================\n// Private Use Area (PUA) — Nerdfont / Powerline Icons\n// ============================================================================\n\n/**\n * Append VS16 (U+FE0F) to emoji characters that have default text presentation.\n *\n * Use this to normalize icon characters for consistent terminal rendering.\n * Characters that already have emoji presentation or VS16 are returned unchanged.\n *\n * @example\n * ```ts\n * ensureEmojiPresentation('⚠') // '⚠\\uFE0F' (⚠️)\n * ensureEmojiPresentation('☑') // '☑\\uFE0F' (☑️)\n * ensureEmojiPresentation('☐') // '☐' (unchanged, not an emoji)\n * ensureEmojiPresentation('📁') // '📁' (unchanged, already emoji presentation)\n * ```\n */\nexport function ensureEmojiPresentation(char: string): string {\n if (char.includes(\"\\uFE0F\")) return char // Already has VS16\n if (isTextPresentationEmoji(char)) return char + \"\\uFE0F\"\n return char\n}\n\n// ============================================================================\n// Display Width Calculation\n// ============================================================================\n\n/**\n * Regex to detect strings that MAY contain text-presentation emoji.\n * Used as a fast pre-check before the more expensive grapheme-based calculation.\n * Covers the Unicode blocks where Extended_Pictographic characters live:\n * - Miscellaneous Technical (U+2300-U+23FF)\n * - Miscellaneous Symbols (U+2600-U+26FF)\n * - Dingbats (U+2700-U+27BF)\n * - Miscellaneous Symbols and Arrows (U+2B00-U+2BFF)\n * - Other scattered ranges\n */\nconst MAY_CONTAIN_TEXT_EMOJI =\n /[\\u203C\\u2049\\u2122\\u2139\\u2194-\\u2199\\u21A9\\u21AA\\u2328\\u23CF\\u23ED-\\u23EF\\u23F1\\u23F2\\u23F8-\\u23FA\\u25AA\\u25AB\\u25B6\\u25C0\\u25FB-\\u25FE\\u2600-\\u2604\\u260E\\u2611\\u2614\\u2615\\u2618\\u261D\\u2620\\u2622\\u2623\\u2626\\u262A\\u262E\\u262F\\u2638-\\u263A\\u2640\\u2642\\u2648-\\u2653\\u265F\\u2660\\u2663\\u2665\\u2666\\u2668\\u267B\\u267E\\u267F\\u2692-\\u2697\\u2699\\u269B\\u269C\\u26A0\\u26A1\\u26A7\\u26AA\\u26AB\\u26B0\\u26B1\\u26BD\\u26BE\\u26C4\\u26C5\\u26C8\\u26CE\\u26CF\\u26D1\\u26D3\\u26D4\\u26E9\\u26EA\\u26F0-\\u26F5\\u26F7-\\u26FA\\u26FD\\u2702\\u2705\\u2708-\\u270D\\u270F\\u2712\\u2714\\u2716\\u271D\\u2721\\u2728\\u2733\\u2734\\u2744\\u2747\\u274C\\u274E\\u2753-\\u2755\\u2757\\u2763\\u2764\\u2795-\\u2797\\u27A1\\u27B0\\u27BF\\u2934\\u2935\\u2B05-\\u2B07\\u2B1B\\u2B1C\\u2B50\\u2B55\\u3030\\u303D\\u3297\\u3299]/\n\n/**\n * Fast pre-check regex for BMP Private Use Area characters (U+E000-U+F8FF).\n * Used to gate the slow grapheme-by-grapheme path when text sizing is enabled.\n */\nconst MAY_CONTAIN_PUA = /[\\uE000-\\uF8FF]/\n\n/**\n * Get the display width of a string (number of terminal columns).\n * Uses string-width which handles:\n * - Wide characters (CJK) -> 2 columns\n * - Regular ASCII -> 1 column\n * - Zero-width characters (combining, ZWJ) -> 0 columns\n * - Emoji -> varies (1 or 2)\n * - ANSI escape sequences -> 0 columns (stripped)\n *\n * Corrects string-width for text-presentation emoji characters\n * (e.g., ⚠ U+26A0) that terminals render as 2 columns wide.\n *\n * Results are cached for performance.\n */\nexport function displayWidth(text: string): number {\n if (_scopedMeasurer) return _scopedMeasurer.displayWidth(text)\n // Check cache first\n const cached = displayWidthCache.get(text)\n if (cached !== undefined) {\n return cached\n }\n\n let width: number\n // Fast path: if text cannot contain text-presentation emoji, use string-width directly.\n // Default measurer does not enable text sizing, so PUA check uses the constant default.\n const needsSlowPath =\n MAY_CONTAIN_TEXT_EMOJI.test(text) || (DEFAULT_TEXT_SIZING_ENABLED && MAY_CONTAIN_PUA.test(text))\n if (!needsSlowPath) {\n width = stringWidth(text)\n } else {\n // Slow path: strip ANSI codes first (they'd inflate the grapheme count),\n // then split into graphemes and sum corrected widths\n const stripped = stripAnsi(text)\n width = 0\n for (const grapheme of splitGraphemes(stripped)) {\n width += graphemeWidth(grapheme)\n }\n }\n\n displayWidthCache.set(text, width)\n return width\n}\n\n/**\n * Get the display width of a single grapheme.\n *\n * Overrides string-width for characters that are Extended_Pictographic with\n * default text presentation. These characters (e.g., ⚠ U+26A0, ☑ U+2611)\n * are reported as width 1 by string-width (per Unicode EAW tables), but most\n * modern terminals render them as 2 columns wide using emoji glyphs.\n *\n * The mismatch causes text after these characters to be placed at the wrong\n * column, leading to truncation or overlap.\n */\nexport function graphemeWidth(grapheme: string): number {\n if (_scopedMeasurer) return _scopedMeasurer.graphemeWidth(grapheme)\n const width = stringWidth(grapheme)\n // If string-width already says 2 (or 0), trust it\n if (width !== 1) return width\n // Check if this is a text-presentation emoji that terminals render wide.\n // Uses DEFAULT_TEXT_EMOJI_WIDE (true) — assumes modern terminal.\n if (DEFAULT_TEXT_EMOJI_WIDE && isTextPresentationEmoji(grapheme)) return 2\n // Default module-level function does not enable text sizing.\n // Scoped measurers handle PUA via their own graphemeWidth.\n if (DEFAULT_TEXT_SIZING_ENABLED) {\n const cp = grapheme.codePointAt(0)\n if (cp !== undefined && isPrivateUseArea(cp)) return 2\n }\n return width\n}\n\n/**\n * Check if a grapheme is a wide character (takes 2 columns).\n */\nexport function isWideGrapheme(grapheme: string): boolean {\n return graphemeWidth(grapheme) === 2\n}\n\n/**\n * Check if a grapheme is zero-width (combining character, ZWJ, etc.).\n */\nexport function isZeroWidthGrapheme(grapheme: string): boolean {\n return stringWidth(grapheme) === 0\n}\n\n// ============================================================================\n// Text Manipulation\n// ============================================================================\n\n/**\n * Truncate a string to fit within a given display width.\n * Handles wide characters and ANSI escape sequences (including OSC 8 hyperlinks) correctly.\n *\n * @param text - The text to truncate (may contain ANSI escape sequences)\n * @param maxWidth - Maximum display width\n * @param ellipsis - Ellipsis to append if truncated (default: \"...\")\n * @returns Truncated string\n */\nexport function truncateText(\n text: string,\n maxWidth: number,\n ellipsis = \"\\u2026\", // Unicode ellipsis (single character)\n): string {\n const textWidth = displayWidth(text)\n\n // No truncation needed\n if (textWidth <= maxWidth) {\n return text\n }\n\n const ellipsisWidth = displayWidth(ellipsis)\n const targetWidth = maxWidth - ellipsisWidth\n\n if (targetWidth <= 0) {\n // Not enough space for even the ellipsis\n return maxWidth > 0 ? ellipsis.slice(0, maxWidth) : \"\"\n }\n\n // Use ANSI-aware grapheme splitting when text contains escape sequences\n // (including OSC 8 hyperlinks) to avoid counting escape bytes as visible width.\n const graphemes = hasAnsi(text) ? splitGraphemesAnsiAware(text) : splitGraphemes(text)\n let result = \"\"\n let currentWidth = 0\n\n for (const grapheme of graphemes) {\n const gWidth = graphemeWidth(grapheme)\n if (currentWidth + gWidth > targetWidth) {\n break\n }\n result += grapheme\n currentWidth += gWidth\n }\n\n return result + ellipsis\n}\n\n/**\n * Pad a string to a given display width.\n *\n * @param text - The text to pad\n * @param width - Target display width\n * @param align - Alignment: 'left', 'right', or 'center'\n * @param padChar - Character to use for padding (default: space)\n * @returns Padded string\n */\nexport function padText(\n text: string,\n width: number,\n align: \"left\" | \"right\" | \"center\" = \"left\",\n padChar = \" \",\n): string {\n const textWidth = displayWidth(text)\n const padWidth = width - textWidth\n\n if (padWidth <= 0) {\n return text\n }\n\n const padCharWidth = displayWidth(padChar)\n if (padCharWidth === 0) {\n // Can't pad with zero-width characters\n return text\n }\n\n // Calculate number of pad characters needed\n const padCount = Math.floor(padWidth / padCharWidth)\n\n switch (align) {\n case \"left\":\n return text + padChar.repeat(padCount)\n case \"right\":\n return padChar.repeat(padCount) + text\n case \"center\": {\n const leftPad = Math.floor(padCount / 2)\n const rightPad = padCount - leftPad\n return padChar.repeat(leftPad) + text + padChar.repeat(rightPad)\n }\n }\n}\n\n/**\n * Constrain text to width and height limits.\n * Combines wrapping and truncation to fit text in a box.\n *\n * @param text - Text to constrain (may contain ANSI codes)\n * @param width - Maximum display width per line\n * @param maxLines - Maximum number of lines\n * @param pad - If true, pad lines to full width\n * @param ellipsis - Custom ellipsis character (default: \"…\")\n * @returns Object with lines array and truncated flag\n */\nexport function constrainText(\n text: string,\n width: number,\n maxLines: number,\n pad = false,\n ellipsis = \"…\",\n): { lines: string[]; truncated: boolean } {\n const allLines = wrapText(text, width)\n const truncated = allLines.length > maxLines\n let lines = allLines.slice(0, maxLines)\n\n if (truncated && lines.length > 0) {\n const lastIdx = lines.length - 1\n const lastLine = lines[lastIdx]\n if (lastLine) {\n const ellipsisLen = displayWidth(ellipsis)\n const lastLineLen = displayWidth(lastLine)\n if (lastLineLen + ellipsisLen <= width) {\n lines[lastIdx] = lastLine + ellipsis\n } else {\n lines[lastIdx] = truncateText(lastLine, width, ellipsis)\n }\n }\n }\n\n if (pad) {\n lines = lines.map((line) => padText(line, width))\n }\n\n return { lines, truncated }\n}\n\n/**\n * Check if a grapheme is a hard word-boundary character — space or tab.\n *\n * Hyphen-minus (`-`) is intentionally NOT a boundary: CSS, browsers, and\n * `@chenglou/pretext` all keep hyphenated tokens together (`cmd-hover` stays\n * one word, not `cmd-` + `hover`). When a hyphenated token is wider than its\n * container the wrap algorithm falls back to the atomic-overflow path\n * (analogous to CSS `overflow-wrap: anywhere`), not a hyphen split. See\n * `@km/silvery/wrap-stop-breaking-at-hyphen` for the rationale.\n */\nexport function isWordBoundary(grapheme: string): boolean {\n return grapheme === \" \" || grapheme === \"\\t\"\n}\n\n/**\n * Check if a grapheme is a *soft* break point inside a token — a separator\n * that paths and identifiers commonly use, so the wrap algorithm can split\n * `.claude/skills/{claim,do}/SKILL.md` at a `/` (or `,`) instead of either\n * letting it overflow or hard-character-wrapping mid-word.\n *\n * Soft break points are SECONDARY: the wrap algorithm prefers true word\n * boundaries (`isWordBoundary`) and only falls back to soft breaks when no\n * hard boundary fits on the current line. Breaking AFTER the separator\n * keeps the boundary character with its left context — `path/` on one\n * line, `to` on the next — which matches how readers visually parse paths.\n *\n * The set is deliberately conservative:\n * - filesystem path separators (`/`, `\\`)\n * - namespace / scope separators (`.`, `:`)\n * - identifier joins (`_`)\n * - list separators (`,`)\n *\n * Brackets/parens (`{ } [ ] ( )`) are EXCLUDED because they pair —\n * breaking after `(` would orphan the opening delimiter from its\n * matching close. The CSS spec does the same. Operator characters\n * (`= ? & |`) are also excluded — they're either part of structural\n * punctuation (URL query strings, expressions) or already handled by\n * `isBreakBeforeOperatorWith` for orphan suppression.\n */\nexport function isSoftBreakPoint(grapheme: string): boolean {\n return (\n grapheme === \"/\" ||\n grapheme === \"\\\\\" ||\n grapheme === \".\" ||\n grapheme === \"_\" ||\n grapheme === \":\" ||\n grapheme === \",\"\n )\n}\n\n/**\n * Look ahead from a space to check if the next word is a single-character\n * operator (like +, =, *, /, etc.) followed by another space. Breaking before\n * such operators looks bad — e.g. \"$12k\\n+ $400\" — so we suppress the break\n * point to keep the operator with its left operand.\n *\n * Accepts an explicit graphemeWidth function so it works with both the\n * module-level default and per-measurer instances.\n */\nfunction isBreakBeforeOperatorWith(\n graphemes: string[],\n spaceIndex: number,\n gWidthFn: (g: string) => number,\n): boolean {\n // Look for pattern: [current space] [operator] [space]\n // spaceIndex is the index of the current space in the graphemes array\n let j = spaceIndex + 1\n // Skip any zero-width characters (ANSI escapes)\n while (j < graphemes.length && gWidthFn(graphemes[j]!) === 0) j++\n if (j >= graphemes.length) return false\n const nextChar = graphemes[j]!\n // Must be a single visible character that is not alphanumeric or space\n if (gWidthFn(nextChar) !== 1) return false\n if (/^[a-zA-Z0-9\\s]$/.test(nextChar)) return false\n // Check that it's followed by a space (it's an infix operator, not a prefix)\n let k = j + 1\n while (k < graphemes.length && gWidthFn(graphemes[k]!) === 0) k++\n if (k >= graphemes.length) return false\n return graphemes[k] === \" \"\n}\n\n/**\n * Check if a grapheme can break anywhere (CJK characters).\n * CJK text doesn't use spaces between words, so any character boundary is valid.\n */\nexport function canBreakAnywhere(grapheme: string): boolean {\n return isCJK(grapheme)\n}\n\n// ANSI CSI pattern: ESC [ (params) (letter)\nconst ANSI_CSI_RE = /^\\x1b\\[[0-9;:?]*[A-Za-z]/\n// ANSI OSC pattern: ESC ] ... (BEL or ST)\nconst ANSI_OSC_RE = /^\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)/\n// Single-char escape: ESC followed by one letter\nconst ANSI_SINGLE_RE = /^\\x1b[DME78(B]/\n\n/**\n * Split text into graphemes, keeping ANSI escape sequences as single zero-width tokens.\n * Without this, `splitGraphemes` would split `\\x1b[38;5;1m` into individual characters\n * like `[`, `3`, `8`, `;`, etc., each consuming display width.\n */\nexport function splitGraphemesAnsiAware(text: string): string[] {\n if (!hasAnsi(text)) {\n return splitGraphemes(text)\n }\n\n const result: string[] = []\n let pos = 0\n\n while (pos < text.length) {\n if (text[pos] === \"\\x1b\") {\n // Try to match an ANSI sequence starting at pos\n const remaining = text.slice(pos)\n const csi = remaining.match(ANSI_CSI_RE)\n if (csi) {\n result.push(csi[0])\n pos += csi[0].length\n continue\n }\n const osc = remaining.match(ANSI_OSC_RE)\n if (osc) {\n result.push(osc[0])\n pos += osc[0].length\n continue\n }\n const single = remaining.match(ANSI_SINGLE_RE)\n if (single) {\n result.push(single[0])\n pos += single[0].length\n continue\n }\n }\n\n // Find the next ESC or end of string\n const nextEsc = text.indexOf(\"\\x1b\", pos + 1)\n const chunk = nextEsc === -1 ? text.slice(pos) : text.slice(pos, nextEsc)\n\n // Split this non-ANSI chunk into graphemes\n for (const g of splitGraphemes(chunk)) {\n result.push(g)\n }\n pos += chunk.length\n }\n\n return result\n}\n\n/**\n * Wrap text to fit within a given width.\n *\n * Implements word-boundary wrapping:\n * 1. Breaks at word boundaries (spaces, hyphens) when possible\n * 2. Falls back to character wrap only when necessary (very long words)\n * 3. Handles CJK text properly (can break anywhere since CJK has no word spaces)\n * 4. Preserves intentional line breaks\n *\n * @param text - The text to wrap (may contain ANSI escape sequences)\n * @param width - Maximum display width per line\n * @param preserveNewlines - Whether to preserve existing newlines\n * @param trim - Trim trailing spaces on broken lines and skip leading spaces on continuation lines (useful for rendering)\n * @param truncateAtomicOverflow - When true, an unbreakable token wider than\n * `width` (no soft-break separator) ends with a Unicode `…` ellipsis on\n * the offending line and the rest of the token is dropped, instead of\n * character-wrapping. Implements the `wrap=\"wrap-truncate\"` mode —\n * CSS-equivalent `overflow-wrap: break-word` + `text-overflow: ellipsis`.\n * Default `false` preserves existing character-wrap fallback (`wrap=\"wrap\"`).\n * @returns Array of wrapped lines\n */\nexport function wrapText(\n text: string,\n width: number,\n preserveNewlines = true,\n trim = false,\n truncateAtomicOverflow = false,\n): string[] {\n // When a scoped measurer with its own wrapText is active (e.g., Pretext),\n // delegate to it directly — it uses word-level measurement (layoutWithLines)\n // rather than character-by-character accumulation via wrapTextWithMeasurer.\n if (_scopedMeasurer?.wrapText) {\n return _scopedMeasurer.wrapText(text, width, trim, false, truncateAtomicOverflow)\n }\n return wrapTextWithMeasurer(\n text,\n width,\n _scopedMeasurer ?? undefined,\n trim,\n false,\n preserveNewlines,\n truncateAtomicOverflow,\n )\n}\n\n/** Get the active line height (from scoped measurer if set, else 1). */\nexport function getActiveLineHeight(): number {\n return _scopedMeasurer?.lineHeight ?? 1\n}\n\n/**\n * One per-visual-line slice produced by `wrapTextWithOffsets`. The offsets\n * are character indices into the *original* input text, so callers can\n * align selection ranges, syntax highlighting, or any per-cell decoration\n * onto the wrapped layout without re-running the wrap algorithm.\n *\n * Convention: `endOffset` is exclusive — `text === source.slice(start, end)`\n * for hard wraps. Soft wraps that swallow a whitespace boundary keep the\n * boundary char inside the offset window (so consecutive `endOffset`s\n * advance past the swallowed character) but omit it from `text` — see the\n * comment block in `wrapTextWithOffsets` for rationale.\n */\nexport interface WrapTextSlice {\n readonly text: string\n readonly startOffset: number\n readonly endOffset: number\n}\n\n/**\n * Wrap text and return offset-tagged slices — one per visual line.\n *\n * This is the offset-aware sibling of `wrapText`. It implements the same\n * word-boundary algorithm as `wrapTextWithMeasurer` but tracks grapheme\n * indices into the source so the geometry layer (`computeSelectionFragments`\n * in `@silvery/ag`) can project a selection range onto each visual row\n * without ambiguity.\n *\n * **No newline preservation, no trim**: the contract is \"one paragraph in,\n * one slice list out.\" Callers split on `\\n` themselves and walk\n * paragraphs through this helper. That keeps the offset mapping simple —\n * paragraph-relative slices translate to source offsets via a single\n * paragraph-start addition. The geometry layer needs neither trim nor\n * newline handling; existing `wrapText` keeps both for the rendering path.\n *\n * **Boundary-character handling**: when a soft wrap drops a boundary\n * (space/hyphen) — e.g. `\"hello world\"` at width 5 produces visible lines\n * `[\"hello\", \"world\"]` — the next slice's `startOffset` advances past the\n * dropped char so visual-line indices remain in source-position lockstep.\n * This means `text.length <= endOffset - startOffset` for soft-wrapped\n * lines (selection-range arithmetic uses the offsets, not the slice\n * length).\n *\n * Returns `[]` for empty text. Returns a single-slice passthrough for text\n * that fits in `width` without wrapping. Width `<= 0` is treated as\n * \"don't wrap\" (single passthrough slice) — callers receive geometry they\n * can project onto a clipped/zero-width content rect without a divide-by-zero.\n */\nexport function wrapTextWithOffsets(text: string, width: number): WrapTextSlice[] {\n if (text.length === 0) return []\n if (width <= 0) {\n // Edge case: caller gave us a non-positive width (clipped content rect,\n // pre-layout). Don't wrap; emit one passthrough so downstream offset\n // math doesn't have to special-case empty arrays.\n return [{ text, startOffset: 0, endOffset: text.length }]\n }\n\n const measurer = _scopedMeasurer\n const gWidthFn = measurer ? measurer.graphemeWidth.bind(measurer) : graphemeWidth\n\n // ANSI-aware grapheme split — zero-width tokens flow through with the\n // previous visible grapheme so they don't disturb the offset count.\n const graphemes = splitGraphemesAnsiAware(text)\n\n // Pre-compute each grapheme's source offset (start index in `text`).\n // `splitGraphemesAnsiAware` preserves all bytes, so offsets are simply\n // running cumulative lengths.\n const grapheneOffsets: number[] = new Array(graphemes.length)\n {\n let pos = 0\n for (let i = 0; i < graphemes.length; i++) {\n grapheneOffsets[i] = pos\n pos += graphemes[i]!.length\n }\n }\n\n const out: WrapTextSlice[] = []\n let currentLine = \"\"\n let currentWidth = 0\n let lineStartGraphemeIdx = 0\n\n // Last word-boundary break point inside the current line.\n let lastBreakIndex = -1 // char position within currentLine\n let lastBreakWidth = 0\n let lastBreakGraphemeIdx = -1 // index into `graphemes` AFTER the break\n\n // Last soft (intra-token) break point — used only when no hard break\n // fits. See `isSoftBreakPoint` for the rationale; same triple shape as\n // the hard-break tracker but stays unused unless we'd otherwise\n // character-wrap mid-token.\n let lastSoftBreakIndex = -1\n let lastSoftBreakWidth = 0\n let lastSoftBreakGraphemeIdx = -1\n\n function pushLine(endGraphemeIdx: number): void {\n // endGraphemeIdx is the FIRST grapheme that does NOT belong to this\n // line. The slice covers source [lineStartOffset, breakOffset).\n const startOffset = grapheneOffsets[lineStartGraphemeIdx] ?? text.length\n const endOffset =\n endGraphemeIdx < graphemes.length\n ? (grapheneOffsets[endGraphemeIdx] ?? text.length)\n : text.length\n out.push({ text: currentLine, startOffset, endOffset })\n }\n\n for (let i = 0; i < graphemes.length; i++) {\n const grapheme = graphemes[i]!\n const gWidth = gWidthFn(grapheme)\n\n // Zero-width: append without consuming visible width. Boundary\n // tracking is unaffected — these chars belong to the surrounding\n // grapheme cluster.\n if (gWidth === 0) {\n currentLine += grapheme\n continue\n }\n\n if (isWordBoundary(grapheme)) {\n // Boundary char: include if it fits, mark as break point.\n if (currentWidth + gWidth <= width) {\n currentLine += grapheme\n currentWidth += gWidth\n if (grapheme !== \" \" || !isBreakBeforeOperatorWith(graphemes, i, gWidthFn)) {\n lastBreakIndex = currentLine.length\n lastBreakWidth = currentWidth\n lastBreakGraphemeIdx = i + 1\n }\n continue\n }\n // Boundary char doesn't fit: break BEFORE it; the boundary is\n // swallowed (consumed as the break). The next slice's startOffset\n // jumps past it, so source coverage stays monotone.\n if (currentLine) {\n pushLine(i) // line ends just before the boundary\n }\n currentLine = \"\"\n currentWidth = 0\n // Skip the boundary character itself — start the next line from i+1.\n lineStartGraphemeIdx = i + 1\n lastBreakIndex = -1\n lastBreakWidth = 0\n lastBreakGraphemeIdx = -1\n lastSoftBreakIndex = -1\n lastSoftBreakWidth = 0\n lastSoftBreakGraphemeIdx = -1\n continue\n } else if (canBreakAnywhere(grapheme)) {\n // CJK: can break before this character. Mark and fall through to\n // the overflow check (CJK chars themselves are visible).\n lastBreakIndex = currentLine.length\n lastBreakWidth = currentWidth\n lastBreakGraphemeIdx = i\n }\n\n if (currentWidth + gWidth > width) {\n if (lastBreakIndex > 0 && lastBreakGraphemeIdx >= 0) {\n // Word-boundary wrap: rewind to last break point.\n const savedLine = currentLine\n // `lastBreakIndex` points just past the boundary grapheme, so the\n // slice INCLUDES the trailing word-boundary char (typically a\n // space). The next slice's `startOffset` already skips that\n // boundary, but the boundary still lives in `text`. Drop trailing\n // whitespace from the text so callers see clean line content\n // (parity with `wrapTextWithMeasurer`'s `trimEnd()`). Non-space\n // boundaries (hyphen, etc.) are not whitespace and pass through\n // untouched. @km/tui/softwrap-leading-space-on-wrap.\n currentLine = savedLine.slice(0, lastBreakIndex).trimEnd()\n pushLine(lastBreakGraphemeIdx)\n // Reset; rewind the loop counter so the rewind-target grapheme is\n // re-processed at the start of the next line.\n lineStartGraphemeIdx = lastBreakGraphemeIdx\n i = lastBreakGraphemeIdx - 1\n currentLine = \"\"\n currentWidth = 0\n lastBreakIndex = -1\n lastBreakWidth = 0\n lastBreakGraphemeIdx = -1\n lastSoftBreakIndex = -1\n lastSoftBreakWidth = 0\n lastSoftBreakGraphemeIdx = -1\n } else if (lastSoftBreakIndex > 0 && lastSoftBreakGraphemeIdx >= 0) {\n // Soft break inside a token (path/identifier separator): rewind\n // to AFTER the separator so `path/to` becomes `path/` + `to`.\n const savedLine = currentLine\n currentLine = savedLine.slice(0, lastSoftBreakIndex)\n pushLine(lastSoftBreakGraphemeIdx)\n lineStartGraphemeIdx = lastSoftBreakGraphemeIdx\n i = lastSoftBreakGraphemeIdx - 1\n currentLine = \"\"\n currentWidth = 0\n lastBreakIndex = -1\n lastBreakWidth = 0\n lastBreakGraphemeIdx = -1\n lastSoftBreakIndex = -1\n lastSoftBreakWidth = 0\n lastSoftBreakGraphemeIdx = -1\n } else {\n // No break point: hard char wrap. Push the current line, start a\n // new one with this grapheme.\n if (currentLine) {\n pushLine(i)\n }\n lineStartGraphemeIdx = i\n currentLine = grapheme\n currentWidth = gWidth\n lastBreakIndex = -1\n lastBreakWidth = 0\n lastBreakGraphemeIdx = -1\n lastSoftBreakIndex = -1\n lastSoftBreakWidth = 0\n lastSoftBreakGraphemeIdx = -1\n }\n } else {\n currentLine += grapheme\n currentWidth += gWidth\n // Track soft (intra-token) break points. Only meaningful if there\n // is already content before this separator on the line.\n if (currentLine.length > 1 && isSoftBreakPoint(grapheme)) {\n lastSoftBreakIndex = currentLine.length\n lastSoftBreakWidth = currentWidth\n lastSoftBreakGraphemeIdx = i + 1\n }\n }\n }\n // Suppress unused-variable warning — symmetric bookkeeping for the\n // soft-break triple, kept for parity with the hard-break tracker.\n void lastSoftBreakWidth\n\n // Trailing content: push as the final line.\n if (currentLine || lineStartGraphemeIdx < graphemes.length) {\n pushLine(graphemes.length)\n }\n\n // Single-slice passthrough? Tell the caller \"no wrap happened\" by\n // returning empty — matches the WrapMeasurer contract documented in\n // `@silvery/ag/wrap-measurer`. The caller (geometry layer) synthesizes a\n // single passthrough slice from the source itself, avoiding an\n // allocation for the (very common) \"fits in one line\" case.\n if (out.length === 1 && out[0]!.startOffset === 0 && out[0]!.endOffset === text.length) {\n return []\n }\n\n // Suppress unused-variable warning — `lastBreakWidth` is part of the\n // bookkeeping triple that mirrors `wrapTextWithMeasurer`, kept for\n // symmetry / future re-use even though this offset-only variant doesn't\n // read it back.\n void lastBreakWidth\n\n return out\n}\n\n/**\n * Wrap text using an explicit measurer for grapheme width calculations.\n * When measurer is undefined, falls back to the module-level graphemeWidth.\n */\nexport function wrapTextWithMeasurer(\n text: string,\n width: number,\n measurer: Measurer | undefined,\n trim = false,\n _hard = false,\n preserveNewlines = true,\n truncateAtomicOverflow = false,\n): string[] {\n if (width <= 0) {\n return []\n }\n\n const gWidthFn = measurer ? measurer.graphemeWidth.bind(measurer) : graphemeWidth\n\n const lines: string[] = []\n\n // Split by newlines first if preserving\n const inputLines = preserveNewlines ? text.split(\"\\n\") : [text.replace(/\\n/g, \" \")]\n\n for (const line of inputLines) {\n // Handle empty lines\n if (line === \"\") {\n lines.push(\"\")\n continue\n }\n\n // If the line contains ANSI escape sequences, split them out so they\n // don't consume display width. We interleave visible graphemes with\n // zero-width ANSI \"tokens\" that are appended to currentLine untouched.\n const graphemes = splitGraphemesAnsiAware(line)\n let currentLine = \"\"\n let currentWidth = 0\n let isFirstLineOfParagraph = true\n\n // Track the last valid break point\n let lastBreakIndex = -1 // Index in currentLine (character position)\n let lastBreakWidth = 0 // Width at break point\n let lastBreakGraphemeIndex = -1 // Index in graphemes array\n\n // Track the last soft break point (intra-token separator like `/`, `.`,\n // `_`, `:`). Soft breaks are SECONDARY: only used when no hard break\n // (space/hyphen) is available within the current line — the\n // `lastBreakIndex` triple takes precedence whenever it exists. Resets\n // alongside the hard-break triple every time a line is pushed.\n let lastSoftBreakIndex = -1\n let lastSoftBreakWidth = 0\n let lastSoftBreakGraphemeIndex = -1\n\n for (let i = 0; i < graphemes.length; i++) {\n const grapheme = graphemes[i]!\n const gWidth = gWidthFn(grapheme)\n\n // Handle zero-width characters\n if (gWidth === 0) {\n currentLine += grapheme\n continue\n }\n\n // In trim mode, skip leading spaces on continuation lines\n if (\n trim &&\n !isFirstLineOfParagraph &&\n currentWidth === 0 &&\n isWordBoundary(grapheme) &&\n grapheme !== \"-\"\n ) {\n continue\n }\n\n // Check if this grapheme is a break point\n // Break AFTER spaces/hyphens, or BEFORE CJK characters\n if (isWordBoundary(grapheme)) {\n // Include the boundary character, then mark as break point\n if (currentWidth + gWidth <= width) {\n currentLine += grapheme\n currentWidth += gWidth\n // Suppress break point if the next word is a lone operator (e.g. \"+\", \"=\")\n // to avoid orphaning operators at the start of the next line.\n if (grapheme !== \" \" || !isBreakBeforeOperatorWith(graphemes, i, gWidthFn)) {\n lastBreakIndex = currentLine.length\n lastBreakWidth = currentWidth\n lastBreakGraphemeIndex = i + 1\n }\n continue\n }\n // Space/hyphen doesn't fit — break here (before the boundary char).\n // The current line is complete; the boundary char is consumed as the break.\n if (currentLine) {\n let lineToAdd = currentLine\n if (trim) lineToAdd = lineToAdd.trimEnd()\n lines.push(lineToAdd)\n isFirstLineOfParagraph = false\n }\n currentLine = \"\"\n currentWidth = 0\n lastBreakIndex = -1\n lastBreakWidth = 0\n lastBreakGraphemeIndex = -1\n lastSoftBreakIndex = -1\n lastSoftBreakWidth = 0\n lastSoftBreakGraphemeIndex = -1\n continue\n } else if (canBreakAnywhere(grapheme)) {\n // CJK: can break before this character\n lastBreakIndex = currentLine.length\n lastBreakWidth = currentWidth\n lastBreakGraphemeIndex = i\n }\n\n // Would this grapheme overflow?\n if (currentWidth + gWidth > width) {\n if (lastBreakIndex > 0) {\n // We have a valid hard break point - use it.\n // Unconditional `trimEnd()`: `lastBreakIndex` points just past\n // the word-boundary grapheme so the slice INCLUDES the trailing\n // boundary character (typically a space). The next line's\n // `lastBreakGraphemeIndex` already skips past it, so keeping the\n // trailing space here double-counts the boundary — when this\n // wrapped line is rendered inside a Box with `backgroundColor`\n // (common for body cards), the trailing space paints as visible\n // chrome. `trim` controls only continuation-line LEADING-space\n // skipping; trailing whitespace from the rewind boundary always\n // drops. @km/tui/softwrap-leading-space-on-wrap.\n const lineToAdd = currentLine.slice(0, lastBreakIndex).trimEnd()\n lines.push(lineToAdd)\n isFirstLineOfParagraph = false\n\n // Reset and continue from break point\n currentLine = currentLine.slice(lastBreakIndex)\n currentWidth = currentWidth - lastBreakWidth\n\n // Rewind to process graphemes after the break\n i = lastBreakGraphemeIndex - 1\n currentLine = \"\"\n currentWidth = 0\n lastBreakIndex = -1\n lastBreakWidth = 0\n lastBreakGraphemeIndex = -1\n lastSoftBreakIndex = -1\n lastSoftBreakWidth = 0\n lastSoftBreakGraphemeIndex = -1\n } else if (lastSoftBreakIndex > 0) {\n // Soft break available inside the current token (e.g. `path/to`\n // becomes `path/` + `to`). Used only when no hard break fits on\n // the current line — paths and identifiers wrap at separators\n // instead of either overflowing or doing mid-word char wrap.\n // Trailing whitespace dropped unconditionally — see hard-break\n // branch above for rationale.\n const lineToAdd = currentLine.slice(0, lastSoftBreakIndex).trimEnd()\n lines.push(lineToAdd)\n isFirstLineOfParagraph = false\n\n // Rewind to the soft-break point: re-process graphemes from the\n // position AFTER the separator. This matches the hard-break path\n // so soft-wrapped continuation lines align with the rest of the\n // wrap algorithm (no special start-of-continuation handling).\n i = lastSoftBreakGraphemeIndex - 1\n currentLine = \"\"\n currentWidth = 0\n lastBreakIndex = -1\n lastBreakWidth = 0\n lastBreakGraphemeIndex = -1\n lastSoftBreakIndex = -1\n lastSoftBreakWidth = 0\n lastSoftBreakGraphemeIndex = -1\n } else if (truncateAtomicOverflow) {\n // No break point found AND wrap-truncate mode is active — emit\n // ellipsis-truncated line and skip the rest of the unbreakable\n // run until we hit a word boundary (where wrapping resumes).\n // Implements `wrap=\"wrap-truncate\"` per Text.md — CSS analogue\n // `overflow-wrap: break-word` + `text-overflow: ellipsis`.\n //\n // Algorithm:\n // 1. Trim trailing graphemes from currentLine until ellipsis fits.\n // 2. Push `<currentLine prefix> + …` as the truncated line.\n // 3. Fast-forward `i` past the unbreakable run (until next\n // word-boundary grapheme) so subsequent text resumes wrapping.\n const ellipsis = \"…\"\n const ellipsisWidth = gWidthFn(ellipsis)\n // Build the truncated prefix by trimming graphemes from the end\n // of currentLine until ellipsis fits within `width`. Split once,\n // pop graphemes off the tail (O(n) instead of O(n²) re-split).\n const lineGraphemes = splitGraphemes(currentLine)\n let truncatedWidth = currentWidth\n while (truncatedWidth + ellipsisWidth > width && lineGraphemes.length > 0) {\n const last = lineGraphemes.pop()!\n truncatedWidth -= gWidthFn(last)\n }\n let truncatedLine = lineGraphemes.join(\"\")\n if (trim) truncatedLine = truncatedLine.trimEnd()\n lines.push(truncatedLine + ellipsis)\n isFirstLineOfParagraph = false\n\n // Fast-forward `i` past the rest of the unbreakable run. We're\n // currently at index `i` (the grapheme that didn't fit). Advance\n // until we find a word-boundary or soft-break grapheme — that's\n // where wrapping can legitimately resume on the next iteration.\n let j = i\n while (\n j < graphemes.length &&\n !isWordBoundary(graphemes[j]!) &&\n !canBreakAnywhere(graphemes[j]!)\n ) {\n j++\n }\n // Reset state and rewind `i` so the next loop iteration picks up\n // at j (the first wrappable grapheme after the truncated run).\n i = j - 1\n currentLine = \"\"\n currentWidth = 0\n lastBreakIndex = -1\n lastBreakWidth = 0\n lastBreakGraphemeIndex = -1\n lastSoftBreakIndex = -1\n lastSoftBreakWidth = 0\n lastSoftBreakGraphemeIndex = -1\n } else {\n // No break point found - must do character wrap\n if (currentLine) {\n if (trim) currentLine = currentLine.trimEnd()\n lines.push(currentLine)\n isFirstLineOfParagraph = false\n }\n currentLine = grapheme\n currentWidth = gWidth\n lastBreakIndex = -1\n lastBreakWidth = 0\n lastBreakGraphemeIndex = -1\n lastSoftBreakIndex = -1\n lastSoftBreakWidth = 0\n lastSoftBreakGraphemeIndex = -1\n }\n } else {\n currentLine += grapheme\n currentWidth += gWidth\n // Track soft break points (path-style separators). Mark AFTER the\n // separator so `path/to` breaks as `path/` + `to`. Only useful if\n // there's already content before this separator on the line — a\n // leading separator has nothing to keep with its left context.\n if (currentLine.length > 1 && isSoftBreakPoint(grapheme)) {\n lastSoftBreakIndex = currentLine.length\n lastSoftBreakWidth = currentWidth\n lastSoftBreakGraphemeIndex = i + 1\n }\n }\n }\n // Suppress unused-variable warning — `lastSoftBreakWidth` is part of\n // the bookkeeping triple kept symmetric with `lastBreakWidth` for\n // future use; the width itself isn't read back when soft-wrapping.\n void lastSoftBreakWidth\n\n // Push remaining content\n if (currentLine) {\n lines.push(currentLine)\n }\n }\n\n // Fix OSC 8 hyperlink state across wrapped lines — each line must be self-contained\n if (text.includes(\"\\x1b]8;;\")) {\n fixOsc8AcrossWrappedLines(lines)\n }\n\n // Fix SGR fg/attr state across wrapped lines — a nested <Text color/bold/…>\n // emits inline push/pop ANSI; without this the colour/attr is lost on every\n // continuation line. Sibling of the OSC 8 fix above.\n // See @km/code/v0.2/19690-status-tuple-wrap-color.\n if (lines.length > 1 && text.includes(\"\\x1b[\")) {\n fixSgrAcrossWrappedLines(lines)\n }\n\n return lines\n}\n\n/**\n * Post-process wrapped lines to ensure OSC 8 hyperlink sequences are self-contained per line.\n * When wrapping splits text containing OSC 8 sequences across lines, the open/close sequences\n * may end up on different lines. This closes unclosed hyperlinks at line ends and re-opens\n * them on continuation lines. Mutates the array in place.\n *\n * The greedy `wrapText` path calls this inline; the Knuth-Plass `optimalWrap`\n * path (`wrap=\"even\"`) lives in a sibling module (`pipeline/pretext.ts`) and\n * applies it via `formatTextLines`. Both must run it: a continuation line that\n * carries only an OSC 8 CLOSE (no matching OPEN) makes `parseAnsiText` fall back\n * to un-stripped source text and leak `]8;;\\` as literal cells.\n * See `@km/code/v0.2/19654-osc-link-leak`.\n */\nexport function fixOsc8AcrossWrappedLines(lines: string[]): void {\n let activeHref: string | null = null\n\n for (let i = 0; i < lines.length; i++) {\n let line = lines[i]!\n\n // Re-open hyperlink from previous line\n if (activeHref !== null) {\n line = `\\x1b]8;;${activeHref}\\x1b\\\\` + line\n }\n\n // Scan line for OSC 8 sequences to determine end-of-line state\n let lineHref: string | null = activeHref\n const osc8Matches = line.matchAll(/\\x1b\\]8;;([^\\x07\\x1b]*)(?:\\x07|\\x1b\\\\)/g)\n for (const m of osc8Matches) {\n lineHref = m[1] === \"\" ? null : m[1]!\n }\n\n // Close unclosed hyperlink at line end\n if (lineHref !== null) {\n line += \"\\x1b]8;;\\x1b\\\\\"\n }\n\n lines[i] = line\n activeHref = lineHref\n }\n}\n\n/**\n * Reconstruct the active SGR style (fg + attributes, never bg) carried by a\n * parsed segment, or null when nothing visual is active.\n *\n * Background is intentionally excluded: nested-`<Text>` backgrounds are tracked\n * as `BgSegment`s and painted at the buffer level (see `collectTextWithBg`),\n * never embedded in the inline ANSI stream this operates on. Re-emitting a bg\n * here would double-paint and bleed.\n */\nfunction activeSgrStyle(seg: StyledSegment): Style | null {\n const fg = seg.fg ?? null\n const underlineColor = seg.underlineColor ?? null\n const attrs = {\n bold: seg.bold,\n dim: seg.dim,\n italic: seg.italic,\n underline: seg.underline,\n underlineStyle: seg.underlineStyle,\n inverse: seg.inverse,\n strikethrough: seg.strikethrough,\n overline: seg.overline,\n }\n const hasAttr =\n Boolean(attrs.bold) ||\n Boolean(attrs.dim) ||\n Boolean(attrs.italic) ||\n Boolean(attrs.underline) ||\n Boolean(attrs.underlineStyle) ||\n Boolean(attrs.inverse) ||\n Boolean(attrs.strikethrough) ||\n Boolean(attrs.overline)\n if (fg === null && underlineColor === null && !hasAttr) return null\n return { fg, bg: null, attrs, underlineColor }\n}\n\n/**\n * The active SGR style at the END of a line — after every code, including a\n * trailing close that has no text after it (`…epsilon\\x1b[39m`). `parseAnsiText`\n * only emits segments for text runs, so a sentinel space is appended to force a\n * final segment that captures the true end-of-line state.\n */\nfunction endOfLineSgrStyle(line: string): Style | null {\n const segs = parseAnsiText(line + \" \")\n const last = segs.length > 0 ? segs[segs.length - 1] : undefined\n return last ? activeSgrStyle(last) : null\n}\n\n/**\n * Post-process wrapped lines so each is self-contained for SGR foreground +\n * attributes — the sibling of {@link fixOsc8AcrossWrappedLines}.\n *\n * A nested `<Text color>` / `<Text bold>` emits its style as inline push/pop\n * ANSI inside the parent's collected text (render-text.ts `applyTextStyleAnsi`).\n * When the styled run soft-wraps, the OPEN SGR sits on the first line and every\n * continuation line is rendered independently from `baseStyle` — so the color /\n * attribute is silently lost on all lines after the first (the live symptom in\n * `@km/code/v0.2/19690-status-tuple-wrap-color`: an inline-code status tuple\n * renders coloured on `M` but default on the wrapped `@km/…` path).\n *\n * This re-opens the active style at the start of each continuation line and\n * closes it at line end, so every line stands alone. Background is excluded by\n * construction (see {@link activeSgrStyle}). Mutates `lines` in place.\n *\n * A single (non-nested) coloured `<Text>` never hit this bug: its colour is a\n * node-level `style.color` applied to every cell, independent of inline ANSI.\n */\nexport function fixSgrAcrossWrappedLines(lines: string[]): void {\n let carry: Style | null = null\n for (let i = 0; i < lines.length; i++) {\n let line = lines[i]!\n if (carry !== null) line = styleToAnsiCodes(carry) + line\n const end = endOfLineSgrStyle(line)\n if (end !== null) line += styleResetCodes(end)\n lines[i] = line\n carry = end\n }\n}\n\n/**\n * Slice text by display width (from start).\n * Returns the first `maxWidth` columns of text.\n * Uses the default measurer for width calculations.\n * Handles both ANSI-styled and plain text.\n *\n * @param text - The text to slice\n * @param maxWidth - Maximum display width to keep from the start\n * @returns Sliced string from the start\n */\nexport function sliceByWidth(text: string, maxWidth: number): string {\n return (_scopedMeasurer ?? getDefaultMeasurer()).sliceByWidth(text, maxWidth)\n}\n\n/**\n * Slice a string by display width range.\n * Like string.slice() but works with display columns.\n *\n * @param text - The text to slice\n * @param start - Start display column (inclusive)\n * @param end - End display column (exclusive)\n * @returns Sliced string\n */\nexport function sliceByWidthRange(text: string, start: number, end?: number): string {\n const graphemes = splitGraphemes(text)\n let result = \"\"\n let currentCol = 0\n const endCol = end ?? Number.POSITIVE_INFINITY\n\n for (const grapheme of graphemes) {\n const gWidth = graphemeWidth(grapheme)\n\n // Haven't reached start yet\n if (currentCol + gWidth <= start) {\n currentCol += gWidth\n continue\n }\n\n // Past the end\n if (currentCol >= endCol) {\n break\n }\n\n // This grapheme is at least partially in range\n result += grapheme\n currentCol += gWidth\n }\n\n return result\n}\n\n/**\n * Slice text by display width from the end.\n * Returns the last `maxWidth` columns of text.\n * Uses the default measurer for width calculations.\n *\n * @param text - The text to slice\n * @param maxWidth - Maximum display width to keep from the end\n * @returns Sliced string from the end\n */\nexport function sliceByWidthFromEnd(text: string, maxWidth: number): string {\n return (_scopedMeasurer ?? getDefaultMeasurer()).sliceByWidthFromEnd(text, maxWidth)\n}\n\n// ============================================================================\n// Buffer Writing\n// ============================================================================\n\n/**\n * Write styled text to a terminal buffer.\n *\n * Handles:\n * - Multi-byte graphemes (emoji, combining characters)\n * - Wide characters (CJK) that take 2 cells\n * - Zero-width characters (appended to previous cell)\n *\n * @param buffer - The buffer to write to\n * @param x - Starting column\n * @param y - Row\n * @param text - Text to write\n * @param style - Style to apply\n * @returns The ending column (x + display_width)\n */\nexport function writeTextToBuffer(\n buffer: TerminalBuffer,\n x: number,\n y: number,\n text: string,\n style: Style = { fg: null, bg: null, attrs: {} },\n): number {\n const graphemes = splitGraphemes(text)\n let col = x\n let combineCell: Cell | null = null\n\n for (const grapheme of graphemes) {\n const width = graphemeWidth(grapheme)\n\n if (width === 0) {\n // Zero-width character: combine with previous cell.\n // Use readCellInto to avoid allocating a fresh Cell on each combine.\n if (col > 0 && buffer.inBounds(col - 1, y)) {\n // Lazy-init reusable cell (zero-width combining is uncommon)\n combineCell ??= createMutableCell()\n buffer.readCellInto(col - 1, y, combineCell)\n combineCell.char = combineCell.char + grapheme\n buffer.setCell(col - 1, y, combineCell)\n }\n } else if (width === 1) {\n // Normal single-width character\n if (buffer.inBounds(col, y)) {\n buffer.setCell(col, y, {\n char: grapheme,\n fg: style.fg,\n bg: style.bg,\n attrs: style.attrs,\n wide: false,\n continuation: false,\n })\n }\n col++\n } else if (width === 2) {\n // Wide character: takes 2 cells\n // For text-presentation emoji, add VS16 so terminals render at 2 columns\n const outputChar = ensureEmojiPresentation(grapheme)\n if (buffer.inBounds(col, y)) {\n buffer.setCell(col, y, {\n char: outputChar,\n fg: style.fg,\n bg: style.bg,\n attrs: style.attrs,\n wide: true,\n continuation: false,\n })\n }\n if (buffer.inBounds(col + 1, y)) {\n buffer.setCell(col + 1, y, {\n char: \"\",\n fg: style.fg,\n bg: style.bg,\n attrs: style.attrs,\n wide: false,\n continuation: true,\n })\n }\n col += 2\n }\n\n // Stop if we've gone past the buffer edge\n if (col >= buffer.width) {\n break\n }\n }\n\n return col\n}\n\n/**\n * Write styled text to a buffer with automatic truncation.\n *\n * @param buffer - The buffer to write to\n * @param x - Starting column\n * @param y - Row\n * @param text - Text to write\n * @param maxWidth - Maximum width (truncate if exceeded)\n * @param style - Style to apply\n * @param ellipsis - Ellipsis for truncated text\n */\nexport function writeTextTruncated(\n buffer: TerminalBuffer,\n x: number,\n y: number,\n text: string,\n maxWidth: number,\n style: Style = { fg: null, bg: null, attrs: {} },\n ellipsis = \"\\u2026\",\n): void {\n const textWidth = displayWidth(text)\n\n if (textWidth <= maxWidth) {\n writeTextToBuffer(buffer, x, y, text, style)\n } else {\n const truncated = truncateText(text, maxWidth, ellipsis)\n writeTextToBuffer(buffer, x, y, truncated, style)\n }\n}\n\n/**\n * Write multiple lines of styled text to a buffer.\n *\n * @param buffer - The buffer to write to\n * @param x - Starting column\n * @param y - Starting row\n * @param lines - Lines to write\n * @param style - Style to apply\n */\nexport function writeLinesToBuffer(\n buffer: TerminalBuffer,\n x: number,\n y: number,\n lines: string[],\n style: Style = { fg: null, bg: null, attrs: {} },\n): void {\n for (let i = 0; i < lines.length; i++) {\n if (y + i >= buffer.height) break\n writeTextToBuffer(buffer, x, y + i, lines[i]!, style)\n }\n}\n\n// ============================================================================\n// ANSI-Aware Operations\n// ============================================================================\n\n/**\n * Strip all ANSI escape codes from a string.\n *\n * Handles:\n * - CSI sequences (cursor movement, colors, SGR, etc.)\n * - OSC sequences (window titles, hyperlinks)\n * - Single-character escape sequences\n * - Character set selection\n */\nexport function stripAnsi(text: string): string {\n return text\n .replace(/\\x1b\\[[0-9;:?]*[A-Za-z]/g, \"\") // ESC CSI sequences (including SGR with colons)\n .replace(/\\x9b[0-9;:?]*[A-Za-z]/g, \"\") // C1 CSI sequences\n .replace(/\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)/g, \"\") // ESC OSC sequences\n .replace(/\\x9d[^\\x07\\x1b\\x9c]*(?:\\x07|\\x1b\\\\|\\x9c)/g, \"\") // C1 OSC sequences\n .replace(/\\x1b[DME78]/g, \"\") // Single-char sequences\n .replace(/\\x1b\\(B/g, \"\") // Character set selection\n}\n\n/**\n * Get display width of text with ANSI sequences.\n * ANSI sequences don't contribute to display width.\n */\nexport function displayWidthAnsi(text: string): number {\n return displayWidth(stripAnsi(text))\n}\n\n/**\n * Truncate text that may contain ANSI sequences.\n * Preserves ANSI codes while truncating visible characters.\n *\n * Note: This is a simplified implementation that strips ANSI before\n * truncation. For proper ANSI-aware truncation, consider using\n * slice-ansi or similar library.\n */\nexport function truncateAnsi(text: string, maxWidth: number, ellipsis = \"\\u2026\"): string {\n // Simple approach: if text has ANSI, strip and truncate\n // A more sophisticated approach would preserve styles\n const stripped = stripAnsi(text)\n return truncateText(stripped, maxWidth, ellipsis)\n}\n\n// ============================================================================\n// ANSI Parsing\n// ============================================================================\n\n// BG_OVERRIDE_CODE is imported from ansi and re-exported at top of file\n\n/** Styled text segment with associated ANSI colors/attributes */\nexport interface StyledSegment {\n text: string\n fg?: number | null // SGR color code (30-37, 90-97, or 38;5;N / 38;2;r;g;b)\n bg?: number | null // SGR color code (40-47, 100-107, or 48;5;N / 48;2;r;g;b)\n /**\n * Underline color (SGR 58).\n * Same format as fg/bg: packed RGB with 0x1000000 marker, or 256-color index.\n */\n underlineColor?: number | null\n bold?: boolean\n dim?: boolean\n italic?: boolean\n underline?: boolean\n /**\n * Underline style variant (SGR 4:x).\n * Uses UnderlineStyle from buffer.ts.\n */\n underlineStyle?: UnderlineStyle\n inverse?: boolean\n /** Strikethrough (SGR 9 on, SGR 29 off). */\n strikethrough?: boolean\n /** Overline (SGR 53 on, SGR 55 off). */\n overline?: boolean\n bgOverride?: boolean // Set when BG_OVERRIDE_CODE (9999) is present\n /**\n * OSC 8 hyperlink URL.\n * Set when the segment is inside an OSC 8 hyperlink sequence.\n */\n hyperlink?: string\n /**\n * True when the foreground color was specified using colon-separated SGR\n * (e.g., `38:2::255:100:0m` instead of `38;2;255;100;0m`).\n * Used to preserve the original format in round-trip output.\n */\n colonFg?: boolean\n /**\n * True when the background color was specified using colon-separated SGR.\n */\n colonBg?: boolean\n}\n\n/**\n * Map SGR 4:x subparameter to underline style.\n * 0=none, 1=single, 2=double, 3=curly, 4=dotted, 5=dashed\n */\nfunction parseUnderlineStyle(subparam: number): UnderlineStyle {\n switch (subparam) {\n case 0:\n return false\n case 1:\n return \"single\"\n case 2:\n return \"double\"\n case 3:\n return \"curly\"\n case 4:\n return \"dotted\"\n case 5:\n return \"dashed\"\n default:\n return \"single\" // Unknown, default to single\n }\n}\n\nfunction packRgb(r: number, g: number, b: number): number {\n return 0x1000000 | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff)\n}\n\nfunction parseColonRgb(subparts: number[]): number | null {\n if (subparts.length < 5) return null\n const [r, g, b] = subparts.slice(-3)\n if (!Number.isFinite(r) || !Number.isFinite(g) || !Number.isFinite(b)) return null\n return packRgb(r!, g!, b!)\n}\n\n/**\n * Parse text with ANSI escape sequences into styled segments.\n * Handles basic SGR (Select Graphic Rendition) codes including:\n * - Standard colors (30-37, 40-47, 90-97, 100-107)\n * - Extended colors (38;5;N, 48;5;N for 256-color, 38;2;r;g;b, 48;2;r;g;b for RGB)\n * - Underline styles (4:x where x = 0-5)\n * - Underline color (58;5;N for 256-color, 58;2;r;g;b for RGB)\n */\nexport function parseAnsiText(text: string): StyledSegment[] {\n const segments: StyledSegment[] = []\n\n // Step 1: Strip non-SGR CSI sequences (cursor movement, erase, etc.) that would\n // otherwise leak through as literal text. SGR sequences end in 'm'; all\n // other CSI sequences (ending in A-L, H, J, K, S, T, etc.) are stripped.\n // Handles both ESC-based CSI (\\x1b[) and C1 CSI (\\x9b).\n // This must happen BEFORE OSC 8 processing so hyperlink position tracking\n // is based on the cleaned text (no position drift from stripped sequences).\n const sanitizedText = text\n .replace(/\\x1b\\[[0-9;:]*[A-LN-Za-ln-z@`]/g, \"\")\n .replace(/\\x9b[0-9;:]*[A-LN-Za-ln-z@`]/g, \"\")\n\n // Step 2: Strip OSC 8 hyperlink sequences and build a position-to-URL map.\n // OSC 8 format: \\x1b]8;;URL(\\x1b\\\\ | \\x07) for open, \\x1b]8;;(\\x1b\\\\ | \\x07) for close.\n // Also handles C1 OSC (\\x9d) form: \\x9d8;;URL(\\x1b\\\\ | \\x07 | \\x9c)\n // We strip these from the text before SGR parsing and track which character\n // positions map to which hyperlink URL.\n //\n // Hyperlink format metadata is encoded in the URL using control char prefixes:\n // \\x01c1b\\x02 = C1 OSC intro + BEL terminator\n // \\x01c1s\\x02 = C1 OSC intro + ST terminator\n // \\x01e7b\\x02 = ESC OSC intro + BEL terminator\n // (no prefix) = ESC OSC intro + ST terminator (default)\n // bufferToStyledText reads these prefixes to emit the original format.\n const oscPattern = /(?:\\x1b\\]|\\x9d)8;;([^\\x07\\x1b\\x9c]*)(?:\\x07|\\x1b\\\\|\\x9c)/g\n let currentHyperlink: string | undefined\n // Map from character index in cleaned text to hyperlink URL\n const hyperlinkRanges: Array<{ start: number; end: number; url: string }> = []\n let rangeStart = -1\n let cleaned = \"\"\n let oscMatch: RegExpExecArray | null\n let oscLastIndex = 0\n\n while ((oscMatch = oscPattern.exec(sanitizedText)) !== null) {\n // Append text between last OSC and this one (preserving SGR codes)\n cleaned += sanitizedText.slice(oscLastIndex, oscMatch.index)\n const url = oscMatch[1]!\n\n // Detect format: C1 vs ESC intro, BEL vs ST terminator\n const matchStr = oscMatch[0]\n const isC1 = matchStr.charCodeAt(0) === 0x9d\n const lastChar = matchStr.charCodeAt(matchStr.length - 1)\n const isBel = lastChar === 0x07\n const isSt9c = lastChar === 0x9c\n\n if (url === \"\") {\n // Close hyperlink — format prefix matches the open sequence\n if (currentHyperlink && rangeStart >= 0) {\n hyperlinkRanges.push({ start: rangeStart, end: cleaned.length, url: currentHyperlink })\n }\n currentHyperlink = undefined\n rangeStart = -1\n } else {\n // Open hyperlink — encode format metadata in URL prefix\n if (currentHyperlink && rangeStart >= 0) {\n // Close previous unclosed hyperlink\n hyperlinkRanges.push({ start: rangeStart, end: cleaned.length, url: currentHyperlink })\n }\n // Encode hyperlink format as a prefix on the URL:\n // \\x01c1b\\x02 = C1 intro + BEL terminator\n // \\x01c1s\\x02 = C1 intro + ST terminator (ESC \\ or C1 ST \\x9c)\n // \\x01e7b\\x02 = ESC intro + BEL terminator\n // no prefix = ESC intro + ST terminator (default)\n let encodedUrl = url\n if (isC1 && (isBel || isSt9c)) {\n encodedUrl = `\\x01c1b\\x02${url}`\n } else if (isC1) {\n encodedUrl = `\\x01c1s\\x02${url}`\n } else if (isBel) {\n encodedUrl = `\\x01e7b\\x02${url}`\n }\n // else: ESC + ST = default, no prefix needed\n currentHyperlink = encodedUrl\n rangeStart = cleaned.length\n }\n\n oscLastIndex = oscMatch.index + oscMatch[0].length\n }\n // Append remaining text after last OSC\n cleaned += sanitizedText.slice(oscLastIndex)\n // Close any still-open hyperlink\n if (currentHyperlink && rangeStart >= 0) {\n hyperlinkRanges.push({ start: rangeStart, end: cleaned.length, url: currentHyperlink })\n }\n\n // If no OSC 8 sequences found, use sanitized text for efficiency\n const processText = hyperlinkRanges.length > 0 ? cleaned : sanitizedText\n\n // Extended pattern: matches SGR with semicolons AND colons (for 4:x, 58:2::r:g:b)\n // Handles both ESC-based CSI (\\x1b[) and C1 CSI (\\x9b)\n const ansiPattern = /(?:\\x1b\\[|\\x9b)([0-9;:]*)m/g\n\n let currentStyle: Omit<StyledSegment, \"text\"> = {}\n let lastIndex = 0\n let match: RegExpExecArray | null\n\n // Helper to find hyperlink URL for a position in the cleaned text.\n // Positions in cleaned text map directly to hyperlinkRanges since OSC 8\n // sequences were stripped but SGR sequences remain at the same indices.\n function getHyperlinkAt(pos: number): string | undefined {\n for (const range of hyperlinkRanges) {\n if (pos >= range.start && pos < range.end) return range.url\n }\n return undefined\n }\n\n while ((match = ansiPattern.exec(processText)) !== null) {\n // Add text before this escape sequence\n if (match.index > lastIndex) {\n const content = processText.slice(lastIndex, match.index)\n if (content.length > 0) {\n if (hyperlinkRanges.length > 0) {\n // Split content into runs by hyperlink URL.\n // lastIndex is the position of content[0] in processText/cleaned.\n let segStart = 0\n for (let ci = 0; ci < content.length; ci++) {\n const hl = getHyperlinkAt(lastIndex + ci)\n const prevHl = ci > 0 ? getHyperlinkAt(lastIndex + ci - 1) : undefined\n if (ci > 0 && hl !== prevHl) {\n const sub = content.slice(segStart, ci)\n if (sub.length > 0) {\n const seg: StyledSegment = { text: sub, ...currentStyle }\n if (prevHl) seg.hyperlink = prevHl\n segments.push(seg)\n }\n segStart = ci\n }\n }\n // Push remaining\n const sub = content.slice(segStart)\n if (sub.length > 0) {\n const hl = getHyperlinkAt(lastIndex + segStart)\n const seg: StyledSegment = { text: sub, ...currentStyle }\n if (hl) seg.hyperlink = hl\n segments.push(seg)\n }\n } else {\n segments.push({ text: content, ...currentStyle })\n }\n }\n }\n\n // Parse SGR codes - split by semicolon first, then handle colon subparams\n const rawParams = match[1]!\n\n // Handle colon-separated sequences (like 4:3 for curly underline, 58:2::r:g:b)\n // Split by semicolon first to get top-level params\n const params = rawParams.split(\";\")\n\n for (let i = 0; i < params.length; i++) {\n const param = params[i]!\n\n // Check if this param has colon subparameters (e.g., \"4:3\", \"58:2::255:0:0\")\n if (param.includes(\":\")) {\n const subparts = param.split(\":\").map((s) => (s === \"\" ? 0 : Number(s)))\n const mainCode = subparts[0]!\n\n if (mainCode === 4) {\n // SGR 4:x - underline style\n const styleCode = subparts[1] ?? 1\n currentStyle.underlineStyle = parseUnderlineStyle(styleCode)\n currentStyle.underline = currentStyle.underlineStyle !== false\n } else if (mainCode === 58) {\n // SGR 58 - underline color\n // Format: 58:5:N, 58:2::r:g:b, 58:2:r:g:b, or 58:2:colorspace:r:g:b.\n if (subparts[1] === 5 && subparts[2] !== undefined) {\n currentStyle.underlineColor = subparts[2]\n } else if (subparts[1] === 2) {\n const rgb = parseColonRgb(subparts)\n if (rgb !== null) currentStyle.underlineColor = rgb\n }\n } else if (mainCode === 38) {\n // SGR 38:2::r:g:b or 38:5:N format (colon-separated)\n if (subparts[1] === 5 && subparts[2] !== undefined) {\n currentStyle.fg = subparts[2]\n currentStyle.colonFg = true\n } else if (subparts[1] === 2) {\n const rgb = parseColonRgb(subparts)\n if (rgb !== null) {\n currentStyle.fg = rgb\n currentStyle.colonFg = true\n }\n }\n } else if (mainCode === 48) {\n // SGR 48:2::r:g:b or 48:5:N format (colon-separated)\n if (subparts[1] === 5 && subparts[2] !== undefined) {\n currentStyle.bg = subparts[2]\n currentStyle.colonBg = true\n } else if (subparts[1] === 2) {\n const rgb = parseColonRgb(subparts)\n if (rgb !== null) {\n currentStyle.bg = rgb\n currentStyle.colonBg = true\n }\n }\n }\n continue\n }\n\n // Standard semicolon-separated params\n const code = Number(param)\n switch (code) {\n case 0:\n // Reset\n currentStyle = {}\n break\n case 1:\n currentStyle.bold = true\n break\n case 2:\n currentStyle.dim = true\n break\n case 3:\n currentStyle.italic = true\n break\n case 4:\n // Plain SGR 4 - simple underline (no subparam)\n currentStyle.underline = true\n currentStyle.underlineStyle = \"single\"\n break\n case 7:\n currentStyle.inverse = true\n break\n case 9:\n // SGR 9 - strikethrough on\n currentStyle.strikethrough = true\n break\n case 22:\n currentStyle.bold = false\n currentStyle.dim = false\n break\n case 23:\n currentStyle.italic = false\n break\n case 24:\n // SGR 24 - underline off\n currentStyle.underline = false\n currentStyle.underlineStyle = false\n break\n case 27:\n currentStyle.inverse = false\n break\n case 29:\n // SGR 29 - strikethrough off\n currentStyle.strikethrough = false\n break\n case 30:\n case 31:\n case 32:\n case 33:\n case 34:\n case 35:\n case 36:\n case 37:\n currentStyle.fg = code\n break\n case 38: {\n // Extended color: 38;5;N (256 color) or 38;2;r;g;b (true color)\n // Semicolon-separated — clear colonFg flag\n const nextParams = params.slice(i + 1).map(Number)\n currentStyle.colonFg = undefined\n if (nextParams[0] === 5 && nextParams[1] !== undefined) {\n currentStyle.fg = nextParams[1]\n i += 2\n } else if (nextParams[0] === 2 && nextParams[3] !== undefined) {\n // True color - store as RGB values packed\n currentStyle.fg =\n 0x1000000 |\n ((nextParams[1]! & 0xff) << 16) |\n ((nextParams[2]! & 0xff) << 8) |\n (nextParams[3]! & 0xff)\n i += 4\n }\n break\n }\n case 39:\n currentStyle.fg = null // Default foreground\n break\n case 40:\n case 41:\n case 42:\n case 43:\n case 44:\n case 45:\n case 46:\n case 47:\n currentStyle.bg = code\n break\n case 48: {\n // Extended color: 48;5;N (256 color) or 48;2;r;g;b (true color)\n // Semicolon-separated — clear colonBg flag\n const nextParams = params.slice(i + 1).map(Number)\n currentStyle.colonBg = undefined\n if (nextParams[0] === 5 && nextParams[1] !== undefined) {\n currentStyle.bg = nextParams[1]\n i += 2\n } else if (nextParams[0] === 2 && nextParams[3] !== undefined) {\n // True color - store as RGB values packed\n currentStyle.bg =\n 0x1000000 |\n ((nextParams[1]! & 0xff) << 16) |\n ((nextParams[2]! & 0xff) << 8) |\n (nextParams[3]! & 0xff)\n i += 4\n }\n break\n }\n case 49:\n currentStyle.bg = null // Default background\n break\n case 53:\n // SGR 53 - overline on\n currentStyle.overline = true\n break\n case 55:\n // SGR 55 - overline off\n currentStyle.overline = false\n break\n case 58: {\n // Underline color: 58;5;N (256 color) or 58;2;r;g;b (true color)\n const nextParams = params.slice(i + 1).map(Number)\n if (nextParams[0] === 5 && nextParams[1] !== undefined) {\n currentStyle.underlineColor = nextParams[1]\n i += 2\n } else if (nextParams[0] === 2 && nextParams[3] !== undefined) {\n // True color - store as RGB values packed\n currentStyle.underlineColor =\n 0x1000000 |\n ((nextParams[1]! & 0xff) << 16) |\n ((nextParams[2]! & 0xff) << 8) |\n (nextParams[3]! & 0xff)\n i += 4\n }\n break\n }\n case 59:\n currentStyle.underlineColor = null // Default underline color\n break\n case 90:\n case 91:\n case 92:\n case 93:\n case 94:\n case 95:\n case 96:\n case 97:\n currentStyle.fg = code // Bright foreground colors\n break\n case 100:\n case 101:\n case 102:\n case 103:\n case 104:\n case 105:\n case 106:\n case 107:\n currentStyle.bg = code // Bright background colors\n break\n case BG_OVERRIDE_CODE:\n // Private code: signals intentional bg override, skip conflict detection\n currentStyle.bgOverride = true\n break\n }\n }\n\n lastIndex = match.index + match[0].length\n }\n\n // Add remaining text\n if (lastIndex < processText.length) {\n const content = processText.slice(lastIndex)\n if (content.length > 0) {\n if (hyperlinkRanges.length > 0) {\n // Split remaining content by hyperlink URL\n let segStart = 0\n for (let ci = 0; ci < content.length; ci++) {\n const hl = getHyperlinkAt(lastIndex + ci)\n const prevHl = ci > 0 ? getHyperlinkAt(lastIndex + ci - 1) : undefined\n if (ci > 0 && hl !== prevHl) {\n const sub = content.slice(segStart, ci)\n if (sub.length > 0) {\n const seg: StyledSegment = { text: sub, ...currentStyle }\n if (prevHl) seg.hyperlink = prevHl\n segments.push(seg)\n }\n segStart = ci\n }\n }\n const sub = content.slice(segStart)\n if (sub.length > 0) {\n const hl = getHyperlinkAt(lastIndex + segStart)\n const seg: StyledSegment = { text: sub, ...currentStyle }\n if (hl) seg.hyperlink = hl\n segments.push(seg)\n }\n } else {\n segments.push({ text: content, ...currentStyle })\n }\n }\n }\n\n return segments\n}\n\nconst ANSI_TEST_REGEX = /\\x1b(?:\\[[0-9;:]*[A-Za-z]|\\])|\\x9b[\\x30-\\x3f]*[\\x40-\\x7e]|\\x9d/\n\n/**\n * Check if text contains ANSI escape sequences (SGR or OSC).\n * Detects both ESC-based (7-bit) and C1 (8-bit) forms:\n * - ESC [ params final (CSI)\n * - ESC ] (OSC)\n * - U+009B params final (C1 CSI)\n * - U+009D (C1 OSC)\n */\nexport function hasAnsi(text: string): boolean {\n // Use a non-global regex for testing to avoid lastIndex issues\n return ANSI_TEST_REGEX.test(text)\n}\n\n// ============================================================================\n// Measurement Utilities\n// ============================================================================\n\n/**\n * Measure the dimensions of multi-line text.\n *\n * @param text - Text to measure (may contain newlines)\n * @returns { width, height } in display columns and rows\n */\nexport function measureText(text: string): { width: number; height: number } {\n const lines = text.split(\"\\n\")\n let maxWidth = 0\n\n for (const line of lines) {\n const lineWidth = displayWidth(line)\n if (lineWidth > maxWidth) {\n maxWidth = lineWidth\n }\n }\n\n return {\n width: maxWidth,\n height: lines.length,\n }\n}\n\n/**\n * Check if a string contains any wide characters.\n */\nexport function hasWideCharacters(text: string): boolean {\n const graphemes = splitGraphemes(text)\n return graphemes.some(isWideGrapheme)\n}\n\n/**\n * Check if a string contains any combining/zero-width characters.\n */\nexport function hasZeroWidthCharacters(text: string): boolean {\n const graphemes = splitGraphemes(text)\n return graphemes.some(isZeroWidthGrapheme)\n}\n\n/**\n * Normalize string for consistent handling.\n * Applies Unicode NFC normalization.\n */\nexport function normalizeText(text: string): string {\n return text.normalize(\"NFC\")\n}\n\n// ============================================================================\n// Character Detection\n// ============================================================================\n\n/**\n * Common character ranges for quick checks.\n */\nconst CHAR_RANGES = {\n // Basic Latin (ASCII)\n isBasicLatin: (cp: number) => cp >= 0x0020 && cp <= 0x007f,\n\n // CJK Unified Ideographs\n isCJK: (cp: number) =>\n (cp >= 0x4e00 && cp <= 0x9fff) || // CJK Unified Ideographs\n (cp >= 0x3400 && cp <= 0x4dbf) || // CJK Unified Ideographs Extension A\n (cp >= 0x20000 && cp <= 0x2a6df) || // CJK Unified Ideographs Extension B\n (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs\n (cp >= 0x2f800 && cp <= 0x2fa1f), // CJK Compatibility Ideographs Supplement\n\n // Japanese Hiragana/Katakana\n isJapaneseKana: (cp: number) =>\n (cp >= 0x3040 && cp <= 0x309f) || // Hiragana\n (cp >= 0x30a0 && cp <= 0x30ff), // Katakana\n\n // Korean Hangul\n isHangul: (cp: number) =>\n (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables\n (cp >= 0x1100 && cp <= 0x11ff), // Hangul Jamo\n\n // Emoji ranges (simplified)\n isEmoji: (cp: number) =>\n (cp >= 0x1f600 && cp <= 0x1f64f) || // Emoticons\n (cp >= 0x1f300 && cp <= 0x1f5ff) || // Misc Symbols and Pictographs\n (cp >= 0x1f680 && cp <= 0x1f6ff) || // Transport and Map\n (cp >= 0x1f700 && cp <= 0x1f77f) || // Alchemical Symbols\n (cp >= 0x1f900 && cp <= 0x1f9ff) || // Supplemental Symbols and Pictographs\n (cp >= 0x2600 && cp <= 0x26ff) || // Misc symbols\n (cp >= 0x2700 && cp <= 0x27bf), // Dingbats\n} as const\n\n/**\n * Get the first code point of a string.\n */\nexport function getFirstCodePoint(str: string): number {\n const cp = str.codePointAt(0)\n return cp ?? 0\n}\n\n/**\n * Check if a grapheme is likely an emoji.\n * Note: This is a heuristic, not comprehensive.\n */\nexport function isLikelyEmoji(grapheme: string): boolean {\n const cp = getFirstCodePoint(grapheme)\n return CHAR_RANGES.isEmoji(cp) || grapheme.includes(\"\\u200d\") // Contains ZWJ\n}\n\n/**\n * Check if a grapheme is a CJK character.\n */\nexport function isCJK(grapheme: string): boolean {\n const cp = getFirstCodePoint(grapheme)\n return CHAR_RANGES.isCJK(cp) || CHAR_RANGES.isJapaneseKana(cp) || CHAR_RANGES.isHangul(cp)\n}\n"],"mappings":"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"}