@fairyhunter13/opentui-core 0.1.89 → 0.1.91

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 (571) hide show
  1. package/README.md +2 -2
  2. package/dev/keypress-debug-renderer.ts +148 -0
  3. package/dev/keypress-debug.ts +43 -0
  4. package/dev/print-env-vars.ts +32 -0
  5. package/dev/test-tmux-graphics-334.sh +68 -0
  6. package/dev/thai-debug-test.ts +68 -0
  7. package/docs/development.md +141 -0
  8. package/docs/env-vars.md +140 -0
  9. package/docs/getting-started.md +353 -0
  10. package/docs/renderables-vs-constructs.md +159 -0
  11. package/docs/tree-sitter.md +311 -0
  12. package/package.json +61 -52
  13. package/scripts/build.ts +400 -0
  14. package/scripts/publish.ts +60 -0
  15. package/src/3d/SpriteResourceManager.ts +286 -0
  16. package/src/3d/SpriteUtils.ts +71 -0
  17. package/src/3d/TextureUtils.ts +196 -0
  18. package/src/3d/ThreeRenderable.ts +197 -0
  19. package/src/3d/WGPURenderer.ts +294 -0
  20. package/src/3d/animation/ExplodingSpriteEffect.ts +513 -0
  21. package/src/3d/animation/PhysicsExplodingSpriteEffect.ts +429 -0
  22. package/src/3d/animation/SpriteAnimator.ts +633 -0
  23. package/src/3d/animation/SpriteParticleGenerator.ts +435 -0
  24. package/src/3d/canvas.ts +464 -0
  25. package/src/3d/index.ts +12 -0
  26. package/src/3d/physics/PlanckPhysicsAdapter.ts +72 -0
  27. package/src/3d/physics/RapierPhysicsAdapter.ts +66 -0
  28. package/src/3d/physics/physics-interface.ts +31 -0
  29. package/src/3d/shaders/supersampling.wgsl +201 -0
  30. package/src/3d.ts +3 -0
  31. package/src/NativeSpanFeed.ts +300 -0
  32. package/src/Renderable.ts +1698 -0
  33. package/src/__snapshots__/buffer.test.ts.snap +28 -0
  34. package/src/animation/Timeline.test.ts +2709 -0
  35. package/src/animation/Timeline.ts +598 -0
  36. package/src/ansi.ts +18 -0
  37. package/src/benchmark/latest-all-bench-run.json +707 -0
  38. package/src/benchmark/latest-async-bench-run.json +336 -0
  39. package/src/benchmark/latest-default-bench-run.json +657 -0
  40. package/src/benchmark/latest-large-bench-run.json +707 -0
  41. package/src/benchmark/latest-quick-bench-run.json +207 -0
  42. package/src/benchmark/markdown-benchmark.ts +1804 -0
  43. package/src/benchmark/native-span-feed-async-benchmark.ts +355 -0
  44. package/src/benchmark/native-span-feed-benchmark.md +56 -0
  45. package/src/benchmark/native-span-feed-benchmark.ts +596 -0
  46. package/src/benchmark/native-span-feed-compare.ts +280 -0
  47. package/src/benchmark/renderer-benchmark.ts +754 -0
  48. package/src/benchmark/text-table-benchmark.ts +947 -0
  49. package/src/buffer.test.ts +291 -0
  50. package/src/buffer.ts +519 -0
  51. package/src/console.test.ts +612 -0
  52. package/src/console.ts +1255 -0
  53. package/src/edit-buffer.test.ts +1769 -0
  54. package/src/edit-buffer.ts +411 -0
  55. package/src/editor-view.test.ts +1032 -0
  56. package/src/editor-view.ts +284 -0
  57. package/src/examples/ascii-font-selection-demo.ts +245 -0
  58. package/src/examples/assets/Water_2_M_Normal.jpg +0 -0
  59. package/src/examples/assets/concrete.png +0 -0
  60. package/src/examples/assets/crate.png +0 -0
  61. package/src/examples/assets/crate_emissive.png +0 -0
  62. package/src/examples/assets/forrest_background.png +0 -0
  63. package/src/examples/assets/hast-example.json +1018 -0
  64. package/src/examples/assets/heart.png +0 -0
  65. package/src/examples/assets/main_char_heavy_attack.png +0 -0
  66. package/src/examples/assets/main_char_idle.png +0 -0
  67. package/src/examples/assets/main_char_jump_end.png +0 -0
  68. package/src/examples/assets/main_char_jump_landing.png +0 -0
  69. package/src/examples/assets/main_char_jump_start.png +0 -0
  70. package/src/examples/assets/main_char_run_loop.png +0 -0
  71. package/src/examples/assets/roughness_map.jpg +0 -0
  72. package/src/examples/build.ts +115 -0
  73. package/src/examples/code-demo.ts +584 -0
  74. package/src/examples/console-demo.ts +358 -0
  75. package/src/examples/core-plugin-slots-demo.ts +759 -0
  76. package/src/examples/diff-demo.ts +699 -0
  77. package/src/examples/draggable-three-demo.ts +259 -0
  78. package/src/examples/editor-demo.ts +322 -0
  79. package/src/examples/extmarks-demo.ts +204 -0
  80. package/src/examples/focus-restore-demo.ts +310 -0
  81. package/src/examples/fonts.ts +245 -0
  82. package/src/examples/fractal-shader-demo.ts +268 -0
  83. package/src/examples/framebuffer-demo.ts +674 -0
  84. package/src/examples/full-unicode-demo.ts +181 -0
  85. package/src/examples/golden-star-demo.ts +933 -0
  86. package/src/examples/grayscale-buffer-demo.ts +249 -0
  87. package/src/examples/hast-syntax-highlighting-demo.ts +129 -0
  88. package/src/examples/index.ts +925 -0
  89. package/src/examples/input-demo.ts +377 -0
  90. package/src/examples/input-select-layout-demo.ts +425 -0
  91. package/src/examples/install.sh +143 -0
  92. package/src/examples/keypress-debug-demo.ts +452 -0
  93. package/src/examples/lib/HexList.ts +122 -0
  94. package/src/examples/lib/PaletteGrid.ts +125 -0
  95. package/src/examples/lib/standalone-keys.ts +25 -0
  96. package/src/examples/lib/tab-controller.ts +243 -0
  97. package/src/examples/lights-phong-demo.ts +290 -0
  98. package/src/examples/link-demo.ts +220 -0
  99. package/src/examples/live-state-demo.ts +480 -0
  100. package/src/examples/markdown-demo.ts +620 -0
  101. package/src/examples/mouse-interaction-demo.ts +428 -0
  102. package/src/examples/nested-zindex-demo.ts +357 -0
  103. package/src/examples/opacity-example.ts +235 -0
  104. package/src/examples/opentui-demo.ts +1057 -0
  105. package/src/examples/physx-planck-2d-demo.ts +507 -0
  106. package/src/examples/physx-rapier-2d-demo.ts +526 -0
  107. package/src/examples/relative-positioning-demo.ts +323 -0
  108. package/src/examples/scroll-example.ts +214 -0
  109. package/src/examples/scrollbox-mouse-test.ts +112 -0
  110. package/src/examples/scrollbox-overlay-hit-test.ts +206 -0
  111. package/src/examples/select-demo.ts +237 -0
  112. package/src/examples/shader-cube-demo.ts +772 -0
  113. package/src/examples/simple-layout-example.ts +591 -0
  114. package/src/examples/slider-demo.ts +617 -0
  115. package/src/examples/split-mode-demo.ts +445 -0
  116. package/src/examples/sprite-animation-demo.ts +443 -0
  117. package/src/examples/sprite-particle-generator-demo.ts +486 -0
  118. package/src/examples/static-sprite-demo.ts +193 -0
  119. package/src/examples/sticky-scroll-example.ts +308 -0
  120. package/src/examples/styled-text-demo.ts +282 -0
  121. package/src/examples/tab-select-demo.ts +219 -0
  122. package/src/examples/terminal-title.ts +29 -0
  123. package/src/examples/terminal.ts +305 -0
  124. package/src/examples/text-node-demo.ts +416 -0
  125. package/src/examples/text-selection-demo.ts +377 -0
  126. package/src/examples/text-table-demo.ts +503 -0
  127. package/src/examples/text-truncation-demo.ts +481 -0
  128. package/src/examples/text-wrap.ts +757 -0
  129. package/src/examples/texture-loading-demo.ts +259 -0
  130. package/src/examples/timeline-example.ts +670 -0
  131. package/src/examples/transparency-demo.ts +241 -0
  132. package/src/examples/vnode-composition-demo.ts +404 -0
  133. package/src/index.ts +22 -0
  134. package/src/lib/KeyHandler.integration.test.ts +292 -0
  135. package/src/lib/KeyHandler.stopPropagation.test.ts +289 -0
  136. package/src/lib/KeyHandler.test.ts +662 -0
  137. package/src/lib/KeyHandler.ts +222 -0
  138. package/src/lib/RGBA.test.ts +984 -0
  139. package/src/lib/RGBA.ts +204 -0
  140. package/src/lib/ascii.font.ts +330 -0
  141. package/src/lib/border.test.ts +83 -0
  142. package/src/lib/border.ts +168 -0
  143. package/src/lib/bunfs.test.ts +27 -0
  144. package/src/lib/bunfs.ts +18 -0
  145. package/src/lib/clipboard.test.ts +41 -0
  146. package/src/lib/clipboard.ts +47 -0
  147. package/src/lib/clock.ts +31 -0
  148. package/src/lib/data-paths.test.ts +133 -0
  149. package/src/lib/data-paths.ts +109 -0
  150. package/src/lib/debounce.ts +106 -0
  151. package/src/lib/detect-links.test.ts +98 -0
  152. package/src/lib/detect-links.ts +56 -0
  153. package/src/lib/env.test.ts +228 -0
  154. package/src/lib/env.ts +209 -0
  155. package/src/lib/extmarks-history.ts +51 -0
  156. package/src/lib/extmarks-multiwidth.test.ts +322 -0
  157. package/src/lib/extmarks.test.ts +3457 -0
  158. package/src/lib/extmarks.ts +843 -0
  159. package/src/lib/fonts/block.json +405 -0
  160. package/src/lib/fonts/grid.json +265 -0
  161. package/src/lib/fonts/huge.json +741 -0
  162. package/src/lib/fonts/pallet.json +314 -0
  163. package/src/lib/fonts/shade.json +591 -0
  164. package/src/lib/fonts/slick.json +321 -0
  165. package/src/lib/fonts/tiny.json +69 -0
  166. package/src/lib/hast-styled-text.ts +59 -0
  167. package/src/lib/index.ts +21 -0
  168. package/src/lib/keymapping.test.ts +280 -0
  169. package/src/lib/keymapping.ts +87 -0
  170. package/src/lib/objects-in-viewport.test.ts +787 -0
  171. package/src/lib/objects-in-viewport.ts +153 -0
  172. package/src/lib/output.capture.ts +58 -0
  173. package/src/lib/parse.keypress-kitty.protocol.test.ts +340 -0
  174. package/src/lib/parse.keypress-kitty.test.ts +663 -0
  175. package/src/lib/parse.keypress-kitty.ts +439 -0
  176. package/src/lib/parse.keypress.test.ts +1849 -0
  177. package/src/lib/parse.keypress.ts +397 -0
  178. package/src/lib/parse.mouse.test.ts +552 -0
  179. package/src/lib/parse.mouse.ts +232 -0
  180. package/src/lib/paste.ts +16 -0
  181. package/src/lib/queue.ts +65 -0
  182. package/src/lib/renderable.validations.test.ts +87 -0
  183. package/src/lib/renderable.validations.ts +83 -0
  184. package/src/lib/scroll-acceleration.ts +98 -0
  185. package/src/lib/selection.ts +240 -0
  186. package/src/lib/singleton.ts +28 -0
  187. package/src/lib/stdin-parser.test.ts +1676 -0
  188. package/src/lib/stdin-parser.ts +1248 -0
  189. package/src/lib/styled-text.ts +178 -0
  190. package/src/lib/terminal-capability-detection.test.ts +202 -0
  191. package/src/lib/terminal-capability-detection.ts +79 -0
  192. package/src/lib/terminal-palette.test.ts +878 -0
  193. package/src/lib/terminal-palette.ts +383 -0
  194. package/src/lib/tree-sitter/assets/README.md +118 -0
  195. package/src/lib/tree-sitter/assets/update.ts +331 -0
  196. package/src/lib/tree-sitter/assets.d.ts +9 -0
  197. package/src/lib/tree-sitter/cache.test.ts +270 -0
  198. package/src/lib/tree-sitter/client.test.ts +1061 -0
  199. package/src/lib/tree-sitter/client.ts +615 -0
  200. package/src/lib/tree-sitter/default-parsers.ts +80 -0
  201. package/src/lib/tree-sitter/download-utils.ts +148 -0
  202. package/src/lib/tree-sitter/index.ts +28 -0
  203. package/src/lib/tree-sitter/parser.worker.ts +1001 -0
  204. package/src/lib/tree-sitter/parsers-config.ts +75 -0
  205. package/src/lib/tree-sitter/resolve-ft.ts +62 -0
  206. package/src/lib/tree-sitter/types.ts +81 -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 +377 -0
  214. package/src/plugins/types.ts +46 -0
  215. package/src/post/filters.ts +888 -0
  216. package/src/renderables/ASCIIFont.ts +219 -0
  217. package/src/renderables/Box.test.ts +160 -0
  218. package/src/renderables/Box.ts +295 -0
  219. package/src/renderables/Code.test.ts +2062 -0
  220. package/src/renderables/Code.ts +357 -0
  221. package/src/renderables/Diff.regression.test.ts +226 -0
  222. package/src/renderables/Diff.test.ts +3027 -0
  223. package/src/renderables/Diff.ts +1209 -0
  224. package/src/renderables/EditBufferRenderable.ts +764 -0
  225. package/src/renderables/FrameBuffer.ts +47 -0
  226. package/src/renderables/Input.test.ts +1228 -0
  227. package/src/renderables/Input.ts +245 -0
  228. package/src/renderables/LineNumberRenderable.ts +675 -0
  229. package/src/renderables/Markdown.ts +1106 -0
  230. package/src/renderables/ScrollBar.ts +422 -0
  231. package/src/renderables/ScrollBox.ts +883 -0
  232. package/src/renderables/Select.test.ts +1010 -0
  233. package/src/renderables/Select.ts +523 -0
  234. package/src/renderables/Slider.test.ts +456 -0
  235. package/src/renderables/Slider.ts +347 -0
  236. package/src/renderables/TabSelect.test.ts +197 -0
  237. package/src/renderables/TabSelect.ts +455 -0
  238. package/src/renderables/Text.selection-buffer.test.ts +123 -0
  239. package/src/renderables/Text.test.ts +2660 -0
  240. package/src/renderables/Text.ts +147 -0
  241. package/src/renderables/TextBufferRenderable.ts +518 -0
  242. package/src/renderables/TextNode.test.ts +1058 -0
  243. package/src/renderables/TextNode.ts +325 -0
  244. package/src/renderables/TextTable.test.ts +1421 -0
  245. package/src/renderables/TextTable.ts +1344 -0
  246. package/src/renderables/Textarea.ts +732 -0
  247. package/src/renderables/TimeToFirstDraw.ts +89 -0
  248. package/src/renderables/__snapshots__/Code.test.ts.snap +13 -0
  249. package/src/renderables/__snapshots__/Diff.test.ts.snap +785 -0
  250. package/src/renderables/__snapshots__/Text.test.ts.snap +421 -0
  251. package/src/renderables/__snapshots__/TextTable.test.ts.snap +215 -0
  252. package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +144 -0
  253. package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +816 -0
  254. package/src/renderables/__tests__/LineNumberRenderable.test.ts +1787 -0
  255. package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +85 -0
  256. package/src/renderables/__tests__/Markdown.test.ts +2287 -0
  257. package/src/renderables/__tests__/MultiRenderable.selection.test.ts +87 -0
  258. package/src/renderables/__tests__/Textarea.buffer.test.ts +682 -0
  259. package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +675 -0
  260. package/src/renderables/__tests__/Textarea.editing.test.ts +2041 -0
  261. package/src/renderables/__tests__/Textarea.error-handling.test.ts +35 -0
  262. package/src/renderables/__tests__/Textarea.events.test.ts +738 -0
  263. package/src/renderables/__tests__/Textarea.highlights.test.ts +590 -0
  264. package/src/renderables/__tests__/Textarea.keybinding.test.ts +3149 -0
  265. package/src/renderables/__tests__/Textarea.paste.test.ts +357 -0
  266. package/src/renderables/__tests__/Textarea.rendering.test.ts +1864 -0
  267. package/src/renderables/__tests__/Textarea.scroll.test.ts +733 -0
  268. package/src/renderables/__tests__/Textarea.selection.test.ts +1590 -0
  269. package/src/renderables/__tests__/Textarea.stress.test.ts +670 -0
  270. package/src/renderables/__tests__/Textarea.undo-redo.test.ts +383 -0
  271. package/src/renderables/__tests__/Textarea.visual-lines.test.ts +310 -0
  272. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +221 -0
  273. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +89 -0
  274. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +457 -0
  275. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +158 -0
  276. package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +387 -0
  277. package/src/renderables/__tests__/markdown-parser.test.ts +217 -0
  278. package/src/renderables/__tests__/renderable-test-utils.ts +60 -0
  279. package/src/renderables/composition/README.md +8 -0
  280. package/src/renderables/composition/VRenderable.ts +32 -0
  281. package/src/renderables/composition/constructs.ts +127 -0
  282. package/src/renderables/composition/vnode.ts +289 -0
  283. package/src/renderables/index.ts +22 -0
  284. package/src/renderables/markdown-parser.ts +66 -0
  285. package/src/renderer.ts +2363 -0
  286. package/src/runtime-plugin-support.ts +39 -0
  287. package/src/runtime-plugin.ts +144 -0
  288. package/src/syntax-style.test.ts +841 -0
  289. package/src/syntax-style.ts +264 -0
  290. package/src/testing/README.md +210 -0
  291. package/src/testing/capture-spans.test.ts +194 -0
  292. package/src/testing/integration.test.ts +276 -0
  293. package/src/testing/manual-clock.ts +106 -0
  294. package/src/testing/mock-keys.test.ts +1356 -0
  295. package/src/testing/mock-keys.ts +449 -0
  296. package/src/testing/mock-mouse.test.ts +218 -0
  297. package/src/testing/mock-mouse.ts +247 -0
  298. package/src/testing/mock-tree-sitter-client.ts +73 -0
  299. package/src/testing/spy.ts +13 -0
  300. package/src/testing/test-recorder.test.ts +415 -0
  301. package/src/testing/test-recorder.ts +145 -0
  302. package/src/testing/test-renderer.ts +116 -0
  303. package/src/testing.ts +7 -0
  304. package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +481 -0
  305. package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +19 -0
  306. package/src/tests/__snapshots__/scrollbox.test.ts.snap +29 -0
  307. package/src/tests/absolute-positioning.snapshot.test.ts +638 -0
  308. package/src/tests/allocator-stats.test.ts +38 -0
  309. package/src/tests/destroy-during-render.test.ts +200 -0
  310. package/src/tests/hover-cursor.test.ts +98 -0
  311. package/src/tests/native-span-feed-async.test.ts +173 -0
  312. package/src/tests/native-span-feed-close.test.ts +120 -0
  313. package/src/tests/native-span-feed-coverage.test.ts +227 -0
  314. package/src/tests/native-span-feed-edge-cases.test.ts +352 -0
  315. package/src/tests/native-span-feed-use-after-free.test.ts +45 -0
  316. package/src/tests/opacity.test.ts +123 -0
  317. package/src/tests/renderable.snapshot.test.ts +524 -0
  318. package/src/tests/renderable.test.ts +1281 -0
  319. package/src/tests/renderer.console-startup.test.ts +65 -0
  320. package/src/tests/renderer.control.test.ts +364 -0
  321. package/src/tests/renderer.core-slot-binding.test.ts +952 -0
  322. package/src/tests/renderer.cursor.test.ts +26 -0
  323. package/src/tests/renderer.destroy-during-render.test.ts +110 -0
  324. package/src/tests/renderer.focus-restore.test.ts +228 -0
  325. package/src/tests/renderer.focus.test.ts +251 -0
  326. package/src/tests/renderer.idle.test.ts +219 -0
  327. package/src/tests/renderer.input.test.ts +2145 -0
  328. package/src/tests/renderer.kitty-flags.test.ts +195 -0
  329. package/src/tests/renderer.mouse.test.ts +1269 -0
  330. package/src/tests/renderer.palette.test.ts +629 -0
  331. package/src/tests/renderer.selection.test.ts +49 -0
  332. package/src/tests/renderer.slot-registry.test.ts +649 -0
  333. package/src/tests/renderer.useMouse.test.ts +50 -0
  334. package/src/tests/runtime-plugin-support.fixture.ts +11 -0
  335. package/src/tests/runtime-plugin-support.test.ts +28 -0
  336. package/src/tests/runtime-plugin.fixture.ts +40 -0
  337. package/src/tests/runtime-plugin.test.ts +190 -0
  338. package/src/tests/scrollbox-culling-bug.test.ts +114 -0
  339. package/src/tests/scrollbox-hitgrid-resize.test.ts +136 -0
  340. package/src/tests/scrollbox-hitgrid.test.ts +909 -0
  341. package/src/tests/scrollbox.test.ts +1530 -0
  342. package/src/tests/wrap-resize-perf.test.ts +229 -0
  343. package/src/tests/yoga-setters.test.ts +921 -0
  344. package/src/text-buffer-view.test.ts +705 -0
  345. package/src/text-buffer-view.ts +189 -0
  346. package/src/text-buffer.test.ts +347 -0
  347. package/src/text-buffer.ts +250 -0
  348. package/src/types.ts +152 -0
  349. package/src/utils.ts +88 -0
  350. package/src/zig/ansi.zig +268 -0
  351. package/src/zig/bench/README.md +50 -0
  352. package/src/zig/bench/buffer-draw-text-buffer_bench.zig +887 -0
  353. package/src/zig/bench/edit-buffer_bench.zig +476 -0
  354. package/src/zig/bench/native-span-feed_bench.zig +100 -0
  355. package/src/zig/bench/rope-markers_bench.zig +713 -0
  356. package/src/zig/bench/rope_bench.zig +514 -0
  357. package/src/zig/bench/styled-text_bench.zig +470 -0
  358. package/src/zig/bench/text-buffer-coords_bench.zig +362 -0
  359. package/src/zig/bench/text-buffer-view_bench.zig +459 -0
  360. package/src/zig/bench/text-chunk-graphemes_bench.zig +273 -0
  361. package/src/zig/bench/utf8_bench.zig +799 -0
  362. package/src/zig/bench-utils.zig +431 -0
  363. package/src/zig/bench.zig +217 -0
  364. package/src/zig/buffer.zig +2223 -0
  365. package/src/zig/build.zig +289 -0
  366. package/src/zig/build.zig.zon +16 -0
  367. package/src/zig/edit-buffer.zig +825 -0
  368. package/src/zig/editor-view.zig +802 -0
  369. package/src/zig/event-bus.zig +13 -0
  370. package/src/zig/event-emitter.zig +65 -0
  371. package/src/zig/file-logger.zig +92 -0
  372. package/src/zig/grapheme.zig +599 -0
  373. package/src/zig/lib.zig +1834 -0
  374. package/src/zig/link.zig +333 -0
  375. package/src/zig/logger.zig +43 -0
  376. package/src/zig/mem-registry.zig +125 -0
  377. package/src/zig/native-span-feed-bench-lib.zig +7 -0
  378. package/src/zig/native-span-feed.zig +708 -0
  379. package/src/zig/renderer.zig +1386 -0
  380. package/src/zig/rope.zig +1220 -0
  381. package/src/zig/syntax-style.zig +161 -0
  382. package/src/zig/terminal.zig +975 -0
  383. package/src/zig/test.zig +70 -0
  384. package/src/zig/tests/README.md +18 -0
  385. package/src/zig/tests/buffer_test.zig +2526 -0
  386. package/src/zig/tests/edit-buffer-history_test.zig +271 -0
  387. package/src/zig/tests/edit-buffer_test.zig +1689 -0
  388. package/src/zig/tests/editor-view_test.zig +3299 -0
  389. package/src/zig/tests/event-emitter_test.zig +249 -0
  390. package/src/zig/tests/grapheme_test.zig +1304 -0
  391. package/src/zig/tests/link_test.zig +190 -0
  392. package/src/zig/tests/mem-registry_test.zig +473 -0
  393. package/src/zig/tests/memory_leak_regression_test.zig +159 -0
  394. package/src/zig/tests/native-span-feed_test.zig +1264 -0
  395. package/src/zig/tests/renderer_test.zig +1010 -0
  396. package/src/zig/tests/rope-nested_test.zig +712 -0
  397. package/src/zig/tests/rope_fuzz_test.zig +238 -0
  398. package/src/zig/tests/rope_test.zig +2362 -0
  399. package/src/zig/tests/segment-merge.test.zig +148 -0
  400. package/src/zig/tests/syntax-style_test.zig +557 -0
  401. package/src/zig/tests/terminal_test.zig +719 -0
  402. package/src/zig/tests/text-buffer-drawing_test.zig +3237 -0
  403. package/src/zig/tests/text-buffer-highlights_test.zig +666 -0
  404. package/src/zig/tests/text-buffer-iterators_test.zig +776 -0
  405. package/src/zig/tests/text-buffer-segment_test.zig +320 -0
  406. package/src/zig/tests/text-buffer-selection_test.zig +1035 -0
  407. package/src/zig/tests/text-buffer-selection_viewport_test.zig +358 -0
  408. package/src/zig/tests/text-buffer-view_test.zig +3649 -0
  409. package/src/zig/tests/text-buffer_test.zig +2191 -0
  410. package/src/zig/tests/unicode-width-map.zon +3909 -0
  411. package/src/zig/tests/utf8_no_zwj_test.zig +260 -0
  412. package/src/zig/tests/utf8_test.zig +4057 -0
  413. package/src/zig/tests/utf8_wcwidth_cursor_test.zig +267 -0
  414. package/src/zig/tests/utf8_wcwidth_test.zig +357 -0
  415. package/src/zig/tests/word-wrap-editing_test.zig +498 -0
  416. package/src/zig/tests/wrap-cache-perf_test.zig +113 -0
  417. package/src/zig/text-buffer-iterators.zig +499 -0
  418. package/src/zig/text-buffer-segment.zig +404 -0
  419. package/src/zig/text-buffer-view.zig +1371 -0
  420. package/src/zig/text-buffer.zig +1180 -0
  421. package/src/zig/utf8.zig +1948 -0
  422. package/src/zig/utils.zig +9 -0
  423. package/src/zig-structs.ts +261 -0
  424. package/src/zig.ts +3843 -0
  425. package/tsconfig.build.json +22 -0
  426. package/tsconfig.json +28 -0
  427. package/3d/SpriteResourceManager.d.ts +0 -74
  428. package/3d/SpriteUtils.d.ts +0 -13
  429. package/3d/TextureUtils.d.ts +0 -24
  430. package/3d/ThreeRenderable.d.ts +0 -40
  431. package/3d/WGPURenderer.d.ts +0 -61
  432. package/3d/animation/ExplodingSpriteEffect.d.ts +0 -71
  433. package/3d/animation/PhysicsExplodingSpriteEffect.d.ts +0 -76
  434. package/3d/animation/SpriteAnimator.d.ts +0 -124
  435. package/3d/animation/SpriteParticleGenerator.d.ts +0 -62
  436. package/3d/canvas.d.ts +0 -44
  437. package/3d/index.d.ts +0 -12
  438. package/3d/physics/PlanckPhysicsAdapter.d.ts +0 -19
  439. package/3d/physics/RapierPhysicsAdapter.d.ts +0 -19
  440. package/3d/physics/physics-interface.d.ts +0 -27
  441. package/3d.d.ts +0 -2
  442. package/3d.js +0 -34042
  443. package/3d.js.map +0 -155
  444. package/LICENSE +0 -21
  445. package/NativeSpanFeed.d.ts +0 -41
  446. package/Renderable.d.ts +0 -334
  447. package/animation/Timeline.d.ts +0 -126
  448. package/ansi.d.ts +0 -13
  449. package/buffer.d.ts +0 -107
  450. package/console.d.ts +0 -143
  451. package/edit-buffer.d.ts +0 -98
  452. package/editor-view.d.ts +0 -73
  453. package/index-e4hzc2j2.js +0 -113
  454. package/index-e4hzc2j2.js.map +0 -10
  455. package/index-nkrr8a4c.js +0 -18415
  456. package/index-nkrr8a4c.js.map +0 -64
  457. package/index-nyw5p3ep.js +0 -12619
  458. package/index-nyw5p3ep.js.map +0 -43
  459. package/index.d.ts +0 -21
  460. package/index.js +0 -430
  461. package/index.js.map +0 -9
  462. package/lib/KeyHandler.d.ts +0 -61
  463. package/lib/RGBA.d.ts +0 -25
  464. package/lib/ascii.font.d.ts +0 -508
  465. package/lib/border.d.ts +0 -49
  466. package/lib/bunfs.d.ts +0 -7
  467. package/lib/clipboard.d.ts +0 -17
  468. package/lib/clock.d.ts +0 -15
  469. package/lib/data-paths.d.ts +0 -26
  470. package/lib/debounce.d.ts +0 -42
  471. package/lib/detect-links.d.ts +0 -6
  472. package/lib/env.d.ts +0 -42
  473. package/lib/extmarks-history.d.ts +0 -17
  474. package/lib/extmarks.d.ts +0 -89
  475. package/lib/hast-styled-text.d.ts +0 -17
  476. package/lib/index.d.ts +0 -21
  477. package/lib/keymapping.d.ts +0 -25
  478. package/lib/objects-in-viewport.d.ts +0 -24
  479. package/lib/output.capture.d.ts +0 -24
  480. package/lib/parse.keypress-kitty.d.ts +0 -2
  481. package/lib/parse.keypress.d.ts +0 -26
  482. package/lib/parse.mouse.d.ts +0 -30
  483. package/lib/paste.d.ts +0 -7
  484. package/lib/queue.d.ts +0 -15
  485. package/lib/renderable.validations.d.ts +0 -12
  486. package/lib/scroll-acceleration.d.ts +0 -43
  487. package/lib/selection.d.ts +0 -63
  488. package/lib/singleton.d.ts +0 -7
  489. package/lib/stdin-parser.d.ts +0 -76
  490. package/lib/styled-text.d.ts +0 -63
  491. package/lib/terminal-capability-detection.d.ts +0 -30
  492. package/lib/terminal-palette.d.ts +0 -50
  493. package/lib/tree-sitter/assets/update.d.ts +0 -11
  494. package/lib/tree-sitter/client.d.ts +0 -47
  495. package/lib/tree-sitter/default-parsers.d.ts +0 -2
  496. package/lib/tree-sitter/download-utils.d.ts +0 -21
  497. package/lib/tree-sitter/index.d.ts +0 -8
  498. package/lib/tree-sitter/parser.worker.d.ts +0 -1
  499. package/lib/tree-sitter/parsers-config.d.ts +0 -38
  500. package/lib/tree-sitter/resolve-ft.d.ts +0 -2
  501. package/lib/tree-sitter/types.d.ts +0 -81
  502. package/lib/tree-sitter-styled-text.d.ts +0 -14
  503. package/lib/validate-dir-name.d.ts +0 -1
  504. package/lib/yoga.options.d.ts +0 -32
  505. package/parser.worker.js +0 -869
  506. package/parser.worker.js.map +0 -12
  507. package/plugins/core-slot.d.ts +0 -72
  508. package/plugins/registry.d.ts +0 -38
  509. package/plugins/types.d.ts +0 -34
  510. package/post/filters.d.ts +0 -105
  511. package/renderables/ASCIIFont.d.ts +0 -52
  512. package/renderables/Box.d.ts +0 -72
  513. package/renderables/Code.d.ts +0 -78
  514. package/renderables/Diff.d.ts +0 -142
  515. package/renderables/EditBufferRenderable.d.ts +0 -162
  516. package/renderables/FrameBuffer.d.ts +0 -16
  517. package/renderables/Input.d.ts +0 -67
  518. package/renderables/LineNumberRenderable.d.ts +0 -74
  519. package/renderables/Markdown.d.ts +0 -173
  520. package/renderables/ScrollBar.d.ts +0 -77
  521. package/renderables/ScrollBox.d.ts +0 -124
  522. package/renderables/Select.d.ts +0 -115
  523. package/renderables/Slider.d.ts +0 -44
  524. package/renderables/TabSelect.d.ts +0 -96
  525. package/renderables/Text.d.ts +0 -36
  526. package/renderables/TextBufferRenderable.d.ts +0 -105
  527. package/renderables/TextNode.d.ts +0 -91
  528. package/renderables/TextTable.d.ts +0 -140
  529. package/renderables/Textarea.d.ts +0 -114
  530. package/renderables/TimeToFirstDraw.d.ts +0 -24
  531. package/renderables/__tests__/renderable-test-utils.d.ts +0 -12
  532. package/renderables/composition/VRenderable.d.ts +0 -16
  533. package/renderables/composition/constructs.d.ts +0 -35
  534. package/renderables/composition/vnode.d.ts +0 -46
  535. package/renderables/index.d.ts +0 -22
  536. package/renderables/markdown-parser.d.ts +0 -10
  537. package/renderer.d.ts +0 -388
  538. package/runtime-plugin-support.d.ts +0 -3
  539. package/runtime-plugin-support.js +0 -29
  540. package/runtime-plugin-support.js.map +0 -10
  541. package/runtime-plugin.d.ts +0 -11
  542. package/runtime-plugin.js +0 -16
  543. package/runtime-plugin.js.map +0 -9
  544. package/syntax-style.d.ts +0 -54
  545. package/testing/manual-clock.d.ts +0 -16
  546. package/testing/mock-keys.d.ts +0 -81
  547. package/testing/mock-mouse.d.ts +0 -38
  548. package/testing/mock-tree-sitter-client.d.ts +0 -23
  549. package/testing/spy.d.ts +0 -7
  550. package/testing/test-recorder.d.ts +0 -61
  551. package/testing/test-renderer.d.ts +0 -23
  552. package/testing.d.ts +0 -6
  553. package/testing.js +0 -675
  554. package/testing.js.map +0 -15
  555. package/text-buffer-view.d.ts +0 -42
  556. package/text-buffer.d.ts +0 -67
  557. package/types.d.ts +0 -131
  558. package/utils.d.ts +0 -14
  559. package/zig-structs.d.ts +0 -155
  560. package/zig.d.ts +0 -351
  561. /package/{assets → src/lib/tree-sitter/assets}/javascript/highlights.scm +0 -0
  562. /package/{assets → src/lib/tree-sitter/assets}/javascript/tree-sitter-javascript.wasm +0 -0
  563. /package/{assets → src/lib/tree-sitter/assets}/markdown/highlights.scm +0 -0
  564. /package/{assets → src/lib/tree-sitter/assets}/markdown/injections.scm +0 -0
  565. /package/{assets → src/lib/tree-sitter/assets}/markdown/tree-sitter-markdown.wasm +0 -0
  566. /package/{assets → src/lib/tree-sitter/assets}/markdown_inline/highlights.scm +0 -0
  567. /package/{assets → src/lib/tree-sitter/assets}/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
  568. /package/{assets → src/lib/tree-sitter/assets}/typescript/highlights.scm +0 -0
  569. /package/{assets → src/lib/tree-sitter/assets}/typescript/tree-sitter-typescript.wasm +0 -0
  570. /package/{assets → src/lib/tree-sitter/assets}/zig/highlights.scm +0 -0
  571. /package/{assets → src/lib/tree-sitter/assets}/zig/tree-sitter-zig.wasm +0 -0
