@xincli/opentui-core 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (476) hide show
  1. package/README.md +63 -0
  2. package/dev/keypress-debug-renderer.ts +148 -0
  3. package/dev/keypress-debug.ts +43 -0
  4. package/dev/print-env-vars.ts +32 -0
  5. package/dev/test-tmux-graphics-334.sh +68 -0
  6. package/dev/test-tmux-theme-queries.sh +151 -0
  7. package/dev/thai-debug-test.ts +68 -0
  8. package/dev/theme-mode.ts +290 -0
  9. package/docs/development.md +144 -0
  10. package/package.json +102 -0
  11. package/prebuilt/aarch64-android/libopentui.so +0 -0
  12. package/scripts/build-android.ts +226 -0
  13. package/scripts/build-native-termux.sh +525 -0
  14. package/scripts/build.ts +530 -0
  15. package/scripts/dist-test.ts +312 -0
  16. package/scripts/package-prebuilt.ts +110 -0
  17. package/scripts/publish.ts +60 -0
  18. package/scripts/test-node-hook.mjs +16 -0
  19. package/scripts/test-node.ts +241 -0
  20. package/scripts/vendor-deps.sh +73 -0
  21. package/src/NativeSpanFeed.ts +375 -0
  22. package/src/Renderable.ts +1878 -0
  23. package/src/__snapshots__/buffer.test.ts.snap +28 -0
  24. package/src/animation/Timeline.test.ts +2709 -0
  25. package/src/animation/Timeline.ts +598 -0
  26. package/src/ansi.ts +22 -0
  27. package/src/audio.ts +479 -0
  28. package/src/benchmark/attenuation-benchmark.ts +81 -0
  29. package/src/benchmark/audio-playback-benchmark.ts +344 -0
  30. package/src/benchmark/box-draw-benchmark.ts +1042 -0
  31. package/src/benchmark/colormatrix-benchmark.ts +123 -0
  32. package/src/benchmark/gain-benchmark.ts +80 -0
  33. package/src/benchmark/latest-all-bench-run.json +707 -0
  34. package/src/benchmark/latest-async-bench-run.json +336 -0
  35. package/src/benchmark/latest-default-bench-run.json +657 -0
  36. package/src/benchmark/latest-large-bench-run.json +707 -0
  37. package/src/benchmark/latest-quick-bench-run.json +207 -0
  38. package/src/benchmark/layout-benchmark.ts +2547 -0
  39. package/src/benchmark/markdown-benchmark.ts +1796 -0
  40. package/src/benchmark/native-span-feed-async-benchmark.ts +355 -0
  41. package/src/benchmark/native-span-feed-benchmark.md +56 -0
  42. package/src/benchmark/native-span-feed-benchmark.ts +596 -0
  43. package/src/benchmark/native-span-feed-compare.ts +280 -0
  44. package/src/benchmark/render-traversal-benchmark.ts +928 -0
  45. package/src/benchmark/text-buffer-render-benchmark.ts +874 -0
  46. package/src/benchmark/text-table-benchmark.ts +948 -0
  47. package/src/buffer.test.ts +292 -0
  48. package/src/buffer.ts +564 -0
  49. package/src/console.test.ts +629 -0
  50. package/src/console.ts +1253 -0
  51. package/src/edit-buffer.test.ts +1852 -0
  52. package/src/edit-buffer.ts +417 -0
  53. package/src/editor-view.test.ts +1056 -0
  54. package/src/editor-view.ts +290 -0
  55. package/src/index.ts +25 -0
  56. package/src/lib/KeyHandler.integration.test.ts +292 -0
  57. package/src/lib/KeyHandler.stopPropagation.test.ts +289 -0
  58. package/src/lib/KeyHandler.test.ts +662 -0
  59. package/src/lib/KeyHandler.ts +222 -0
  60. package/src/lib/RGBA.test.ts +60 -0
  61. package/src/lib/RGBA.ts +343 -0
  62. package/src/lib/ascii.font.ts +330 -0
  63. package/src/lib/border.test.ts +83 -0
  64. package/src/lib/border.ts +170 -0
  65. package/src/lib/bunfs.test.ts +27 -0
  66. package/src/lib/bunfs.ts +18 -0
  67. package/src/lib/clipboard.test.ts +63 -0
  68. package/src/lib/clipboard.ts +41 -0
  69. package/src/lib/clock.ts +35 -0
  70. package/src/lib/data-paths.test.ts +133 -0
  71. package/src/lib/data-paths.ts +109 -0
  72. package/src/lib/debounce.ts +106 -0
  73. package/src/lib/detect-links.test.ts +98 -0
  74. package/src/lib/detect-links.ts +56 -0
  75. package/src/lib/env.test.ts +228 -0
  76. package/src/lib/env.ts +209 -0
  77. package/src/lib/extmarks-history.ts +51 -0
  78. package/src/lib/extmarks-multiwidth.test.ts +324 -0
  79. package/src/lib/extmarks.test.ts +3547 -0
  80. package/src/lib/extmarks.ts +861 -0
  81. package/src/lib/fonts/block.json +405 -0
  82. package/src/lib/fonts/grid.json +265 -0
  83. package/src/lib/fonts/huge.json +741 -0
  84. package/src/lib/fonts/pallet.json +314 -0
  85. package/src/lib/fonts/shade.json +591 -0
  86. package/src/lib/fonts/slick.json +321 -0
  87. package/src/lib/fonts/tiny.json +69 -0
  88. package/src/lib/hast-styled-text.ts +59 -0
  89. package/src/lib/index.ts +22 -0
  90. package/src/lib/keybinding.internal.test.ts +342 -0
  91. package/src/lib/keybinding.internal.ts +182 -0
  92. package/src/lib/objects-in-viewport.test.ts +789 -0
  93. package/src/lib/objects-in-viewport.ts +153 -0
  94. package/src/lib/output.capture.ts +58 -0
  95. package/src/lib/parse.keypress-kitty.protocol.test.ts +347 -0
  96. package/src/lib/parse.keypress-kitty.test.ts +712 -0
  97. package/src/lib/parse.keypress-kitty.ts +470 -0
  98. package/src/lib/parse.keypress.test.ts +2020 -0
  99. package/src/lib/parse.keypress.ts +480 -0
  100. package/src/lib/parse.mouse.test.ts +552 -0
  101. package/src/lib/parse.mouse.ts +232 -0
  102. package/src/lib/paste.ts +18 -0
  103. package/src/lib/queue.ts +65 -0
  104. package/src/lib/render-geometry.ts +36 -0
  105. package/src/lib/renderable.validations.test.ts +87 -0
  106. package/src/lib/renderable.validations.ts +83 -0
  107. package/src/lib/scroll-acceleration.ts +98 -0
  108. package/src/lib/selection.ts +261 -0
  109. package/src/lib/singleton.ts +34 -0
  110. package/src/lib/stdin-parser.test.ts +2439 -0
  111. package/src/lib/stdin-parser.ts +2007 -0
  112. package/src/lib/styled-text.ts +178 -0
  113. package/src/lib/terminal-capability-detection.test.ts +226 -0
  114. package/src/lib/terminal-capability-detection.ts +98 -0
  115. package/src/lib/terminal-palette.test.ts +954 -0
  116. package/src/lib/terminal-palette.ts +441 -0
  117. package/src/lib/tree-sitter/assets/README.md +119 -0
  118. package/src/lib/tree-sitter/assets/javascript/highlights.scm +205 -0
  119. package/src/lib/tree-sitter/assets/javascript/tree-sitter-javascript.wasm +0 -0
  120. package/src/lib/tree-sitter/assets/markdown/highlights.scm +150 -0
  121. package/src/lib/tree-sitter/assets/markdown/injections.scm +27 -0
  122. package/src/lib/tree-sitter/assets/markdown/tree-sitter-markdown.wasm +0 -0
  123. package/src/lib/tree-sitter/assets/markdown_inline/highlights.scm +115 -0
  124. package/src/lib/tree-sitter/assets/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
  125. package/src/lib/tree-sitter/assets/typescript/highlights.scm +604 -0
  126. package/src/lib/tree-sitter/assets/typescript/tree-sitter-typescript.wasm +0 -0
  127. package/src/lib/tree-sitter/assets/update.ts +360 -0
  128. package/src/lib/tree-sitter/assets/zig/highlights.scm +284 -0
  129. package/src/lib/tree-sitter/assets/zig/tree-sitter-zig.wasm +0 -0
  130. package/src/lib/tree-sitter/assets.d.ts +9 -0
  131. package/src/lib/tree-sitter/cache.test.ts +291 -0
  132. package/src/lib/tree-sitter/client.test.ts +1434 -0
  133. package/src/lib/tree-sitter/client.ts +847 -0
  134. package/src/lib/tree-sitter/default-parsers.ts +133 -0
  135. package/src/lib/tree-sitter/download-utils.ts +148 -0
  136. package/src/lib/tree-sitter/index.ts +75 -0
  137. package/src/lib/tree-sitter/parser.worker.ts +1098 -0
  138. package/src/lib/tree-sitter/parsers-config.ts +81 -0
  139. package/src/lib/tree-sitter/resolve-ft.test.ts +55 -0
  140. package/src/lib/tree-sitter/resolve-ft.ts +189 -0
  141. package/src/lib/tree-sitter/types.ts +132 -0
  142. package/src/lib/tree-sitter/update-assets.ts +10 -0
  143. package/src/lib/tree-sitter-styled-text.test.ts +1253 -0
  144. package/src/lib/tree-sitter-styled-text.ts +314 -0
  145. package/src/lib/validate-dir-name.ts +55 -0
  146. package/src/lib/yoga.options.test.ts +628 -0
  147. package/src/lib/yoga.options.ts +346 -0
  148. package/src/native-event-worker-repro.test.ts +48 -0
  149. package/src/native-event-worker-repro.worker.ts +9 -0
  150. package/src/native-handle.test.ts +221 -0
  151. package/src/platform/ffi.test.ts +516 -0
  152. package/src/platform/ffi.ts +653 -0
  153. package/src/platform/runtime.test.ts +52 -0
  154. package/src/platform/runtime.ts +121 -0
  155. package/src/platform/test.ts +45 -0
  156. package/src/platform/worker-handler-cleanup.fixture.ts +12 -0
  157. package/src/platform/worker-onmessage-startup.fixture.ts +10 -0
  158. package/src/platform/worker-startup.fixture.ts +9 -0
  159. package/src/platform/worker.node-test.ts +153 -0
  160. package/src/platform/worker.ts +425 -0
  161. package/src/plugins/core-slot.ts +579 -0
  162. package/src/plugins/registry.ts +402 -0
  163. package/src/plugins/types.ts +46 -0
  164. package/src/post/effects.ts +948 -0
  165. package/src/post/filters.ts +512 -0
  166. package/src/post/matrices.ts +288 -0
  167. package/src/renderables/ASCIIFont.ts +219 -0
  168. package/src/renderables/Box.test.ts +474 -0
  169. package/src/renderables/Box.ts +353 -0
  170. package/src/renderables/Code.test.ts +2388 -0
  171. package/src/renderables/Code.ts +555 -0
  172. package/src/renderables/Diff.regression.test.ts +381 -0
  173. package/src/renderables/Diff.test.ts +3212 -0
  174. package/src/renderables/Diff.ts +1278 -0
  175. package/src/renderables/EditBufferRenderable.test.ts +288 -0
  176. package/src/renderables/EditBufferRenderable.ts +1152 -0
  177. package/src/renderables/FrameBuffer.ts +47 -0
  178. package/src/renderables/Input.test.ts +1310 -0
  179. package/src/renderables/Input.ts +273 -0
  180. package/src/renderables/LineNumberRenderable.ts +725 -0
  181. package/src/renderables/Markdown.ts +2144 -0
  182. package/src/renderables/ScrollBar.ts +423 -0
  183. package/src/renderables/ScrollBox.ts +903 -0
  184. package/src/renderables/Select.test.ts +1134 -0
  185. package/src/renderables/Select.ts +535 -0
  186. package/src/renderables/Slider.test.ts +456 -0
  187. package/src/renderables/Slider.ts +342 -0
  188. package/src/renderables/TabSelect.test.ts +238 -0
  189. package/src/renderables/TabSelect.ts +447 -0
  190. package/src/renderables/Text.selection-buffer.test.ts +118 -0
  191. package/src/renderables/Text.test.ts +2662 -0
  192. package/src/renderables/Text.ts +147 -0
  193. package/src/renderables/TextBufferRenderable.ts +504 -0
  194. package/src/renderables/TextNode.test.ts +1137 -0
  195. package/src/renderables/TextNode.ts +379 -0
  196. package/src/renderables/TextTable.test.ts +1411 -0
  197. package/src/renderables/TextTable.ts +1434 -0
  198. package/src/renderables/Textarea.ts +425 -0
  199. package/src/renderables/TimeToFirstDraw.ts +89 -0
  200. package/src/renderables/__snapshots__/Code.test.ts.snap +13 -0
  201. package/src/renderables/__snapshots__/Diff.test.ts.snap +785 -0
  202. package/src/renderables/__snapshots__/Text.test.ts.snap +421 -0
  203. package/src/renderables/__snapshots__/TextTable.test.ts.snap +215 -0
  204. package/src/renderables/__tests__/Code.test.ts +55 -0
  205. package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +144 -0
  206. package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +827 -0
  207. package/src/renderables/__tests__/LineNumberRenderable.test.ts +1906 -0
  208. package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +85 -0
  209. package/src/renderables/__tests__/Markdown.code-colors.test.ts +288 -0
  210. package/src/renderables/__tests__/Markdown.test.ts +4175 -0
  211. package/src/renderables/__tests__/MultiRenderable.selection.test.ts +87 -0
  212. package/src/renderables/__tests__/Textarea.buffer.test.ts +683 -0
  213. package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +675 -0
  214. package/src/renderables/__tests__/Textarea.editing.test.ts +2120 -0
  215. package/src/renderables/__tests__/Textarea.error-handling.test.ts +35 -0
  216. package/src/renderables/__tests__/Textarea.events.test.ts +738 -0
  217. package/src/renderables/__tests__/Textarea.highlights.test.ts +590 -0
  218. package/src/renderables/__tests__/Textarea.keybinding.test.ts +3275 -0
  219. package/src/renderables/__tests__/Textarea.paste.test.ts +357 -0
  220. package/src/renderables/__tests__/Textarea.rendering.test.ts +1927 -0
  221. package/src/renderables/__tests__/Textarea.scroll.test.ts +733 -0
  222. package/src/renderables/__tests__/Textarea.selection.test.ts +1589 -0
  223. package/src/renderables/__tests__/Textarea.stress.test.ts +670 -0
  224. package/src/renderables/__tests__/Textarea.undo-redo.test.ts +383 -0
  225. package/src/renderables/__tests__/Textarea.visual-lines.test.ts +310 -0
  226. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +221 -0
  227. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +89 -0
  228. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +457 -0
  229. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +158 -0
  230. package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +387 -0
  231. package/src/renderables/__tests__/markdown-parser.test.ts +241 -0
  232. package/src/renderables/__tests__/renderable-test-utils.ts +60 -0
  233. package/src/renderables/composition/README.md +8 -0
  234. package/src/renderables/composition/VRenderable.ts +32 -0
  235. package/src/renderables/composition/constructs.ts +127 -0
  236. package/src/renderables/composition/vnode.ts +277 -0
  237. package/src/renderables/index.ts +23 -0
  238. package/src/renderables/markdown-parser.ts +79 -0
  239. package/src/renderer-theme-mode.ts +199 -0
  240. package/src/renderer.ts +4975 -0
  241. package/src/runtime-plugin-support-configure.ts +63 -0
  242. package/src/runtime-plugin-support.ts +13 -0
  243. package/src/runtime-plugin.ts +626 -0
  244. package/src/specs/terminal-startup.md +64 -0
  245. package/src/syntax-style.test.ts +841 -0
  246. package/src/syntax-style.ts +274 -0
  247. package/src/testing/README.md +219 -0
  248. package/src/testing/bun-test-node.ts +880 -0
  249. package/src/testing/capture-spans.test.ts +216 -0
  250. package/src/testing/integration.test.ts +276 -0
  251. package/src/testing/manual-clock.ts +117 -0
  252. package/src/testing/mock-keys.test.ts +1378 -0
  253. package/src/testing/mock-keys.ts +457 -0
  254. package/src/testing/mock-mouse.test.ts +218 -0
  255. package/src/testing/mock-mouse.ts +247 -0
  256. package/src/testing/mock-tree-sitter-client.ts +81 -0
  257. package/src/testing/spy.ts +13 -0
  258. package/src/testing/terminal-capabilities.ts +47 -0
  259. package/src/testing/test-recorder.test.ts +410 -0
  260. package/src/testing/test-recorder.ts +133 -0
  261. package/src/testing/test-renderer.ts +375 -0
  262. package/src/testing/test-renderer.wait.test.ts +278 -0
  263. package/src/testing/test-streams.ts +31 -0
  264. package/src/testing.ts +9 -0
  265. package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +481 -0
  266. package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +19 -0
  267. package/src/tests/__snapshots__/scrollbox.test.ts.snap +29 -0
  268. package/src/tests/absolute-positioning.snapshot.test.ts +638 -0
  269. package/src/tests/allocator-stats.test.ts +53 -0
  270. package/src/tests/audio.test.ts +303 -0
  271. package/src/tests/destroy-during-render.test.ts +200 -0
  272. package/src/tests/destroy-on-exit.fixture.ts +36 -0
  273. package/src/tests/destroy-on-exit.test.ts +62 -0
  274. package/src/tests/ffi-borrowed-pointer-callsites.test.ts +153 -0
  275. package/src/tests/hover-cursor.test.ts +98 -0
  276. package/src/tests/native-backed-measurement-lifecycle.test.ts +133 -0
  277. package/src/tests/native-backed-measurement-parity.test.ts +322 -0
  278. package/src/tests/native-span-feed-async.test.ts +173 -0
  279. package/src/tests/native-span-feed-close.test.ts +120 -0
  280. package/src/tests/native-span-feed-coverage.test.ts +227 -0
  281. package/src/tests/native-span-feed-edge-cases.test.ts +384 -0
  282. package/src/tests/native-span-feed-use-after-free.test.ts +45 -0
  283. package/src/tests/opacity.test.ts +123 -0
  284. package/src/tests/renderable.snapshot.test.ts +524 -0
  285. package/src/tests/renderable.test.ts +1851 -0
  286. package/src/tests/renderer.clock.test.ts +270 -0
  287. package/src/tests/renderer.console-startup.test.ts +2151 -0
  288. package/src/tests/renderer.control.test.ts +458 -0
  289. package/src/tests/renderer.core-slot-binding.test.ts +962 -0
  290. package/src/tests/renderer.cursor.test.ts +26 -0
  291. package/src/tests/renderer.custom-stdout.test.ts +1112 -0
  292. package/src/tests/renderer.destroy-during-render.test.ts +147 -0
  293. package/src/tests/renderer.focus-restore.test.ts +257 -0
  294. package/src/tests/renderer.focus.test.ts +329 -0
  295. package/src/tests/renderer.idle.test.ts +219 -0
  296. package/src/tests/renderer.input.test.ts +2615 -0
  297. package/src/tests/renderer.kitty-flags.test.ts +195 -0
  298. package/src/tests/renderer.lifecycle.test.ts +34 -0
  299. package/src/tests/renderer.mouse.test.ts +1315 -0
  300. package/src/tests/renderer.notifications.test.ts +27 -0
  301. package/src/tests/renderer.palette.test.ts +1199 -0
  302. package/src/tests/renderer.remote.test.ts +247 -0
  303. package/src/tests/renderer.render-stats.test.ts +146 -0
  304. package/src/tests/renderer.scrollback-surface.test.ts +474 -0
  305. package/src/tests/renderer.selection.test.ts +138 -0
  306. package/src/tests/renderer.slot-registry.test.ts +684 -0
  307. package/src/tests/renderer.tracker.test.ts +202 -0
  308. package/src/tests/renderer.useMouse.test.ts +83 -0
  309. package/src/tests/runtime-plugin-node-modules-cycle.fixture.ts +76 -0
  310. package/src/tests/runtime-plugin-node-modules-import-like-string.fixture.ts +48 -0
  311. package/src/tests/runtime-plugin-node-modules-mjs.fixture.ts +43 -0
  312. package/src/tests/runtime-plugin-node-modules-no-bare-rewrite.fixture.ts +67 -0
  313. package/src/tests/runtime-plugin-node-modules-package-type-cache.fixture.ts +72 -0
  314. package/src/tests/runtime-plugin-node-modules-runtime-specifier.fixture.ts +44 -0
  315. package/src/tests/runtime-plugin-node-modules-scoped-package-bare-rewrite.fixture.ts +85 -0
  316. package/src/tests/runtime-plugin-path-alias.fixture.ts +43 -0
  317. package/src/tests/runtime-plugin-resolve-roots.fixture.ts +65 -0
  318. package/src/tests/runtime-plugin-support-configure.fixture.ts +31 -0
  319. package/src/tests/runtime-plugin-support-late-addition.fixture.ts +17 -0
  320. package/src/tests/runtime-plugin-support.fixture.ts +11 -0
  321. package/src/tests/runtime-plugin-support.test.ts +54 -0
  322. package/src/tests/runtime-plugin-windows-file-url.fixture.ts +30 -0
  323. package/src/tests/runtime-plugin.fixture.ts +40 -0
  324. package/src/tests/runtime-plugin.test.ts +369 -0
  325. package/src/tests/scrollbox-culling-bug.test.ts +114 -0
  326. package/src/tests/scrollbox-hitgrid-resize.test.ts +136 -0
  327. package/src/tests/scrollbox-hitgrid.test.ts +909 -0
  328. package/src/tests/scrollbox.test.ts +1895 -0
  329. package/src/tests/wrap-resize-perf.test.ts +276 -0
  330. package/src/tests/yoga-api-coverage.test.ts +409 -0
  331. package/src/tests/yoga-callback-stress.test.ts +122 -0
  332. package/src/tests/yoga-ffi.test.ts +117 -0
  333. package/src/tests/yoga-setters.test.ts +921 -0
  334. package/src/tests/yoga-upstream/YGAlignBaselineTest.test.ts +124 -0
  335. package/src/tests/yoga-upstream/YGComputedBorderTest.test.ts +27 -0
  336. package/src/tests/yoga-upstream/YGComputedMarginTest.test.ts +27 -0
  337. package/src/tests/yoga-upstream/YGComputedPaddingTest.test.ts +27 -0
  338. package/src/tests/yoga-upstream/YGDirtiedTest.test.ts +157 -0
  339. package/src/tests/yoga-upstream/YGErrataTest.test.ts +40 -0
  340. package/src/tests/yoga-upstream/YGFlexBasisAuto.test.ts +23 -0
  341. package/src/tests/yoga-upstream/YGHasNewLayout.test.ts +81 -0
  342. package/src/tests/yoga-upstream/YGMeasureCacheTest.test.ts +31 -0
  343. package/src/tests/yoga-upstream/YGMeasureTest.test.ts +70 -0
  344. package/src/tests/yoga-upstream/generated/YGAbsolutePositionTest.test.ts +1864 -0
  345. package/src/tests/yoga-upstream/generated/YGAlignContentTest.test.ts +5829 -0
  346. package/src/tests/yoga-upstream/generated/YGAlignItemsTest.test.ts +2444 -0
  347. package/src/tests/yoga-upstream/generated/YGAlignSelfTest.test.ts +293 -0
  348. package/src/tests/yoga-upstream/generated/YGAndroidNewsFeed.test.ts +315 -0
  349. package/src/tests/yoga-upstream/generated/YGAspectRatioTest.test.ts +231 -0
  350. package/src/tests/yoga-upstream/generated/YGAutoTest.test.ts +347 -0
  351. package/src/tests/yoga-upstream/generated/YGBorderTest.test.ts +256 -0
  352. package/src/tests/yoga-upstream/generated/YGBoxSizingTest.test.ts +2747 -0
  353. package/src/tests/yoga-upstream/generated/YGDimensionTest.test.ts +129 -0
  354. package/src/tests/yoga-upstream/generated/YGDisplayContentsTest.test.ts +109 -0
  355. package/src/tests/yoga-upstream/generated/YGDisplayTest.test.ts +1097 -0
  356. package/src/tests/yoga-upstream/generated/YGFlexDirectionTest.test.ts +4562 -0
  357. package/src/tests/yoga-upstream/generated/YGFlexTest.test.ts +677 -0
  358. package/src/tests/yoga-upstream/generated/YGFlexWrapTest.test.ts +2071 -0
  359. package/src/tests/yoga-upstream/generated/YGGapTest.test.ts +3303 -0
  360. package/src/tests/yoga-upstream/generated/YGIntrinsicSizeTest.test.ts +582 -0
  361. package/src/tests/yoga-upstream/generated/YGJustifyContentTest.test.ts +2150 -0
  362. package/src/tests/yoga-upstream/generated/YGMarginTest.test.ts +1963 -0
  363. package/src/tests/yoga-upstream/generated/YGMinMaxDimensionTest.test.ts +1446 -0
  364. package/src/tests/yoga-upstream/generated/YGPaddingTest.test.ts +354 -0
  365. package/src/tests/yoga-upstream/generated/YGPercentageTest.test.ts +1760 -0
  366. package/src/tests/yoga-upstream/generated/YGRoundingTest.test.ts +1161 -0
  367. package/src/tests/yoga-upstream/generated/YGSizeOverflowTest.test.ts +207 -0
  368. package/src/tests/yoga-upstream/generated/YGStaticPositionTest.test.ts +6338 -0
  369. package/src/tests/yoga-upstream/tools/MeasureCounter.ts +54 -0
  370. package/src/tests/yoga-upstream/tools/utils.ts +81 -0
  371. package/src/text-buffer-view.test.ts +722 -0
  372. package/src/text-buffer-view.ts +193 -0
  373. package/src/text-buffer.test.ts +512 -0
  374. package/src/text-buffer.ts +250 -0
  375. package/src/types.ts +199 -0
  376. package/src/utils.ts +88 -0
  377. package/src/yoga.ts +1146 -0
  378. package/src/zig/ansi.zig +502 -0
  379. package/src/zig/audio.zig +1206 -0
  380. package/src/zig/bench/README.md +50 -0
  381. package/src/zig/bench/buffer-color-blending_bench.zig +350 -0
  382. package/src/zig/bench/buffer-draw-box_bench.zig +786 -0
  383. package/src/zig/bench/buffer-draw-text-buffer_bench.zig +894 -0
  384. package/src/zig/bench/edit-buffer_bench.zig +476 -0
  385. package/src/zig/bench/editor-view_bench.zig +169 -0
  386. package/src/zig/bench/native-span-feed_bench.zig +107 -0
  387. package/src/zig/bench/rope-markers_bench.zig +713 -0
  388. package/src/zig/bench/rope_bench.zig +514 -0
  389. package/src/zig/bench/styled-text_bench.zig +476 -0
  390. package/src/zig/bench/text-buffer-coords_bench.zig +362 -0
  391. package/src/zig/bench/text-buffer-view_bench.zig +459 -0
  392. package/src/zig/bench/text-chunk-graphemes_bench.zig +272 -0
  393. package/src/zig/bench/utf8_bench.zig +799 -0
  394. package/src/zig/bench-utils.zig +431 -0
  395. package/src/zig/bench.zig +223 -0
  396. package/src/zig/buffer-methods.zig +215 -0
  397. package/src/zig/buffer.zig +2515 -0
  398. package/src/zig/build.zig +700 -0
  399. package/src/zig/build.zig.zon +19 -0
  400. package/src/zig/edit-buffer.zig +900 -0
  401. package/src/zig/editor-view.zig +812 -0
  402. package/src/zig/event-bus.zig +28 -0
  403. package/src/zig/event-emitter.zig +65 -0
  404. package/src/zig/file-logger.zig +92 -0
  405. package/src/zig/grapheme.zig +838 -0
  406. package/src/zig/handles.zig +267 -0
  407. package/src/zig/lib.zig +2813 -0
  408. package/src/zig/link.zig +349 -0
  409. package/src/zig/logger.zig +43 -0
  410. package/src/zig/mem-registry.zig +126 -0
  411. package/src/zig/miniaudio_shim.c +21 -0
  412. package/src/zig/native-renderable.zig +112 -0
  413. package/src/zig/native-span-feed-bench-lib.zig +7 -0
  414. package/src/zig/native-span-feed.zig +747 -0
  415. package/src/zig/renderer-output.zig +677 -0
  416. package/src/zig/renderer.zig +2106 -0
  417. package/src/zig/rope.zig +1220 -0
  418. package/src/zig/split-scrollback.zig +88 -0
  419. package/src/zig/syntax-style.zig +174 -0
  420. package/src/zig/terminal.zig +1652 -0
  421. package/src/zig/test.zig +82 -0
  422. package/src/zig/tests/README.md +18 -0
  423. package/src/zig/tests/ansi_test.zig +18 -0
  424. package/src/zig/tests/audio_test.zig +439 -0
  425. package/src/zig/tests/buffer-methods_test.zig +1111 -0
  426. package/src/zig/tests/buffer_test.zig +2926 -0
  427. package/src/zig/tests/edit-buffer-history_test.zig +299 -0
  428. package/src/zig/tests/edit-buffer_test.zig +1688 -0
  429. package/src/zig/tests/editor-view_test.zig +3454 -0
  430. package/src/zig/tests/event-emitter_test.zig +249 -0
  431. package/src/zig/tests/grapheme_test.zig +1329 -0
  432. package/src/zig/tests/handles_test.zig +123 -0
  433. package/src/zig/tests/link_test.zig +220 -0
  434. package/src/zig/tests/mem-registry_test.zig +473 -0
  435. package/src/zig/tests/memory_leak_regression_test.zig +234 -0
  436. package/src/zig/tests/native-renderable_test.zig +16 -0
  437. package/src/zig/tests/native-span-feed_test.zig +1266 -0
  438. package/src/zig/tests/renderer_test.zig +2604 -0
  439. package/src/zig/tests/rope-nested_test.zig +712 -0
  440. package/src/zig/tests/rope_fuzz_test.zig +238 -0
  441. package/src/zig/tests/rope_test.zig +2362 -0
  442. package/src/zig/tests/segment-merge.test.zig +148 -0
  443. package/src/zig/tests/split-scrollback_test.zig +70 -0
  444. package/src/zig/tests/syntax-style_test.zig +558 -0
  445. package/src/zig/tests/terminal_test.zig +1489 -0
  446. package/src/zig/tests/test-renderer.zig +126 -0
  447. package/src/zig/tests/text-buffer-drawing_test.zig +3291 -0
  448. package/src/zig/tests/text-buffer-highlights_test.zig +706 -0
  449. package/src/zig/tests/text-buffer-iterators_test.zig +776 -0
  450. package/src/zig/tests/text-buffer-segment_test.zig +320 -0
  451. package/src/zig/tests/text-buffer-selection_test.zig +1056 -0
  452. package/src/zig/tests/text-buffer-selection_viewport_test.zig +358 -0
  453. package/src/zig/tests/text-buffer-view_test.zig +3672 -0
  454. package/src/zig/tests/text-buffer_test.zig +2191 -0
  455. package/src/zig/tests/unicode-width-map.zon +3909 -0
  456. package/src/zig/tests/utf8_no_zwj_test.zig +260 -0
  457. package/src/zig/tests/utf8_test.zig +4060 -0
  458. package/src/zig/tests/utf8_wcwidth_cursor_test.zig +267 -0
  459. package/src/zig/tests/utf8_wcwidth_test.zig +357 -0
  460. package/src/zig/tests/word-wrap-editing_test.zig +498 -0
  461. package/src/zig/tests/wrap-cache-perf_test.zig +113 -0
  462. package/src/zig/tests/yoga_test.zig +65 -0
  463. package/src/zig/text-buffer-iterators.zig +496 -0
  464. package/src/zig/text-buffer-segment.zig +403 -0
  465. package/src/zig/text-buffer-view.zig +1428 -0
  466. package/src/zig/text-buffer.zig +1272 -0
  467. package/src/zig/utf8.zig +1954 -0
  468. package/src/zig/utils.zig +12 -0
  469. package/src/zig/uucode-stub.zig +76 -0
  470. package/src/zig/vendor/miniaudio/miniaudio.h +93468 -0
  471. package/src/zig/yoga.zig +658 -0
  472. package/src/zig-structs.ts +381 -0
  473. package/src/zig.ts +5260 -0
  474. package/tsconfig.build.json +17 -0
  475. package/tsconfig.json +27 -0
  476. package/tsconfig.node-test.json +138 -0
