@bubblebrain-ai/bubble 0.0.52 → 0.0.54

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 (483) hide show
  1. package/README.md +4 -12
  2. package/dist/agent/routing-catalog.js +3 -2
  3. package/dist/agent/subagent/router.js +5 -0
  4. package/dist/agent/subagent/runtime.d.ts +1 -0
  5. package/dist/agent/subagent/runtime.js +13 -1
  6. package/dist/agent/subagent-control.d.ts +4 -0
  7. package/dist/agent/subagent-control.js +1 -0
  8. package/dist/agent/subagent-store.js +2 -0
  9. package/dist/agent/workflow/control.d.ts +2 -0
  10. package/dist/agent/workflow/runs.d.ts +1 -0
  11. package/dist/agent/workflow/runs.js +3 -0
  12. package/dist/agent.d.ts +11 -1
  13. package/dist/agent.js +158 -57
  14. package/dist/bin.js +2 -44
  15. package/dist/config.d.ts +14 -1
  16. package/dist/config.js +45 -4
  17. package/dist/context/budget.d.ts +7 -1
  18. package/dist/context/budget.js +25 -2
  19. package/dist/context/compact-llm.d.ts +1 -1
  20. package/dist/context/compact-llm.js +5 -2
  21. package/dist/context/overflow.js +2 -0
  22. package/dist/context/projector.d.ts +8 -0
  23. package/dist/context/projector.js +55 -6
  24. package/dist/context/tool-output-truncate.d.ts +17 -1
  25. package/dist/context/tool-output-truncate.js +171 -49
  26. package/dist/feishu/agent-host/run-driver.js +3 -0
  27. package/dist/feishu/serve.js +2 -0
  28. package/dist/goal/command.d.ts +1 -1
  29. package/dist/goal/command.js +7 -2
  30. package/dist/main.d.ts +1 -1
  31. package/dist/main.js +38 -8
  32. package/dist/memory/db.js +0 -7
  33. package/dist/model-catalog.js +25 -6
  34. package/dist/model-config.js +3 -2
  35. package/dist/model-pricing.d.ts +24 -5
  36. package/dist/model-pricing.js +0 -0
  37. package/dist/model-selection.js +13 -4
  38. package/dist/network/chatgpt-transport.js +0 -34
  39. package/dist/network/errors.d.ts +19 -0
  40. package/dist/network/errors.js +24 -0
  41. package/dist/network/provider-transport.d.ts +0 -4
  42. package/dist/network/provider-transport.js +3 -35
  43. package/dist/network/retry.d.ts +6 -1
  44. package/dist/network/retry.js +9 -0
  45. package/dist/provider-model-policy.d.ts +16 -0
  46. package/dist/provider-model-policy.js +33 -0
  47. package/dist/provider-registry.d.ts +1 -1
  48. package/dist/provider-registry.js +73 -10
  49. package/dist/provider-transform.js +32 -4
  50. package/dist/provider.d.ts +6 -2
  51. package/dist/provider.js +95 -6
  52. package/dist/question/controller.js +5 -4
  53. package/dist/question/normalize.d.ts +6 -0
  54. package/dist/question/normalize.js +11 -0
  55. package/dist/rewind.d.ts +20 -0
  56. package/dist/rewind.js +29 -0
  57. package/dist/sdk/index.d.ts +1 -1
  58. package/dist/sdk/index.js +11 -3
  59. package/dist/session.d.ts +7 -0
  60. package/dist/session.js +20 -0
  61. package/dist/skills/discovery.js +18 -0
  62. package/dist/skills/registry.d.ts +12 -2
  63. package/dist/skills/registry.js +40 -5
  64. package/dist/skills/types.d.ts +2 -0
  65. package/dist/slash-commands/commands.js +120 -81
  66. package/dist/slash-commands/registry.d.ts +2 -1
  67. package/dist/slash-commands/registry.js +3 -0
  68. package/dist/slash-commands/types.d.ts +22 -4
  69. package/dist/stats/usage.d.ts +3 -1
  70. package/dist/stats/usage.js +22 -13
  71. package/dist/tasks/manager.d.ts +4 -2
  72. package/dist/tasks/manager.js +34 -9
  73. package/dist/tasks/promotion.d.ts +2 -2
  74. package/dist/tasks/promotion.js +2 -2
  75. package/dist/tools/agent-lifecycle.js +4 -0
  76. package/dist/tools/bash.d.ts +3 -1
  77. package/dist/tools/bash.js +13 -2
  78. package/dist/tools/grep.d.ts +5 -1
  79. package/dist/tools/grep.js +318 -69
  80. package/dist/tools/index.d.ts +3 -1
  81. package/dist/tools/index.js +1 -0
  82. package/dist/tools/question.js +5 -4
  83. package/dist/tools/read.js +40 -21
  84. package/dist/tui/animation-clock.d.ts +14 -0
  85. package/dist/tui/animation-clock.js +34 -0
  86. package/dist/tui/app.d.ts +176 -0
  87. package/dist/tui/app.js +1647 -0
  88. package/dist/tui/components/approval-dialog.d.ts +43 -0
  89. package/dist/tui/components/approval-dialog.js +247 -0
  90. package/dist/tui/components/bottom-sheet.d.ts +9 -0
  91. package/dist/tui/components/bottom-sheet.js +32 -0
  92. package/dist/tui/components/context-info.d.ts +54 -0
  93. package/dist/tui/components/context-info.js +388 -0
  94. package/dist/tui/components/feedback-dialog.d.ts +56 -0
  95. package/dist/tui/components/feedback-dialog.js +262 -0
  96. package/dist/tui/components/feishu-setup-dialog.d.ts +65 -0
  97. package/dist/tui/components/feishu-setup-dialog.js +332 -0
  98. package/dist/tui/components/image-preview.d.ts +32 -0
  99. package/dist/tui/components/image-preview.js +130 -0
  100. package/dist/tui/components/plan-dialog.d.ts +34 -0
  101. package/dist/tui/components/plan-dialog.js +196 -0
  102. package/dist/tui/components/provider-key-input.d.ts +18 -0
  103. package/dist/tui/components/provider-key-input.js +49 -0
  104. package/dist/tui/components/question-dialog.d.ts +25 -0
  105. package/dist/tui/components/question-dialog.js +263 -0
  106. package/dist/tui/components/responsive-transcript.d.ts +31 -0
  107. package/dist/tui/components/responsive-transcript.js +77 -0
  108. package/dist/tui/components/rewind-picker.d.ts +62 -0
  109. package/dist/tui/components/rewind-picker.js +317 -0
  110. package/dist/tui/components/session-picker.d.ts +49 -0
  111. package/dist/tui/components/session-picker.js +303 -0
  112. package/dist/tui/components/skills-panel.d.ts +59 -0
  113. package/dist/tui/components/skills-panel.js +568 -0
  114. package/dist/tui/components/stats-panel.d.ts +40 -0
  115. package/dist/tui/components/stats-panel.js +301 -0
  116. package/dist/tui/components/streaming-message.d.ts +69 -0
  117. package/dist/tui/components/streaming-message.js +393 -0
  118. package/dist/tui/components/subagent-inspector.d.ts +28 -0
  119. package/dist/tui/components/subagent-inspector.js +127 -0
  120. package/dist/tui/components/task-inspector.d.ts +26 -0
  121. package/dist/tui/components/task-inspector.js +70 -0
  122. package/dist/tui/components/tasks-pane.d.ts +88 -0
  123. package/dist/tui/components/tasks-pane.js +416 -0
  124. package/dist/tui/components/transcript.d.ts +96 -0
  125. package/dist/tui/components/transcript.js +771 -0
  126. package/dist/tui/components/welcome.d.ts +20 -0
  127. package/dist/tui/components/welcome.js +133 -0
  128. package/dist/tui/components/workflow-inspector.d.ts +30 -0
  129. package/dist/tui/components/workflow-inspector.js +136 -0
  130. package/dist/tui/composer-autocomplete.d.ts +61 -0
  131. package/dist/tui/composer-autocomplete.js +383 -0
  132. package/dist/tui/composer-style.d.ts +7 -0
  133. package/dist/tui/composer-style.js +29 -0
  134. package/dist/tui/controller/agent-event-reducer.d.ts +75 -0
  135. package/dist/tui/controller/agent-event-reducer.js +238 -0
  136. package/dist/tui/controller/composer-controller.d.ts +95 -0
  137. package/dist/tui/controller/composer-controller.js +502 -0
  138. package/dist/tui/controller/controller.d.ts +194 -0
  139. package/dist/tui/controller/controller.js +1035 -0
  140. package/dist/tui/controller/effects.d.ts +71 -0
  141. package/dist/tui/controller/effects.js +1 -0
  142. package/dist/tui/controller/goal-runtime-controller.d.ts +47 -0
  143. package/dist/tui/controller/goal-runtime-controller.js +202 -0
  144. package/dist/tui/controller/input-queue-machine.d.ts +71 -0
  145. package/dist/tui/controller/input-queue-machine.js +121 -0
  146. package/dist/tui/controller/intents.d.ts +56 -0
  147. package/dist/tui/controller/intents.js +6 -0
  148. package/dist/tui/controller/overlay-controller.d.ts +64 -0
  149. package/dist/tui/controller/overlay-controller.js +129 -0
  150. package/dist/tui/controller/ports.d.ts +57 -0
  151. package/dist/tui/controller/ports.js +9 -0
  152. package/dist/tui/controller/session-transition.d.ts +54 -0
  153. package/dist/tui/controller/session-transition.js +68 -0
  154. package/dist/tui/controller/state.d.ts +14 -0
  155. package/dist/tui/controller/state.js +36 -0
  156. package/dist/tui/controller/task-runtime-controller.d.ts +66 -0
  157. package/dist/tui/controller/task-runtime-controller.js +158 -0
  158. package/dist/tui/footer.d.ts +37 -0
  159. package/dist/tui/footer.js +80 -0
  160. package/dist/{tui-ink → tui/formatting}/edit-diff.d.ts +1 -1
  161. package/dist/{tui-ink → tui/formatting}/edit-diff.js +1 -1
  162. package/dist/{tui-ink/app-helpers.d.ts → tui/formatting/summary.d.ts} +4 -4
  163. package/dist/{tui-ink/app-helpers.js → tui/formatting/summary.js} +2 -2
  164. package/dist/tui/fullscreen.d.ts +48 -0
  165. package/dist/tui/fullscreen.js +255 -0
  166. package/dist/tui/image-display.d.ts +4 -0
  167. package/dist/tui/image-display.js +30 -12
  168. package/dist/tui/markdown-style.d.ts +5 -0
  169. package/dist/tui/markdown-style.js +23 -0
  170. package/dist/{tui-ink → tui/model}/composer-buffer.js +1 -1
  171. package/dist/tui/model/composer-types.d.ts +17 -0
  172. package/dist/tui/model/composer-types.js +1 -0
  173. package/dist/{tui-ink → tui/model}/display-history.d.ts +7 -1
  174. package/dist/{tui-ink → tui/model}/display-reconstruct.d.ts +1 -1
  175. package/dist/{tui-ink → tui/model}/display-reconstruct.js +54 -6
  176. package/dist/{tui-ink → tui/model}/file-mentions.d.ts +2 -0
  177. package/dist/{tui-ink → tui/model}/file-mentions.js +4 -0
  178. package/dist/tui/model/image-attachment.d.ts +15 -0
  179. package/dist/tui/model/image-attachment.js +1 -0
  180. package/dist/{tui-ink → tui/model}/image-paste.d.ts +7 -1
  181. package/dist/{tui-ink → tui/model}/image-paste.js +46 -0
  182. package/dist/tui/model/input-history.d.ts +1 -0
  183. package/dist/tui/model/input-history.js +1 -0
  184. package/dist/{tui-ink → tui/model}/input-queue.d.ts +1 -1
  185. package/dist/{tui-ink → tui/model}/recent-activity.js +1 -1
  186. package/dist/{tui-ink → tui/model}/subagent-view.d.ts +17 -2
  187. package/dist/{tui-ink → tui/model}/subagent-view.js +125 -4
  188. package/dist/{tui-ink → tui/model}/submit-dedupe.d.ts +1 -1
  189. package/dist/tui/model/theme-style.d.ts +10 -0
  190. package/dist/tui/model/theme-style.js +79 -0
  191. package/dist/{tui-ink → tui/model}/theme.d.ts +11 -23
  192. package/dist/{tui-ink → tui/model}/theme.js +23 -38
  193. package/dist/{tui-ink → tui/model}/trace-groups.d.ts +2 -0
  194. package/dist/{tui-ink → tui/model}/trace-groups.js +91 -30
  195. package/dist/tui/model/trace-interaction.d.ts +63 -0
  196. package/dist/tui/model/trace-interaction.js +87 -0
  197. package/dist/tui/model-picker-data.d.ts +1 -2
  198. package/dist/tui/model-picker-data.js +8 -1
  199. package/dist/tui/model-switch.js +2 -0
  200. package/dist/{tui-ink → tui}/run-session-picker.d.ts +2 -3
  201. package/dist/tui/run-session-picker.js +64 -0
  202. package/dist/tui/run.d.ts +72 -0
  203. package/dist/tui/run.js +139 -0
  204. package/dist/tui/terminal/multiplexer.d.ts +5 -0
  205. package/dist/tui/terminal/multiplexer.js +14 -0
  206. package/dist/tui/testing/fake-agent.d.ts +24 -0
  207. package/dist/tui/testing/fake-agent.js +22 -0
  208. package/dist/tui/testing/spy-host.d.ts +21 -0
  209. package/dist/tui/testing/spy-host.js +55 -0
  210. package/dist/tui/trace-groups.js +27 -6
  211. package/dist/types.d.ts +7 -0
  212. package/dist/update/index.d.ts +1 -0
  213. package/dist/update/index.js +22 -17
  214. package/node_modules/@bubblebrain-ai/pi-tui/LICENSE +21 -0
  215. package/node_modules/@bubblebrain-ai/pi-tui/README.md +854 -0
  216. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.d.ts +26 -0
  217. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.d.ts.map +1 -0
  218. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.js +121 -0
  219. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.js.map +1 -0
  220. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.d.ts +86 -0
  221. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.d.ts.map +1 -0
  222. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.js +649 -0
  223. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.js.map +1 -0
  224. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.d.ts +13 -0
  225. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.d.ts.map +1 -0
  226. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.js +36 -0
  227. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.js.map +1 -0
  228. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.d.ts +22 -0
  229. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.d.ts.map +1 -0
  230. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.js +99 -0
  231. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.js.map +1 -0
  232. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.d.ts +22 -0
  233. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.d.ts.map +1 -0
  234. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.js +36 -0
  235. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.js.map +1 -0
  236. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.d.ts +330 -0
  237. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.d.ts.map +1 -0
  238. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.js +2344 -0
  239. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.js.map +1 -0
  240. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.d.ts +7 -0
  241. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.d.ts.map +1 -0
  242. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.js +43 -0
  243. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.js.map +1 -0
  244. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.d.ts +28 -0
  245. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.d.ts.map +1 -0
  246. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.js +82 -0
  247. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.js.map +1 -0
  248. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.d.ts +48 -0
  249. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.d.ts.map +1 -0
  250. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.js +390 -0
  251. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.js.map +1 -0
  252. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.d.ts +31 -0
  253. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.d.ts.map +1 -0
  254. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.js +67 -0
  255. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.js.map +1 -0
  256. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.d.ts +103 -0
  257. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.d.ts.map +1 -0
  258. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.js +797 -0
  259. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.js.map +1 -0
  260. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.d.ts +56 -0
  261. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.d.ts.map +1 -0
  262. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.js +179 -0
  263. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.js.map +1 -0
  264. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.d.ts +53 -0
  265. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.d.ts.map +1 -0
  266. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.js +175 -0
  267. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.js.map +1 -0
  268. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.d.ts +50 -0
  269. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.d.ts.map +1 -0
  270. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.js +174 -0
  271. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.js.map +1 -0
  272. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.d.ts +12 -0
  273. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.d.ts.map +1 -0
  274. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.js +22 -0
  275. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.js.map +1 -0
  276. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.d.ts +32 -0
  277. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.d.ts.map +1 -0
  278. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.js +109 -0
  279. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.js.map +1 -0
  280. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.d.ts +19 -0
  281. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.d.ts.map +1 -0
  282. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.js +81 -0
  283. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.js.map +1 -0
  284. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.d.ts +13 -0
  285. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.d.ts.map +1 -0
  286. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.js +48 -0
  287. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.js.map +1 -0
  288. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.d.ts +8 -0
  289. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.d.ts.map +1 -0
  290. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.js +26 -0
  291. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.js.map +1 -0
  292. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.d.ts +39 -0
  293. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.d.ts.map +1 -0
  294. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.js +2 -0
  295. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.js.map +1 -0
  296. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.d.ts +16 -0
  297. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.d.ts.map +1 -0
  298. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.js +110 -0
  299. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.js.map +1 -0
  300. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.d.ts +31 -0
  301. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.d.ts.map +1 -0
  302. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.js +42 -0
  303. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.js.map +1 -0
  304. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.d.ts +273 -0
  305. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.d.ts.map +1 -0
  306. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.js +231 -0
  307. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.js.map +1 -0
  308. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.d.ts +184 -0
  309. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.d.ts.map +1 -0
  310. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.js +1174 -0
  311. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.js.map +1 -0
  312. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.d.ts +28 -0
  313. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.d.ts.map +1 -0
  314. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.js +46 -0
  315. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.js.map +1 -0
  316. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.d.ts +10 -0
  317. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.d.ts.map +1 -0
  318. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.js +1268 -0
  319. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.js.map +1 -0
  320. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.d.ts +40 -0
  321. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.d.ts.map +1 -0
  322. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.js +6 -0
  323. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.js.map +1 -0
  324. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.d.ts +42 -0
  325. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.d.ts.map +1 -0
  326. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.js +335 -0
  327. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.js.map +1 -0
  328. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.d.ts +3 -0
  329. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.d.ts.map +1 -0
  330. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.js +60 -0
  331. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.js.map +1 -0
  332. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.d.ts +56 -0
  333. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.d.ts.map +1 -0
  334. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.js +363 -0
  335. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.js.map +1 -0
  336. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.d.ts +10 -0
  337. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.d.ts.map +1 -0
  338. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.js +59 -0
  339. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.js.map +1 -0
  340. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.d.ts +116 -0
  341. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.d.ts.map +1 -0
  342. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.js +518 -0
  343. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.js.map +1 -0
  344. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.d.ts +142 -0
  345. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.d.ts.map +1 -0
  346. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.js +544 -0
  347. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.js.map +1 -0
  348. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.d.ts +11 -0
  349. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.d.ts.map +1 -0
  350. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.js +11 -0
  351. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.js.map +1 -0
  352. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.d.ts +140 -0
  353. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.d.ts.map +1 -0
  354. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.js +1334 -0
  355. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.js.map +1 -0
  356. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.d.ts +39 -0
  357. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.d.ts.map +1 -0
  358. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.js +536 -0
  359. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.js.map +1 -0
  360. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.d.ts +338 -0
  361. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.d.ts.map +1 -0
  362. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.js +1016 -0
  363. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.js.map +1 -0
  364. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.d.ts +17 -0
  365. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.d.ts.map +1 -0
  366. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.js +27 -0
  367. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.js.map +1 -0
  368. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.d.ts +96 -0
  369. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.d.ts.map +1 -0
  370. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.js +1193 -0
  371. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.js.map +1 -0
  372. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.d.ts +25 -0
  373. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.d.ts.map +1 -0
  374. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.js +96 -0
  375. package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.js.map +1 -0
  376. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.d.ts +73 -0
  377. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.d.ts.map +1 -0
  378. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.js +188 -0
  379. package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.js.map +1 -0
  380. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/README.md +20 -0
  381. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/build.sh +62 -0
  382. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
  383. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
  384. package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/src/darwin-modifiers.c +70 -0
  385. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/README.md +22 -0
  386. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/build.mjs +229 -0
  387. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  388. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  389. package/node_modules/@bubblebrain-ai/pi-tui/native/win32/src/win32-console-mode.c +119 -0
  390. package/node_modules/@bubblebrain-ai/pi-tui/package.json +66 -0
  391. package/node_modules/get-east-asian-width/index.d.ts +60 -0
  392. package/node_modules/get-east-asian-width/index.js +30 -0
  393. package/node_modules/get-east-asian-width/license +9 -0
  394. package/node_modules/get-east-asian-width/lookup-data.js +21 -0
  395. package/node_modules/get-east-asian-width/lookup.js +138 -0
  396. package/node_modules/get-east-asian-width/package.json +71 -0
  397. package/node_modules/get-east-asian-width/readme.md +65 -0
  398. package/node_modules/get-east-asian-width/utilities.js +24 -0
  399. package/node_modules/marked/LICENSE +44 -0
  400. package/node_modules/marked/README.md +115 -0
  401. package/node_modules/marked/bin/main.js +284 -0
  402. package/node_modules/marked/bin/marked.js +16 -0
  403. package/node_modules/marked/lib/marked.d.ts +760 -0
  404. package/node_modules/marked/lib/marked.esm.js +77 -0
  405. package/node_modules/marked/lib/marked.esm.js.map +7 -0
  406. package/node_modules/marked/lib/marked.umd.js +79 -0
  407. package/node_modules/marked/lib/marked.umd.js.map +7 -0
  408. package/node_modules/marked/man/marked.1 +113 -0
  409. package/node_modules/marked/man/marked.1.md +93 -0
  410. package/node_modules/marked/package.json +103 -0
  411. package/package.json +25 -10
  412. package/dist/tui-ink/app.d.ts +0 -76
  413. package/dist/tui-ink/app.js +0 -2377
  414. package/dist/tui-ink/approval/approval-dialog.d.ts +0 -13
  415. package/dist/tui-ink/approval/approval-dialog.js +0 -184
  416. package/dist/tui-ink/approval/diff-view.d.ts +0 -7
  417. package/dist/tui-ink/approval/diff-view.js +0 -44
  418. package/dist/tui-ink/approval/select.d.ts +0 -35
  419. package/dist/tui-ink/approval/select.js +0 -98
  420. package/dist/tui-ink/code-highlight.d.ts +0 -8
  421. package/dist/tui-ink/code-highlight.js +0 -121
  422. package/dist/tui-ink/command-palette.d.ts +0 -32
  423. package/dist/tui-ink/command-palette.js +0 -158
  424. package/dist/tui-ink/compaction-progress.d.ts +0 -19
  425. package/dist/tui-ink/compaction-progress.js +0 -74
  426. package/dist/tui-ink/detect-theme.d.ts +0 -1
  427. package/dist/tui-ink/detect-theme.js +0 -1
  428. package/dist/tui-ink/feedback-dialog.d.ts +0 -19
  429. package/dist/tui-ink/feedback-dialog.js +0 -133
  430. package/dist/tui-ink/feishu-setup-picker.d.ts +0 -5
  431. package/dist/tui-ink/feishu-setup-picker.js +0 -271
  432. package/dist/tui-ink/footer.d.ts +0 -26
  433. package/dist/tui-ink/footer.js +0 -51
  434. package/dist/tui-ink/input-box.d.ts +0 -189
  435. package/dist/tui-ink/input-box.js +0 -1279
  436. package/dist/tui-ink/input-history.d.ts +0 -1
  437. package/dist/tui-ink/input-history.js +0 -1
  438. package/dist/tui-ink/markdown.d.ts +0 -103
  439. package/dist/tui-ink/markdown.js +0 -747
  440. package/dist/tui-ink/message-list.d.ts +0 -49
  441. package/dist/tui-ink/message-list.js +0 -841
  442. package/dist/tui-ink/model-picker.d.ts +0 -143
  443. package/dist/tui-ink/model-picker.js +0 -966
  444. package/dist/tui-ink/plan-confirm.d.ts +0 -7
  445. package/dist/tui-ink/plan-confirm.js +0 -115
  446. package/dist/tui-ink/question-dialog.d.ts +0 -8
  447. package/dist/tui-ink/question-dialog.js +0 -135
  448. package/dist/tui-ink/rewind-picker.d.ts +0 -8
  449. package/dist/tui-ink/rewind-picker.js +0 -102
  450. package/dist/tui-ink/run-session-picker.js +0 -22
  451. package/dist/tui-ink/run.d.ts +0 -62
  452. package/dist/tui-ink/run.js +0 -103
  453. package/dist/tui-ink/session-picker.d.ts +0 -10
  454. package/dist/tui-ink/session-picker.js +0 -113
  455. package/dist/tui-ink/stats-panel.d.ts +0 -11
  456. package/dist/tui-ink/stats-panel.js +0 -65
  457. package/dist/tui-ink/subagent-inspector.d.ts +0 -21
  458. package/dist/tui-ink/subagent-inspector.js +0 -205
  459. package/dist/tui-ink/use-terminal-size.d.ts +0 -4
  460. package/dist/tui-ink/use-terminal-size.js +0 -48
  461. package/dist/tui-ink/waiting-indicator.d.ts +0 -12
  462. package/dist/tui-ink/waiting-indicator.js +0 -129
  463. package/dist/tui-ink/welcome.d.ts +0 -25
  464. package/dist/tui-ink/welcome.js +0 -88
  465. package/dist/tui-ink/wordmark.d.ts +0 -8
  466. package/dist/tui-ink/wordmark.js +0 -103
  467. /package/dist/{tui-ink → tui/model}/composer-buffer.d.ts +0 -0
  468. /package/dist/{tui-ink → tui/model}/display-history.js +0 -0
  469. /package/dist/{tui-ink → tui/model}/input-queue.js +0 -0
  470. /package/dist/{tui-ink → tui/model}/key-events.d.ts +0 -0
  471. /package/dist/{tui-ink → tui/model}/key-events.js +0 -0
  472. /package/dist/{tui-ink → tui/model}/paste-operation-tracker.d.ts +0 -0
  473. /package/dist/{tui-ink → tui/model}/paste-operation-tracker.js +0 -0
  474. /package/dist/{tui-ink → tui/model}/recent-activity.d.ts +0 -0
  475. /package/dist/{tui-ink → tui/model}/submit-dedupe.js +0 -0
  476. /package/dist/{tui-ink → tui/model}/sync-output.d.ts +0 -0
  477. /package/dist/{tui-ink → tui/model}/sync-output.js +0 -0
  478. /package/dist/{tui-ink → tui/model}/terminal-env.d.ts +0 -0
  479. /package/dist/{tui-ink → tui/model}/terminal-env.js +0 -0
  480. /package/dist/{tui-ink → tui/model}/terminal-mouse.d.ts +0 -0
  481. /package/dist/{tui-ink → tui/model}/terminal-mouse.js +0 -0
  482. /package/dist/{tui-ink → tui/model}/width.d.ts +0 -0
  483. /package/dist/{tui-ink → tui/model}/width.js +0 -0
