@fairyhunter13/opentui-core 0.1.88 → 0.1.90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (570) hide show
  1. package/dev/keypress-debug-renderer.ts +148 -0
  2. package/dev/keypress-debug.ts +43 -0
  3. package/dev/print-env-vars.ts +32 -0
  4. package/dev/test-tmux-graphics-334.sh +68 -0
  5. package/dev/thai-debug-test.ts +68 -0
  6. package/docs/development.md +141 -0
  7. package/docs/env-vars.md +140 -0
  8. package/docs/getting-started.md +353 -0
  9. package/docs/renderables-vs-constructs.md +159 -0
  10. package/docs/tree-sitter.md +311 -0
  11. package/package.json +61 -52
  12. package/scripts/build.ts +400 -0
  13. package/scripts/publish.ts +60 -0
  14. package/src/3d/SpriteResourceManager.ts +286 -0
  15. package/src/3d/SpriteUtils.ts +71 -0
  16. package/src/3d/TextureUtils.ts +196 -0
  17. package/src/3d/ThreeRenderable.ts +197 -0
  18. package/src/3d/WGPURenderer.ts +294 -0
  19. package/src/3d/animation/ExplodingSpriteEffect.ts +513 -0
  20. package/src/3d/animation/PhysicsExplodingSpriteEffect.ts +429 -0
  21. package/src/3d/animation/SpriteAnimator.ts +633 -0
  22. package/src/3d/animation/SpriteParticleGenerator.ts +435 -0
  23. package/src/3d/canvas.ts +464 -0
  24. package/src/3d/index.ts +12 -0
  25. package/src/3d/physics/PlanckPhysicsAdapter.ts +72 -0
  26. package/src/3d/physics/RapierPhysicsAdapter.ts +66 -0
  27. package/src/3d/physics/physics-interface.ts +31 -0
  28. package/src/3d/shaders/supersampling.wgsl +201 -0
  29. package/src/3d.ts +3 -0
  30. package/src/NativeSpanFeed.ts +300 -0
  31. package/src/Renderable.ts +1698 -0
  32. package/src/__snapshots__/buffer.test.ts.snap +28 -0
  33. package/src/animation/Timeline.test.ts +2709 -0
  34. package/src/animation/Timeline.ts +598 -0
  35. package/src/ansi.ts +18 -0
  36. package/src/benchmark/latest-all-bench-run.json +707 -0
  37. package/src/benchmark/latest-async-bench-run.json +336 -0
  38. package/src/benchmark/latest-default-bench-run.json +657 -0
  39. package/src/benchmark/latest-large-bench-run.json +707 -0
  40. package/src/benchmark/latest-quick-bench-run.json +207 -0
  41. package/src/benchmark/markdown-benchmark.ts +1804 -0
  42. package/src/benchmark/native-span-feed-async-benchmark.ts +355 -0
  43. package/src/benchmark/native-span-feed-benchmark.md +56 -0
  44. package/src/benchmark/native-span-feed-benchmark.ts +596 -0
  45. package/src/benchmark/native-span-feed-compare.ts +280 -0
  46. package/src/benchmark/renderer-benchmark.ts +754 -0
  47. package/src/benchmark/text-table-benchmark.ts +947 -0
  48. package/src/buffer.test.ts +291 -0
  49. package/src/buffer.ts +519 -0
  50. package/src/console.test.ts +612 -0
  51. package/src/console.ts +1255 -0
  52. package/src/edit-buffer.test.ts +1769 -0
  53. package/src/edit-buffer.ts +411 -0
  54. package/src/editor-view.test.ts +1032 -0
  55. package/src/editor-view.ts +284 -0
  56. package/src/examples/ascii-font-selection-demo.ts +245 -0
  57. package/src/examples/assets/Water_2_M_Normal.jpg +0 -0
  58. package/src/examples/assets/concrete.png +0 -0
  59. package/src/examples/assets/crate.png +0 -0
  60. package/src/examples/assets/crate_emissive.png +0 -0
  61. package/src/examples/assets/forrest_background.png +0 -0
  62. package/src/examples/assets/hast-example.json +1018 -0
  63. package/src/examples/assets/heart.png +0 -0
  64. package/src/examples/assets/main_char_heavy_attack.png +0 -0
  65. package/src/examples/assets/main_char_idle.png +0 -0
  66. package/src/examples/assets/main_char_jump_end.png +0 -0
  67. package/src/examples/assets/main_char_jump_landing.png +0 -0
  68. package/src/examples/assets/main_char_jump_start.png +0 -0
  69. package/src/examples/assets/main_char_run_loop.png +0 -0
  70. package/src/examples/assets/roughness_map.jpg +0 -0
  71. package/src/examples/build.ts +115 -0
  72. package/src/examples/code-demo.ts +584 -0
  73. package/src/examples/console-demo.ts +358 -0
  74. package/src/examples/core-plugin-slots-demo.ts +759 -0
  75. package/src/examples/diff-demo.ts +699 -0
  76. package/src/examples/draggable-three-demo.ts +259 -0
  77. package/src/examples/editor-demo.ts +322 -0
  78. package/src/examples/extmarks-demo.ts +204 -0
  79. package/src/examples/focus-restore-demo.ts +310 -0
  80. package/src/examples/fonts.ts +245 -0
  81. package/src/examples/fractal-shader-demo.ts +268 -0
  82. package/src/examples/framebuffer-demo.ts +674 -0
  83. package/src/examples/full-unicode-demo.ts +181 -0
  84. package/src/examples/golden-star-demo.ts +933 -0
  85. package/src/examples/grayscale-buffer-demo.ts +249 -0
  86. package/src/examples/hast-syntax-highlighting-demo.ts +129 -0
  87. package/src/examples/index.ts +925 -0
  88. package/src/examples/input-demo.ts +377 -0
  89. package/src/examples/input-select-layout-demo.ts +425 -0
  90. package/src/examples/install.sh +143 -0
  91. package/src/examples/keypress-debug-demo.ts +452 -0
  92. package/src/examples/lib/HexList.ts +122 -0
  93. package/src/examples/lib/PaletteGrid.ts +125 -0
  94. package/src/examples/lib/standalone-keys.ts +25 -0
  95. package/src/examples/lib/tab-controller.ts +243 -0
  96. package/src/examples/lights-phong-demo.ts +290 -0
  97. package/src/examples/link-demo.ts +220 -0
  98. package/src/examples/live-state-demo.ts +480 -0
  99. package/src/examples/markdown-demo.ts +620 -0
  100. package/src/examples/mouse-interaction-demo.ts +428 -0
  101. package/src/examples/nested-zindex-demo.ts +357 -0
  102. package/src/examples/opacity-example.ts +235 -0
  103. package/src/examples/opentui-demo.ts +1057 -0
  104. package/src/examples/physx-planck-2d-demo.ts +507 -0
  105. package/src/examples/physx-rapier-2d-demo.ts +526 -0
  106. package/src/examples/relative-positioning-demo.ts +323 -0
  107. package/src/examples/scroll-example.ts +214 -0
  108. package/src/examples/scrollbox-mouse-test.ts +112 -0
  109. package/src/examples/scrollbox-overlay-hit-test.ts +206 -0
  110. package/src/examples/select-demo.ts +237 -0
  111. package/src/examples/shader-cube-demo.ts +772 -0
  112. package/src/examples/simple-layout-example.ts +591 -0
  113. package/src/examples/slider-demo.ts +617 -0
  114. package/src/examples/split-mode-demo.ts +445 -0
  115. package/src/examples/sprite-animation-demo.ts +443 -0
  116. package/src/examples/sprite-particle-generator-demo.ts +486 -0
  117. package/src/examples/static-sprite-demo.ts +193 -0
  118. package/src/examples/sticky-scroll-example.ts +308 -0
  119. package/src/examples/styled-text-demo.ts +282 -0
  120. package/src/examples/tab-select-demo.ts +219 -0
  121. package/src/examples/terminal-title.ts +29 -0
  122. package/src/examples/terminal.ts +305 -0
  123. package/src/examples/text-node-demo.ts +416 -0
  124. package/src/examples/text-selection-demo.ts +377 -0
  125. package/src/examples/text-table-demo.ts +503 -0
  126. package/src/examples/text-truncation-demo.ts +481 -0
  127. package/src/examples/text-wrap.ts +757 -0
  128. package/src/examples/texture-loading-demo.ts +259 -0
  129. package/src/examples/timeline-example.ts +670 -0
  130. package/src/examples/transparency-demo.ts +241 -0
  131. package/src/examples/vnode-composition-demo.ts +404 -0
  132. package/src/index.ts +22 -0
  133. package/src/lib/KeyHandler.integration.test.ts +292 -0
  134. package/src/lib/KeyHandler.stopPropagation.test.ts +289 -0
  135. package/src/lib/KeyHandler.test.ts +662 -0
  136. package/src/lib/KeyHandler.ts +222 -0
  137. package/src/lib/RGBA.test.ts +984 -0
  138. package/src/lib/RGBA.ts +204 -0
  139. package/src/lib/ascii.font.ts +330 -0
  140. package/src/lib/border.test.ts +83 -0
  141. package/src/lib/border.ts +168 -0
  142. package/src/lib/bunfs.test.ts +27 -0
  143. package/src/lib/bunfs.ts +18 -0
  144. package/src/lib/clipboard.test.ts +41 -0
  145. package/src/lib/clipboard.ts +47 -0
  146. package/src/lib/clock.ts +31 -0
  147. package/src/lib/data-paths.test.ts +133 -0
  148. package/src/lib/data-paths.ts +109 -0
  149. package/src/lib/debounce.ts +106 -0
  150. package/src/lib/detect-links.test.ts +98 -0
  151. package/src/lib/detect-links.ts +56 -0
  152. package/src/lib/env.test.ts +228 -0
  153. package/src/lib/env.ts +209 -0
  154. package/src/lib/extmarks-history.ts +51 -0
  155. package/src/lib/extmarks-multiwidth.test.ts +322 -0
  156. package/src/lib/extmarks.test.ts +3457 -0
  157. package/src/lib/extmarks.ts +843 -0
  158. package/src/lib/fonts/block.json +405 -0
  159. package/src/lib/fonts/grid.json +265 -0
  160. package/src/lib/fonts/huge.json +741 -0
  161. package/src/lib/fonts/pallet.json +314 -0
  162. package/src/lib/fonts/shade.json +591 -0
  163. package/src/lib/fonts/slick.json +321 -0
  164. package/src/lib/fonts/tiny.json +69 -0
  165. package/src/lib/hast-styled-text.ts +59 -0
  166. package/src/lib/index.ts +21 -0
  167. package/src/lib/keymapping.test.ts +280 -0
  168. package/src/lib/keymapping.ts +87 -0
  169. package/src/lib/objects-in-viewport.test.ts +787 -0
  170. package/src/lib/objects-in-viewport.ts +153 -0
  171. package/src/lib/output.capture.ts +58 -0
  172. package/src/lib/parse.keypress-kitty.protocol.test.ts +340 -0
  173. package/src/lib/parse.keypress-kitty.test.ts +663 -0
  174. package/src/lib/parse.keypress-kitty.ts +439 -0
  175. package/src/lib/parse.keypress.test.ts +1849 -0
  176. package/src/lib/parse.keypress.ts +397 -0
  177. package/src/lib/parse.mouse.test.ts +552 -0
  178. package/src/lib/parse.mouse.ts +232 -0
  179. package/src/lib/paste.ts +16 -0
  180. package/src/lib/queue.ts +65 -0
  181. package/src/lib/renderable.validations.test.ts +87 -0
  182. package/src/lib/renderable.validations.ts +83 -0
  183. package/src/lib/scroll-acceleration.ts +98 -0
  184. package/src/lib/selection.ts +240 -0
  185. package/src/lib/singleton.ts +28 -0
  186. package/src/lib/stdin-parser.test.ts +1676 -0
  187. package/src/lib/stdin-parser.ts +1248 -0
  188. package/src/lib/styled-text.ts +178 -0
  189. package/src/lib/terminal-capability-detection.test.ts +202 -0
  190. package/src/lib/terminal-capability-detection.ts +79 -0
  191. package/src/lib/terminal-palette.test.ts +878 -0
  192. package/src/lib/terminal-palette.ts +383 -0
  193. package/src/lib/tree-sitter/assets/README.md +118 -0
  194. package/src/lib/tree-sitter/assets/update.ts +331 -0
  195. package/src/lib/tree-sitter/assets.d.ts +9 -0
  196. package/src/lib/tree-sitter/cache.test.ts +270 -0
  197. package/src/lib/tree-sitter/client.test.ts +1061 -0
  198. package/src/lib/tree-sitter/client.ts +615 -0
  199. package/src/lib/tree-sitter/default-parsers.ts +80 -0
  200. package/src/lib/tree-sitter/download-utils.ts +148 -0
  201. package/src/lib/tree-sitter/index.ts +28 -0
  202. package/src/lib/tree-sitter/parser.worker.ts +1001 -0
  203. package/src/lib/tree-sitter/parsers-config.ts +75 -0
  204. package/src/lib/tree-sitter/resolve-ft.ts +62 -0
  205. package/src/lib/tree-sitter/types.ts +81 -0
  206. package/src/lib/tree-sitter-styled-text.test.ts +1253 -0
  207. package/src/lib/tree-sitter-styled-text.ts +306 -0
  208. package/src/lib/validate-dir-name.ts +55 -0
  209. package/src/lib/yoga.options.test.ts +628 -0
  210. package/src/lib/yoga.options.ts +346 -0
  211. package/src/plugins/core-slot.ts +579 -0
  212. package/src/plugins/registry.ts +377 -0
  213. package/src/plugins/types.ts +46 -0
  214. package/src/post/filters.ts +888 -0
  215. package/src/renderables/ASCIIFont.ts +219 -0
  216. package/src/renderables/Box.test.ts +160 -0
  217. package/src/renderables/Box.ts +295 -0
  218. package/src/renderables/Code.test.ts +2062 -0
  219. package/src/renderables/Code.ts +357 -0
  220. package/src/renderables/Diff.regression.test.ts +226 -0
  221. package/src/renderables/Diff.test.ts +3027 -0
  222. package/src/renderables/Diff.ts +1209 -0
  223. package/src/renderables/EditBufferRenderable.ts +764 -0
  224. package/src/renderables/FrameBuffer.ts +47 -0
  225. package/src/renderables/Input.test.ts +1228 -0
  226. package/src/renderables/Input.ts +245 -0
  227. package/src/renderables/LineNumberRenderable.ts +675 -0
  228. package/src/renderables/Markdown.ts +1106 -0
  229. package/src/renderables/ScrollBar.ts +422 -0
  230. package/src/renderables/ScrollBox.ts +883 -0
  231. package/src/renderables/Select.test.ts +1010 -0
  232. package/src/renderables/Select.ts +523 -0
  233. package/src/renderables/Slider.test.ts +456 -0
  234. package/src/renderables/Slider.ts +347 -0
  235. package/src/renderables/TabSelect.test.ts +197 -0
  236. package/src/renderables/TabSelect.ts +455 -0
  237. package/src/renderables/Text.selection-buffer.test.ts +123 -0
  238. package/src/renderables/Text.test.ts +2660 -0
  239. package/src/renderables/Text.ts +147 -0
  240. package/src/renderables/TextBufferRenderable.ts +518 -0
  241. package/src/renderables/TextNode.test.ts +1058 -0
  242. package/src/renderables/TextNode.ts +325 -0
  243. package/src/renderables/TextTable.test.ts +1421 -0
  244. package/src/renderables/TextTable.ts +1344 -0
  245. package/src/renderables/Textarea.ts +732 -0
  246. package/src/renderables/TimeToFirstDraw.ts +89 -0
  247. package/src/renderables/__snapshots__/Code.test.ts.snap +13 -0
  248. package/src/renderables/__snapshots__/Diff.test.ts.snap +785 -0
  249. package/src/renderables/__snapshots__/Text.test.ts.snap +421 -0
  250. package/src/renderables/__snapshots__/TextTable.test.ts.snap +215 -0
  251. package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +144 -0
  252. package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +816 -0
  253. package/src/renderables/__tests__/LineNumberRenderable.test.ts +1787 -0
  254. package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +85 -0
  255. package/src/renderables/__tests__/Markdown.test.ts +2287 -0
  256. package/src/renderables/__tests__/MultiRenderable.selection.test.ts +87 -0
  257. package/src/renderables/__tests__/Textarea.buffer.test.ts +682 -0
  258. package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +675 -0
  259. package/src/renderables/__tests__/Textarea.editing.test.ts +2041 -0
  260. package/src/renderables/__tests__/Textarea.error-handling.test.ts +35 -0
  261. package/src/renderables/__tests__/Textarea.events.test.ts +738 -0
  262. package/src/renderables/__tests__/Textarea.highlights.test.ts +590 -0
  263. package/src/renderables/__tests__/Textarea.keybinding.test.ts +3149 -0
  264. package/src/renderables/__tests__/Textarea.paste.test.ts +357 -0
  265. package/src/renderables/__tests__/Textarea.rendering.test.ts +1864 -0
  266. package/src/renderables/__tests__/Textarea.scroll.test.ts +733 -0
  267. package/src/renderables/__tests__/Textarea.selection.test.ts +1590 -0
  268. package/src/renderables/__tests__/Textarea.stress.test.ts +670 -0
  269. package/src/renderables/__tests__/Textarea.undo-redo.test.ts +383 -0
  270. package/src/renderables/__tests__/Textarea.visual-lines.test.ts +310 -0
  271. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +221 -0
  272. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +89 -0
  273. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +457 -0
  274. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +158 -0
  275. package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +387 -0
  276. package/src/renderables/__tests__/markdown-parser.test.ts +217 -0
  277. package/src/renderables/__tests__/renderable-test-utils.ts +60 -0
  278. package/src/renderables/composition/README.md +8 -0
  279. package/src/renderables/composition/VRenderable.ts +32 -0
  280. package/src/renderables/composition/constructs.ts +127 -0
  281. package/src/renderables/composition/vnode.ts +289 -0
  282. package/src/renderables/index.ts +22 -0
  283. package/src/renderables/markdown-parser.ts +66 -0
  284. package/src/renderer.ts +2363 -0
  285. package/src/runtime-plugin-support.ts +39 -0
  286. package/src/runtime-plugin.ts +144 -0
  287. package/src/syntax-style.test.ts +841 -0
  288. package/src/syntax-style.ts +264 -0
  289. package/src/testing/README.md +210 -0
  290. package/src/testing/capture-spans.test.ts +194 -0
  291. package/src/testing/integration.test.ts +276 -0
  292. package/src/testing/manual-clock.ts +106 -0
  293. package/src/testing/mock-keys.test.ts +1356 -0
  294. package/src/testing/mock-keys.ts +449 -0
  295. package/src/testing/mock-mouse.test.ts +218 -0
  296. package/src/testing/mock-mouse.ts +247 -0
  297. package/src/testing/mock-tree-sitter-client.ts +73 -0
  298. package/src/testing/spy.ts +13 -0
  299. package/src/testing/test-recorder.test.ts +415 -0
  300. package/src/testing/test-recorder.ts +145 -0
  301. package/src/testing/test-renderer.ts +116 -0
  302. package/src/testing.ts +7 -0
  303. package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +481 -0
  304. package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +19 -0
  305. package/src/tests/__snapshots__/scrollbox.test.ts.snap +29 -0
  306. package/src/tests/absolute-positioning.snapshot.test.ts +638 -0
  307. package/src/tests/allocator-stats.test.ts +38 -0
  308. package/src/tests/destroy-during-render.test.ts +200 -0
  309. package/src/tests/hover-cursor.test.ts +98 -0
  310. package/src/tests/native-span-feed-async.test.ts +173 -0
  311. package/src/tests/native-span-feed-close.test.ts +120 -0
  312. package/src/tests/native-span-feed-coverage.test.ts +227 -0
  313. package/src/tests/native-span-feed-edge-cases.test.ts +352 -0
  314. package/src/tests/native-span-feed-use-after-free.test.ts +45 -0
  315. package/src/tests/opacity.test.ts +123 -0
  316. package/src/tests/renderable.snapshot.test.ts +524 -0
  317. package/src/tests/renderable.test.ts +1281 -0
  318. package/src/tests/renderer.console-startup.test.ts +65 -0
  319. package/src/tests/renderer.control.test.ts +364 -0
  320. package/src/tests/renderer.core-slot-binding.test.ts +952 -0
  321. package/src/tests/renderer.cursor.test.ts +26 -0
  322. package/src/tests/renderer.destroy-during-render.test.ts +110 -0
  323. package/src/tests/renderer.focus-restore.test.ts +228 -0
  324. package/src/tests/renderer.focus.test.ts +251 -0
  325. package/src/tests/renderer.idle.test.ts +219 -0
  326. package/src/tests/renderer.input.test.ts +2145 -0
  327. package/src/tests/renderer.kitty-flags.test.ts +195 -0
  328. package/src/tests/renderer.mouse.test.ts +1269 -0
  329. package/src/tests/renderer.palette.test.ts +629 -0
  330. package/src/tests/renderer.selection.test.ts +49 -0
  331. package/src/tests/renderer.slot-registry.test.ts +649 -0
  332. package/src/tests/renderer.useMouse.test.ts +50 -0
  333. package/src/tests/runtime-plugin-support.fixture.ts +11 -0
  334. package/src/tests/runtime-plugin-support.test.ts +28 -0
  335. package/src/tests/runtime-plugin.fixture.ts +40 -0
  336. package/src/tests/runtime-plugin.test.ts +190 -0
  337. package/src/tests/scrollbox-culling-bug.test.ts +114 -0
  338. package/src/tests/scrollbox-hitgrid-resize.test.ts +136 -0
  339. package/src/tests/scrollbox-hitgrid.test.ts +909 -0
  340. package/src/tests/scrollbox.test.ts +1530 -0
  341. package/src/tests/wrap-resize-perf.test.ts +229 -0
  342. package/src/tests/yoga-setters.test.ts +921 -0
  343. package/src/text-buffer-view.test.ts +705 -0
  344. package/src/text-buffer-view.ts +189 -0
  345. package/src/text-buffer.test.ts +347 -0
  346. package/src/text-buffer.ts +250 -0
  347. package/src/types.ts +152 -0
  348. package/src/utils.ts +88 -0
  349. package/src/zig/ansi.zig +268 -0
  350. package/src/zig/bench/README.md +50 -0
  351. package/src/zig/bench/buffer-draw-text-buffer_bench.zig +887 -0
  352. package/src/zig/bench/edit-buffer_bench.zig +476 -0
  353. package/src/zig/bench/native-span-feed_bench.zig +100 -0
  354. package/src/zig/bench/rope-markers_bench.zig +713 -0
  355. package/src/zig/bench/rope_bench.zig +514 -0
  356. package/src/zig/bench/styled-text_bench.zig +470 -0
  357. package/src/zig/bench/text-buffer-coords_bench.zig +362 -0
  358. package/src/zig/bench/text-buffer-view_bench.zig +459 -0
  359. package/src/zig/bench/text-chunk-graphemes_bench.zig +273 -0
  360. package/src/zig/bench/utf8_bench.zig +799 -0
  361. package/src/zig/bench-utils.zig +431 -0
  362. package/src/zig/bench.zig +217 -0
  363. package/src/zig/buffer.zig +2223 -0
  364. package/src/zig/build.zig +289 -0
  365. package/src/zig/build.zig.zon +16 -0
  366. package/src/zig/edit-buffer.zig +825 -0
  367. package/src/zig/editor-view.zig +802 -0
  368. package/src/zig/event-bus.zig +13 -0
  369. package/src/zig/event-emitter.zig +65 -0
  370. package/src/zig/file-logger.zig +92 -0
  371. package/src/zig/grapheme.zig +599 -0
  372. package/src/zig/lib.zig +1834 -0
  373. package/src/zig/link.zig +333 -0
  374. package/src/zig/logger.zig +43 -0
  375. package/src/zig/mem-registry.zig +125 -0
  376. package/src/zig/native-span-feed-bench-lib.zig +7 -0
  377. package/src/zig/native-span-feed.zig +708 -0
  378. package/src/zig/renderer.zig +1386 -0
  379. package/src/zig/rope.zig +1220 -0
  380. package/src/zig/syntax-style.zig +161 -0
  381. package/src/zig/terminal.zig +975 -0
  382. package/src/zig/test.zig +70 -0
  383. package/src/zig/tests/README.md +18 -0
  384. package/src/zig/tests/buffer_test.zig +2526 -0
  385. package/src/zig/tests/edit-buffer-history_test.zig +271 -0
  386. package/src/zig/tests/edit-buffer_test.zig +1689 -0
  387. package/src/zig/tests/editor-view_test.zig +3299 -0
  388. package/src/zig/tests/event-emitter_test.zig +249 -0
  389. package/src/zig/tests/grapheme_test.zig +1304 -0
  390. package/src/zig/tests/link_test.zig +190 -0
  391. package/src/zig/tests/mem-registry_test.zig +473 -0
  392. package/src/zig/tests/memory_leak_regression_test.zig +159 -0
  393. package/src/zig/tests/native-span-feed_test.zig +1264 -0
  394. package/src/zig/tests/renderer_test.zig +1010 -0
  395. package/src/zig/tests/rope-nested_test.zig +712 -0
  396. package/src/zig/tests/rope_fuzz_test.zig +238 -0
  397. package/src/zig/tests/rope_test.zig +2362 -0
  398. package/src/zig/tests/segment-merge.test.zig +148 -0
  399. package/src/zig/tests/syntax-style_test.zig +557 -0
  400. package/src/zig/tests/terminal_test.zig +719 -0
  401. package/src/zig/tests/text-buffer-drawing_test.zig +3237 -0
  402. package/src/zig/tests/text-buffer-highlights_test.zig +666 -0
  403. package/src/zig/tests/text-buffer-iterators_test.zig +776 -0
  404. package/src/zig/tests/text-buffer-segment_test.zig +320 -0
  405. package/src/zig/tests/text-buffer-selection_test.zig +1035 -0
  406. package/src/zig/tests/text-buffer-selection_viewport_test.zig +358 -0
  407. package/src/zig/tests/text-buffer-view_test.zig +3649 -0
  408. package/src/zig/tests/text-buffer_test.zig +2191 -0
  409. package/src/zig/tests/unicode-width-map.zon +3909 -0
  410. package/src/zig/tests/utf8_no_zwj_test.zig +260 -0
  411. package/src/zig/tests/utf8_test.zig +4057 -0
  412. package/src/zig/tests/utf8_wcwidth_cursor_test.zig +267 -0
  413. package/src/zig/tests/utf8_wcwidth_test.zig +357 -0
  414. package/src/zig/tests/word-wrap-editing_test.zig +498 -0
  415. package/src/zig/tests/wrap-cache-perf_test.zig +113 -0
  416. package/src/zig/text-buffer-iterators.zig +499 -0
  417. package/src/zig/text-buffer-segment.zig +404 -0
  418. package/src/zig/text-buffer-view.zig +1371 -0
  419. package/src/zig/text-buffer.zig +1180 -0
  420. package/src/zig/utf8.zig +1948 -0
  421. package/src/zig/utils.zig +9 -0
  422. package/src/zig-structs.ts +261 -0
  423. package/src/zig.ts +3843 -0
  424. package/tsconfig.build.json +22 -0
  425. package/tsconfig.json +28 -0
  426. package/3d/SpriteResourceManager.d.ts +0 -74
  427. package/3d/SpriteUtils.d.ts +0 -13
  428. package/3d/TextureUtils.d.ts +0 -24
  429. package/3d/ThreeRenderable.d.ts +0 -40
  430. package/3d/WGPURenderer.d.ts +0 -61
  431. package/3d/animation/ExplodingSpriteEffect.d.ts +0 -71
  432. package/3d/animation/PhysicsExplodingSpriteEffect.d.ts +0 -76
  433. package/3d/animation/SpriteAnimator.d.ts +0 -124
  434. package/3d/animation/SpriteParticleGenerator.d.ts +0 -62
  435. package/3d/canvas.d.ts +0 -44
  436. package/3d/index.d.ts +0 -12
  437. package/3d/physics/PlanckPhysicsAdapter.d.ts +0 -19
  438. package/3d/physics/RapierPhysicsAdapter.d.ts +0 -19
  439. package/3d/physics/physics-interface.d.ts +0 -27
  440. package/3d.d.ts +0 -2
  441. package/3d.js +0 -34042
  442. package/3d.js.map +0 -155
  443. package/LICENSE +0 -21
  444. package/NativeSpanFeed.d.ts +0 -41
  445. package/Renderable.d.ts +0 -334
  446. package/animation/Timeline.d.ts +0 -126
  447. package/ansi.d.ts +0 -13
  448. package/buffer.d.ts +0 -107
  449. package/console.d.ts +0 -143
  450. package/edit-buffer.d.ts +0 -98
  451. package/editor-view.d.ts +0 -73
  452. package/index-e4hzc2j2.js +0 -113
  453. package/index-e4hzc2j2.js.map +0 -10
  454. package/index-nkrr8a4c.js +0 -18415
  455. package/index-nkrr8a4c.js.map +0 -64
  456. package/index-nyw5p3ep.js +0 -12619
  457. package/index-nyw5p3ep.js.map +0 -43
  458. package/index.d.ts +0 -21
  459. package/index.js +0 -430
  460. package/index.js.map +0 -9
  461. package/lib/KeyHandler.d.ts +0 -61
  462. package/lib/RGBA.d.ts +0 -25
  463. package/lib/ascii.font.d.ts +0 -508
  464. package/lib/border.d.ts +0 -49
  465. package/lib/bunfs.d.ts +0 -7
  466. package/lib/clipboard.d.ts +0 -17
  467. package/lib/clock.d.ts +0 -15
  468. package/lib/data-paths.d.ts +0 -26
  469. package/lib/debounce.d.ts +0 -42
  470. package/lib/detect-links.d.ts +0 -6
  471. package/lib/env.d.ts +0 -42
  472. package/lib/extmarks-history.d.ts +0 -17
  473. package/lib/extmarks.d.ts +0 -89
  474. package/lib/hast-styled-text.d.ts +0 -17
  475. package/lib/index.d.ts +0 -21
  476. package/lib/keymapping.d.ts +0 -25
  477. package/lib/objects-in-viewport.d.ts +0 -24
  478. package/lib/output.capture.d.ts +0 -24
  479. package/lib/parse.keypress-kitty.d.ts +0 -2
  480. package/lib/parse.keypress.d.ts +0 -26
  481. package/lib/parse.mouse.d.ts +0 -30
  482. package/lib/paste.d.ts +0 -7
  483. package/lib/queue.d.ts +0 -15
  484. package/lib/renderable.validations.d.ts +0 -12
  485. package/lib/scroll-acceleration.d.ts +0 -43
  486. package/lib/selection.d.ts +0 -63
  487. package/lib/singleton.d.ts +0 -7
  488. package/lib/stdin-parser.d.ts +0 -76
  489. package/lib/styled-text.d.ts +0 -63
  490. package/lib/terminal-capability-detection.d.ts +0 -30
  491. package/lib/terminal-palette.d.ts +0 -50
  492. package/lib/tree-sitter/assets/update.d.ts +0 -11
  493. package/lib/tree-sitter/client.d.ts +0 -47
  494. package/lib/tree-sitter/default-parsers.d.ts +0 -2
  495. package/lib/tree-sitter/download-utils.d.ts +0 -21
  496. package/lib/tree-sitter/index.d.ts +0 -8
  497. package/lib/tree-sitter/parser.worker.d.ts +0 -1
  498. package/lib/tree-sitter/parsers-config.d.ts +0 -38
  499. package/lib/tree-sitter/resolve-ft.d.ts +0 -2
  500. package/lib/tree-sitter/types.d.ts +0 -81
  501. package/lib/tree-sitter-styled-text.d.ts +0 -14
  502. package/lib/validate-dir-name.d.ts +0 -1
  503. package/lib/yoga.options.d.ts +0 -32
  504. package/parser.worker.js +0 -869
  505. package/parser.worker.js.map +0 -12
  506. package/plugins/core-slot.d.ts +0 -72
  507. package/plugins/registry.d.ts +0 -38
  508. package/plugins/types.d.ts +0 -34
  509. package/post/filters.d.ts +0 -105
  510. package/renderables/ASCIIFont.d.ts +0 -52
  511. package/renderables/Box.d.ts +0 -72
  512. package/renderables/Code.d.ts +0 -78
  513. package/renderables/Diff.d.ts +0 -142
  514. package/renderables/EditBufferRenderable.d.ts +0 -162
  515. package/renderables/FrameBuffer.d.ts +0 -16
  516. package/renderables/Input.d.ts +0 -67
  517. package/renderables/LineNumberRenderable.d.ts +0 -74
  518. package/renderables/Markdown.d.ts +0 -173
  519. package/renderables/ScrollBar.d.ts +0 -77
  520. package/renderables/ScrollBox.d.ts +0 -124
  521. package/renderables/Select.d.ts +0 -115
  522. package/renderables/Slider.d.ts +0 -44
  523. package/renderables/TabSelect.d.ts +0 -96
  524. package/renderables/Text.d.ts +0 -36
  525. package/renderables/TextBufferRenderable.d.ts +0 -105
  526. package/renderables/TextNode.d.ts +0 -91
  527. package/renderables/TextTable.d.ts +0 -140
  528. package/renderables/Textarea.d.ts +0 -114
  529. package/renderables/TimeToFirstDraw.d.ts +0 -24
  530. package/renderables/__tests__/renderable-test-utils.d.ts +0 -12
  531. package/renderables/composition/VRenderable.d.ts +0 -16
  532. package/renderables/composition/constructs.d.ts +0 -35
  533. package/renderables/composition/vnode.d.ts +0 -46
  534. package/renderables/index.d.ts +0 -22
  535. package/renderables/markdown-parser.d.ts +0 -10
  536. package/renderer.d.ts +0 -388
  537. package/runtime-plugin-support.d.ts +0 -3
  538. package/runtime-plugin-support.js +0 -29
  539. package/runtime-plugin-support.js.map +0 -10
  540. package/runtime-plugin.d.ts +0 -11
  541. package/runtime-plugin.js +0 -16
  542. package/runtime-plugin.js.map +0 -9
  543. package/syntax-style.d.ts +0 -54
  544. package/testing/manual-clock.d.ts +0 -16
  545. package/testing/mock-keys.d.ts +0 -81
  546. package/testing/mock-mouse.d.ts +0 -38
  547. package/testing/mock-tree-sitter-client.d.ts +0 -23
  548. package/testing/spy.d.ts +0 -7
  549. package/testing/test-recorder.d.ts +0 -61
  550. package/testing/test-renderer.d.ts +0 -23
  551. package/testing.d.ts +0 -6
  552. package/testing.js +0 -675
  553. package/testing.js.map +0 -15
  554. package/text-buffer-view.d.ts +0 -42
  555. package/text-buffer.d.ts +0 -67
  556. package/types.d.ts +0 -131
  557. package/utils.d.ts +0 -14
  558. package/zig-structs.d.ts +0 -155
  559. package/zig.d.ts +0 -351
  560. /package/{assets → src/lib/tree-sitter/assets}/javascript/highlights.scm +0 -0
  561. /package/{assets → src/lib/tree-sitter/assets}/javascript/tree-sitter-javascript.wasm +0 -0
  562. /package/{assets → src/lib/tree-sitter/assets}/markdown/highlights.scm +0 -0
  563. /package/{assets → src/lib/tree-sitter/assets}/markdown/injections.scm +0 -0
  564. /package/{assets → src/lib/tree-sitter/assets}/markdown/tree-sitter-markdown.wasm +0 -0
  565. /package/{assets → src/lib/tree-sitter/assets}/markdown_inline/highlights.scm +0 -0
  566. /package/{assets → src/lib/tree-sitter/assets}/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
  567. /package/{assets → src/lib/tree-sitter/assets}/typescript/highlights.scm +0 -0
  568. /package/{assets → src/lib/tree-sitter/assets}/typescript/tree-sitter-typescript.wasm +0 -0
  569. /package/{assets → src/lib/tree-sitter/assets}/zig/highlights.scm +0 -0
  570. /package/{assets → src/lib/tree-sitter/assets}/zig/tree-sitter-zig.wasm +0 -0
