@xincli/opentui-core 0.4.6

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 (476) hide show
  1. package/README.md +63 -0
  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/test-tmux-theme-queries.sh +151 -0
  7. package/dev/thai-debug-test.ts +68 -0
  8. package/dev/theme-mode.ts +290 -0
  9. package/docs/development.md +144 -0
  10. package/package.json +102 -0
  11. package/prebuilt/aarch64-android/libopentui.so +0 -0
  12. package/scripts/build-android.ts +226 -0
  13. package/scripts/build-native-termux.sh +525 -0
  14. package/scripts/build.ts +530 -0
  15. package/scripts/dist-test.ts +312 -0
  16. package/scripts/package-prebuilt.ts +110 -0
  17. package/scripts/publish.ts +60 -0
  18. package/scripts/test-node-hook.mjs +16 -0
  19. package/scripts/test-node.ts +241 -0
  20. package/scripts/vendor-deps.sh +73 -0
  21. package/src/NativeSpanFeed.ts +375 -0
  22. package/src/Renderable.ts +1878 -0
  23. package/src/__snapshots__/buffer.test.ts.snap +28 -0
  24. package/src/animation/Timeline.test.ts +2709 -0
  25. package/src/animation/Timeline.ts +598 -0
  26. package/src/ansi.ts +22 -0
  27. package/src/audio.ts +479 -0
  28. package/src/benchmark/attenuation-benchmark.ts +81 -0
  29. package/src/benchmark/audio-playback-benchmark.ts +344 -0
  30. package/src/benchmark/box-draw-benchmark.ts +1042 -0
  31. package/src/benchmark/colormatrix-benchmark.ts +123 -0
  32. package/src/benchmark/gain-benchmark.ts +80 -0
  33. package/src/benchmark/latest-all-bench-run.json +707 -0
  34. package/src/benchmark/latest-async-bench-run.json +336 -0
  35. package/src/benchmark/latest-default-bench-run.json +657 -0
  36. package/src/benchmark/latest-large-bench-run.json +707 -0
  37. package/src/benchmark/latest-quick-bench-run.json +207 -0
  38. package/src/benchmark/layout-benchmark.ts +2547 -0
  39. package/src/benchmark/markdown-benchmark.ts +1796 -0
  40. package/src/benchmark/native-span-feed-async-benchmark.ts +355 -0
  41. package/src/benchmark/native-span-feed-benchmark.md +56 -0
  42. package/src/benchmark/native-span-feed-benchmark.ts +596 -0
  43. package/src/benchmark/native-span-feed-compare.ts +280 -0
  44. package/src/benchmark/render-traversal-benchmark.ts +928 -0
  45. package/src/benchmark/text-buffer-render-benchmark.ts +874 -0
  46. package/src/benchmark/text-table-benchmark.ts +948 -0
  47. package/src/buffer.test.ts +292 -0
  48. package/src/buffer.ts +564 -0
  49. package/src/console.test.ts +629 -0
  50. package/src/console.ts +1253 -0
  51. package/src/edit-buffer.test.ts +1852 -0
  52. package/src/edit-buffer.ts +417 -0
  53. package/src/editor-view.test.ts +1056 -0
  54. package/src/editor-view.ts +290 -0
  55. package/src/index.ts +25 -0
  56. package/src/lib/KeyHandler.integration.test.ts +292 -0
  57. package/src/lib/KeyHandler.stopPropagation.test.ts +289 -0
  58. package/src/lib/KeyHandler.test.ts +662 -0
  59. package/src/lib/KeyHandler.ts +222 -0
  60. package/src/lib/RGBA.test.ts +60 -0
  61. package/src/lib/RGBA.ts +343 -0
  62. package/src/lib/ascii.font.ts +330 -0
  63. package/src/lib/border.test.ts +83 -0
  64. package/src/lib/border.ts +170 -0
  65. package/src/lib/bunfs.test.ts +27 -0
  66. package/src/lib/bunfs.ts +18 -0
  67. package/src/lib/clipboard.test.ts +63 -0
  68. package/src/lib/clipboard.ts +41 -0
  69. package/src/lib/clock.ts +35 -0
  70. package/src/lib/data-paths.test.ts +133 -0
  71. package/src/lib/data-paths.ts +109 -0
  72. package/src/lib/debounce.ts +106 -0
  73. package/src/lib/detect-links.test.ts +98 -0
  74. package/src/lib/detect-links.ts +56 -0
  75. package/src/lib/env.test.ts +228 -0
  76. package/src/lib/env.ts +209 -0
  77. package/src/lib/extmarks-history.ts +51 -0
  78. package/src/lib/extmarks-multiwidth.test.ts +324 -0
  79. package/src/lib/extmarks.test.ts +3547 -0
  80. package/src/lib/extmarks.ts +861 -0
  81. package/src/lib/fonts/block.json +405 -0
  82. package/src/lib/fonts/grid.json +265 -0
  83. package/src/lib/fonts/huge.json +741 -0
  84. package/src/lib/fonts/pallet.json +314 -0
  85. package/src/lib/fonts/shade.json +591 -0
  86. package/src/lib/fonts/slick.json +321 -0
  87. package/src/lib/fonts/tiny.json +69 -0
  88. package/src/lib/hast-styled-text.ts +59 -0
  89. package/src/lib/index.ts +22 -0
  90. package/src/lib/keybinding.internal.test.ts +342 -0
  91. package/src/lib/keybinding.internal.ts +182 -0
  92. package/src/lib/objects-in-viewport.test.ts +789 -0
  93. package/src/lib/objects-in-viewport.ts +153 -0
  94. package/src/lib/output.capture.ts +58 -0
  95. package/src/lib/parse.keypress-kitty.protocol.test.ts +347 -0
  96. package/src/lib/parse.keypress-kitty.test.ts +712 -0
  97. package/src/lib/parse.keypress-kitty.ts +470 -0
  98. package/src/lib/parse.keypress.test.ts +2020 -0
  99. package/src/lib/parse.keypress.ts +480 -0
  100. package/src/lib/parse.mouse.test.ts +552 -0
  101. package/src/lib/parse.mouse.ts +232 -0
  102. package/src/lib/paste.ts +18 -0
  103. package/src/lib/queue.ts +65 -0
  104. package/src/lib/render-geometry.ts +36 -0
  105. package/src/lib/renderable.validations.test.ts +87 -0
  106. package/src/lib/renderable.validations.ts +83 -0
  107. package/src/lib/scroll-acceleration.ts +98 -0
  108. package/src/lib/selection.ts +261 -0
  109. package/src/lib/singleton.ts +34 -0
  110. package/src/lib/stdin-parser.test.ts +2439 -0
  111. package/src/lib/stdin-parser.ts +2007 -0
  112. package/src/lib/styled-text.ts +178 -0
  113. package/src/lib/terminal-capability-detection.test.ts +226 -0
  114. package/src/lib/terminal-capability-detection.ts +98 -0
  115. package/src/lib/terminal-palette.test.ts +954 -0
  116. package/src/lib/terminal-palette.ts +441 -0
  117. package/src/lib/tree-sitter/assets/README.md +119 -0
  118. package/src/lib/tree-sitter/assets/javascript/highlights.scm +205 -0
  119. package/src/lib/tree-sitter/assets/javascript/tree-sitter-javascript.wasm +0 -0
  120. package/src/lib/tree-sitter/assets/markdown/highlights.scm +150 -0
  121. package/src/lib/tree-sitter/assets/markdown/injections.scm +27 -0
  122. package/src/lib/tree-sitter/assets/markdown/tree-sitter-markdown.wasm +0 -0
  123. package/src/lib/tree-sitter/assets/markdown_inline/highlights.scm +115 -0
  124. package/src/lib/tree-sitter/assets/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
  125. package/src/lib/tree-sitter/assets/typescript/highlights.scm +604 -0
  126. package/src/lib/tree-sitter/assets/typescript/tree-sitter-typescript.wasm +0 -0
  127. package/src/lib/tree-sitter/assets/update.ts +360 -0
  128. package/src/lib/tree-sitter/assets/zig/highlights.scm +284 -0
  129. package/src/lib/tree-sitter/assets/zig/tree-sitter-zig.wasm +0 -0
  130. package/src/lib/tree-sitter/assets.d.ts +9 -0
  131. package/src/lib/tree-sitter/cache.test.ts +291 -0
  132. package/src/lib/tree-sitter/client.test.ts +1434 -0
  133. package/src/lib/tree-sitter/client.ts +847 -0
  134. package/src/lib/tree-sitter/default-parsers.ts +133 -0
  135. package/src/lib/tree-sitter/download-utils.ts +148 -0
  136. package/src/lib/tree-sitter/index.ts +75 -0
  137. package/src/lib/tree-sitter/parser.worker.ts +1098 -0
  138. package/src/lib/tree-sitter/parsers-config.ts +81 -0
  139. package/src/lib/tree-sitter/resolve-ft.test.ts +55 -0
  140. package/src/lib/tree-sitter/resolve-ft.ts +189 -0
  141. package/src/lib/tree-sitter/types.ts +132 -0
  142. package/src/lib/tree-sitter/update-assets.ts +10 -0
  143. package/src/lib/tree-sitter-styled-text.test.ts +1253 -0
  144. package/src/lib/tree-sitter-styled-text.ts +314 -0
  145. package/src/lib/validate-dir-name.ts +55 -0
  146. package/src/lib/yoga.options.test.ts +628 -0
  147. package/src/lib/yoga.options.ts +346 -0
  148. package/src/native-event-worker-repro.test.ts +48 -0
  149. package/src/native-event-worker-repro.worker.ts +9 -0
  150. package/src/native-handle.test.ts +221 -0
  151. package/src/platform/ffi.test.ts +516 -0
  152. package/src/platform/ffi.ts +653 -0
  153. package/src/platform/runtime.test.ts +52 -0
  154. package/src/platform/runtime.ts +121 -0
  155. package/src/platform/test.ts +45 -0
  156. package/src/platform/worker-handler-cleanup.fixture.ts +12 -0
  157. package/src/platform/worker-onmessage-startup.fixture.ts +10 -0
  158. package/src/platform/worker-startup.fixture.ts +9 -0
  159. package/src/platform/worker.node-test.ts +153 -0
  160. package/src/platform/worker.ts +425 -0
  161. package/src/plugins/core-slot.ts +579 -0
  162. package/src/plugins/registry.ts +402 -0
  163. package/src/plugins/types.ts +46 -0
  164. package/src/post/effects.ts +948 -0
  165. package/src/post/filters.ts +512 -0
  166. package/src/post/matrices.ts +288 -0
  167. package/src/renderables/ASCIIFont.ts +219 -0
  168. package/src/renderables/Box.test.ts +474 -0
  169. package/src/renderables/Box.ts +353 -0
  170. package/src/renderables/Code.test.ts +2388 -0
  171. package/src/renderables/Code.ts +555 -0
  172. package/src/renderables/Diff.regression.test.ts +381 -0
  173. package/src/renderables/Diff.test.ts +3212 -0
  174. package/src/renderables/Diff.ts +1278 -0
  175. package/src/renderables/EditBufferRenderable.test.ts +288 -0
  176. package/src/renderables/EditBufferRenderable.ts +1152 -0
  177. package/src/renderables/FrameBuffer.ts +47 -0
  178. package/src/renderables/Input.test.ts +1310 -0
  179. package/src/renderables/Input.ts +273 -0
  180. package/src/renderables/LineNumberRenderable.ts +725 -0
  181. package/src/renderables/Markdown.ts +2144 -0
  182. package/src/renderables/ScrollBar.ts +423 -0
  183. package/src/renderables/ScrollBox.ts +903 -0
  184. package/src/renderables/Select.test.ts +1134 -0
  185. package/src/renderables/Select.ts +535 -0
  186. package/src/renderables/Slider.test.ts +456 -0
  187. package/src/renderables/Slider.ts +342 -0
  188. package/src/renderables/TabSelect.test.ts +238 -0
  189. package/src/renderables/TabSelect.ts +447 -0
  190. package/src/renderables/Text.selection-buffer.test.ts +118 -0
  191. package/src/renderables/Text.test.ts +2662 -0
  192. package/src/renderables/Text.ts +147 -0
  193. package/src/renderables/TextBufferRenderable.ts +504 -0
  194. package/src/renderables/TextNode.test.ts +1137 -0
  195. package/src/renderables/TextNode.ts +379 -0
  196. package/src/renderables/TextTable.test.ts +1411 -0
  197. package/src/renderables/TextTable.ts +1434 -0
  198. package/src/renderables/Textarea.ts +425 -0
  199. package/src/renderables/TimeToFirstDraw.ts +89 -0
  200. package/src/renderables/__snapshots__/Code.test.ts.snap +13 -0
  201. package/src/renderables/__snapshots__/Diff.test.ts.snap +785 -0
  202. package/src/renderables/__snapshots__/Text.test.ts.snap +421 -0
  203. package/src/renderables/__snapshots__/TextTable.test.ts.snap +215 -0
  204. package/src/renderables/__tests__/Code.test.ts +55 -0
  205. package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +144 -0
  206. package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +827 -0
  207. package/src/renderables/__tests__/LineNumberRenderable.test.ts +1906 -0
  208. package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +85 -0
  209. package/src/renderables/__tests__/Markdown.code-colors.test.ts +288 -0
  210. package/src/renderables/__tests__/Markdown.test.ts +4175 -0
  211. package/src/renderables/__tests__/MultiRenderable.selection.test.ts +87 -0
  212. package/src/renderables/__tests__/Textarea.buffer.test.ts +683 -0
  213. package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +675 -0
  214. package/src/renderables/__tests__/Textarea.editing.test.ts +2120 -0
  215. package/src/renderables/__tests__/Textarea.error-handling.test.ts +35 -0
  216. package/src/renderables/__tests__/Textarea.events.test.ts +738 -0
  217. package/src/renderables/__tests__/Textarea.highlights.test.ts +590 -0
  218. package/src/renderables/__tests__/Textarea.keybinding.test.ts +3275 -0
  219. package/src/renderables/__tests__/Textarea.paste.test.ts +357 -0
  220. package/src/renderables/__tests__/Textarea.rendering.test.ts +1927 -0
  221. package/src/renderables/__tests__/Textarea.scroll.test.ts +733 -0
  222. package/src/renderables/__tests__/Textarea.selection.test.ts +1589 -0
  223. package/src/renderables/__tests__/Textarea.stress.test.ts +670 -0
  224. package/src/renderables/__tests__/Textarea.undo-redo.test.ts +383 -0
  225. package/src/renderables/__tests__/Textarea.visual-lines.test.ts +310 -0
  226. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +221 -0
  227. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +89 -0
  228. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +457 -0
  229. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +158 -0
  230. package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +387 -0
  231. package/src/renderables/__tests__/markdown-parser.test.ts +241 -0
  232. package/src/renderables/__tests__/renderable-test-utils.ts +60 -0
  233. package/src/renderables/composition/README.md +8 -0
  234. package/src/renderables/composition/VRenderable.ts +32 -0
  235. package/src/renderables/composition/constructs.ts +127 -0
  236. package/src/renderables/composition/vnode.ts +277 -0
  237. package/src/renderables/index.ts +23 -0
  238. package/src/renderables/markdown-parser.ts +79 -0
  239. package/src/renderer-theme-mode.ts +199 -0
  240. package/src/renderer.ts +4975 -0
  241. package/src/runtime-plugin-support-configure.ts +63 -0
  242. package/src/runtime-plugin-support.ts +13 -0
  243. package/src/runtime-plugin.ts +626 -0
  244. package/src/specs/terminal-startup.md +64 -0
  245. package/src/syntax-style.test.ts +841 -0
  246. package/src/syntax-style.ts +274 -0
  247. package/src/testing/README.md +219 -0
  248. package/src/testing/bun-test-node.ts +880 -0
  249. package/src/testing/capture-spans.test.ts +216 -0
  250. package/src/testing/integration.test.ts +276 -0
  251. package/src/testing/manual-clock.ts +117 -0
  252. package/src/testing/mock-keys.test.ts +1378 -0
  253. package/src/testing/mock-keys.ts +457 -0
  254. package/src/testing/mock-mouse.test.ts +218 -0
  255. package/src/testing/mock-mouse.ts +247 -0
  256. package/src/testing/mock-tree-sitter-client.ts +81 -0
  257. package/src/testing/spy.ts +13 -0
  258. package/src/testing/terminal-capabilities.ts +47 -0
  259. package/src/testing/test-recorder.test.ts +410 -0
  260. package/src/testing/test-recorder.ts +133 -0
  261. package/src/testing/test-renderer.ts +375 -0
  262. package/src/testing/test-renderer.wait.test.ts +278 -0
  263. package/src/testing/test-streams.ts +31 -0
  264. package/src/testing.ts +9 -0
  265. package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +481 -0
  266. package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +19 -0
  267. package/src/tests/__snapshots__/scrollbox.test.ts.snap +29 -0
  268. package/src/tests/absolute-positioning.snapshot.test.ts +638 -0
  269. package/src/tests/allocator-stats.test.ts +53 -0
  270. package/src/tests/audio.test.ts +303 -0
  271. package/src/tests/destroy-during-render.test.ts +200 -0
  272. package/src/tests/destroy-on-exit.fixture.ts +36 -0
  273. package/src/tests/destroy-on-exit.test.ts +62 -0
  274. package/src/tests/ffi-borrowed-pointer-callsites.test.ts +153 -0
  275. package/src/tests/hover-cursor.test.ts +98 -0
  276. package/src/tests/native-backed-measurement-lifecycle.test.ts +133 -0
  277. package/src/tests/native-backed-measurement-parity.test.ts +322 -0
  278. package/src/tests/native-span-feed-async.test.ts +173 -0
  279. package/src/tests/native-span-feed-close.test.ts +120 -0
  280. package/src/tests/native-span-feed-coverage.test.ts +227 -0
  281. package/src/tests/native-span-feed-edge-cases.test.ts +384 -0
  282. package/src/tests/native-span-feed-use-after-free.test.ts +45 -0
  283. package/src/tests/opacity.test.ts +123 -0
  284. package/src/tests/renderable.snapshot.test.ts +524 -0
  285. package/src/tests/renderable.test.ts +1851 -0
  286. package/src/tests/renderer.clock.test.ts +270 -0
  287. package/src/tests/renderer.console-startup.test.ts +2151 -0
  288. package/src/tests/renderer.control.test.ts +458 -0
  289. package/src/tests/renderer.core-slot-binding.test.ts +962 -0
  290. package/src/tests/renderer.cursor.test.ts +26 -0
  291. package/src/tests/renderer.custom-stdout.test.ts +1112 -0
  292. package/src/tests/renderer.destroy-during-render.test.ts +147 -0
  293. package/src/tests/renderer.focus-restore.test.ts +257 -0
  294. package/src/tests/renderer.focus.test.ts +329 -0
  295. package/src/tests/renderer.idle.test.ts +219 -0
  296. package/src/tests/renderer.input.test.ts +2615 -0
  297. package/src/tests/renderer.kitty-flags.test.ts +195 -0
  298. package/src/tests/renderer.lifecycle.test.ts +34 -0
  299. package/src/tests/renderer.mouse.test.ts +1315 -0
  300. package/src/tests/renderer.notifications.test.ts +27 -0
  301. package/src/tests/renderer.palette.test.ts +1199 -0
  302. package/src/tests/renderer.remote.test.ts +247 -0
  303. package/src/tests/renderer.render-stats.test.ts +146 -0
  304. package/src/tests/renderer.scrollback-surface.test.ts +474 -0
  305. package/src/tests/renderer.selection.test.ts +138 -0
  306. package/src/tests/renderer.slot-registry.test.ts +684 -0
  307. package/src/tests/renderer.tracker.test.ts +202 -0
  308. package/src/tests/renderer.useMouse.test.ts +83 -0
  309. package/src/tests/runtime-plugin-node-modules-cycle.fixture.ts +76 -0
  310. package/src/tests/runtime-plugin-node-modules-import-like-string.fixture.ts +48 -0
  311. package/src/tests/runtime-plugin-node-modules-mjs.fixture.ts +43 -0
  312. package/src/tests/runtime-plugin-node-modules-no-bare-rewrite.fixture.ts +67 -0
  313. package/src/tests/runtime-plugin-node-modules-package-type-cache.fixture.ts +72 -0
  314. package/src/tests/runtime-plugin-node-modules-runtime-specifier.fixture.ts +44 -0
  315. package/src/tests/runtime-plugin-node-modules-scoped-package-bare-rewrite.fixture.ts +85 -0
  316. package/src/tests/runtime-plugin-path-alias.fixture.ts +43 -0
  317. package/src/tests/runtime-plugin-resolve-roots.fixture.ts +65 -0
  318. package/src/tests/runtime-plugin-support-configure.fixture.ts +31 -0
  319. package/src/tests/runtime-plugin-support-late-addition.fixture.ts +17 -0
  320. package/src/tests/runtime-plugin-support.fixture.ts +11 -0
  321. package/src/tests/runtime-plugin-support.test.ts +54 -0
  322. package/src/tests/runtime-plugin-windows-file-url.fixture.ts +30 -0
  323. package/src/tests/runtime-plugin.fixture.ts +40 -0
  324. package/src/tests/runtime-plugin.test.ts +369 -0
  325. package/src/tests/scrollbox-culling-bug.test.ts +114 -0
  326. package/src/tests/scrollbox-hitgrid-resize.test.ts +136 -0
  327. package/src/tests/scrollbox-hitgrid.test.ts +909 -0
  328. package/src/tests/scrollbox.test.ts +1895 -0
  329. package/src/tests/wrap-resize-perf.test.ts +276 -0
  330. package/src/tests/yoga-api-coverage.test.ts +409 -0
  331. package/src/tests/yoga-callback-stress.test.ts +122 -0
  332. package/src/tests/yoga-ffi.test.ts +117 -0
  333. package/src/tests/yoga-setters.test.ts +921 -0
  334. package/src/tests/yoga-upstream/YGAlignBaselineTest.test.ts +124 -0
  335. package/src/tests/yoga-upstream/YGComputedBorderTest.test.ts +27 -0
  336. package/src/tests/yoga-upstream/YGComputedMarginTest.test.ts +27 -0
  337. package/src/tests/yoga-upstream/YGComputedPaddingTest.test.ts +27 -0
  338. package/src/tests/yoga-upstream/YGDirtiedTest.test.ts +157 -0
  339. package/src/tests/yoga-upstream/YGErrataTest.test.ts +40 -0
  340. package/src/tests/yoga-upstream/YGFlexBasisAuto.test.ts +23 -0
  341. package/src/tests/yoga-upstream/YGHasNewLayout.test.ts +81 -0
  342. package/src/tests/yoga-upstream/YGMeasureCacheTest.test.ts +31 -0
  343. package/src/tests/yoga-upstream/YGMeasureTest.test.ts +70 -0
  344. package/src/tests/yoga-upstream/generated/YGAbsolutePositionTest.test.ts +1864 -0
  345. package/src/tests/yoga-upstream/generated/YGAlignContentTest.test.ts +5829 -0
  346. package/src/tests/yoga-upstream/generated/YGAlignItemsTest.test.ts +2444 -0
  347. package/src/tests/yoga-upstream/generated/YGAlignSelfTest.test.ts +293 -0
  348. package/src/tests/yoga-upstream/generated/YGAndroidNewsFeed.test.ts +315 -0
  349. package/src/tests/yoga-upstream/generated/YGAspectRatioTest.test.ts +231 -0
  350. package/src/tests/yoga-upstream/generated/YGAutoTest.test.ts +347 -0
  351. package/src/tests/yoga-upstream/generated/YGBorderTest.test.ts +256 -0
  352. package/src/tests/yoga-upstream/generated/YGBoxSizingTest.test.ts +2747 -0
  353. package/src/tests/yoga-upstream/generated/YGDimensionTest.test.ts +129 -0
  354. package/src/tests/yoga-upstream/generated/YGDisplayContentsTest.test.ts +109 -0
  355. package/src/tests/yoga-upstream/generated/YGDisplayTest.test.ts +1097 -0
  356. package/src/tests/yoga-upstream/generated/YGFlexDirectionTest.test.ts +4562 -0
  357. package/src/tests/yoga-upstream/generated/YGFlexTest.test.ts +677 -0
  358. package/src/tests/yoga-upstream/generated/YGFlexWrapTest.test.ts +2071 -0
  359. package/src/tests/yoga-upstream/generated/YGGapTest.test.ts +3303 -0
  360. package/src/tests/yoga-upstream/generated/YGIntrinsicSizeTest.test.ts +582 -0
  361. package/src/tests/yoga-upstream/generated/YGJustifyContentTest.test.ts +2150 -0
  362. package/src/tests/yoga-upstream/generated/YGMarginTest.test.ts +1963 -0
  363. package/src/tests/yoga-upstream/generated/YGMinMaxDimensionTest.test.ts +1446 -0
  364. package/src/tests/yoga-upstream/generated/YGPaddingTest.test.ts +354 -0
  365. package/src/tests/yoga-upstream/generated/YGPercentageTest.test.ts +1760 -0
  366. package/src/tests/yoga-upstream/generated/YGRoundingTest.test.ts +1161 -0
  367. package/src/tests/yoga-upstream/generated/YGSizeOverflowTest.test.ts +207 -0
  368. package/src/tests/yoga-upstream/generated/YGStaticPositionTest.test.ts +6338 -0
  369. package/src/tests/yoga-upstream/tools/MeasureCounter.ts +54 -0
  370. package/src/tests/yoga-upstream/tools/utils.ts +81 -0
  371. package/src/text-buffer-view.test.ts +722 -0
  372. package/src/text-buffer-view.ts +193 -0
  373. package/src/text-buffer.test.ts +512 -0
  374. package/src/text-buffer.ts +250 -0
  375. package/src/types.ts +199 -0
  376. package/src/utils.ts +88 -0
  377. package/src/yoga.ts +1146 -0
  378. package/src/zig/ansi.zig +502 -0
  379. package/src/zig/audio.zig +1206 -0
  380. package/src/zig/bench/README.md +50 -0
  381. package/src/zig/bench/buffer-color-blending_bench.zig +350 -0
  382. package/src/zig/bench/buffer-draw-box_bench.zig +786 -0
  383. package/src/zig/bench/buffer-draw-text-buffer_bench.zig +894 -0
  384. package/src/zig/bench/edit-buffer_bench.zig +476 -0
  385. package/src/zig/bench/editor-view_bench.zig +169 -0
  386. package/src/zig/bench/native-span-feed_bench.zig +107 -0
  387. package/src/zig/bench/rope-markers_bench.zig +713 -0
  388. package/src/zig/bench/rope_bench.zig +514 -0
  389. package/src/zig/bench/styled-text_bench.zig +476 -0
  390. package/src/zig/bench/text-buffer-coords_bench.zig +362 -0
  391. package/src/zig/bench/text-buffer-view_bench.zig +459 -0
  392. package/src/zig/bench/text-chunk-graphemes_bench.zig +272 -0
  393. package/src/zig/bench/utf8_bench.zig +799 -0
  394. package/src/zig/bench-utils.zig +431 -0
  395. package/src/zig/bench.zig +223 -0
  396. package/src/zig/buffer-methods.zig +215 -0
  397. package/src/zig/buffer.zig +2515 -0
  398. package/src/zig/build.zig +700 -0
  399. package/src/zig/build.zig.zon +19 -0
  400. package/src/zig/edit-buffer.zig +900 -0
  401. package/src/zig/editor-view.zig +812 -0
  402. package/src/zig/event-bus.zig +28 -0
  403. package/src/zig/event-emitter.zig +65 -0
  404. package/src/zig/file-logger.zig +92 -0
  405. package/src/zig/grapheme.zig +838 -0
  406. package/src/zig/handles.zig +267 -0
  407. package/src/zig/lib.zig +2813 -0
  408. package/src/zig/link.zig +349 -0
  409. package/src/zig/logger.zig +43 -0
  410. package/src/zig/mem-registry.zig +126 -0
  411. package/src/zig/miniaudio_shim.c +21 -0
  412. package/src/zig/native-renderable.zig +112 -0
  413. package/src/zig/native-span-feed-bench-lib.zig +7 -0
  414. package/src/zig/native-span-feed.zig +747 -0
  415. package/src/zig/renderer-output.zig +677 -0
  416. package/src/zig/renderer.zig +2106 -0
  417. package/src/zig/rope.zig +1220 -0
  418. package/src/zig/split-scrollback.zig +88 -0
  419. package/src/zig/syntax-style.zig +174 -0
  420. package/src/zig/terminal.zig +1652 -0
  421. package/src/zig/test.zig +82 -0
  422. package/src/zig/tests/README.md +18 -0
  423. package/src/zig/tests/ansi_test.zig +18 -0
  424. package/src/zig/tests/audio_test.zig +439 -0
  425. package/src/zig/tests/buffer-methods_test.zig +1111 -0
  426. package/src/zig/tests/buffer_test.zig +2926 -0
  427. package/src/zig/tests/edit-buffer-history_test.zig +299 -0
  428. package/src/zig/tests/edit-buffer_test.zig +1688 -0
  429. package/src/zig/tests/editor-view_test.zig +3454 -0
  430. package/src/zig/tests/event-emitter_test.zig +249 -0
  431. package/src/zig/tests/grapheme_test.zig +1329 -0
  432. package/src/zig/tests/handles_test.zig +123 -0
  433. package/src/zig/tests/link_test.zig +220 -0
  434. package/src/zig/tests/mem-registry_test.zig +473 -0
  435. package/src/zig/tests/memory_leak_regression_test.zig +234 -0
  436. package/src/zig/tests/native-renderable_test.zig +16 -0
  437. package/src/zig/tests/native-span-feed_test.zig +1266 -0
  438. package/src/zig/tests/renderer_test.zig +2604 -0
  439. package/src/zig/tests/rope-nested_test.zig +712 -0
  440. package/src/zig/tests/rope_fuzz_test.zig +238 -0
  441. package/src/zig/tests/rope_test.zig +2362 -0
  442. package/src/zig/tests/segment-merge.test.zig +148 -0
  443. package/src/zig/tests/split-scrollback_test.zig +70 -0
  444. package/src/zig/tests/syntax-style_test.zig +558 -0
  445. package/src/zig/tests/terminal_test.zig +1489 -0
  446. package/src/zig/tests/test-renderer.zig +126 -0
  447. package/src/zig/tests/text-buffer-drawing_test.zig +3291 -0
  448. package/src/zig/tests/text-buffer-highlights_test.zig +706 -0
  449. package/src/zig/tests/text-buffer-iterators_test.zig +776 -0
  450. package/src/zig/tests/text-buffer-segment_test.zig +320 -0
  451. package/src/zig/tests/text-buffer-selection_test.zig +1056 -0
  452. package/src/zig/tests/text-buffer-selection_viewport_test.zig +358 -0
  453. package/src/zig/tests/text-buffer-view_test.zig +3672 -0
  454. package/src/zig/tests/text-buffer_test.zig +2191 -0
  455. package/src/zig/tests/unicode-width-map.zon +3909 -0
  456. package/src/zig/tests/utf8_no_zwj_test.zig +260 -0
  457. package/src/zig/tests/utf8_test.zig +4060 -0
  458. package/src/zig/tests/utf8_wcwidth_cursor_test.zig +267 -0
  459. package/src/zig/tests/utf8_wcwidth_test.zig +357 -0
  460. package/src/zig/tests/word-wrap-editing_test.zig +498 -0
  461. package/src/zig/tests/wrap-cache-perf_test.zig +113 -0
  462. package/src/zig/tests/yoga_test.zig +65 -0
  463. package/src/zig/text-buffer-iterators.zig +496 -0
  464. package/src/zig/text-buffer-segment.zig +403 -0
  465. package/src/zig/text-buffer-view.zig +1428 -0
  466. package/src/zig/text-buffer.zig +1272 -0
  467. package/src/zig/utf8.zig +1954 -0
  468. package/src/zig/utils.zig +12 -0
  469. package/src/zig/uucode-stub.zig +76 -0
  470. package/src/zig/vendor/miniaudio/miniaudio.h +93468 -0
  471. package/src/zig/yoga.zig +658 -0
  472. package/src/zig-structs.ts +381 -0
  473. package/src/zig.ts +5260 -0
  474. package/tsconfig.build.json +17 -0
  475. package/tsconfig.json +27 -0
  476. package/tsconfig.node-test.json +138 -0