@@ -0,0 +1,410 @@
1
+ import { describe, test, expect, beforeEach, afterEach } from "bun:test"
2
+ import { createTestRenderer, type TestRenderer } from "./test-renderer.js"
3
+ import { TestRecorder } from "./test-recorder.js"
4
+ import { TextRenderable } from "../renderables/Text.js"
5
+
6
+ async function waitForScheduledRender(): Promise<void> {
7
+ await new Promise<void>((resolve) => process.nextTick(resolve))
8
+ await Promise.resolve()
9
+ }
10
+
11
+ describe("TestRecorder", () => {
12
+ let renderer: TestRenderer
13
+ let recorder: TestRecorder
14
+ let renderOnce: () => Promise<void>
15
+
16
+ beforeEach(async () => {
17
+ const setup = await createTestRenderer({ width: 80, height: 24 })
18
+ renderer = setup.renderer
19
+ renderOnce = setup.renderOnce
20
+ recorder = new TestRecorder(renderer)
21
+ })
22
+
23
+ afterEach(() => {
24
+ recorder.stop()
25
+ renderer.destroy()
26
+ })
27
+
28
+ test("should initialize with empty frames", () => {
29
+ expect(recorder.recordedFrames).toEqual([])
30
+ expect(recorder.isRecording).toBe(false)
31
+ })
32
+
33
+ test("should start recording", () => {
34
+ recorder.rec()
35
+ expect(recorder.isRecording).toBe(true)
36
+ })
37
+
38
+ test("should stop recording", () => {
39
+ recorder.rec()
40
+ expect(recorder.isRecording).toBe(true)
41
+ recorder.stop()
42
+ expect(recorder.isRecording).toBe(false)
43
+ })
44
+
45
+ test("should record frames during rendering", async () => {
46
+ recorder.rec()
47
+
48
+ const text = new TextRenderable(renderer, { content: "Hello World" })
49
+ renderer.root.add(text)
50
+ await waitForScheduledRender()
51
+
52
+ expect(recorder.recordedFrames.length).toBe(1)
53
+
54
+ await renderOnce()
55
+ expect(recorder.recordedFrames.length).toBe(2)
56
+
57
+ recorder.stop()
58
+ })
59
+
60
+ test("should capture frame content correctly", async () => {
61
+ recorder.rec()
62
+
63
+ const text = new TextRenderable(renderer, { content: "Test Content" })
64
+ renderer.root.add(text)
65
+ await waitForScheduledRender()
66
+
67
+ const frames = recorder.recordedFrames
68
+ expect(frames.length).toBe(1)
69
+ expect(frames[0].frame).toContain("Test Content")
70
+
71
+ recorder.stop()
72
+ })
73
+
74
+ test("should include frame metadata", async () => {
75
+ recorder.rec()
76
+
77
+ const text = new TextRenderable(renderer, { content: "Frame Metadata" })
78
+ renderer.root.add(text)
79
+ await waitForScheduledRender()
80
+
81
+ const frames = recorder.recordedFrames
82
+ expect(frames.length).toBe(1)
83
+ expect(frames[0].timestamp).toBeGreaterThanOrEqual(0)
84
+ expect(frames[0].frameNumber).toBe(0)
85
+
86
+ recorder.stop()
87
+ })
88
+
89
+ test("should increment frame numbers", async () => {
90
+ recorder.rec()
91
+
92
+ const text = new TextRenderable(renderer, { content: "Multiple Frames" })
93
+ renderer.root.add(text)
94
+ await waitForScheduledRender()
95
+
96
+ await renderOnce()
97
+ await renderOnce()
98
+
99
+ const frames = recorder.recordedFrames
100
+ expect(frames.length).toBe(3)
101
+ expect(frames[0].frameNumber).toBe(0)
102
+ expect(frames[1].frameNumber).toBe(1)
103
+ expect(frames[2].frameNumber).toBe(2)
104
+
105
+ recorder.stop()
106
+ })
107
+
108
+ test("should capture changing content across frames", async () => {
109
+ recorder.rec()
110
+
111
+ const text = new TextRenderable(renderer, { content: "Initial" })
112
+ renderer.root.add(text)
113
+ await renderer.idle()
114
+
115
+ text.content = "Changed"
116
+ await renderer.idle()
117
+ recorder.stop()
118
+
119
+ const frames = recorder.recordedFrames
120
+ const initialFrameIndex = frames.findIndex(({ frame }) => frame.includes("Initial"))
121
+ const changedFrameIndex = frames.findIndex(
122
+ ({ frame }, index) => index > initialFrameIndex && frame.includes("Changed"),
123
+ )
124
+
125
+ expect(initialFrameIndex).toBeGreaterThanOrEqual(0)
126
+ expect(changedFrameIndex).toBeGreaterThan(initialFrameIndex)
127
+
128
+ const frame1 = frames[initialFrameIndex].frame
129
+ const frame2 = frames[changedFrameIndex].frame
130
+
131
+ expect(frame1).toContain("Initial")
132
+ expect(frame2).toContain("Changed")
133
+ expect(frame1).not.toEqual(frame2)
134
+ })
135
+
136
+ test("should not record when not started", async () => {
137
+ const text = new TextRenderable(renderer, { content: "Not Recording" })
138
+ renderer.root.add(text)
139
+ await waitForScheduledRender()
140
+
141
+ expect(recorder.recordedFrames.length).toBe(0)
142
+ })
143
+
144
+ test("should not record after stopped", async () => {
145
+ recorder.rec()
146
+
147
+ const text = new TextRenderable(renderer, { content: "Stopped" })
148
+ renderer.root.add(text)
149
+ await waitForScheduledRender()
150
+
151
+ expect(recorder.recordedFrames.length).toBe(1)
152
+
153
+ recorder.stop()
154
+ await renderOnce()
155
+ expect(recorder.recordedFrames.length).toBe(1)
156
+ })
157
+
158
+ test("should clear recorded frames", async () => {
159
+ recorder.rec()
160
+
161
+ const text = new TextRenderable(renderer, { content: "Clear Test" })
162
+ renderer.root.add(text)
163
+ await waitForScheduledRender()
164
+
165
+ await renderOnce()
166
+
167
+ expect(recorder.recordedFrames.length).toBe(2)
168
+ recorder.clear()
169
+ expect(recorder.recordedFrames.length).toBe(0)
170
+
171
+ recorder.stop()
172
+ })
173
+
174
+ test("should handle multiple rec/stop cycles", async () => {
175
+ const text = new TextRenderable(renderer, { content: "Cycle Test" })
176
+
177
+ recorder.rec()
178
+ renderer.root.add(text)
179
+ await waitForScheduledRender()
180
+ recorder.stop()
181
+ expect(recorder.recordedFrames.length).toBe(1)
182
+
183
+ recorder.clear()
184
+ recorder.rec()
185
+ await renderOnce()
186
+ await renderOnce()
187
+ recorder.stop()
188
+ expect(recorder.recordedFrames.length).toBe(2)
189
+ })
190
+
191
+ test("should not duplicate frames when rec is called multiple times", async () => {
192
+ recorder.rec()
193
+ recorder.rec()
194
+
195
+ const text = new TextRenderable(renderer, { content: "Duplicate Test" })
196
+ renderer.root.add(text)
197
+ await waitForScheduledRender()
198
+
199
+ recorder.stop()
200
+
201
+ expect(recorder.recordedFrames.length).toBe(1)
202
+ })
203
+
204
+ test("should capture timestamps in increasing order", async () => {
205
+ let time = 0
206
+ recorder = new TestRecorder(renderer, { now: () => time })
207
+ recorder.rec()
208
+
209
+ await renderOnce()
210
+ time += 10
211
+ await renderOnce()
212
+
213
+ const frames = recorder.recordedFrames
214
+ expect(frames.length).toBe(2)
215
+ expect(frames[1].timestamp).toBeGreaterThan(frames[0].timestamp)
216
+ expect(frames[1].timestamp - frames[0].timestamp).toBe(10)
217
+
218
+ recorder.stop()
219
+ })
220
+
221
+ test("should return a copy of recorded frames", async () => {
222
+ recorder.rec()
223
+
224
+ const text = new TextRenderable(renderer, { content: "Copy Test" })
225
+ renderer.root.add(text)
226
+ await waitForScheduledRender()
227
+
228
+ const frames1 = recorder.recordedFrames
229
+ const frames2 = recorder.recordedFrames
230
+
231
+ expect(frames1).toEqual(frames2)
232
+ expect(frames1).not.toBe(frames2)
233
+
234
+ recorder.stop()
235
+ })
236
+
237
+ test("should handle empty renders", async () => {
238
+ recorder.rec()
239
+ await renderOnce()
240
+
241
+ expect(recorder.recordedFrames.length).toBe(1)
242
+ expect(recorder.recordedFrames[0].frame).toBeDefined()
243
+
244
+ recorder.stop()
245
+ })
246
+
247
+ test("should capture complex content", async () => {
248
+ recorder.rec()
249
+
250
+ const text1 = new TextRenderable(renderer, { content: "Line 1" })
251
+ const text2 = new TextRenderable(renderer, { content: "Line 2" })
252
+ renderer.root.add(text1)
253
+ renderer.root.add(text2)
254
+ await waitForScheduledRender()
255
+
256
+ const frame = recorder.recordedFrames[0].frame
257
+ expect(frame).toContain("Line 1")
258
+ expect(frame).toContain("Line 2")
259
+
260
+ recorder.stop()
261
+ })
262
+
263
+ test("should handle rapid render calls", async () => {
264
+ recorder.rec()
265
+
266
+ const text = new TextRenderable(renderer, { content: "Rapid Test" })
267
+ renderer.root.add(text)
268
+ await waitForScheduledRender()
269
+
270
+ for (let i = 0; i < 4; i++) {
271
+ await renderOnce()
272
+ }
273
+
274
+ expect(recorder.recordedFrames.length).toBe(5)
275
+
276
+ recorder.stop()
277
+ })
278
+
279
+ test("should optionally record fg buffer", async () => {
280
+ const recorderWithFg = new TestRecorder(renderer, { recordBuffers: { fg: true } })
281
+ recorderWithFg.rec()
282
+
283
+ const text = new TextRenderable(renderer, { content: "Buffer Test" })
284
+ renderer.root.add(text)
285
+ await waitForScheduledRender()
286
+
287
+ const frames = recorderWithFg.recordedFrames
288
+ expect(frames.length).toBe(1)
289
+ expect(frames[0].buffers).toBeDefined()
290
+ expect(frames[0].buffers?.fg).toBeInstanceOf(Uint16Array)
291
+ expect(frames[0].buffers?.bg).toBeUndefined()
292
+ expect(frames[0].buffers?.attributes).toBeUndefined()
293
+
294
+ recorderWithFg.stop()
295
+ })
296
+
297
+ test("should optionally record bg buffer", async () => {
298
+ const recorderWithBg = new TestRecorder(renderer, { recordBuffers: { bg: true } })
299
+ recorderWithBg.rec()
300
+
301
+ const text = new TextRenderable(renderer, { content: "Buffer Test" })
302
+ renderer.root.add(text)
303
+ await waitForScheduledRender()
304
+
305
+ const frames = recorderWithBg.recordedFrames
306
+ expect(frames.length).toBe(1)
307
+ expect(frames[0].buffers).toBeDefined()
308
+ expect(frames[0].buffers?.bg).toBeInstanceOf(Uint16Array)
309
+ expect(frames[0].buffers?.fg).toBeUndefined()
310
+ expect(frames[0].buffers?.attributes).toBeUndefined()
311
+
312
+ recorderWithBg.stop()
313
+ })
314
+
315
+ test("should optionally record attributes buffer", async () => {
316
+ const recorderWithAttrs = new TestRecorder(renderer, { recordBuffers: { attributes: true } })
317
+ recorderWithAttrs.rec()
318
+
319
+ const text = new TextRenderable(renderer, { content: "Buffer Test" })
320
+ renderer.root.add(text)
321
+ await waitForScheduledRender()
322
+
323
+ const frames = recorderWithAttrs.recordedFrames
324
+ expect(frames.length).toBe(1)
325
+ expect(frames[0].buffers).toBeDefined()
326
+ expect(frames[0].buffers?.attributes).toBeInstanceOf(Uint8Array)
327
+ expect(frames[0].buffers?.fg).toBeUndefined()
328
+ expect(frames[0].buffers?.bg).toBeUndefined()
329
+
330
+ recorderWithAttrs.stop()
331
+ })
332
+
333
+ test("should record multiple buffers when requested", async () => {
334
+ const recorderWithAll = new TestRecorder(renderer, {
335
+ recordBuffers: { fg: true, bg: true, attributes: true },
336
+ })
337
+ recorderWithAll.rec()
338
+
339
+ const text = new TextRenderable(renderer, { content: "Buffer Test" })
340
+ renderer.root.add(text)
341
+ await waitForScheduledRender()
342
+
343
+ const frames = recorderWithAll.recordedFrames
344
+ expect(frames.length).toBe(1)
345
+ expect(frames[0].buffers).toBeDefined()
346
+ expect(frames[0].buffers?.fg).toBeInstanceOf(Uint16Array)
347
+ expect(frames[0].buffers?.bg).toBeInstanceOf(Uint16Array)
348
+ expect(frames[0].buffers?.attributes).toBeInstanceOf(Uint8Array)
349
+
350
+ recorderWithAll.stop()
351
+ })
352
+
353
+ test("should not record buffers when not requested", async () => {
354
+ recorder.rec()
355
+
356
+ const text = new TextRenderable(renderer, { content: "No Buffer Test" })
357
+ renderer.root.add(text)
358
+ await waitForScheduledRender()
359
+
360
+ const frames = recorder.recordedFrames
361
+ expect(frames.length).toBe(1)
362
+ expect(frames[0].buffers).toBeUndefined()
363
+
364
+ recorder.stop()
365
+ })
366
+
367
+ test("should record independent buffer copies", async () => {
368
+ const recorderWithBuffers = new TestRecorder(renderer, { recordBuffers: { fg: true } })
369
+ recorderWithBuffers.rec()
370
+
371
+ const text = new TextRenderable(renderer, { content: "Copy Test" })
372
+ renderer.root.add(text)
373
+ await waitForScheduledRender()
374
+
375
+ await renderOnce()
376
+
377
+ const frames = recorderWithBuffers.recordedFrames
378
+ expect(frames.length).toBe(2)
379
+
380
+ const frame1Fg = frames[0].buffers?.fg
381
+ const frame2Fg = frames[1].buffers?.fg
382
+
383
+ expect(frame1Fg).toBeDefined()
384
+ expect(frame2Fg).toBeDefined()
385
+ expect(frame1Fg).not.toBe(frame2Fg)
386
+
387
+ recorderWithBuffers.stop()
388
+ })
389
+
390
+ test("should have correct buffer sizes", async () => {
391
+ const recorderWithAll = new TestRecorder(renderer, {
392
+ recordBuffers: { fg: true, bg: true, attributes: true },
393
+ })
394
+ recorderWithAll.rec()
395
+
396
+ const text = new TextRenderable(renderer, { content: "Size Test" })
397
+ renderer.root.add(text)
398
+ await waitForScheduledRender()
399
+
400
+ const frames = recorderWithAll.recordedFrames
401
+ expect(frames.length).toBe(1)
402
+
403
+ const expectedSize = renderer.width * renderer.height
404
+ expect(frames[0].buffers?.fg?.length).toBe(expectedSize * 4)
405
+ expect(frames[0].buffers?.bg?.length).toBe(expectedSize * 4)
406
+ expect(frames[0].buffers?.attributes?.length).toBe(expectedSize)
407
+
408
+ recorderWithAll.stop()
409
+ })
410
+ })
@@ -0,0 +1,133 @@
1
+ import type { TestRenderer } from "./test-renderer.js"
2
+ import { CliRenderEvents } from "../renderer.js"
3
+
4
+ export interface RecordBuffersOptions {
5
+ fg?: boolean
6
+ bg?: boolean
7
+ attributes?: boolean
8
+ }
9
+
10
+ export interface RecordedBuffers {
11
+ fg?: Uint16Array
12
+ bg?: Uint16Array
13
+ attributes?: Uint8Array
14
+ }
15
+
16
+ export interface RecordedFrame {
17
+ frame: string
18
+ timestamp: number
19
+ frameNumber: number
20
+ buffers?: RecordedBuffers
21
+ }
22
+
23
+ export interface TestRecorderOptions {
24
+ recordBuffers?: RecordBuffersOptions
25
+ now?: () => number
26
+ }
27
+
28
+ /**
29
+ * TestRecorder records frames from a TestRenderer by listening to rendered frame events.
30
+ * It captures the character frame after each native render pass.
31
+ */
32
+ export class TestRecorder {
33
+ private renderer: TestRenderer
34
+ private frames: RecordedFrame[] = []
35
+ private recording: boolean = false
36
+ private frameNumber: number = 0
37
+ private startTime: number = 0
38
+ private decoder = new TextDecoder()
39
+ private recordBuffers: RecordBuffersOptions
40
+ private now: () => number
41
+ private readonly onFrame = () => {
42
+ if (!this.recording) return
43
+ this.captureFrame()
44
+ }
45
+
46
+ constructor(renderer: TestRenderer, options?: TestRecorderOptions) {
47
+ this.renderer = renderer
48
+ this.recordBuffers = options?.recordBuffers || {}
49
+ this.now = options?.now ?? (() => performance.now())
50
+ }
51
+
52
+ /**
53
+ * Start recording frames.
54
+ */
55
+ public rec(): void {
56
+ if (this.recording) {
57
+ return
58
+ }
59
+
60
+ this.recording = true
61
+ this.frames = []
62
+ this.frameNumber = 0
63
+ this.startTime = this.now()
64
+ this.renderer.on(CliRenderEvents.FRAME, this.onFrame)
65
+ }
66
+
67
+ /**
68
+ * Stop recording frames.
69
+ */
70
+ public stop(): void {
71
+ if (!this.recording) {
72
+ return
73
+ }
74
+
75
+ this.recording = false
76
+ this.renderer.off(CliRenderEvents.FRAME, this.onFrame)
77
+ }
78
+
79
+ /**
80
+ * Get the recorded frames.
81
+ */
82
+ public get recordedFrames(): RecordedFrame[] {
83
+ return [...this.frames]
84
+ }
85
+
86
+ /**
87
+ * Clear all recorded frames.
88
+ */
89
+ public clear(): void {
90
+ this.frames = []
91
+ this.frameNumber = 0
92
+ }
93
+
94
+ /**
95
+ * Check if currently recording.
96
+ */
97
+ public get isRecording(): boolean {
98
+ return this.recording
99
+ }
100
+
101
+ /**
102
+ * Capture the current frame from the renderer's buffer.
103
+ */
104
+ private captureFrame(): void {
105
+ const currentBuffer = this.renderer.currentRenderBuffer
106
+ const frameBytes = currentBuffer.getRealCharBytes(true)
107
+ const frame = this.decoder.decode(frameBytes)
108
+
109
+ const recordedFrame: RecordedFrame = {
110
+ frame,
111
+ timestamp: this.now() - this.startTime,
112
+ frameNumber: this.frameNumber++,
113
+ }
114
+
115
+ // Optionally record buffer data from currentRenderBuffer
116
+ if (this.recordBuffers.fg || this.recordBuffers.bg || this.recordBuffers.attributes) {
117
+ const buffers = currentBuffer.buffers
118
+ recordedFrame.buffers = {}
119
+
120
+ if (this.recordBuffers.fg) {
121
+ recordedFrame.buffers.fg = new Uint16Array(buffers.fg)
122
+ }
123
+ if (this.recordBuffers.bg) {
124
+ recordedFrame.buffers.bg = new Uint16Array(buffers.bg)
125
+ }
126
+ if (this.recordBuffers.attributes) {
127
+ recordedFrame.buffers.attributes = new Uint8Array(buffers.attributes)
128
+ }
129
+ }
130
+
131
+ this.frames.push(recordedFrame)
132
+ }
133
+ }