@fairyhunter13/opentui-core 0.1.88 → 0.1.90

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/dev/keypress-debug-renderer.ts +148 -0
  2. package/dev/keypress-debug.ts +43 -0
  3. package/dev/print-env-vars.ts +32 -0
  4. package/dev/test-tmux-graphics-334.sh +68 -0
  5. package/dev/thai-debug-test.ts +68 -0
  6. package/docs/development.md +141 -0
  7. package/docs/env-vars.md +140 -0
  8. package/docs/getting-started.md +353 -0
  9. package/docs/renderables-vs-constructs.md +159 -0
  10. package/docs/tree-sitter.md +311 -0
  11. package/package.json +61 -52
  12. package/scripts/build.ts +400 -0
  13. package/scripts/publish.ts +60 -0
  14. package/src/3d/SpriteResourceManager.ts +286 -0
  15. package/src/3d/SpriteUtils.ts +71 -0
  16. package/src/3d/TextureUtils.ts +196 -0
  17. package/src/3d/ThreeRenderable.ts +197 -0
  18. package/src/3d/WGPURenderer.ts +294 -0
  19. package/src/3d/animation/ExplodingSpriteEffect.ts +513 -0
  20. package/src/3d/animation/PhysicsExplodingSpriteEffect.ts +429 -0
  21. package/src/3d/animation/SpriteAnimator.ts +633 -0
  22. package/src/3d/animation/SpriteParticleGenerator.ts +435 -0
  23. package/src/3d/canvas.ts +464 -0
  24. package/src/3d/index.ts +12 -0
  25. package/src/3d/physics/PlanckPhysicsAdapter.ts +72 -0
  26. package/src/3d/physics/RapierPhysicsAdapter.ts +66 -0
  27. package/src/3d/physics/physics-interface.ts +31 -0
  28. package/src/3d/shaders/supersampling.wgsl +201 -0
  29. package/src/3d.ts +3 -0
  30. package/src/NativeSpanFeed.ts +300 -0
  31. package/src/Renderable.ts +1698 -0
  32. package/src/__snapshots__/buffer.test.ts.snap +28 -0
  33. package/src/animation/Timeline.test.ts +2709 -0
  34. package/src/animation/Timeline.ts +598 -0
  35. package/src/ansi.ts +18 -0
  36. package/src/benchmark/latest-all-bench-run.json +707 -0
  37. package/src/benchmark/latest-async-bench-run.json +336 -0
  38. package/src/benchmark/latest-default-bench-run.json +657 -0
  39. package/src/benchmark/latest-large-bench-run.json +707 -0
  40. package/src/benchmark/latest-quick-bench-run.json +207 -0
  41. package/src/benchmark/markdown-benchmark.ts +1804 -0
  42. package/src/benchmark/native-span-feed-async-benchmark.ts +355 -0
  43. package/src/benchmark/native-span-feed-benchmark.md +56 -0
  44. package/src/benchmark/native-span-feed-benchmark.ts +596 -0
  45. package/src/benchmark/native-span-feed-compare.ts +280 -0
  46. package/src/benchmark/renderer-benchmark.ts +754 -0
  47. package/src/benchmark/text-table-benchmark.ts +947 -0
  48. package/src/buffer.test.ts +291 -0
  49. package/src/buffer.ts +519 -0
  50. package/src/console.test.ts +612 -0
  51. package/src/console.ts +1255 -0
  52. package/src/edit-buffer.test.ts +1769 -0
  53. package/src/edit-buffer.ts +411 -0
  54. package/src/editor-view.test.ts +1032 -0
  55. package/src/editor-view.ts +284 -0
  56. package/src/examples/ascii-font-selection-demo.ts +245 -0
  57. package/src/examples/assets/Water_2_M_Normal.jpg +0 -0
  58. package/src/examples/assets/concrete.png +0 -0
  59. package/src/examples/assets/crate.png +0 -0
  60. package/src/examples/assets/crate_emissive.png +0 -0
  61. package/src/examples/assets/forrest_background.png +0 -0
  62. package/src/examples/assets/hast-example.json +1018 -0
  63. package/src/examples/assets/heart.png +0 -0
  64. package/src/examples/assets/main_char_heavy_attack.png +0 -0
  65. package/src/examples/assets/main_char_idle.png +0 -0
  66. package/src/examples/assets/main_char_jump_end.png +0 -0
  67. package/src/examples/assets/main_char_jump_landing.png +0 -0
  68. package/src/examples/assets/main_char_jump_start.png +0 -0
  69. package/src/examples/assets/main_char_run_loop.png +0 -0
  70. package/src/examples/assets/roughness_map.jpg +0 -0
  71. package/src/examples/build.ts +115 -0
  72. package/src/examples/code-demo.ts +584 -0
  73. package/src/examples/console-demo.ts +358 -0
  74. package/src/examples/core-plugin-slots-demo.ts +759 -0
  75. package/src/examples/diff-demo.ts +699 -0
  76. package/src/examples/draggable-three-demo.ts +259 -0
  77. package/src/examples/editor-demo.ts +322 -0
  78. package/src/examples/extmarks-demo.ts +204 -0
  79. package/src/examples/focus-restore-demo.ts +310 -0
  80. package/src/examples/fonts.ts +245 -0
  81. package/src/examples/fractal-shader-demo.ts +268 -0
  82. package/src/examples/framebuffer-demo.ts +674 -0
  83. package/src/examples/full-unicode-demo.ts +181 -0
  84. package/src/examples/golden-star-demo.ts +933 -0
  85. package/src/examples/grayscale-buffer-demo.ts +249 -0
  86. package/src/examples/hast-syntax-highlighting-demo.ts +129 -0
  87. package/src/examples/index.ts +925 -0
  88. package/src/examples/input-demo.ts +377 -0
  89. package/src/examples/input-select-layout-demo.ts +425 -0
  90. package/src/examples/install.sh +143 -0
  91. package/src/examples/keypress-debug-demo.ts +452 -0
  92. package/src/examples/lib/HexList.ts +122 -0
  93. package/src/examples/lib/PaletteGrid.ts +125 -0
  94. package/src/examples/lib/standalone-keys.ts +25 -0
  95. package/src/examples/lib/tab-controller.ts +243 -0
  96. package/src/examples/lights-phong-demo.ts +290 -0
  97. package/src/examples/link-demo.ts +220 -0
  98. package/src/examples/live-state-demo.ts +480 -0
  99. package/src/examples/markdown-demo.ts +620 -0
  100. package/src/examples/mouse-interaction-demo.ts +428 -0
  101. package/src/examples/nested-zindex-demo.ts +357 -0
  102. package/src/examples/opacity-example.ts +235 -0
  103. package/src/examples/opentui-demo.ts +1057 -0
  104. package/src/examples/physx-planck-2d-demo.ts +507 -0
  105. package/src/examples/physx-rapier-2d-demo.ts +526 -0
  106. package/src/examples/relative-positioning-demo.ts +323 -0
  107. package/src/examples/scroll-example.ts +214 -0
  108. package/src/examples/scrollbox-mouse-test.ts +112 -0
  109. package/src/examples/scrollbox-overlay-hit-test.ts +206 -0
  110. package/src/examples/select-demo.ts +237 -0
  111. package/src/examples/shader-cube-demo.ts +772 -0
  112. package/src/examples/simple-layout-example.ts +591 -0
  113. package/src/examples/slider-demo.ts +617 -0
  114. package/src/examples/split-mode-demo.ts +445 -0
  115. package/src/examples/sprite-animation-demo.ts +443 -0
  116. package/src/examples/sprite-particle-generator-demo.ts +486 -0
  117. package/src/examples/static-sprite-demo.ts +193 -0
  118. package/src/examples/sticky-scroll-example.ts +308 -0
  119. package/src/examples/styled-text-demo.ts +282 -0
  120. package/src/examples/tab-select-demo.ts +219 -0
  121. package/src/examples/terminal-title.ts +29 -0
  122. package/src/examples/terminal.ts +305 -0
  123. package/src/examples/text-node-demo.ts +416 -0
  124. package/src/examples/text-selection-demo.ts +377 -0
  125. package/src/examples/text-table-demo.ts +503 -0
  126. package/src/examples/text-truncation-demo.ts +481 -0
  127. package/src/examples/text-wrap.ts +757 -0
  128. package/src/examples/texture-loading-demo.ts +259 -0
  129. package/src/examples/timeline-example.ts +670 -0
  130. package/src/examples/transparency-demo.ts +241 -0
  131. package/src/examples/vnode-composition-demo.ts +404 -0
  132. package/src/index.ts +22 -0
  133. package/src/lib/KeyHandler.integration.test.ts +292 -0
  134. package/src/lib/KeyHandler.stopPropagation.test.ts +289 -0
  135. package/src/lib/KeyHandler.test.ts +662 -0
  136. package/src/lib/KeyHandler.ts +222 -0
  137. package/src/lib/RGBA.test.ts +984 -0
  138. package/src/lib/RGBA.ts +204 -0
  139. package/src/lib/ascii.font.ts +330 -0
  140. package/src/lib/border.test.ts +83 -0
  141. package/src/lib/border.ts +168 -0
  142. package/src/lib/bunfs.test.ts +27 -0
  143. package/src/lib/bunfs.ts +18 -0
  144. package/src/lib/clipboard.test.ts +41 -0
  145. package/src/lib/clipboard.ts +47 -0
  146. package/src/lib/clock.ts +31 -0
  147. package/src/lib/data-paths.test.ts +133 -0
  148. package/src/lib/data-paths.ts +109 -0
  149. package/src/lib/debounce.ts +106 -0
  150. package/src/lib/detect-links.test.ts +98 -0
  151. package/src/lib/detect-links.ts +56 -0
  152. package/src/lib/env.test.ts +228 -0
  153. package/src/lib/env.ts +209 -0
  154. package/src/lib/extmarks-history.ts +51 -0
  155. package/src/lib/extmarks-multiwidth.test.ts +322 -0
  156. package/src/lib/extmarks.test.ts +3457 -0
  157. package/src/lib/extmarks.ts +843 -0
  158. package/src/lib/fonts/block.json +405 -0
  159. package/src/lib/fonts/grid.json +265 -0
  160. package/src/lib/fonts/huge.json +741 -0
  161. package/src/lib/fonts/pallet.json +314 -0
  162. package/src/lib/fonts/shade.json +591 -0
  163. package/src/lib/fonts/slick.json +321 -0
  164. package/src/lib/fonts/tiny.json +69 -0
  165. package/src/lib/hast-styled-text.ts +59 -0
  166. package/src/lib/index.ts +21 -0
  167. package/src/lib/keymapping.test.ts +280 -0
  168. package/src/lib/keymapping.ts +87 -0
  169. package/src/lib/objects-in-viewport.test.ts +787 -0
  170. package/src/lib/objects-in-viewport.ts +153 -0
  171. package/src/lib/output.capture.ts +58 -0
  172. package/src/lib/parse.keypress-kitty.protocol.test.ts +340 -0
  173. package/src/lib/parse.keypress-kitty.test.ts +663 -0
  174. package/src/lib/parse.keypress-kitty.ts +439 -0
  175. package/src/lib/parse.keypress.test.ts +1849 -0
  176. package/src/lib/parse.keypress.ts +397 -0
  177. package/src/lib/parse.mouse.test.ts +552 -0
  178. package/src/lib/parse.mouse.ts +232 -0
  179. package/src/lib/paste.ts +16 -0
  180. package/src/lib/queue.ts +65 -0
  181. package/src/lib/renderable.validations.test.ts +87 -0
  182. package/src/lib/renderable.validations.ts +83 -0
  183. package/src/lib/scroll-acceleration.ts +98 -0
  184. package/src/lib/selection.ts +240 -0
  185. package/src/lib/singleton.ts +28 -0
  186. package/src/lib/stdin-parser.test.ts +1676 -0
  187. package/src/lib/stdin-parser.ts +1248 -0
  188. package/src/lib/styled-text.ts +178 -0
  189. package/src/lib/terminal-capability-detection.test.ts +202 -0
  190. package/src/lib/terminal-capability-detection.ts +79 -0
  191. package/src/lib/terminal-palette.test.ts +878 -0
  192. package/src/lib/terminal-palette.ts +383 -0
  193. package/src/lib/tree-sitter/assets/README.md +118 -0
  194. package/src/lib/tree-sitter/assets/update.ts +331 -0
  195. package/src/lib/tree-sitter/assets.d.ts +9 -0
  196. package/src/lib/tree-sitter/cache.test.ts +270 -0
  197. package/src/lib/tree-sitter/client.test.ts +1061 -0
  198. package/src/lib/tree-sitter/client.ts +615 -0
  199. package/src/lib/tree-sitter/default-parsers.ts +80 -0
  200. package/src/lib/tree-sitter/download-utils.ts +148 -0
  201. package/src/lib/tree-sitter/index.ts +28 -0
  202. package/src/lib/tree-sitter/parser.worker.ts +1001 -0
  203. package/src/lib/tree-sitter/parsers-config.ts +75 -0
  204. package/src/lib/tree-sitter/resolve-ft.ts +62 -0
  205. package/src/lib/tree-sitter/types.ts +81 -0
  206. package/src/lib/tree-sitter-styled-text.test.ts +1253 -0
  207. package/src/lib/tree-sitter-styled-text.ts +306 -0
  208. package/src/lib/validate-dir-name.ts +55 -0
  209. package/src/lib/yoga.options.test.ts +628 -0
  210. package/src/lib/yoga.options.ts +346 -0
  211. package/src/plugins/core-slot.ts +579 -0
  212. package/src/plugins/registry.ts +377 -0
  213. package/src/plugins/types.ts +46 -0
  214. package/src/post/filters.ts +888 -0
  215. package/src/renderables/ASCIIFont.ts +219 -0
  216. package/src/renderables/Box.test.ts +160 -0
  217. package/src/renderables/Box.ts +295 -0
  218. package/src/renderables/Code.test.ts +2062 -0
  219. package/src/renderables/Code.ts +357 -0
  220. package/src/renderables/Diff.regression.test.ts +226 -0
  221. package/src/renderables/Diff.test.ts +3027 -0
  222. package/src/renderables/Diff.ts +1209 -0
  223. package/src/renderables/EditBufferRenderable.ts +764 -0
  224. package/src/renderables/FrameBuffer.ts +47 -0
  225. package/src/renderables/Input.test.ts +1228 -0
  226. package/src/renderables/Input.ts +245 -0
  227. package/src/renderables/LineNumberRenderable.ts +675 -0
  228. package/src/renderables/Markdown.ts +1106 -0
  229. package/src/renderables/ScrollBar.ts +422 -0
  230. package/src/renderables/ScrollBox.ts +883 -0
  231. package/src/renderables/Select.test.ts +1010 -0
  232. package/src/renderables/Select.ts +523 -0
  233. package/src/renderables/Slider.test.ts +456 -0
  234. package/src/renderables/Slider.ts +347 -0
  235. package/src/renderables/TabSelect.test.ts +197 -0
  236. package/src/renderables/TabSelect.ts +455 -0
  237. package/src/renderables/Text.selection-buffer.test.ts +123 -0
  238. package/src/renderables/Text.test.ts +2660 -0
  239. package/src/renderables/Text.ts +147 -0
  240. package/src/renderables/TextBufferRenderable.ts +518 -0
  241. package/src/renderables/TextNode.test.ts +1058 -0
  242. package/src/renderables/TextNode.ts +325 -0
  243. package/src/renderables/TextTable.test.ts +1421 -0
  244. package/src/renderables/TextTable.ts +1344 -0
  245. package/src/renderables/Textarea.ts +732 -0
  246. package/src/renderables/TimeToFirstDraw.ts +89 -0
  247. package/src/renderables/__snapshots__/Code.test.ts.snap +13 -0
  248. package/src/renderables/__snapshots__/Diff.test.ts.snap +785 -0
  249. package/src/renderables/__snapshots__/Text.test.ts.snap +421 -0
  250. package/src/renderables/__snapshots__/TextTable.test.ts.snap +215 -0
  251. package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +144 -0
  252. package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +816 -0
  253. package/src/renderables/__tests__/LineNumberRenderable.test.ts +1787 -0
  254. package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +85 -0
  255. package/src/renderables/__tests__/Markdown.test.ts +2287 -0
  256. package/src/renderables/__tests__/MultiRenderable.selection.test.ts +87 -0
  257. package/src/renderables/__tests__/Textarea.buffer.test.ts +682 -0
  258. package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +675 -0
  259. package/src/renderables/__tests__/Textarea.editing.test.ts +2041 -0
  260. package/src/renderables/__tests__/Textarea.error-handling.test.ts +35 -0
  261. package/src/renderables/__tests__/Textarea.events.test.ts +738 -0
  262. package/src/renderables/__tests__/Textarea.highlights.test.ts +590 -0
  263. package/src/renderables/__tests__/Textarea.keybinding.test.ts +3149 -0
  264. package/src/renderables/__tests__/Textarea.paste.test.ts +357 -0
  265. package/src/renderables/__tests__/Textarea.rendering.test.ts +1864 -0
  266. package/src/renderables/__tests__/Textarea.scroll.test.ts +733 -0
  267. package/src/renderables/__tests__/Textarea.selection.test.ts +1590 -0
  268. package/src/renderables/__tests__/Textarea.stress.test.ts +670 -0
  269. package/src/renderables/__tests__/Textarea.undo-redo.test.ts +383 -0
  270. package/src/renderables/__tests__/Textarea.visual-lines.test.ts +310 -0
  271. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +221 -0
  272. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +89 -0
  273. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +457 -0
  274. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +158 -0
  275. package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +387 -0
  276. package/src/renderables/__tests__/markdown-parser.test.ts +217 -0
  277. package/src/renderables/__tests__/renderable-test-utils.ts +60 -0
  278. package/src/renderables/composition/README.md +8 -0
  279. package/src/renderables/composition/VRenderable.ts +32 -0
  280. package/src/renderables/composition/constructs.ts +127 -0
  281. package/src/renderables/composition/vnode.ts +289 -0
  282. package/src/renderables/index.ts +22 -0
  283. package/src/renderables/markdown-parser.ts +66 -0
  284. package/src/renderer.ts +2363 -0
  285. package/src/runtime-plugin-support.ts +39 -0
  286. package/src/runtime-plugin.ts +144 -0
  287. package/src/syntax-style.test.ts +841 -0
  288. package/src/syntax-style.ts +264 -0
  289. package/src/testing/README.md +210 -0
  290. package/src/testing/capture-spans.test.ts +194 -0
  291. package/src/testing/integration.test.ts +276 -0
  292. package/src/testing/manual-clock.ts +106 -0
  293. package/src/testing/mock-keys.test.ts +1356 -0
  294. package/src/testing/mock-keys.ts +449 -0
  295. package/src/testing/mock-mouse.test.ts +218 -0
  296. package/src/testing/mock-mouse.ts +247 -0
  297. package/src/testing/mock-tree-sitter-client.ts +73 -0
  298. package/src/testing/spy.ts +13 -0
  299. package/src/testing/test-recorder.test.ts +415 -0
  300. package/src/testing/test-recorder.ts +145 -0
  301. package/src/testing/test-renderer.ts +116 -0
  302. package/src/testing.ts +7 -0
  303. package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +481 -0
  304. package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +19 -0
  305. package/src/tests/__snapshots__/scrollbox.test.ts.snap +29 -0
  306. package/src/tests/absolute-positioning.snapshot.test.ts +638 -0
  307. package/src/tests/allocator-stats.test.ts +38 -0
  308. package/src/tests/destroy-during-render.test.ts +200 -0
  309. package/src/tests/hover-cursor.test.ts +98 -0
  310. package/src/tests/native-span-feed-async.test.ts +173 -0
  311. package/src/tests/native-span-feed-close.test.ts +120 -0
  312. package/src/tests/native-span-feed-coverage.test.ts +227 -0
  313. package/src/tests/native-span-feed-edge-cases.test.ts +352 -0
  314. package/src/tests/native-span-feed-use-after-free.test.ts +45 -0
  315. package/src/tests/opacity.test.ts +123 -0
  316. package/src/tests/renderable.snapshot.test.ts +524 -0
  317. package/src/tests/renderable.test.ts +1281 -0
  318. package/src/tests/renderer.console-startup.test.ts +65 -0
  319. package/src/tests/renderer.control.test.ts +364 -0
  320. package/src/tests/renderer.core-slot-binding.test.ts +952 -0
  321. package/src/tests/renderer.cursor.test.ts +26 -0
  322. package/src/tests/renderer.destroy-during-render.test.ts +110 -0
  323. package/src/tests/renderer.focus-restore.test.ts +228 -0
  324. package/src/tests/renderer.focus.test.ts +251 -0
  325. package/src/tests/renderer.idle.test.ts +219 -0
  326. package/src/tests/renderer.input.test.ts +2145 -0
  327. package/src/tests/renderer.kitty-flags.test.ts +195 -0
  328. package/src/tests/renderer.mouse.test.ts +1269 -0
  329. package/src/tests/renderer.palette.test.ts +629 -0
  330. package/src/tests/renderer.selection.test.ts +49 -0
  331. package/src/tests/renderer.slot-registry.test.ts +649 -0
  332. package/src/tests/renderer.useMouse.test.ts +50 -0
  333. package/src/tests/runtime-plugin-support.fixture.ts +11 -0
  334. package/src/tests/runtime-plugin-support.test.ts +28 -0
  335. package/src/tests/runtime-plugin.fixture.ts +40 -0
  336. package/src/tests/runtime-plugin.test.ts +190 -0
  337. package/src/tests/scrollbox-culling-bug.test.ts +114 -0
  338. package/src/tests/scrollbox-hitgrid-resize.test.ts +136 -0
  339. package/src/tests/scrollbox-hitgrid.test.ts +909 -0
  340. package/src/tests/scrollbox.test.ts +1530 -0
  341. package/src/tests/wrap-resize-perf.test.ts +229 -0
  342. package/src/tests/yoga-setters.test.ts +921 -0
  343. package/src/text-buffer-view.test.ts +705 -0
  344. package/src/text-buffer-view.ts +189 -0
  345. package/src/text-buffer.test.ts +347 -0
  346. package/src/text-buffer.ts +250 -0
  347. package/src/types.ts +152 -0
  348. package/src/utils.ts +88 -0
  349. package/src/zig/ansi.zig +268 -0
  350. package/src/zig/bench/README.md +50 -0
  351. package/src/zig/bench/buffer-draw-text-buffer_bench.zig +887 -0
  352. package/src/zig/bench/edit-buffer_bench.zig +476 -0
  353. package/src/zig/bench/native-span-feed_bench.zig +100 -0
  354. package/src/zig/bench/rope-markers_bench.zig +713 -0
  355. package/src/zig/bench/rope_bench.zig +514 -0
  356. package/src/zig/bench/styled-text_bench.zig +470 -0
  357. package/src/zig/bench/text-buffer-coords_bench.zig +362 -0
  358. package/src/zig/bench/text-buffer-view_bench.zig +459 -0
  359. package/src/zig/bench/text-chunk-graphemes_bench.zig +273 -0
  360. package/src/zig/bench/utf8_bench.zig +799 -0
  361. package/src/zig/bench-utils.zig +431 -0
  362. package/src/zig/bench.zig +217 -0
  363. package/src/zig/buffer.zig +2223 -0
  364. package/src/zig/build.zig +289 -0
  365. package/src/zig/build.zig.zon +16 -0
  366. package/src/zig/edit-buffer.zig +825 -0
  367. package/src/zig/editor-view.zig +802 -0
  368. package/src/zig/event-bus.zig +13 -0
  369. package/src/zig/event-emitter.zig +65 -0
  370. package/src/zig/file-logger.zig +92 -0
  371. package/src/zig/grapheme.zig +599 -0
  372. package/src/zig/lib.zig +1834 -0
  373. package/src/zig/link.zig +333 -0
  374. package/src/zig/logger.zig +43 -0
  375. package/src/zig/mem-registry.zig +125 -0
  376. package/src/zig/native-span-feed-bench-lib.zig +7 -0
  377. package/src/zig/native-span-feed.zig +708 -0
  378. package/src/zig/renderer.zig +1386 -0
  379. package/src/zig/rope.zig +1220 -0
  380. package/src/zig/syntax-style.zig +161 -0
  381. package/src/zig/terminal.zig +975 -0
  382. package/src/zig/test.zig +70 -0
  383. package/src/zig/tests/README.md +18 -0
  384. package/src/zig/tests/buffer_test.zig +2526 -0
  385. package/src/zig/tests/edit-buffer-history_test.zig +271 -0
  386. package/src/zig/tests/edit-buffer_test.zig +1689 -0
  387. package/src/zig/tests/editor-view_test.zig +3299 -0
  388. package/src/zig/tests/event-emitter_test.zig +249 -0
  389. package/src/zig/tests/grapheme_test.zig +1304 -0
  390. package/src/zig/tests/link_test.zig +190 -0
  391. package/src/zig/tests/mem-registry_test.zig +473 -0
  392. package/src/zig/tests/memory_leak_regression_test.zig +159 -0
  393. package/src/zig/tests/native-span-feed_test.zig +1264 -0
  394. package/src/zig/tests/renderer_test.zig +1010 -0
  395. package/src/zig/tests/rope-nested_test.zig +712 -0
  396. package/src/zig/tests/rope_fuzz_test.zig +238 -0
  397. package/src/zig/tests/rope_test.zig +2362 -0
  398. package/src/zig/tests/segment-merge.test.zig +148 -0
  399. package/src/zig/tests/syntax-style_test.zig +557 -0
  400. package/src/zig/tests/terminal_test.zig +719 -0
  401. package/src/zig/tests/text-buffer-drawing_test.zig +3237 -0
  402. package/src/zig/tests/text-buffer-highlights_test.zig +666 -0
  403. package/src/zig/tests/text-buffer-iterators_test.zig +776 -0
  404. package/src/zig/tests/text-buffer-segment_test.zig +320 -0
  405. package/src/zig/tests/text-buffer-selection_test.zig +1035 -0
  406. package/src/zig/tests/text-buffer-selection_viewport_test.zig +358 -0
  407. package/src/zig/tests/text-buffer-view_test.zig +3649 -0
  408. package/src/zig/tests/text-buffer_test.zig +2191 -0
  409. package/src/zig/tests/unicode-width-map.zon +3909 -0
  410. package/src/zig/tests/utf8_no_zwj_test.zig +260 -0
  411. package/src/zig/tests/utf8_test.zig +4057 -0
  412. package/src/zig/tests/utf8_wcwidth_cursor_test.zig +267 -0
  413. package/src/zig/tests/utf8_wcwidth_test.zig +357 -0
  414. package/src/zig/tests/word-wrap-editing_test.zig +498 -0
  415. package/src/zig/tests/wrap-cache-perf_test.zig +113 -0
  416. package/src/zig/text-buffer-iterators.zig +499 -0
  417. package/src/zig/text-buffer-segment.zig +404 -0
  418. package/src/zig/text-buffer-view.zig +1371 -0
  419. package/src/zig/text-buffer.zig +1180 -0
  420. package/src/zig/utf8.zig +1948 -0
  421. package/src/zig/utils.zig +9 -0
  422. package/src/zig-structs.ts +261 -0
  423. package/src/zig.ts +3843 -0
  424. package/tsconfig.build.json +22 -0
  425. package/tsconfig.json +28 -0
  426. package/3d/SpriteResourceManager.d.ts +0 -74
  427. package/3d/SpriteUtils.d.ts +0 -13
  428. package/3d/TextureUtils.d.ts +0 -24
  429. package/3d/ThreeRenderable.d.ts +0 -40
  430. package/3d/WGPURenderer.d.ts +0 -61
  431. package/3d/animation/ExplodingSpriteEffect.d.ts +0 -71
  432. package/3d/animation/PhysicsExplodingSpriteEffect.d.ts +0 -76
  433. package/3d/animation/SpriteAnimator.d.ts +0 -124
  434. package/3d/animation/SpriteParticleGenerator.d.ts +0 -62
  435. package/3d/canvas.d.ts +0 -44
  436. package/3d/index.d.ts +0 -12
  437. package/3d/physics/PlanckPhysicsAdapter.d.ts +0 -19
  438. package/3d/physics/RapierPhysicsAdapter.d.ts +0 -19
  439. package/3d/physics/physics-interface.d.ts +0 -27
  440. package/3d.d.ts +0 -2
  441. package/3d.js +0 -34042
  442. package/3d.js.map +0 -155
  443. package/LICENSE +0 -21
  444. package/NativeSpanFeed.d.ts +0 -41
  445. package/Renderable.d.ts +0 -334
  446. package/animation/Timeline.d.ts +0 -126
  447. package/ansi.d.ts +0 -13
  448. package/buffer.d.ts +0 -107
  449. package/console.d.ts +0 -143
  450. package/edit-buffer.d.ts +0 -98
  451. package/editor-view.d.ts +0 -73
  452. package/index-e4hzc2j2.js +0 -113
  453. package/index-e4hzc2j2.js.map +0 -10
  454. package/index-nkrr8a4c.js +0 -18415
  455. package/index-nkrr8a4c.js.map +0 -64
  456. package/index-nyw5p3ep.js +0 -12619
  457. package/index-nyw5p3ep.js.map +0 -43
  458. package/index.d.ts +0 -21
  459. package/index.js +0 -430
  460. package/index.js.map +0 -9
  461. package/lib/KeyHandler.d.ts +0 -61
  462. package/lib/RGBA.d.ts +0 -25
  463. package/lib/ascii.font.d.ts +0 -508
  464. package/lib/border.d.ts +0 -49
  465. package/lib/bunfs.d.ts +0 -7
  466. package/lib/clipboard.d.ts +0 -17
  467. package/lib/clock.d.ts +0 -15
  468. package/lib/data-paths.d.ts +0 -26
  469. package/lib/debounce.d.ts +0 -42
  470. package/lib/detect-links.d.ts +0 -6
  471. package/lib/env.d.ts +0 -42
  472. package/lib/extmarks-history.d.ts +0 -17
  473. package/lib/extmarks.d.ts +0 -89
  474. package/lib/hast-styled-text.d.ts +0 -17
  475. package/lib/index.d.ts +0 -21
  476. package/lib/keymapping.d.ts +0 -25
  477. package/lib/objects-in-viewport.d.ts +0 -24
  478. package/lib/output.capture.d.ts +0 -24
  479. package/lib/parse.keypress-kitty.d.ts +0 -2
  480. package/lib/parse.keypress.d.ts +0 -26
  481. package/lib/parse.mouse.d.ts +0 -30
  482. package/lib/paste.d.ts +0 -7
  483. package/lib/queue.d.ts +0 -15
  484. package/lib/renderable.validations.d.ts +0 -12
  485. package/lib/scroll-acceleration.d.ts +0 -43
  486. package/lib/selection.d.ts +0 -63
  487. package/lib/singleton.d.ts +0 -7
  488. package/lib/stdin-parser.d.ts +0 -76
  489. package/lib/styled-text.d.ts +0 -63
  490. package/lib/terminal-capability-detection.d.ts +0 -30
  491. package/lib/terminal-palette.d.ts +0 -50
  492. package/lib/tree-sitter/assets/update.d.ts +0 -11
  493. package/lib/tree-sitter/client.d.ts +0 -47
  494. package/lib/tree-sitter/default-parsers.d.ts +0 -2
  495. package/lib/tree-sitter/download-utils.d.ts +0 -21
  496. package/lib/tree-sitter/index.d.ts +0 -8
  497. package/lib/tree-sitter/parser.worker.d.ts +0 -1
  498. package/lib/tree-sitter/parsers-config.d.ts +0 -38
  499. package/lib/tree-sitter/resolve-ft.d.ts +0 -2
  500. package/lib/tree-sitter/types.d.ts +0 -81
  501. package/lib/tree-sitter-styled-text.d.ts +0 -14
  502. package/lib/validate-dir-name.d.ts +0 -1
  503. package/lib/yoga.options.d.ts +0 -32
  504. package/parser.worker.js +0 -869
  505. package/parser.worker.js.map +0 -12
  506. package/plugins/core-slot.d.ts +0 -72
  507. package/plugins/registry.d.ts +0 -38
  508. package/plugins/types.d.ts +0 -34
  509. package/post/filters.d.ts +0 -105
  510. package/renderables/ASCIIFont.d.ts +0 -52
  511. package/renderables/Box.d.ts +0 -72
  512. package/renderables/Code.d.ts +0 -78
  513. package/renderables/Diff.d.ts +0 -142
  514. package/renderables/EditBufferRenderable.d.ts +0 -162
  515. package/renderables/FrameBuffer.d.ts +0 -16
  516. package/renderables/Input.d.ts +0 -67
  517. package/renderables/LineNumberRenderable.d.ts +0 -74
  518. package/renderables/Markdown.d.ts +0 -173
  519. package/renderables/ScrollBar.d.ts +0 -77
  520. package/renderables/ScrollBox.d.ts +0 -124
  521. package/renderables/Select.d.ts +0 -115
  522. package/renderables/Slider.d.ts +0 -44
  523. package/renderables/TabSelect.d.ts +0 -96
  524. package/renderables/Text.d.ts +0 -36
  525. package/renderables/TextBufferRenderable.d.ts +0 -105
  526. package/renderables/TextNode.d.ts +0 -91
  527. package/renderables/TextTable.d.ts +0 -140
  528. package/renderables/Textarea.d.ts +0 -114
  529. package/renderables/TimeToFirstDraw.d.ts +0 -24
  530. package/renderables/__tests__/renderable-test-utils.d.ts +0 -12
  531. package/renderables/composition/VRenderable.d.ts +0 -16
  532. package/renderables/composition/constructs.d.ts +0 -35
  533. package/renderables/composition/vnode.d.ts +0 -46
  534. package/renderables/index.d.ts +0 -22
  535. package/renderables/markdown-parser.d.ts +0 -10
  536. package/renderer.d.ts +0 -388
  537. package/runtime-plugin-support.d.ts +0 -3
  538. package/runtime-plugin-support.js +0 -29
  539. package/runtime-plugin-support.js.map +0 -10
  540. package/runtime-plugin.d.ts +0 -11
  541. package/runtime-plugin.js +0 -16
  542. package/runtime-plugin.js.map +0 -9
  543. package/syntax-style.d.ts +0 -54
  544. package/testing/manual-clock.d.ts +0 -16
  545. package/testing/mock-keys.d.ts +0 -81
  546. package/testing/mock-mouse.d.ts +0 -38
  547. package/testing/mock-tree-sitter-client.d.ts +0 -23
  548. package/testing/spy.d.ts +0 -7
  549. package/testing/test-recorder.d.ts +0 -61
  550. package/testing/test-renderer.d.ts +0 -23
  551. package/testing.d.ts +0 -6
  552. package/testing.js +0 -675
  553. package/testing.js.map +0 -15
  554. package/text-buffer-view.d.ts +0 -42
  555. package/text-buffer.d.ts +0 -67
  556. package/types.d.ts +0 -131
  557. package/utils.d.ts +0 -14
  558. package/zig-structs.d.ts +0 -155
  559. package/zig.d.ts +0 -351
  560. /package/{assets → src/lib/tree-sitter/assets}/javascript/highlights.scm +0 -0
  561. /package/{assets → src/lib/tree-sitter/assets}/javascript/tree-sitter-javascript.wasm +0 -0
  562. /package/{assets → src/lib/tree-sitter/assets}/markdown/highlights.scm +0 -0
  563. /package/{assets → src/lib/tree-sitter/assets}/markdown/injections.scm +0 -0
  564. /package/{assets → src/lib/tree-sitter/assets}/markdown/tree-sitter-markdown.wasm +0 -0
  565. /package/{assets → src/lib/tree-sitter/assets}/markdown_inline/highlights.scm +0 -0
  566. /package/{assets → src/lib/tree-sitter/assets}/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
  567. /package/{assets → src/lib/tree-sitter/assets}/typescript/highlights.scm +0 -0
  568. /package/{assets → src/lib/tree-sitter/assets}/typescript/tree-sitter-typescript.wasm +0 -0
  569. /package/{assets → src/lib/tree-sitter/assets}/zig/highlights.scm +0 -0
  570. /package/{assets → src/lib/tree-sitter/assets}/zig/tree-sitter-zig.wasm +0 -0