@@ -0,0 +1,290 @@
1
+ import { RGBA } from "./lib/RGBA.js"
2
+ import {
3
+ resolveRenderLib,
4
+ type EditorViewHandle,
5
+ type RenderLib,
6
+ type TextBufferViewHandle,
7
+ type VisualCursor,
8
+ type LineInfo,
9
+ } from "./zig.js"
10
+ import type { EditBuffer } from "./edit-buffer.js"
11
+ import { createExtmarksController } from "./lib/index.js"
12
+
13
+ export interface Viewport {
14
+ offsetY: number
15
+ offsetX: number
16
+ height: number
17
+ width: number
18
+ }
19
+
20
+ export type { VisualCursor }
21
+
22
+ export class EditorView {
23
+ private lib: RenderLib
24
+ private viewPtr: EditorViewHandle
25
+ private editBuffer: EditBuffer
26
+ private _destroyed: boolean = false
27
+ private _extmarksController?: any
28
+ private _textBufferViewPtr?: TextBufferViewHandle
29
+
30
+ constructor(lib: RenderLib, ptr: EditorViewHandle, editBuffer: EditBuffer) {
31
+ this.lib = lib
32
+ this.viewPtr = ptr
33
+ this.editBuffer = editBuffer
34
+ }
35
+
36
+ static create(editBuffer: EditBuffer, viewportWidth: number, viewportHeight: number): EditorView {
37
+ const lib = resolveRenderLib()
38
+ const viewPtr = lib.createEditorView(editBuffer.ptr, viewportWidth, viewportHeight)
39
+ return new EditorView(lib, viewPtr, editBuffer)
40
+ }
41
+
42
+ private guard(): void {
43
+ if (this._destroyed) throw new Error("EditorView is destroyed")
44
+ }
45
+
46
+ public get ptr(): EditorViewHandle {
47
+ this.guard()
48
+ return this.viewPtr
49
+ }
50
+
51
+ public setViewportSize(width: number, height: number): void {
52
+ this.guard()
53
+ this.lib.editorViewSetViewportSize(this.viewPtr, width, height)
54
+ }
55
+
56
+ public setViewport(x: number, y: number, width: number, height: number, moveCursor: boolean = true): void {
57
+ this.guard()
58
+ this.lib.editorViewSetViewport(this.viewPtr, x, y, width, height, moveCursor)
59
+ }
60
+
61
+ public getViewport(): Viewport {
62
+ this.guard()
63
+ return this.lib.editorViewGetViewport(this.viewPtr)
64
+ }
65
+
66
+ public setScrollMargin(margin: number): void {
67
+ this.guard()
68
+ this.lib.editorViewSetScrollMargin(this.viewPtr, margin)
69
+ }
70
+
71
+ public setWrapMode(mode: "none" | "char" | "word"): void {
72
+ this.guard()
73
+ this.lib.editorViewSetWrapMode(this.viewPtr, mode)
74
+ }
75
+
76
+ public getVirtualLineCount(): number {
77
+ this.guard()
78
+ return this.lib.editorViewGetVirtualLineCount(this.viewPtr)
79
+ }
80
+
81
+ public getTotalVirtualLineCount(): number {
82
+ this.guard()
83
+ return this.lib.editorViewGetTotalVirtualLineCount(this.viewPtr)
84
+ }
85
+
86
+ public setSelection(start: number, end: number, bgColor?: RGBA, fgColor?: RGBA): void {
87
+ this.guard()
88
+ this.lib.editorViewSetSelection(this.viewPtr, start, end, bgColor || null, fgColor || null)
89
+ }
90
+
91
+ public updateSelection(end: number, bgColor?: RGBA, fgColor?: RGBA): void {
92
+ this.guard()
93
+ this.lib.editorViewUpdateSelection(this.viewPtr, end, bgColor || null, fgColor || null)
94
+ }
95
+
96
+ public resetSelection(): void {
97
+ this.guard()
98
+ this.lib.editorViewResetSelection(this.viewPtr)
99
+ }
100
+
101
+ public getSelection(): { start: number; end: number } | null {
102
+ this.guard()
103
+ return this.lib.editorViewGetSelection(this.viewPtr)
104
+ }
105
+
106
+ public hasSelection(): boolean {
107
+ this.guard()
108
+ return this.getSelection() !== null
109
+ }
110
+
111
+ public setLocalSelection(
112
+ anchorX: number,
113
+ anchorY: number,
114
+ focusX: number,
115
+ focusY: number,
116
+ bgColor?: RGBA,
117
+ fgColor?: RGBA,
118
+ updateCursor?: boolean,
119
+ followCursor?: boolean,
120
+ ): boolean {
121
+ this.guard()
122
+ return this.lib.editorViewSetLocalSelection(
123
+ this.viewPtr,
124
+ anchorX,
125
+ anchorY,
126
+ focusX,
127
+ focusY,
128
+ bgColor || null,
129
+ fgColor || null,
130
+ updateCursor ?? false,
131
+ followCursor ?? false,
132
+ )
133
+ }
134
+
135
+ public updateLocalSelection(
136
+ anchorX: number,
137
+ anchorY: number,
138
+ focusX: number,
139
+ focusY: number,
140
+ bgColor?: RGBA,
141
+ fgColor?: RGBA,
142
+ updateCursor?: boolean,
143
+ followCursor?: boolean,
144
+ ): boolean {
145
+ this.guard()
146
+ return this.lib.editorViewUpdateLocalSelection(
147
+ this.viewPtr,
148
+ anchorX,
149
+ anchorY,
150
+ focusX,
151
+ focusY,
152
+ bgColor || null,
153
+ fgColor || null,
154
+ updateCursor ?? false,
155
+ followCursor ?? false,
156
+ )
157
+ }
158
+
159
+ public resetLocalSelection(): void {
160
+ this.guard()
161
+ this.lib.editorViewResetLocalSelection(this.viewPtr)
162
+ }
163
+
164
+ public getSelectedText(): string {
165
+ this.guard()
166
+ // TODO: native can stack alloc all the text and decode will alloc as js string then
167
+ const maxLength = 1024 * 1024 // 1MB should be enough for most selections
168
+ const selectedBytes = this.lib.editorViewGetSelectedTextBytes(this.viewPtr, maxLength)
169
+
170
+ if (!selectedBytes) return ""
171
+
172
+ return this.lib.decoder.decode(selectedBytes)
173
+ }
174
+
175
+ public getCursor(): { row: number; col: number } {
176
+ this.guard()
177
+ return this.lib.editorViewGetCursor(this.viewPtr)
178
+ }
179
+
180
+ public getText(): string {
181
+ this.guard()
182
+ const maxLength = 1024 * 1024 // 1MB buffer
183
+ const textBytes = this.lib.editorViewGetText(this.viewPtr, maxLength)
184
+ if (!textBytes) return ""
185
+ return this.lib.decoder.decode(textBytes)
186
+ }
187
+
188
+ public getVisualCursor(): VisualCursor {
189
+ this.guard()
190
+ return this.lib.editorViewGetVisualCursor(this.viewPtr)
191
+ }
192
+
193
+ public moveUpVisual(): void {
194
+ this.guard()
195
+ this.lib.editorViewMoveUpVisual(this.viewPtr)
196
+ }
197
+
198
+ public moveDownVisual(): void {
199
+ this.guard()
200
+ this.lib.editorViewMoveDownVisual(this.viewPtr)
201
+ }
202
+
203
+ public deleteSelectedText(): void {
204
+ this.guard()
205
+ this.lib.editorViewDeleteSelectedText(this.viewPtr)
206
+ }
207
+
208
+ public setCursorByOffset(offset: number): void {
209
+ this.guard()
210
+ this.lib.editorViewSetCursorByOffset(this.viewPtr, offset)
211
+ }
212
+
213
+ public getNextWordBoundary(): VisualCursor {
214
+ this.guard()
215
+ return this.lib.editorViewGetNextWordBoundary(this.viewPtr)
216
+ }
217
+
218
+ public getPrevWordBoundary(): VisualCursor {
219
+ this.guard()
220
+ return this.lib.editorViewGetPrevWordBoundary(this.viewPtr)
221
+ }
222
+
223
+ public getEOL(): VisualCursor {
224
+ this.guard()
225
+ return this.lib.editorViewGetEOL(this.viewPtr)
226
+ }
227
+
228
+ public getVisualSOL(): VisualCursor {
229
+ this.guard()
230
+ return this.lib.editorViewGetVisualSOL(this.viewPtr)
231
+ }
232
+
233
+ public getVisualEOL(): VisualCursor {
234
+ this.guard()
235
+ return this.lib.editorViewGetVisualEOL(this.viewPtr)
236
+ }
237
+
238
+ public getLineInfo(): LineInfo {
239
+ this.guard()
240
+ return this.lib.editorViewGetLineInfo(this.viewPtr)
241
+ }
242
+
243
+ public getLogicalLineInfo(): LineInfo {
244
+ this.guard()
245
+ return this.lib.editorViewGetLogicalLineInfo(this.viewPtr)
246
+ }
247
+
248
+ public get extmarks(): any {
249
+ if (!this._extmarksController) {
250
+ this._extmarksController = createExtmarksController(this.editBuffer, this)
251
+ }
252
+ return this._extmarksController
253
+ }
254
+
255
+ public setPlaceholderStyledText(chunks: { text: string; fg?: RGBA; bg?: RGBA; attributes?: number }[]): void {
256
+ this.guard()
257
+ this.lib.editorViewSetPlaceholderStyledText(this.viewPtr, chunks)
258
+ }
259
+
260
+ public setTabIndicator(indicator: string | number): void {
261
+ this.guard()
262
+ const codePoint = typeof indicator === "string" ? (indicator.codePointAt(0) ?? 0) : indicator
263
+ this.lib.editorViewSetTabIndicator(this.viewPtr, codePoint)
264
+ }
265
+
266
+ public setTabIndicatorColor(color: RGBA): void {
267
+ this.guard()
268
+ this.lib.editorViewSetTabIndicatorColor(this.viewPtr, color)
269
+ }
270
+
271
+ public measureForDimensions(width: number, height: number): { lineCount: number; widthColsMax: number } | null {
272
+ this.guard()
273
+ if (!this._textBufferViewPtr) {
274
+ this._textBufferViewPtr = this.lib.editorViewGetTextBufferView(this.viewPtr)
275
+ }
276
+ return this.lib.textBufferViewMeasureForDimensions(this._textBufferViewPtr, width, height)
277
+ }
278
+
279
+ public destroy(): void {
280
+ if (this._destroyed) return
281
+
282
+ if (this._extmarksController) {
283
+ this._extmarksController.destroy()
284
+ this._extmarksController = undefined
285
+ }
286
+
287
+ this._destroyed = true
288
+ this.lib.destroyEditorView(this.viewPtr)
289
+ }
290
+ }
package/src/index.ts ADDED
@@ -0,0 +1,25 @@
1
+ // Core exports without 3D dependencies
2
+ export * from "./Renderable.js"
3
+ export * from "./types.js"
4
+ export * from "./utils.js"
5
+ export * from "./buffer.js"
6
+ export * from "./text-buffer.js"
7
+ export * from "./text-buffer-view.js"
8
+ export * from "./edit-buffer.js"
9
+ export * from "./editor-view.js"
10
+ export * from "./syntax-style.js"
11
+ export * from "./post/effects.js"
12
+ export * from "./post/filters.js"
13
+ export * from "./post/matrices.js"
14
+ export * from "./animation/Timeline.js"
15
+ export * from "./lib/index.js"
16
+ export * from "./renderer.js"
17
+ export * from "./plugins/types.js"
18
+ export * from "./plugins/registry.js"
19
+ export * from "./plugins/core-slot.js"
20
+ export * from "./NativeSpanFeed.js"
21
+ export * from "./audio.js"
22
+ export * from "./renderables/index.js"
23
+ export * from "./zig.js"
24
+ export * from "./console.js"
25
+ export * as Yoga from "./yoga.js"
@@ -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
+ })