@fairyhunter13/opentui-core 0.1.113 → 0.1.114

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (591) hide show
  1. package/dev/keypress-debug-renderer.ts +148 -0
  2. package/dev/keypress-debug.ts +43 -0
  3. package/dev/print-env-vars.ts +32 -0
  4. package/dev/test-tmux-graphics-334.sh +68 -0
  5. package/dev/thai-debug-test.ts +68 -0
  6. package/docs/development.md +144 -0
  7. package/package.json +62 -53
  8. package/scripts/build.ts +400 -0
  9. package/scripts/publish.ts +60 -0
  10. package/src/3d/SpriteResourceManager.ts +286 -0
  11. package/src/3d/SpriteUtils.ts +70 -0
  12. package/src/3d/TextureUtils.ts +196 -0
  13. package/src/3d/ThreeRenderable.ts +197 -0
  14. package/src/3d/WGPURenderer.ts +294 -0
  15. package/src/3d/animation/ExplodingSpriteEffect.ts +513 -0
  16. package/src/3d/animation/PhysicsExplodingSpriteEffect.ts +429 -0
  17. package/src/3d/animation/SpriteAnimator.ts +633 -0
  18. package/src/3d/animation/SpriteParticleGenerator.ts +435 -0
  19. package/src/3d/canvas.ts +464 -0
  20. package/src/3d/index.ts +12 -0
  21. package/src/3d/physics/PlanckPhysicsAdapter.ts +72 -0
  22. package/src/3d/physics/RapierPhysicsAdapter.ts +66 -0
  23. package/src/3d/physics/physics-interface.ts +31 -0
  24. package/src/3d/shaders/supersampling.wgsl +201 -0
  25. package/src/3d.ts +3 -0
  26. package/src/NativeSpanFeed.ts +300 -0
  27. package/src/Renderable.ts +1704 -0
  28. package/src/__snapshots__/buffer.test.ts.snap +28 -0
  29. package/src/animation/Timeline.test.ts +2709 -0
  30. package/src/animation/Timeline.ts +598 -0
  31. package/src/ansi.ts +18 -0
  32. package/src/benchmark/attenuation-benchmark.ts +81 -0
  33. package/src/benchmark/colormatrix-benchmark.ts +128 -0
  34. package/src/benchmark/gain-benchmark.ts +80 -0
  35. package/src/benchmark/latest-all-bench-run.json +707 -0
  36. package/src/benchmark/latest-async-bench-run.json +336 -0
  37. package/src/benchmark/latest-default-bench-run.json +657 -0
  38. package/src/benchmark/latest-large-bench-run.json +707 -0
  39. package/src/benchmark/latest-quick-bench-run.json +207 -0
  40. package/src/benchmark/markdown-benchmark.ts +1796 -0
  41. package/src/benchmark/native-span-feed-async-benchmark.ts +355 -0
  42. package/src/benchmark/native-span-feed-benchmark.md +56 -0
  43. package/src/benchmark/native-span-feed-benchmark.ts +596 -0
  44. package/src/benchmark/native-span-feed-compare.ts +280 -0
  45. package/src/benchmark/renderer-benchmark.ts +754 -0
  46. package/src/benchmark/text-table-benchmark.ts +948 -0
  47. package/src/buffer.test.ts +291 -0
  48. package/src/buffer.ts +554 -0
  49. package/src/console.test.ts +612 -0
  50. package/src/console.ts +1254 -0
  51. package/src/edit-buffer.test.ts +1769 -0
  52. package/src/edit-buffer.ts +411 -0
  53. package/src/editor-view.test.ts +1032 -0
  54. package/src/editor-view.ts +284 -0
  55. package/src/examples/ascii-font-selection-demo.ts +245 -0
  56. package/src/examples/assets/Water_2_M_Normal.jpg +0 -0
  57. package/src/examples/assets/concrete.png +0 -0
  58. package/src/examples/assets/crate.png +0 -0
  59. package/src/examples/assets/crate_emissive.png +0 -0
  60. package/src/examples/assets/forrest_background.png +0 -0
  61. package/src/examples/assets/hast-example.json +1018 -0
  62. package/src/examples/assets/heart.png +0 -0
  63. package/src/examples/assets/main_char_heavy_attack.png +0 -0
  64. package/src/examples/assets/main_char_idle.png +0 -0
  65. package/src/examples/assets/main_char_jump_end.png +0 -0
  66. package/src/examples/assets/main_char_jump_landing.png +0 -0
  67. package/src/examples/assets/main_char_jump_start.png +0 -0
  68. package/src/examples/assets/main_char_run_loop.png +0 -0
  69. package/src/examples/assets/roughness_map.jpg +0 -0
  70. package/src/examples/build.ts +115 -0
  71. package/src/examples/code-demo.ts +924 -0
  72. package/src/examples/console-demo.ts +358 -0
  73. package/src/examples/core-plugin-slots-demo.ts +759 -0
  74. package/src/examples/diff-demo.ts +701 -0
  75. package/src/examples/draggable-three-demo.ts +259 -0
  76. package/src/examples/editor-demo.ts +322 -0
  77. package/src/examples/extmarks-demo.ts +196 -0
  78. package/src/examples/focus-restore-demo.ts +310 -0
  79. package/src/examples/fonts.ts +245 -0
  80. package/src/examples/fractal-shader-demo.ts +268 -0
  81. package/src/examples/framebuffer-demo.ts +674 -0
  82. package/src/examples/full-unicode-demo.ts +241 -0
  83. package/src/examples/golden-star-demo.ts +933 -0
  84. package/src/examples/grayscale-buffer-demo.ts +249 -0
  85. package/src/examples/hast-syntax-highlighting-demo.ts +129 -0
  86. package/src/examples/index.ts +926 -0
  87. package/src/examples/input-demo.ts +377 -0
  88. package/src/examples/input-select-layout-demo.ts +425 -0
  89. package/src/examples/install.sh +143 -0
  90. package/src/examples/keypress-debug-demo.ts +452 -0
  91. package/src/examples/lib/HexList.ts +122 -0
  92. package/src/examples/lib/PaletteGrid.ts +125 -0
  93. package/src/examples/lib/standalone-keys.ts +25 -0
  94. package/src/examples/lib/tab-controller.ts +243 -0
  95. package/src/examples/lights-phong-demo.ts +290 -0
  96. package/src/examples/link-demo.ts +220 -0
  97. package/src/examples/live-state-demo.ts +480 -0
  98. package/src/examples/markdown-demo.ts +725 -0
  99. package/src/examples/mouse-interaction-demo.ts +428 -0
  100. package/src/examples/nested-zindex-demo.ts +357 -0
  101. package/src/examples/opacity-example.ts +235 -0
  102. package/src/examples/opentui-demo.ts +1057 -0
  103. package/src/examples/physx-planck-2d-demo.ts +623 -0
  104. package/src/examples/physx-rapier-2d-demo.ts +655 -0
  105. package/src/examples/relative-positioning-demo.ts +323 -0
  106. package/src/examples/scroll-example.ts +214 -0
  107. package/src/examples/scrollbox-mouse-test.ts +112 -0
  108. package/src/examples/scrollbox-overlay-hit-test.ts +206 -0
  109. package/src/examples/select-demo.ts +237 -0
  110. package/src/examples/shader-cube-demo.ts +1015 -0
  111. package/src/examples/simple-layout-example.ts +591 -0
  112. package/src/examples/slider-demo.ts +617 -0
  113. package/src/examples/split-mode-demo.ts +453 -0
  114. package/src/examples/sprite-animation-demo.ts +443 -0
  115. package/src/examples/sprite-particle-generator-demo.ts +486 -0
  116. package/src/examples/static-sprite-demo.ts +193 -0
  117. package/src/examples/sticky-scroll-example.ts +308 -0
  118. package/src/examples/styled-text-demo.ts +282 -0
  119. package/src/examples/tab-select-demo.ts +219 -0
  120. package/src/examples/terminal-title.ts +29 -0
  121. package/src/examples/terminal.ts +305 -0
  122. package/src/examples/text-node-demo.ts +416 -0
  123. package/src/examples/text-selection-demo.ts +377 -0
  124. package/src/examples/text-table-demo.ts +503 -0
  125. package/src/examples/text-truncation-demo.ts +481 -0
  126. package/src/examples/text-wrap.ts +757 -0
  127. package/src/examples/texture-loading-demo.ts +259 -0
  128. package/src/examples/timeline-example.ts +670 -0
  129. package/src/examples/transparency-demo.ts +400 -0
  130. package/src/examples/vnode-composition-demo.ts +404 -0
  131. package/src/examples/wide-grapheme-overlay-demo.ts +280 -0
  132. package/src/index.ts +24 -0
  133. package/src/lib/KeyHandler.integration.test.ts +292 -0
  134. package/src/lib/KeyHandler.stopPropagation.test.ts +289 -0
  135. package/src/lib/KeyHandler.test.ts +662 -0
  136. package/src/lib/KeyHandler.ts +222 -0
  137. package/src/lib/RGBA.test.ts +984 -0
  138. package/src/lib/RGBA.ts +204 -0
  139. package/src/lib/ascii.font.ts +330 -0
  140. package/src/lib/border.test.ts +83 -0
  141. package/src/lib/border.ts +170 -0
  142. package/src/lib/bunfs.test.ts +27 -0
  143. package/src/lib/bunfs.ts +18 -0
  144. package/src/lib/clipboard.test.ts +41 -0
  145. package/src/lib/clipboard.ts +47 -0
  146. package/src/lib/clock.ts +35 -0
  147. package/src/lib/data-paths.test.ts +133 -0
  148. package/src/lib/data-paths.ts +109 -0
  149. package/src/lib/debounce.ts +106 -0
  150. package/src/lib/detect-links.test.ts +98 -0
  151. package/src/lib/detect-links.ts +56 -0
  152. package/src/lib/env.test.ts +228 -0
  153. package/src/lib/env.ts +209 -0
  154. package/src/lib/extmarks-history.ts +51 -0
  155. package/src/lib/extmarks-multiwidth.test.ts +322 -0
  156. package/src/lib/extmarks.test.ts +3457 -0
  157. package/src/lib/extmarks.ts +843 -0
  158. package/src/lib/fonts/block.json +405 -0
  159. package/src/lib/fonts/grid.json +265 -0
  160. package/src/lib/fonts/huge.json +741 -0
  161. package/src/lib/fonts/pallet.json +314 -0
  162. package/src/lib/fonts/shade.json +591 -0
  163. package/src/lib/fonts/slick.json +321 -0
  164. package/src/lib/fonts/tiny.json +69 -0
  165. package/src/lib/hast-styled-text.ts +59 -0
  166. package/src/lib/index.ts +21 -0
  167. package/src/lib/keymapping.test.ts +317 -0
  168. package/src/lib/keymapping.ts +115 -0
  169. package/src/lib/objects-in-viewport.test.ts +787 -0
  170. package/src/lib/objects-in-viewport.ts +153 -0
  171. package/src/lib/output.capture.ts +58 -0
  172. package/src/lib/parse.keypress-kitty.protocol.test.ts +340 -0
  173. package/src/lib/parse.keypress-kitty.test.ts +663 -0
  174. package/src/lib/parse.keypress-kitty.ts +439 -0
  175. package/src/lib/parse.keypress.test.ts +1849 -0
  176. package/src/lib/parse.keypress.ts +397 -0
  177. package/src/lib/parse.mouse.test.ts +552 -0
  178. package/src/lib/parse.mouse.ts +232 -0
  179. package/src/lib/paste.ts +16 -0
  180. package/src/lib/queue.ts +65 -0
  181. package/src/lib/renderable.validations.test.ts +87 -0
  182. package/src/lib/renderable.validations.ts +83 -0
  183. package/src/lib/scroll-acceleration.ts +98 -0
  184. package/src/lib/selection.ts +240 -0
  185. package/src/lib/singleton.ts +28 -0
  186. package/src/lib/stdin-parser.test.ts +2290 -0
  187. package/src/lib/stdin-parser.ts +1810 -0
  188. package/src/lib/styled-text.ts +178 -0
  189. package/src/lib/terminal-capability-detection.test.ts +202 -0
  190. package/src/lib/terminal-capability-detection.ts +79 -0
  191. package/src/lib/terminal-palette.test.ts +878 -0
  192. package/src/lib/terminal-palette.ts +383 -0
  193. package/src/lib/tree-sitter/assets/README.md +118 -0
  194. package/src/lib/tree-sitter/assets/update.ts +334 -0
  195. package/src/lib/tree-sitter/assets.d.ts +9 -0
  196. package/src/lib/tree-sitter/cache.test.ts +273 -0
  197. package/src/lib/tree-sitter/client.test.ts +1165 -0
  198. package/src/lib/tree-sitter/client.ts +607 -0
  199. package/src/lib/tree-sitter/default-parsers.ts +86 -0
  200. package/src/lib/tree-sitter/download-utils.ts +148 -0
  201. package/src/lib/tree-sitter/index.ts +28 -0
  202. package/src/lib/tree-sitter/parser.worker.ts +1042 -0
  203. package/src/lib/tree-sitter/parsers-config.ts +81 -0
  204. package/src/lib/tree-sitter/resolve-ft.test.ts +55 -0
  205. package/src/lib/tree-sitter/resolve-ft.ts +189 -0
  206. package/src/lib/tree-sitter/types.ts +82 -0
  207. package/src/lib/tree-sitter-styled-text.test.ts +1253 -0
  208. package/src/lib/tree-sitter-styled-text.ts +306 -0
  209. package/src/lib/validate-dir-name.ts +55 -0
  210. package/src/lib/yoga.options.test.ts +628 -0
  211. package/src/lib/yoga.options.ts +346 -0
  212. package/src/plugins/core-slot.ts +579 -0
  213. package/src/plugins/registry.ts +402 -0
  214. package/src/plugins/types.ts +46 -0
  215. package/src/post/effects.ts +930 -0
  216. package/src/post/filters.ts +489 -0
  217. package/src/post/matrices.ts +288 -0
  218. package/src/renderables/ASCIIFont.ts +219 -0
  219. package/src/renderables/Box.test.ts +205 -0
  220. package/src/renderables/Box.ts +326 -0
  221. package/src/renderables/Code.test.ts +2062 -0
  222. package/src/renderables/Code.ts +357 -0
  223. package/src/renderables/Diff.regression.test.ts +226 -0
  224. package/src/renderables/Diff.test.ts +3101 -0
  225. package/src/renderables/Diff.ts +1211 -0
  226. package/src/renderables/EditBufferRenderable.test.ts +288 -0
  227. package/src/renderables/EditBufferRenderable.ts +1166 -0
  228. package/src/renderables/FrameBuffer.ts +47 -0
  229. package/src/renderables/Input.test.ts +1228 -0
  230. package/src/renderables/Input.ts +247 -0
  231. package/src/renderables/LineNumberRenderable.ts +724 -0
  232. package/src/renderables/Markdown.ts +1393 -0
  233. package/src/renderables/ScrollBar.ts +422 -0
  234. package/src/renderables/ScrollBox.ts +883 -0
  235. package/src/renderables/Select.test.ts +1033 -0
  236. package/src/renderables/Select.ts +524 -0
  237. package/src/renderables/Slider.test.ts +456 -0
  238. package/src/renderables/Slider.ts +342 -0
  239. package/src/renderables/TabSelect.test.ts +197 -0
  240. package/src/renderables/TabSelect.ts +455 -0
  241. package/src/renderables/Text.selection-buffer.test.ts +123 -0
  242. package/src/renderables/Text.test.ts +2660 -0
  243. package/src/renderables/Text.ts +147 -0
  244. package/src/renderables/TextBufferRenderable.ts +518 -0
  245. package/src/renderables/TextNode.test.ts +1058 -0
  246. package/src/renderables/TextNode.ts +325 -0
  247. package/src/renderables/TextTable.test.ts +1421 -0
  248. package/src/renderables/TextTable.ts +1344 -0
  249. package/src/renderables/Textarea.ts +430 -0
  250. package/src/renderables/TimeToFirstDraw.ts +89 -0
  251. package/src/renderables/__snapshots__/Code.test.ts.snap +13 -0
  252. package/src/renderables/__snapshots__/Diff.test.ts.snap +785 -0
  253. package/src/renderables/__snapshots__/Text.test.ts.snap +421 -0
  254. package/src/renderables/__snapshots__/TextTable.test.ts.snap +215 -0
  255. package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +144 -0
  256. package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +816 -0
  257. package/src/renderables/__tests__/LineNumberRenderable.test.ts +1865 -0
  258. package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +85 -0
  259. package/src/renderables/__tests__/Markdown.code-colors.test.ts +242 -0
  260. package/src/renderables/__tests__/Markdown.test.ts +2518 -0
  261. package/src/renderables/__tests__/MultiRenderable.selection.test.ts +87 -0
  262. package/src/renderables/__tests__/Textarea.buffer.test.ts +682 -0
  263. package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +675 -0
  264. package/src/renderables/__tests__/Textarea.editing.test.ts +2041 -0
  265. package/src/renderables/__tests__/Textarea.error-handling.test.ts +35 -0
  266. package/src/renderables/__tests__/Textarea.events.test.ts +738 -0
  267. package/src/renderables/__tests__/Textarea.highlights.test.ts +590 -0
  268. package/src/renderables/__tests__/Textarea.keybinding.test.ts +3149 -0
  269. package/src/renderables/__tests__/Textarea.paste.test.ts +357 -0
  270. package/src/renderables/__tests__/Textarea.rendering.test.ts +1866 -0
  271. package/src/renderables/__tests__/Textarea.scroll.test.ts +733 -0
  272. package/src/renderables/__tests__/Textarea.selection.test.ts +1590 -0
  273. package/src/renderables/__tests__/Textarea.stress.test.ts +670 -0
  274. package/src/renderables/__tests__/Textarea.undo-redo.test.ts +383 -0
  275. package/src/renderables/__tests__/Textarea.visual-lines.test.ts +310 -0
  276. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +221 -0
  277. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +89 -0
  278. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +457 -0
  279. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +158 -0
  280. package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +387 -0
  281. package/src/renderables/__tests__/markdown-parser.test.ts +217 -0
  282. package/src/renderables/__tests__/renderable-test-utils.ts +60 -0
  283. package/src/renderables/composition/README.md +8 -0
  284. package/src/renderables/composition/VRenderable.ts +32 -0
  285. package/src/renderables/composition/constructs.ts +127 -0
  286. package/src/renderables/composition/vnode.ts +289 -0
  287. package/src/renderables/index.ts +23 -0
  288. package/src/renderables/markdown-parser.ts +66 -0
  289. package/src/renderer.ts +2681 -0
  290. package/src/runtime-plugin-support.ts +39 -0
  291. package/src/runtime-plugin.ts +615 -0
  292. package/src/syntax-style.test.ts +841 -0
  293. package/src/syntax-style.ts +257 -0
  294. package/src/testing/README.md +210 -0
  295. package/src/testing/capture-spans.test.ts +194 -0
  296. package/src/testing/integration.test.ts +276 -0
  297. package/src/testing/manual-clock.ts +117 -0
  298. package/src/testing/mock-keys.test.ts +1378 -0
  299. package/src/testing/mock-keys.ts +457 -0
  300. package/src/testing/mock-mouse.test.ts +218 -0
  301. package/src/testing/mock-mouse.ts +247 -0
  302. package/src/testing/mock-tree-sitter-client.ts +73 -0
  303. package/src/testing/spy.ts +13 -0
  304. package/src/testing/test-recorder.test.ts +415 -0
  305. package/src/testing/test-recorder.ts +145 -0
  306. package/src/testing/test-renderer.ts +132 -0
  307. package/src/testing.ts +7 -0
  308. package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +481 -0
  309. package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +19 -0
  310. package/src/tests/__snapshots__/scrollbox.test.ts.snap +29 -0
  311. package/src/tests/absolute-positioning.snapshot.test.ts +638 -0
  312. package/src/tests/allocator-stats.test.ts +38 -0
  313. package/src/tests/destroy-during-render.test.ts +200 -0
  314. package/src/tests/destroy-on-exit.fixture.ts +36 -0
  315. package/src/tests/destroy-on-exit.test.ts +41 -0
  316. package/src/tests/hover-cursor.test.ts +98 -0
  317. package/src/tests/native-span-feed-async.test.ts +173 -0
  318. package/src/tests/native-span-feed-close.test.ts +120 -0
  319. package/src/tests/native-span-feed-coverage.test.ts +227 -0
  320. package/src/tests/native-span-feed-edge-cases.test.ts +352 -0
  321. package/src/tests/native-span-feed-use-after-free.test.ts +45 -0
  322. package/src/tests/opacity.test.ts +123 -0
  323. package/src/tests/renderable.snapshot.test.ts +524 -0
  324. package/src/tests/renderable.test.ts +1281 -0
  325. package/src/tests/renderer.clock.test.ts +158 -0
  326. package/src/tests/renderer.console-startup.test.ts +185 -0
  327. package/src/tests/renderer.control.test.ts +425 -0
  328. package/src/tests/renderer.core-slot-binding.test.ts +952 -0
  329. package/src/tests/renderer.cursor.test.ts +26 -0
  330. package/src/tests/renderer.destroy-during-render.test.ts +147 -0
  331. package/src/tests/renderer.focus-restore.test.ts +257 -0
  332. package/src/tests/renderer.focus.test.ts +294 -0
  333. package/src/tests/renderer.idle.test.ts +219 -0
  334. package/src/tests/renderer.input.test.ts +2237 -0
  335. package/src/tests/renderer.kitty-flags.test.ts +195 -0
  336. package/src/tests/renderer.mouse.test.ts +1274 -0
  337. package/src/tests/renderer.palette.test.ts +629 -0
  338. package/src/tests/renderer.selection.test.ts +49 -0
  339. package/src/tests/renderer.slot-registry.test.ts +684 -0
  340. package/src/tests/renderer.useMouse.test.ts +47 -0
  341. package/src/tests/runtime-plugin-node-modules-cycle.fixture.ts +76 -0
  342. package/src/tests/runtime-plugin-node-modules-mjs.fixture.ts +43 -0
  343. package/src/tests/runtime-plugin-node-modules-no-bare-rewrite.fixture.ts +67 -0
  344. package/src/tests/runtime-plugin-node-modules-package-type-cache.fixture.ts +72 -0
  345. package/src/tests/runtime-plugin-node-modules-runtime-specifier.fixture.ts +44 -0
  346. package/src/tests/runtime-plugin-node-modules-scoped-package-bare-rewrite.fixture.ts +85 -0
  347. package/src/tests/runtime-plugin-path-alias.fixture.ts +43 -0
  348. package/src/tests/runtime-plugin-resolve-roots.fixture.ts +65 -0
  349. package/src/tests/runtime-plugin-support.fixture.ts +11 -0
  350. package/src/tests/runtime-plugin-support.test.ts +19 -0
  351. package/src/tests/runtime-plugin-windows-file-url.fixture.ts +30 -0
  352. package/src/tests/runtime-plugin.fixture.ts +40 -0
  353. package/src/tests/runtime-plugin.test.ts +354 -0
  354. package/src/tests/scrollbox-culling-bug.test.ts +114 -0
  355. package/src/tests/scrollbox-hitgrid-resize.test.ts +136 -0
  356. package/src/tests/scrollbox-hitgrid.test.ts +909 -0
  357. package/src/tests/scrollbox.test.ts +1530 -0
  358. package/src/tests/wrap-resize-perf.test.ts +276 -0
  359. package/src/tests/yoga-setters.test.ts +921 -0
  360. package/src/text-buffer-view.test.ts +705 -0
  361. package/src/text-buffer-view.ts +189 -0
  362. package/src/text-buffer.test.ts +347 -0
  363. package/src/text-buffer.ts +250 -0
  364. package/src/types.ts +161 -0
  365. package/src/utils.ts +88 -0
  366. package/src/zig/ansi.zig +268 -0
  367. package/src/zig/bench/README.md +50 -0
  368. package/src/zig/bench/buffer-draw-text-buffer_bench.zig +887 -0
  369. package/src/zig/bench/edit-buffer_bench.zig +476 -0
  370. package/src/zig/bench/native-span-feed_bench.zig +100 -0
  371. package/src/zig/bench/rope-markers_bench.zig +713 -0
  372. package/src/zig/bench/rope_bench.zig +514 -0
  373. package/src/zig/bench/styled-text_bench.zig +470 -0
  374. package/src/zig/bench/text-buffer-coords_bench.zig +362 -0
  375. package/src/zig/bench/text-buffer-view_bench.zig +459 -0
  376. package/src/zig/bench/text-chunk-graphemes_bench.zig +273 -0
  377. package/src/zig/bench/utf8_bench.zig +799 -0
  378. package/src/zig/bench-utils.zig +431 -0
  379. package/src/zig/bench.zig +217 -0
  380. package/src/zig/buffer-methods.zig +211 -0
  381. package/src/zig/buffer.zig +2281 -0
  382. package/src/zig/build.zig +289 -0
  383. package/src/zig/build.zig.zon +16 -0
  384. package/src/zig/edit-buffer.zig +825 -0
  385. package/src/zig/editor-view.zig +802 -0
  386. package/src/zig/event-bus.zig +13 -0
  387. package/src/zig/event-emitter.zig +65 -0
  388. package/src/zig/file-logger.zig +92 -0
  389. package/src/zig/grapheme.zig +599 -0
  390. package/src/zig/lib.zig +1854 -0
  391. package/src/zig/link.zig +333 -0
  392. package/src/zig/logger.zig +43 -0
  393. package/src/zig/mem-registry.zig +125 -0
  394. package/src/zig/native-span-feed-bench-lib.zig +7 -0
  395. package/src/zig/native-span-feed.zig +708 -0
  396. package/src/zig/renderer.zig +1393 -0
  397. package/src/zig/rope.zig +1220 -0
  398. package/src/zig/syntax-style.zig +161 -0
  399. package/src/zig/terminal.zig +987 -0
  400. package/src/zig/test.zig +72 -0
  401. package/src/zig/tests/README.md +18 -0
  402. package/src/zig/tests/buffer-methods_test.zig +1109 -0
  403. package/src/zig/tests/buffer_test.zig +2557 -0
  404. package/src/zig/tests/edit-buffer-history_test.zig +271 -0
  405. package/src/zig/tests/edit-buffer_test.zig +1689 -0
  406. package/src/zig/tests/editor-view_test.zig +3299 -0
  407. package/src/zig/tests/event-emitter_test.zig +249 -0
  408. package/src/zig/tests/grapheme_test.zig +1304 -0
  409. package/src/zig/tests/link_test.zig +190 -0
  410. package/src/zig/tests/mem-registry_test.zig +473 -0
  411. package/src/zig/tests/memory_leak_regression_test.zig +159 -0
  412. package/src/zig/tests/native-span-feed_test.zig +1264 -0
  413. package/src/zig/tests/renderer_test.zig +1017 -0
  414. package/src/zig/tests/rope-nested_test.zig +712 -0
  415. package/src/zig/tests/rope_fuzz_test.zig +238 -0
  416. package/src/zig/tests/rope_test.zig +2362 -0
  417. package/src/zig/tests/segment-merge.test.zig +148 -0
  418. package/src/zig/tests/syntax-style_test.zig +557 -0
  419. package/src/zig/tests/terminal_test.zig +754 -0
  420. package/src/zig/tests/text-buffer-drawing_test.zig +3237 -0
  421. package/src/zig/tests/text-buffer-highlights_test.zig +666 -0
  422. package/src/zig/tests/text-buffer-iterators_test.zig +776 -0
  423. package/src/zig/tests/text-buffer-segment_test.zig +320 -0
  424. package/src/zig/tests/text-buffer-selection_test.zig +1035 -0
  425. package/src/zig/tests/text-buffer-selection_viewport_test.zig +358 -0
  426. package/src/zig/tests/text-buffer-view_test.zig +3649 -0
  427. package/src/zig/tests/text-buffer_test.zig +2191 -0
  428. package/src/zig/tests/unicode-width-map.zon +3909 -0
  429. package/src/zig/tests/utf8_no_zwj_test.zig +260 -0
  430. package/src/zig/tests/utf8_test.zig +4057 -0
  431. package/src/zig/tests/utf8_wcwidth_cursor_test.zig +267 -0
  432. package/src/zig/tests/utf8_wcwidth_test.zig +357 -0
  433. package/src/zig/tests/word-wrap-editing_test.zig +498 -0
  434. package/src/zig/tests/wrap-cache-perf_test.zig +113 -0
  435. package/src/zig/text-buffer-iterators.zig +499 -0
  436. package/src/zig/text-buffer-segment.zig +404 -0
  437. package/src/zig/text-buffer-view.zig +1371 -0
  438. package/src/zig/text-buffer.zig +1180 -0
  439. package/src/zig/utf8.zig +1948 -0
  440. package/src/zig/utils.zig +9 -0
  441. package/src/zig-structs.ts +261 -0
  442. package/src/zig.ts +3884 -0
  443. package/tsconfig.build.json +24 -0
  444. package/tsconfig.json +27 -0
  445. package/3d/SpriteResourceManager.d.ts +0 -74
  446. package/3d/SpriteUtils.d.ts +0 -13
  447. package/3d/TextureUtils.d.ts +0 -24
  448. package/3d/ThreeRenderable.d.ts +0 -40
  449. package/3d/WGPURenderer.d.ts +0 -61
  450. package/3d/animation/ExplodingSpriteEffect.d.ts +0 -71
  451. package/3d/animation/PhysicsExplodingSpriteEffect.d.ts +0 -76
  452. package/3d/animation/SpriteAnimator.d.ts +0 -124
  453. package/3d/animation/SpriteParticleGenerator.d.ts +0 -62
  454. package/3d/canvas.d.ts +0 -44
  455. package/3d/index.d.ts +0 -12
  456. package/3d/physics/PlanckPhysicsAdapter.d.ts +0 -19
  457. package/3d/physics/RapierPhysicsAdapter.d.ts +0 -19
  458. package/3d/physics/physics-interface.d.ts +0 -27
  459. package/3d.d.ts +0 -2
  460. package/3d.js +0 -34041
  461. package/3d.js.map +0 -155
  462. package/LICENSE +0 -21
  463. package/NativeSpanFeed.d.ts +0 -41
  464. package/Renderable.d.ts +0 -334
  465. package/animation/Timeline.d.ts +0 -126
  466. package/ansi.d.ts +0 -13
  467. package/buffer.d.ts +0 -111
  468. package/console.d.ts +0 -144
  469. package/edit-buffer.d.ts +0 -98
  470. package/editor-view.d.ts +0 -73
  471. package/index-9vwc3fg6.js +0 -12260
  472. package/index-9vwc3fg6.js.map +0 -42
  473. package/index-dcj62y8t.js +0 -20614
  474. package/index-dcj62y8t.js.map +0 -67
  475. package/index-f7n39gpy.js +0 -411
  476. package/index-f7n39gpy.js.map +0 -10
  477. package/index.d.ts +0 -23
  478. package/index.js +0 -478
  479. package/index.js.map +0 -9
  480. package/lib/KeyHandler.d.ts +0 -61
  481. package/lib/RGBA.d.ts +0 -25
  482. package/lib/ascii.font.d.ts +0 -508
  483. package/lib/border.d.ts +0 -51
  484. package/lib/bunfs.d.ts +0 -7
  485. package/lib/clipboard.d.ts +0 -17
  486. package/lib/clock.d.ts +0 -15
  487. package/lib/data-paths.d.ts +0 -26
  488. package/lib/debounce.d.ts +0 -42
  489. package/lib/detect-links.d.ts +0 -6
  490. package/lib/env.d.ts +0 -42
  491. package/lib/extmarks-history.d.ts +0 -17
  492. package/lib/extmarks.d.ts +0 -89
  493. package/lib/hast-styled-text.d.ts +0 -17
  494. package/lib/index.d.ts +0 -21
  495. package/lib/keymapping.d.ts +0 -25
  496. package/lib/objects-in-viewport.d.ts +0 -24
  497. package/lib/output.capture.d.ts +0 -24
  498. package/lib/parse.keypress-kitty.d.ts +0 -2
  499. package/lib/parse.keypress.d.ts +0 -26
  500. package/lib/parse.mouse.d.ts +0 -30
  501. package/lib/paste.d.ts +0 -7
  502. package/lib/queue.d.ts +0 -15
  503. package/lib/renderable.validations.d.ts +0 -12
  504. package/lib/scroll-acceleration.d.ts +0 -43
  505. package/lib/selection.d.ts +0 -63
  506. package/lib/singleton.d.ts +0 -7
  507. package/lib/stdin-parser.d.ts +0 -87
  508. package/lib/styled-text.d.ts +0 -63
  509. package/lib/terminal-capability-detection.d.ts +0 -30
  510. package/lib/terminal-palette.d.ts +0 -50
  511. package/lib/tree-sitter/assets/update.d.ts +0 -11
  512. package/lib/tree-sitter/client.d.ts +0 -47
  513. package/lib/tree-sitter/default-parsers.d.ts +0 -2
  514. package/lib/tree-sitter/download-utils.d.ts +0 -21
  515. package/lib/tree-sitter/index.d.ts +0 -8
  516. package/lib/tree-sitter/parser.worker.d.ts +0 -1
  517. package/lib/tree-sitter/parsers-config.d.ts +0 -53
  518. package/lib/tree-sitter/resolve-ft.d.ts +0 -5
  519. package/lib/tree-sitter/types.d.ts +0 -82
  520. package/lib/tree-sitter-styled-text.d.ts +0 -14
  521. package/lib/validate-dir-name.d.ts +0 -1
  522. package/lib/yoga.options.d.ts +0 -32
  523. package/parser.worker.js +0 -899
  524. package/parser.worker.js.map +0 -12
  525. package/plugins/core-slot.d.ts +0 -72
  526. package/plugins/registry.d.ts +0 -42
  527. package/plugins/types.d.ts +0 -34
  528. package/post/effects.d.ts +0 -147
  529. package/post/filters.d.ts +0 -65
  530. package/post/matrices.d.ts +0 -20
  531. package/renderables/ASCIIFont.d.ts +0 -52
  532. package/renderables/Box.d.ts +0 -81
  533. package/renderables/Code.d.ts +0 -78
  534. package/renderables/Diff.d.ts +0 -142
  535. package/renderables/EditBufferRenderable.d.ts +0 -237
  536. package/renderables/FrameBuffer.d.ts +0 -16
  537. package/renderables/Input.d.ts +0 -67
  538. package/renderables/LineNumberRenderable.d.ts +0 -78
  539. package/renderables/Markdown.d.ts +0 -185
  540. package/renderables/ScrollBar.d.ts +0 -77
  541. package/renderables/ScrollBox.d.ts +0 -124
  542. package/renderables/Select.d.ts +0 -115
  543. package/renderables/Slider.d.ts +0 -47
  544. package/renderables/TabSelect.d.ts +0 -96
  545. package/renderables/Text.d.ts +0 -36
  546. package/renderables/TextBufferRenderable.d.ts +0 -105
  547. package/renderables/TextNode.d.ts +0 -91
  548. package/renderables/TextTable.d.ts +0 -140
  549. package/renderables/Textarea.d.ts +0 -63
  550. package/renderables/TimeToFirstDraw.d.ts +0 -24
  551. package/renderables/__tests__/renderable-test-utils.d.ts +0 -12
  552. package/renderables/composition/VRenderable.d.ts +0 -16
  553. package/renderables/composition/constructs.d.ts +0 -35
  554. package/renderables/composition/vnode.d.ts +0 -46
  555. package/renderables/index.d.ts +0 -23
  556. package/renderables/markdown-parser.d.ts +0 -10
  557. package/renderer.d.ts +0 -419
  558. package/runtime-plugin-support.d.ts +0 -3
  559. package/runtime-plugin-support.js +0 -29
  560. package/runtime-plugin-support.js.map +0 -10
  561. package/runtime-plugin.d.ts +0 -16
  562. package/runtime-plugin.js +0 -16
  563. package/runtime-plugin.js.map +0 -9
  564. package/syntax-style.d.ts +0 -54
  565. package/testing/manual-clock.d.ts +0 -17
  566. package/testing/mock-keys.d.ts +0 -81
  567. package/testing/mock-mouse.d.ts +0 -38
  568. package/testing/mock-tree-sitter-client.d.ts +0 -23
  569. package/testing/spy.d.ts +0 -7
  570. package/testing/test-recorder.d.ts +0 -61
  571. package/testing/test-renderer.d.ts +0 -23
  572. package/testing.d.ts +0 -6
  573. package/testing.js +0 -697
  574. package/testing.js.map +0 -15
  575. package/text-buffer-view.d.ts +0 -42
  576. package/text-buffer.d.ts +0 -67
  577. package/types.d.ts +0 -139
  578. package/utils.d.ts +0 -14
  579. package/zig-structs.d.ts +0 -155
  580. package/zig.d.ts +0 -353
  581. /package/{assets → src/lib/tree-sitter/assets}/javascript/highlights.scm +0 -0
  582. /package/{assets → src/lib/tree-sitter/assets}/javascript/tree-sitter-javascript.wasm +0 -0
  583. /package/{assets → src/lib/tree-sitter/assets}/markdown/highlights.scm +0 -0
  584. /package/{assets → src/lib/tree-sitter/assets}/markdown/injections.scm +0 -0
  585. /package/{assets → src/lib/tree-sitter/assets}/markdown/tree-sitter-markdown.wasm +0 -0
  586. /package/{assets → src/lib/tree-sitter/assets}/markdown_inline/highlights.scm +0 -0
  587. /package/{assets → src/lib/tree-sitter/assets}/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
  588. /package/{assets → src/lib/tree-sitter/assets}/typescript/highlights.scm +0 -0
  589. /package/{assets → src/lib/tree-sitter/assets}/typescript/tree-sitter-typescript.wasm +0 -0
  590. /package/{assets → src/lib/tree-sitter/assets}/zig/highlights.scm +0 -0
  591. /package/{assets → src/lib/tree-sitter/assets}/zig/tree-sitter-zig.wasm +0 -0
