@fairyhunter13/opentui-core 0.1.113 → 0.1.114

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 (591) 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 +144 -0
  7. package/package.json +62 -53
  8. package/scripts/build.ts +400 -0
  9. package/scripts/publish.ts +60 -0
  10. package/src/3d/SpriteResourceManager.ts +286 -0
  11. package/src/3d/SpriteUtils.ts +70 -0
  12. package/src/3d/TextureUtils.ts +196 -0
  13. package/src/3d/ThreeRenderable.ts +197 -0
  14. package/src/3d/WGPURenderer.ts +294 -0
  15. package/src/3d/animation/ExplodingSpriteEffect.ts +513 -0
  16. package/src/3d/animation/PhysicsExplodingSpriteEffect.ts +429 -0
  17. package/src/3d/animation/SpriteAnimator.ts +633 -0
  18. package/src/3d/animation/SpriteParticleGenerator.ts +435 -0
  19. package/src/3d/canvas.ts +464 -0
  20. package/src/3d/index.ts +12 -0
  21. package/src/3d/physics/PlanckPhysicsAdapter.ts +72 -0
  22. package/src/3d/physics/RapierPhysicsAdapter.ts +66 -0
  23. package/src/3d/physics/physics-interface.ts +31 -0
  24. package/src/3d/shaders/supersampling.wgsl +201 -0
  25. package/src/3d.ts +3 -0
  26. package/src/NativeSpanFeed.ts +300 -0
  27. package/src/Renderable.ts +1704 -0
  28. package/src/__snapshots__/buffer.test.ts.snap +28 -0
  29. package/src/animation/Timeline.test.ts +2709 -0
  30. package/src/animation/Timeline.ts +598 -0
  31. package/src/ansi.ts +18 -0
  32. package/src/benchmark/attenuation-benchmark.ts +81 -0
  33. package/src/benchmark/colormatrix-benchmark.ts +128 -0
  34. package/src/benchmark/gain-benchmark.ts +80 -0
  35. package/src/benchmark/latest-all-bench-run.json +707 -0
  36. package/src/benchmark/latest-async-bench-run.json +336 -0
  37. package/src/benchmark/latest-default-bench-run.json +657 -0
  38. package/src/benchmark/latest-large-bench-run.json +707 -0
  39. package/src/benchmark/latest-quick-bench-run.json +207 -0
  40. package/src/benchmark/markdown-benchmark.ts +1796 -0
  41. package/src/benchmark/native-span-feed-async-benchmark.ts +355 -0
  42. package/src/benchmark/native-span-feed-benchmark.md +56 -0
  43. package/src/benchmark/native-span-feed-benchmark.ts +596 -0
  44. package/src/benchmark/native-span-feed-compare.ts +280 -0
  45. package/src/benchmark/renderer-benchmark.ts +754 -0
  46. package/src/benchmark/text-table-benchmark.ts +948 -0
  47. package/src/buffer.test.ts +291 -0
  48. package/src/buffer.ts +554 -0
  49. package/src/console.test.ts +612 -0
  50. package/src/console.ts +1254 -0
  51. package/src/edit-buffer.test.ts +1769 -0
  52. package/src/edit-buffer.ts +411 -0
  53. package/src/editor-view.test.ts +1032 -0
  54. package/src/editor-view.ts +284 -0
  55. package/src/examples/ascii-font-selection-demo.ts +245 -0
  56. package/src/examples/assets/Water_2_M_Normal.jpg +0 -0
  57. package/src/examples/assets/concrete.png +0 -0
  58. package/src/examples/assets/crate.png +0 -0
  59. package/src/examples/assets/crate_emissive.png +0 -0
  60. package/src/examples/assets/forrest_background.png +0 -0
  61. package/src/examples/assets/hast-example.json +1018 -0
  62. package/src/examples/assets/heart.png +0 -0
  63. package/src/examples/assets/main_char_heavy_attack.png +0 -0
  64. package/src/examples/assets/main_char_idle.png +0 -0
  65. package/src/examples/assets/main_char_jump_end.png +0 -0
  66. package/src/examples/assets/main_char_jump_landing.png +0 -0
  67. package/src/examples/assets/main_char_jump_start.png +0 -0
  68. package/src/examples/assets/main_char_run_loop.png +0 -0
  69. package/src/examples/assets/roughness_map.jpg +0 -0
  70. package/src/examples/build.ts +115 -0
  71. package/src/examples/code-demo.ts +924 -0
  72. package/src/examples/console-demo.ts +358 -0
  73. package/src/examples/core-plugin-slots-demo.ts +759 -0
  74. package/src/examples/diff-demo.ts +701 -0
  75. package/src/examples/draggable-three-demo.ts +259 -0
  76. package/src/examples/editor-demo.ts +322 -0
  77. package/src/examples/extmarks-demo.ts +196 -0
  78. package/src/examples/focus-restore-demo.ts +310 -0
  79. package/src/examples/fonts.ts +245 -0
  80. package/src/examples/fractal-shader-demo.ts +268 -0
  81. package/src/examples/framebuffer-demo.ts +674 -0
  82. package/src/examples/full-unicode-demo.ts +241 -0
  83. package/src/examples/golden-star-demo.ts +933 -0
  84. package/src/examples/grayscale-buffer-demo.ts +249 -0
  85. package/src/examples/hast-syntax-highlighting-demo.ts +129 -0
  86. package/src/examples/index.ts +926 -0
  87. package/src/examples/input-demo.ts +377 -0
  88. package/src/examples/input-select-layout-demo.ts +425 -0
  89. package/src/examples/install.sh +143 -0
  90. package/src/examples/keypress-debug-demo.ts +452 -0
  91. package/src/examples/lib/HexList.ts +122 -0
  92. package/src/examples/lib/PaletteGrid.ts +125 -0
  93. package/src/examples/lib/standalone-keys.ts +25 -0
  94. package/src/examples/lib/tab-controller.ts +243 -0
  95. package/src/examples/lights-phong-demo.ts +290 -0
  96. package/src/examples/link-demo.ts +220 -0
  97. package/src/examples/live-state-demo.ts +480 -0
  98. package/src/examples/markdown-demo.ts +725 -0
  99. package/src/examples/mouse-interaction-demo.ts +428 -0
  100. package/src/examples/nested-zindex-demo.ts +357 -0
  101. package/src/examples/opacity-example.ts +235 -0
  102. package/src/examples/opentui-demo.ts +1057 -0
  103. package/src/examples/physx-planck-2d-demo.ts +623 -0
  104. package/src/examples/physx-rapier-2d-demo.ts +655 -0
  105. package/src/examples/relative-positioning-demo.ts +323 -0
  106. package/src/examples/scroll-example.ts +214 -0
  107. package/src/examples/scrollbox-mouse-test.ts +112 -0
  108. package/src/examples/scrollbox-overlay-hit-test.ts +206 -0
  109. package/src/examples/select-demo.ts +237 -0
  110. package/src/examples/shader-cube-demo.ts +1015 -0
  111. package/src/examples/simple-layout-example.ts +591 -0
  112. package/src/examples/slider-demo.ts +617 -0
  113. package/src/examples/split-mode-demo.ts +453 -0
  114. package/src/examples/sprite-animation-demo.ts +443 -0
  115. package/src/examples/sprite-particle-generator-demo.ts +486 -0
  116. package/src/examples/static-sprite-demo.ts +193 -0
  117. package/src/examples/sticky-scroll-example.ts +308 -0
  118. package/src/examples/styled-text-demo.ts +282 -0
  119. package/src/examples/tab-select-demo.ts +219 -0
  120. package/src/examples/terminal-title.ts +29 -0
  121. package/src/examples/terminal.ts +305 -0
  122. package/src/examples/text-node-demo.ts +416 -0
  123. package/src/examples/text-selection-demo.ts +377 -0
  124. package/src/examples/text-table-demo.ts +503 -0
  125. package/src/examples/text-truncation-demo.ts +481 -0
  126. package/src/examples/text-wrap.ts +757 -0
  127. package/src/examples/texture-loading-demo.ts +259 -0
  128. package/src/examples/timeline-example.ts +670 -0
  129. package/src/examples/transparency-demo.ts +400 -0
  130. package/src/examples/vnode-composition-demo.ts +404 -0
  131. package/src/examples/wide-grapheme-overlay-demo.ts +280 -0
  132. package/src/index.ts +24 -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 +170 -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 +35 -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 +317 -0
  168. package/src/lib/keymapping.ts +115 -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 +2290 -0
  187. package/src/lib/stdin-parser.ts +1810 -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 +334 -0
  195. package/src/lib/tree-sitter/assets.d.ts +9 -0
  196. package/src/lib/tree-sitter/cache.test.ts +273 -0
  197. package/src/lib/tree-sitter/client.test.ts +1165 -0
  198. package/src/lib/tree-sitter/client.ts +607 -0
  199. package/src/lib/tree-sitter/default-parsers.ts +86 -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 +1042 -0
  203. package/src/lib/tree-sitter/parsers-config.ts +81 -0
  204. package/src/lib/tree-sitter/resolve-ft.test.ts +55 -0
  205. package/src/lib/tree-sitter/resolve-ft.ts +189 -0
  206. package/src/lib/tree-sitter/types.ts +82 -0
  207. package/src/lib/tree-sitter-styled-text.test.ts +1253 -0
  208. package/src/lib/tree-sitter-styled-text.ts +306 -0
  209. package/src/lib/validate-dir-name.ts +55 -0
  210. package/src/lib/yoga.options.test.ts +628 -0
  211. package/src/lib/yoga.options.ts +346 -0
  212. package/src/plugins/core-slot.ts +579 -0
  213. package/src/plugins/registry.ts +402 -0
  214. package/src/plugins/types.ts +46 -0
  215. package/src/post/effects.ts +930 -0
  216. package/src/post/filters.ts +489 -0
  217. package/src/post/matrices.ts +288 -0
  218. package/src/renderables/ASCIIFont.ts +219 -0
  219. package/src/renderables/Box.test.ts +205 -0
  220. package/src/renderables/Box.ts +326 -0
  221. package/src/renderables/Code.test.ts +2062 -0
  222. package/src/renderables/Code.ts +357 -0
  223. package/src/renderables/Diff.regression.test.ts +226 -0
  224. package/src/renderables/Diff.test.ts +3101 -0
  225. package/src/renderables/Diff.ts +1211 -0
  226. package/src/renderables/EditBufferRenderable.test.ts +288 -0
  227. package/src/renderables/EditBufferRenderable.ts +1166 -0
  228. package/src/renderables/FrameBuffer.ts +47 -0
  229. package/src/renderables/Input.test.ts +1228 -0
  230. package/src/renderables/Input.ts +247 -0
  231. package/src/renderables/LineNumberRenderable.ts +724 -0
  232. package/src/renderables/Markdown.ts +1393 -0
  233. package/src/renderables/ScrollBar.ts +422 -0
  234. package/src/renderables/ScrollBox.ts +883 -0
  235. package/src/renderables/Select.test.ts +1033 -0
  236. package/src/renderables/Select.ts +524 -0
  237. package/src/renderables/Slider.test.ts +456 -0
  238. package/src/renderables/Slider.ts +342 -0
  239. package/src/renderables/TabSelect.test.ts +197 -0
  240. package/src/renderables/TabSelect.ts +455 -0
  241. package/src/renderables/Text.selection-buffer.test.ts +123 -0
  242. package/src/renderables/Text.test.ts +2660 -0
  243. package/src/renderables/Text.ts +147 -0
  244. package/src/renderables/TextBufferRenderable.ts +518 -0
  245. package/src/renderables/TextNode.test.ts +1058 -0
  246. package/src/renderables/TextNode.ts +325 -0
  247. package/src/renderables/TextTable.test.ts +1421 -0
  248. package/src/renderables/TextTable.ts +1344 -0
  249. package/src/renderables/Textarea.ts +430 -0
  250. package/src/renderables/TimeToFirstDraw.ts +89 -0
  251. package/src/renderables/__snapshots__/Code.test.ts.snap +13 -0
  252. package/src/renderables/__snapshots__/Diff.test.ts.snap +785 -0
  253. package/src/renderables/__snapshots__/Text.test.ts.snap +421 -0
  254. package/src/renderables/__snapshots__/TextTable.test.ts.snap +215 -0
  255. package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +144 -0
  256. package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +816 -0
  257. package/src/renderables/__tests__/LineNumberRenderable.test.ts +1865 -0
  258. package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +85 -0
  259. package/src/renderables/__tests__/Markdown.code-colors.test.ts +242 -0
  260. package/src/renderables/__tests__/Markdown.test.ts +2518 -0
  261. package/src/renderables/__tests__/MultiRenderable.selection.test.ts +87 -0
  262. package/src/renderables/__tests__/Textarea.buffer.test.ts +682 -0
  263. package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +675 -0
  264. package/src/renderables/__tests__/Textarea.editing.test.ts +2041 -0
  265. package/src/renderables/__tests__/Textarea.error-handling.test.ts +35 -0
  266. package/src/renderables/__tests__/Textarea.events.test.ts +738 -0
  267. package/src/renderables/__tests__/Textarea.highlights.test.ts +590 -0
  268. package/src/renderables/__tests__/Textarea.keybinding.test.ts +3149 -0
  269. package/src/renderables/__tests__/Textarea.paste.test.ts +357 -0
  270. package/src/renderables/__tests__/Textarea.rendering.test.ts +1866 -0
  271. package/src/renderables/__tests__/Textarea.scroll.test.ts +733 -0
  272. package/src/renderables/__tests__/Textarea.selection.test.ts +1590 -0
  273. package/src/renderables/__tests__/Textarea.stress.test.ts +670 -0
  274. package/src/renderables/__tests__/Textarea.undo-redo.test.ts +383 -0
  275. package/src/renderables/__tests__/Textarea.visual-lines.test.ts +310 -0
  276. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +221 -0
  277. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +89 -0
  278. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +457 -0
  279. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +158 -0
  280. package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +387 -0
  281. package/src/renderables/__tests__/markdown-parser.test.ts +217 -0
  282. package/src/renderables/__tests__/renderable-test-utils.ts +60 -0
  283. package/src/renderables/composition/README.md +8 -0
  284. package/src/renderables/composition/VRenderable.ts +32 -0
  285. package/src/renderables/composition/constructs.ts +127 -0
  286. package/src/renderables/composition/vnode.ts +289 -0
  287. package/src/renderables/index.ts +23 -0
  288. package/src/renderables/markdown-parser.ts +66 -0
  289. package/src/renderer.ts +2681 -0
  290. package/src/runtime-plugin-support.ts +39 -0
  291. package/src/runtime-plugin.ts +615 -0
  292. package/src/syntax-style.test.ts +841 -0
  293. package/src/syntax-style.ts +257 -0
  294. package/src/testing/README.md +210 -0
  295. package/src/testing/capture-spans.test.ts +194 -0
  296. package/src/testing/integration.test.ts +276 -0
  297. package/src/testing/manual-clock.ts +117 -0
  298. package/src/testing/mock-keys.test.ts +1378 -0
  299. package/src/testing/mock-keys.ts +457 -0
  300. package/src/testing/mock-mouse.test.ts +218 -0
  301. package/src/testing/mock-mouse.ts +247 -0
  302. package/src/testing/mock-tree-sitter-client.ts +73 -0
  303. package/src/testing/spy.ts +13 -0
  304. package/src/testing/test-recorder.test.ts +415 -0
  305. package/src/testing/test-recorder.ts +145 -0
  306. package/src/testing/test-renderer.ts +132 -0
  307. package/src/testing.ts +7 -0
  308. package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +481 -0
  309. package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +19 -0
  310. package/src/tests/__snapshots__/scrollbox.test.ts.snap +29 -0
  311. package/src/tests/absolute-positioning.snapshot.test.ts +638 -0
  312. package/src/tests/allocator-stats.test.ts +38 -0
  313. package/src/tests/destroy-during-render.test.ts +200 -0
  314. package/src/tests/destroy-on-exit.fixture.ts +36 -0
  315. package/src/tests/destroy-on-exit.test.ts +41 -0
  316. package/src/tests/hover-cursor.test.ts +98 -0
  317. package/src/tests/native-span-feed-async.test.ts +173 -0
  318. package/src/tests/native-span-feed-close.test.ts +120 -0
  319. package/src/tests/native-span-feed-coverage.test.ts +227 -0
  320. package/src/tests/native-span-feed-edge-cases.test.ts +352 -0
  321. package/src/tests/native-span-feed-use-after-free.test.ts +45 -0
  322. package/src/tests/opacity.test.ts +123 -0
  323. package/src/tests/renderable.snapshot.test.ts +524 -0
  324. package/src/tests/renderable.test.ts +1281 -0
  325. package/src/tests/renderer.clock.test.ts +158 -0
  326. package/src/tests/renderer.console-startup.test.ts +185 -0
  327. package/src/tests/renderer.control.test.ts +425 -0
  328. package/src/tests/renderer.core-slot-binding.test.ts +952 -0
  329. package/src/tests/renderer.cursor.test.ts +26 -0
  330. package/src/tests/renderer.destroy-during-render.test.ts +147 -0
  331. package/src/tests/renderer.focus-restore.test.ts +257 -0
  332. package/src/tests/renderer.focus.test.ts +294 -0
  333. package/src/tests/renderer.idle.test.ts +219 -0
  334. package/src/tests/renderer.input.test.ts +2237 -0
  335. package/src/tests/renderer.kitty-flags.test.ts +195 -0
  336. package/src/tests/renderer.mouse.test.ts +1274 -0
  337. package/src/tests/renderer.palette.test.ts +629 -0
  338. package/src/tests/renderer.selection.test.ts +49 -0
  339. package/src/tests/renderer.slot-registry.test.ts +684 -0
  340. package/src/tests/renderer.useMouse.test.ts +47 -0
  341. package/src/tests/runtime-plugin-node-modules-cycle.fixture.ts +76 -0
  342. package/src/tests/runtime-plugin-node-modules-mjs.fixture.ts +43 -0
  343. package/src/tests/runtime-plugin-node-modules-no-bare-rewrite.fixture.ts +67 -0
  344. package/src/tests/runtime-plugin-node-modules-package-type-cache.fixture.ts +72 -0
  345. package/src/tests/runtime-plugin-node-modules-runtime-specifier.fixture.ts +44 -0
  346. package/src/tests/runtime-plugin-node-modules-scoped-package-bare-rewrite.fixture.ts +85 -0
  347. package/src/tests/runtime-plugin-path-alias.fixture.ts +43 -0
  348. package/src/tests/runtime-plugin-resolve-roots.fixture.ts +65 -0
  349. package/src/tests/runtime-plugin-support.fixture.ts +11 -0
  350. package/src/tests/runtime-plugin-support.test.ts +19 -0
  351. package/src/tests/runtime-plugin-windows-file-url.fixture.ts +30 -0
  352. package/src/tests/runtime-plugin.fixture.ts +40 -0
  353. package/src/tests/runtime-plugin.test.ts +354 -0
  354. package/src/tests/scrollbox-culling-bug.test.ts +114 -0
  355. package/src/tests/scrollbox-hitgrid-resize.test.ts +136 -0
  356. package/src/tests/scrollbox-hitgrid.test.ts +909 -0
  357. package/src/tests/scrollbox.test.ts +1530 -0
  358. package/src/tests/wrap-resize-perf.test.ts +276 -0
  359. package/src/tests/yoga-setters.test.ts +921 -0
  360. package/src/text-buffer-view.test.ts +705 -0
  361. package/src/text-buffer-view.ts +189 -0
  362. package/src/text-buffer.test.ts +347 -0
  363. package/src/text-buffer.ts +250 -0
  364. package/src/types.ts +161 -0
  365. package/src/utils.ts +88 -0
  366. package/src/zig/ansi.zig +268 -0
  367. package/src/zig/bench/README.md +50 -0
  368. package/src/zig/bench/buffer-draw-text-buffer_bench.zig +887 -0
  369. package/src/zig/bench/edit-buffer_bench.zig +476 -0
  370. package/src/zig/bench/native-span-feed_bench.zig +100 -0
  371. package/src/zig/bench/rope-markers_bench.zig +713 -0
  372. package/src/zig/bench/rope_bench.zig +514 -0
  373. package/src/zig/bench/styled-text_bench.zig +470 -0
  374. package/src/zig/bench/text-buffer-coords_bench.zig +362 -0
  375. package/src/zig/bench/text-buffer-view_bench.zig +459 -0
  376. package/src/zig/bench/text-chunk-graphemes_bench.zig +273 -0
  377. package/src/zig/bench/utf8_bench.zig +799 -0
  378. package/src/zig/bench-utils.zig +431 -0
  379. package/src/zig/bench.zig +217 -0
  380. package/src/zig/buffer-methods.zig +211 -0
  381. package/src/zig/buffer.zig +2281 -0
  382. package/src/zig/build.zig +289 -0
  383. package/src/zig/build.zig.zon +16 -0
  384. package/src/zig/edit-buffer.zig +825 -0
  385. package/src/zig/editor-view.zig +802 -0
  386. package/src/zig/event-bus.zig +13 -0
  387. package/src/zig/event-emitter.zig +65 -0
  388. package/src/zig/file-logger.zig +92 -0
  389. package/src/zig/grapheme.zig +599 -0
  390. package/src/zig/lib.zig +1854 -0
  391. package/src/zig/link.zig +333 -0
  392. package/src/zig/logger.zig +43 -0
  393. package/src/zig/mem-registry.zig +125 -0
  394. package/src/zig/native-span-feed-bench-lib.zig +7 -0
  395. package/src/zig/native-span-feed.zig +708 -0
  396. package/src/zig/renderer.zig +1393 -0
  397. package/src/zig/rope.zig +1220 -0
  398. package/src/zig/syntax-style.zig +161 -0
  399. package/src/zig/terminal.zig +987 -0
  400. package/src/zig/test.zig +72 -0
  401. package/src/zig/tests/README.md +18 -0
  402. package/src/zig/tests/buffer-methods_test.zig +1109 -0
  403. package/src/zig/tests/buffer_test.zig +2557 -0
  404. package/src/zig/tests/edit-buffer-history_test.zig +271 -0
  405. package/src/zig/tests/edit-buffer_test.zig +1689 -0
  406. package/src/zig/tests/editor-view_test.zig +3299 -0
  407. package/src/zig/tests/event-emitter_test.zig +249 -0
  408. package/src/zig/tests/grapheme_test.zig +1304 -0
  409. package/src/zig/tests/link_test.zig +190 -0
  410. package/src/zig/tests/mem-registry_test.zig +473 -0
  411. package/src/zig/tests/memory_leak_regression_test.zig +159 -0
  412. package/src/zig/tests/native-span-feed_test.zig +1264 -0
  413. package/src/zig/tests/renderer_test.zig +1017 -0
  414. package/src/zig/tests/rope-nested_test.zig +712 -0
  415. package/src/zig/tests/rope_fuzz_test.zig +238 -0
  416. package/src/zig/tests/rope_test.zig +2362 -0
  417. package/src/zig/tests/segment-merge.test.zig +148 -0
  418. package/src/zig/tests/syntax-style_test.zig +557 -0
  419. package/src/zig/tests/terminal_test.zig +754 -0
  420. package/src/zig/tests/text-buffer-drawing_test.zig +3237 -0
  421. package/src/zig/tests/text-buffer-highlights_test.zig +666 -0
  422. package/src/zig/tests/text-buffer-iterators_test.zig +776 -0
  423. package/src/zig/tests/text-buffer-segment_test.zig +320 -0
  424. package/src/zig/tests/text-buffer-selection_test.zig +1035 -0
  425. package/src/zig/tests/text-buffer-selection_viewport_test.zig +358 -0
  426. package/src/zig/tests/text-buffer-view_test.zig +3649 -0
  427. package/src/zig/tests/text-buffer_test.zig +2191 -0
  428. package/src/zig/tests/unicode-width-map.zon +3909 -0
  429. package/src/zig/tests/utf8_no_zwj_test.zig +260 -0
  430. package/src/zig/tests/utf8_test.zig +4057 -0
  431. package/src/zig/tests/utf8_wcwidth_cursor_test.zig +267 -0
  432. package/src/zig/tests/utf8_wcwidth_test.zig +357 -0
  433. package/src/zig/tests/word-wrap-editing_test.zig +498 -0
  434. package/src/zig/tests/wrap-cache-perf_test.zig +113 -0
  435. package/src/zig/text-buffer-iterators.zig +499 -0
  436. package/src/zig/text-buffer-segment.zig +404 -0
  437. package/src/zig/text-buffer-view.zig +1371 -0
  438. package/src/zig/text-buffer.zig +1180 -0
  439. package/src/zig/utf8.zig +1948 -0
  440. package/src/zig/utils.zig +9 -0
  441. package/src/zig-structs.ts +261 -0
  442. package/src/zig.ts +3884 -0
  443. package/tsconfig.build.json +24 -0
  444. package/tsconfig.json +27 -0
  445. package/3d/SpriteResourceManager.d.ts +0 -74
  446. package/3d/SpriteUtils.d.ts +0 -13
  447. package/3d/TextureUtils.d.ts +0 -24
  448. package/3d/ThreeRenderable.d.ts +0 -40
  449. package/3d/WGPURenderer.d.ts +0 -61
  450. package/3d/animation/ExplodingSpriteEffect.d.ts +0 -71
  451. package/3d/animation/PhysicsExplodingSpriteEffect.d.ts +0 -76
  452. package/3d/animation/SpriteAnimator.d.ts +0 -124
  453. package/3d/animation/SpriteParticleGenerator.d.ts +0 -62
  454. package/3d/canvas.d.ts +0 -44
  455. package/3d/index.d.ts +0 -12
  456. package/3d/physics/PlanckPhysicsAdapter.d.ts +0 -19
  457. package/3d/physics/RapierPhysicsAdapter.d.ts +0 -19
  458. package/3d/physics/physics-interface.d.ts +0 -27
  459. package/3d.d.ts +0 -2
  460. package/3d.js +0 -34041
  461. package/3d.js.map +0 -155
  462. package/LICENSE +0 -21
  463. package/NativeSpanFeed.d.ts +0 -41
  464. package/Renderable.d.ts +0 -334
  465. package/animation/Timeline.d.ts +0 -126
  466. package/ansi.d.ts +0 -13
  467. package/buffer.d.ts +0 -111
  468. package/console.d.ts +0 -144
  469. package/edit-buffer.d.ts +0 -98
  470. package/editor-view.d.ts +0 -73
  471. package/index-9vwc3fg6.js +0 -12260
  472. package/index-9vwc3fg6.js.map +0 -42
  473. package/index-dcj62y8t.js +0 -20614
  474. package/index-dcj62y8t.js.map +0 -67
  475. package/index-f7n39gpy.js +0 -411
  476. package/index-f7n39gpy.js.map +0 -10
  477. package/index.d.ts +0 -23
  478. package/index.js +0 -478
  479. package/index.js.map +0 -9
  480. package/lib/KeyHandler.d.ts +0 -61
  481. package/lib/RGBA.d.ts +0 -25
  482. package/lib/ascii.font.d.ts +0 -508
  483. package/lib/border.d.ts +0 -51
  484. package/lib/bunfs.d.ts +0 -7
  485. package/lib/clipboard.d.ts +0 -17
  486. package/lib/clock.d.ts +0 -15
  487. package/lib/data-paths.d.ts +0 -26
  488. package/lib/debounce.d.ts +0 -42
  489. package/lib/detect-links.d.ts +0 -6
  490. package/lib/env.d.ts +0 -42
  491. package/lib/extmarks-history.d.ts +0 -17
  492. package/lib/extmarks.d.ts +0 -89
  493. package/lib/hast-styled-text.d.ts +0 -17
  494. package/lib/index.d.ts +0 -21
  495. package/lib/keymapping.d.ts +0 -25
  496. package/lib/objects-in-viewport.d.ts +0 -24
  497. package/lib/output.capture.d.ts +0 -24
  498. package/lib/parse.keypress-kitty.d.ts +0 -2
  499. package/lib/parse.keypress.d.ts +0 -26
  500. package/lib/parse.mouse.d.ts +0 -30
  501. package/lib/paste.d.ts +0 -7
  502. package/lib/queue.d.ts +0 -15
  503. package/lib/renderable.validations.d.ts +0 -12
  504. package/lib/scroll-acceleration.d.ts +0 -43
  505. package/lib/selection.d.ts +0 -63
  506. package/lib/singleton.d.ts +0 -7
  507. package/lib/stdin-parser.d.ts +0 -87
  508. package/lib/styled-text.d.ts +0 -63
  509. package/lib/terminal-capability-detection.d.ts +0 -30
  510. package/lib/terminal-palette.d.ts +0 -50
  511. package/lib/tree-sitter/assets/update.d.ts +0 -11
  512. package/lib/tree-sitter/client.d.ts +0 -47
  513. package/lib/tree-sitter/default-parsers.d.ts +0 -2
  514. package/lib/tree-sitter/download-utils.d.ts +0 -21
  515. package/lib/tree-sitter/index.d.ts +0 -8
  516. package/lib/tree-sitter/parser.worker.d.ts +0 -1
  517. package/lib/tree-sitter/parsers-config.d.ts +0 -53
  518. package/lib/tree-sitter/resolve-ft.d.ts +0 -5
  519. package/lib/tree-sitter/types.d.ts +0 -82
  520. package/lib/tree-sitter-styled-text.d.ts +0 -14
  521. package/lib/validate-dir-name.d.ts +0 -1
  522. package/lib/yoga.options.d.ts +0 -32
  523. package/parser.worker.js +0 -899
  524. package/parser.worker.js.map +0 -12
  525. package/plugins/core-slot.d.ts +0 -72
  526. package/plugins/registry.d.ts +0 -42
  527. package/plugins/types.d.ts +0 -34
  528. package/post/effects.d.ts +0 -147
  529. package/post/filters.d.ts +0 -65
  530. package/post/matrices.d.ts +0 -20
  531. package/renderables/ASCIIFont.d.ts +0 -52
  532. package/renderables/Box.d.ts +0 -81
  533. package/renderables/Code.d.ts +0 -78
  534. package/renderables/Diff.d.ts +0 -142
  535. package/renderables/EditBufferRenderable.d.ts +0 -237
  536. package/renderables/FrameBuffer.d.ts +0 -16
  537. package/renderables/Input.d.ts +0 -67
  538. package/renderables/LineNumberRenderable.d.ts +0 -78
  539. package/renderables/Markdown.d.ts +0 -185
  540. package/renderables/ScrollBar.d.ts +0 -77
  541. package/renderables/ScrollBox.d.ts +0 -124
  542. package/renderables/Select.d.ts +0 -115
  543. package/renderables/Slider.d.ts +0 -47
  544. package/renderables/TabSelect.d.ts +0 -96
  545. package/renderables/Text.d.ts +0 -36
  546. package/renderables/TextBufferRenderable.d.ts +0 -105
  547. package/renderables/TextNode.d.ts +0 -91
  548. package/renderables/TextTable.d.ts +0 -140
  549. package/renderables/Textarea.d.ts +0 -63
  550. package/renderables/TimeToFirstDraw.d.ts +0 -24
  551. package/renderables/__tests__/renderable-test-utils.d.ts +0 -12
  552. package/renderables/composition/VRenderable.d.ts +0 -16
  553. package/renderables/composition/constructs.d.ts +0 -35
  554. package/renderables/composition/vnode.d.ts +0 -46
  555. package/renderables/index.d.ts +0 -23
  556. package/renderables/markdown-parser.d.ts +0 -10
  557. package/renderer.d.ts +0 -419
  558. package/runtime-plugin-support.d.ts +0 -3
  559. package/runtime-plugin-support.js +0 -29
  560. package/runtime-plugin-support.js.map +0 -10
  561. package/runtime-plugin.d.ts +0 -16
  562. package/runtime-plugin.js +0 -16
  563. package/runtime-plugin.js.map +0 -9
  564. package/syntax-style.d.ts +0 -54
  565. package/testing/manual-clock.d.ts +0 -17
  566. package/testing/mock-keys.d.ts +0 -81
  567. package/testing/mock-mouse.d.ts +0 -38
  568. package/testing/mock-tree-sitter-client.d.ts +0 -23
  569. package/testing/spy.d.ts +0 -7
  570. package/testing/test-recorder.d.ts +0 -61
  571. package/testing/test-renderer.d.ts +0 -23
  572. package/testing.d.ts +0 -6
  573. package/testing.js +0 -697
  574. package/testing.js.map +0 -15
  575. package/text-buffer-view.d.ts +0 -42
  576. package/text-buffer.d.ts +0 -67
  577. package/types.d.ts +0 -139
  578. package/utils.d.ts +0 -14
  579. package/zig-structs.d.ts +0 -155
  580. package/zig.d.ts +0 -353
  581. /package/{assets → src/lib/tree-sitter/assets}/javascript/highlights.scm +0 -0
  582. /package/{assets → src/lib/tree-sitter/assets}/javascript/tree-sitter-javascript.wasm +0 -0
  583. /package/{assets → src/lib/tree-sitter/assets}/markdown/highlights.scm +0 -0
  584. /package/{assets → src/lib/tree-sitter/assets}/markdown/injections.scm +0 -0
  585. /package/{assets → src/lib/tree-sitter/assets}/markdown/tree-sitter-markdown.wasm +0 -0
  586. /package/{assets → src/lib/tree-sitter/assets}/markdown_inline/highlights.scm +0 -0
  587. /package/{assets → src/lib/tree-sitter/assets}/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
  588. /package/{assets → src/lib/tree-sitter/assets}/typescript/highlights.scm +0 -0
  589. /package/{assets → src/lib/tree-sitter/assets}/typescript/tree-sitter-typescript.wasm +0 -0
  590. /package/{assets → src/lib/tree-sitter/assets}/zig/highlights.scm +0 -0
  591. /package/{assets → src/lib/tree-sitter/assets}/zig/tree-sitter-zig.wasm +0 -0