@@ -0,0 +1,292 @@
1
+ import { test, expect } from "bun:test"
2
+ import { InternalKeyHandler, KeyEvent } from "./KeyHandler.js"
3
+ import { parseKeypress } from "./parse.keypress.js"
4
+
5
+ /**
6
+ * Integration tests demonstrating real-world scenarios with stopPropagation
7
+ */
8
+
9
+ function createKeyHandler(): InternalKeyHandler {
10
+ return new InternalKeyHandler()
11
+ }
12
+
13
+ function dispatchInput(handler: InternalKeyHandler, data: string): boolean {
14
+ const parsedKey = parseKeypress(data)
15
+ if (!parsedKey) {
16
+ return false
17
+ }
18
+
19
+ return handler.processParsedKey(parsedKey)
20
+ }
21
+
22
+ test("Integration - Modal ESC handler prevents subsequent handlers", () => {
23
+ const handler = createKeyHandler()
24
+
25
+ let modalOpen = true
26
+ let modalHandledEsc = false
27
+ let backgroundHandledEsc = false
28
+
29
+ // Modal ESC handler (registered first, so it runs first)
30
+ handler.on("keypress", (key: KeyEvent) => {
31
+ if (key.name === "escape" && modalOpen) {
32
+ modalHandledEsc = true
33
+ modalOpen = false
34
+ key.stopPropagation() // Stop other handlers from running
35
+ }
36
+ })
37
+
38
+ // Background/app-level ESC handler (registered second, should not run)
39
+ handler.on("keypress", (key: KeyEvent) => {
40
+ if (key.name === "escape") {
41
+ backgroundHandledEsc = true
42
+ }
43
+ })
44
+
45
+ // Simulate ESC key press while modal is open
46
+ dispatchInput(handler, "\x1b")
47
+
48
+ expect(modalOpen).toBe(false)
49
+ expect(modalHandledEsc).toBe(true)
50
+ expect(backgroundHandledEsc).toBe(false) // Modal stopped propagation
51
+ })
52
+
53
+ test("Integration - Focused input field handles key, stops parent handlers", () => {
54
+ const handler = createKeyHandler()
55
+
56
+ const inputValue: string[] = []
57
+ let parentHandledKey = false
58
+
59
+ // Parent container handler
60
+ handler.on("keypress", (key: KeyEvent) => {
61
+ if (!key.propagationStopped) {
62
+ parentHandledKey = true
63
+ }
64
+ })
65
+
66
+ // Focused input field handler (internal/renderable)
67
+ handler.onInternal("keypress", (key: KeyEvent) => {
68
+ if (key.name === "a" || key.name === "b" || key.name === "c") {
69
+ inputValue.push(key.name)
70
+ key.stopPropagation() // Input consumed the key
71
+ }
72
+ })
73
+
74
+ // Type some keys
75
+ dispatchInput(handler, "a")
76
+ dispatchInput(handler, "b")
77
+ dispatchInput(handler, "c")
78
+
79
+ expect(inputValue).toEqual(["a", "b", "c"])
80
+ expect(parentHandledKey).toBe(true) // Parent ran first (global priority)
81
+
82
+ // But internal handler got to consume the keys and stop propagation
83
+ // doesn't prevent parent from seeing them first (global runs before internal)
84
+ })
85
+
86
+ test("Integration - Dialog system with priority: innermost modal wins", () => {
87
+ const handler = createKeyHandler()
88
+
89
+ let outerModalClosed = false
90
+ let innerModalClosed = false
91
+ const closeLog: string[] = []
92
+
93
+ // Outer modal ESC handler
94
+ const outerHandler = (key: KeyEvent) => {
95
+ if (key.name === "escape" && !key.propagationStopped) {
96
+ closeLog.push("outer")
97
+ outerModalClosed = true
98
+ key.stopPropagation()
99
+ }
100
+ }
101
+
102
+ // Inner modal ESC handler (registered later, so it comes first in listener order)
103
+ const innerHandler = (key: KeyEvent) => {
104
+ if (key.name === "escape") {
105
+ closeLog.push("inner")
106
+ innerModalClosed = true
107
+ key.stopPropagation()
108
+ }
109
+ }
110
+
111
+ // Register outer first
112
+ handler.on("keypress", outerHandler)
113
+
114
+ // Then inner (but we want inner to handle first)
115
+ // In a real app, we'd use prependInputHandler or similar
116
+ // For now, let's simulate by removing outer and re-adding in correct order
117
+ handler.removeListener("keypress", outerHandler)
118
+ handler.on("keypress", innerHandler)
119
+ handler.on("keypress", outerHandler)
120
+
121
+ // Press ESC
122
+ dispatchInput(handler, "\x1b")
123
+
124
+ expect(closeLog).toEqual(["inner"])
125
+ expect(innerModalClosed).toBe(true)
126
+ expect(outerModalClosed).toBe(false) // Inner stopped propagation
127
+ })
128
+
129
+ test("Integration - Keyboard shortcut system with priorities", () => {
130
+ const handler = createKeyHandler()
131
+
132
+ const actions: string[] = []
133
+
134
+ // Global shortcuts (Ctrl+S = Save, Ctrl+O = Open)
135
+ handler.on("keypress", (key: KeyEvent) => {
136
+ if (key.ctrl && key.name === "s") {
137
+ actions.push("save")
138
+ // Don't stop propagation - allow other handlers to see it
139
+ }
140
+ if (key.ctrl && key.name === "o") {
141
+ actions.push("open")
142
+ }
143
+ })
144
+
145
+ // Text editor overrides Ctrl+S when focused
146
+ let editorFocused = true
147
+ handler.onInternal("keypress", (key: KeyEvent) => {
148
+ if (editorFocused && key.ctrl && key.name === "s") {
149
+ actions.push("save-document")
150
+ key.stopPropagation() // Override global save
151
+ }
152
+ })
153
+
154
+ // Ctrl+S with editor focused
155
+ dispatchInput(handler, "\x13") // Ctrl+S
156
+
157
+ expect(actions).toEqual(["save", "save-document"])
158
+ // Note: global runs first, then internal. To truly override,
159
+ // the editor would need to be a global handler registered first
160
+ })
161
+
162
+ test("Integration - preventDefault vs stopPropagation behavior", () => {
163
+ const handler = createKeyHandler()
164
+
165
+ const log: string[] = []
166
+
167
+ // Handler 1: preventDefault only
168
+ handler.on("keypress", (key: KeyEvent) => {
169
+ if (key.name === "a") {
170
+ log.push("handler1-saw-a")
171
+ key.preventDefault()
172
+ }
173
+ })
174
+
175
+ // Handler 2: Should still run (preventDefault doesn't stop global handlers)
176
+ handler.on("keypress", (key: KeyEvent) => {
177
+ if (key.name === "a") {
178
+ log.push("handler2-saw-a")
179
+ if (key.defaultPrevented) {
180
+ log.push("handler2-saw-prevented")
181
+ }
182
+ }
183
+ })
184
+
185
+ // Handler 3: Internal handler should not run (preventDefault stops internal)
186
+ handler.onInternal("keypress", (key: KeyEvent) => {
187
+ if (key.name === "a") {
188
+ log.push("handler3-internal-saw-a")
189
+ }
190
+ })
191
+
192
+ dispatchInput(handler, "a")
193
+
194
+ expect(log).toEqual([
195
+ "handler1-saw-a",
196
+ "handler2-saw-a",
197
+ "handler2-saw-prevented",
198
+ // handler3 doesn't run because preventDefault stops internal handlers
199
+ ])
200
+
201
+ // Now test with stopPropagation
202
+ log.length = 0
203
+
204
+ handler.removeAllListeners("keypress")
205
+
206
+ handler.on("keypress", (key: KeyEvent) => {
207
+ if (key.name === "b") {
208
+ log.push("handler1-saw-b")
209
+ key.stopPropagation()
210
+ }
211
+ })
212
+
213
+ handler.on("keypress", (key: KeyEvent) => {
214
+ if (key.name === "b") {
215
+ log.push("handler2-saw-b")
216
+ }
217
+ })
218
+
219
+ dispatchInput(handler, "b")
220
+
221
+ expect(log).toEqual([
222
+ "handler1-saw-b",
223
+ // handler2 doesn't run because stopPropagation stops all subsequent handlers
224
+ ])
225
+ })
226
+
227
+ test("Integration - Form submission with Enter key", () => {
228
+ const handler = createKeyHandler()
229
+
230
+ let formSubmitted = false
231
+ let inputValue = ""
232
+
233
+ // Form's Enter handler
234
+ handler.on("keypress", (key: KeyEvent) => {
235
+ if (key.name === "return" && !key.propagationStopped) {
236
+ formSubmitted = true
237
+ }
238
+ })
239
+
240
+ // Input field's Enter handler
241
+ handler.onInternal("keypress", (key: KeyEvent) => {
242
+ if (key.name === "return") {
243
+ // Multi-line input: add newline and stop propagation
244
+ inputValue += "\n"
245
+ key.stopPropagation()
246
+ }
247
+ })
248
+
249
+ // Press Enter
250
+ dispatchInput(handler, "\r")
251
+
252
+ expect(inputValue).toBe("\n")
253
+ expect(formSubmitted).toBe(true) // Global handler ran first
254
+
255
+ // In a real app, you'd check defaultPrevented in the form handler
256
+ // or the input would be registered as a global handler first
257
+ })
258
+
259
+ test("Integration - Event bubbling with multiple nested components", () => {
260
+ const handler = createKeyHandler()
261
+
262
+ const eventLog: Array<{ component: string; stopped: boolean }> = []
263
+
264
+ // Root component
265
+ handler.on("keypress", (key: KeyEvent) => {
266
+ eventLog.push({ component: "root", stopped: key.propagationStopped })
267
+ })
268
+
269
+ // Child component (registered as internal, represents focused element)
270
+ handler.onInternal("keypress", (key: KeyEvent) => {
271
+ eventLog.push({ component: "child", stopped: key.propagationStopped })
272
+
273
+ // Child handles space key and stops propagation
274
+ if (key.name === "space") {
275
+ key.stopPropagation()
276
+ }
277
+ })
278
+
279
+ // Another internal handler (sibling or parent)
280
+ handler.onInternal("keypress", (key: KeyEvent) => {
281
+ eventLog.push({ component: "sibling", stopped: key.propagationStopped })
282
+ })
283
+
284
+ dispatchInput(handler, " ") // Space key
285
+
286
+ expect(eventLog).toEqual([
287
+ { component: "root", stopped: false },
288
+ { component: "child", stopped: false },
289
+ // sibling doesn't run because child stopped propagation
290
+ ])
291
+ expect(eventLog).toHaveLength(2)
292
+ })
@@ -0,0 +1,289 @@
1
+ import { test, expect } from "bun:test"
2
+ import { InternalKeyHandler, KeyEvent } from "./KeyHandler.js"
3
+ import { parseKeypress } from "./parse.keypress.js"
4
+ import { pasteBytes } from "../testing/mock-keys.js"
5
+
6
+ function createKeyHandler(): InternalKeyHandler {
7
+ return new InternalKeyHandler()
8
+ }
9
+
10
+ function dispatchInput(handler: InternalKeyHandler, data: string): boolean {
11
+ const parsedKey = parseKeypress(data)
12
+ if (!parsedKey) {
13
+ return false
14
+ }
15
+
16
+ return handler.processParsedKey(parsedKey)
17
+ }
18
+
19
+ test("stopPropagation - stops subsequent global handlers", () => {
20
+ const handler = createKeyHandler()
21
+
22
+ const callOrder: string[] = []
23
+
24
+ handler.on("keypress", (key: KeyEvent) => {
25
+ callOrder.push("global1")
26
+ key.stopPropagation()
27
+ })
28
+
29
+ handler.on("keypress", (key: KeyEvent) => {
30
+ callOrder.push("global2")
31
+ })
32
+
33
+ dispatchInput(handler, "a")
34
+
35
+ expect(callOrder).toEqual(["global1"])
36
+ })
37
+
38
+ test("stopPropagation - stops internal handlers from running", () => {
39
+ const handler = createKeyHandler()
40
+
41
+ const callOrder: string[] = []
42
+
43
+ handler.on("keypress", (key: KeyEvent) => {
44
+ callOrder.push("global")
45
+ key.stopPropagation()
46
+ })
47
+
48
+ handler.onInternal("keypress", (key: KeyEvent) => {
49
+ callOrder.push("internal")
50
+ })
51
+
52
+ dispatchInput(handler, "a")
53
+
54
+ expect(callOrder).toEqual(["global"])
55
+ })
56
+
57
+ test("stopPropagation - internal handler can stop other internal handlers", () => {
58
+ const handler = createKeyHandler()
59
+
60
+ const callOrder: string[] = []
61
+
62
+ handler.onInternal("keypress", (key: KeyEvent) => {
63
+ callOrder.push("internal1")
64
+ key.stopPropagation()
65
+ })
66
+
67
+ handler.onInternal("keypress", (key: KeyEvent) => {
68
+ callOrder.push("internal2")
69
+ })
70
+
71
+ dispatchInput(handler, "a")
72
+
73
+ expect(callOrder).toEqual(["internal1"])
74
+ })
75
+
76
+ test("stopPropagation - does not affect preventDefault", () => {
77
+ const handler = createKeyHandler()
78
+
79
+ let stoppedPropagation = false
80
+ let preventedDefault = false
81
+
82
+ handler.on("keypress", (key: KeyEvent) => {
83
+ key.stopPropagation()
84
+ key.preventDefault()
85
+ stoppedPropagation = key.propagationStopped
86
+ preventedDefault = key.defaultPrevented
87
+ })
88
+
89
+ dispatchInput(handler, "a")
90
+
91
+ expect(stoppedPropagation).toBe(true)
92
+ expect(preventedDefault).toBe(true)
93
+ })
94
+
95
+ test("stopPropagation - without calling it, all handlers run", () => {
96
+ const handler = createKeyHandler()
97
+
98
+ const callOrder: string[] = []
99
+
100
+ handler.on("keypress", (key: KeyEvent) => {
101
+ callOrder.push("global1")
102
+ })
103
+
104
+ handler.on("keypress", (key: KeyEvent) => {
105
+ callOrder.push("global2")
106
+ })
107
+
108
+ handler.onInternal("keypress", (key: KeyEvent) => {
109
+ callOrder.push("internal1")
110
+ })
111
+
112
+ handler.onInternal("keypress", (key: KeyEvent) => {
113
+ callOrder.push("internal2")
114
+ })
115
+
116
+ dispatchInput(handler, "a")
117
+
118
+ expect(callOrder).toEqual(["global1", "global2", "internal1", "internal2"])
119
+ })
120
+
121
+ test("stopPropagation - paste events support stopPropagation", () => {
122
+ const handler = createKeyHandler()
123
+
124
+ const callOrder: string[] = []
125
+
126
+ handler.on("paste", (event) => {
127
+ callOrder.push("global")
128
+ event.stopPropagation()
129
+ })
130
+
131
+ handler.onInternal("paste", (event) => {
132
+ callOrder.push("internal")
133
+ })
134
+
135
+ handler.processPaste(pasteBytes("hello"))
136
+
137
+ expect(callOrder).toEqual(["global"])
138
+ })
139
+
140
+ test("stopPropagation - works with keyrelease events", () => {
141
+ const handler = createKeyHandler()
142
+
143
+ const callOrder: string[] = []
144
+
145
+ handler.on("keyrelease", (key: KeyEvent) => {
146
+ callOrder.push("global")
147
+ key.stopPropagation()
148
+ })
149
+
150
+ handler.onInternal("keyrelease", (key: KeyEvent) => {
151
+ callOrder.push("internal")
152
+ })
153
+
154
+ // Emit a release event directly since we need kitty protocol
155
+ handler.emit(
156
+ "keyrelease",
157
+ new KeyEvent({
158
+ name: "a",
159
+ ctrl: false,
160
+ meta: false,
161
+ shift: false,
162
+ option: false,
163
+ sequence: "a",
164
+ number: false,
165
+ raw: "a",
166
+ eventType: "release",
167
+ source: "kitty",
168
+ }),
169
+ )
170
+
171
+ expect(callOrder).toEqual(["global"])
172
+ })
173
+
174
+ test("stopPropagation - error in handler does not affect propagation stopped state", () => {
175
+ const handler = createKeyHandler()
176
+
177
+ const callOrder: string[] = []
178
+
179
+ handler.on("keypress", (key: KeyEvent) => {
180
+ callOrder.push("global1")
181
+ key.stopPropagation()
182
+ throw new Error("Test error")
183
+ })
184
+
185
+ handler.on("keypress", (key: KeyEvent) => {
186
+ callOrder.push("global2")
187
+ })
188
+
189
+ expect(() => dispatchInput(handler, "a")).not.toThrow()
190
+
191
+ expect(callOrder).toEqual(["global1"])
192
+ })
193
+
194
+ test("stopPropagation - modal scenario: ESC key handled by modal, stops at modal", () => {
195
+ const handler = createKeyHandler()
196
+
197
+ const callOrder: string[] = []
198
+ let modalClosed = false
199
+ let appHandledEsc = false
200
+
201
+ // Modal handler (internal, should be focused element) - runs BEFORE app handler
202
+ // In a real app, the focused modal element would use onInternal
203
+ handler.onInternal("keypress", (key: KeyEvent) => {
204
+ if (key.name === "escape") {
205
+ callOrder.push("modal")
206
+ modalClosed = true
207
+ key.stopPropagation()
208
+ }
209
+ })
210
+
211
+ // App-level global ESC handler (should NOT run if modal stops propagation)
212
+ handler.on("keypress", (key: KeyEvent) => {
213
+ if (key.name === "escape") {
214
+ callOrder.push("app")
215
+ appHandledEsc = true
216
+ }
217
+ })
218
+
219
+ dispatchInput(handler, "\x1b")
220
+
221
+ // Global handlers run before internal handlers
222
+ // So app handler runs first, but modal can still stop further internal handlers
223
+ expect(callOrder).toEqual(["app", "modal"])
224
+ expect(modalClosed).toBe(true)
225
+ expect(appHandledEsc).toBe(true)
226
+ })
227
+
228
+ test("stopPropagation - modal scenario: global modal handler prevents app handler", () => {
229
+ const handler = createKeyHandler()
230
+
231
+ const callOrder: string[] = []
232
+ let modalClosed = false
233
+ let appHandledEsc = false
234
+
235
+ // Modal as a global handler (registered first) - to stop before app handler
236
+ handler.on("keypress", (key: KeyEvent) => {
237
+ if (key.name === "escape") {
238
+ callOrder.push("modal")
239
+ modalClosed = true
240
+ key.stopPropagation()
241
+ }
242
+ })
243
+
244
+ // App-level ESC handler (should NOT run due to stopPropagation)
245
+ handler.on("keypress", (key: KeyEvent) => {
246
+ if (key.name === "escape") {
247
+ callOrder.push("app")
248
+ appHandledEsc = true
249
+ }
250
+ })
251
+
252
+ dispatchInput(handler, "\x1b")
253
+
254
+ // When modal is registered as a global handler first, it can stop the app handler
255
+ expect(callOrder).toEqual(["modal"])
256
+ expect(modalClosed).toBe(true)
257
+ expect(appHandledEsc).toBe(false)
258
+ })
259
+
260
+ test("stopPropagation - event flow without stopPropagation shows order", () => {
261
+ const handler = createKeyHandler()
262
+
263
+ const events: string[] = []
264
+
265
+ handler.on("keypress", (key: KeyEvent) => {
266
+ events.push("global1")
267
+ expect(key.propagationStopped).toBe(false)
268
+ })
269
+
270
+ handler.on("keypress", (key: KeyEvent) => {
271
+ events.push("global2")
272
+ expect(key.propagationStopped).toBe(false)
273
+ })
274
+
275
+ handler.onInternal("keypress", (key: KeyEvent) => {
276
+ events.push("internal1")
277
+ expect(key.propagationStopped).toBe(false)
278
+ })
279
+
280
+ handler.onInternal("keypress", (key: KeyEvent) => {
281
+ events.push("internal2")
282
+ expect(key.propagationStopped).toBe(false)
283
+ })
284
+
285
+ dispatchInput(handler, "a")
286
+
287
+ // Verify execution order: global handlers first, then internal handlers
288
+ expect(events).toEqual(["global1", "global2", "internal1", "internal2"])
289
+ })