@@ -0,0 +1,28 @@
1
+ import { describe, expect, it } from "bun:test"
2
+ import { join } from "node:path"
3
+
4
+ describe("runtime plugin support", () => {
5
+ it("installs exactly once via drop-in module", () => {
6
+ const fixturePath = join(import.meta.dir, "runtime-plugin-support.fixture.ts")
7
+ const result = Bun.spawnSync([process.execPath, fixturePath], {
8
+ cwd: join(import.meta.dir, "..", ".."),
9
+ stdout: "pipe",
10
+ stderr: "pipe",
11
+ env: process.env,
12
+ })
13
+
14
+ const stdout = result.stdout.toString().trim()
15
+ const stderr = result.stderr.toString().trim()
16
+
17
+ if (stdout) {
18
+ console.debug(`[runtime-plugin-support.fixture] stdout:\n${stdout}`)
19
+ }
20
+
21
+ if (stderr) {
22
+ console.debug(`[runtime-plugin-support.fixture] stderr:\n${stderr}`)
23
+ }
24
+
25
+ expect(result.exitCode).toBe(0)
26
+ expect(stdout).toContain("idempotent=true")
27
+ })
28
+ })
@@ -0,0 +1,40 @@
1
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs"
2
+ import { tmpdir } from "node:os"
3
+ import { join } from "node:path"
4
+ import { plugin as registerPlugin } from "bun"
5
+ import { createRuntimePlugin } from "../runtime-plugin"
6
+
7
+ const tempRoot = mkdtempSync(join(tmpdir(), "core-runtime-plugin-fixture-"))
8
+ const entryPath = join(tempRoot, "entry.ts")
9
+
10
+ const source = [
11
+ 'import { marker as coreMarker } from "@opentui/core"',
12
+ 'import { createTestRenderer } from "@opentui/core/testing"',
13
+ 'import { value as syncValue } from "fixture-sync"',
14
+ 'import { value as asyncValue } from "@fixture/async-module"',
15
+ "console.log(`core=${coreMarker};coreTesting=${typeof createTestRenderer === 'function'};sync=${syncValue};async=${asyncValue}`)",
16
+ "export const noop = 1",
17
+ ].join("\n")
18
+
19
+ writeFileSync(entryPath, source)
20
+
21
+ registerPlugin.clearAll()
22
+
23
+ registerPlugin(
24
+ createRuntimePlugin({
25
+ core: {
26
+ marker: "core-value",
27
+ },
28
+ additional: {
29
+ "fixture-sync": { value: "sync-value" },
30
+ "@fixture/async-module": async () => ({ value: "async-value" }),
31
+ },
32
+ }),
33
+ )
34
+
35
+ try {
36
+ await import(entryPath)
37
+ } finally {
38
+ registerPlugin.clearAll()
39
+ rmSync(tempRoot, { recursive: true, force: true })
40
+ }
@@ -0,0 +1,190 @@
1
+ import { describe, expect, it } from "bun:test"
2
+ import { join } from "node:path"
3
+ import * as coreRuntime from "../index"
4
+ import { createRuntimePlugin, runtimeModuleIdForSpecifier } from "../runtime-plugin"
5
+
6
+ type ResolveResult = { path: string; namespace?: string } | void
7
+ type ResolveCallback = (args: { path: string; importer: string }) => ResolveResult | Promise<ResolveResult>
8
+ type LoadCallback = (args: { path: string }) => unknown | Promise<unknown>
9
+ type ModuleCallback = () => unknown | Promise<unknown>
10
+
11
+ type ResolveHandler = {
12
+ filter: RegExp
13
+ callback: ResolveCallback
14
+ }
15
+
16
+ type MockBuild = {
17
+ onResolve: (args: { filter: RegExp }, callback: ResolveCallback) => void
18
+ onLoad: (args: { filter: RegExp }, callback: LoadCallback) => void
19
+ module: (path: string, callback: ModuleCallback) => void
20
+ }
21
+
22
+ const createMockBuild = (): {
23
+ build: MockBuild
24
+ resolveHandlers: ResolveHandler[]
25
+ modules: Map<string, ModuleCallback>
26
+ } => {
27
+ const resolveHandlers: ResolveHandler[] = []
28
+ const modules = new Map<string, ModuleCallback>()
29
+
30
+ const build: MockBuild = {
31
+ onResolve(args, callback) {
32
+ resolveHandlers.push({ filter: args.filter, callback })
33
+ },
34
+ onLoad() {
35
+ return
36
+ },
37
+ module(path, callback) {
38
+ modules.set(path, callback)
39
+ },
40
+ }
41
+
42
+ return { build, resolveHandlers, modules }
43
+ }
44
+
45
+ const resolveSpecifier = async (handlers: ResolveHandler[], specifier: string): Promise<ResolveResult> => {
46
+ for (const handler of handlers) {
47
+ if (!handler.filter.test(specifier)) continue
48
+
49
+ const result = await handler.callback({
50
+ path: specifier,
51
+ importer: import.meta.path,
52
+ })
53
+
54
+ if (result) {
55
+ return result
56
+ }
57
+ }
58
+
59
+ return undefined
60
+ }
61
+
62
+ describe("runtime plugin", () => {
63
+ it("registers core runtime modules by default", async () => {
64
+ const { build, resolveHandlers, modules } = createMockBuild()
65
+ createRuntimePlugin().setup(build as any)
66
+
67
+ const coreResolution = await resolveSpecifier(resolveHandlers, "@opentui/core")
68
+ const core3dResolution = await resolveSpecifier(resolveHandlers, "@opentui/core/3d")
69
+ const coreTestingResolution = await resolveSpecifier(resolveHandlers, "@opentui/core/testing")
70
+
71
+ expect(coreResolution).toEqual({ path: runtimeModuleIdForSpecifier("@opentui/core") })
72
+ expect(core3dResolution).toEqual({ path: runtimeModuleIdForSpecifier("@opentui/core/3d") })
73
+ expect(coreTestingResolution).toEqual({ path: runtimeModuleIdForSpecifier("@opentui/core/testing") })
74
+
75
+ if (!coreResolution || !core3dResolution || !coreTestingResolution) {
76
+ throw new Error("Expected core runtime module resolutions")
77
+ }
78
+
79
+ const coreModuleFactory = modules.get(coreResolution.path)
80
+ const core3dModuleFactory = modules.get(core3dResolution.path)
81
+ const coreTestingModuleFactory = modules.get(coreTestingResolution.path)
82
+
83
+ expect(coreModuleFactory).toBeDefined()
84
+ expect(core3dModuleFactory).toBeDefined()
85
+ expect(coreTestingModuleFactory).toBeDefined()
86
+
87
+ if (!coreModuleFactory || !core3dModuleFactory || !coreTestingModuleFactory) {
88
+ throw new Error("Expected core runtime module factories")
89
+ }
90
+
91
+ expect(await coreModuleFactory()).toEqual({
92
+ exports: coreRuntime as Record<string, unknown>,
93
+ loader: "object",
94
+ })
95
+
96
+ const coreTestingModule = (await coreTestingModuleFactory()) as {
97
+ exports: Record<string, unknown>
98
+ loader: string
99
+ }
100
+
101
+ expect(coreTestingModule.loader).toBe("object")
102
+ expect(typeof coreTestingModule.exports.createTestRenderer).toBe("function")
103
+ })
104
+
105
+ it("registers additional runtime modules with sync and async loaders", async () => {
106
+ const { build, resolveHandlers, modules } = createMockBuild()
107
+
108
+ createRuntimePlugin({
109
+ core: { marker: "core" },
110
+ additional: {
111
+ "fixture-sync": { value: "sync-value" },
112
+ "@fixture/async-module": async () => ({ value: "async-value" }),
113
+ },
114
+ }).setup(build as any)
115
+
116
+ const coreResolution = await resolveSpecifier(resolveHandlers, "@opentui/core")
117
+ const syncResolution = await resolveSpecifier(resolveHandlers, "fixture-sync")
118
+ const asyncResolution = await resolveSpecifier(resolveHandlers, "@fixture/async-module")
119
+
120
+ expect(coreResolution).toEqual({ path: runtimeModuleIdForSpecifier("@opentui/core") })
121
+ expect(syncResolution).toEqual({ path: runtimeModuleIdForSpecifier("fixture-sync") })
122
+ expect(asyncResolution).toEqual({ path: runtimeModuleIdForSpecifier("@fixture/async-module") })
123
+
124
+ if (!coreResolution || !syncResolution || !asyncResolution) {
125
+ throw new Error("Expected runtime module resolutions")
126
+ }
127
+
128
+ const coreModuleFactory = modules.get(coreResolution.path)
129
+ const syncModuleFactory = modules.get(syncResolution.path)
130
+ const asyncModuleFactory = modules.get(asyncResolution.path)
131
+
132
+ expect(coreModuleFactory).toBeDefined()
133
+ expect(syncModuleFactory).toBeDefined()
134
+ expect(asyncModuleFactory).toBeDefined()
135
+
136
+ if (!coreModuleFactory || !syncModuleFactory || !asyncModuleFactory) {
137
+ throw new Error("Expected runtime module factories")
138
+ }
139
+
140
+ expect(await coreModuleFactory()).toEqual({ exports: { marker: "core" }, loader: "object" })
141
+ expect(await syncModuleFactory()).toEqual({ exports: { value: "sync-value" }, loader: "object" })
142
+ expect(await asyncModuleFactory()).toEqual({ exports: { value: "async-value" }, loader: "object" })
143
+ })
144
+
145
+ it("escapes exact-match resolver filters for special characters", async () => {
146
+ const { build, resolveHandlers } = createMockBuild()
147
+
148
+ createRuntimePlugin({
149
+ additional: {
150
+ "fixture.with.dot": { value: "dot-value" },
151
+ },
152
+ }).setup(build as any)
153
+
154
+ const exactMatch = await resolveSpecifier(resolveHandlers, "fixture.with.dot")
155
+ const nonMatch = await resolveSpecifier(resolveHandlers, "fixtureXwithXdot")
156
+
157
+ expect(exactMatch).toEqual({ path: runtimeModuleIdForSpecifier("fixture.with.dot") })
158
+ expect(nonMatch).toBeUndefined()
159
+ })
160
+
161
+ it("encodes runtime module IDs deterministically", () => {
162
+ expect(runtimeModuleIdForSpecifier("@opentui/core/testing")).toBe(
163
+ "opentui:runtime-module:%40opentui%2Fcore%2Ftesting",
164
+ )
165
+ })
166
+
167
+ it("resolves runtime modules end-to-end in a subprocess", () => {
168
+ const fixturePath = join(import.meta.dir, "runtime-plugin.fixture.ts")
169
+ const result = Bun.spawnSync([process.execPath, fixturePath], {
170
+ cwd: join(import.meta.dir, "..", ".."),
171
+ stdout: "pipe",
172
+ stderr: "pipe",
173
+ env: process.env,
174
+ })
175
+
176
+ const stdout = result.stdout.toString().trim()
177
+ const stderr = result.stderr.toString().trim()
178
+
179
+ if (stdout) {
180
+ console.debug(`[runtime-plugin.fixture] stdout:\n${stdout}`)
181
+ }
182
+
183
+ if (stderr) {
184
+ console.debug(`[runtime-plugin.fixture] stderr:\n${stderr}`)
185
+ }
186
+
187
+ expect(result.exitCode).toBe(0)
188
+ expect(stdout).toContain("core=core-value;coreTesting=true;sync=sync-value;async=async-value")
189
+ })
190
+ })
@@ -0,0 +1,114 @@
1
+ import { test, expect, beforeEach, afterEach } from "bun:test"
2
+ import { createTestRenderer, type TestRenderer } from "../testing.js"
3
+ import { ManualClock } from "../testing/manual-clock.js"
4
+ import { ScrollBoxRenderable } from "../renderables/ScrollBox.js"
5
+ import { BoxRenderable } from "../renderables/Box.js"
6
+ import { TextRenderable } from "../renderables/Text.js"
7
+ import { TestRecorder } from "../testing/test-recorder.js"
8
+
9
+ let testRenderer: TestRenderer
10
+ let renderOnce: () => Promise<void>
11
+ let clock: ManualClock
12
+
13
+ beforeEach(async () => {
14
+ clock = new ManualClock()
15
+ ;({ renderer: testRenderer, renderOnce } = await createTestRenderer({ width: 50, height: 12, clock }))
16
+ })
17
+
18
+ afterEach(() => {
19
+ testRenderer.destroy()
20
+ })
21
+
22
+ test("scrollbox culling issue: last item not visible in frame after content grows with stickyScroll", async () => {
23
+ // ISSUE: During updateLayout, when content.onSizeChange triggers recalculateBarProps,
24
+ // it changes translateY via the scrollbar onChange callback. Then _getVisibleChildren()
25
+ // is called for culling, but it uses the NEW translateY value with OLD child layout
26
+ // positions (since children haven't had updateFromLayout called yet). This causes
27
+ // incorrect culling where the last item is not rendered even though it should be visible.
28
+
29
+ // Container box with border to see constraints clearly
30
+ const container = new BoxRenderable(testRenderer, {
31
+ width: 48,
32
+ height: 10,
33
+ border: true,
34
+ })
35
+ testRenderer.root.add(container)
36
+
37
+ const scrollBox = new ScrollBoxRenderable(testRenderer, {
38
+ width: "100%",
39
+ height: "100%",
40
+ stickyScroll: true,
41
+ stickyStart: "bottom",
42
+ })
43
+ container.add(scrollBox)
44
+
45
+ const recorder = new TestRecorder(testRenderer)
46
+ recorder.rec()
47
+
48
+ for (let i = 0; i < 50; i++) {
49
+ const item = new BoxRenderable(testRenderer, {
50
+ id: `item-${i}`,
51
+ height: 3,
52
+ border: true,
53
+ })
54
+
55
+ const text = new TextRenderable(testRenderer, {
56
+ content: `Item ${i}`,
57
+ })
58
+ item.add(text)
59
+
60
+ scrollBox.add(item)
61
+ await renderOnce()
62
+ }
63
+
64
+ // Advance clock to trigger any pending re-render scheduled by stickyScroll's requestRender()
65
+ clock.advance(100)
66
+ await renderOnce()
67
+
68
+ recorder.stop()
69
+
70
+ const frames = recorder.recordedFrames
71
+
72
+ // With stickyScroll to bottom, there should NEVER be empty space at the bottom
73
+ // when there are items available to render
74
+
75
+ for (let frameIdx = 0; frameIdx < frames.length; frameIdx++) {
76
+ const frame = frames[frameIdx].frame
77
+ const lines = frame.split("\n")
78
+
79
+ const containerStart = lines.findIndex((line) => line.startsWith("┌"))
80
+ const containerEnd = containerStart + 10 - 1
81
+
82
+ if (containerStart >= 0 && containerEnd > containerStart && containerEnd < lines.length) {
83
+ const contentLines = lines.slice(containerStart + 1, containerEnd)
84
+
85
+ let emptyLinesAtBottom = 0
86
+
87
+ for (let i = contentLines.length - 1; i >= 0; i--) {
88
+ const line = contentLines[i]
89
+ const content = line.replace(/^[│\s]*/, "").replace(/[│█▄\s]*$/, "")
90
+
91
+ if (content.length === 0) {
92
+ emptyLinesAtBottom++
93
+ } else {
94
+ break
95
+ }
96
+ }
97
+
98
+ const expectedItems = frameIdx + 1
99
+
100
+ // With stickyScroll to bottom, once we have enough items to fill the viewport,
101
+ // there should be NO empty space at the bottom
102
+ // Viewport is 8 lines (10 - 2 for borders), items are 3 lines each
103
+ // So with 3+ items (9 lines of content), we should always fill the viewport
104
+ if (expectedItems >= 3) {
105
+ expect(emptyLinesAtBottom).toBe(0)
106
+ }
107
+ }
108
+ }
109
+
110
+ // With stickyScroll to bottom, the last item should be visible after all items are added
111
+ const finalFrame = frames[frames.length - 1].frame
112
+ const hasItem49 = finalFrame.includes("Item 49")
113
+ expect(hasItem49).toBe(true)
114
+ })
@@ -0,0 +1,136 @@
1
+ import { test, expect } from "bun:test"
2
+ import { createTestRenderer, type MockMouse, type TestRenderer } from "../testing.js"
3
+ import { ScrollBoxRenderable } from "../renderables/ScrollBox.js"
4
+ import { BoxRenderable } from "../renderables/Box.js"
5
+ import { Renderable } from "../Renderable.js"
6
+
7
+ test("hit grid works at all Y coordinates after terminal shrink", async () => {
8
+ // Start wide: 160x50 = 8000 cells
9
+ const { renderer, mockMouse, resize } = await createTestRenderer({
10
+ width: 160,
11
+ height: 50,
12
+ })
13
+
14
+ const scrollBox = new ScrollBoxRenderable(renderer, {
15
+ width: "100%",
16
+ height: "100%",
17
+ scrollY: true,
18
+ })
19
+ renderer.root.add(scrollBox)
20
+
21
+ const items: BoxRenderable[] = []
22
+ for (let i = 0; i < 200; i++) {
23
+ const item = new BoxRenderable(renderer, {
24
+ id: `item-${i}`,
25
+ height: 2,
26
+ })
27
+ items.push(item)
28
+ scrollBox.add(item)
29
+ }
30
+
31
+ await renderer.idle()
32
+
33
+ // Verify hit grid works at the original size
34
+ const hitBefore = renderer.hitTest(5, 10)
35
+ expect(hitBefore).not.toBe(0)
36
+
37
+ // Shrink to narrow+tall: 60x100 = 6000 cells (smaller total area)
38
+ resize(60, 100)
39
+ renderer.root.resize(60, 100)
40
+ await renderer.idle()
41
+
42
+ // Row 70 is beyond the old height (50). Before the fix, checkHit
43
+ // returned 0 here because hitGridHeight was still 50.
44
+ const hitAtRow70 = renderer.hitTest(5, 70)
45
+ expect(hitAtRow70).not.toBe(0)
46
+
47
+ // Row 95 -- near the bottom of the new terminal
48
+ const hitAtRow95 = renderer.hitTest(5, 95)
49
+ expect(hitAtRow95).not.toBe(0)
50
+
51
+ renderer.destroy()
52
+ })
53
+
54
+ test("mouse scroll reaches scrollbox after terminal shrink", async () => {
55
+ const { renderer, mockMouse, resize } = await createTestRenderer({
56
+ width: 160,
57
+ height: 50,
58
+ })
59
+
60
+ const scrollBox = new ScrollBoxRenderable(renderer, {
61
+ width: "100%",
62
+ height: "100%",
63
+ scrollY: true,
64
+ })
65
+ renderer.root.add(scrollBox)
66
+
67
+ for (let i = 0; i < 200; i++) {
68
+ scrollBox.add(
69
+ new BoxRenderable(renderer, {
70
+ id: `item-${i}`,
71
+ height: 2,
72
+ }),
73
+ )
74
+ }
75
+
76
+ await renderer.idle()
77
+
78
+ // Shrink terminal
79
+ resize(60, 100)
80
+ renderer.root.resize(60, 100)
81
+ await renderer.idle()
82
+
83
+ // Scroll to bottom first so we have room to scroll up
84
+ scrollBox.scrollTop = scrollBox.scrollHeight - 100
85
+ await renderer.idle()
86
+ const positionBefore = scrollBox.scrollTop
87
+
88
+ // Mouse wheel at row 70 (beyond the old height of 50)
89
+ await mockMouse.scroll(30, 70, "up")
90
+ await renderer.idle()
91
+
92
+ // scrollTop should have decreased -- the scroll event reached the scrollbox
93
+ expect(scrollBox.scrollTop).toBeLessThan(positionBefore)
94
+
95
+ renderer.destroy()
96
+ })
97
+
98
+ test("hit grid works after multiple resize cycles", async () => {
99
+ const { renderer, resize } = await createTestRenderer({
100
+ width: 80,
101
+ height: 40,
102
+ })
103
+
104
+ const box = new BoxRenderable(renderer, {
105
+ id: "target",
106
+ width: "100%",
107
+ height: "100%",
108
+ })
109
+ renderer.root.add(box)
110
+ await renderer.idle()
111
+
112
+ // Grow: 80x40=3200 -> 120x60=7200
113
+ resize(120, 60)
114
+ renderer.root.resize(120, 60)
115
+ await renderer.idle()
116
+ expect(renderer.hitTest(5, 55)).not.toBe(0)
117
+
118
+ // Shrink back: 120x60=7200 -> 80x40=3200
119
+ resize(80, 40)
120
+ renderer.root.resize(80, 40)
121
+ await renderer.idle()
122
+ expect(renderer.hitTest(5, 35)).not.toBe(0)
123
+ // x=100 is outside the new width, should return 0
124
+ expect(renderer.hitTest(100, 10)).toBe(0)
125
+
126
+ // Shrink further: 80x40=3200 -> 40x30=1200
127
+ resize(40, 30)
128
+ renderer.root.resize(40, 30)
129
+ await renderer.idle()
130
+ expect(renderer.hitTest(5, 25)).not.toBe(0)
131
+ // Old coordinates should be out of bounds
132
+ expect(renderer.hitTest(5, 35)).toBe(0)
133
+ expect(renderer.hitTest(50, 10)).toBe(0)
134
+
135
+ renderer.destroy()
136
+ })