@@ -0,0 +1,909 @@
1
+ import { test, expect, beforeEach, afterEach } from "bun:test"
2
+ import { createTestRenderer, MouseButtons, 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
+ let testRenderer: TestRenderer
8
+ let mockMouse: MockMouse
9
+
10
+ class MovingBoxRenderable extends BoxRenderable {
11
+ public shouldMove = false
12
+
13
+ protected override onUpdate(_deltaTime: number): void {
14
+ if (this.shouldMove) {
15
+ this.shouldMove = false
16
+ this.translateY = 3
17
+ }
18
+ }
19
+ }
20
+
21
+ beforeEach(async () => {
22
+ ;({ renderer: testRenderer, mockMouse } = await createTestRenderer({
23
+ width: 50,
24
+ height: 30,
25
+ }))
26
+ })
27
+
28
+ afterEach(() => {
29
+ testRenderer.destroy()
30
+ })
31
+
32
+ test("hit grid updates after render when scrollbox scrolls", async () => {
33
+ const scrollBox = new ScrollBoxRenderable(testRenderer, {
34
+ width: 40,
35
+ height: 20,
36
+ scrollY: true,
37
+ })
38
+ testRenderer.root.add(scrollBox)
39
+
40
+ const items: BoxRenderable[] = []
41
+ for (let i = 0; i < 30; i++) {
42
+ const item = new BoxRenderable(testRenderer, {
43
+ id: `item-${i}`,
44
+ height: 2,
45
+ backgroundColor: i % 2 === 0 ? "red" : "blue",
46
+ })
47
+ items.push(item)
48
+ scrollBox.add(item)
49
+ }
50
+
51
+ await testRenderer.idle()
52
+
53
+ const item0 = items[0]
54
+ const item4 = items[4]
55
+
56
+ expect(item0.y).toBe(0)
57
+ expect(item4.y).toBe(8)
58
+
59
+ const checkHitAt = (x: number, y: number): Renderable | undefined => {
60
+ const renderableId = testRenderer.hitTest(x, y)
61
+ return Renderable.renderablesByNumber.get(renderableId)
62
+ }
63
+
64
+ let hitAtItem0 = checkHitAt(5, item0.y)
65
+ expect(hitAtItem0?.id).toBe("item-0")
66
+
67
+ let hitAtItem4 = checkHitAt(5, item4.y)
68
+ expect(hitAtItem4?.id).toBe("item-4")
69
+
70
+ scrollBox.scrollTop = 10
71
+
72
+ expect(item0.y).toBe(-10)
73
+ expect(item4.y).toBe(-2)
74
+
75
+ const item5 = items[5]
76
+ const item9 = items[9]
77
+
78
+ expect(item5.y).toBe(0)
79
+ expect(item9.y).toBe(8)
80
+
81
+ // Hit grid updates after render
82
+ await testRenderer.idle()
83
+
84
+ const hitAtItem5 = checkHitAt(5, item5.y)
85
+ expect(hitAtItem5?.id).toBe("item-5")
86
+
87
+ const hitAtItem9 = checkHitAt(5, item9.y)
88
+ expect(hitAtItem9?.id).toBe("item-9")
89
+ })
90
+
91
+ test("hover updates after scroll when pointer moves", async () => {
92
+ const scrollBox = new ScrollBoxRenderable(testRenderer, {
93
+ width: 20,
94
+ height: 6,
95
+ scrollY: true,
96
+ })
97
+ testRenderer.root.add(scrollBox)
98
+
99
+ const hoverEvents: string[] = []
100
+ let hoveredId: string | null = null
101
+
102
+ const items: BoxRenderable[] = []
103
+ for (let i = 0; i < 5; i++) {
104
+ const itemId = `item-${i}`
105
+ const item = new BoxRenderable(testRenderer, {
106
+ id: itemId,
107
+ width: "100%",
108
+ height: 2,
109
+ onMouseOver: () => {
110
+ hoveredId = itemId
111
+ hoverEvents.push(`over:${itemId}`)
112
+ },
113
+ onMouseOut: () => {
114
+ if (hoveredId === itemId) {
115
+ hoveredId = null
116
+ }
117
+ hoverEvents.push(`out:${itemId}`)
118
+ },
119
+ })
120
+ items.push(item)
121
+ scrollBox.add(item)
122
+ }
123
+
124
+ await testRenderer.idle()
125
+
126
+ const pointerX = items[0].x + 1
127
+ const pointerY = items[0].y + 1
128
+
129
+ await mockMouse.moveTo(pointerX, pointerY)
130
+ expect(hoveredId).toBe("item-0")
131
+ expect(hoverEvents).toEqual(["over:item-0"])
132
+
133
+ scrollBox.scrollTop = 2
134
+ await testRenderer.idle()
135
+
136
+ // Hover updates when pointer moves after scroll and render
137
+ await mockMouse.moveTo(pointerX, pointerY)
138
+ expect(hoveredId).toBe("item-1")
139
+ expect(hoverEvents).toEqual(["over:item-0", "out:item-0", "over:item-1"])
140
+ })
141
+
142
+ test("hover updates after scroll without pointer movement", async () => {
143
+ const scrollBox = new ScrollBoxRenderable(testRenderer, {
144
+ width: 20,
145
+ height: 6,
146
+ scrollY: true,
147
+ })
148
+ testRenderer.root.add(scrollBox)
149
+
150
+ const hoverEvents: string[] = []
151
+ let hoveredId: string | null = null
152
+
153
+ const items: BoxRenderable[] = []
154
+ for (let i = 0; i < 5; i++) {
155
+ const itemId = `item-${i}`
156
+ const item = new BoxRenderable(testRenderer, {
157
+ id: itemId,
158
+ width: "100%",
159
+ height: 2,
160
+ onMouseOver: () => {
161
+ hoveredId = itemId
162
+ hoverEvents.push(`over:${itemId}`)
163
+ },
164
+ onMouseOut: () => {
165
+ if (hoveredId === itemId) {
166
+ hoveredId = null
167
+ }
168
+ hoverEvents.push(`out:${itemId}`)
169
+ },
170
+ })
171
+ items.push(item)
172
+ scrollBox.add(item)
173
+ }
174
+
175
+ await testRenderer.idle()
176
+
177
+ const pointerX = items[0].x + 1
178
+ const pointerY = items[0].y + 1
179
+
180
+ await mockMouse.moveTo(pointerX, pointerY)
181
+ expect(hoveredId).toBe("item-0")
182
+ expect(hoverEvents).toEqual(["over:item-0"])
183
+
184
+ scrollBox.scrollTop = 2
185
+ await testRenderer.idle()
186
+
187
+ expect(hoveredId).toBe("item-1")
188
+ expect(hoverEvents).toEqual(["over:item-0", "out:item-0", "over:item-1"])
189
+ })
190
+
191
+ test("hover recheck uses neutral button and modifiers", async () => {
192
+ const scrollBox = new ScrollBoxRenderable(testRenderer, {
193
+ width: 20,
194
+ height: 6,
195
+ scrollY: true,
196
+ })
197
+ testRenderer.root.add(scrollBox)
198
+
199
+ const hoverEvents: Array<{
200
+ type: "over" | "out"
201
+ button: number
202
+ modifiers: { shift: boolean; alt: boolean; ctrl: boolean }
203
+ }> = []
204
+ let hoveredId: string | null = null
205
+
206
+ const items: BoxRenderable[] = []
207
+ for (let i = 0; i < 5; i++) {
208
+ const itemId = `item-${i}`
209
+ const item = new BoxRenderable(testRenderer, {
210
+ id: itemId,
211
+ width: "100%",
212
+ height: 2,
213
+ onMouseOver: (event) => {
214
+ hoveredId = itemId
215
+ hoverEvents.push({
216
+ type: "over",
217
+ button: event.button,
218
+ modifiers: { ...event.modifiers },
219
+ })
220
+ },
221
+ onMouseOut: (event) => {
222
+ if (hoveredId === itemId) {
223
+ hoveredId = null
224
+ }
225
+ hoverEvents.push({
226
+ type: "out",
227
+ button: event.button,
228
+ modifiers: { ...event.modifiers },
229
+ })
230
+ },
231
+ })
232
+ items.push(item)
233
+ scrollBox.add(item)
234
+ }
235
+
236
+ await testRenderer.idle()
237
+
238
+ const pointerX = items[0].x + 1
239
+ const pointerY = items[0].y + 1
240
+
241
+ await mockMouse.moveTo(pointerX, pointerY, { modifiers: { shift: true } })
242
+ expect(hoveredId).toBe("item-0")
243
+
244
+ await mockMouse.pressDown(pointerX, pointerY, MouseButtons.RIGHT, { modifiers: { shift: true } })
245
+
246
+ scrollBox.scrollTop = 2
247
+ await testRenderer.idle()
248
+
249
+ expect(hoveredId).toBe("item-1")
250
+ expect(hoverEvents).toHaveLength(3)
251
+ const outEvent = hoverEvents[1]
252
+ const overEvent = hoverEvents[2]
253
+ // Synthetic hover recheck uses neutral button but preserves last known modifiers
254
+ expect(outEvent.button).toBe(0)
255
+ expect(outEvent.modifiers).toEqual({ shift: true, alt: false, ctrl: false })
256
+ expect(overEvent.button).toBe(0)
257
+ expect(overEvent.modifiers).toEqual({ shift: true, alt: false, ctrl: false })
258
+ })
259
+
260
+ test("hover recheck over event has no source when not dragging", async () => {
261
+ const scrollBox = new ScrollBoxRenderable(testRenderer, {
262
+ width: 20,
263
+ height: 6,
264
+ scrollY: true,
265
+ })
266
+ testRenderer.root.add(scrollBox)
267
+
268
+ const hoverEvents: Array<{
269
+ type: "over" | "out"
270
+ source: Renderable | undefined
271
+ }> = []
272
+
273
+ const items: BoxRenderable[] = []
274
+ for (let i = 0; i < 5; i++) {
275
+ const itemId = `item-${i}`
276
+ const item = new BoxRenderable(testRenderer, {
277
+ id: itemId,
278
+ width: "100%",
279
+ height: 2,
280
+ onMouseOver: (event) => {
281
+ hoverEvents.push({
282
+ type: "over",
283
+ source: event.source,
284
+ })
285
+ },
286
+ onMouseOut: (event) => {
287
+ hoverEvents.push({
288
+ type: "out",
289
+ source: event.source,
290
+ })
291
+ },
292
+ })
293
+ items.push(item)
294
+ scrollBox.add(item)
295
+ }
296
+
297
+ await testRenderer.idle()
298
+
299
+ const pointerX = items[0].x + 1
300
+ const pointerY = items[0].y + 1
301
+
302
+ // Move to item-0 (not dragging)
303
+ await mockMouse.moveTo(pointerX, pointerY)
304
+ expect(hoverEvents).toHaveLength(1)
305
+ expect(hoverEvents[0].type).toBe("over")
306
+ expect(hoverEvents[0].source).toBeUndefined()
307
+
308
+ // Scroll to trigger hover recheck - should have no source since we're not dragging
309
+ scrollBox.scrollTop = 2
310
+ await testRenderer.idle()
311
+
312
+ expect(hoverEvents).toHaveLength(3)
313
+ // out event from item-0
314
+ expect(hoverEvents[1].type).toBe("out")
315
+ expect(hoverEvents[1].source).toBeUndefined()
316
+ // over event to item-1 - source should be undefined (not dragging)
317
+ expect(hoverEvents[2].type).toBe("over")
318
+ expect(hoverEvents[2].source).toBeUndefined()
319
+ })
320
+
321
+ test("hover updates on multiple scroll changes", async () => {
322
+ const scrollBox = new ScrollBoxRenderable(testRenderer, {
323
+ width: 20,
324
+ height: 6,
325
+ scrollY: true,
326
+ })
327
+ testRenderer.root.add(scrollBox)
328
+
329
+ const hoverEvents: string[] = []
330
+ let hoveredId: string | null = null
331
+
332
+ const items: BoxRenderable[] = []
333
+ for (let i = 0; i < 5; i++) {
334
+ const itemId = `item-${i}`
335
+ const item = new BoxRenderable(testRenderer, {
336
+ id: itemId,
337
+ width: "100%",
338
+ height: 2,
339
+ onMouseOver: () => {
340
+ hoveredId = itemId
341
+ hoverEvents.push(`over:${itemId}`)
342
+ },
343
+ onMouseOut: () => {
344
+ if (hoveredId === itemId) {
345
+ hoveredId = null
346
+ }
347
+ hoverEvents.push(`out:${itemId}`)
348
+ },
349
+ })
350
+ items.push(item)
351
+ scrollBox.add(item)
352
+ }
353
+
354
+ await testRenderer.idle()
355
+
356
+ const pointerX = items[0].x + 1
357
+ const pointerY = items[0].y + 1
358
+
359
+ await mockMouse.moveTo(pointerX, pointerY)
360
+ expect(hoveredId).toBe("item-0")
361
+ expect(hoverEvents).toEqual(["over:item-0"])
362
+
363
+ // First scroll - hover recheck happens immediately after render
364
+ scrollBox.scrollTop = 2
365
+ await testRenderer.idle()
366
+ expect(hoveredId).toBe("item-1")
367
+
368
+ // Second scroll - another immediate hover recheck
369
+ scrollBox.scrollTop = 4
370
+ await testRenderer.idle()
371
+
372
+ expect(hoveredId).toBe("item-2")
373
+ // Each render triggers immediate hover recheck, so we see all transitions
374
+ expect(hoverEvents).toEqual(["over:item-0", "out:item-0", "over:item-1", "out:item-1", "over:item-2"])
375
+ })
376
+
377
+ test("mouse move during scroll triggers normal hover", async () => {
378
+ const scrollBox = new ScrollBoxRenderable(testRenderer, {
379
+ width: 20,
380
+ height: 6,
381
+ scrollY: true,
382
+ })
383
+ testRenderer.root.add(scrollBox)
384
+
385
+ const hoverEvents: string[] = []
386
+ let hoveredId: string | null = null
387
+
388
+ const items: BoxRenderable[] = []
389
+ for (let i = 0; i < 5; i++) {
390
+ const itemId = `item-${i}`
391
+ const item = new BoxRenderable(testRenderer, {
392
+ id: itemId,
393
+ width: "100%",
394
+ height: 2,
395
+ onMouseOver: () => {
396
+ hoveredId = itemId
397
+ hoverEvents.push(`over:${itemId}`)
398
+ },
399
+ onMouseOut: () => {
400
+ if (hoveredId === itemId) {
401
+ hoveredId = null
402
+ }
403
+ hoverEvents.push(`out:${itemId}`)
404
+ },
405
+ })
406
+ items.push(item)
407
+ scrollBox.add(item)
408
+ }
409
+
410
+ await testRenderer.idle()
411
+
412
+ const pointerX = items[0].x + 1
413
+ const pointerY = items[0].y + 1
414
+
415
+ await mockMouse.moveTo(pointerX, pointerY)
416
+ expect(hoveredId).toBe("item-0")
417
+ expect(hoverEvents).toEqual(["over:item-0"])
418
+
419
+ // Scroll triggers render which triggers immediate hover recheck
420
+ scrollBox.scrollTop = 2
421
+ await testRenderer.idle()
422
+ expect(hoveredId).toBe("item-1")
423
+ expect(hoverEvents).toEqual(["over:item-0", "out:item-0", "over:item-1"])
424
+
425
+ // Mouse move also works and doesn't duplicate events since we're already on item-1
426
+ await mockMouse.moveTo(pointerX, pointerY)
427
+ expect(hoveredId).toBe("item-1")
428
+ expect(hoverEvents).toEqual(["over:item-0", "out:item-0", "over:item-1"])
429
+ })
430
+
431
+ test("hover updates immediately after render", async () => {
432
+ const scrollBox = new ScrollBoxRenderable(testRenderer, {
433
+ width: 20,
434
+ height: 6,
435
+ scrollY: true,
436
+ })
437
+ testRenderer.root.add(scrollBox)
438
+
439
+ let hoveredId: string | null = null
440
+
441
+ const items: BoxRenderable[] = []
442
+ for (let i = 0; i < 5; i++) {
443
+ const itemId = `item-${i}`
444
+ const item = new BoxRenderable(testRenderer, {
445
+ id: itemId,
446
+ width: "100%",
447
+ height: 2,
448
+ onMouseOver: () => {
449
+ hoveredId = itemId
450
+ },
451
+ onMouseOut: () => {
452
+ if (hoveredId === itemId) {
453
+ hoveredId = null
454
+ }
455
+ },
456
+ })
457
+ items.push(item)
458
+ scrollBox.add(item)
459
+ }
460
+
461
+ await testRenderer.idle()
462
+
463
+ const pointerX = items[0].x + 1
464
+ const pointerY = items[0].y + 1
465
+
466
+ await mockMouse.moveTo(pointerX, pointerY)
467
+ expect(hoveredId).toBe("item-0")
468
+
469
+ // Hover updates immediately after render - no delay needed
470
+ scrollBox.scrollTop = 2
471
+ await testRenderer.idle()
472
+ expect(hoveredId).toBe("item-1")
473
+ })
474
+
475
+ test("hit grid handles multiple scroll operations correctly", async () => {
476
+ const scrollBox = new ScrollBoxRenderable(testRenderer, {
477
+ width: 40,
478
+ height: 20,
479
+ scrollY: true,
480
+ })
481
+ testRenderer.root.add(scrollBox)
482
+
483
+ const items: BoxRenderable[] = []
484
+ for (let i = 0; i < 40; i++) {
485
+ const item = new BoxRenderable(testRenderer, {
486
+ id: `item-${i}`,
487
+ height: 2,
488
+ })
489
+ items.push(item)
490
+ scrollBox.add(item)
491
+ }
492
+
493
+ await testRenderer.idle()
494
+
495
+ const checkHitAt = (x: number, y: number): Renderable | undefined => {
496
+ const renderableId = testRenderer.hitTest(x, y)
497
+ return Renderable.renderablesByNumber.get(renderableId)
498
+ }
499
+
500
+ scrollBox.scrollTop = 20
501
+ expect(items[10].y).toBe(0)
502
+ await testRenderer.idle()
503
+ let hit = checkHitAt(5, items[10].y)
504
+ expect(hit?.id).toBe("item-10")
505
+
506
+ scrollBox.scrollTop = 40
507
+ expect(items[20].y).toBe(0)
508
+ await testRenderer.idle()
509
+ hit = checkHitAt(5, items[20].y)
510
+ expect(hit?.id).toBe("item-20")
511
+
512
+ scrollBox.scrollTop = 0
513
+ expect(items[0].y).toBe(0)
514
+ await testRenderer.idle()
515
+ hit = checkHitAt(5, items[0].y)
516
+ expect(hit?.id).toBe("item-0")
517
+ })
518
+
519
+ test("hit grid respects scrollbox viewport clipping when offset", async () => {
520
+ const container = new BoxRenderable(testRenderer, {
521
+ flexDirection: "column",
522
+ width: "100%",
523
+ height: "100%",
524
+ })
525
+ testRenderer.root.add(container)
526
+
527
+ const header = new BoxRenderable(testRenderer, {
528
+ id: "header",
529
+ height: 5,
530
+ width: "100%",
531
+ })
532
+ container.add(header)
533
+
534
+ const scrollBox = new ScrollBoxRenderable(testRenderer, {
535
+ width: 40,
536
+ height: 10,
537
+ scrollY: true,
538
+ })
539
+ container.add(scrollBox)
540
+
541
+ const items: BoxRenderable[] = []
542
+ for (let i = 0; i < 10; i++) {
543
+ const item = new BoxRenderable(testRenderer, {
544
+ id: `item-${i}`,
545
+ height: 2,
546
+ })
547
+ items.push(item)
548
+ scrollBox.add(item)
549
+ }
550
+
551
+ await testRenderer.idle()
552
+
553
+ const checkHitAt = (x: number, y: number): Renderable | undefined => {
554
+ const renderableId = testRenderer.hitTest(x, y)
555
+ return Renderable.renderablesByNumber.get(renderableId)
556
+ }
557
+
558
+ const headerHit = checkHitAt(2, header.y + 1)
559
+ expect(headerHit?.id).toBe("header")
560
+
561
+ scrollBox.scrollTop = 4
562
+ await testRenderer.idle()
563
+
564
+ const headerHitAfterScroll = checkHitAt(2, header.y + 1)
565
+ expect(headerHitAfterScroll?.id).toBe("header")
566
+
567
+ const viewportHit = checkHitAt(2, scrollBox.viewport.y + 1)
568
+ expect(viewportHit?.id).toBe("item-2")
569
+ })
570
+
571
+ test("hover recheck skips while dragging captured renderable", async () => {
572
+ const scrollBox = new ScrollBoxRenderable(testRenderer, {
573
+ width: 20,
574
+ height: 6,
575
+ scrollY: true,
576
+ })
577
+ testRenderer.root.add(scrollBox)
578
+
579
+ const hoverEvents: string[] = []
580
+
581
+ const items: BoxRenderable[] = []
582
+ for (let i = 0; i < 5; i++) {
583
+ const itemId = `item-${i}`
584
+ const item = new BoxRenderable(testRenderer, {
585
+ id: itemId,
586
+ width: "100%",
587
+ height: 2,
588
+ onMouseOver: () => {
589
+ hoverEvents.push(`over:${itemId}`)
590
+ },
591
+ onMouseOut: () => {
592
+ hoverEvents.push(`out:${itemId}`)
593
+ },
594
+ })
595
+ items.push(item)
596
+ scrollBox.add(item)
597
+ }
598
+
599
+ await testRenderer.idle()
600
+
601
+ const pointerX = items[0].x + 1
602
+ const pointerY = items[0].y + 1
603
+
604
+ await mockMouse.moveTo(pointerX, pointerY)
605
+ await mockMouse.pressDown(pointerX, pointerY)
606
+ await mockMouse.moveTo(pointerX, pointerY)
607
+
608
+ scrollBox.scrollTop = 2
609
+ await testRenderer.idle()
610
+
611
+ // Hover recheck is skipped when there's a captured renderable (during drag)
612
+ expect(hoverEvents).toEqual(["over:item-0"])
613
+ })
614
+
615
+ test("captured renderable is not in hit grid during scroll", async () => {
616
+ const scrollBox = new ScrollBoxRenderable(testRenderer, {
617
+ width: 40,
618
+ height: 10,
619
+ scrollY: true,
620
+ })
621
+ testRenderer.root.add(scrollBox)
622
+
623
+ const items: BoxRenderable[] = []
624
+ for (let i = 0; i < 20; i++) {
625
+ const item = new BoxRenderable(testRenderer, {
626
+ id: `item-${i}`,
627
+ height: 2,
628
+ })
629
+ items.push(item)
630
+ scrollBox.add(item)
631
+ }
632
+
633
+ await testRenderer.idle()
634
+
635
+ const pointerX = 2
636
+ const pointerY = scrollBox.viewport.y + 1
637
+
638
+ await mockMouse.pressDown(pointerX, pointerY)
639
+ await mockMouse.moveTo(pointerX, pointerY + 1)
640
+
641
+ scrollBox.scrollTop = 4
642
+ await testRenderer.idle()
643
+
644
+ const renderableId = testRenderer.hitTest(pointerX, pointerY)
645
+ const hit = Renderable.renderablesByNumber.get(renderableId)
646
+ expect(hit?.id).toBe("item-2")
647
+ })
648
+
649
+ test("hit grid stays clipped after render", async () => {
650
+ const container = new BoxRenderable(testRenderer, {
651
+ id: "container",
652
+ width: 10,
653
+ height: 4,
654
+ overflow: "hidden",
655
+ })
656
+ testRenderer.root.add(container)
657
+
658
+ const child = new BoxRenderable(testRenderer, {
659
+ id: "child",
660
+ width: 20,
661
+ height: 4,
662
+ })
663
+ container.add(child)
664
+
665
+ await testRenderer.idle()
666
+
667
+ const insideHitId = testRenderer.hitTest(container.x + 1, container.y + 1)
668
+ const insideHit = Renderable.renderablesByNumber.get(insideHitId)
669
+ expect(insideHit?.id).toBe("child")
670
+
671
+ const outsideHitId = testRenderer.hitTest(container.x + container.width + 1, container.y + 1)
672
+ expect(outsideHitId).toBe(0)
673
+ })
674
+
675
+ test("buffered overflow scissor uses screen coordinates for hit grid", async () => {
676
+ const container = new BoxRenderable(testRenderer, {
677
+ id: "buffered-container",
678
+ width: 10,
679
+ height: 4,
680
+ overflow: "hidden",
681
+ buffered: true,
682
+ position: "absolute",
683
+ left: 10,
684
+ top: 5,
685
+ })
686
+ testRenderer.root.add(container)
687
+
688
+ const child = new BoxRenderable(testRenderer, {
689
+ id: "buffered-child",
690
+ width: 10,
691
+ height: 4,
692
+ })
693
+ container.add(child)
694
+
695
+ await testRenderer.idle()
696
+
697
+ const hitId = testRenderer.hitTest(container.x + 1, container.y + 1)
698
+ const hit = Renderable.renderablesByNumber.get(hitId)
699
+ expect(hit?.id).toBe("buffered-child")
700
+ })
701
+
702
+ test("hover updates after translate animation", async () => {
703
+ const hoverEvents: string[] = []
704
+ let hoveredId: string | null = null
705
+
706
+ const under = new BoxRenderable(testRenderer, {
707
+ id: "under",
708
+ position: "absolute",
709
+ left: 2,
710
+ top: 2,
711
+ width: 6,
712
+ height: 2,
713
+ zIndex: 0,
714
+ onMouseOver: () => {
715
+ hoveredId = "under"
716
+ hoverEvents.push("over:under")
717
+ },
718
+ onMouseOut: () => {
719
+ if (hoveredId === "under") {
720
+ hoveredId = null
721
+ }
722
+ hoverEvents.push("out:under")
723
+ },
724
+ })
725
+ testRenderer.root.add(under)
726
+
727
+ const moving = new MovingBoxRenderable(testRenderer, {
728
+ id: "moving",
729
+ position: "absolute",
730
+ left: 2,
731
+ top: 2,
732
+ width: 6,
733
+ height: 2,
734
+ zIndex: 1,
735
+ onMouseOver: () => {
736
+ hoveredId = "moving"
737
+ hoverEvents.push("over:moving")
738
+ },
739
+ onMouseOut: () => {
740
+ if (hoveredId === "moving") {
741
+ hoveredId = null
742
+ }
743
+ hoverEvents.push("out:moving")
744
+ },
745
+ })
746
+ testRenderer.root.add(moving)
747
+
748
+ await testRenderer.idle()
749
+
750
+ const pointerX = moving.x + 1
751
+ const pointerY = moving.y + 1
752
+
753
+ await mockMouse.moveTo(pointerX, pointerY)
754
+ expect(hoveredId).toBe("moving")
755
+ expect(hoverEvents).toEqual(["over:moving"])
756
+
757
+ moving.shouldMove = true
758
+ moving.requestRender()
759
+ await testRenderer.idle()
760
+
761
+ expect(hoveredId).toBe("under")
762
+ expect(hoverEvents).toEqual(["over:moving", "out:moving", "over:under"])
763
+ })
764
+
765
+ test("hover updates after z-index change", async () => {
766
+ const hoverEvents: string[] = []
767
+ let hoveredId: string | null = null
768
+
769
+ const back = new BoxRenderable(testRenderer, {
770
+ id: "back",
771
+ position: "absolute",
772
+ left: 2,
773
+ top: 2,
774
+ width: 6,
775
+ height: 2,
776
+ zIndex: 0,
777
+ onMouseOver: () => {
778
+ hoveredId = "back"
779
+ hoverEvents.push("over:back")
780
+ },
781
+ onMouseOut: () => {
782
+ if (hoveredId === "back") {
783
+ hoveredId = null
784
+ }
785
+ hoverEvents.push("out:back")
786
+ },
787
+ })
788
+ testRenderer.root.add(back)
789
+
790
+ const front = new BoxRenderable(testRenderer, {
791
+ id: "front",
792
+ position: "absolute",
793
+ left: 2,
794
+ top: 2,
795
+ width: 6,
796
+ height: 2,
797
+ zIndex: 1,
798
+ onMouseOver: () => {
799
+ hoveredId = "front"
800
+ hoverEvents.push("over:front")
801
+ },
802
+ onMouseOut: () => {
803
+ if (hoveredId === "front") {
804
+ hoveredId = null
805
+ }
806
+ hoverEvents.push("out:front")
807
+ },
808
+ })
809
+ testRenderer.root.add(front)
810
+
811
+ await testRenderer.idle()
812
+
813
+ const pointerX = front.x + 1
814
+ const pointerY = front.y + 1
815
+
816
+ await mockMouse.moveTo(pointerX, pointerY)
817
+ expect(hoveredId).toBe("front")
818
+ expect(hoverEvents).toEqual(["over:front"])
819
+
820
+ back.zIndex = 2
821
+ await testRenderer.idle()
822
+
823
+ expect(hoveredId).toBe("back")
824
+ expect(hoverEvents).toEqual(["over:front", "out:front", "over:back"])
825
+ })
826
+
827
+ test("scrolling does not steal clicks outside the list", async () => {
828
+ let lastClick = "none"
829
+
830
+ const overlay = new BoxRenderable(testRenderer, {
831
+ id: "overlay",
832
+ position: "absolute",
833
+ left: 0,
834
+ top: 0,
835
+ width: "100%",
836
+ height: "100%",
837
+ zIndex: 100,
838
+ onMouseDown: () => {
839
+ lastClick = "overlay"
840
+ },
841
+ })
842
+ testRenderer.root.add(overlay)
843
+
844
+ const dialog = new BoxRenderable(testRenderer, {
845
+ id: "dialog",
846
+ position: "absolute",
847
+ left: 5,
848
+ top: 4,
849
+ width: 30,
850
+ height: 14,
851
+ flexDirection: "column",
852
+ padding: 1,
853
+ gap: 1,
854
+ onMouseDown: (event) => {
855
+ lastClick = "dialog"
856
+ event.stopPropagation()
857
+ },
858
+ })
859
+ overlay.add(dialog)
860
+
861
+ const header = new BoxRenderable(testRenderer, {
862
+ id: "dialog-header",
863
+ width: "100%",
864
+ height: 2,
865
+ onMouseDown: (event) => {
866
+ lastClick = "header"
867
+ event.stopPropagation()
868
+ },
869
+ })
870
+ dialog.add(header)
871
+
872
+ const scrollBox = new ScrollBoxRenderable(testRenderer, {
873
+ id: "dialog-scrollbox",
874
+ width: "100%",
875
+ height: 7,
876
+ scrollY: true,
877
+ onMouseDown: (event) => {
878
+ lastClick = "scrollbox"
879
+ event.stopPropagation()
880
+ },
881
+ })
882
+ dialog.add(scrollBox)
883
+
884
+ for (let i = 0; i < 20; i++) {
885
+ const item = new BoxRenderable(testRenderer, {
886
+ id: `line-${i}`,
887
+ width: "100%",
888
+ height: 1,
889
+ })
890
+ scrollBox.add(item)
891
+ }
892
+
893
+ await testRenderer.idle()
894
+
895
+ await mockMouse.click(scrollBox.viewport.x + 1, scrollBox.viewport.y + 1)
896
+ expect(lastClick).toBe("scrollbox")
897
+
898
+ const headerClickY = header.y + 1
899
+ const targetScrollTop = Math.max(1, scrollBox.viewport.y - headerClickY)
900
+ scrollBox.scrollTop = targetScrollTop
901
+
902
+ await mockMouse.click(header.x + 1, headerClickY)
903
+ expect(lastClick).toBe("header")
904
+
905
+ await mockMouse.click(dialog.x + 1, dialog.y - 1)
906
+ expect(lastClick).toBe("overlay")
907
+
908
+ await testRenderer.idle()
909
+ })