@@ -0,0 +1,1334 @@
1
+ var _a;
2
+ import { AltScreenSearchComponent, findAltScreenSearchMatches, getAltScreenSearchMatchKey, } from "./alt-screen-search.js";
3
+ import { AltScreenFlashContainer } from "./components/alt-screen-flash.js";
4
+ import { ScrollView } from "./components/scroll-view.js";
5
+ import { getKeybindings } from "./keybindings.js";
6
+ import { isKeyRelease } from "./keys.js";
7
+ import { getScrollbarGeometry, getLayoutBoxesAt, getScrollViewBox, getScrollViewsAt, renderLayoutFrame, } from "./layout.js";
8
+ import { deleteAllKittyImages, deleteAllKittyPlacements, deleteKittyImage, getCapabilities, getKittyImagePlacement, isImageLine, setCapabilities, } from "./terminal-image.js";
9
+ import { CURSOR_MARKER, compositeTuiLine, TuiBase, VIEWPORT_TUI, } from "./tui.js";
10
+ import { extractAnsiCode, getGraphemeCellRange, getOsc8LinkAtColumn, getWordSegmenter, sliceByColumn, stripTerminalSequences, visibleWidth, } from "./utils.js";
11
+ const ENTER_ALT_SCREEN = "\x1b[?1049h";
12
+ const EXIT_ALT_SCREEN = "\x1b[?1049l";
13
+ const DISABLE_AUTOWRAP = "\x1b[?7l";
14
+ const ENABLE_AUTOWRAP = "\x1b[?7h";
15
+ const ENABLE_BUTTON_MOTION_MOUSE = "\x1b[?1000h\x1b[?1002h\x1b[?1004h\x1b[?1006h";
16
+ const ENABLE_ALL_MOTION_MOUSE = "\x1b[?1000h\x1b[?1002h\x1b[?1003h\x1b[?1004h\x1b[?1006h";
17
+ const DISABLE_MOUSE = "\x1b[?1006l\x1b[?1004l\x1b[?1003l\x1b[?1002l\x1b[?1000l";
18
+ const FOCUS_IN = "\x1b[I";
19
+ const FOCUS_OUT = "\x1b[O";
20
+ const BEGIN_SYNCHRONIZED_OUTPUT = "\x1b[?2026h";
21
+ const END_SYNCHRONIZED_OUTPUT = "\x1b[?2026l";
22
+ const OSC133_ZONE_PREFIX = /^(?:\x1b\]133;[ABC](?:\x07|\x1b\\))+/;
23
+ const OSC133_PROMPT_START = /^\x1b\]133;A(?:\x07|\x1b\\)/;
24
+ const PAGE_SCROLL_OVERLAP = 4;
25
+ const MAX_CACHED_OFFSCREEN_KITTY_IMAGES = 16;
26
+ const MAX_CACHED_OFFSCREEN_KITTY_TRANSMISSION_BYTES = 32 * 1024 * 1024;
27
+ const MAX_CACHED_OFFSCREEN_KITTY_DECODED_BYTES = 64 * 1024 * 1024;
28
+ const DOUBLE_CLICK_INTERVAL_MS = 500;
29
+ const wordSegmenter = getWordSegmenter();
30
+ /** Alternate-screen TUI with a scrollable, application-owned viewport. */
31
+ export class TuiAltScreen extends TuiBase {
32
+ static { _a = VIEWPORT_TUI; }
33
+ constructor(terminal, showHardwareCursor, logDirectory, options = {}) {
34
+ super(terminal, showHardwareCursor, logDirectory);
35
+ this.mode = "fullscreen";
36
+ this[_a] = true;
37
+ this.previousScreen = [];
38
+ this.lastDocument = [];
39
+ this.previousScreenWidth = 0;
40
+ this.previousScreenHeight = 0;
41
+ this.altScreenActive = false;
42
+ this.imageProtocol = null;
43
+ this.uploadedKittyImages = new Map();
44
+ this.selectionGranularity = "character";
45
+ this.selectionAutoScrollDirection = 0;
46
+ this.selectionPressActive = false;
47
+ this.selectionDragged = false;
48
+ this.previousScreenBackground = "";
49
+ this.implicitDocument = {
50
+ render: (width) => super.render(width),
51
+ invalidate: () => {
52
+ for (const child of this.children)
53
+ child.invalidate();
54
+ },
55
+ };
56
+ this.implicitScrollView = new ScrollView(this.implicitDocument, { follow: "end", primary: true });
57
+ this.flashes = new AltScreenFlashContainer(() => this.requestRender());
58
+ this.wheelScrollLines = Math.max(1, Math.floor(options.wheelScrollLines ?? 1));
59
+ this.mouseEnabled = options.mouse ?? true;
60
+ this.mouseMotion = options.mouseMotion ?? "auto";
61
+ this.searchMatchStyle = options.searchMatchStyle ?? ((text) => `\x1b[4m${text}\x1b[24m`);
62
+ this.searchCurrentMatchStyle = options.searchCurrentMatchStyle ?? ((text) => `\x1b[1;7m${text}\x1b[22;27m`);
63
+ this.openUrl = options.openUrl;
64
+ this.onRightClickPaste = options.onRightClickPaste;
65
+ this.copySelection = options.copySelection;
66
+ this.screenBackground = options.screenBackground;
67
+ this.addInputListener((data) => this.handleViewportInput(data));
68
+ }
69
+ get viewportTop() {
70
+ return this.getPrimaryScrollView().scrollTop;
71
+ }
72
+ get isFollowingOutput() {
73
+ return this.getPrimaryScrollView().isFollowingEnd;
74
+ }
75
+ setLayoutRoot(component) {
76
+ if (this.layoutRoot === component)
77
+ return;
78
+ this.layoutRoot = component;
79
+ this.currentLayout = undefined;
80
+ this.requestRender();
81
+ }
82
+ render(width) {
83
+ return this.layoutRoot?.render(width) ?? super.render(width);
84
+ }
85
+ getMountedRoots() {
86
+ return this.layoutRoot ? [this.layoutRoot] : this.children;
87
+ }
88
+ getPrimaryScrollView() {
89
+ return this.currentLayout?.primaryScrollView ?? this.implicitScrollView;
90
+ }
91
+ beforeTerminalStart() {
92
+ this.stopSelectionAutoScroll();
93
+ this.selectionPressActive = false;
94
+ this.stopScrollbarHover();
95
+ this.stopScrollbarDrag();
96
+ this.flashes.dispose();
97
+ this.altScreenActive = true;
98
+ const capabilities = getCapabilities();
99
+ this.imageProtocol = capabilities.images;
100
+ this.uploadedKittyImages.clear();
101
+ if (capabilities.images === "iterm2") {
102
+ this.savedCapabilities = capabilities;
103
+ setCapabilities({ ...capabilities, images: null });
104
+ this.invalidate();
105
+ }
106
+ this.lastDocument = [];
107
+ this.selectionAnchor = undefined;
108
+ this.selectionFocus = undefined;
109
+ this.selectionGranularity = "character";
110
+ this.selectionInitialRange = undefined;
111
+ this.lastClick = undefined;
112
+ this.lastComponentClick = undefined;
113
+ this.componentHover = undefined;
114
+ this.pressedUrl = undefined;
115
+ this.selectionDragged = false;
116
+ this.resetRenderState();
117
+ const term = process.env.TERM?.toLowerCase() ?? "";
118
+ // Multiplexers can lag when every pointer movement is forwarded. Button-motion
119
+ // tracking preserves clicks, wheel events, selections, and scrollbar dragging.
120
+ const inMultiplexer = process.env.TMUX !== undefined ||
121
+ process.env.ZELLIJ !== undefined ||
122
+ process.env.STY !== undefined ||
123
+ term.startsWith("tmux") ||
124
+ term.startsWith("screen");
125
+ const mouseSequence = this.mouseMotion === "all"
126
+ ? ENABLE_ALL_MOTION_MOUSE
127
+ : this.mouseMotion === "button" || inMultiplexer
128
+ ? ENABLE_BUTTON_MOTION_MOUSE
129
+ : ENABLE_ALL_MOTION_MOUSE;
130
+ this.terminal.write(`${ENTER_ALT_SCREEN}${DISABLE_AUTOWRAP}${this.mouseEnabled ? mouseSequence : ""}\x1b[2J\x1b[H\x1b[?25l`);
131
+ }
132
+ beforeTerminalStop(_options) {
133
+ this.clearComponentHover(false);
134
+ this.closeSearch();
135
+ this.stopSelectionAutoScroll();
136
+ this.selectionPressActive = false;
137
+ this.stopScrollbarHover();
138
+ this.stopScrollbarDrag();
139
+ this.flashes.dispose();
140
+ if (!this.altScreenActive)
141
+ return;
142
+ this.terminal.write(`${BEGIN_SYNCHRONIZED_OUTPUT}${this.deleteKittyImages()}${this.mouseEnabled ? DISABLE_MOUSE : ""}${ENABLE_AUTOWRAP}${END_SYNCHRONIZED_OUTPUT}`);
143
+ this.uploadedKittyImages.clear();
144
+ }
145
+ afterTerminalStop(options) {
146
+ if (!this.altScreenActive)
147
+ return;
148
+ this.altScreenActive = false;
149
+ if (options.preserveScreen) {
150
+ this.terminal.write(`${BEGIN_SYNCHRONIZED_OUTPUT}${EXIT_ALT_SCREEN}\x1b[?25h${END_SYNCHRONIZED_OUTPUT}`);
151
+ }
152
+ else {
153
+ const width = Math.max(1, this.terminal.columns);
154
+ const documentLines = this.render(width).map((line) => line.replace(OSC133_ZONE_PREFIX, ""));
155
+ this.lastDocument = this.applyLineResets(documentLines.map((line) => line.replaceAll(CURSOR_MARKER, ""))).map((line) => (isImageLine(line) || visibleWidth(line) <= width ? line : sliceByColumn(line, 0, width, true)));
156
+ let buffer = `${BEGIN_SYNCHRONIZED_OUTPUT}${EXIT_ALT_SCREEN}${DISABLE_AUTOWRAP}`;
157
+ for (let row = 0; row < this.lastDocument.length; row++) {
158
+ if (row > 0)
159
+ buffer += "\r\n";
160
+ buffer += `\r\x1b[2K${this.lastDocument[row] ?? ""}`;
161
+ }
162
+ buffer += `\x1b[0m${ENABLE_AUTOWRAP}\r\n\x1b[?25h${END_SYNCHRONIZED_OUTPUT}`;
163
+ this.terminal.write(buffer);
164
+ }
165
+ if (this.savedCapabilities) {
166
+ setCapabilities(this.savedCapabilities);
167
+ this.savedCapabilities = undefined;
168
+ }
169
+ }
170
+ deleteKittyImages() {
171
+ return this.imageProtocol === "kitty" ? deleteAllKittyImages() : "";
172
+ }
173
+ prepareKittyScreen(screen) {
174
+ const visibleImageIds = new Set();
175
+ const lines = screen.map((line) => {
176
+ const placement = getKittyImagePlacement(line);
177
+ if (!placement)
178
+ return line;
179
+ visibleImageIds.add(placement.imageId);
180
+ const cachedImage = this.uploadedKittyImages.get(placement.imageId);
181
+ const nextCachedImage = {
182
+ transmissionGeneration: placement.transmissionGeneration,
183
+ transmissionBytes: placement.transmissionBytes,
184
+ estimatedDecodedBytes: placement.estimatedDecodedBytes,
185
+ };
186
+ if (cachedImage)
187
+ this.uploadedKittyImages.delete(placement.imageId);
188
+ this.uploadedKittyImages.set(placement.imageId, nextCachedImage);
189
+ return cachedImage?.transmissionGeneration === placement.transmissionGeneration
190
+ ? placement.replacementLine
191
+ : line;
192
+ });
193
+ let cachedOffscreenImageCount = 0;
194
+ let cachedOffscreenTransmissionBytes = 0;
195
+ let cachedOffscreenDecodedBytes = 0;
196
+ for (const [imageId, cachedImage] of this.uploadedKittyImages) {
197
+ if (visibleImageIds.has(imageId))
198
+ continue;
199
+ cachedOffscreenImageCount += 1;
200
+ cachedOffscreenTransmissionBytes += cachedImage.transmissionBytes;
201
+ cachedOffscreenDecodedBytes += cachedImage.estimatedDecodedBytes;
202
+ }
203
+ let evictedImageDeletion = "";
204
+ for (const [imageId, cachedImage] of this.uploadedKittyImages) {
205
+ if (cachedOffscreenImageCount <= MAX_CACHED_OFFSCREEN_KITTY_IMAGES &&
206
+ cachedOffscreenTransmissionBytes <= MAX_CACHED_OFFSCREEN_KITTY_TRANSMISSION_BYTES &&
207
+ cachedOffscreenDecodedBytes <= MAX_CACHED_OFFSCREEN_KITTY_DECODED_BYTES) {
208
+ break;
209
+ }
210
+ if (visibleImageIds.has(imageId))
211
+ continue;
212
+ evictedImageDeletion += deleteKittyImage(imageId);
213
+ this.uploadedKittyImages.delete(imageId);
214
+ cachedOffscreenImageCount -= 1;
215
+ cachedOffscreenTransmissionBytes -= cachedImage.transmissionBytes;
216
+ cachedOffscreenDecodedBytes -= cachedImage.estimatedDecodedBytes;
217
+ }
218
+ return { lines, evictedImageDeletion };
219
+ }
220
+ resetRenderState() {
221
+ this.previousScreen = [];
222
+ this.previousScreenWidth = 0;
223
+ this.previousScreenHeight = 0;
224
+ this.previousScreenBackground = "";
225
+ this.currentLayout = undefined;
226
+ }
227
+ scrollBy(lines) {
228
+ this.getPrimaryScrollView().scrollBy(lines);
229
+ this.requestRender();
230
+ }
231
+ scrollToTop() {
232
+ this.getPrimaryScrollView().scrollToStart();
233
+ this.requestRender();
234
+ }
235
+ scrollToBottom() {
236
+ this.getPrimaryScrollView().scrollToEnd();
237
+ this.requestRender();
238
+ }
239
+ scrollToPrompt(direction) {
240
+ if (!this.currentLayout)
241
+ return;
242
+ const scrollView = this.getPrimaryScrollView();
243
+ const lines = getScrollViewBox(this.currentLayout, scrollView)?.scrollContentLines;
244
+ if (!lines)
245
+ return;
246
+ for (let row = scrollView.scrollTop + direction; row >= 0 && row < lines.length; row += direction) {
247
+ if (!OSC133_PROMPT_START.test(lines[row] ?? ""))
248
+ continue;
249
+ scrollView.scrollTo(row);
250
+ this.requestRender();
251
+ return;
252
+ }
253
+ }
254
+ openSearch() {
255
+ if (this.activeSearch) {
256
+ this.activeSearch.overlay?.focus();
257
+ return;
258
+ }
259
+ const component = new AltScreenSearchComponent((query) => this.updateSearchQuery(query));
260
+ const search = {
261
+ component,
262
+ query: "",
263
+ matches: [],
264
+ selectedIndex: -1,
265
+ anchorRow: this.getPrimaryScrollView().scrollTop,
266
+ selectionMode: "query",
267
+ };
268
+ this.activeSearch = search;
269
+ search.overlay = this.showOverlay(component, {
270
+ anchor: "top-right",
271
+ width: "40%",
272
+ minWidth: 24,
273
+ margin: 1,
274
+ });
275
+ }
276
+ closeSearch() {
277
+ const search = this.activeSearch;
278
+ if (!search)
279
+ return;
280
+ this.activeSearch = undefined;
281
+ search.overlay?.hide();
282
+ this.requestRender();
283
+ }
284
+ updateSearchQuery(query) {
285
+ const search = this.activeSearch;
286
+ if (!search || query === search.query)
287
+ return;
288
+ const selected = search.matches[search.selectedIndex];
289
+ search.anchorRow = selected?.segments[0]?.row ?? this.getPrimaryScrollView().scrollTop;
290
+ search.query = query;
291
+ search.selectionMode = "query";
292
+ search.component.setResult(-1, 0);
293
+ this.requestRender();
294
+ }
295
+ navigateSearch(direction) {
296
+ const search = this.activeSearch;
297
+ if (!search?.query)
298
+ return;
299
+ search.selectionMode = direction < 0 ? "previous" : "next";
300
+ this.requestRender();
301
+ }
302
+ refreshSearch(layout) {
303
+ const search = this.activeSearch;
304
+ if (!search)
305
+ return false;
306
+ const scrollView = layout.primaryScrollView ?? this.implicitScrollView;
307
+ const box = getScrollViewBox(layout, scrollView);
308
+ const lines = box?.scrollContentLines;
309
+ if (!lines || !search.query.trim()) {
310
+ search.matches = [];
311
+ search.selectedIndex = -1;
312
+ search.selectedKey = undefined;
313
+ search.selectionMode = "retain";
314
+ search.component.setResult(-1, 0);
315
+ return false;
316
+ }
317
+ const shouldRevealSelection = search.selectionMode !== "retain";
318
+ const matches = findAltScreenSearchMatches(lines, search.query);
319
+ const exactIndex = search.selectedKey
320
+ ? matches.findIndex((match) => getAltScreenSearchMatchKey(match) === search.selectedKey)
321
+ : -1;
322
+ let selectedIndex = -1;
323
+ if (matches.length > 0) {
324
+ if (search.selectionMode === "query") {
325
+ selectedIndex = matches.findIndex((match) => (match.segments[0]?.row ?? 0) >= search.anchorRow);
326
+ if (selectedIndex < 0)
327
+ selectedIndex = 0;
328
+ }
329
+ else if (search.selectionMode === "next") {
330
+ const baseIndex = exactIndex >= 0 ? exactIndex : Math.min(search.selectedIndex, matches.length - 1);
331
+ selectedIndex = baseIndex < 0 ? 0 : (baseIndex + 1) % matches.length;
332
+ }
333
+ else if (search.selectionMode === "previous") {
334
+ const baseIndex = exactIndex >= 0 ? exactIndex : Math.min(search.selectedIndex, matches.length - 1);
335
+ selectedIndex = baseIndex < 0 ? matches.length - 1 : (baseIndex - 1 + matches.length) % matches.length;
336
+ }
337
+ else {
338
+ selectedIndex =
339
+ exactIndex >= 0 ? exactIndex : Math.min(Math.max(0, search.selectedIndex), matches.length - 1);
340
+ }
341
+ }
342
+ search.matches = matches;
343
+ search.selectedIndex = selectedIndex;
344
+ search.selectedKey = selectedIndex >= 0 ? getAltScreenSearchMatchKey(matches[selectedIndex]) : undefined;
345
+ search.selectionMode = "retain";
346
+ search.component.setResult(selectedIndex, matches.length);
347
+ if (!shouldRevealSelection)
348
+ return false;
349
+ const selected = matches[selectedIndex];
350
+ const firstSegment = selected?.segments[0];
351
+ const lastSegment = selected?.segments[selected.segments.length - 1];
352
+ if (!box || !firstSegment || !lastSegment || scrollView.viewportHeight <= 0)
353
+ return false;
354
+ const before = scrollView.scrollTop;
355
+ const visibleBottom = before + scrollView.viewportHeight - 1;
356
+ let target = before;
357
+ if (firstSegment.row < before || lastSegment.row > visibleBottom) {
358
+ target = firstSegment.row - Math.floor(scrollView.viewportHeight / 3);
359
+ }
360
+ scrollView.scrollTo(target, { disableFollow: true });
361
+ return scrollView.scrollTop !== before;
362
+ }
363
+ /** Show a transient message in the alternate-screen flash stack. */
364
+ flash(message, durationMs) {
365
+ this.flashes.flash(message, durationMs);
366
+ }
367
+ shouldDeferViewportInputToOverlay() {
368
+ return this.isOverlayFocused() && this.activeSearch?.overlay?.isFocused() !== true;
369
+ }
370
+ handleViewportInput(data) {
371
+ if (data === FOCUS_OUT) {
372
+ const hadActiveSelection = this.selectionPressActive;
373
+ const hadNonEmptyActiveSelection = hadActiveSelection && this.getSelectionBounds() !== undefined;
374
+ this.selectionPressActive = false;
375
+ this.stopSelectionAutoScroll();
376
+ this.stopScrollbarHover();
377
+ this.stopScrollbarDrag();
378
+ this.pressedUrl = undefined;
379
+ this.selectionDragged = false;
380
+ if (hadActiveSelection) {
381
+ this.selectionAnchor = undefined;
382
+ this.selectionFocus = undefined;
383
+ this.selectionGranularity = "character";
384
+ this.selectionInitialRange = undefined;
385
+ if (hadNonEmptyActiveSelection)
386
+ this.requestRender();
387
+ }
388
+ this.lastClick = undefined;
389
+ this.lastComponentClick = undefined;
390
+ this.clearComponentHover();
391
+ return { consume: true };
392
+ }
393
+ if (data === FOCUS_IN)
394
+ return { consume: true };
395
+ const wheelEvent = this.parseWheelEvent(data);
396
+ if (wheelEvent) {
397
+ if (this.shouldDeferViewportInputToOverlay())
398
+ return undefined;
399
+ this.routeWheel(wheelEvent);
400
+ return { consume: true };
401
+ }
402
+ const mouseEvent = this.parseSgrMouseEvent(data);
403
+ if (mouseEvent) {
404
+ if (this.handleRightClickPaste(mouseEvent))
405
+ return { consume: true };
406
+ if (this.handleOverlayMouseEvent(mouseEvent))
407
+ return { consume: true };
408
+ const handled = this.handleScrollbarMouseEvent(mouseEvent);
409
+ if (!this.scrollbarDrag)
410
+ this.updateScrollbarHover(mouseEvent.x, mouseEvent.y);
411
+ const pointerOnScrollbar = this.isPointerMove(mouseEvent)
412
+ && this.getScrollbarTargetAt(mouseEvent.x, mouseEvent.y) !== undefined;
413
+ const componentHandled = !handled && !pointerOnScrollbar && this.handleComponentMouseEvent(mouseEvent);
414
+ if ((handled || pointerOnScrollbar) && this.isPointerMove(mouseEvent))
415
+ this.clearComponentHover();
416
+ if (!handled && !componentHandled)
417
+ this.handleSelectionMouseEvent(mouseEvent);
418
+ return { consume: true };
419
+ }
420
+ if (this.isMouseSequence(data))
421
+ return { consume: true };
422
+ const keybindings = getKeybindings();
423
+ const isRelease = isKeyRelease(data);
424
+ if (keybindings.matches(data, "tui.altScreen.search")) {
425
+ if (!isRelease)
426
+ this.openSearch();
427
+ return { consume: true };
428
+ }
429
+ if (this.activeSearch?.overlay?.isFocused()) {
430
+ if (keybindings.matches(data, "tui.altScreen.searchNext")) {
431
+ if (!isRelease)
432
+ this.navigateSearch(1);
433
+ return { consume: true };
434
+ }
435
+ if (keybindings.matches(data, "tui.altScreen.searchPrevious")) {
436
+ if (!isRelease)
437
+ this.navigateSearch(-1);
438
+ return { consume: true };
439
+ }
440
+ if (keybindings.matches(data, "tui.altScreen.searchClose")) {
441
+ if (!isRelease)
442
+ this.closeSearch();
443
+ return { consume: true };
444
+ }
445
+ }
446
+ if (this.shouldDeferViewportInputToOverlay())
447
+ return undefined;
448
+ if (keybindings.matches(data, "tui.altScreen.pageUp")) {
449
+ if (!isRelease) {
450
+ this.scrollBy(-Math.max(1, this.getPrimaryScrollView().viewportHeight - PAGE_SCROLL_OVERLAP));
451
+ }
452
+ return { consume: true };
453
+ }
454
+ if (keybindings.matches(data, "tui.altScreen.pageDown")) {
455
+ if (!isRelease) {
456
+ this.scrollBy(Math.max(1, this.getPrimaryScrollView().viewportHeight - PAGE_SCROLL_OVERLAP));
457
+ }
458
+ return { consume: true };
459
+ }
460
+ if (keybindings.matches(data, "tui.altScreen.halfPageUp")) {
461
+ if (!isRelease)
462
+ this.scrollBy(-Math.max(1, Math.floor(this.getPrimaryScrollView().viewportHeight / 2)));
463
+ return { consume: true };
464
+ }
465
+ if (keybindings.matches(data, "tui.altScreen.halfPageDown")) {
466
+ if (!isRelease)
467
+ this.scrollBy(Math.max(1, Math.floor(this.getPrimaryScrollView().viewportHeight / 2)));
468
+ return { consume: true };
469
+ }
470
+ if (keybindings.matches(data, "tui.altScreen.lineUp")) {
471
+ if (!isRelease)
472
+ this.scrollBy(-1);
473
+ return { consume: true };
474
+ }
475
+ if (keybindings.matches(data, "tui.altScreen.lineDown")) {
476
+ if (!isRelease)
477
+ this.scrollBy(1);
478
+ return { consume: true };
479
+ }
480
+ if (keybindings.matches(data, "tui.altScreen.previousPrompt")) {
481
+ if (!isRelease)
482
+ this.scrollToPrompt(-1);
483
+ return { consume: true };
484
+ }
485
+ if (keybindings.matches(data, "tui.altScreen.nextPrompt")) {
486
+ if (!isRelease)
487
+ this.scrollToPrompt(1);
488
+ return { consume: true };
489
+ }
490
+ if (keybindings.matches(data, "tui.altScreen.top")) {
491
+ if (!isRelease)
492
+ this.scrollToTop();
493
+ return { consume: true };
494
+ }
495
+ if (keybindings.matches(data, "tui.altScreen.bottom")) {
496
+ if (!isRelease)
497
+ this.scrollToBottom();
498
+ return { consume: true };
499
+ }
500
+ return undefined;
501
+ }
502
+ parseWheelEvent(data) {
503
+ const sgr = /^\x1b\[<(\d+);(\d+);(\d+)[Mm]$/.exec(data);
504
+ if (sgr) {
505
+ const button = Number.parseInt(sgr[1], 10);
506
+ if ((button & 64) === 0)
507
+ return undefined;
508
+ const direction = button & 3;
509
+ if (direction !== 0 && direction !== 1)
510
+ return undefined;
511
+ return {
512
+ direction: direction === 0 ? -1 : 1,
513
+ x: Number.parseInt(sgr[2], 10) - 1,
514
+ y: Number.parseInt(sgr[3], 10) - 1,
515
+ };
516
+ }
517
+ if (data.length === 6 && data.startsWith("\x1b[M")) {
518
+ const button = data.charCodeAt(3) - 32;
519
+ if ((button & 64) === 0)
520
+ return undefined;
521
+ const direction = button & 3;
522
+ if (direction !== 0 && direction !== 1)
523
+ return undefined;
524
+ return {
525
+ direction: direction === 0 ? -1 : 1,
526
+ x: data.charCodeAt(4) - 33,
527
+ y: data.charCodeAt(5) - 33,
528
+ };
529
+ }
530
+ return undefined;
531
+ }
532
+ routeWheel(event) {
533
+ let remaining = event.direction * this.wheelScrollLines;
534
+ const seen = new Set();
535
+ for (const scrollView of this.currentLayout ? getScrollViewsAt(this.currentLayout, event.x, event.y) : []) {
536
+ seen.add(scrollView);
537
+ remaining = scrollView.scrollBy(remaining);
538
+ if (remaining === 0 || scrollView.overscroll === "contain")
539
+ break;
540
+ }
541
+ const primary = this.getPrimaryScrollView();
542
+ if (remaining !== 0 && !seen.has(primary))
543
+ primary.scrollBy(remaining);
544
+ this.updateScrollbarHover(event.x, event.y);
545
+ this.requestRender();
546
+ }
547
+ parseSgrMouseEvent(data) {
548
+ const match = /^\x1b\[<(\d+);(\d+);(\d+)([Mm])$/.exec(data);
549
+ if (!match)
550
+ return undefined;
551
+ return {
552
+ button: Number.parseInt(match[1], 10),
553
+ x: Number.parseInt(match[2], 10) - 1,
554
+ y: Number.parseInt(match[3], 10) - 1,
555
+ release: match[4] === "m",
556
+ };
557
+ }
558
+ handleRightClickPaste(event) {
559
+ if (!this.onRightClickPaste ||
560
+ process.platform !== "win32" ||
561
+ process.env.TERM_PROGRAM?.toLowerCase() === "vscode" ||
562
+ event.release ||
563
+ event.button !== 2) {
564
+ return false;
565
+ }
566
+ try {
567
+ this.onRightClickPaste();
568
+ }
569
+ catch {
570
+ // Clipboard paste is best-effort.
571
+ }
572
+ return true;
573
+ }
574
+ handleComponentMouseEvent(event) {
575
+ if (this.isPointerMove(event)) {
576
+ this.handleComponentHoverMove(event);
577
+ return false;
578
+ }
579
+ if (this.hasOverlay()
580
+ || !this.currentLayout
581
+ || event.release
582
+ || (event.button & 32) !== 0
583
+ || (event.button & 3) !== 0) {
584
+ return false;
585
+ }
586
+ for (const box of getLayoutBoxesAt(this.currentLayout, event.x, event.y)) {
587
+ const handler = box.component.handleMouse;
588
+ if (!handler)
589
+ continue;
590
+ const localRow = event.y - box.rect.y;
591
+ const now = Date.now();
592
+ const previous = this.lastComponentClick;
593
+ const clickCount = previous
594
+ && previous.component === box.component
595
+ && previous.row === localRow
596
+ && now - previous.timestamp <= DOUBLE_CLICK_INTERVAL_MS
597
+ && previous.count === 1
598
+ ? 2
599
+ : 1;
600
+ const pointerEvent = {
601
+ kind: "press",
602
+ button: event.button,
603
+ x: event.x - box.rect.x,
604
+ y: localRow,
605
+ release: false,
606
+ clickCount,
607
+ };
608
+ if (!handler.call(box.component, pointerEvent))
609
+ continue;
610
+ this.lastComponentClick = { timestamp: now, component: box.component, row: localRow, count: clickCount };
611
+ this.selectionPressActive = false;
612
+ this.stopSelectionAutoScroll();
613
+ this.selectionAnchor = undefined;
614
+ this.selectionFocus = undefined;
615
+ this.selectionInitialRange = undefined;
616
+ this.selectionDragged = false;
617
+ this.pressedUrl = undefined;
618
+ this.lastClick = undefined;
619
+ // A fold changes content below the clicked row. If the viewport was
620
+ // following the end, pin its current top so the clicked header stays
621
+ // under the pointer instead of jumping upward with the new content.
622
+ const scrollView = getScrollViewsAt(this.currentLayout, event.x, event.y)[0];
623
+ scrollView?.scrollTo(scrollView.scrollTop, { disableFollow: true });
624
+ this.requestRender();
625
+ return true;
626
+ }
627
+ this.lastComponentClick = undefined;
628
+ return false;
629
+ }
630
+ handleOverlayMouseEvent(event) {
631
+ if (!this.hasOverlay())
632
+ return false;
633
+ const target = this.getOverlayMouseTargetAt(event.x, event.y);
634
+ const handler = target?.component.handleMouse;
635
+ if (this.isPointerMove(event)) {
636
+ if (!target || !handler) {
637
+ this.clearComponentHover();
638
+ return true;
639
+ }
640
+ if (this.componentHover?.component !== target.component)
641
+ this.clearComponentHover();
642
+ const pointerEvent = {
643
+ kind: "move",
644
+ button: event.button,
645
+ x: target.x,
646
+ y: target.y,
647
+ release: false,
648
+ clickCount: 1,
649
+ };
650
+ const changed = handler.call(target.component, pointerEvent);
651
+ this.componentHover = {
652
+ component: target.component,
653
+ x: pointerEvent.x,
654
+ y: pointerEvent.y,
655
+ };
656
+ if (changed)
657
+ this.requestRender();
658
+ return true;
659
+ }
660
+ if (event.release || (event.button & 32) !== 0 || (event.button & 3) !== 0)
661
+ return true;
662
+ if (!target) {
663
+ this.dismissTopmostOverlayOnOutsideClick();
664
+ return true;
665
+ }
666
+ if (!handler)
667
+ return true;
668
+ const now = Date.now();
669
+ const previous = this.lastComponentClick;
670
+ const clickCount = previous
671
+ && previous.component === target.component
672
+ && previous.row === target.y
673
+ && now - previous.timestamp <= DOUBLE_CLICK_INTERVAL_MS
674
+ && previous.count === 1
675
+ ? 2
676
+ : 1;
677
+ const handled = handler.call(target.component, {
678
+ kind: "press",
679
+ button: event.button,
680
+ x: target.x,
681
+ y: target.y,
682
+ release: false,
683
+ clickCount,
684
+ });
685
+ if (!handled)
686
+ return true;
687
+ this.lastComponentClick = {
688
+ timestamp: now,
689
+ component: target.component,
690
+ row: target.y,
691
+ count: clickCount,
692
+ };
693
+ this.selectionPressActive = false;
694
+ this.stopSelectionAutoScroll();
695
+ this.selectionAnchor = undefined;
696
+ this.selectionFocus = undefined;
697
+ this.selectionInitialRange = undefined;
698
+ this.selectionDragged = false;
699
+ this.pressedUrl = undefined;
700
+ this.lastClick = undefined;
701
+ this.requestRender();
702
+ return true;
703
+ }
704
+ isPointerMove(event) {
705
+ return !event.release && (event.button & 32) !== 0 && (event.button & 3) === 3;
706
+ }
707
+ handleComponentHoverMove(event) {
708
+ if (this.hasOverlay() || !this.currentLayout) {
709
+ this.clearComponentHover();
710
+ return;
711
+ }
712
+ const box = getLayoutBoxesAt(this.currentLayout, event.x, event.y)
713
+ .find((candidate) => candidate.component.handleMouse !== undefined);
714
+ if (!box?.component.handleMouse) {
715
+ this.clearComponentHover();
716
+ return;
717
+ }
718
+ if (this.componentHover?.component !== box.component)
719
+ this.clearComponentHover();
720
+ const pointerEvent = {
721
+ kind: "move",
722
+ button: event.button,
723
+ x: event.x - box.rect.x,
724
+ y: event.y - box.rect.y,
725
+ release: false,
726
+ clickCount: 1,
727
+ };
728
+ const changed = box.component.handleMouse.call(box.component, pointerEvent);
729
+ this.componentHover = {
730
+ component: box.component,
731
+ x: pointerEvent.x,
732
+ y: pointerEvent.y,
733
+ };
734
+ if (changed)
735
+ this.requestRender();
736
+ }
737
+ clearComponentHover(render = true) {
738
+ const previous = this.componentHover;
739
+ if (!previous)
740
+ return;
741
+ this.componentHover = undefined;
742
+ const changed = previous.component.handleMouse?.call(previous.component, {
743
+ kind: "leave",
744
+ button: 35,
745
+ x: previous.x,
746
+ y: previous.y,
747
+ release: false,
748
+ clickCount: 1,
749
+ }) ?? false;
750
+ if (render && changed)
751
+ this.requestRender();
752
+ }
753
+ getScrollbarTargetAt(x, y) {
754
+ if (this.hasOverlay() || !this.currentLayout)
755
+ return undefined;
756
+ for (const scrollView of getScrollViewsAt(this.currentLayout, x, y)) {
757
+ const box = getScrollViewBox(this.currentLayout, scrollView);
758
+ const geometry = box ? getScrollbarGeometry(box) : undefined;
759
+ if (geometry &&
760
+ x === geometry.column &&
761
+ y >= geometry.thumbTop &&
762
+ y < geometry.thumbTop + geometry.thumbHeight) {
763
+ return { scrollView, geometry };
764
+ }
765
+ }
766
+ return undefined;
767
+ }
768
+ setScrollbarHover(scrollView) {
769
+ if (scrollView === this.scrollbarHover)
770
+ return;
771
+ this.scrollbarHover?.setScrollbarActive(false);
772
+ this.scrollbarHover = scrollView;
773
+ this.scrollbarHover?.setScrollbarActive(true);
774
+ }
775
+ updateScrollbarHover(x, y) {
776
+ this.setScrollbarHover(this.getScrollbarTargetAt(x, y)?.scrollView);
777
+ }
778
+ stopScrollbarHover() {
779
+ this.setScrollbarHover(undefined);
780
+ }
781
+ handleScrollbarMouseEvent(event) {
782
+ if (this.scrollbarDrag) {
783
+ if (event.release) {
784
+ this.stopScrollbarDrag();
785
+ return true;
786
+ }
787
+ const box = this.currentLayout
788
+ ? getScrollViewBox(this.currentLayout, this.scrollbarDrag.scrollView)
789
+ : undefined;
790
+ const geometry = box ? getScrollbarGeometry(box) : undefined;
791
+ if (geometry) {
792
+ const maxThumbOffset = geometry.trackHeight - geometry.thumbHeight;
793
+ const thumbOffset = Math.max(0, Math.min(maxThumbOffset, event.y - geometry.trackTop - this.scrollbarDrag.grabOffset));
794
+ const scrollTop = maxThumbOffset === 0 ? 0 : Math.round((thumbOffset / maxThumbOffset) * geometry.maxScrollTop);
795
+ this.scrollbarDrag.scrollView.scrollTo(scrollTop);
796
+ }
797
+ return true;
798
+ }
799
+ if (event.release || (event.button & 32) !== 0 || (event.button & 3) !== 0)
800
+ return false;
801
+ const target = this.getScrollbarTargetAt(event.x, event.y);
802
+ if (!target)
803
+ return false;
804
+ this.stopSelectionAutoScroll();
805
+ this.selectionPressActive = false;
806
+ this.selectionAnchor = undefined;
807
+ this.selectionFocus = undefined;
808
+ this.selectionGranularity = "character";
809
+ this.selectionInitialRange = undefined;
810
+ this.lastClick = undefined;
811
+ this.lastComponentClick = undefined;
812
+ this.pressedUrl = undefined;
813
+ this.selectionDragged = false;
814
+ this.setScrollbarHover(target.scrollView);
815
+ this.scrollbarDrag = {
816
+ scrollView: target.scrollView,
817
+ grabOffset: event.y - target.geometry.thumbTop,
818
+ };
819
+ return true;
820
+ }
821
+ stopScrollbarDrag() {
822
+ this.scrollbarDrag = undefined;
823
+ }
824
+ getScrollSelectionPoint(scrollView, x, y) {
825
+ if (!this.currentLayout)
826
+ return undefined;
827
+ const box = getScrollViewBox(this.currentLayout, scrollView);
828
+ if (!box || box.rect.height <= 0 || box.clip.height <= 0)
829
+ return undefined;
830
+ const visibleTop = Math.max(0, box.rect.y, box.clip.y);
831
+ const visibleBottom = Math.min(this.terminal.rows - 1, box.rect.y + box.rect.height - 1, box.clip.y + box.clip.height - 1);
832
+ if (visibleBottom < visibleTop)
833
+ return undefined;
834
+ const pointerRow = Math.max(visibleTop, Math.min(visibleBottom, y));
835
+ const maxContentRow = Math.max(0, (box.scrollContentLines?.length ?? 1) - 1);
836
+ return {
837
+ row: Math.max(0, Math.min(maxContentRow, scrollView.scrollTop + pointerRow - box.rect.y)),
838
+ col: Math.max(0, Math.min(box.rect.width - 1, x - box.rect.x)),
839
+ scrollView,
840
+ };
841
+ }
842
+ getSelectionPoint(event, scrollView) {
843
+ if (scrollView) {
844
+ const point = this.getScrollSelectionPoint(scrollView, event.x, event.y);
845
+ if (point)
846
+ return point;
847
+ }
848
+ return {
849
+ row: Math.max(0, Math.min(this.terminal.rows - 1, event.y)),
850
+ col: Math.max(0, Math.min(this.terminal.columns - 1, event.x)),
851
+ };
852
+ }
853
+ getSelectionSourceLine(point) {
854
+ if (point.scrollView && this.currentLayout) {
855
+ const lines = getScrollViewBox(this.currentLayout, point.scrollView)?.scrollContentLines;
856
+ if (lines)
857
+ return lines[point.row] ?? "";
858
+ }
859
+ return this.previousScreen[point.row] ?? "";
860
+ }
861
+ getWordSelection(point) {
862
+ const line = stripTerminalSequences(this.getSelectionSourceLine(point));
863
+ let start = 0;
864
+ for (const segment of wordSegmenter.segment(line)) {
865
+ const end = start + visibleWidth(segment.segment);
866
+ if (point.col >= start && point.col < end) {
867
+ return {
868
+ start: { ...point, col: start },
869
+ end: { ...point, col: end, boundary: true },
870
+ };
871
+ }
872
+ start = end;
873
+ }
874
+ return undefined;
875
+ }
876
+ getLineSelection(point) {
877
+ return {
878
+ start: { ...point, col: 0 },
879
+ end: { ...point, col: visibleWidth(this.getSelectionSourceLine(point)), boundary: true },
880
+ };
881
+ }
882
+ updateSelectionFocus(point) {
883
+ if (this.selectionGranularity === "character" || !this.selectionInitialRange) {
884
+ this.selectionFocus = point;
885
+ return;
886
+ }
887
+ const range = this.selectionGranularity === "word" ? this.getWordSelection(point) : this.getLineSelection(point);
888
+ if (!range)
889
+ return;
890
+ const initial = this.selectionInitialRange;
891
+ const targetBeforeInitial = range.start.row < initial.start.row ||
892
+ (range.start.row === initial.start.row && range.start.col < initial.start.col);
893
+ if (targetBeforeInitial) {
894
+ this.selectionAnchor = initial.end;
895
+ this.selectionFocus = range.start;
896
+ }
897
+ else {
898
+ this.selectionAnchor = initial.start;
899
+ this.selectionFocus = range.end;
900
+ }
901
+ }
902
+ getClickCount(point, word) {
903
+ const now = Date.now();
904
+ const previous = this.lastClick;
905
+ const count = word &&
906
+ previous &&
907
+ now - previous.timestamp <= DOUBLE_CLICK_INTERVAL_MS &&
908
+ previous.row === point.row &&
909
+ previous.scrollView === point.scrollView &&
910
+ previous.wordStart === word.start.col &&
911
+ previous.wordEnd === word.end.col
912
+ ? (previous.count % 3) + 1
913
+ : 1;
914
+ this.lastClick = word
915
+ ? {
916
+ timestamp: now,
917
+ count,
918
+ row: point.row,
919
+ scrollView: point.scrollView,
920
+ wordStart: word.start.col,
921
+ wordEnd: word.end.col,
922
+ }
923
+ : undefined;
924
+ return count;
925
+ }
926
+ updateSelectionAutoScroll(event) {
927
+ const scrollView = this.selectionAnchor?.scrollView;
928
+ if (!scrollView || !this.currentLayout) {
929
+ this.stopSelectionAutoScroll();
930
+ return;
931
+ }
932
+ const box = getScrollViewBox(this.currentLayout, scrollView);
933
+ if (!box || box.rect.height <= 0 || box.clip.height <= 0) {
934
+ this.stopSelectionAutoScroll();
935
+ return;
936
+ }
937
+ const visibleTop = Math.max(0, box.rect.y, box.clip.y);
938
+ const visibleBottom = Math.min(this.terminal.rows - 1, box.rect.y + box.rect.height - 1, box.clip.y + box.clip.height - 1);
939
+ this.selectionDragPointer = { x: event.x, y: event.y };
940
+ this.selectionAutoScrollDirection = event.y <= visibleTop ? -1 : event.y >= visibleBottom ? 1 : 0;
941
+ if (this.selectionAutoScrollDirection === 0) {
942
+ this.stopSelectionAutoScroll();
943
+ return;
944
+ }
945
+ if (this.selectionAutoScrollTimer)
946
+ return;
947
+ this.selectionAutoScrollTimer = setInterval(() => this.autoScrollSelection(), 50);
948
+ this.selectionAutoScrollTimer.unref();
949
+ }
950
+ autoScrollSelection() {
951
+ const scrollView = this.selectionAnchor?.scrollView;
952
+ const pointer = this.selectionDragPointer;
953
+ const direction = this.selectionAutoScrollDirection;
954
+ if (!scrollView || !pointer || direction === 0) {
955
+ this.stopSelectionAutoScroll();
956
+ return;
957
+ }
958
+ const remaining = scrollView.scrollBy(direction);
959
+ if (remaining === direction) {
960
+ this.stopSelectionAutoScroll();
961
+ return;
962
+ }
963
+ const point = this.getScrollSelectionPoint(scrollView, pointer.x, pointer.y);
964
+ if (point)
965
+ this.updateSelectionFocus(point);
966
+ this.requestRender();
967
+ }
968
+ stopSelectionAutoScroll() {
969
+ if (this.selectionAutoScrollTimer) {
970
+ clearInterval(this.selectionAutoScrollTimer);
971
+ this.selectionAutoScrollTimer = undefined;
972
+ }
973
+ this.selectionAutoScrollDirection = 0;
974
+ this.selectionDragPointer = undefined;
975
+ }
976
+ handleSelectionMouseEvent(event) {
977
+ const button = event.button & 3;
978
+ if (button !== 0 && !(event.release && button === 3))
979
+ return;
980
+ const anchorScrollView = this.selectionAnchor?.scrollView;
981
+ const point = this.getSelectionPoint(event, anchorScrollView);
982
+ if (event.release) {
983
+ if (!this.selectionPressActive)
984
+ return;
985
+ this.selectionPressActive = false;
986
+ this.stopSelectionAutoScroll();
987
+ if (!this.selectionAnchor)
988
+ return;
989
+ this.updateSelectionFocus(point);
990
+ const clickedUrl = !this.selectionDragged &&
991
+ this.selectionAnchor.scrollView === point.scrollView &&
992
+ this.selectionAnchor.row === point.row &&
993
+ this.selectionAnchor.col === point.col
994
+ ? this.pressedUrl
995
+ : undefined;
996
+ this.pressedUrl = undefined;
997
+ if (clickedUrl && this.openUrl) {
998
+ this.selectionAnchor = undefined;
999
+ this.selectionFocus = undefined;
1000
+ try {
1001
+ this.openUrl(clickedUrl);
1002
+ }
1003
+ catch {
1004
+ // URL activation is best-effort.
1005
+ }
1006
+ this.requestRender();
1007
+ return;
1008
+ }
1009
+ void this.copySelectionToClipboard();
1010
+ this.requestRender();
1011
+ return;
1012
+ }
1013
+ if ((event.button & 32) !== 0) {
1014
+ if (!this.selectionPressActive || !this.selectionAnchor)
1015
+ return;
1016
+ this.selectionDragged = true;
1017
+ this.lastClick = undefined;
1018
+ this.pressedUrl = undefined;
1019
+ this.updateSelectionFocus(point);
1020
+ this.updateSelectionAutoScroll(event);
1021
+ this.requestRender();
1022
+ return;
1023
+ }
1024
+ this.stopSelectionAutoScroll();
1025
+ this.selectionPressActive = true;
1026
+ const scrollView = !this.hasOverlay() && this.currentLayout
1027
+ ? getScrollViewsAt(this.currentLayout, event.x, event.y)[0]
1028
+ : undefined;
1029
+ const anchor = this.getSelectionPoint(event, scrollView);
1030
+ const word = this.getWordSelection(anchor);
1031
+ const clickCount = this.getClickCount(anchor, word);
1032
+ const range = clickCount === 2 ? word : clickCount === 3 ? this.getLineSelection(anchor) : undefined;
1033
+ this.selectionGranularity = range ? (clickCount === 2 ? "word" : "line") : "character";
1034
+ this.selectionInitialRange = range;
1035
+ this.selectionAnchor = range?.start ?? anchor;
1036
+ this.selectionFocus = range?.end ?? anchor;
1037
+ this.selectionDragged = false;
1038
+ this.pressedUrl = range
1039
+ ? undefined
1040
+ : getOsc8LinkAtColumn(this.previousScreen[Math.max(0, Math.min(this.terminal.rows - 1, event.y))] ?? "", Math.max(0, Math.min(this.terminal.columns - 1, event.x)));
1041
+ this.requestRender();
1042
+ }
1043
+ getSelectionBounds() {
1044
+ if (!this.selectionAnchor || !this.selectionFocus)
1045
+ return undefined;
1046
+ if (this.selectionAnchor.scrollView !== this.selectionFocus.scrollView)
1047
+ return undefined;
1048
+ const anchorBeforeFocus = this.selectionAnchor.row < this.selectionFocus.row ||
1049
+ (this.selectionAnchor.row === this.selectionFocus.row && this.selectionAnchor.col < this.selectionFocus.col);
1050
+ if (this.selectionAnchor.row === this.selectionFocus.row &&
1051
+ this.selectionAnchor.col === this.selectionFocus.col) {
1052
+ return undefined;
1053
+ }
1054
+ return anchorBeforeFocus
1055
+ ? { start: this.selectionAnchor, end: this.selectionFocus }
1056
+ : { start: this.selectionFocus, end: this.selectionAnchor };
1057
+ }
1058
+ getSelectionColumns(line, row, selection, minColumn = 0, maxColumn = visibleWidth(line)) {
1059
+ const lineWidth = visibleWidth(line);
1060
+ let start = Math.max(0, minColumn);
1061
+ let end = Math.min(lineWidth, maxColumn);
1062
+ if (row === selection.start.row) {
1063
+ start = getGraphemeCellRange(line, selection.start.col)?.start ?? Math.min(selection.start.col, lineWidth);
1064
+ }
1065
+ if (row === selection.end.row) {
1066
+ end = selection.end.boundary
1067
+ ? Math.min(selection.end.col, lineWidth)
1068
+ : (getGraphemeCellRange(line, selection.end.col)?.end ?? Math.min(selection.end.col + 1, lineWidth));
1069
+ }
1070
+ return { start: Math.max(minColumn, start), end: Math.min(maxColumn, end) };
1071
+ }
1072
+ async copySelectionToClipboard() {
1073
+ const selection = this.getSelectionBounds();
1074
+ if (!selection)
1075
+ return;
1076
+ let sourceLines = this.previousScreen;
1077
+ if (selection.start.scrollView) {
1078
+ if (!this.currentLayout)
1079
+ return;
1080
+ const box = getScrollViewBox(this.currentLayout, selection.start.scrollView);
1081
+ if (!box?.scrollContentLines)
1082
+ return;
1083
+ sourceLines = box.scrollContentLines;
1084
+ }
1085
+ const lines = [];
1086
+ for (let row = selection.start.row; row <= selection.end.row; row++) {
1087
+ const line = sourceLines[row] ?? "";
1088
+ const columns = this.getSelectionColumns(line, row, selection);
1089
+ lines.push(stripTerminalSequences(sliceByColumn(line, columns.start, Math.max(0, columns.end - columns.start), true)).trimEnd());
1090
+ }
1091
+ const text = lines.join("\n");
1092
+ if (text.length === 0)
1093
+ return;
1094
+ // Prefer an injected clipboard implementation (native clipboard + platform tools with a
1095
+ // verified success path) when the host app provides one. A bare OSC 52 write can show
1096
+ // "Copied!" while leaving the system clipboard untouched (e.g. macOS Terminal.app, tmux
1097
+ // without OSC 52 clipboard passthrough), so only report success when it actually copies.
1098
+ if (this.copySelection) {
1099
+ const ok = await this.copySelection(text);
1100
+ this.flash(ok ? "Copied!" : "Copy failed");
1101
+ return;
1102
+ }
1103
+ this.terminal.write(`\x1b]52;c;${Buffer.from(text).toString("base64")}\x07`);
1104
+ this.flash("Copied!");
1105
+ }
1106
+ applySearchTextHighlight(text, current) {
1107
+ const style = current ? this.searchCurrentMatchStyle : this.searchMatchStyle;
1108
+ let result = "";
1109
+ let plainStart = 0;
1110
+ let index = 0;
1111
+ while (index < text.length) {
1112
+ const ansi = extractAnsiCode(text, index);
1113
+ if (!ansi) {
1114
+ index += 1;
1115
+ continue;
1116
+ }
1117
+ if (index > plainStart)
1118
+ result += style(text.slice(plainStart, index));
1119
+ result += ansi.code;
1120
+ index += ansi.length;
1121
+ plainStart = index;
1122
+ }
1123
+ if (plainStart < text.length)
1124
+ result += style(text.slice(plainStart));
1125
+ return result;
1126
+ }
1127
+ applySearchHighlights(screen, layout) {
1128
+ const search = this.activeSearch;
1129
+ if (!search || search.selectedIndex < 0 || search.matches.length === 0)
1130
+ return screen;
1131
+ const scrollView = layout.primaryScrollView ?? this.implicitScrollView;
1132
+ const box = getScrollViewBox(layout, scrollView);
1133
+ if (!box)
1134
+ return screen;
1135
+ const rangesByRow = new Map();
1136
+ const scrollbarColumn = getScrollbarGeometry(box)?.column;
1137
+ const minRow = Math.max(0, box.rect.y, box.clip.y);
1138
+ const maxRow = Math.min(screen.length, box.rect.y + box.rect.height, box.clip.y + box.clip.height);
1139
+ const minColumn = Math.max(0, box.rect.x, box.clip.x);
1140
+ const maxColumn = Math.min(this.terminal.columns, box.rect.x + box.rect.width, box.clip.x + box.clip.width, scrollbarColumn ?? Number.POSITIVE_INFINITY);
1141
+ for (let matchIndex = 0; matchIndex < search.matches.length; matchIndex++) {
1142
+ for (const segment of search.matches[matchIndex].segments) {
1143
+ const row = box.rect.y + segment.row - scrollView.scrollTop;
1144
+ if (row < minRow || row >= maxRow)
1145
+ continue;
1146
+ const startCol = Math.max(minColumn, box.rect.x + segment.startCol);
1147
+ const endCol = Math.min(maxColumn, box.rect.x + segment.endCol);
1148
+ if (endCol <= startCol)
1149
+ continue;
1150
+ const ranges = rangesByRow.get(row) ?? [];
1151
+ ranges.push({ startCol, endCol, current: matchIndex === search.selectedIndex });
1152
+ rangesByRow.set(row, ranges);
1153
+ }
1154
+ }
1155
+ const result = [...screen];
1156
+ for (const [row, ranges] of rangesByRow) {
1157
+ let line = result[row] ?? "";
1158
+ if (isImageLine(line))
1159
+ continue;
1160
+ const lineWidth = visibleWidth(line);
1161
+ for (const range of ranges.sort((a, b) => b.startCol - a.startCol)) {
1162
+ const startCol = Math.min(range.startCol, lineWidth);
1163
+ const endCol = Math.min(range.endCol, lineWidth);
1164
+ if (endCol <= startCol)
1165
+ continue;
1166
+ const before = sliceByColumn(line, 0, startCol, true);
1167
+ const highlighted = sliceByColumn(line, startCol, endCol - startCol, true);
1168
+ const after = sliceByColumn(line, endCol, Math.max(0, lineWidth - endCol), true);
1169
+ line = `${before}${this.applySearchTextHighlight(highlighted, range.current)}${after}`;
1170
+ }
1171
+ result[row] = line;
1172
+ }
1173
+ return result;
1174
+ }
1175
+ applySelectionHighlight(text) {
1176
+ let result = "\x1b[7m";
1177
+ let index = 0;
1178
+ while (index < text.length) {
1179
+ const ansi = extractAnsiCode(text, index);
1180
+ if (!ansi) {
1181
+ result += text[index];
1182
+ index += 1;
1183
+ continue;
1184
+ }
1185
+ result += ansi.code;
1186
+ if (ansi.code.endsWith("m"))
1187
+ result += "\x1b[7m";
1188
+ index += ansi.length;
1189
+ }
1190
+ return `${result}\x1b[27m`;
1191
+ }
1192
+ applySelection(screen, layout = this.currentLayout) {
1193
+ const selection = this.getSelectionBounds();
1194
+ if (!selection)
1195
+ return screen;
1196
+ let screenSelection = selection;
1197
+ let minRow = 0;
1198
+ let maxRow = screen.length - 1;
1199
+ let minColumn = 0;
1200
+ let maxColumn = this.terminal.columns;
1201
+ if (selection.start.scrollView) {
1202
+ if (!layout)
1203
+ return screen;
1204
+ const box = getScrollViewBox(layout, selection.start.scrollView);
1205
+ if (!box)
1206
+ return screen;
1207
+ minRow = Math.max(0, box.rect.y, box.clip.y);
1208
+ maxRow = Math.min(screen.length - 1, box.rect.y + box.rect.height - 1, box.clip.y + box.clip.height - 1);
1209
+ minColumn = Math.max(0, box.rect.x, box.clip.x);
1210
+ maxColumn = Math.min(this.terminal.columns, box.rect.x + box.rect.width, box.clip.x + box.clip.width);
1211
+ screenSelection = {
1212
+ start: {
1213
+ ...selection.start,
1214
+ row: box.rect.y + selection.start.row - selection.start.scrollView.scrollTop,
1215
+ col: box.rect.x + selection.start.col,
1216
+ },
1217
+ end: {
1218
+ ...selection.end,
1219
+ row: box.rect.y + selection.end.row - selection.start.scrollView.scrollTop,
1220
+ col: box.rect.x + selection.end.col,
1221
+ },
1222
+ };
1223
+ }
1224
+ return screen.map((line, row) => {
1225
+ if (row < minRow ||
1226
+ row > maxRow ||
1227
+ row < screenSelection.start.row ||
1228
+ row > screenSelection.end.row ||
1229
+ isImageLine(line)) {
1230
+ return line;
1231
+ }
1232
+ const lineWidth = visibleWidth(line);
1233
+ const columns = this.getSelectionColumns(line, row, screenSelection, minColumn, maxColumn);
1234
+ if (columns.end <= columns.start)
1235
+ return line;
1236
+ const before = sliceByColumn(line, 0, columns.start, true);
1237
+ const selected = sliceByColumn(line, columns.start, columns.end - columns.start, true);
1238
+ const after = sliceByColumn(line, columns.end, Math.max(0, lineWidth - columns.end), true);
1239
+ return `${before}${this.applySelectionHighlight(selected)}${after}`;
1240
+ });
1241
+ }
1242
+ isMouseSequence(data) {
1243
+ return /^\x1b\[<\d+;\d+;\d+[Mm]$/.test(data) || (data.length === 6 && data.startsWith("\x1b[M"));
1244
+ }
1245
+ compositeFlashes(screen, width, height) {
1246
+ const flashLines = this.flashes.render(width).slice(-height);
1247
+ if (flashLines.length === 0)
1248
+ return screen;
1249
+ const result = [...screen];
1250
+ while (result.length < height)
1251
+ result.push("");
1252
+ for (let row = 0; row < flashLines.length; row++) {
1253
+ const line = flashLines[row];
1254
+ const flashWidth = visibleWidth(line);
1255
+ if (flashWidth === 0)
1256
+ continue;
1257
+ result[row] = compositeTuiLine(result[row] ?? "", line, width - flashWidth, flashWidth, width);
1258
+ }
1259
+ return result;
1260
+ }
1261
+ doRender() {
1262
+ if (this.stopped || !this.altScreenActive)
1263
+ return;
1264
+ const width = Math.max(1, this.terminal.columns);
1265
+ const height = Math.max(1, this.terminal.rows);
1266
+ const root = this.layoutRoot ?? this.implicitScrollView;
1267
+ let nextLayout = renderLayoutFrame(root, width, height, () => this.requestRender());
1268
+ if (this.refreshSearch(nextLayout)) {
1269
+ nextLayout = renderLayoutFrame(root, width, height, () => this.requestRender());
1270
+ }
1271
+ let screen = nextLayout.lines.map((line) => line.replace(OSC133_ZONE_PREFIX, ""));
1272
+ screen = this.applySearchHighlights(screen, nextLayout);
1273
+ screen = this.compositeOverlays(screen, width, height);
1274
+ if (screen.length > height)
1275
+ screen = screen.slice(screen.length - height);
1276
+ screen = this.applySelection(screen, nextLayout);
1277
+ screen = this.compositeFlashes(screen, width, height);
1278
+ const cursorPos = this.extractCursorPosition(screen, height);
1279
+ screen = this.applyLineResets(screen).map((line) => {
1280
+ if (isImageLine(line) || visibleWidth(line) <= width)
1281
+ return line;
1282
+ return sliceByColumn(line, 0, width, true);
1283
+ });
1284
+ const screenBackground = this.screenBackground?.();
1285
+ const screenBackgroundKey = screenBackground ? `${screenBackground.open}\0${screenBackground.close}` : "";
1286
+ const fullRedraw = this.previousScreen.length === 0 ||
1287
+ this.previousScreenWidth !== width ||
1288
+ this.previousScreenHeight !== height ||
1289
+ this.previousScreenBackground !== screenBackgroundKey;
1290
+ const imagesNeedRedraw = screen.some((line, row) => line !== this.previousScreen[row] && (isImageLine(line) || isImageLine(this.previousScreen[row] ?? "")));
1291
+ const redrawImages = fullRedraw || imagesNeedRedraw;
1292
+ const hadUploadedKittyImages = this.uploadedKittyImages.size > 0;
1293
+ const preparedKittyScreen = redrawImages && this.imageProtocol === "kitty"
1294
+ ? this.prepareKittyScreen(screen)
1295
+ : { lines: screen, evictedImageDeletion: "" };
1296
+ let buffer = BEGIN_SYNCHRONIZED_OUTPUT;
1297
+ if (fullRedraw) {
1298
+ this.fullRedrawCount += 1;
1299
+ const clearImages = this.imageProtocol === "kitty" && hadUploadedKittyImages
1300
+ ? deleteAllKittyPlacements()
1301
+ : this.deleteKittyImages();
1302
+ buffer += `${clearImages}\x1b[2J`;
1303
+ }
1304
+ else if (imagesNeedRedraw) {
1305
+ if (this.imageProtocol === "iterm2")
1306
+ buffer += "\x1b[2J";
1307
+ else if (this.imageProtocol === "kitty")
1308
+ buffer += deleteAllKittyPlacements();
1309
+ }
1310
+ buffer += preparedKittyScreen.evictedImageDeletion;
1311
+ for (let row = 0; row < height; row++) {
1312
+ if (!fullRedraw && !imagesNeedRedraw && screen[row] === this.previousScreen[row])
1313
+ continue;
1314
+ const backgroundOpen = screenBackground?.open ?? "";
1315
+ const backgroundClose = screenBackground?.close ?? "";
1316
+ buffer += `\x1b[${row + 1};1H${backgroundOpen}\x1b[2K${preparedKittyScreen.lines[row] ?? ""}${backgroundClose}`;
1317
+ }
1318
+ if (cursorPos) {
1319
+ buffer += `\x1b[${cursorPos.row + 1};${Math.min(width, cursorPos.col) + 1}H`;
1320
+ buffer += this.getShowHardwareCursor() ? "\x1b[?25h" : "\x1b[?25l";
1321
+ }
1322
+ else {
1323
+ buffer += "\x1b[?25l";
1324
+ }
1325
+ buffer += END_SYNCHRONIZED_OUTPUT;
1326
+ this.terminal.write(buffer);
1327
+ this.previousScreen = screen;
1328
+ this.previousScreenWidth = width;
1329
+ this.previousScreenHeight = height;
1330
+ this.previousScreenBackground = screenBackgroundKey;
1331
+ this.currentLayout = nextLayout;
1332
+ }
1333
+ }
1334
+ //# sourceMappingURL=tui-alt-screen.js.map