@fairyhunter13/opentui-core 0.1.91 → 0.1.94

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 (570) hide show
  1. package/3d/SpriteResourceManager.d.ts +74 -0
  2. package/3d/SpriteUtils.d.ts +13 -0
  3. package/3d/TextureUtils.d.ts +24 -0
  4. package/3d/ThreeRenderable.d.ts +40 -0
  5. package/3d/WGPURenderer.d.ts +61 -0
  6. package/3d/animation/ExplodingSpriteEffect.d.ts +71 -0
  7. package/3d/animation/PhysicsExplodingSpriteEffect.d.ts +76 -0
  8. package/3d/animation/SpriteAnimator.d.ts +124 -0
  9. package/3d/animation/SpriteParticleGenerator.d.ts +62 -0
  10. package/3d/canvas.d.ts +44 -0
  11. package/3d/index.d.ts +12 -0
  12. package/3d/physics/PlanckPhysicsAdapter.d.ts +19 -0
  13. package/3d/physics/RapierPhysicsAdapter.d.ts +19 -0
  14. package/3d/physics/physics-interface.d.ts +27 -0
  15. package/3d.d.ts +2 -0
  16. package/3d.js +34042 -0
  17. package/3d.js.map +155 -0
  18. package/LICENSE +21 -0
  19. package/NativeSpanFeed.d.ts +41 -0
  20. package/Renderable.d.ts +334 -0
  21. package/animation/Timeline.d.ts +126 -0
  22. package/ansi.d.ts +13 -0
  23. package/buffer.d.ts +107 -0
  24. package/console.d.ts +143 -0
  25. package/edit-buffer.d.ts +98 -0
  26. package/editor-view.d.ts +73 -0
  27. package/index-e6ec7apq.js +18415 -0
  28. package/index-e6ec7apq.js.map +64 -0
  29. package/index-h066zmrb.js +12619 -0
  30. package/index-h066zmrb.js.map +43 -0
  31. package/index-ynzawt3n.js +113 -0
  32. package/index-ynzawt3n.js.map +10 -0
  33. package/index.d.ts +21 -0
  34. package/index.js +430 -0
  35. package/index.js.map +9 -0
  36. package/lib/KeyHandler.d.ts +61 -0
  37. package/lib/RGBA.d.ts +25 -0
  38. package/lib/ascii.font.d.ts +508 -0
  39. package/lib/border.d.ts +49 -0
  40. package/lib/bunfs.d.ts +7 -0
  41. package/lib/clipboard.d.ts +17 -0
  42. package/lib/clock.d.ts +15 -0
  43. package/lib/data-paths.d.ts +26 -0
  44. package/lib/debounce.d.ts +42 -0
  45. package/lib/detect-links.d.ts +6 -0
  46. package/lib/env.d.ts +42 -0
  47. package/lib/extmarks-history.d.ts +17 -0
  48. package/lib/extmarks.d.ts +89 -0
  49. package/lib/hast-styled-text.d.ts +17 -0
  50. package/lib/index.d.ts +21 -0
  51. package/lib/keymapping.d.ts +25 -0
  52. package/lib/objects-in-viewport.d.ts +24 -0
  53. package/lib/output.capture.d.ts +24 -0
  54. package/lib/parse.keypress-kitty.d.ts +2 -0
  55. package/lib/parse.keypress.d.ts +26 -0
  56. package/lib/parse.mouse.d.ts +30 -0
  57. package/lib/paste.d.ts +7 -0
  58. package/lib/queue.d.ts +15 -0
  59. package/lib/renderable.validations.d.ts +12 -0
  60. package/lib/scroll-acceleration.d.ts +43 -0
  61. package/lib/selection.d.ts +63 -0
  62. package/lib/singleton.d.ts +7 -0
  63. package/lib/stdin-parser.d.ts +76 -0
  64. package/lib/styled-text.d.ts +63 -0
  65. package/lib/terminal-capability-detection.d.ts +30 -0
  66. package/lib/terminal-palette.d.ts +50 -0
  67. package/lib/tree-sitter/assets/update.d.ts +11 -0
  68. package/lib/tree-sitter/client.d.ts +47 -0
  69. package/lib/tree-sitter/default-parsers.d.ts +2 -0
  70. package/lib/tree-sitter/download-utils.d.ts +21 -0
  71. package/lib/tree-sitter/index.d.ts +8 -0
  72. package/lib/tree-sitter/parser.worker.d.ts +1 -0
  73. package/lib/tree-sitter/parsers-config.d.ts +38 -0
  74. package/lib/tree-sitter/resolve-ft.d.ts +2 -0
  75. package/lib/tree-sitter/types.d.ts +81 -0
  76. package/lib/tree-sitter-styled-text.d.ts +14 -0
  77. package/lib/validate-dir-name.d.ts +1 -0
  78. package/lib/yoga.options.d.ts +32 -0
  79. package/package.json +51 -63
  80. package/parser.worker.js +869 -0
  81. package/parser.worker.js.map +12 -0
  82. package/plugins/core-slot.d.ts +72 -0
  83. package/plugins/registry.d.ts +38 -0
  84. package/plugins/types.d.ts +34 -0
  85. package/post/filters.d.ts +105 -0
  86. package/renderables/ASCIIFont.d.ts +52 -0
  87. package/renderables/Box.d.ts +72 -0
  88. package/renderables/Code.d.ts +78 -0
  89. package/renderables/Diff.d.ts +142 -0
  90. package/renderables/EditBufferRenderable.d.ts +162 -0
  91. package/renderables/FrameBuffer.d.ts +16 -0
  92. package/renderables/Input.d.ts +67 -0
  93. package/renderables/LineNumberRenderable.d.ts +74 -0
  94. package/renderables/Markdown.d.ts +173 -0
  95. package/renderables/ScrollBar.d.ts +77 -0
  96. package/renderables/ScrollBox.d.ts +124 -0
  97. package/renderables/Select.d.ts +115 -0
  98. package/renderables/Slider.d.ts +44 -0
  99. package/renderables/TabSelect.d.ts +96 -0
  100. package/renderables/Text.d.ts +36 -0
  101. package/renderables/TextBufferRenderable.d.ts +105 -0
  102. package/renderables/TextNode.d.ts +91 -0
  103. package/renderables/TextTable.d.ts +140 -0
  104. package/renderables/Textarea.d.ts +114 -0
  105. package/renderables/TimeToFirstDraw.d.ts +24 -0
  106. package/renderables/__tests__/renderable-test-utils.d.ts +12 -0
  107. package/renderables/composition/VRenderable.d.ts +16 -0
  108. package/renderables/composition/constructs.d.ts +35 -0
  109. package/renderables/composition/vnode.d.ts +46 -0
  110. package/renderables/index.d.ts +22 -0
  111. package/renderables/markdown-parser.d.ts +10 -0
  112. package/renderer.d.ts +388 -0
  113. package/runtime-plugin-support.d.ts +3 -0
  114. package/runtime-plugin-support.js +29 -0
  115. package/runtime-plugin-support.js.map +10 -0
  116. package/runtime-plugin.d.ts +11 -0
  117. package/runtime-plugin.js +16 -0
  118. package/runtime-plugin.js.map +9 -0
  119. package/syntax-style.d.ts +54 -0
  120. package/testing/manual-clock.d.ts +16 -0
  121. package/testing/mock-keys.d.ts +81 -0
  122. package/testing/mock-mouse.d.ts +38 -0
  123. package/testing/mock-tree-sitter-client.d.ts +23 -0
  124. package/testing/spy.d.ts +7 -0
  125. package/testing/test-recorder.d.ts +61 -0
  126. package/testing/test-renderer.d.ts +23 -0
  127. package/testing.d.ts +6 -0
  128. package/testing.js +675 -0
  129. package/testing.js.map +15 -0
  130. package/text-buffer-view.d.ts +42 -0
  131. package/text-buffer.d.ts +67 -0
  132. package/types.d.ts +131 -0
  133. package/utils.d.ts +14 -0
  134. package/zig-structs.d.ts +155 -0
  135. package/zig.d.ts +351 -0
  136. package/dev/keypress-debug-renderer.ts +0 -148
  137. package/dev/keypress-debug.ts +0 -43
  138. package/dev/print-env-vars.ts +0 -32
  139. package/dev/test-tmux-graphics-334.sh +0 -68
  140. package/dev/thai-debug-test.ts +0 -68
  141. package/docs/development.md +0 -141
  142. package/docs/env-vars.md +0 -140
  143. package/docs/getting-started.md +0 -353
  144. package/docs/renderables-vs-constructs.md +0 -159
  145. package/docs/tree-sitter.md +0 -311
  146. package/scripts/build.ts +0 -400
  147. package/scripts/publish.ts +0 -60
  148. package/src/3d/SpriteResourceManager.ts +0 -286
  149. package/src/3d/SpriteUtils.ts +0 -71
  150. package/src/3d/TextureUtils.ts +0 -196
  151. package/src/3d/ThreeRenderable.ts +0 -197
  152. package/src/3d/WGPURenderer.ts +0 -294
  153. package/src/3d/animation/ExplodingSpriteEffect.ts +0 -513
  154. package/src/3d/animation/PhysicsExplodingSpriteEffect.ts +0 -429
  155. package/src/3d/animation/SpriteAnimator.ts +0 -633
  156. package/src/3d/animation/SpriteParticleGenerator.ts +0 -435
  157. package/src/3d/canvas.ts +0 -464
  158. package/src/3d/index.ts +0 -12
  159. package/src/3d/physics/PlanckPhysicsAdapter.ts +0 -72
  160. package/src/3d/physics/RapierPhysicsAdapter.ts +0 -66
  161. package/src/3d/physics/physics-interface.ts +0 -31
  162. package/src/3d/shaders/supersampling.wgsl +0 -201
  163. package/src/3d.ts +0 -3
  164. package/src/NativeSpanFeed.ts +0 -300
  165. package/src/Renderable.ts +0 -1698
  166. package/src/__snapshots__/buffer.test.ts.snap +0 -28
  167. package/src/animation/Timeline.test.ts +0 -2709
  168. package/src/animation/Timeline.ts +0 -598
  169. package/src/ansi.ts +0 -18
  170. package/src/benchmark/latest-all-bench-run.json +0 -707
  171. package/src/benchmark/latest-async-bench-run.json +0 -336
  172. package/src/benchmark/latest-default-bench-run.json +0 -657
  173. package/src/benchmark/latest-large-bench-run.json +0 -707
  174. package/src/benchmark/latest-quick-bench-run.json +0 -207
  175. package/src/benchmark/markdown-benchmark.ts +0 -1804
  176. package/src/benchmark/native-span-feed-async-benchmark.ts +0 -355
  177. package/src/benchmark/native-span-feed-benchmark.md +0 -56
  178. package/src/benchmark/native-span-feed-benchmark.ts +0 -596
  179. package/src/benchmark/native-span-feed-compare.ts +0 -280
  180. package/src/benchmark/renderer-benchmark.ts +0 -754
  181. package/src/benchmark/text-table-benchmark.ts +0 -947
  182. package/src/buffer.test.ts +0 -291
  183. package/src/buffer.ts +0 -519
  184. package/src/console.test.ts +0 -612
  185. package/src/console.ts +0 -1255
  186. package/src/edit-buffer.test.ts +0 -1769
  187. package/src/edit-buffer.ts +0 -411
  188. package/src/editor-view.test.ts +0 -1032
  189. package/src/editor-view.ts +0 -284
  190. package/src/examples/ascii-font-selection-demo.ts +0 -245
  191. package/src/examples/assets/Water_2_M_Normal.jpg +0 -0
  192. package/src/examples/assets/concrete.png +0 -0
  193. package/src/examples/assets/crate.png +0 -0
  194. package/src/examples/assets/crate_emissive.png +0 -0
  195. package/src/examples/assets/forrest_background.png +0 -0
  196. package/src/examples/assets/hast-example.json +0 -1018
  197. package/src/examples/assets/heart.png +0 -0
  198. package/src/examples/assets/main_char_heavy_attack.png +0 -0
  199. package/src/examples/assets/main_char_idle.png +0 -0
  200. package/src/examples/assets/main_char_jump_end.png +0 -0
  201. package/src/examples/assets/main_char_jump_landing.png +0 -0
  202. package/src/examples/assets/main_char_jump_start.png +0 -0
  203. package/src/examples/assets/main_char_run_loop.png +0 -0
  204. package/src/examples/assets/roughness_map.jpg +0 -0
  205. package/src/examples/build.ts +0 -115
  206. package/src/examples/code-demo.ts +0 -584
  207. package/src/examples/console-demo.ts +0 -358
  208. package/src/examples/core-plugin-slots-demo.ts +0 -759
  209. package/src/examples/diff-demo.ts +0 -699
  210. package/src/examples/draggable-three-demo.ts +0 -259
  211. package/src/examples/editor-demo.ts +0 -322
  212. package/src/examples/extmarks-demo.ts +0 -204
  213. package/src/examples/focus-restore-demo.ts +0 -310
  214. package/src/examples/fonts.ts +0 -245
  215. package/src/examples/fractal-shader-demo.ts +0 -268
  216. package/src/examples/framebuffer-demo.ts +0 -674
  217. package/src/examples/full-unicode-demo.ts +0 -181
  218. package/src/examples/golden-star-demo.ts +0 -933
  219. package/src/examples/grayscale-buffer-demo.ts +0 -249
  220. package/src/examples/hast-syntax-highlighting-demo.ts +0 -129
  221. package/src/examples/index.ts +0 -925
  222. package/src/examples/input-demo.ts +0 -377
  223. package/src/examples/input-select-layout-demo.ts +0 -425
  224. package/src/examples/install.sh +0 -143
  225. package/src/examples/keypress-debug-demo.ts +0 -452
  226. package/src/examples/lib/HexList.ts +0 -122
  227. package/src/examples/lib/PaletteGrid.ts +0 -125
  228. package/src/examples/lib/standalone-keys.ts +0 -25
  229. package/src/examples/lib/tab-controller.ts +0 -243
  230. package/src/examples/lights-phong-demo.ts +0 -290
  231. package/src/examples/link-demo.ts +0 -220
  232. package/src/examples/live-state-demo.ts +0 -480
  233. package/src/examples/markdown-demo.ts +0 -620
  234. package/src/examples/mouse-interaction-demo.ts +0 -428
  235. package/src/examples/nested-zindex-demo.ts +0 -357
  236. package/src/examples/opacity-example.ts +0 -235
  237. package/src/examples/opentui-demo.ts +0 -1057
  238. package/src/examples/physx-planck-2d-demo.ts +0 -507
  239. package/src/examples/physx-rapier-2d-demo.ts +0 -526
  240. package/src/examples/relative-positioning-demo.ts +0 -323
  241. package/src/examples/scroll-example.ts +0 -214
  242. package/src/examples/scrollbox-mouse-test.ts +0 -112
  243. package/src/examples/scrollbox-overlay-hit-test.ts +0 -206
  244. package/src/examples/select-demo.ts +0 -237
  245. package/src/examples/shader-cube-demo.ts +0 -772
  246. package/src/examples/simple-layout-example.ts +0 -591
  247. package/src/examples/slider-demo.ts +0 -617
  248. package/src/examples/split-mode-demo.ts +0 -445
  249. package/src/examples/sprite-animation-demo.ts +0 -443
  250. package/src/examples/sprite-particle-generator-demo.ts +0 -486
  251. package/src/examples/static-sprite-demo.ts +0 -193
  252. package/src/examples/sticky-scroll-example.ts +0 -308
  253. package/src/examples/styled-text-demo.ts +0 -282
  254. package/src/examples/tab-select-demo.ts +0 -219
  255. package/src/examples/terminal-title.ts +0 -29
  256. package/src/examples/terminal.ts +0 -305
  257. package/src/examples/text-node-demo.ts +0 -416
  258. package/src/examples/text-selection-demo.ts +0 -377
  259. package/src/examples/text-table-demo.ts +0 -503
  260. package/src/examples/text-truncation-demo.ts +0 -481
  261. package/src/examples/text-wrap.ts +0 -757
  262. package/src/examples/texture-loading-demo.ts +0 -259
  263. package/src/examples/timeline-example.ts +0 -670
  264. package/src/examples/transparency-demo.ts +0 -241
  265. package/src/examples/vnode-composition-demo.ts +0 -404
  266. package/src/index.ts +0 -22
  267. package/src/lib/KeyHandler.integration.test.ts +0 -292
  268. package/src/lib/KeyHandler.stopPropagation.test.ts +0 -289
  269. package/src/lib/KeyHandler.test.ts +0 -662
  270. package/src/lib/KeyHandler.ts +0 -222
  271. package/src/lib/RGBA.test.ts +0 -984
  272. package/src/lib/RGBA.ts +0 -204
  273. package/src/lib/ascii.font.ts +0 -330
  274. package/src/lib/border.test.ts +0 -83
  275. package/src/lib/border.ts +0 -168
  276. package/src/lib/bunfs.test.ts +0 -27
  277. package/src/lib/bunfs.ts +0 -18
  278. package/src/lib/clipboard.test.ts +0 -41
  279. package/src/lib/clipboard.ts +0 -47
  280. package/src/lib/clock.ts +0 -31
  281. package/src/lib/data-paths.test.ts +0 -133
  282. package/src/lib/data-paths.ts +0 -109
  283. package/src/lib/debounce.ts +0 -106
  284. package/src/lib/detect-links.test.ts +0 -98
  285. package/src/lib/detect-links.ts +0 -56
  286. package/src/lib/env.test.ts +0 -228
  287. package/src/lib/env.ts +0 -209
  288. package/src/lib/extmarks-history.ts +0 -51
  289. package/src/lib/extmarks-multiwidth.test.ts +0 -322
  290. package/src/lib/extmarks.test.ts +0 -3457
  291. package/src/lib/extmarks.ts +0 -843
  292. package/src/lib/fonts/block.json +0 -405
  293. package/src/lib/fonts/grid.json +0 -265
  294. package/src/lib/fonts/huge.json +0 -741
  295. package/src/lib/fonts/pallet.json +0 -314
  296. package/src/lib/fonts/shade.json +0 -591
  297. package/src/lib/fonts/slick.json +0 -321
  298. package/src/lib/fonts/tiny.json +0 -69
  299. package/src/lib/hast-styled-text.ts +0 -59
  300. package/src/lib/index.ts +0 -21
  301. package/src/lib/keymapping.test.ts +0 -280
  302. package/src/lib/keymapping.ts +0 -87
  303. package/src/lib/objects-in-viewport.test.ts +0 -787
  304. package/src/lib/objects-in-viewport.ts +0 -153
  305. package/src/lib/output.capture.ts +0 -58
  306. package/src/lib/parse.keypress-kitty.protocol.test.ts +0 -340
  307. package/src/lib/parse.keypress-kitty.test.ts +0 -663
  308. package/src/lib/parse.keypress-kitty.ts +0 -439
  309. package/src/lib/parse.keypress.test.ts +0 -1849
  310. package/src/lib/parse.keypress.ts +0 -397
  311. package/src/lib/parse.mouse.test.ts +0 -552
  312. package/src/lib/parse.mouse.ts +0 -232
  313. package/src/lib/paste.ts +0 -16
  314. package/src/lib/queue.ts +0 -65
  315. package/src/lib/renderable.validations.test.ts +0 -87
  316. package/src/lib/renderable.validations.ts +0 -83
  317. package/src/lib/scroll-acceleration.ts +0 -98
  318. package/src/lib/selection.ts +0 -240
  319. package/src/lib/singleton.ts +0 -28
  320. package/src/lib/stdin-parser.test.ts +0 -1676
  321. package/src/lib/stdin-parser.ts +0 -1248
  322. package/src/lib/styled-text.ts +0 -178
  323. package/src/lib/terminal-capability-detection.test.ts +0 -202
  324. package/src/lib/terminal-capability-detection.ts +0 -79
  325. package/src/lib/terminal-palette.test.ts +0 -878
  326. package/src/lib/terminal-palette.ts +0 -383
  327. package/src/lib/tree-sitter/assets/README.md +0 -118
  328. package/src/lib/tree-sitter/assets/update.ts +0 -331
  329. package/src/lib/tree-sitter/assets.d.ts +0 -9
  330. package/src/lib/tree-sitter/cache.test.ts +0 -270
  331. package/src/lib/tree-sitter/client.test.ts +0 -1061
  332. package/src/lib/tree-sitter/client.ts +0 -615
  333. package/src/lib/tree-sitter/default-parsers.ts +0 -80
  334. package/src/lib/tree-sitter/download-utils.ts +0 -148
  335. package/src/lib/tree-sitter/index.ts +0 -28
  336. package/src/lib/tree-sitter/parser.worker.ts +0 -1001
  337. package/src/lib/tree-sitter/parsers-config.ts +0 -75
  338. package/src/lib/tree-sitter/resolve-ft.ts +0 -62
  339. package/src/lib/tree-sitter/types.ts +0 -81
  340. package/src/lib/tree-sitter-styled-text.test.ts +0 -1253
  341. package/src/lib/tree-sitter-styled-text.ts +0 -306
  342. package/src/lib/validate-dir-name.ts +0 -55
  343. package/src/lib/yoga.options.test.ts +0 -628
  344. package/src/lib/yoga.options.ts +0 -346
  345. package/src/plugins/core-slot.ts +0 -579
  346. package/src/plugins/registry.ts +0 -377
  347. package/src/plugins/types.ts +0 -46
  348. package/src/post/filters.ts +0 -888
  349. package/src/renderables/ASCIIFont.ts +0 -219
  350. package/src/renderables/Box.test.ts +0 -160
  351. package/src/renderables/Box.ts +0 -295
  352. package/src/renderables/Code.test.ts +0 -2062
  353. package/src/renderables/Code.ts +0 -357
  354. package/src/renderables/Diff.regression.test.ts +0 -226
  355. package/src/renderables/Diff.test.ts +0 -3027
  356. package/src/renderables/Diff.ts +0 -1209
  357. package/src/renderables/EditBufferRenderable.ts +0 -764
  358. package/src/renderables/FrameBuffer.ts +0 -47
  359. package/src/renderables/Input.test.ts +0 -1228
  360. package/src/renderables/Input.ts +0 -245
  361. package/src/renderables/LineNumberRenderable.ts +0 -675
  362. package/src/renderables/Markdown.ts +0 -1106
  363. package/src/renderables/ScrollBar.ts +0 -422
  364. package/src/renderables/ScrollBox.ts +0 -883
  365. package/src/renderables/Select.test.ts +0 -1010
  366. package/src/renderables/Select.ts +0 -523
  367. package/src/renderables/Slider.test.ts +0 -456
  368. package/src/renderables/Slider.ts +0 -347
  369. package/src/renderables/TabSelect.test.ts +0 -197
  370. package/src/renderables/TabSelect.ts +0 -455
  371. package/src/renderables/Text.selection-buffer.test.ts +0 -123
  372. package/src/renderables/Text.test.ts +0 -2660
  373. package/src/renderables/Text.ts +0 -147
  374. package/src/renderables/TextBufferRenderable.ts +0 -518
  375. package/src/renderables/TextNode.test.ts +0 -1058
  376. package/src/renderables/TextNode.ts +0 -325
  377. package/src/renderables/TextTable.test.ts +0 -1421
  378. package/src/renderables/TextTable.ts +0 -1344
  379. package/src/renderables/Textarea.ts +0 -732
  380. package/src/renderables/TimeToFirstDraw.ts +0 -89
  381. package/src/renderables/__snapshots__/Code.test.ts.snap +0 -13
  382. package/src/renderables/__snapshots__/Diff.test.ts.snap +0 -785
  383. package/src/renderables/__snapshots__/Text.test.ts.snap +0 -421
  384. package/src/renderables/__snapshots__/TextTable.test.ts.snap +0 -215
  385. package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +0 -144
  386. package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +0 -816
  387. package/src/renderables/__tests__/LineNumberRenderable.test.ts +0 -1787
  388. package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +0 -85
  389. package/src/renderables/__tests__/Markdown.test.ts +0 -2287
  390. package/src/renderables/__tests__/MultiRenderable.selection.test.ts +0 -87
  391. package/src/renderables/__tests__/Textarea.buffer.test.ts +0 -682
  392. package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +0 -675
  393. package/src/renderables/__tests__/Textarea.editing.test.ts +0 -2041
  394. package/src/renderables/__tests__/Textarea.error-handling.test.ts +0 -35
  395. package/src/renderables/__tests__/Textarea.events.test.ts +0 -738
  396. package/src/renderables/__tests__/Textarea.highlights.test.ts +0 -590
  397. package/src/renderables/__tests__/Textarea.keybinding.test.ts +0 -3149
  398. package/src/renderables/__tests__/Textarea.paste.test.ts +0 -357
  399. package/src/renderables/__tests__/Textarea.rendering.test.ts +0 -1864
  400. package/src/renderables/__tests__/Textarea.scroll.test.ts +0 -733
  401. package/src/renderables/__tests__/Textarea.selection.test.ts +0 -1590
  402. package/src/renderables/__tests__/Textarea.stress.test.ts +0 -670
  403. package/src/renderables/__tests__/Textarea.undo-redo.test.ts +0 -383
  404. package/src/renderables/__tests__/Textarea.visual-lines.test.ts +0 -310
  405. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +0 -221
  406. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +0 -89
  407. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +0 -457
  408. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +0 -158
  409. package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +0 -387
  410. package/src/renderables/__tests__/markdown-parser.test.ts +0 -217
  411. package/src/renderables/__tests__/renderable-test-utils.ts +0 -60
  412. package/src/renderables/composition/README.md +0 -8
  413. package/src/renderables/composition/VRenderable.ts +0 -32
  414. package/src/renderables/composition/constructs.ts +0 -127
  415. package/src/renderables/composition/vnode.ts +0 -289
  416. package/src/renderables/index.ts +0 -22
  417. package/src/renderables/markdown-parser.ts +0 -66
  418. package/src/renderer.ts +0 -2363
  419. package/src/runtime-plugin-support.ts +0 -39
  420. package/src/runtime-plugin.ts +0 -144
  421. package/src/syntax-style.test.ts +0 -841
  422. package/src/syntax-style.ts +0 -264
  423. package/src/testing/README.md +0 -210
  424. package/src/testing/capture-spans.test.ts +0 -194
  425. package/src/testing/integration.test.ts +0 -276
  426. package/src/testing/manual-clock.ts +0 -106
  427. package/src/testing/mock-keys.test.ts +0 -1356
  428. package/src/testing/mock-keys.ts +0 -449
  429. package/src/testing/mock-mouse.test.ts +0 -218
  430. package/src/testing/mock-mouse.ts +0 -247
  431. package/src/testing/mock-tree-sitter-client.ts +0 -73
  432. package/src/testing/spy.ts +0 -13
  433. package/src/testing/test-recorder.test.ts +0 -415
  434. package/src/testing/test-recorder.ts +0 -145
  435. package/src/testing/test-renderer.ts +0 -116
  436. package/src/testing.ts +0 -7
  437. package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +0 -481
  438. package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +0 -19
  439. package/src/tests/__snapshots__/scrollbox.test.ts.snap +0 -29
  440. package/src/tests/absolute-positioning.snapshot.test.ts +0 -638
  441. package/src/tests/allocator-stats.test.ts +0 -38
  442. package/src/tests/destroy-during-render.test.ts +0 -200
  443. package/src/tests/hover-cursor.test.ts +0 -98
  444. package/src/tests/native-span-feed-async.test.ts +0 -173
  445. package/src/tests/native-span-feed-close.test.ts +0 -120
  446. package/src/tests/native-span-feed-coverage.test.ts +0 -227
  447. package/src/tests/native-span-feed-edge-cases.test.ts +0 -352
  448. package/src/tests/native-span-feed-use-after-free.test.ts +0 -45
  449. package/src/tests/opacity.test.ts +0 -123
  450. package/src/tests/renderable.snapshot.test.ts +0 -524
  451. package/src/tests/renderable.test.ts +0 -1281
  452. package/src/tests/renderer.console-startup.test.ts +0 -65
  453. package/src/tests/renderer.control.test.ts +0 -364
  454. package/src/tests/renderer.core-slot-binding.test.ts +0 -952
  455. package/src/tests/renderer.cursor.test.ts +0 -26
  456. package/src/tests/renderer.destroy-during-render.test.ts +0 -110
  457. package/src/tests/renderer.focus-restore.test.ts +0 -228
  458. package/src/tests/renderer.focus.test.ts +0 -251
  459. package/src/tests/renderer.idle.test.ts +0 -219
  460. package/src/tests/renderer.input.test.ts +0 -2145
  461. package/src/tests/renderer.kitty-flags.test.ts +0 -195
  462. package/src/tests/renderer.mouse.test.ts +0 -1269
  463. package/src/tests/renderer.palette.test.ts +0 -629
  464. package/src/tests/renderer.selection.test.ts +0 -49
  465. package/src/tests/renderer.slot-registry.test.ts +0 -649
  466. package/src/tests/renderer.useMouse.test.ts +0 -50
  467. package/src/tests/runtime-plugin-support.fixture.ts +0 -11
  468. package/src/tests/runtime-plugin-support.test.ts +0 -28
  469. package/src/tests/runtime-plugin.fixture.ts +0 -40
  470. package/src/tests/runtime-plugin.test.ts +0 -190
  471. package/src/tests/scrollbox-culling-bug.test.ts +0 -114
  472. package/src/tests/scrollbox-hitgrid-resize.test.ts +0 -136
  473. package/src/tests/scrollbox-hitgrid.test.ts +0 -909
  474. package/src/tests/scrollbox.test.ts +0 -1530
  475. package/src/tests/wrap-resize-perf.test.ts +0 -229
  476. package/src/tests/yoga-setters.test.ts +0 -921
  477. package/src/text-buffer-view.test.ts +0 -705
  478. package/src/text-buffer-view.ts +0 -189
  479. package/src/text-buffer.test.ts +0 -347
  480. package/src/text-buffer.ts +0 -250
  481. package/src/types.ts +0 -152
  482. package/src/utils.ts +0 -88
  483. package/src/zig/ansi.zig +0 -268
  484. package/src/zig/bench/README.md +0 -50
  485. package/src/zig/bench/buffer-draw-text-buffer_bench.zig +0 -887
  486. package/src/zig/bench/edit-buffer_bench.zig +0 -476
  487. package/src/zig/bench/native-span-feed_bench.zig +0 -100
  488. package/src/zig/bench/rope-markers_bench.zig +0 -713
  489. package/src/zig/bench/rope_bench.zig +0 -514
  490. package/src/zig/bench/styled-text_bench.zig +0 -470
  491. package/src/zig/bench/text-buffer-coords_bench.zig +0 -362
  492. package/src/zig/bench/text-buffer-view_bench.zig +0 -459
  493. package/src/zig/bench/text-chunk-graphemes_bench.zig +0 -273
  494. package/src/zig/bench/utf8_bench.zig +0 -799
  495. package/src/zig/bench-utils.zig +0 -431
  496. package/src/zig/bench.zig +0 -217
  497. package/src/zig/buffer.zig +0 -2223
  498. package/src/zig/build.zig +0 -289
  499. package/src/zig/build.zig.zon +0 -16
  500. package/src/zig/edit-buffer.zig +0 -825
  501. package/src/zig/editor-view.zig +0 -802
  502. package/src/zig/event-bus.zig +0 -13
  503. package/src/zig/event-emitter.zig +0 -65
  504. package/src/zig/file-logger.zig +0 -92
  505. package/src/zig/grapheme.zig +0 -599
  506. package/src/zig/lib.zig +0 -1834
  507. package/src/zig/link.zig +0 -333
  508. package/src/zig/logger.zig +0 -43
  509. package/src/zig/mem-registry.zig +0 -125
  510. package/src/zig/native-span-feed-bench-lib.zig +0 -7
  511. package/src/zig/native-span-feed.zig +0 -708
  512. package/src/zig/renderer.zig +0 -1386
  513. package/src/zig/rope.zig +0 -1220
  514. package/src/zig/syntax-style.zig +0 -161
  515. package/src/zig/terminal.zig +0 -975
  516. package/src/zig/test.zig +0 -70
  517. package/src/zig/tests/README.md +0 -18
  518. package/src/zig/tests/buffer_test.zig +0 -2526
  519. package/src/zig/tests/edit-buffer-history_test.zig +0 -271
  520. package/src/zig/tests/edit-buffer_test.zig +0 -1689
  521. package/src/zig/tests/editor-view_test.zig +0 -3299
  522. package/src/zig/tests/event-emitter_test.zig +0 -249
  523. package/src/zig/tests/grapheme_test.zig +0 -1304
  524. package/src/zig/tests/link_test.zig +0 -190
  525. package/src/zig/tests/mem-registry_test.zig +0 -473
  526. package/src/zig/tests/memory_leak_regression_test.zig +0 -159
  527. package/src/zig/tests/native-span-feed_test.zig +0 -1264
  528. package/src/zig/tests/renderer_test.zig +0 -1010
  529. package/src/zig/tests/rope-nested_test.zig +0 -712
  530. package/src/zig/tests/rope_fuzz_test.zig +0 -238
  531. package/src/zig/tests/rope_test.zig +0 -2362
  532. package/src/zig/tests/segment-merge.test.zig +0 -148
  533. package/src/zig/tests/syntax-style_test.zig +0 -557
  534. package/src/zig/tests/terminal_test.zig +0 -719
  535. package/src/zig/tests/text-buffer-drawing_test.zig +0 -3237
  536. package/src/zig/tests/text-buffer-highlights_test.zig +0 -666
  537. package/src/zig/tests/text-buffer-iterators_test.zig +0 -776
  538. package/src/zig/tests/text-buffer-segment_test.zig +0 -320
  539. package/src/zig/tests/text-buffer-selection_test.zig +0 -1035
  540. package/src/zig/tests/text-buffer-selection_viewport_test.zig +0 -358
  541. package/src/zig/tests/text-buffer-view_test.zig +0 -3649
  542. package/src/zig/tests/text-buffer_test.zig +0 -2191
  543. package/src/zig/tests/unicode-width-map.zon +0 -3909
  544. package/src/zig/tests/utf8_no_zwj_test.zig +0 -260
  545. package/src/zig/tests/utf8_test.zig +0 -4057
  546. package/src/zig/tests/utf8_wcwidth_cursor_test.zig +0 -267
  547. package/src/zig/tests/utf8_wcwidth_test.zig +0 -357
  548. package/src/zig/tests/word-wrap-editing_test.zig +0 -498
  549. package/src/zig/tests/wrap-cache-perf_test.zig +0 -113
  550. package/src/zig/text-buffer-iterators.zig +0 -499
  551. package/src/zig/text-buffer-segment.zig +0 -404
  552. package/src/zig/text-buffer-view.zig +0 -1371
  553. package/src/zig/text-buffer.zig +0 -1180
  554. package/src/zig/utf8.zig +0 -1948
  555. package/src/zig/utils.zig +0 -9
  556. package/src/zig-structs.ts +0 -261
  557. package/src/zig.ts +0 -3843
  558. package/tsconfig.build.json +0 -22
  559. package/tsconfig.json +0 -28
  560. /package/{src/lib/tree-sitter/assets → assets}/javascript/highlights.scm +0 -0
  561. /package/{src/lib/tree-sitter/assets → assets}/javascript/tree-sitter-javascript.wasm +0 -0
  562. /package/{src/lib/tree-sitter/assets → assets}/markdown/highlights.scm +0 -0
  563. /package/{src/lib/tree-sitter/assets → assets}/markdown/injections.scm +0 -0
  564. /package/{src/lib/tree-sitter/assets → assets}/markdown/tree-sitter-markdown.wasm +0 -0
  565. /package/{src/lib/tree-sitter/assets → assets}/markdown_inline/highlights.scm +0 -0
  566. /package/{src/lib/tree-sitter/assets → assets}/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
  567. /package/{src/lib/tree-sitter/assets → assets}/typescript/highlights.scm +0 -0
  568. /package/{src/lib/tree-sitter/assets → assets}/typescript/tree-sitter-typescript.wasm +0 -0
  569. /package/{src/lib/tree-sitter/assets → assets}/zig/highlights.scm +0 -0
  570. /package/{src/lib/tree-sitter/assets → assets}/zig/tree-sitter-zig.wasm +0 -0
