@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
package/src/audio.ts ADDED
@@ -0,0 +1,479 @@
1
+ import { EventEmitter } from "events"
2
+ import { readFile } from "node:fs/promises"
3
+ import { resolveRenderLib, type AudioEngineHandle, type RenderLib } from "./zig.js"
4
+ import type { AudioStats } from "./zig-structs.js"
5
+
6
+ export interface AudioSetupOptions {
7
+ autoStart?: boolean
8
+ sampleRate?: number
9
+ playbackChannels?: number
10
+ startOptions?: AudioStartOptions
11
+ }
12
+
13
+ export interface AudioStartOptions {
14
+ periodSizeInFrames?: number
15
+ periodSizeInMilliseconds?: number
16
+ periods?: number
17
+ performanceProfile?: number
18
+ shareMode?: number
19
+ noPreSilencedOutputBuffer?: boolean
20
+ noClip?: boolean
21
+ noDisableDenormals?: boolean
22
+ noFixedSizedCallback?: boolean
23
+ wasapiNoAutoConvertSrc?: boolean
24
+ wasapiNoDefaultQualitySrc?: boolean
25
+ alsaNoMMap?: boolean
26
+ alsaNoAutoFormat?: boolean
27
+ alsaNoAutoChannels?: boolean
28
+ alsaNoAutoResample?: boolean
29
+ }
30
+
31
+ export interface AudioPlayOptions {
32
+ volume?: number
33
+ pan?: number
34
+ loop?: boolean
35
+ groupId?: number
36
+ }
37
+
38
+ export type AudioGroup = number
39
+ export type AudioVoice = number
40
+ export type AudioSound = number
41
+
42
+ export interface AudioPlaybackDevice {
43
+ index: number
44
+ name: string
45
+ isDefault: boolean
46
+ }
47
+
48
+ export type AudioAction =
49
+ | "createAudioEngine"
50
+ | "start"
51
+ | "startMixer"
52
+ | "stop"
53
+ | "loadSound"
54
+ | "loadSoundFile"
55
+ | "unloadSound"
56
+ | "group"
57
+ | "play"
58
+ | "stopVoice"
59
+ | "setVoiceGroup"
60
+ | "setGroupVolume"
61
+ | "setMasterVolume"
62
+ | "mixFrames"
63
+ | "enableTap"
64
+ | "readTapFrames"
65
+ | "listPlaybackDevices"
66
+ | "selectPlaybackDevice"
67
+ | "clearPlaybackDeviceSelection"
68
+ | "getStats"
69
+
70
+ export interface AudioErrorContext {
71
+ action: AudioAction
72
+ status?: number
73
+ }
74
+
75
+ export interface AudioEvents {
76
+ error: [error: Error, context: AudioErrorContext]
77
+ started: []
78
+ mixerStarted: []
79
+ stopped: []
80
+ disposed: []
81
+ }
82
+
83
+ function statusToError(action: string, status: number): Error {
84
+ return new Error(`Audio ${action} failed: ${status}`)
85
+ }
86
+
87
+ function toBytes(data: Uint8Array | ArrayBuffer): Uint8Array {
88
+ return data instanceof Uint8Array ? data : new Uint8Array(data)
89
+ }
90
+
91
+ export class Audio extends EventEmitter<AudioEvents> {
92
+ static create(options: AudioSetupOptions = {}): Audio {
93
+ return new Audio(resolveRenderLib(), options)
94
+ }
95
+
96
+ private readonly lib: RenderLib
97
+ private readonly defaultStartOptions: AudioStartOptions | undefined
98
+ private engine: AudioEngineHandle | null = null
99
+ private readonly groups = new Map<string, number>()
100
+ private playbackStarted = false
101
+ private mixerStarted = false
102
+
103
+ private constructor(lib: RenderLib, options: AudioSetupOptions) {
104
+ super()
105
+ this.lib = lib
106
+ this.defaultStartOptions = options.startOptions
107
+ const createOptions =
108
+ options.sampleRate == null && options.playbackChannels == null
109
+ ? undefined
110
+ : {
111
+ sampleRate: options.sampleRate == null ? undefined : Math.max(0, Math.trunc(options.sampleRate)),
112
+ playbackChannels:
113
+ options.playbackChannels == null ? undefined : Math.max(0, Math.trunc(options.playbackChannels)),
114
+ }
115
+ this.engine = this.lib.createAudioEngine(createOptions)
116
+ if (!this.engine) {
117
+ this.emitError("createAudioEngine", undefined, "Audio createAudioEngine returned null")
118
+ return
119
+ }
120
+
121
+ if (options.autoStart ?? false) {
122
+ this.start(this.defaultStartOptions)
123
+ }
124
+ }
125
+
126
+ private emitError(action: AudioAction, status?: number, message?: string, cause?: unknown): void {
127
+ const error = message ? new Error(message) : statusToError(action, status ?? -1)
128
+ if (cause) (error as Error & { cause?: unknown }).cause = cause
129
+ this.emit("error", error, { action, status })
130
+ }
131
+
132
+ start(options?: AudioStartOptions): boolean {
133
+ if (this.playbackStarted) return true
134
+ const engine = this.engine
135
+ if (!engine) {
136
+ this.emitError("start", undefined, "Audio engine unavailable during start")
137
+ return false
138
+ }
139
+ const startOptions = options ?? this.defaultStartOptions
140
+ const status = this.lib.audioStart(engine, startOptions)
141
+ if (status !== 0) {
142
+ this.emitError("start", status)
143
+ return false
144
+ }
145
+ this.playbackStarted = true
146
+ this.mixerStarted = true
147
+ this.emit("started")
148
+ return true
149
+ }
150
+
151
+ startMixer(): boolean {
152
+ if (this.mixerStarted) return true
153
+ const engine = this.engine
154
+ if (!engine) {
155
+ this.emitError("startMixer", undefined, "Audio engine unavailable during startMixer")
156
+ return false
157
+ }
158
+ const status = this.lib.audioStartMixer(engine)
159
+ if (status !== 0) {
160
+ this.emitError("startMixer", status)
161
+ return false
162
+ }
163
+ this.mixerStarted = true
164
+ this.emit("mixerStarted")
165
+ return true
166
+ }
167
+
168
+ stop(): boolean {
169
+ if (!this.mixerStarted) return true
170
+ const engine = this.engine
171
+ if (!engine) {
172
+ this.emitError("stop", undefined, "Audio engine unavailable during stop")
173
+ return false
174
+ }
175
+ const status = this.lib.audioStop(engine)
176
+ if (status !== 0) {
177
+ this.emitError("stop", status)
178
+ return false
179
+ }
180
+ this.playbackStarted = false
181
+ this.mixerStarted = false
182
+ this.emit("stopped")
183
+ return true
184
+ }
185
+
186
+ isStarted(): boolean {
187
+ return this.playbackStarted
188
+ }
189
+
190
+ isMixerStarted(): boolean {
191
+ return this.mixerStarted
192
+ }
193
+
194
+ loadSound(data: Uint8Array | ArrayBuffer): AudioSound | null {
195
+ const engine = this.engine
196
+ if (!engine) {
197
+ this.emitError("loadSound", undefined, "Audio engine unavailable during loadSound")
198
+ return null
199
+ }
200
+ const result = this.lib.audioLoad(engine, toBytes(data))
201
+ if (result.status !== 0 || result.soundId == null) {
202
+ this.emitError("loadSound", result.status)
203
+ return null
204
+ }
205
+ return result.soundId
206
+ }
207
+
208
+ async loadSoundFile(filePath: string): Promise<AudioSound | null> {
209
+ const bytes = await readFile(filePath).catch((err) => {
210
+ this.emitError("loadSoundFile", undefined, `Failed to read file '${filePath}': ${err.message}`, err)
211
+ return null
212
+ })
213
+ if (bytes == null) return null
214
+ return this.loadSound(bytes)
215
+ }
216
+
217
+ unloadSound(sound: AudioSound): boolean {
218
+ const engine = this.engine
219
+ if (!engine) {
220
+ this.emitError("unloadSound", undefined, "Audio engine unavailable during unloadSound")
221
+ return false
222
+ }
223
+
224
+ const status = this.lib.audioUnload(engine, sound)
225
+ if (status !== 0) {
226
+ this.emitError("unloadSound", status)
227
+ return false
228
+ }
229
+ return true
230
+ }
231
+
232
+ group(name: string): AudioGroup | null {
233
+ const existing = this.groups.get(name)
234
+ if (existing != null) {
235
+ return existing
236
+ }
237
+
238
+ const engine = this.engine
239
+ if (!engine) {
240
+ this.emitError("group", undefined, "Audio engine unavailable during group")
241
+ return null
242
+ }
243
+ const result = this.lib.audioCreateGroup(engine, name)
244
+ if (result.status !== 0 || result.groupId == null) {
245
+ this.emitError("group", result.status)
246
+ return null
247
+ }
248
+
249
+ this.groups.set(name, result.groupId)
250
+ return result.groupId
251
+ }
252
+
253
+ play(sound: AudioSound, options?: AudioPlayOptions): AudioVoice | null {
254
+ const rawOptions = options
255
+ ? {
256
+ volume: options.volume,
257
+ pan: options.pan,
258
+ loop: options.loop,
259
+ groupId: options.groupId ?? 0,
260
+ }
261
+ : undefined
262
+
263
+ const engine = this.engine
264
+ if (!engine) {
265
+ this.emitError("play", undefined, "Audio engine unavailable during play")
266
+ return null
267
+ }
268
+ const result = this.lib.audioPlay(engine, sound, rawOptions)
269
+ if (result.status !== 0 || result.voiceId == null) {
270
+ this.emitError("play", result.status)
271
+ return null
272
+ }
273
+
274
+ return result.voiceId
275
+ }
276
+
277
+ stopVoice(voice: AudioVoice): boolean {
278
+ const engine = this.engine
279
+ if (!engine) {
280
+ this.emitError("stopVoice", undefined, "Audio engine unavailable during stopVoice")
281
+ return false
282
+ }
283
+ const status = this.lib.audioStopVoice(engine, voice)
284
+ if (status !== 0) {
285
+ this.emitError("stopVoice", status)
286
+ return false
287
+ }
288
+ return true
289
+ }
290
+
291
+ setVoiceGroup(voice: AudioVoice, group: AudioGroup): boolean {
292
+ const engine = this.engine
293
+ if (!engine) {
294
+ this.emitError("setVoiceGroup", undefined, "Audio engine unavailable during setVoiceGroup")
295
+ return false
296
+ }
297
+ const status = this.lib.audioSetVoiceGroup(engine, voice, group)
298
+ if (status !== 0) {
299
+ this.emitError("setVoiceGroup", status)
300
+ return false
301
+ }
302
+ return true
303
+ }
304
+
305
+ setGroupVolume(group: AudioGroup, volume: number): boolean {
306
+ const engine = this.engine
307
+ if (!engine) {
308
+ this.emitError("setGroupVolume", undefined, "Audio engine unavailable during setGroupVolume")
309
+ return false
310
+ }
311
+ const status = this.lib.audioSetGroupVolume(engine, group, volume)
312
+ if (status !== 0) {
313
+ this.emitError("setGroupVolume", status)
314
+ return false
315
+ }
316
+ return true
317
+ }
318
+
319
+ setMasterVolume(volume: number): boolean {
320
+ const engine = this.engine
321
+ if (!engine) {
322
+ this.emitError("setMasterVolume", undefined, "Audio engine unavailable during setMasterVolume")
323
+ return false
324
+ }
325
+ const status = this.lib.audioSetMasterVolume(engine, volume)
326
+ if (status !== 0) {
327
+ this.emitError("setMasterVolume", status)
328
+ return false
329
+ }
330
+ return true
331
+ }
332
+
333
+ mixFrames(frameCount: number, channels: number = 2): Float32Array | null {
334
+ const engine = this.engine
335
+ if (!engine) {
336
+ this.emitError("mixFrames", undefined, "Audio engine unavailable during mixFrames")
337
+ return null
338
+ }
339
+ const output = new Float32Array(frameCount * channels)
340
+ const status = this.lib.audioMixToBuffer(engine, output, frameCount, channels)
341
+ if (status !== 0) {
342
+ this.emitError("mixFrames", status)
343
+ return null
344
+ }
345
+ return output
346
+ }
347
+
348
+ enableTap(capacityFrames: number = 8192): boolean {
349
+ const engine = this.engine
350
+ if (!engine) {
351
+ this.emitError("enableTap", undefined, "Audio engine unavailable during enableTap")
352
+ return false
353
+ }
354
+ const status = this.lib.audioEnableTap(engine, true, capacityFrames)
355
+ if (status !== 0) {
356
+ this.emitError("enableTap", status)
357
+ return false
358
+ }
359
+ return true
360
+ }
361
+
362
+ disableTap(): boolean {
363
+ const engine = this.engine
364
+ if (!engine) {
365
+ this.emitError("enableTap", undefined, "Audio engine unavailable during disableTap")
366
+ return false
367
+ }
368
+ const status = this.lib.audioEnableTap(engine, false, 0)
369
+ if (status !== 0) {
370
+ this.emitError("enableTap", status)
371
+ return false
372
+ }
373
+ return true
374
+ }
375
+
376
+ readTapFrames(frameCount: number, channels: number = 2): { frames: Float32Array; framesRead: number } | null {
377
+ const engine = this.engine
378
+ if (!engine) {
379
+ this.emitError("readTapFrames", undefined, "Audio engine unavailable during readTapFrames")
380
+ return null
381
+ }
382
+ const output = new Float32Array(frameCount * channels)
383
+ const result = this.lib.audioReadTap(engine, output, frameCount, channels)
384
+ if (result.status !== 0) {
385
+ this.emitError("readTapFrames", result.status)
386
+ return null
387
+ }
388
+ return { frames: output, framesRead: result.framesRead }
389
+ }
390
+
391
+ listPlaybackDevices(): AudioPlaybackDevice[] | null {
392
+ const engine = this.engine
393
+ if (!engine) {
394
+ this.emitError("listPlaybackDevices", undefined, "Audio engine unavailable during listPlaybackDevices")
395
+ return null
396
+ }
397
+
398
+ const refreshStatus = this.lib.audioRefreshPlaybackDevices(engine)
399
+ if (refreshStatus !== 0) {
400
+ this.emitError("listPlaybackDevices", refreshStatus)
401
+ return null
402
+ }
403
+
404
+ const count = this.lib.audioGetPlaybackDeviceCount(engine)
405
+ const devices: AudioPlaybackDevice[] = []
406
+ for (let index = 0; index < count; index += 1) {
407
+ devices.push({
408
+ index,
409
+ name: this.lib.audioGetPlaybackDeviceName(engine, index),
410
+ isDefault: this.lib.audioIsPlaybackDeviceDefault(engine, index),
411
+ })
412
+ }
413
+
414
+ return devices
415
+ }
416
+
417
+ selectPlaybackDevice(index: number): boolean {
418
+ const engine = this.engine
419
+ if (!engine) {
420
+ this.emitError("selectPlaybackDevice", undefined, "Audio engine unavailable during selectPlaybackDevice")
421
+ return false
422
+ }
423
+
424
+ const refreshStatus = this.lib.audioRefreshPlaybackDevices(engine)
425
+ if (refreshStatus !== 0) {
426
+ this.emitError("selectPlaybackDevice", refreshStatus)
427
+ return false
428
+ }
429
+
430
+ const status = this.lib.audioSelectPlaybackDevice(engine, index)
431
+ if (status !== 0) {
432
+ this.emitError("selectPlaybackDevice", status)
433
+ return false
434
+ }
435
+
436
+ return true
437
+ }
438
+
439
+ clearPlaybackDeviceSelection(): void {
440
+ const engine = this.engine
441
+ if (!engine) {
442
+ this.emitError(
443
+ "clearPlaybackDeviceSelection",
444
+ undefined,
445
+ "Audio engine unavailable during clearPlaybackDeviceSelection",
446
+ )
447
+ return
448
+ }
449
+ this.lib.audioClearPlaybackDeviceSelection(engine)
450
+ }
451
+
452
+ getStats(): AudioStats | null {
453
+ const engine = this.engine
454
+ if (!engine) {
455
+ this.emitError("getStats", undefined, "Audio engine unavailable during getStats")
456
+ return null
457
+ }
458
+ const stats = this.lib.audioGetStats(engine)
459
+ if (stats == null) {
460
+ this.emitError("getStats", undefined, "Failed to retrieve audio stats")
461
+ }
462
+ return stats
463
+ }
464
+
465
+ dispose(): void {
466
+ if (!this.engine) return
467
+ if (this.mixerStarted) {
468
+ this.stop()
469
+ }
470
+ this.groups.clear()
471
+ this.lib.destroyAudioEngine(this.engine)
472
+ this.engine = null
473
+ this.emit("disposed")
474
+ }
475
+ }
476
+
477
+ export function setupAudio(options: AudioSetupOptions = {}): Audio {
478
+ return Audio.create(options)
479
+ }
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { performance } from "node:perf_hooks"
4
+ import { OptimizedBuffer } from "../buffer.js"
5
+ import { VignetteEffect } from "../post/effects.js"
6
+
7
+ type Scenario = { width: number; height: number }
8
+ type ScenarioResult = {
9
+ size: string
10
+ cells: number
11
+ avgMs: number
12
+ avgNsPerCell: number
13
+ medianMs: number
14
+ p95Ms: number
15
+ }
16
+
17
+ const ITERATIONS = 5000
18
+ const WARMUP_ITERATIONS = 100
19
+ const STRENGTH = 0.7
20
+ const baseScenarios: Array<{ width: number; height: number }> = [
21
+ { width: 40, height: 20 },
22
+ { width: 80, height: 24 },
23
+ { width: 120, height: 40 },
24
+ { width: 200, height: 60 },
25
+ ]
26
+ const scenarios: Scenario[] = baseScenarios
27
+
28
+ function calculateStats(samples: number[]): { avgMs: number; medianMs: number; p95Ms: number } {
29
+ const sorted = [...samples].sort((a, b) => a - b)
30
+ const total = samples.reduce((sum, value) => sum + value, 0)
31
+ const avgMs = total / samples.length
32
+ const mid = Math.floor(sorted.length / 2)
33
+ const medianMs = sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid]
34
+ const p95Index = Math.floor(0.95 * (sorted.length - 1))
35
+ const p95Ms = sorted[p95Index]
36
+
37
+ return { avgMs, medianMs, p95Ms }
38
+ }
39
+
40
+ function formatMs(value: number): number {
41
+ return Number(value.toFixed(4))
42
+ }
43
+
44
+ function formatNs(value: number): number {
45
+ return Number(value.toFixed(2))
46
+ }
47
+
48
+ function runScenario({ width, height }: Scenario): ScenarioResult {
49
+ const buffer = OptimizedBuffer.create(width, height, "unicode", { id: `vignette-bench-${width}x${height}` })
50
+ const vignetteEffect = new VignetteEffect(STRENGTH)
51
+ const { fg, bg } = buffer.buffers
52
+ fg.fill(1)
53
+ bg.fill(1)
54
+
55
+ for (let i = 0; i < WARMUP_ITERATIONS; i++) {
56
+ vignetteEffect.apply(buffer)
57
+ }
58
+
59
+ const samples = new Array<number>(ITERATIONS)
60
+ for (let i = 0; i < ITERATIONS; i++) {
61
+ const start = performance.now()
62
+ vignetteEffect.apply(buffer)
63
+ samples[i] = performance.now() - start
64
+ }
65
+
66
+ buffer.destroy()
67
+
68
+ const stats = calculateStats(samples)
69
+ return {
70
+ size: `${width}x${height}`,
71
+ cells: width * height,
72
+ avgMs: formatMs(stats.avgMs),
73
+ avgNsPerCell: formatNs((stats.avgMs * 1_000_000) / (width * height)),
74
+ medianMs: formatMs(stats.medianMs),
75
+ p95Ms: formatMs(stats.p95Ms),
76
+ }
77
+ }
78
+
79
+ console.log(`Vignette Effect Benchmark (${ITERATIONS} iterations per scenario)`)
80
+ const results = scenarios.map(runScenario)
81
+ console.table(results)