@@ -0,0 +1,257 @@
1
+ import { RGBA, parseColor, type ColorInput } from "./lib/RGBA.js"
2
+ import { resolveRenderLib, type RenderLib } from "./zig.js"
3
+ import { type Pointer } from "bun:ffi"
4
+ import { createTextAttributes } from "./utils.js"
5
+
6
+ export interface StyleDefinition {
7
+ fg?: RGBA
8
+ bg?: RGBA
9
+ bold?: boolean
10
+ italic?: boolean
11
+ underline?: boolean
12
+ dim?: boolean
13
+ }
14
+
15
+ export interface MergedStyle {
16
+ fg?: RGBA
17
+ bg?: RGBA
18
+ attributes: number
19
+ }
20
+
21
+ export interface ThemeTokenStyle {
22
+ scope: string[]
23
+ style: {
24
+ foreground?: ColorInput
25
+ background?: ColorInput
26
+ bold?: boolean
27
+ italic?: boolean
28
+ underline?: boolean
29
+ dim?: boolean
30
+ }
31
+ }
32
+
33
+ export function convertThemeToStyles(theme: ThemeTokenStyle[]): Record<string, StyleDefinition> {
34
+ const flatStyles: Record<string, StyleDefinition> = {}
35
+
36
+ for (const tokenStyle of theme) {
37
+ const styleDefinition: StyleDefinition = {}
38
+
39
+ if (tokenStyle.style.foreground) {
40
+ styleDefinition.fg = parseColor(tokenStyle.style.foreground)
41
+ }
42
+ if (tokenStyle.style.background) {
43
+ styleDefinition.bg = parseColor(tokenStyle.style.background)
44
+ }
45
+
46
+ if (tokenStyle.style.bold !== undefined) {
47
+ styleDefinition.bold = tokenStyle.style.bold
48
+ }
49
+ if (tokenStyle.style.italic !== undefined) {
50
+ styleDefinition.italic = tokenStyle.style.italic
51
+ }
52
+ if (tokenStyle.style.underline !== undefined) {
53
+ styleDefinition.underline = tokenStyle.style.underline
54
+ }
55
+ if (tokenStyle.style.dim !== undefined) {
56
+ styleDefinition.dim = tokenStyle.style.dim
57
+ }
58
+
59
+ // Apply the same style to all scopes
60
+ for (const scope of tokenStyle.scope) {
61
+ flatStyles[scope] = styleDefinition
62
+ }
63
+ }
64
+
65
+ return flatStyles
66
+ }
67
+
68
+ export class SyntaxStyle {
69
+ private lib: RenderLib
70
+ private stylePtr: Pointer
71
+ private _destroyed: boolean = false
72
+ private nameCache: Map<string, number> = new Map()
73
+ private styleDefs: Map<string, StyleDefinition> = new Map()
74
+ private mergedCache: Map<string, MergedStyle> = new Map()
75
+
76
+ constructor(lib: RenderLib, ptr: Pointer) {
77
+ this.lib = lib
78
+ this.stylePtr = ptr
79
+ }
80
+
81
+ static create(): SyntaxStyle {
82
+ const lib = resolveRenderLib()
83
+ const ptr = lib.createSyntaxStyle()
84
+ return new SyntaxStyle(lib, ptr)
85
+ }
86
+
87
+ static fromTheme(theme: ThemeTokenStyle[]): SyntaxStyle {
88
+ const style = SyntaxStyle.create()
89
+ const flatStyles = convertThemeToStyles(theme)
90
+
91
+ for (const [name, styleDef] of Object.entries(flatStyles)) {
92
+ style.registerStyle(name, styleDef)
93
+ }
94
+
95
+ return style
96
+ }
97
+
98
+ static fromStyles(styles: Record<string, StyleDefinition>): SyntaxStyle {
99
+ const style = SyntaxStyle.create()
100
+
101
+ for (const [name, styleDef] of Object.entries(styles)) {
102
+ style.registerStyle(name, styleDef)
103
+ }
104
+
105
+ return style
106
+ }
107
+
108
+ private guard(): void {
109
+ if (this._destroyed) throw new Error("NativeSyntaxStyle is destroyed")
110
+ }
111
+
112
+ public registerStyle(name: string, style: StyleDefinition): number {
113
+ this.guard()
114
+
115
+ const attributes = createTextAttributes({
116
+ bold: style.bold,
117
+ italic: style.italic,
118
+ underline: style.underline,
119
+ dim: style.dim,
120
+ })
121
+
122
+ const id = this.lib.syntaxStyleRegister(this.stylePtr, name, style.fg || null, style.bg || null, attributes)
123
+
124
+ this.nameCache.set(name, id)
125
+ this.styleDefs.set(name, style)
126
+
127
+ return id
128
+ }
129
+
130
+ public resolveStyleId(name: string): number | null {
131
+ this.guard()
132
+
133
+ // Check cache first
134
+ const cached = this.nameCache.get(name)
135
+ if (cached !== undefined) return cached
136
+
137
+ const id = this.lib.syntaxStyleResolveByName(this.stylePtr, name)
138
+
139
+ if (id !== null) {
140
+ this.nameCache.set(name, id)
141
+ }
142
+
143
+ return id
144
+ }
145
+
146
+ public getStyleId(name: string): number | null {
147
+ this.guard()
148
+
149
+ const id = this.resolveStyleId(name)
150
+ if (id !== null) return id
151
+
152
+ // Try base name if it's a scoped style
153
+ if (name.includes(".")) {
154
+ const baseName = name.split(".")[0]
155
+ return this.resolveStyleId(baseName)
156
+ }
157
+
158
+ return null
159
+ }
160
+
161
+ public get ptr(): Pointer {
162
+ this.guard()
163
+ return this.stylePtr
164
+ }
165
+
166
+ public getStyleCount(): number {
167
+ this.guard()
168
+ return this.lib.syntaxStyleGetStyleCount(this.stylePtr)
169
+ }
170
+
171
+ public clearNameCache(): void {
172
+ this.nameCache.clear()
173
+ }
174
+
175
+ public getStyle(name: string): StyleDefinition | undefined {
176
+ this.guard()
177
+
178
+ const style = this.styleDefs.get(name)
179
+ if (style) return style
180
+
181
+ if (name.includes(".")) {
182
+ const baseName = name.split(".")[0]
183
+ return this.styleDefs.get(baseName)
184
+ }
185
+
186
+ return undefined
187
+ }
188
+
189
+ public mergeStyles(...styleNames: string[]): MergedStyle {
190
+ this.guard()
191
+
192
+ const cacheKey = styleNames.join(":")
193
+ const cached = this.mergedCache.get(cacheKey)
194
+ if (cached) return cached
195
+
196
+ const styleDefinition: StyleDefinition = {}
197
+
198
+ for (const name of styleNames) {
199
+ const style = this.getStyle(name)
200
+
201
+ if (!style) continue
202
+
203
+ if (style.fg) styleDefinition.fg = style.fg
204
+ if (style.bg) styleDefinition.bg = style.bg
205
+ if (style.bold !== undefined) styleDefinition.bold = style.bold
206
+ if (style.italic !== undefined) styleDefinition.italic = style.italic
207
+ if (style.underline !== undefined) styleDefinition.underline = style.underline
208
+ if (style.dim !== undefined) styleDefinition.dim = style.dim
209
+ }
210
+
211
+ const attributes = createTextAttributes({
212
+ bold: styleDefinition.bold,
213
+ italic: styleDefinition.italic,
214
+ underline: styleDefinition.underline,
215
+ dim: styleDefinition.dim,
216
+ })
217
+
218
+ const merged: MergedStyle = {
219
+ fg: styleDefinition.fg,
220
+ bg: styleDefinition.bg,
221
+ attributes,
222
+ }
223
+
224
+ this.mergedCache.set(cacheKey, merged)
225
+
226
+ return merged
227
+ }
228
+
229
+ public clearCache(): void {
230
+ this.guard()
231
+ this.mergedCache.clear()
232
+ }
233
+
234
+ public getCacheSize(): number {
235
+ this.guard()
236
+ return this.mergedCache.size
237
+ }
238
+
239
+ public getAllStyles(): Map<string, StyleDefinition> {
240
+ this.guard()
241
+ return new Map(this.styleDefs)
242
+ }
243
+
244
+ public getRegisteredNames(): string[] {
245
+ this.guard()
246
+ return Array.from(this.styleDefs.keys())
247
+ }
248
+
249
+ public destroy(): void {
250
+ if (this._destroyed) return
251
+ this._destroyed = true
252
+ this.nameCache.clear()
253
+ this.styleDefs.clear()
254
+ this.mergedCache.clear()
255
+ this.lib.destroySyntaxStyle(this.stylePtr)
256
+ }
257
+ }
@@ -0,0 +1,210 @@
1
+ # Testing Utilities
2
+
3
+ Test utilities for opentui terminal UI testing.
4
+
5
+ ## Test Renderer
6
+
7
+ ```ts
8
+ import { createTestRenderer } from "@fairyhunter13/opentui-core/testing"
9
+
10
+ const { renderer, mockInput, mockMouse, renderOnce, captureCharFrame, resize } = await createTestRenderer({
11
+ width: 80,
12
+ height: 24,
13
+ })
14
+
15
+ // Render once and capture output
16
+ await renderOnce()
17
+ const output = captureCharFrame()
18
+
19
+ // Resize terminal
20
+ resize(100, 30)
21
+ ```
22
+
23
+ ## Mock Keyboard Input
24
+
25
+ ```ts
26
+ import { createMockKeys, KeyCodes } from "@fairyhunter13/opentui-core/testing"
27
+
28
+ const mockInput = createMockKeys(renderer)
29
+
30
+ // Type text
31
+ mockInput.typeText("hello world")
32
+ await mockInput.typeText("hello", 10) // 10ms delay between keys
33
+
34
+ // Press single keys
35
+ mockInput.pressKey("a")
36
+ mockInput.pressKey(KeyCodes.ENTER)
37
+
38
+ // Press keys with modifiers
39
+ mockInput.pressKey("a", { ctrl: true })
40
+ mockInput.pressKey("f", { meta: true })
41
+ mockInput.pressKey("z", { ctrl: true, shift: true })
42
+ mockInput.pressKey(KeyCodes.ARROW_LEFT, { meta: true })
43
+
44
+ // Press multiple keys
45
+ mockInput.pressKeys(["h", "e", "l", "l", "o"])
46
+ await mockInput.pressKeys(["a", "b"], 10) // with delay
47
+
48
+ // Convenience methods
49
+ mockInput.pressEnter()
50
+ mockInput.pressEnter({ meta: true })
51
+ mockInput.pressEscape()
52
+ mockInput.pressTab()
53
+ mockInput.pressBackspace()
54
+ mockInput.pressArrow("up" | "down" | "left" | "right")
55
+ mockInput.pressArrow("left", { meta: true })
56
+ mockInput.pressCtrlC()
57
+ mockInput.pasteBracketedText("paste content")
58
+ ```
59
+
60
+ ### KeyCodes
61
+
62
+ Special keycodes available: `RETURN`, `LINEFEED`, `TAB`, `BACKSPACE`, `DELETE`, `HOME`, `END`, `ESCAPE`, `ARROW_UP`, `ARROW_DOWN`, `ARROW_LEFT`, `ARROW_RIGHT`, `F1`-`F12`
63
+
64
+ ### Modifiers
65
+
66
+ All `pressKey()`, `pressEnter()`, `pressEscape()`, `pressTab()`, `pressBackspace()`, and `pressArrow()` methods support an optional modifiers object:
67
+
68
+ ```ts
69
+ { ctrl?: boolean; shift?: boolean; meta?: boolean }
70
+ ```
71
+
72
+ ## Mock Mouse Input
73
+
74
+ ```ts
75
+ import { createMockMouse, MouseButtons } from "@fairyhunter13/opentui-core/testing"
76
+
77
+ const mockMouse = createMockMouse(renderer)
78
+
79
+ // Click
80
+ await mockMouse.click(x, y)
81
+ await mockMouse.click(x, y, MouseButtons.RIGHT)
82
+ await mockMouse.click(x, y, MouseButtons.LEFT, {
83
+ modifiers: { ctrl: true, shift: true, alt: true },
84
+ delayMs: 10,
85
+ })
86
+
87
+ // Double click
88
+ await mockMouse.doubleClick(x, y)
89
+
90
+ // Press and release
91
+ await mockMouse.pressDown(x, y, MouseButtons.MIDDLE)
92
+ await mockMouse.release(x, y, MouseButtons.MIDDLE)
93
+
94
+ // Move
95
+ await mockMouse.moveTo(x, y)
96
+ await mockMouse.moveTo(x, y, { modifiers: { shift: true } })
97
+
98
+ // Drag
99
+ await mockMouse.drag(startX, startY, endX, endY)
100
+ await mockMouse.drag(startX, startY, endX, endY, MouseButtons.RIGHT, {
101
+ modifiers: { alt: true },
102
+ })
103
+
104
+ // Scroll
105
+ await mockMouse.scroll(x, y, "up" | "down" | "left" | "right")
106
+ await mockMouse.scroll(x, y, "up", { modifiers: { shift: true } })
107
+
108
+ // State
109
+ const pos = mockMouse.getCurrentPosition() // { x, y }
110
+ const buttons = mockMouse.getPressedButtons() // MouseButton[]
111
+ ```
112
+
113
+ ### MouseButtons
114
+
115
+ `LEFT` (0), `MIDDLE` (1), `RIGHT` (2)
116
+
117
+ `WHEEL_UP` (64), `WHEEL_DOWN` (65), `WHEEL_LEFT` (66), `WHEEL_RIGHT` (67)
118
+
119
+ ## Spy
120
+
121
+ Simple function spy for testing callbacks.
122
+
123
+ ```ts
124
+ import { createSpy } from "@fairyhunter13/opentui-core/testing"
125
+
126
+ const spy = createSpy()
127
+
128
+ // Use as callback
129
+ someFunction(spy)
130
+
131
+ // Assertions
132
+ spy.callCount() // number
133
+ spy.calledWith(arg1, arg2) // boolean
134
+ spy.calls // any[][]
135
+ spy.reset()
136
+ ```
137
+
138
+ ## Test Recorder
139
+
140
+ Record frames during rendering for testing or analysis.
141
+
142
+ ```ts
143
+ import { TestRecorder } from "@fairyhunter13/opentui-core/testing"
144
+
145
+ const { renderer, renderOnce } = await createTestRenderer({ width: 80, height: 24 })
146
+ const recorder = new TestRecorder(renderer)
147
+
148
+ // Start recording
149
+ recorder.rec()
150
+
151
+ // Add content and trigger renders
152
+ const text = new TextRenderable(renderer, { content: "Hello" })
153
+ renderer.root.add(text)
154
+ await Bun.sleep(1) // Wait for automatic render from add()
155
+
156
+ text.content = "World"
157
+ await Bun.sleep(1) // Wait for automatic render from content change
158
+
159
+ // Stop recording
160
+ recorder.stop()
161
+
162
+ // Access recorded frames
163
+ const frames = recorder.recordedFrames
164
+ console.log(`Recorded ${frames.length} frames`)
165
+
166
+ frames.forEach((frame) => {
167
+ console.log(`Frame ${frame.frameNumber} at ${frame.timestamp}ms:`)
168
+ console.log(frame.frame)
169
+ })
170
+
171
+ // Clear and start new recording
172
+ recorder.clear()
173
+ recorder.rec()
174
+ ```
175
+
176
+ ### TestRecorder API
177
+
178
+ - `rec()` - Start recording frames
179
+ - `stop()` - Stop recording frames
180
+ - `clear()` - Clear all recorded frames
181
+ - `isRecording` - Check if currently recording
182
+ - `recordedFrames` - Get array of recorded frames (returns a copy)
183
+
184
+ ### RecordedFrame
185
+
186
+ Each frame contains:
187
+
188
+ - `frame: string` - The captured frame content
189
+ - `timestamp: number` - Time in milliseconds since recording started
190
+ - `frameNumber: number` - Sequential frame number (0-indexed)
191
+
192
+ ## Example
193
+
194
+ ```ts
195
+ import { test, expect } from "bun:test"
196
+ import { createTestRenderer } from "@fairyhunter13/opentui-core/testing"
197
+
198
+ test("button click", async () => {
199
+ const { renderer, mockMouse, renderOnce, captureCharFrame } = await createTestRenderer({ width: 80, height: 24 })
200
+
201
+ const clicked = createSpy()
202
+ const button = new Button("btn", { text: "Click me", onClick: clicked })
203
+
204
+ renderer.add(button)
205
+ await renderOnce()
206
+
207
+ await mockMouse.click(10, 5)
208
+ expect(clicked.callCount()).toBe(1)
209
+ })
210
+ ```
@@ -0,0 +1,194 @@
1
+ import { describe, test, expect, beforeEach, afterEach } from "bun:test"
2
+ import { createTestRenderer, type TestRenderer } from "./test-renderer.js"
3
+ import { TextRenderable } from "../renderables/Text.js"
4
+ import { BoxRenderable } from "../renderables/Box.js"
5
+ import { TextAttributes, type CapturedFrame } from "../types.js"
6
+ import { RGBA } from "../lib/index.js"
7
+
8
+ describe("captureSpans", () => {
9
+ let renderer: TestRenderer
10
+ let renderOnce: () => Promise<void>
11
+ let captureSpans: () => CapturedFrame
12
+
13
+ beforeEach(async () => {
14
+ const setup = await createTestRenderer({ width: 40, height: 10 })
15
+ renderer = setup.renderer
16
+ renderOnce = setup.renderOnce
17
+ captureSpans = setup.captureSpans
18
+ })
19
+
20
+ afterEach(() => {
21
+ renderer.destroy()
22
+ })
23
+
24
+ test("returns correct dimensions and line count", async () => {
25
+ await renderOnce()
26
+ const data = captureSpans()
27
+
28
+ expect(data.cols).toBe(40)
29
+ expect(data.rows).toBe(10)
30
+ expect(data.lines.length).toBe(10)
31
+ })
32
+
33
+ test("captures text content in spans", async () => {
34
+ const text = new TextRenderable(renderer, { content: "Hello World" })
35
+ renderer.root.add(text)
36
+ await renderOnce()
37
+
38
+ const data = captureSpans()
39
+ const firstLine = data.lines[0]
40
+ const textContent = firstLine.spans.map((s) => s.text).join("")
41
+
42
+ expect(textContent).toContain("Hello World")
43
+ })
44
+
45
+ test("groups consecutive cells with same styling into single span", async () => {
46
+ const text = new TextRenderable(renderer, { content: "AAAA" })
47
+ renderer.root.add(text)
48
+ await renderOnce()
49
+
50
+ const data = captureSpans()
51
+ const firstLine = data.lines[0]
52
+ const aaaSpan = firstLine.spans.find((s) => s.text.includes("AAAA"))
53
+
54
+ expect(aaaSpan).toBeDefined()
55
+ expect(aaaSpan!.width).toBeGreaterThanOrEqual(4)
56
+ })
57
+
58
+ test("captures foreground color", async () => {
59
+ const text = new TextRenderable(renderer, {
60
+ content: "Red Text",
61
+ fg: RGBA.fromHex("#ff0000"),
62
+ })
63
+ renderer.root.add(text)
64
+ await renderOnce()
65
+
66
+ const data = captureSpans()
67
+ const firstLine = data.lines[0]
68
+ const redSpan = firstLine.spans.find((s) => s.text.includes("Red"))
69
+
70
+ expect(redSpan).toBeDefined()
71
+ expect(redSpan!.fg.r).toBe(1)
72
+ expect(redSpan!.fg.g).toBe(0)
73
+ expect(redSpan!.fg.b).toBe(0)
74
+ })
75
+
76
+ test("captures background color", async () => {
77
+ const box = new BoxRenderable(renderer, {
78
+ width: 10,
79
+ height: 3,
80
+ backgroundColor: RGBA.fromHex("#00ff00"),
81
+ })
82
+ renderer.root.add(box)
83
+ await renderOnce()
84
+
85
+ const data = captureSpans()
86
+ const secondLine = data.lines[1]
87
+ const greenSpan = secondLine.spans.find((s) => s.bg.g === 1 && s.bg.r === 0 && s.bg.b === 0)
88
+
89
+ expect(greenSpan).toBeDefined()
90
+ })
91
+
92
+ test("returns alpha 0 for transparent colors", async () => {
93
+ await renderOnce()
94
+
95
+ const data = captureSpans()
96
+ const firstLine = data.lines[0]
97
+ const transparentSpan = firstLine.spans.find((s) => s.bg.a === 0)
98
+
99
+ expect(transparentSpan).toBeDefined()
100
+ })
101
+
102
+ test("captures text attributes", async () => {
103
+ const text = new TextRenderable(renderer, {
104
+ content: "Styled",
105
+ attributes: TextAttributes.BOLD | TextAttributes.ITALIC | TextAttributes.UNDERLINE | TextAttributes.DIM,
106
+ })
107
+ renderer.root.add(text)
108
+ await renderOnce()
109
+
110
+ const data = captureSpans()
111
+ const firstLine = data.lines[0]
112
+ const styledSpan = firstLine.spans.find((s) => s.text.includes("Styled"))
113
+
114
+ expect(styledSpan).toBeDefined()
115
+ expect(styledSpan!.attributes & TextAttributes.BOLD).toBeTruthy()
116
+ expect(styledSpan!.attributes & TextAttributes.ITALIC).toBeTruthy()
117
+ expect(styledSpan!.attributes & TextAttributes.UNDERLINE).toBeTruthy()
118
+ expect(styledSpan!.attributes & TextAttributes.DIM).toBeTruthy()
119
+ })
120
+
121
+ test("includes cursor position", async () => {
122
+ await renderOnce()
123
+ const data = captureSpans()
124
+
125
+ expect(data.cursor).toEqual([expect.any(Number), expect.any(Number)])
126
+ })
127
+
128
+ test("splits spans when styling changes", async () => {
129
+ const text1 = new TextRenderable(renderer, {
130
+ content: "AAA",
131
+ fg: RGBA.fromHex("#ff0000"),
132
+ })
133
+ const text2 = new TextRenderable(renderer, {
134
+ content: "BBB",
135
+ fg: RGBA.fromHex("#00ff00"),
136
+ })
137
+ renderer.root.add(text1)
138
+ renderer.root.add(text2)
139
+ await renderOnce()
140
+
141
+ const data = captureSpans()
142
+ const allSpans = data.lines.flatMap((l) => l.spans)
143
+
144
+ expect(allSpans.some((s) => s.fg.r === 1 && s.fg.g === 0)).toBe(true)
145
+ expect(allSpans.some((s) => s.fg.g === 1 && s.fg.r === 0)).toBe(true)
146
+ })
147
+
148
+ test("handles box-drawing characters without crashing", async () => {
149
+ const text = new TextRenderable(renderer, {
150
+ content: "├── folder",
151
+ })
152
+ renderer.root.add(text)
153
+ await renderOnce()
154
+
155
+ const data = captureSpans()
156
+ const firstLine = data.lines[0]
157
+ const textContent = firstLine.spans.map((s) => s.text).join("")
158
+
159
+ expect(textContent).toContain("├── folder")
160
+ })
161
+
162
+ test("handles box borders without crashing", async () => {
163
+ const box = new BoxRenderable(renderer, {
164
+ width: 10,
165
+ height: 4,
166
+ border: true,
167
+ borderStyle: "single",
168
+ borderColor: RGBA.fromHex("#ffffff"),
169
+ })
170
+ renderer.root.add(box)
171
+ await renderOnce()
172
+
173
+ const data = captureSpans()
174
+ expect(data.lines.length).toBe(10)
175
+
176
+ const firstLine = data.lines[0]
177
+ const textContent = firstLine.spans.map((s) => s.text).join("")
178
+ expect(textContent.includes("┌") || textContent.includes("─")).toBe(true)
179
+ })
180
+
181
+ test("handles multi-width characters correctly", async () => {
182
+ const text = new TextRenderable(renderer, {
183
+ content: "A🌟B",
184
+ })
185
+ renderer.root.add(text)
186
+ await renderOnce()
187
+
188
+ const data = captureSpans()
189
+ const firstLine = data.lines[0]
190
+ const textContent = firstLine.spans.map((s) => s.text).join("")
191
+
192
+ expect(textContent).toContain("A🌟B")
193
+ })
194
+ })