package/src/renderer.ts DELETED
@@ -1,2363 +0,0 @@
1
- import { ANSI } from "./ansi.js"
2
- import { Renderable, RootRenderable } from "./Renderable.js"
3
- import {
4
- DebugOverlayCorner,
5
- type CursorStyleOptions,
6
- type MousePointerStyle,
7
- type RenderContext,
8
- type ThemeMode,
9
- type ViewportBounds,
10
- type WidthMethod,
11
- } from "./types.js"
12
- import { RGBA, parseColor, type ColorInput } from "./lib/RGBA.js"
13
- import type { Pointer } from "bun:ffi"
14
- import { OptimizedBuffer } from "./buffer.js"
15
- import { resolveRenderLib, type RenderLib } from "./zig.js"
16
- import { TerminalConsole, type ConsoleOptions, capture } from "./console.js"
17
- import { type MouseEventType, type RawMouseEvent, type ScrollInfo } from "./lib/parse.mouse.js"
18
- import { Selection } from "./lib/selection.js"
19
- import { Clipboard, type ClipboardTarget } from "./lib/clipboard.js"
20
- import { EventEmitter } from "events"
21
- import { destroySingleton, hasSingleton, singleton } from "./lib/singleton.js"
22
- import { getObjectsInViewport } from "./lib/objects-in-viewport.js"
23
- import { KeyHandler, InternalKeyHandler } from "./lib/KeyHandler.js"
24
- import { env, registerEnvVar } from "./lib/env.js"
25
- import { getTreeSitterClient } from "./lib/tree-sitter/index.js"
26
- import {
27
- createTerminalPalette,
28
- type TerminalPaletteDetector,
29
- type TerminalColors,
30
- type GetPaletteOptions,
31
- } from "./lib/terminal-palette.js"
32
- import {
33
- isCapabilityResponse,
34
- isPixelResolutionResponse,
35
- parsePixelResolution,
36
- } from "./lib/terminal-capability-detection.js"
37
- import { type Clock, type TimerHandle, SystemClock } from "./lib/clock.js"
38
- import { StdinParser, type StdinEvent } from "./lib/stdin-parser.js"
39
-
40
- registerEnvVar({
41
- name: "OTUI_DUMP_CAPTURES",
42
- description: "Dump captured output when the renderer exits.",
43
- type: "boolean",
44
- default: false,
45
- })
46
-
47
- registerEnvVar({
48
- name: "OTUI_NO_NATIVE_RENDER",
49
- description: "Disable native rendering. This will not actually output ansi and is useful for debugging.",
50
- type: "boolean",
51
- default: false,
52
- })
53
-
54
- registerEnvVar({
55
- name: "OTUI_USE_ALTERNATE_SCREEN",
56
- description: "Whether to use the console. Will not capture console output if set to false.",
57
- type: "boolean",
58
- default: true,
59
- })
60
-
61
- registerEnvVar({
62
- name: "OTUI_OVERRIDE_STDOUT",
63
- description: "Override the stdout stream. This is useful for debugging.",
64
- type: "boolean",
65
- default: true,
66
- })
67
-
68
- registerEnvVar({
69
- name: "OTUI_DEBUG",
70
- description: "Enable debug mode to capture all raw input for debugging purposes.",
71
- type: "boolean",
72
- default: false,
73
- })
74
-
75
- registerEnvVar({
76
- name: "OTUI_SHOW_STATS",
77
- description: "Show the debug overlay at startup.",
78
- type: "boolean",
79
- default: false,
80
- })
81
-
82
- export interface CliRendererConfig {
83
- stdin?: NodeJS.ReadStream
84
- stdout?: NodeJS.WriteStream
85
- remote?: boolean
86
- testing?: boolean
87
- exitOnCtrlC?: boolean
88
- exitSignals?: NodeJS.Signals[]
89
- forwardEnvKeys?: string[]
90
- debounceDelay?: number
91
- targetFps?: number
92
- maxFps?: number
93
- memorySnapshotInterval?: number
94
- useThread?: boolean
95
- gatherStats?: boolean
96
- maxStatSamples?: number
97
- consoleOptions?: Omit<ConsoleOptions, "clock">
98
- postProcessFns?: ((buffer: OptimizedBuffer, deltaTime: number) => void)[]
99
- enableMouseMovement?: boolean
100
- useMouse?: boolean
101
- autoFocus?: boolean
102
- useAlternateScreen?: boolean
103
- useConsole?: boolean
104
- experimental_splitHeight?: number
105
- useKittyKeyboard?: KittyKeyboardOptions | null
106
- backgroundColor?: ColorInput
107
- openConsoleOnError?: boolean
108
- prependInputHandlers?: ((sequence: string) => boolean)[]
109
- stdinParserMaxBufferBytes?: number
110
- clock?: Clock
111
- onDestroy?: () => void
112
- }
113
-
114
- export type PixelResolution = {
115
- width: number
116
- height: number
117
- }
118
-
119
- const DEFAULT_FORWARDED_ENV_KEYS = [
120
- "TMUX",
121
- "TERM",
122
- "OPENTUI_GRAPHICS",
123
- "TERM_PROGRAM",
124
- "TERM_PROGRAM_VERSION",
125
- "ALACRITTY_SOCKET",
126
- "ALACRITTY_LOG",
127
- "COLORTERM",
128
- "TERMUX_VERSION",
129
- "VHS_RECORD",
130
- "OPENTUI_FORCE_WCWIDTH",
131
- "OPENTUI_FORCE_UNICODE",
132
- "OPENTUI_FORCE_NOZWJ",
133
- "OPENTUI_FORCE_EXPLICIT_WIDTH",
134
- "WT_SESSION",
135
- "STY",
136
- ] as const
137
-
138
- // Kitty keyboard protocol flags
139
- // See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
140
- const KITTY_FLAG_DISAMBIGUATE = 0b1 // Report disambiguated escape codes
141
- const KITTY_FLAG_EVENT_TYPES = 0b10 // Report event types (press/repeat/release)
142
- const KITTY_FLAG_ALTERNATE_KEYS = 0b100 // Report alternate keys (e.g., numpad vs regular)
143
- const KITTY_FLAG_ALL_KEYS_AS_ESCAPES = 0b1000 // Report all keys as escape codes
144
- const KITTY_FLAG_REPORT_TEXT = 0b10000 // Report text associated with key events
145
-
146
- const DEFAULT_STDIN_PARSER_MAX_BUFFER_BYTES = 64 * 1024 * 1024
147
-
148
- /**
149
- * Kitty Keyboard Protocol configuration options
150
- * See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
151
- */
152
- export interface KittyKeyboardOptions {
153
- /** Disambiguate escape codes (fixes ESC timing, alt+key ambiguity, ctrl+c as event). Default: true */
154
- disambiguate?: boolean
155
- /** Report alternate keys (numpad, shifted, base layout) for cross-keyboard shortcuts. Default: true */
156
- alternateKeys?: boolean
157
- /** Report event types (press/repeat/release). Default: false */
158
- events?: boolean
159
- /** Report all keys as escape codes. Default: false */
160
- allKeysAsEscapes?: boolean
161
- /** Report text associated with key events. Default: false */
162
- reportText?: boolean
163
- }
164
-
165
- /**
166
- * Build kitty keyboard protocol flags based on configuration
167
- * @param config Kitty keyboard configuration object (null/undefined = disabled)
168
- * @returns The combined flags value (0 = disabled, >0 = enabled)
169
- * @internal Exported for testing
170
- */
171
- export function buildKittyKeyboardFlags(config: KittyKeyboardOptions | null | undefined): number {
172
- if (!config) {
173
- return 0
174
- }
175
-
176
- let flags = 0
177
-
178
- // Default: disambiguate + alternate keys (both default to true)
179
- // - Disambiguate (0b1): Fixes ESC timing issues, alt+key ambiguity, makes ctrl+c a key event
180
- // - Alternate keys (0b100): Reports shifted/base-layout keys for cross-keyboard shortcuts
181
-
182
- // disambiguate defaults to true unless explicitly set to false
183
- if (config.disambiguate !== false) {
184
- flags |= KITTY_FLAG_DISAMBIGUATE
185
- }
186
-
187
- // alternateKeys defaults to true unless explicitly set to false
188
- if (config.alternateKeys !== false) {
189
- flags |= KITTY_FLAG_ALTERNATE_KEYS
190
- }
191
-
192
- // Optional flags (default to false, only enabled when explicitly true)
193
- if (config.events === true) {
194
- flags |= KITTY_FLAG_EVENT_TYPES
195
- }
196
-
197
- if (config.allKeysAsEscapes === true) {
198
- flags |= KITTY_FLAG_ALL_KEYS_AS_ESCAPES
199
- }
200
-
201
- if (config.reportText === true) {
202
- flags |= KITTY_FLAG_REPORT_TEXT
203
- }
204
-
205
- return flags
206
- }
207
-
208
- export class MouseEvent {
209
- public readonly type: MouseEventType
210
- public readonly button: number
211
- public readonly x: number
212
- public readonly y: number
213
- public readonly source?: Renderable
214
- public readonly modifiers: {
215
- shift: boolean
216
- alt: boolean
217
- ctrl: boolean
218
- }
219
- public readonly scroll?: ScrollInfo
220
- public readonly target: Renderable | null
221
- public readonly isDragging?: boolean
222
- private _propagationStopped: boolean = false
223
- private _defaultPrevented: boolean = false
224
-
225
- public get propagationStopped(): boolean {
226
- return this._propagationStopped
227
- }
228
-
229
- public get defaultPrevented(): boolean {
230
- return this._defaultPrevented
231
- }
232
-
233
- constructor(target: Renderable | null, attributes: RawMouseEvent & { source?: Renderable; isDragging?: boolean }) {
234
- this.target = target
235
- this.type = attributes.type
236
- this.button = attributes.button
237
- this.x = attributes.x
238
- this.y = attributes.y
239
- this.modifiers = attributes.modifiers
240
- this.scroll = attributes.scroll
241
- this.source = attributes.source
242
- this.isDragging = attributes.isDragging
243
- }
244
-
245
- public stopPropagation(): void {
246
- this._propagationStopped = true
247
- }
248
-
249
- public preventDefault(): void {
250
- this._defaultPrevented = true
251
- }
252
- }
253
-
254
- export enum MouseButton {
255
- LEFT = 0,
256
- MIDDLE = 1,
257
- RIGHT = 2,
258
- WHEEL_UP = 4,
259
- WHEEL_DOWN = 5,
260
- }
261
-
262
- const rendererTracker = singleton("RendererTracker", () => {
263
- const renderers = new Set<CliRenderer>()
264
- return {
265
- addRenderer: (renderer: CliRenderer) => {
266
- renderers.add(renderer)
267
- },
268
- removeRenderer: (renderer: CliRenderer) => {
269
- renderers.delete(renderer)
270
- if (renderers.size === 0) {
271
- process.stdin.pause()
272
-
273
- if (hasSingleton("tree-sitter-client")) {
274
- getTreeSitterClient().destroy()
275
- destroySingleton("tree-sitter-client")
276
- }
277
- }
278
- },
279
- }
280
- })
281
-
282
- export async function createCliRenderer(config: CliRendererConfig = {}): Promise<CliRenderer> {
283
- if (process.argv.includes("--delay-start")) {
284
- await new Promise((resolve) => setTimeout(resolve, 5000))
285
- }
286
- const stdin = config.stdin || process.stdin
287
- const stdout = config.stdout || process.stdout
288
-
289
- const width = stdout.columns || 80
290
- const height = stdout.rows || 24
291
- const renderHeight =
292
- config.experimental_splitHeight && config.experimental_splitHeight > 0 ? config.experimental_splitHeight : height
293
-
294
- const ziglib = resolveRenderLib()
295
- const rendererPtr = ziglib.createRenderer(width, renderHeight, {
296
- remote: config.remote ?? false,
297
- testing: config.testing ?? false,
298
- })
299
- if (!rendererPtr) {
300
- throw new Error("Failed to create renderer")
301
- }
302
- if (config.useThread === undefined) {
303
- config.useThread = true
304
- }
305
-
306
- // Disable threading on linux because there currently is currently an issue
307
- // might be just a missing dependency for the build or something, but threads crash on linux
308
- if (process.platform === "linux") {
309
- config.useThread = false
310
- }
311
- ziglib.setUseThread(rendererPtr, config.useThread)
312
-
313
- const kittyConfig = config.useKittyKeyboard ?? {}
314
- const kittyFlags = buildKittyKeyboardFlags(kittyConfig)
315
-
316
- ziglib.setKittyKeyboardFlags(rendererPtr, kittyFlags)
317
-
318
- const renderer = new CliRenderer(ziglib, rendererPtr, stdin, stdout, width, height, config)
319
- if (!config.testing) {
320
- await renderer.setupTerminal()
321
- }
322
- return renderer
323
- }
324
-
325
- export enum CliRenderEvents {
326
- DEBUG_OVERLAY_TOGGLE = "debugOverlay:toggle",
327
- DESTROY = "destroy",
328
- }
329
-
330
- export enum RendererControlState {
331
- IDLE = "idle",
332
- AUTO_STARTED = "auto_started",
333
- EXPLICIT_STARTED = "explicit_started",
334
- EXPLICIT_PAUSED = "explicit_paused",
335
- EXPLICIT_SUSPENDED = "explicit_suspended",
336
- EXPLICIT_STOPPED = "explicit_stopped",
337
- }
338
-
339
- export class CliRenderer extends EventEmitter implements RenderContext {
340
- private static animationFrameId = 0
341
- private lib: RenderLib
342
- public rendererPtr: Pointer
343
- public stdin: NodeJS.ReadStream
344
- private stdout: NodeJS.WriteStream
345
- private exitOnCtrlC: boolean
346
- private exitSignals: NodeJS.Signals[]
347
- private _exitListenersAdded: boolean = false
348
- private _isDestroyed: boolean = false
349
- private _destroyPending: boolean = false
350
- private _destroyFinalized: boolean = false
351
- public nextRenderBuffer: OptimizedBuffer
352
- public currentRenderBuffer: OptimizedBuffer
353
- private _isRunning: boolean = false
354
- private targetFps: number = 30
355
- private maxFps: number = 60
356
- private automaticMemorySnapshot: boolean = false
357
- private memorySnapshotInterval: number
358
- private memorySnapshotTimer: TimerHandle | null = null
359
- private lastMemorySnapshot: { heapUsed: number; heapTotal: number; arrayBuffers: number } = {
360
- heapUsed: 0,
361
- heapTotal: 0,
362
- arrayBuffers: 0,
363
- }
364
- public readonly root: RootRenderable
365
- public width: number
366
- public height: number
367
- private _useThread: boolean = false
368
- private gatherStats: boolean = false
369
- private frameTimes: number[] = []
370
- private maxStatSamples: number = 300
371
- private postProcessFns: ((buffer: OptimizedBuffer, deltaTime: number) => void)[] = []
372
- private backgroundColor: RGBA = RGBA.fromInts(0, 0, 0, 0)
373
- private waitingForPixelResolution: boolean = false
374
- private readonly clock: Clock
375
-
376
- private rendering: boolean = false
377
- private renderingNative: boolean = false
378
- private renderTimeout: TimerHandle | null = null
379
- private lastTime: number = 0
380
- private frameCount: number = 0
381
- private lastFpsTime: number = 0
382
- private currentFps: number = 0
383
- private targetFrameTime: number = 1000 / this.targetFps
384
- private minTargetFrameTime: number = 1000 / this.maxFps
385
- private immediateRerenderRequested: boolean = false
386
- private updateScheduled: boolean = false
387
-
388
- private liveRequestCounter: number = 0
389
- private _controlState: RendererControlState = RendererControlState.IDLE
390
-
391
- private frameCallbacks: ((deltaTime: number) => Promise<void>)[] = []
392
- private renderStats: {
393
- frameCount: number
394
- fps: number
395
- renderTime?: number
396
- frameCallbackTime: number
397
- } = {
398
- frameCount: 0,
399
- fps: 0,
400
- renderTime: 0,
401
- frameCallbackTime: 0,
402
- }
403
- public debugOverlay = {
404
- enabled: env.OTUI_SHOW_STATS,
405
- corner: DebugOverlayCorner.bottomRight,
406
- }
407
-
408
- private _console: TerminalConsole
409
- private _resolution: PixelResolution | null = null
410
- private _keyHandler: InternalKeyHandler
411
- private stdinParser: StdinParser | null = null
412
- private readonly oscSubscribers = new Set<(sequence: string) => void>()
413
- private hasLoggedStdinParserError = false
414
-
415
- private animationRequest: Map<number, FrameRequestCallback> = new Map()
416
-
417
- private resizeTimeoutId: TimerHandle | null = null
418
- private capabilityTimeoutId: TimerHandle | null = null
419
- private resizeDebounceDelay: number = 100
420
-
421
- private enableMouseMovement: boolean = false
422
- private _useMouse: boolean = true
423
- private autoFocus: boolean = true
424
- private _useAlternateScreen: boolean = env.OTUI_USE_ALTERNATE_SCREEN
425
- private _suspendedMouseEnabled: boolean = false
426
- private _previousControlState: RendererControlState = RendererControlState.IDLE
427
- private capturedRenderable?: Renderable
428
- private lastOverRenderableNum: number = 0
429
- private lastOverRenderable?: Renderable
430
-
431
- private currentSelection: Selection | null = null
432
- private selectionContainers: Renderable[] = []
433
- private clipboard: Clipboard
434
-
435
- private _splitHeight: number = 0
436
- private renderOffset: number = 0
437
-
438
- private _terminalWidth: number = 0
439
- private _terminalHeight: number = 0
440
- private _terminalIsSetup: boolean = false
441
-
442
- private realStdoutWrite: (chunk: any, encoding?: any, callback?: any) => boolean
443
- private captureCallback: () => void = () => {
444
- if (this._splitHeight > 0) {
445
- this.requestRender()
446
- }
447
- }
448
-
449
- private _useConsole: boolean = true
450
- private sigwinchHandler: () => void = (() => {
451
- const width = this.stdout.columns || 80
452
- const height = this.stdout.rows || 24
453
- this.handleResize(width, height)
454
- }).bind(this)
455
- private _capabilities: any | null = null
456
- private _latestPointer: { x: number; y: number } = { x: 0, y: 0 }
457
- private _hasPointer: boolean = false
458
- private _lastPointerModifiers: RawMouseEvent["modifiers"] = { shift: false, alt: false, ctrl: false }
459
- private _currentMousePointerStyle: MousePointerStyle | undefined = undefined
460
-
461
- private _currentFocusedRenderable: Renderable | null = null
462
- private lifecyclePasses: Set<Renderable> = new Set()
463
- private _openConsoleOnError: boolean = true
464
- private _paletteDetector: TerminalPaletteDetector | null = null
465
- private _cachedPalette: TerminalColors | null = null
466
- private _paletteDetectionPromise: Promise<TerminalColors> | null = null
467
- private _onDestroy?: () => void
468
- private _themeMode: ThemeMode | null = null
469
-
470
- private sequenceHandlers: ((sequence: string) => boolean)[] = []
471
- private prependedInputHandlers: ((sequence: string) => boolean)[] = []
472
- private shouldRestoreModesOnNextFocus: boolean = false
473
-
474
- private idleResolvers: (() => void)[] = []
475
-
476
- private _debugInputs: Array<{ timestamp: string; sequence: string }> = []
477
- private _debugModeEnabled: boolean = env.OTUI_DEBUG
478
-
479
- private handleError: (error: Error) => void = ((error: Error) => {
480
- console.error(error)
481
-
482
- if (this._openConsoleOnError) {
483
- this.console.show()
484
- }
485
- }).bind(this)
486
-
487
- private dumpOutputCache(optionalMessage: string = ""): void {
488
- const cachedLogs = this.console.getCachedLogs()
489
- const capturedOutput = capture.claimOutput()
490
-
491
- if (capturedOutput.length > 0 || cachedLogs.length > 0) {
492
- this.realStdoutWrite.call(this.stdout, optionalMessage)
493
- }
494
-
495
- if (cachedLogs.length > 0) {
496
- this.realStdoutWrite.call(this.stdout, "Console cache:\n")
497
- this.realStdoutWrite.call(this.stdout, cachedLogs)
498
- }
499
-
500
- if (capturedOutput.length > 0) {
501
- this.realStdoutWrite.call(this.stdout, "\nCaptured output:\n")
502
- this.realStdoutWrite.call(this.stdout, capturedOutput + "\n")
503
- }
504
-
505
- this.realStdoutWrite.call(this.stdout, ANSI.reset)
506
- }
507
-
508
- private exitHandler: () => void = (() => {
509
- this.destroy()
510
- if (env.OTUI_DUMP_CAPTURES) {
511
- Bun.sleep(100).then(() => {
512
- this.dumpOutputCache("=== CAPTURED OUTPUT ===\n")
513
- })
514
- }
515
- }).bind(this)
516
-
517
- private warningHandler: (warning: any) => void = ((warning: any) => {
518
- console.warn(JSON.stringify(warning.message, null, 2))
519
- }).bind(this)
520
-
521
- public get controlState(): RendererControlState {
522
- return this._controlState
523
- }
524
-
525
- constructor(
526
- lib: RenderLib,
527
- rendererPtr: Pointer,
528
- stdin: NodeJS.ReadStream,
529
- stdout: NodeJS.WriteStream,
530
- width: number,
531
- height: number,
532
- config: CliRendererConfig = {},
533
- ) {
534
- super()
535
-
536
- rendererTracker.addRenderer(this)
537
-
538
- this.stdin = stdin
539
- this.stdout = stdout
540
- this.realStdoutWrite = stdout.write
541
- this.lib = lib
542
- this._terminalWidth = stdout.columns ?? width
543
- this._terminalHeight = stdout.rows ?? height
544
- this.width = width
545
- this.height = height
546
- this._useThread = config.useThread === undefined ? false : config.useThread
547
- this._splitHeight = config.experimental_splitHeight || 0
548
-
549
- if (this._splitHeight > 0) {
550
- capture.on("write", this.captureCallback)
551
- this.renderOffset = height - this._splitHeight
552
- this.height = this._splitHeight
553
- lib.setRenderOffset(rendererPtr, this.renderOffset)
554
- }
555
-
556
- this.rendererPtr = rendererPtr
557
-
558
- const forwardEnvKeys = config.forwardEnvKeys ?? [...DEFAULT_FORWARDED_ENV_KEYS]
559
- for (const key of forwardEnvKeys) {
560
- const value = process.env[key]
561
- if (value === undefined) continue
562
- this.lib.setTerminalEnvVar(this.rendererPtr, key, value)
563
- }
564
-
565
- this.exitOnCtrlC = config.exitOnCtrlC === undefined ? true : config.exitOnCtrlC
566
- this.exitSignals = config.exitSignals || [
567
- "SIGINT", // Ctrl+C
568
- "SIGTERM", // Termination signal
569
- "SIGQUIT", // Ctrl+\
570
- "SIGABRT", // Abort signal
571
- "SIGHUP", // Hangup (terminal closed)
572
- "SIGBREAK", // Ctrl+Break on Windows
573
- "SIGPIPE", // Broken pipe
574
- "SIGBUS", // Bus error
575
- "SIGFPE", // Floating point exception
576
- ]
577
-
578
- this.clipboard = new Clipboard(this.lib, this.rendererPtr)
579
- this.resizeDebounceDelay = config.debounceDelay || 100
580
- this.targetFps = config.targetFps || 30
581
- this.maxFps = config.maxFps || 60
582
- this.targetFrameTime = 1000 / this.targetFps
583
- this.minTargetFrameTime = 1000 / this.maxFps
584
- this.memorySnapshotInterval = config.memorySnapshotInterval ?? 0
585
- this.gatherStats = config.gatherStats || false
586
- this.maxStatSamples = config.maxStatSamples || 300
587
- this.enableMouseMovement = config.enableMouseMovement ?? true
588
- this._useMouse = config.useMouse ?? true
589
- this.autoFocus = config.autoFocus ?? true
590
- this._useAlternateScreen = config.useAlternateScreen ?? env.OTUI_USE_ALTERNATE_SCREEN
591
- this.nextRenderBuffer = this.lib.getNextBuffer(this.rendererPtr)
592
- this.currentRenderBuffer = this.lib.getCurrentBuffer(this.rendererPtr)
593
- this.postProcessFns = config.postProcessFns || []
594
- this.prependedInputHandlers = config.prependInputHandlers || []
595
-
596
- this.root = new RootRenderable(this)
597
-
598
- if (this.memorySnapshotInterval > 0) {
599
- this.startMemorySnapshotTimer()
600
- }
601
-
602
- if (env.OTUI_OVERRIDE_STDOUT) {
603
- this.stdout.write = this.interceptStdoutWrite.bind(this)
604
- }
605
-
606
- // Handle terminal resize
607
- process.on("SIGWINCH", this.sigwinchHandler)
608
-
609
- process.on("warning", this.warningHandler)
610
-
611
- process.on("uncaughtException", this.handleError)
612
- process.on("unhandledRejection", this.handleError)
613
- process.on("beforeExit", this.exitHandler)
614
-
615
- const kittyConfig = config.useKittyKeyboard ?? {}
616
- const useKittyForParsing = kittyConfig !== null
617
- this._keyHandler = new InternalKeyHandler()
618
- this._keyHandler.on("keypress", (event) => {
619
- if (this.exitOnCtrlC && event.name === "c" && event.ctrl) {
620
- process.nextTick(() => {
621
- this.destroy()
622
- })
623
- return
624
- }
625
- })
626
-
627
- this.addExitListeners()
628
-
629
- this.clock = config.clock ?? new SystemClock()
630
-
631
- const stdinParserMaxBufferBytes = config.stdinParserMaxBufferBytes ?? DEFAULT_STDIN_PARSER_MAX_BUFFER_BYTES
632
- this.stdinParser = new StdinParser({
633
- timeoutMs: 10,
634
- maxPendingBytes: stdinParserMaxBufferBytes,
635
- armTimeouts: true,
636
- onTimeoutFlush: () => {
637
- this.drainStdinParser()
638
- },
639
- useKittyKeyboard: useKittyForParsing,
640
- clock: this.clock,
641
- })
642
-
643
- this._console = new TerminalConsole(this, {
644
- ...(config.consoleOptions ?? {}),
645
- clock: this.clock,
646
- })
647
- this.useConsole = config.useConsole ?? true
648
- this._openConsoleOnError = config.openConsoleOnError ?? process.env.NODE_ENV !== "production"
649
- this._onDestroy = config.onDestroy
650
-
651
- global.requestAnimationFrame = (callback: FrameRequestCallback) => {
652
- const id = CliRenderer.animationFrameId++
653
- this.animationRequest.set(id, callback)
654
- this.requestLive()
655
- return id
656
- }
657
- global.cancelAnimationFrame = (handle: number) => {
658
- this.animationRequest.delete(handle)
659
- }
660
-
661
- const window = global.window
662
- if (!window) {
663
- global.window = {} as Window & typeof globalThis
664
- }
665
- global.window.requestAnimationFrame = requestAnimationFrame
666
-
667
- // Prevents output from being written to the terminal, useful for debugging
668
- if (env.OTUI_NO_NATIVE_RENDER) {
669
- this.renderNative = () => {
670
- if (this._splitHeight > 0) {
671
- this.flushStdoutCache(this._splitHeight)
672
- }
673
- }
674
- }
675
-
676
- this.setupInput()
677
- }
678
-
679
- private addExitListeners(): void {
680
- if (this._exitListenersAdded || this.exitSignals.length === 0) return
681
-
682
- this.exitSignals.forEach((signal) => {
683
- process.addListener(signal, this.exitHandler)
684
- })
685
-
686
- this._exitListenersAdded = true
687
- }
688
-
689
- private removeExitListeners(): void {
690
- if (!this._exitListenersAdded || this.exitSignals.length === 0) return
691
-
692
- this.exitSignals.forEach((signal) => {
693
- process.removeListener(signal, this.exitHandler)
694
- })
695
-
696
- this._exitListenersAdded = false
697
- }
698
-
699
- public get isDestroyed(): boolean {
700
- return this._isDestroyed
701
- }
702
-
703
- public registerLifecyclePass(renderable: Renderable) {
704
- this.lifecyclePasses.add(renderable)
705
- }
706
-
707
- public unregisterLifecyclePass(renderable: Renderable) {
708
- this.lifecyclePasses.delete(renderable)
709
- }
710
-
711
- public getLifecyclePasses() {
712
- return this.lifecyclePasses
713
- }
714
-
715
- public get currentFocusedRenderable(): Renderable | null {
716
- return this._currentFocusedRenderable
717
- }
718
-
719
- public focusRenderable(renderable: Renderable) {
720
- if (this._currentFocusedRenderable === renderable) return
721
-
722
- if (this._currentFocusedRenderable) {
723
- this._currentFocusedRenderable.blur()
724
- }
725
-
726
- this._currentFocusedRenderable = renderable
727
- }
728
-
729
- private setCapturedRenderable(renderable: Renderable | undefined): void {
730
- if (this.capturedRenderable === renderable) {
731
- return
732
- }
733
- this.capturedRenderable = renderable
734
- }
735
-
736
- public addToHitGrid(x: number, y: number, width: number, height: number, id: number) {
737
- if (id !== this.capturedRenderable?.num) {
738
- this.lib.addToHitGrid(this.rendererPtr, x, y, width, height, id)
739
- }
740
- }
741
-
742
- public pushHitGridScissorRect(x: number, y: number, width: number, height: number): void {
743
- this.lib.hitGridPushScissorRect(this.rendererPtr, x, y, width, height)
744
- }
745
-
746
- public popHitGridScissorRect(): void {
747
- this.lib.hitGridPopScissorRect(this.rendererPtr)
748
- }
749
-
750
- public clearHitGridScissorRects(): void {
751
- this.lib.hitGridClearScissorRects(this.rendererPtr)
752
- }
753
-
754
- public get widthMethod(): WidthMethod {
755
- const caps = this.capabilities
756
- return caps?.unicode === "wcwidth" ? "wcwidth" : "unicode"
757
- }
758
-
759
- private writeOut(chunk: any, encoding?: any, callback?: any): boolean {
760
- if (this.rendererPtr && this._useThread) {
761
- const data = typeof chunk === "string" ? chunk : (chunk?.toString() ?? "")
762
- this.lib.writeOut(this.rendererPtr, data)
763
- if (typeof callback === "function") {
764
- process.nextTick(callback)
765
- }
766
- return true
767
- }
768
-
769
- return this.realStdoutWrite.call(this.stdout, chunk, encoding, callback)
770
- }
771
-
772
- public requestRender() {
773
- if (this._controlState === RendererControlState.EXPLICIT_SUSPENDED) {
774
- return
775
- }
776
-
777
- if (this._isRunning) {
778
- return
779
- }
780
-
781
- // NOTE: Using a frame callback that causes a re-render while already rendering
782
- // leads to a continuous loop of renders.
783
- if (this.rendering) {
784
- this.immediateRerenderRequested = true
785
- return
786
- }
787
-
788
- if (!this.updateScheduled && !this.renderTimeout) {
789
- this.updateScheduled = true
790
- const now = this.clock.now()
791
- const elapsed = now - this.lastTime
792
- const delay = Math.max(this.minTargetFrameTime - elapsed, 0)
793
-
794
- if (delay === 0) {
795
- process.nextTick(() => this.activateFrame())
796
- return
797
- }
798
-
799
- this.clock.setTimeout(() => this.activateFrame(), delay)
800
- }
801
- }
802
-
803
- private async activateFrame() {
804
- await this.loop()
805
- this.updateScheduled = false
806
- this.resolveIdleIfNeeded()
807
- }
808
-
809
- public get useConsole(): boolean {
810
- return this._useConsole
811
- }
812
-
813
- public set useConsole(value: boolean) {
814
- this._useConsole = value
815
- if (value) {
816
- this.console.activate()
817
- } else {
818
- this.console.deactivate()
819
- }
820
- }
821
-
822
- public get isRunning(): boolean {
823
- return this._isRunning
824
- }
825
-
826
- private isIdleNow(): boolean {
827
- return (
828
- !this._isRunning &&
829
- !this.rendering &&
830
- !this.renderTimeout &&
831
- !this.updateScheduled &&
832
- !this.immediateRerenderRequested
833
- )
834
- }
835
-
836
- private resolveIdleIfNeeded(): void {
837
- if (!this.isIdleNow()) return
838
- const resolvers = this.idleResolvers.splice(0)
839
- for (const resolve of resolvers) {
840
- resolve()
841
- }
842
- }
843
-
844
- public idle(): Promise<void> {
845
- if (this._isDestroyed) return Promise.resolve()
846
- if (this.isIdleNow()) return Promise.resolve()
847
- return new Promise<void>((resolve) => {
848
- this.idleResolvers.push(resolve)
849
- })
850
- }
851
-
852
- public get resolution(): PixelResolution | null {
853
- return this._resolution
854
- }
855
-
856
- public get console(): TerminalConsole {
857
- return this._console
858
- }
859
-
860
- public get keyInput(): KeyHandler {
861
- return this._keyHandler
862
- }
863
-
864
- public get _internalKeyInput(): InternalKeyHandler {
865
- return this._keyHandler
866
- }
867
-
868
- public get terminalWidth(): number {
869
- return this._terminalWidth
870
- }
871
-
872
- public get terminalHeight(): number {
873
- return this._terminalHeight
874
- }
875
-
876
- public get useThread(): boolean {
877
- return this._useThread
878
- }
879
-
880
- public get useMouse(): boolean {
881
- return this._useMouse
882
- }
883
-
884
- public set useMouse(useMouse: boolean) {
885
- if (this._useMouse === useMouse) return // No change needed
886
-
887
- this._useMouse = useMouse
888
-
889
- if (useMouse) {
890
- this.enableMouse()
891
- } else {
892
- this.disableMouse()
893
- }
894
- }
895
-
896
- public get experimental_splitHeight(): number {
897
- return this._splitHeight
898
- }
899
-
900
- public get liveRequestCount(): number {
901
- return this.liveRequestCounter
902
- }
903
-
904
- public get currentControlState(): string {
905
- return this._controlState
906
- }
907
-
908
- public get capabilities(): any | null {
909
- return this._capabilities
910
- }
911
-
912
- public get themeMode(): ThemeMode | null {
913
- return this._themeMode
914
- }
915
-
916
- public getDebugInputs(): Array<{ timestamp: string; sequence: string }> {
917
- return [...this._debugInputs]
918
- }
919
-
920
- public get useKittyKeyboard(): boolean {
921
- return this.lib.getKittyKeyboardFlags(this.rendererPtr) > 0
922
- }
923
-
924
- public set useKittyKeyboard(use: boolean) {
925
- const flags = use ? KITTY_FLAG_DISAMBIGUATE | KITTY_FLAG_ALTERNATE_KEYS : 0
926
- this.lib.setKittyKeyboardFlags(this.rendererPtr, flags)
927
- }
928
-
929
- public set experimental_splitHeight(splitHeight: number) {
930
- if (splitHeight < 0) splitHeight = 0
931
-
932
- const prevSplitHeight = this._splitHeight
933
-
934
- if (splitHeight > 0) {
935
- this._splitHeight = splitHeight
936
- this.renderOffset = this._terminalHeight - this._splitHeight
937
- this.height = this._splitHeight
938
-
939
- if (prevSplitHeight === 0) {
940
- this.useConsole = false
941
- capture.on("write", this.captureCallback)
942
- const freedLines = this._terminalHeight - this._splitHeight
943
- const scrollDown = ANSI.scrollDown(freedLines)
944
- this.writeOut(scrollDown)
945
- } else if (prevSplitHeight > this._splitHeight) {
946
- const freedLines = prevSplitHeight - this._splitHeight
947
- const scrollDown = ANSI.scrollDown(freedLines)
948
- this.writeOut(scrollDown)
949
- } else if (prevSplitHeight < this._splitHeight) {
950
- const additionalLines = this._splitHeight - prevSplitHeight
951
- const scrollUp = ANSI.scrollUp(additionalLines)
952
- this.writeOut(scrollUp)
953
- }
954
- } else {
955
- if (prevSplitHeight > 0) {
956
- this.flushStdoutCache(this._terminalHeight, true)
957
-
958
- capture.off("write", this.captureCallback)
959
- this.useConsole = true
960
- }
961
-
962
- this._splitHeight = 0
963
- this.renderOffset = 0
964
- this.height = this._terminalHeight
965
- }
966
-
967
- this.width = this._terminalWidth
968
- this.lib.setRenderOffset(this.rendererPtr, this.renderOffset)
969
- this.lib.resizeRenderer(this.rendererPtr, this.width, this.height)
970
- this.nextRenderBuffer = this.lib.getNextBuffer(this.rendererPtr)
971
-
972
- this._console.resize(this.width, this.height)
973
- this.root.resize(this.width, this.height)
974
- this.emit("resize", this.width, this.height)
975
- this.requestRender()
976
- }
977
-
978
- private interceptStdoutWrite = (chunk: any, encoding?: any, callback?: any): boolean => {
979
- const text = chunk.toString()
980
-
981
- capture.write("stdout", text)
982
- if (this._splitHeight > 0) {
983
- this.requestRender()
984
- }
985
-
986
- if (typeof callback === "function") {
987
- process.nextTick(callback)
988
- }
989
-
990
- return true
991
- }
992
-
993
- public disableStdoutInterception(): void {
994
- this.stdout.write = this.realStdoutWrite
995
- }
996
-
997
- // TODO: Move this to native
998
- private flushStdoutCache(space: number, force: boolean = false): boolean {
999
- if (capture.size === 0 && !force) return false
1000
-
1001
- const output = capture.claimOutput()
1002
-
1003
- const rendererStartLine = this._terminalHeight - this._splitHeight
1004
- const flush = ANSI.moveCursorAndClear(rendererStartLine, 1)
1005
-
1006
- const outputLine = this._terminalHeight - this._splitHeight
1007
- const move = ANSI.moveCursor(outputLine, 1)
1008
-
1009
- let clear = ""
1010
- if (space > 0) {
1011
- const backgroundColor = this.backgroundColor.toInts()
1012
- const newlines = " ".repeat(this.width) + "\n".repeat(space)
1013
- // Check if background is transparent (alpha = 0)
1014
- if (backgroundColor[3] === 0) {
1015
- clear = newlines
1016
- } else {
1017
- clear =
1018
- ANSI.setRgbBackground(backgroundColor[0], backgroundColor[1], backgroundColor[2]) +
1019
- newlines +
1020
- ANSI.resetBackground
1021
- }
1022
- }
1023
-
1024
- this.writeOut(flush + move + output + clear)
1025
-
1026
- return true
1027
- }
1028
-
1029
- private enableMouse(): void {
1030
- this._useMouse = true
1031
- this.lib.enableMouse(this.rendererPtr, this.enableMouseMovement)
1032
- }
1033
-
1034
- private disableMouse(): void {
1035
- this._useMouse = false
1036
- this.setCapturedRenderable(undefined)
1037
- this.stdinParser?.resetMouseState()
1038
- this.lib.disableMouse(this.rendererPtr)
1039
- }
1040
-
1041
- public enableKittyKeyboard(flags: number = 0b00011): void {
1042
- this.lib.enableKittyKeyboard(this.rendererPtr, flags)
1043
- }
1044
-
1045
- public disableKittyKeyboard(): void {
1046
- this.lib.disableKittyKeyboard(this.rendererPtr)
1047
- }
1048
-
1049
- public set useThread(useThread: boolean) {
1050
- this._useThread = useThread
1051
- this.lib.setUseThread(this.rendererPtr, useThread)
1052
- }
1053
-
1054
- // TODO: All input management may move to native when zig finally has async io support again,
1055
- // without rolling a full event loop
1056
- public async setupTerminal(): Promise<void> {
1057
- if (this._terminalIsSetup) return
1058
- this._terminalIsSetup = true
1059
-
1060
- this.lib.setupTerminal(this.rendererPtr, this._useAlternateScreen)
1061
- this._capabilities = this.lib.getTerminalCapabilities(this.rendererPtr)
1062
-
1063
- if (this.debugOverlay.enabled) {
1064
- this.lib.setDebugOverlay(this.rendererPtr, true, this.debugOverlay.corner)
1065
- if (!this.memorySnapshotInterval) {
1066
- this.memorySnapshotInterval = 3000
1067
- this.startMemorySnapshotTimer()
1068
- this.automaticMemorySnapshot = true
1069
- }
1070
- }
1071
-
1072
- this.capabilityTimeoutId = this.clock.setTimeout(() => {
1073
- this.capabilityTimeoutId = null
1074
- this.removeInputHandler(this.capabilityHandler)
1075
- }, 5000)
1076
-
1077
- if (this._useMouse) {
1078
- this.enableMouse()
1079
- }
1080
-
1081
- this.queryPixelResolution()
1082
- }
1083
-
1084
- private stdinListener: (chunk: Buffer | string) => void = ((chunk: Buffer | string) => {
1085
- const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
1086
- if (!this.stdinParser) return
1087
-
1088
- try {
1089
- this.stdinParser.push(data)
1090
- this.drainStdinParser()
1091
- } catch (error) {
1092
- this.handleStdinParserFailure(error)
1093
- }
1094
- }).bind(this)
1095
-
1096
- public addInputHandler(handler: (sequence: string) => boolean): void {
1097
- this.sequenceHandlers.push(handler)
1098
- }
1099
-
1100
- public prependInputHandler(handler: (sequence: string) => boolean): void {
1101
- this.sequenceHandlers.unshift(handler)
1102
- }
1103
-
1104
- public removeInputHandler(handler: (sequence: string) => boolean): void {
1105
- this.sequenceHandlers = this.sequenceHandlers.filter((candidate) => candidate !== handler)
1106
- }
1107
-
1108
- public subscribeOsc(handler: (sequence: string) => void): () => void {
1109
- this.oscSubscribers.add(handler)
1110
- return () => {
1111
- this.oscSubscribers.delete(handler)
1112
- }
1113
- }
1114
-
1115
- private capabilityHandler: (sequence: string) => boolean = ((sequence: string) => {
1116
- if (isCapabilityResponse(sequence)) {
1117
- this.lib.processCapabilityResponse(this.rendererPtr, sequence)
1118
- this._capabilities = this.lib.getTerminalCapabilities(this.rendererPtr)
1119
- this.emit("capabilities", this._capabilities)
1120
- return true
1121
- }
1122
- return false
1123
- }).bind(this)
1124
-
1125
- private focusHandler: (sequence: string) => boolean = ((sequence: string) => {
1126
- if (sequence === "\x1b[I") {
1127
- // When the terminal regains focus, some terminal emulators (notably
1128
- // Windows Terminal / ConPTY) may have stripped DEC private modes like
1129
- // mouse tracking, bracketed paste, and focus tracking itself while the
1130
- // window was unfocused.
1131
- if (this.shouldRestoreModesOnNextFocus) {
1132
- this.lib.restoreTerminalModes(this.rendererPtr)
1133
- this.shouldRestoreModesOnNextFocus = false
1134
- }
1135
- this.emit("focus")
1136
- return true
1137
- }
1138
- if (sequence === "\x1b[O") {
1139
- this.shouldRestoreModesOnNextFocus = true
1140
- this.emit("blur")
1141
- return true
1142
- }
1143
- return false
1144
- }).bind(this)
1145
-
1146
- private themeModeHandler: (sequence: string) => boolean = ((sequence: string) => {
1147
- if (sequence === "\x1b[?997;1n") {
1148
- if (this._themeMode !== "dark") {
1149
- this._themeMode = "dark"
1150
- this.emit("theme_mode", "dark")
1151
- }
1152
- return true
1153
- }
1154
- if (sequence === "\x1b[?997;2n") {
1155
- if (this._themeMode !== "light") {
1156
- this._themeMode = "light"
1157
- this.emit("theme_mode", "light")
1158
- }
1159
- return true
1160
- }
1161
- return false
1162
- }).bind(this)
1163
-
1164
- private dispatchSequenceHandlers(sequence: string): boolean {
1165
- if (this._debugModeEnabled) {
1166
- this._debugInputs.push({
1167
- timestamp: new Date().toISOString(),
1168
- sequence,
1169
- })
1170
- }
1171
-
1172
- for (const handler of this.sequenceHandlers) {
1173
- if (handler(sequence)) {
1174
- return true
1175
- }
1176
- }
1177
-
1178
- return false
1179
- }
1180
-
1181
- private drainStdinParser(): void {
1182
- if (!this.stdinParser) return
1183
-
1184
- this.stdinParser.drain((event) => {
1185
- this.handleStdinEvent(event)
1186
- })
1187
- }
1188
-
1189
- private handleStdinEvent(event: StdinEvent): void {
1190
- switch (event.type) {
1191
- case "key":
1192
- if (this.dispatchSequenceHandlers(event.raw)) {
1193
- return
1194
- }
1195
-
1196
- this._keyHandler.processParsedKey(event.key)
1197
- return
1198
- case "mouse":
1199
- if (this._useMouse && this.processSingleMouseEvent(event.event)) {
1200
- return
1201
- }
1202
-
1203
- this.dispatchSequenceHandlers(event.raw)
1204
- return
1205
- case "paste":
1206
- this._keyHandler.processPaste(event.bytes, event.metadata)
1207
- return
1208
- case "response":
1209
- if (event.protocol === "osc") {
1210
- for (const subscriber of this.oscSubscribers) {
1211
- subscriber(event.sequence)
1212
- }
1213
- }
1214
-
1215
- this.dispatchSequenceHandlers(event.sequence)
1216
- return
1217
- }
1218
- }
1219
-
1220
- private handleStdinParserFailure(error: unknown): void {
1221
- if (!this.hasLoggedStdinParserError) {
1222
- this.hasLoggedStdinParserError = true
1223
- if (process.env.NODE_ENV !== "test") {
1224
- console.error("[stdin-parser-error] parser failure, resetting parser", error)
1225
- }
1226
- }
1227
-
1228
- try {
1229
- this.stdinParser?.reset()
1230
- } catch (resetError) {
1231
- console.error("stdin parser reset failed after parser error", resetError)
1232
- }
1233
- }
1234
-
1235
- private setupInput(): void {
1236
- for (const handler of this.prependedInputHandlers) {
1237
- this.addInputHandler(handler)
1238
- }
1239
-
1240
- this.addInputHandler((sequence: string) => {
1241
- if (isPixelResolutionResponse(sequence) && this.waitingForPixelResolution) {
1242
- const resolution = parsePixelResolution(sequence)
1243
- if (resolution) {
1244
- this._resolution = resolution
1245
- this.waitingForPixelResolution = false
1246
- }
1247
- return true
1248
- }
1249
- return false
1250
- })
1251
- this.addInputHandler(this.capabilityHandler)
1252
- this.addInputHandler(this.focusHandler)
1253
- this.addInputHandler(this.themeModeHandler)
1254
-
1255
- if (this.stdin.setRawMode) {
1256
- this.stdin.setRawMode(true)
1257
- }
1258
-
1259
- this.stdin.resume()
1260
- this.stdin.on("data", this.stdinListener)
1261
- }
1262
-
1263
- private dispatchMouseEvent(
1264
- target: Renderable,
1265
- attributes: RawMouseEvent & { source?: Renderable; isDragging?: boolean },
1266
- ): MouseEvent {
1267
- const event = new MouseEvent(target, attributes)
1268
- target.processMouseEvent(event)
1269
-
1270
- if (this.autoFocus && event.type === "down" && event.button === MouseButton.LEFT && !event.defaultPrevented) {
1271
- let current: Renderable | null = target
1272
- while (current) {
1273
- if (current.focusable) {
1274
- current.focus()
1275
- break
1276
- }
1277
- current = current.parent
1278
- }
1279
- }
1280
-
1281
- return event
1282
- }
1283
-
1284
- private processSingleMouseEvent(mouseEvent: RawMouseEvent): boolean {
1285
- if (this._splitHeight > 0) {
1286
- if (mouseEvent.y < this.renderOffset) {
1287
- return false
1288
- }
1289
- mouseEvent.y -= this.renderOffset
1290
- }
1291
-
1292
- this._latestPointer.x = mouseEvent.x
1293
- this._latestPointer.y = mouseEvent.y
1294
- this._hasPointer = true
1295
- this._lastPointerModifiers = mouseEvent.modifiers
1296
-
1297
- if (this._console.visible) {
1298
- const consoleBounds = this._console.bounds
1299
- if (
1300
- mouseEvent.x >= consoleBounds.x &&
1301
- mouseEvent.x < consoleBounds.x + consoleBounds.width &&
1302
- mouseEvent.y >= consoleBounds.y &&
1303
- mouseEvent.y < consoleBounds.y + consoleBounds.height
1304
- ) {
1305
- const event = new MouseEvent(null, mouseEvent)
1306
- const handled = this._console.handleMouse(event)
1307
- if (handled) return true
1308
- }
1309
- }
1310
-
1311
- if (mouseEvent.type === "scroll") {
1312
- const maybeRenderableId = this.hitTest(mouseEvent.x, mouseEvent.y)
1313
- const maybeRenderable = Renderable.renderablesByNumber.get(maybeRenderableId)
1314
- const fallbackTarget =
1315
- this._currentFocusedRenderable &&
1316
- !this._currentFocusedRenderable.isDestroyed &&
1317
- this._currentFocusedRenderable.focused
1318
- ? this._currentFocusedRenderable
1319
- : null
1320
- const scrollTarget = maybeRenderable ?? fallbackTarget
1321
-
1322
- if (scrollTarget) {
1323
- const event = new MouseEvent(scrollTarget, mouseEvent)
1324
- scrollTarget.processMouseEvent(event)
1325
- }
1326
- return true
1327
- }
1328
-
1329
- const maybeRenderableId = this.hitTest(mouseEvent.x, mouseEvent.y)
1330
- const sameElement = maybeRenderableId === this.lastOverRenderableNum
1331
- this.lastOverRenderableNum = maybeRenderableId
1332
- const maybeRenderable = Renderable.renderablesByNumber.get(maybeRenderableId)
1333
-
1334
- if (
1335
- mouseEvent.type === "down" &&
1336
- mouseEvent.button === MouseButton.LEFT &&
1337
- !this.currentSelection?.isDragging &&
1338
- !mouseEvent.modifiers.ctrl
1339
- ) {
1340
- const canStartSelection = Boolean(
1341
- maybeRenderable &&
1342
- maybeRenderable.selectable &&
1343
- !maybeRenderable.isDestroyed &&
1344
- maybeRenderable.shouldStartSelection(mouseEvent.x, mouseEvent.y),
1345
- )
1346
-
1347
- if (canStartSelection && maybeRenderable) {
1348
- this.startSelection(maybeRenderable, mouseEvent.x, mouseEvent.y)
1349
- this.dispatchMouseEvent(maybeRenderable, mouseEvent)
1350
- return true
1351
- }
1352
- }
1353
-
1354
- if (mouseEvent.type === "drag" && this.currentSelection?.isDragging) {
1355
- this.updateSelection(maybeRenderable, mouseEvent.x, mouseEvent.y)
1356
-
1357
- if (maybeRenderable) {
1358
- const event = new MouseEvent(maybeRenderable, { ...mouseEvent, isDragging: true })
1359
- maybeRenderable.processMouseEvent(event)
1360
- }
1361
-
1362
- return true
1363
- }
1364
-
1365
- if (mouseEvent.type === "up" && this.currentSelection?.isDragging) {
1366
- if (maybeRenderable) {
1367
- const event = new MouseEvent(maybeRenderable, { ...mouseEvent, isDragging: true })
1368
- maybeRenderable.processMouseEvent(event)
1369
- }
1370
-
1371
- this.finishSelection()
1372
- return true
1373
- }
1374
-
1375
- if (mouseEvent.type === "down" && mouseEvent.button === MouseButton.LEFT && this.currentSelection) {
1376
- if (mouseEvent.modifiers.ctrl) {
1377
- this.currentSelection.isDragging = true
1378
- this.updateSelection(maybeRenderable, mouseEvent.x, mouseEvent.y)
1379
- return true
1380
- }
1381
- }
1382
-
1383
- if (!sameElement && (mouseEvent.type === "drag" || mouseEvent.type === "move")) {
1384
- if (
1385
- this.lastOverRenderable &&
1386
- this.lastOverRenderable !== this.capturedRenderable &&
1387
- !this.lastOverRenderable.isDestroyed
1388
- ) {
1389
- const event = new MouseEvent(this.lastOverRenderable, { ...mouseEvent, type: "out" })
1390
- this.lastOverRenderable.processMouseEvent(event)
1391
- }
1392
- this.lastOverRenderable = maybeRenderable
1393
- if (maybeRenderable) {
1394
- const event = new MouseEvent(maybeRenderable, {
1395
- ...mouseEvent,
1396
- type: "over",
1397
- source: this.capturedRenderable,
1398
- })
1399
- maybeRenderable.processMouseEvent(event)
1400
- }
1401
- }
1402
-
1403
- if (this.capturedRenderable && mouseEvent.type !== "up") {
1404
- const event = new MouseEvent(this.capturedRenderable, mouseEvent)
1405
- this.capturedRenderable.processMouseEvent(event)
1406
- return true
1407
- }
1408
-
1409
- if (this.capturedRenderable && mouseEvent.type === "up") {
1410
- const event = new MouseEvent(this.capturedRenderable, { ...mouseEvent, type: "drag-end" })
1411
- this.capturedRenderable.processMouseEvent(event)
1412
- this.capturedRenderable.processMouseEvent(new MouseEvent(this.capturedRenderable, mouseEvent))
1413
- if (maybeRenderable) {
1414
- const event = new MouseEvent(maybeRenderable, {
1415
- ...mouseEvent,
1416
- type: "drop",
1417
- source: this.capturedRenderable,
1418
- })
1419
- maybeRenderable.processMouseEvent(event)
1420
- }
1421
- this.lastOverRenderable = this.capturedRenderable
1422
- this.lastOverRenderableNum = this.capturedRenderable.num
1423
- this.setCapturedRenderable(undefined)
1424
- // Dropping the renderable needs to push another frame when the renderer is not live
1425
- // to update the hit grid, otherwise capturedRenderable won't be in the hit grid and will not receive mouse events
1426
- this.requestRender()
1427
- }
1428
-
1429
- let event: MouseEvent | undefined
1430
- if (maybeRenderable) {
1431
- if (mouseEvent.type === "drag" && mouseEvent.button === MouseButton.LEFT) {
1432
- this.setCapturedRenderable(maybeRenderable)
1433
- } else {
1434
- this.setCapturedRenderable(undefined)
1435
- }
1436
- event = this.dispatchMouseEvent(maybeRenderable, mouseEvent)
1437
- } else {
1438
- this.setCapturedRenderable(undefined)
1439
- this.lastOverRenderable = undefined
1440
- }
1441
-
1442
- if (!event?.defaultPrevented && mouseEvent.type === "down" && this.currentSelection) {
1443
- this.clearSelection()
1444
- }
1445
-
1446
- return true
1447
- }
1448
-
1449
- /**
1450
- * Recheck hover state after hit grid changes.
1451
- * Called after render when native code detects the hit grid changed.
1452
- * Fires out/over events if the element under the cursor changed.
1453
- */
1454
- private recheckHoverState(): void {
1455
- if (this._isDestroyed || !this._hasPointer) return
1456
- if (this.capturedRenderable) return
1457
-
1458
- const hitId = this.hitTest(this._latestPointer.x, this._latestPointer.y)
1459
- const hitRenderable = Renderable.renderablesByNumber.get(hitId)
1460
- const lastOver = this.lastOverRenderable
1461
-
1462
- // No change
1463
- if (lastOver?.num === hitId) {
1464
- this.lastOverRenderableNum = hitId
1465
- return
1466
- }
1467
-
1468
- const baseEvent: RawMouseEvent = {
1469
- type: "move",
1470
- button: 0,
1471
- x: this._latestPointer.x,
1472
- y: this._latestPointer.y,
1473
- modifiers: this._lastPointerModifiers,
1474
- }
1475
-
1476
- // Fire out on old element
1477
- if (lastOver && !lastOver.isDestroyed) {
1478
- const event = new MouseEvent(lastOver, { ...baseEvent, type: "out" })
1479
- lastOver.processMouseEvent(event)
1480
- }
1481
-
1482
- this.lastOverRenderable = hitRenderable
1483
- this.lastOverRenderableNum = hitId
1484
-
1485
- // Fire over on new element
1486
- if (hitRenderable) {
1487
- const event = new MouseEvent(hitRenderable, {
1488
- ...baseEvent,
1489
- type: "over",
1490
- })
1491
- hitRenderable.processMouseEvent(event)
1492
- }
1493
- }
1494
- public setMousePointer(style: MousePointerStyle): void {
1495
- this._currentMousePointerStyle = style
1496
- this.lib.setCursorStyleOptions(this.rendererPtr, { cursor: style })
1497
- }
1498
-
1499
- public hitTest(x: number, y: number): number {
1500
- return this.lib.checkHit(this.rendererPtr, x, y)
1501
- }
1502
-
1503
- private takeMemorySnapshot(): void {
1504
- if (this._isDestroyed) return
1505
-
1506
- const memoryUsage = process.memoryUsage()
1507
- this.lastMemorySnapshot = {
1508
- heapUsed: memoryUsage.heapUsed,
1509
- heapTotal: memoryUsage.heapTotal,
1510
- arrayBuffers: memoryUsage.arrayBuffers,
1511
- }
1512
-
1513
- this.lib.updateMemoryStats(
1514
- this.rendererPtr,
1515
- this.lastMemorySnapshot.heapUsed,
1516
- this.lastMemorySnapshot.heapTotal,
1517
- this.lastMemorySnapshot.arrayBuffers,
1518
- )
1519
-
1520
- this.emit("memory:snapshot", this.lastMemorySnapshot)
1521
- }
1522
-
1523
- private startMemorySnapshotTimer(): void {
1524
- this.stopMemorySnapshotTimer()
1525
-
1526
- this.memorySnapshotTimer = this.clock.setInterval(() => {
1527
- this.takeMemorySnapshot()
1528
- }, this.memorySnapshotInterval)
1529
- }
1530
-
1531
- private stopMemorySnapshotTimer(): void {
1532
- if (this.memorySnapshotTimer) {
1533
- this.clock.clearInterval(this.memorySnapshotTimer)
1534
- this.memorySnapshotTimer = null
1535
- }
1536
- }
1537
-
1538
- public setMemorySnapshotInterval(interval: number): void {
1539
- this.memorySnapshotInterval = interval
1540
-
1541
- if (this._isRunning && interval > 0) {
1542
- this.startMemorySnapshotTimer()
1543
- } else if (interval <= 0 && this.memorySnapshotTimer) {
1544
- this.clock.clearInterval(this.memorySnapshotTimer)
1545
- this.memorySnapshotTimer = null
1546
- }
1547
- }
1548
-
1549
- private handleResize(width: number, height: number): void {
1550
- if (this._isDestroyed) return
1551
- if (this._splitHeight > 0) {
1552
- this.processResize(width, height)
1553
- return
1554
- }
1555
-
1556
- if (this.resizeTimeoutId !== null) {
1557
- this.clock.clearTimeout(this.resizeTimeoutId)
1558
- this.resizeTimeoutId = null
1559
- }
1560
-
1561
- this.resizeTimeoutId = this.clock.setTimeout(() => {
1562
- this.resizeTimeoutId = null
1563
- this.processResize(width, height)
1564
- }, this.resizeDebounceDelay)
1565
- }
1566
-
1567
- private queryPixelResolution() {
1568
- this.waitingForPixelResolution = true
1569
- this.lib.queryPixelResolution(this.rendererPtr)
1570
- }
1571
-
1572
- private processResize(width: number, height: number): void {
1573
- if (width === this._terminalWidth && height === this._terminalHeight) return
1574
-
1575
- const prevWidth = this._terminalWidth
1576
-
1577
- this._terminalWidth = width
1578
- this._terminalHeight = height
1579
- this.queryPixelResolution()
1580
-
1581
- this.setCapturedRenderable(undefined)
1582
- this.stdinParser?.resetMouseState()
1583
-
1584
- if (this._splitHeight > 0) {
1585
- // TODO: Handle resizing split mode properly
1586
- if (width < prevWidth) {
1587
- const start = this._terminalHeight - this._splitHeight * 2
1588
- const flush = ANSI.moveCursorAndClear(start, 1)
1589
- this.writeOut(flush)
1590
- }
1591
- this.renderOffset = height - this._splitHeight
1592
- this.width = width
1593
- this.height = this._splitHeight
1594
- this.currentRenderBuffer.clear(this.backgroundColor)
1595
- this.lib.setRenderOffset(this.rendererPtr, this.renderOffset)
1596
- } else {
1597
- this.width = width
1598
- this.height = height
1599
- }
1600
-
1601
- this.lib.resizeRenderer(this.rendererPtr, this.width, this.height)
1602
- this.nextRenderBuffer = this.lib.getNextBuffer(this.rendererPtr)
1603
- this.currentRenderBuffer = this.lib.getCurrentBuffer(this.rendererPtr)
1604
- this._console.resize(this.width, this.height)
1605
- this.root.resize(this.width, this.height)
1606
- this.emit("resize", this.width, this.height)
1607
- this.requestRender()
1608
- }
1609
-
1610
- public setBackgroundColor(color: ColorInput): void {
1611
- const parsedColor = parseColor(color)
1612
- this.lib.setBackgroundColor(this.rendererPtr, parsedColor as RGBA)
1613
- this.backgroundColor = parsedColor as RGBA
1614
- this.nextRenderBuffer.clear(parsedColor as RGBA)
1615
- this.requestRender()
1616
- }
1617
-
1618
- public toggleDebugOverlay(): void {
1619
- const willBeEnabled = !this.debugOverlay.enabled
1620
-
1621
- if (willBeEnabled && !this.memorySnapshotInterval) {
1622
- this.memorySnapshotInterval = 3000
1623
- this.startMemorySnapshotTimer()
1624
- this.automaticMemorySnapshot = true
1625
- } else if (!willBeEnabled && this.automaticMemorySnapshot) {
1626
- this.stopMemorySnapshotTimer()
1627
- this.memorySnapshotInterval = 0
1628
- this.automaticMemorySnapshot = false
1629
- }
1630
-
1631
- this.debugOverlay.enabled = !this.debugOverlay.enabled
1632
- this.lib.setDebugOverlay(this.rendererPtr, this.debugOverlay.enabled, this.debugOverlay.corner)
1633
- this.emit(CliRenderEvents.DEBUG_OVERLAY_TOGGLE, this.debugOverlay.enabled)
1634
- this.requestRender()
1635
- }
1636
-
1637
- public configureDebugOverlay(options: { enabled?: boolean; corner?: DebugOverlayCorner }): void {
1638
- this.debugOverlay.enabled = options.enabled ?? this.debugOverlay.enabled
1639
- this.debugOverlay.corner = options.corner ?? this.debugOverlay.corner
1640
- this.lib.setDebugOverlay(this.rendererPtr, this.debugOverlay.enabled, this.debugOverlay.corner)
1641
- this.requestRender()
1642
- }
1643
-
1644
- public setTerminalTitle(title: string): void {
1645
- this.lib.setTerminalTitle(this.rendererPtr, title)
1646
- }
1647
-
1648
- public copyToClipboardOSC52(text: string, target?: ClipboardTarget): boolean {
1649
- return this.clipboard.copyToClipboardOSC52(text, target)
1650
- }
1651
-
1652
- public clearClipboardOSC52(target?: ClipboardTarget): boolean {
1653
- return this.clipboard.clearClipboardOSC52(target)
1654
- }
1655
-
1656
- public isOsc52Supported(): boolean {
1657
- return this._capabilities?.osc52 ?? this.clipboard.isOsc52Supported()
1658
- }
1659
-
1660
- public dumpHitGrid(): void {
1661
- this.lib.dumpHitGrid(this.rendererPtr)
1662
- }
1663
-
1664
- public dumpBuffers(timestamp?: number): void {
1665
- this.lib.dumpBuffers(this.rendererPtr, timestamp)
1666
- }
1667
-
1668
- public dumpStdoutBuffer(timestamp?: number): void {
1669
- this.lib.dumpStdoutBuffer(this.rendererPtr, timestamp)
1670
- }
1671
-
1672
- public static setCursorPosition(renderer: CliRenderer, x: number, y: number, visible: boolean = true): void {
1673
- const lib = resolveRenderLib()
1674
- lib.setCursorPosition(renderer.rendererPtr, x, y, visible)
1675
- }
1676
-
1677
- public static setCursorStyle(renderer: CliRenderer, options: CursorStyleOptions): void {
1678
- const lib = resolveRenderLib()
1679
- lib.setCursorStyleOptions(renderer.rendererPtr, options)
1680
- if (options.cursor !== undefined) {
1681
- renderer._currentMousePointerStyle = options.cursor
1682
- }
1683
- }
1684
-
1685
- public static setCursorColor(renderer: CliRenderer, color: RGBA): void {
1686
- const lib = resolveRenderLib()
1687
- lib.setCursorColor(renderer.rendererPtr, color)
1688
- }
1689
-
1690
- public setCursorPosition(x: number, y: number, visible: boolean = true): void {
1691
- this.lib.setCursorPosition(this.rendererPtr, x, y, visible)
1692
- }
1693
-
1694
- public setCursorStyle(options: CursorStyleOptions): void {
1695
- this.lib.setCursorStyleOptions(this.rendererPtr, options)
1696
- if (options.cursor !== undefined) {
1697
- this._currentMousePointerStyle = options.cursor
1698
- }
1699
- }
1700
-
1701
- public setCursorColor(color: RGBA): void {
1702
- this.lib.setCursorColor(this.rendererPtr, color)
1703
- }
1704
-
1705
- public getCursorState() {
1706
- return this.lib.getCursorState(this.rendererPtr)
1707
- }
1708
-
1709
- public addPostProcessFn(processFn: (buffer: OptimizedBuffer, deltaTime: number) => void): void {
1710
- this.postProcessFns.push(processFn)
1711
- }
1712
-
1713
- public removePostProcessFn(processFn: (buffer: OptimizedBuffer, deltaTime: number) => void): void {
1714
- this.postProcessFns = this.postProcessFns.filter((fn) => fn !== processFn)
1715
- }
1716
-
1717
- public clearPostProcessFns(): void {
1718
- this.postProcessFns = []
1719
- }
1720
-
1721
- public setFrameCallback(callback: (deltaTime: number) => Promise<void>): void {
1722
- this.frameCallbacks.push(callback)
1723
- }
1724
-
1725
- public removeFrameCallback(callback: (deltaTime: number) => Promise<void>): void {
1726
- this.frameCallbacks = this.frameCallbacks.filter((cb) => cb !== callback)
1727
- }
1728
-
1729
- public clearFrameCallbacks(): void {
1730
- this.frameCallbacks = []
1731
- }
1732
-
1733
- public requestLive(): void {
1734
- this.liveRequestCounter++
1735
-
1736
- if (this._controlState === RendererControlState.IDLE && this.liveRequestCounter > 0) {
1737
- this._controlState = RendererControlState.AUTO_STARTED
1738
- this.internalStart()
1739
- }
1740
- }
1741
-
1742
- public dropLive(): void {
1743
- this.liveRequestCounter = Math.max(0, this.liveRequestCounter - 1)
1744
-
1745
- if (this._controlState === RendererControlState.AUTO_STARTED && this.liveRequestCounter === 0) {
1746
- this._controlState = RendererControlState.IDLE
1747
- this.internalPause()
1748
- }
1749
- }
1750
-
1751
- public start(): void {
1752
- this._controlState = RendererControlState.EXPLICIT_STARTED
1753
- this.internalStart()
1754
- }
1755
-
1756
- public auto(): void {
1757
- this._controlState = this._isRunning ? RendererControlState.AUTO_STARTED : RendererControlState.IDLE
1758
- }
1759
-
1760
- private internalStart(): void {
1761
- if (!this._isRunning && !this._isDestroyed) {
1762
- this._isRunning = true
1763
-
1764
- if (this.memorySnapshotInterval > 0) {
1765
- this.startMemorySnapshotTimer()
1766
- }
1767
-
1768
- this.startRenderLoop()
1769
- }
1770
- }
1771
-
1772
- public pause(): void {
1773
- this._controlState = RendererControlState.EXPLICIT_PAUSED
1774
- this.internalPause()
1775
- }
1776
-
1777
- public suspend(): void {
1778
- this._previousControlState = this._controlState
1779
-
1780
- this._controlState = RendererControlState.EXPLICIT_SUSPENDED
1781
- this.internalPause()
1782
-
1783
- this._suspendedMouseEnabled = this._useMouse
1784
-
1785
- this.disableMouse()
1786
- this.removeExitListeners()
1787
- this.stdinParser?.reset()
1788
- this.stdin.removeListener("data", this.stdinListener)
1789
- this.lib.suspendRenderer(this.rendererPtr)
1790
-
1791
- if (this.stdin.setRawMode) {
1792
- this.stdin.setRawMode(false)
1793
- }
1794
-
1795
- this.stdin.pause()
1796
- }
1797
-
1798
- public resume(): void {
1799
- if (this.stdin.setRawMode) {
1800
- this.stdin.setRawMode(true)
1801
- }
1802
-
1803
- this.stdin.resume()
1804
- this.addExitListeners()
1805
-
1806
- setImmediate(() => {
1807
- // Consume any existing stdin data to avoid processing stale input
1808
- while (this.stdin.read() !== null) {}
1809
- this.stdin.on("data", this.stdinListener)
1810
- })
1811
-
1812
- this.lib.resumeRenderer(this.rendererPtr)
1813
-
1814
- if (this._suspendedMouseEnabled) {
1815
- this.enableMouse()
1816
- }
1817
-
1818
- this.currentRenderBuffer.clear(this.backgroundColor)
1819
- this._controlState = this._previousControlState
1820
-
1821
- if (
1822
- this._previousControlState === RendererControlState.AUTO_STARTED ||
1823
- this._previousControlState === RendererControlState.EXPLICIT_STARTED
1824
- ) {
1825
- this.internalStart()
1826
- } else {
1827
- this.requestRender()
1828
- }
1829
- }
1830
-
1831
- private internalPause(): void {
1832
- this._isRunning = false
1833
-
1834
- if (this.renderTimeout) {
1835
- this.clock.clearTimeout(this.renderTimeout)
1836
- this.renderTimeout = null
1837
- }
1838
-
1839
- if (!this.rendering) {
1840
- this.resolveIdleIfNeeded()
1841
- }
1842
- }
1843
-
1844
- public stop(): void {
1845
- this._controlState = RendererControlState.EXPLICIT_STOPPED
1846
- this.internalStop()
1847
- }
1848
-
1849
- private internalStop(): void {
1850
- if (this.isRunning && !this._isDestroyed) {
1851
- this._isRunning = false
1852
-
1853
- if (this.memorySnapshotTimer) {
1854
- this.clock.clearInterval(this.memorySnapshotTimer)
1855
- this.memorySnapshotTimer = null
1856
- }
1857
-
1858
- if (this.renderTimeout) {
1859
- this.clock.clearTimeout(this.renderTimeout)
1860
- this.renderTimeout = null
1861
- }
1862
-
1863
- // If we're currently rendering, the frame will resolve idle when it completes
1864
- // Otherwise, resolve immediately
1865
- if (!this.rendering) {
1866
- this.resolveIdleIfNeeded()
1867
- }
1868
- }
1869
- }
1870
-
1871
- public destroy(): void {
1872
- if (this._isDestroyed) return
1873
- this._isDestroyed = true
1874
- this._destroyPending = true
1875
-
1876
- if (this.rendering) {
1877
- // Defer teardown until the active frame completes to avoid freeing native resources mid-render.
1878
- return
1879
- }
1880
-
1881
- this.finalizeDestroy()
1882
- }
1883
-
1884
- private finalizeDestroy(): void {
1885
- if (this._destroyFinalized) return
1886
- this._destroyFinalized = true
1887
- this._destroyPending = false
1888
-
1889
- process.removeListener("SIGWINCH", this.sigwinchHandler)
1890
- process.removeListener("uncaughtException", this.handleError)
1891
- process.removeListener("unhandledRejection", this.handleError)
1892
- process.removeListener("warning", this.warningHandler)
1893
- process.removeListener("beforeExit", this.exitHandler)
1894
- capture.removeListener("write", this.captureCallback)
1895
- this.removeExitListeners()
1896
-
1897
- if (this.resizeTimeoutId !== null) {
1898
- this.clock.clearTimeout(this.resizeTimeoutId)
1899
- this.resizeTimeoutId = null
1900
- }
1901
-
1902
- if (this.capabilityTimeoutId !== null) {
1903
- this.clock.clearTimeout(this.capabilityTimeoutId)
1904
- this.capabilityTimeoutId = null
1905
- }
1906
-
1907
- if (this.memorySnapshotTimer) {
1908
- this.clock.clearInterval(this.memorySnapshotTimer)
1909
- }
1910
-
1911
- // Clean up palette detector
1912
- if (this._paletteDetector) {
1913
- this._paletteDetector.cleanup()
1914
- this._paletteDetector = null
1915
- }
1916
- this._paletteDetectionPromise = null
1917
- this._cachedPalette = null
1918
-
1919
- this.emit(CliRenderEvents.DESTROY)
1920
-
1921
- if (this.renderTimeout) {
1922
- this.clock.clearTimeout(this.renderTimeout)
1923
- this.renderTimeout = null
1924
- }
1925
- this._isRunning = false
1926
-
1927
- this.waitingForPixelResolution = false
1928
- this.setCapturedRenderable(undefined)
1929
-
1930
- try {
1931
- this.root.destroyRecursively()
1932
- } catch (e) {
1933
- console.error("Error destroying root renderable:", e instanceof Error ? e.stack : String(e))
1934
- }
1935
-
1936
- // Remove listener before destroying parser
1937
- this.stdin.removeListener("data", this.stdinListener)
1938
- if (this.stdin.setRawMode) {
1939
- this.stdin.setRawMode(false)
1940
- }
1941
-
1942
- this.stdinParser?.destroy()
1943
- this.stdinParser = null
1944
- this.oscSubscribers.clear()
1945
- this._console.destroy()
1946
- this.disableStdoutInterception()
1947
-
1948
- if (this._splitHeight > 0) {
1949
- this.flushStdoutCache(this._splitHeight, true)
1950
- }
1951
-
1952
- this.lib.destroyRenderer(this.rendererPtr)
1953
- rendererTracker.removeRenderer(this)
1954
-
1955
- if (this._onDestroy) {
1956
- try {
1957
- this._onDestroy()
1958
- } catch (e) {
1959
- console.error("Error in onDestroy callback:", e instanceof Error ? e.stack : String(e))
1960
- }
1961
- }
1962
-
1963
- // Resolve any pending idle() calls
1964
- this.resolveIdleIfNeeded()
1965
- }
1966
-
1967
- private startRenderLoop(): void {
1968
- if (!this._isRunning) return
1969
-
1970
- this.lastTime = this.clock.now()
1971
- this.frameCount = 0
1972
- this.lastFpsTime = this.lastTime
1973
- this.currentFps = 0
1974
-
1975
- this.loop()
1976
- }
1977
-
1978
- private async loop(): Promise<void> {
1979
- if (this.rendering || this._isDestroyed) return
1980
- this.renderTimeout = null
1981
-
1982
- this.rendering = true
1983
- if (this.renderTimeout) {
1984
- this.clock.clearTimeout(this.renderTimeout)
1985
- this.renderTimeout = null
1986
- }
1987
- try {
1988
- const now = this.clock.now()
1989
- const elapsed = now - this.lastTime
1990
-
1991
- const deltaTime = elapsed
1992
- this.lastTime = now
1993
-
1994
- this.frameCount++
1995
- if (now - this.lastFpsTime >= 1000) {
1996
- this.currentFps = this.frameCount
1997
- this.frameCount = 0
1998
- this.lastFpsTime = now
1999
- }
2000
-
2001
- this.renderStats.frameCount++
2002
- this.renderStats.fps = this.currentFps
2003
- const overallStart = performance.now()
2004
-
2005
- const frameRequests = Array.from(this.animationRequest.values())
2006
- this.animationRequest.clear()
2007
- const animationRequestStart = performance.now()
2008
- for (const callback of frameRequests) {
2009
- callback(deltaTime)
2010
- this.dropLive()
2011
- }
2012
- const animationRequestEnd = performance.now()
2013
- const animationRequestTime = animationRequestEnd - animationRequestStart
2014
-
2015
- const start = performance.now()
2016
- for (const frameCallback of this.frameCallbacks) {
2017
- try {
2018
- await frameCallback(deltaTime)
2019
- } catch (error) {
2020
- console.error("Error in frame callback:", error)
2021
- }
2022
- }
2023
- const end = performance.now()
2024
- this.renderStats.frameCallbackTime = end - start
2025
-
2026
- this.root.render(this.nextRenderBuffer, deltaTime)
2027
-
2028
- for (const postProcessFn of this.postProcessFns) {
2029
- postProcessFn(this.nextRenderBuffer, deltaTime)
2030
- }
2031
-
2032
- this._console.renderToBuffer(this.nextRenderBuffer)
2033
-
2034
- // If destroy() was requested during this frame, skip native work and scheduling.
2035
- if (!this._isDestroyed) {
2036
- this.renderNative()
2037
-
2038
- // Check if hit grid changed and recheck hover state if needed
2039
- if (this._useMouse && this.lib.getHitGridDirty(this.rendererPtr)) {
2040
- this.recheckHoverState()
2041
- }
2042
-
2043
- const overallFrameTime = performance.now() - overallStart
2044
-
2045
- // TODO: Add animationRequestTime to stats
2046
- this.lib.updateStats(
2047
- this.rendererPtr,
2048
- overallFrameTime,
2049
- this.renderStats.fps,
2050
- this.renderStats.frameCallbackTime,
2051
- )
2052
-
2053
- if (this.gatherStats) {
2054
- this.collectStatSample(overallFrameTime)
2055
- }
2056
-
2057
- if (this._isRunning || this.immediateRerenderRequested) {
2058
- const targetFrameTime = this.immediateRerenderRequested ? this.minTargetFrameTime : this.targetFrameTime
2059
- const delay = Math.max(1, targetFrameTime - Math.floor(overallFrameTime))
2060
- this.immediateRerenderRequested = false
2061
- this.renderTimeout = this.clock.setTimeout(() => {
2062
- this.renderTimeout = null
2063
- this.loop()
2064
- }, delay)
2065
- } else {
2066
- this.clock.clearTimeout(this.renderTimeout!)
2067
- this.renderTimeout = null
2068
- }
2069
- }
2070
- } finally {
2071
- this.rendering = false
2072
- if (this._destroyPending) {
2073
- this.finalizeDestroy()
2074
- }
2075
- this.resolveIdleIfNeeded()
2076
- }
2077
- }
2078
-
2079
- public intermediateRender(): void {
2080
- this.immediateRerenderRequested = true
2081
- this.loop()
2082
- }
2083
-
2084
- private renderNative(): void {
2085
- if (this.renderingNative) {
2086
- console.error("Rendering called concurrently")
2087
- throw new Error("Rendering called concurrently")
2088
- }
2089
-
2090
- let force = false
2091
- if (this._splitHeight > 0) {
2092
- // TODO: Flickering could maybe be even more reduced by moving the flush to the native layer,
2093
- // to output the flush with the buffered writer, after the render is done.
2094
- force = this.flushStdoutCache(this._splitHeight)
2095
- }
2096
-
2097
- this.renderingNative = true
2098
- this.lib.render(this.rendererPtr, force)
2099
- // this.dumpStdoutBuffer(Date.now())
2100
- this.renderingNative = false
2101
- }
2102
-
2103
- private collectStatSample(frameTime: number): void {
2104
- this.frameTimes.push(frameTime)
2105
- if (this.frameTimes.length > this.maxStatSamples) {
2106
- this.frameTimes.shift()
2107
- }
2108
- }
2109
-
2110
- public getStats(): {
2111
- fps: number
2112
- frameCount: number
2113
- frameTimes: number[]
2114
- averageFrameTime: number
2115
- minFrameTime: number
2116
- maxFrameTime: number
2117
- } {
2118
- const frameTimes = [...this.frameTimes]
2119
- const sum = frameTimes.reduce((acc, time) => acc + time, 0)
2120
- const avg = frameTimes.length ? sum / frameTimes.length : 0
2121
- const min = frameTimes.length ? Math.min(...frameTimes) : 0
2122
- const max = frameTimes.length ? Math.max(...frameTimes) : 0
2123
-
2124
- return {
2125
- fps: this.renderStats.fps,
2126
- frameCount: this.renderStats.frameCount,
2127
- frameTimes,
2128
- averageFrameTime: avg,
2129
- minFrameTime: min,
2130
- maxFrameTime: max,
2131
- }
2132
- }
2133
-
2134
- public resetStats(): void {
2135
- this.frameTimes = []
2136
- this.renderStats.frameCount = 0
2137
- }
2138
-
2139
- public setGatherStats(enabled: boolean): void {
2140
- this.gatherStats = enabled
2141
- if (!enabled) {
2142
- this.frameTimes = []
2143
- }
2144
- }
2145
-
2146
- public getSelection(): Selection | null {
2147
- return this.currentSelection
2148
- }
2149
-
2150
- public get hasSelection(): boolean {
2151
- return !!this.currentSelection
2152
- }
2153
-
2154
- public getSelectionContainer(): Renderable | null {
2155
- return this.selectionContainers.length > 0 ? this.selectionContainers[this.selectionContainers.length - 1] : null
2156
- }
2157
-
2158
- public clearSelection(): void {
2159
- if (this.currentSelection) {
2160
- for (const renderable of this.currentSelection.touchedRenderables) {
2161
- if (renderable.selectable && !renderable.isDestroyed) {
2162
- renderable.onSelectionChanged(null)
2163
- }
2164
- }
2165
- this.currentSelection = null
2166
- }
2167
- this.selectionContainers = []
2168
- }
2169
-
2170
- /**
2171
- * Start a new selection at the given coordinates.
2172
- * Used by both mouse and keyboard selection.
2173
- */
2174
- public startSelection(renderable: Renderable, x: number, y: number): void {
2175
- if (!renderable.selectable) return
2176
-
2177
- this.clearSelection()
2178
- this.selectionContainers.push(renderable.parent || this.root)
2179
- this.currentSelection = new Selection(renderable, { x, y }, { x, y })
2180
- this.currentSelection.isStart = true
2181
-
2182
- this.notifySelectablesOfSelectionChange()
2183
- }
2184
-
2185
- public updateSelection(
2186
- currentRenderable: Renderable | undefined,
2187
- x: number,
2188
- y: number,
2189
- options?: { finishDragging?: boolean },
2190
- ): void {
2191
- if (this.currentSelection) {
2192
- this.currentSelection.isStart = false
2193
- this.currentSelection.focus = { x, y }
2194
-
2195
- if (options?.finishDragging) {
2196
- this.currentSelection.isDragging = false
2197
- }
2198
-
2199
- if (this.selectionContainers.length > 0) {
2200
- const currentContainer = this.selectionContainers[this.selectionContainers.length - 1]
2201
-
2202
- if (!currentRenderable || !this.isWithinContainer(currentRenderable, currentContainer)) {
2203
- const parentContainer = currentContainer.parent || this.root
2204
- this.selectionContainers.push(parentContainer)
2205
- } else if (currentRenderable && this.selectionContainers.length > 1) {
2206
- let containerIndex = this.selectionContainers.indexOf(currentRenderable)
2207
-
2208
- if (containerIndex === -1) {
2209
- const immediateParent = currentRenderable.parent || this.root
2210
- containerIndex = this.selectionContainers.indexOf(immediateParent)
2211
- }
2212
-
2213
- if (containerIndex !== -1 && containerIndex < this.selectionContainers.length - 1) {
2214
- this.selectionContainers = this.selectionContainers.slice(0, containerIndex + 1)
2215
- }
2216
- }
2217
- }
2218
-
2219
- this.notifySelectablesOfSelectionChange()
2220
- }
2221
- }
2222
-
2223
- public requestSelectionUpdate(): void {
2224
- if (this.currentSelection?.isDragging) {
2225
- const pointer = this._latestPointer
2226
-
2227
- const maybeRenderableId = this.hitTest(pointer.x, pointer.y)
2228
- const maybeRenderable = Renderable.renderablesByNumber.get(maybeRenderableId)
2229
-
2230
- this.updateSelection(maybeRenderable, pointer.x, pointer.y)
2231
- }
2232
- }
2233
-
2234
- private isWithinContainer(renderable: Renderable, container: Renderable): boolean {
2235
- let current: Renderable | null = renderable
2236
- while (current) {
2237
- if (current === container) return true
2238
- current = current.parent
2239
- }
2240
- return false
2241
- }
2242
-
2243
- private finishSelection(): void {
2244
- if (this.currentSelection) {
2245
- this.currentSelection.isDragging = false
2246
- this.emit("selection", this.currentSelection)
2247
- this.notifySelectablesOfSelectionChange()
2248
- }
2249
- }
2250
-
2251
- private notifySelectablesOfSelectionChange(): void {
2252
- const selectedRenderables: Renderable[] = []
2253
- const touchedRenderables: Renderable[] = []
2254
- const currentContainer =
2255
- this.selectionContainers.length > 0 ? this.selectionContainers[this.selectionContainers.length - 1] : this.root
2256
-
2257
- if (this.currentSelection) {
2258
- this.walkSelectableRenderables(
2259
- currentContainer,
2260
- this.currentSelection.bounds,
2261
- selectedRenderables,
2262
- touchedRenderables,
2263
- )
2264
-
2265
- for (const renderable of this.currentSelection.touchedRenderables) {
2266
- if (!touchedRenderables.includes(renderable) && !renderable.isDestroyed) {
2267
- renderable.onSelectionChanged(null)
2268
- }
2269
- }
2270
-
2271
- this.currentSelection.updateSelectedRenderables(selectedRenderables)
2272
- this.currentSelection.updateTouchedRenderables(touchedRenderables)
2273
- }
2274
- }
2275
-
2276
- private walkSelectableRenderables(
2277
- container: Renderable,
2278
- selectionBounds: ViewportBounds,
2279
- selectedRenderables: Renderable[],
2280
- touchedRenderables: Renderable[],
2281
- ): void {
2282
- const children = getObjectsInViewport<Renderable>(
2283
- selectionBounds,
2284
- container.getChildrenSortedByPrimaryAxis(),
2285
- container.primaryAxis,
2286
- 0, // padding
2287
- 0, // minTriggerSize - always perform overlap checks for selection
2288
- )
2289
-
2290
- for (const child of children) {
2291
- if (child.selectable) {
2292
- const hasSelection = child.onSelectionChanged(this.currentSelection)
2293
- if (hasSelection) {
2294
- selectedRenderables.push(child)
2295
- }
2296
- touchedRenderables.push(child)
2297
- }
2298
- if (child.getChildrenCount() > 0) {
2299
- this.walkSelectableRenderables(child, selectionBounds, selectedRenderables, touchedRenderables)
2300
- }
2301
- }
2302
- }
2303
-
2304
- public get paletteDetectionStatus(): "idle" | "detecting" | "cached" {
2305
- if (this._cachedPalette) return "cached"
2306
- if (this._paletteDetectionPromise) return "detecting"
2307
- return "idle"
2308
- }
2309
-
2310
- public clearPaletteCache(): void {
2311
- this._cachedPalette = null
2312
- }
2313
-
2314
- /**
2315
- * Detects the terminal's color palette
2316
- *
2317
- * @returns Promise resolving to TerminalColors object containing palette and special colors
2318
- * @throws Error if renderer is suspended
2319
- */
2320
- public async getPalette(options?: GetPaletteOptions): Promise<TerminalColors> {
2321
- if (this._controlState === RendererControlState.EXPLICIT_SUSPENDED) {
2322
- throw new Error("Cannot detect palette while renderer is suspended")
2323
- }
2324
-
2325
- const requestedSize = options?.size ?? 16
2326
-
2327
- if (this._cachedPalette && this._cachedPalette.palette.length !== requestedSize) {
2328
- this._cachedPalette = null
2329
- }
2330
-
2331
- if (this._cachedPalette) {
2332
- return this._cachedPalette
2333
- }
2334
-
2335
- if (this._paletteDetectionPromise) {
2336
- return this._paletteDetectionPromise
2337
- }
2338
-
2339
- if (!this._paletteDetector) {
2340
- const isLegacyTmux =
2341
- this.capabilities?.terminal?.name?.toLowerCase()?.includes("tmux") &&
2342
- this.capabilities?.terminal?.version?.localeCompare("3.6") < 0
2343
- this._paletteDetector = createTerminalPalette(
2344
- this.stdin,
2345
- this.stdout,
2346
- this.writeOut.bind(this),
2347
- isLegacyTmux,
2348
- {
2349
- subscribeOsc: this.subscribeOsc.bind(this),
2350
- },
2351
- this.clock,
2352
- )
2353
- }
2354
-
2355
- this._paletteDetectionPromise = this._paletteDetector.detect(options).then((result) => {
2356
- this._cachedPalette = result
2357
- this._paletteDetectionPromise = null
2358
- return result
2359
- })
2360
-
2361
- return this._paletteDetectionPromise
2362
- }
2363
- }