@@ -0,0 +1,738 @@
1
+ import { describe, expect, it, beforeEach, afterEach } from "bun:test"
2
+ import { createTestRenderer, type TestRenderer, type MockMouse, type MockInput } from "../../testing/test-renderer.js"
3
+ import { createTextareaRenderable } from "./renderable-test-utils.js"
4
+
5
+ let currentRenderer: TestRenderer
6
+ let renderOnce: () => Promise<void>
7
+ let currentMockInput: MockInput
8
+
9
+ describe("Textarea - Event Handlers Tests", () => {
10
+ beforeEach(async () => {
11
+ ;({
12
+ renderer: currentRenderer,
13
+ renderOnce,
14
+ mockInput: currentMockInput,
15
+ } = await createTestRenderer({
16
+ width: 80,
17
+ height: 24,
18
+ }))
19
+ })
20
+
21
+ afterEach(() => {
22
+ currentRenderer.destroy()
23
+ })
24
+
25
+ describe("Change Events", () => {
26
+ describe("onCursorChange", () => {
27
+ it("should fire onCursorChange when cursor moves", async () => {
28
+ let cursorChangeCount = 0
29
+ let lastCursorEvent: { line: number; visualColumn: number } | null = null
30
+
31
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
32
+ initialValue: "Line 1\nLine 2\nLine 3",
33
+ width: 40,
34
+ height: 10,
35
+ onCursorChange: (event) => {
36
+ cursorChangeCount++
37
+ lastCursorEvent = event
38
+ },
39
+ })
40
+
41
+ editor.focus()
42
+ const initialCount = cursorChangeCount
43
+
44
+ editor.moveCursorRight()
45
+ await new Promise((resolve) => setTimeout(resolve, 10))
46
+
47
+ expect(cursorChangeCount).toBeGreaterThan(initialCount)
48
+ expect(lastCursorEvent).not.toBe(null)
49
+ expect(lastCursorEvent!.line).toBe(0)
50
+ expect(lastCursorEvent!.visualColumn).toBe(1)
51
+
52
+ const prevCount = cursorChangeCount
53
+
54
+ editor.moveCursorDown()
55
+ await new Promise((resolve) => setTimeout(resolve, 20))
56
+
57
+ expect(cursorChangeCount).toBeGreaterThanOrEqual(prevCount)
58
+ expect(lastCursorEvent).not.toBe(null)
59
+ expect(lastCursorEvent!.line).toBeGreaterThanOrEqual(0)
60
+ })
61
+
62
+ it("should fire onCursorChange when typing moves cursor", async () => {
63
+ let cursorChangeCount = 0
64
+ let lastCursorEvent: { line: number; visualColumn: number } | null = null
65
+
66
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
67
+ initialValue: "",
68
+ width: 40,
69
+ height: 10,
70
+ onCursorChange: (event) => {
71
+ cursorChangeCount++
72
+ lastCursorEvent = event
73
+ },
74
+ })
75
+
76
+ editor.focus()
77
+ const initialCount = cursorChangeCount
78
+
79
+ currentMockInput.pressKey("H")
80
+ await new Promise((resolve) => setTimeout(resolve, 10))
81
+
82
+ expect(cursorChangeCount).toBeGreaterThan(initialCount)
83
+ expect(lastCursorEvent).not.toBe(null)
84
+ expect(lastCursorEvent!.line).toBe(0)
85
+ expect(lastCursorEvent!.visualColumn).toBe(1)
86
+ })
87
+
88
+ it("should fire onCursorChange when pressing arrow keys", async () => {
89
+ let cursorEventCount = 0
90
+ let lastCursorEvent: { line: number; visualColumn: number } | null = null
91
+
92
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
93
+ initialValue: "ABC\nDEF",
94
+ width: 40,
95
+ height: 10,
96
+ onCursorChange: (event) => {
97
+ cursorEventCount++
98
+ lastCursorEvent = event
99
+ },
100
+ })
101
+
102
+ editor.focus()
103
+ const initialCount = cursorEventCount
104
+
105
+ currentMockInput.pressArrow("right")
106
+ await new Promise((resolve) => setTimeout(resolve, 10))
107
+
108
+ expect(cursorEventCount).toBeGreaterThan(initialCount)
109
+ expect(lastCursorEvent).not.toBe(null)
110
+ expect(lastCursorEvent!.visualColumn).toBe(1)
111
+
112
+ const beforeDown = cursorEventCount
113
+ currentMockInput.pressArrow("down")
114
+ await new Promise((resolve) => setTimeout(resolve, 20))
115
+
116
+ expect(cursorEventCount).toBeGreaterThanOrEqual(beforeDown)
117
+ expect(lastCursorEvent).not.toBe(null)
118
+ })
119
+
120
+ it("should fire onCursorChange when using gotoLine", async () => {
121
+ let cursorChangeCount = 0
122
+ let lastCursorEvent: { line: number; visualColumn: number } | null = null
123
+
124
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
125
+ initialValue: "Line 0\nLine 1\nLine 2",
126
+ width: 40,
127
+ height: 10,
128
+ onCursorChange: (event) => {
129
+ cursorChangeCount++
130
+ lastCursorEvent = event
131
+ },
132
+ })
133
+
134
+ editor.focus()
135
+ const initialCount = cursorChangeCount
136
+
137
+ editor.gotoLine(2)
138
+ await new Promise((resolve) => setTimeout(resolve, 10))
139
+
140
+ expect(cursorChangeCount).toBeGreaterThan(initialCount)
141
+ expect(lastCursorEvent).not.toBe(null)
142
+ expect(lastCursorEvent!.line).toBe(2)
143
+ expect(lastCursorEvent!.visualColumn).toBe(0)
144
+ })
145
+
146
+ it("should fire onCursorChange after undo", async () => {
147
+ let cursorChangeCount = 0
148
+
149
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
150
+ initialValue: "",
151
+ width: 40,
152
+ height: 10,
153
+ onCursorChange: () => {
154
+ cursorChangeCount++
155
+ },
156
+ })
157
+
158
+ editor.focus()
159
+
160
+ currentMockInput.pressKey("H")
161
+ currentMockInput.pressKey("i")
162
+ await new Promise((resolve) => setTimeout(resolve, 10))
163
+
164
+ const beforeUndo = cursorChangeCount
165
+
166
+ editor.undo()
167
+ await new Promise((resolve) => setTimeout(resolve, 10))
168
+
169
+ expect(cursorChangeCount).toBeGreaterThan(beforeUndo)
170
+ })
171
+
172
+ it("should update event handler when set dynamically", async () => {
173
+ let firstHandlerCalled = false
174
+ let secondHandlerCalled = false
175
+
176
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
177
+ initialValue: "Test",
178
+ width: 40,
179
+ height: 10,
180
+ onCursorChange: () => {
181
+ firstHandlerCalled = true
182
+ },
183
+ })
184
+
185
+ editor.focus()
186
+
187
+ editor.moveCursorRight()
188
+ await new Promise((resolve) => setTimeout(resolve, 10))
189
+ expect(firstHandlerCalled).toBe(true)
190
+
191
+ editor.onCursorChange = () => {
192
+ secondHandlerCalled = true
193
+ }
194
+
195
+ editor.moveCursorRight()
196
+ await new Promise((resolve) => setTimeout(resolve, 10))
197
+ expect(secondHandlerCalled).toBe(true)
198
+ })
199
+
200
+ it("should not fire when handler is undefined", async () => {
201
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
202
+ initialValue: "Test",
203
+ width: 40,
204
+ height: 10,
205
+ onCursorChange: undefined,
206
+ })
207
+
208
+ editor.focus()
209
+
210
+ editor.moveCursorRight()
211
+ expect(editor.logicalCursor.col).toBe(1)
212
+ })
213
+ })
214
+
215
+ describe("onContentChange", () => {
216
+ it("should fire onContentChange when typing", async () => {
217
+ let contentChangeCount = 0
218
+
219
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
220
+ initialValue: "",
221
+ width: 40,
222
+ height: 10,
223
+ onContentChange: () => {
224
+ contentChangeCount++
225
+ },
226
+ })
227
+
228
+ editor.focus()
229
+ const initialCount = contentChangeCount
230
+
231
+ currentMockInput.pressKey("H")
232
+ await new Promise((resolve) => setTimeout(resolve, 10))
233
+
234
+ expect(contentChangeCount).toBeGreaterThan(initialCount)
235
+ expect(editor.plainText).toBe("H")
236
+ })
237
+
238
+ it("should fire onContentChange when deleting", async () => {
239
+ let contentChangeCount = 0
240
+
241
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
242
+ initialValue: "Hello",
243
+ width: 40,
244
+ height: 10,
245
+ onContentChange: () => {
246
+ contentChangeCount++
247
+ },
248
+ })
249
+
250
+ editor.focus()
251
+ editor.gotoLine(9999)
252
+ const initialCount = contentChangeCount
253
+
254
+ currentMockInput.pressBackspace()
255
+ await new Promise((resolve) => setTimeout(resolve, 10))
256
+
257
+ expect(contentChangeCount).toBeGreaterThan(initialCount)
258
+ expect(editor.plainText).toBe("Hell")
259
+ })
260
+
261
+ it("should fire onContentChange when inserting newline", async () => {
262
+ let contentChangeCount = 0
263
+
264
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
265
+ initialValue: "Test",
266
+ width: 40,
267
+ height: 10,
268
+ onContentChange: () => {
269
+ contentChangeCount++
270
+ },
271
+ })
272
+
273
+ editor.focus()
274
+ editor.gotoLine(9999)
275
+ const initialCount = contentChangeCount
276
+
277
+ currentMockInput.pressEnter()
278
+ await new Promise((resolve) => setTimeout(resolve, 10))
279
+
280
+ expect(contentChangeCount).toBeGreaterThan(initialCount)
281
+ expect(editor.plainText).toBe("Test\n")
282
+ })
283
+
284
+ it("should fire onContentChange when pasting", async () => {
285
+ let contentChangeCount = 0
286
+
287
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
288
+ initialValue: "Hello",
289
+ width: 40,
290
+ height: 10,
291
+ onContentChange: () => {
292
+ contentChangeCount++
293
+ },
294
+ })
295
+
296
+ editor.focus()
297
+ editor.gotoLine(9999)
298
+
299
+ const initialCount = contentChangeCount
300
+
301
+ await currentMockInput.pasteBracketedText(" World")
302
+
303
+ expect(contentChangeCount).toBeGreaterThan(initialCount)
304
+ expect(editor.plainText).toBe("Hello World")
305
+ })
306
+
307
+ it("should fire onContentChange after undo", async () => {
308
+ let contentChangeCount = 0
309
+
310
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
311
+ initialValue: "",
312
+ width: 40,
313
+ height: 10,
314
+ onContentChange: () => {
315
+ contentChangeCount++
316
+ },
317
+ })
318
+
319
+ editor.focus()
320
+
321
+ currentMockInput.pressKey("T")
322
+ currentMockInput.pressKey("e")
323
+ await new Promise((resolve) => setTimeout(resolve, 20))
324
+
325
+ const beforeUndo = contentChangeCount
326
+
327
+ editor.undo()
328
+ await new Promise((resolve) => setTimeout(resolve, 20))
329
+
330
+ expect(contentChangeCount).toBeGreaterThanOrEqual(beforeUndo)
331
+ expect(editor.plainText).toBe("T")
332
+ })
333
+
334
+ it("should fire onContentChange after redo", async () => {
335
+ let contentChangeCount = 0
336
+
337
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
338
+ initialValue: "",
339
+ width: 40,
340
+ height: 10,
341
+ onContentChange: () => {
342
+ contentChangeCount++
343
+ },
344
+ })
345
+
346
+ editor.focus()
347
+
348
+ currentMockInput.pressKey("X")
349
+ await new Promise((resolve) => setTimeout(resolve, 20))
350
+ editor.undo()
351
+ await new Promise((resolve) => setTimeout(resolve, 20))
352
+
353
+ const beforeRedo = contentChangeCount
354
+
355
+ editor.redo()
356
+ await new Promise((resolve) => setTimeout(resolve, 20))
357
+
358
+ expect(contentChangeCount).toBeGreaterThanOrEqual(beforeRedo)
359
+ expect(editor.plainText).toBe("X")
360
+ })
361
+
362
+ it("should fire onContentChange when setting value programmatically", async () => {
363
+ let contentChangeCount = 0
364
+
365
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
366
+ initialValue: "Initial",
367
+ width: 40,
368
+ height: 10,
369
+ onContentChange: () => {
370
+ contentChangeCount++
371
+ },
372
+ })
373
+
374
+ const initialCount = contentChangeCount
375
+
376
+ editor.setText("Updated")
377
+ await new Promise((resolve) => setTimeout(resolve, 10))
378
+
379
+ expect(contentChangeCount).toBeGreaterThan(initialCount)
380
+ expect(editor.plainText).toBe("Updated")
381
+ })
382
+
383
+ it("should fire onContentChange when deleting selection", async () => {
384
+ let contentChangeCount = 0
385
+
386
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
387
+ initialValue: "Hello World",
388
+ width: 40,
389
+ height: 10,
390
+ selectable: true,
391
+ onContentChange: () => {
392
+ contentChangeCount++
393
+ },
394
+ })
395
+
396
+ editor.focus()
397
+
398
+ for (let i = 0; i < 5; i++) {
399
+ currentMockInput.pressArrow("right", { shift: true })
400
+ }
401
+ await new Promise((resolve) => setTimeout(resolve, 10))
402
+
403
+ const beforeDelete = contentChangeCount
404
+
405
+ currentMockInput.pressBackspace()
406
+ await new Promise((resolve) => setTimeout(resolve, 10))
407
+
408
+ expect(contentChangeCount).toBeGreaterThan(beforeDelete)
409
+ expect(editor.plainText).toBe(" World")
410
+ })
411
+
412
+ it("should update event handler when set dynamically", async () => {
413
+ let firstHandlerCalled = false
414
+ let secondHandlerCalled = false
415
+
416
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
417
+ initialValue: "",
418
+ width: 40,
419
+ height: 10,
420
+ onContentChange: () => {
421
+ firstHandlerCalled = true
422
+ },
423
+ })
424
+
425
+ editor.focus()
426
+
427
+ currentMockInput.pressKey("A")
428
+ await new Promise((resolve) => setTimeout(resolve, 10))
429
+ expect(firstHandlerCalled).toBe(true)
430
+
431
+ editor.onContentChange = () => {
432
+ secondHandlerCalled = true
433
+ }
434
+
435
+ currentMockInput.pressKey("B")
436
+ await new Promise((resolve) => setTimeout(resolve, 10))
437
+ expect(secondHandlerCalled).toBe(true)
438
+ })
439
+
440
+ it("should not fire when handler is undefined", async () => {
441
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
442
+ initialValue: "",
443
+ width: 40,
444
+ height: 10,
445
+ onContentChange: undefined,
446
+ })
447
+
448
+ editor.focus()
449
+
450
+ currentMockInput.pressKey("X")
451
+ expect(editor.plainText).toBe("X")
452
+ })
453
+
454
+ it("should fire exactly once when setting via setter and pressing a key", async () => {
455
+ let contentChangeCount = 0
456
+
457
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
458
+ initialValue: "",
459
+ width: 40,
460
+ height: 10,
461
+ })
462
+
463
+ editor.focus()
464
+
465
+ editor.onContentChange = () => {
466
+ contentChangeCount++
467
+ }
468
+
469
+ currentMockInput.pressKey("X")
470
+ await new Promise((resolve) => setTimeout(resolve, 10))
471
+
472
+ expect(contentChangeCount).toBe(1)
473
+ expect(editor.plainText).toBe("X")
474
+ })
475
+ })
476
+
477
+ describe("onSubmit", () => {
478
+ it("should fire onSubmit with default keybinding (Meta+Enter)", async () => {
479
+ let submitCount = 0
480
+
481
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
482
+ initialValue: "Test content",
483
+ width: 40,
484
+ height: 10,
485
+ onSubmit: () => {
486
+ submitCount++
487
+ },
488
+ })
489
+
490
+ editor.focus()
491
+ const initialCount = submitCount
492
+
493
+ currentMockInput.pressEnter({ meta: true })
494
+ await new Promise((resolve) => setTimeout(resolve, 10))
495
+
496
+ expect(submitCount).toBe(initialCount + 1)
497
+ expect(editor.plainText).toBe("Test content")
498
+ })
499
+
500
+ it("should fire onSubmit with alternative keybinding (Meta+Return)", async () => {
501
+ let submitCount = 0
502
+
503
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
504
+ initialValue: "Test",
505
+ width: 40,
506
+ height: 10,
507
+ onSubmit: () => {
508
+ submitCount++
509
+ },
510
+ })
511
+
512
+ editor.focus()
513
+
514
+ currentMockInput.pressEnter({ meta: true })
515
+ await new Promise((resolve) => setTimeout(resolve, 10))
516
+
517
+ expect(submitCount).toBe(1)
518
+ })
519
+
520
+ it("should not insert newline when submitting", async () => {
521
+ let submitCount = 0
522
+
523
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
524
+ initialValue: "Test",
525
+ width: 40,
526
+ height: 10,
527
+ onSubmit: () => {
528
+ submitCount++
529
+ },
530
+ })
531
+
532
+ editor.focus()
533
+ editor.gotoLine(9999)
534
+
535
+ currentMockInput.pressEnter({ meta: true })
536
+ await new Promise((resolve) => setTimeout(resolve, 10))
537
+
538
+ expect(submitCount).toBe(1)
539
+ expect(editor.plainText).toBe("Test")
540
+ })
541
+
542
+ it("should update handler via setter", async () => {
543
+ let firstHandlerCalled = false
544
+ let secondHandlerCalled = false
545
+
546
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
547
+ initialValue: "",
548
+ width: 40,
549
+ height: 10,
550
+ onSubmit: () => {
551
+ firstHandlerCalled = true
552
+ },
553
+ })
554
+
555
+ editor.focus()
556
+
557
+ currentMockInput.pressEnter({ meta: true })
558
+ await new Promise((resolve) => setTimeout(resolve, 10))
559
+ expect(firstHandlerCalled).toBe(true)
560
+
561
+ editor.onSubmit = () => {
562
+ secondHandlerCalled = true
563
+ }
564
+
565
+ currentMockInput.pressEnter({ meta: true })
566
+ await new Promise((resolve) => setTimeout(resolve, 10))
567
+ expect(secondHandlerCalled).toBe(true)
568
+ })
569
+
570
+ it("should not fire when handler is undefined", async () => {
571
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
572
+ initialValue: "Test",
573
+ width: 40,
574
+ height: 10,
575
+ onSubmit: undefined,
576
+ })
577
+
578
+ editor.focus()
579
+
580
+ currentMockInput.pressEnter({ meta: true })
581
+ expect(editor.plainText).toBe("Test")
582
+ })
583
+
584
+ it("should support custom keybinding for submit", async () => {
585
+ let submitCount = 0
586
+
587
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
588
+ initialValue: "Test",
589
+ width: 40,
590
+ height: 10,
591
+ keyBindings: [{ name: "s", ctrl: true, action: "submit" }],
592
+ onSubmit: () => {
593
+ submitCount++
594
+ },
595
+ })
596
+
597
+ editor.focus()
598
+
599
+ currentMockInput.pressKey("s", { ctrl: true })
600
+ await new Promise((resolve) => setTimeout(resolve, 10))
601
+
602
+ expect(submitCount).toBe(1)
603
+ })
604
+
605
+ it("should get current handler via getter", async () => {
606
+ const handler = () => {}
607
+
608
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
609
+ initialValue: "",
610
+ width: 40,
611
+ height: 10,
612
+ onSubmit: handler,
613
+ })
614
+
615
+ expect(editor.onSubmit).toBe(handler)
616
+ })
617
+
618
+ it("should allow removing handler by setting to undefined", async () => {
619
+ let submitCount = 0
620
+
621
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
622
+ initialValue: "",
623
+ width: 40,
624
+ height: 10,
625
+ onSubmit: () => {
626
+ submitCount++
627
+ },
628
+ })
629
+
630
+ editor.focus()
631
+
632
+ currentMockInput.pressEnter({ meta: true })
633
+ await new Promise((resolve) => setTimeout(resolve, 10))
634
+ expect(submitCount).toBe(1)
635
+
636
+ editor.onSubmit = undefined
637
+
638
+ currentMockInput.pressEnter({ meta: true })
639
+ await new Promise((resolve) => setTimeout(resolve, 10))
640
+ expect(submitCount).toBe(1)
641
+ })
642
+ })
643
+
644
+ describe("Combined cursor and content events", () => {
645
+ it("should fire both onCursorChange and onContentChange when typing", async () => {
646
+ let cursorChangeCount = 0
647
+ let contentChangeCount = 0
648
+
649
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
650
+ initialValue: "",
651
+ width: 40,
652
+ height: 10,
653
+ onCursorChange: () => {
654
+ cursorChangeCount++
655
+ },
656
+ onContentChange: () => {
657
+ contentChangeCount++
658
+ },
659
+ })
660
+
661
+ editor.focus()
662
+ const initialCursorCount = cursorChangeCount
663
+ const initialContentCount = contentChangeCount
664
+
665
+ currentMockInput.pressKey("H")
666
+ await new Promise((resolve) => setTimeout(resolve, 10))
667
+
668
+ expect(cursorChangeCount).toBeGreaterThan(initialCursorCount)
669
+ expect(contentChangeCount).toBeGreaterThan(initialContentCount)
670
+ })
671
+
672
+ it("should fire onCursorChange but not onContentChange when only moving cursor", async () => {
673
+ let cursorChangeCount = 0
674
+ let contentChangeCount = 0
675
+
676
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
677
+ initialValue: "Test",
678
+ width: 40,
679
+ height: 10,
680
+ onCursorChange: () => {
681
+ cursorChangeCount++
682
+ },
683
+ onContentChange: () => {
684
+ contentChangeCount++
685
+ },
686
+ })
687
+
688
+ editor.focus()
689
+ const initialCursorCount = cursorChangeCount
690
+ const initialContentCount = contentChangeCount
691
+
692
+ editor.moveCursorRight()
693
+ await new Promise((resolve) => setTimeout(resolve, 10))
694
+
695
+ expect(cursorChangeCount).toBeGreaterThan(initialCursorCount)
696
+ expect(contentChangeCount).toBe(initialContentCount) // Should not change
697
+ })
698
+
699
+ it("should track events through complex editing sequence", async () => {
700
+ const events: Array<{ type: "cursor" | "content"; time: number }> = []
701
+
702
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
703
+ initialValue: "",
704
+ width: 40,
705
+ height: 10,
706
+ onCursorChange: () => {
707
+ events.push({ type: "cursor", time: Date.now() })
708
+ },
709
+ onContentChange: () => {
710
+ events.push({ type: "content", time: Date.now() })
711
+ },
712
+ })
713
+
714
+ editor.focus()
715
+ events.length = 0 // Clear initial events
716
+
717
+ currentMockInput.pressKey("H")
718
+ currentMockInput.pressKey("e")
719
+ currentMockInput.pressKey("l")
720
+ currentMockInput.pressKey("l")
721
+ currentMockInput.pressKey("o")
722
+
723
+ editor.moveCursorLeft()
724
+ editor.moveCursorLeft()
725
+
726
+ currentMockInput.pressBackspace()
727
+
728
+ await new Promise((resolve) => setTimeout(resolve, 50))
729
+
730
+ const cursorEvents = events.filter((e) => e.type === "cursor")
731
+ const contentEvents = events.filter((e) => e.type === "content")
732
+
733
+ expect(cursorEvents.length).toBeGreaterThan(0)
734
+ expect(contentEvents.length).toBeGreaterThan(0)
735
+ })
736
+ })
737
+ })
738
+ })