@fairyhunter13/opentui-core 0.1.112 → 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 +63 -51
  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-8fks7yv1.js +0 -411
  472. package/index-8fks7yv1.js.map +0 -10
  473. package/index-egy5e2rs.js +0 -12267
  474. package/index-egy5e2rs.js.map +0 -42
  475. package/index-tse8gzh0.js +0 -20614
  476. package/index-tse8gzh0.js.map +0 -67
  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,948 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import {
4
+ TextTableRenderable,
5
+ type TextTableCellContent,
6
+ type TextTableColumnFitter,
7
+ type TextTableColumnWidthMode,
8
+ type TextTableContent,
9
+ type CliRenderer,
10
+ } from "../index.js"
11
+ import { createTestRenderer } from "../testing.js"
12
+ import { Command } from "commander"
13
+ import { existsSync } from "node:fs"
14
+ import { mkdir } from "node:fs/promises"
15
+ import path from "node:path"
16
+
17
+ const realStdoutWrite = process.stdout.write.bind(process.stdout)
18
+
19
+ const WORDS = [
20
+ "alpha",
21
+ "bravo",
22
+ "charlie",
23
+ "delta",
24
+ "echo",
25
+ "foxtrot",
26
+ "golf",
27
+ "hotel",
28
+ "india",
29
+ "juliet",
30
+ "kilo",
31
+ "lima",
32
+ "mango",
33
+ "nectar",
34
+ "oscar",
35
+ "papa",
36
+ "quartz",
37
+ "romeo",
38
+ "sierra",
39
+ "tango",
40
+ "uniform",
41
+ "vector",
42
+ "whiskey",
43
+ "xray",
44
+ "yankee",
45
+ "zulu",
46
+ "matrix",
47
+ "signal",
48
+ "tensor",
49
+ "render",
50
+ "schema",
51
+ "buffer",
52
+ "layout",
53
+ "stream",
54
+ "parser",
55
+ "syntax",
56
+ "viewport",
57
+ "cursor",
58
+ ]
59
+
60
+ type MemorySample = {
61
+ rss: number
62
+ heapTotal: number
63
+ heapUsed: number
64
+ external: number
65
+ arrayBuffers: number
66
+ }
67
+
68
+ type MemoryStats = {
69
+ samples: number
70
+ start: MemorySample
71
+ end: MemorySample
72
+ delta: MemorySample
73
+ peak: MemorySample
74
+ }
75
+
76
+ type TimingStats = {
77
+ count: number
78
+ averageMs: number
79
+ medianMs: number
80
+ p95Ms: number
81
+ minMs: number
82
+ maxMs: number
83
+ stdDevMs: number
84
+ }
85
+
86
+ type ScenarioResult = {
87
+ name: string
88
+ description: string
89
+ category: "replace" | "incremental" | "selection"
90
+ timingMode: "content-set-and-render" | "selection-update-and-render"
91
+ iterations: number
92
+ warmupIterations: number
93
+ elapsedMs: number
94
+ updateStats: TimingStats
95
+ memoryStats?: MemoryStats
96
+ tableStats: {
97
+ initialRows: number
98
+ finalRows: number
99
+ maxRows: number
100
+ columns: number
101
+ updates: number
102
+ datasetVariants: number
103
+ }
104
+ settings: Record<string, unknown>
105
+ }
106
+
107
+ type ReplaceScenarioPlan = {
108
+ kind: "replace"
109
+ name: string
110
+ description: string
111
+ iterations: number
112
+ warmupIterations: number
113
+ rows: number
114
+ cols: number
115
+ variants: TextTableContent[]
116
+ tableConfig: BenchmarkTableConfig
117
+ }
118
+
119
+ type IncrementalScenarioPlan = {
120
+ kind: "incremental"
121
+ name: string
122
+ description: string
123
+ iterations: number
124
+ warmupIterations: number
125
+ cols: number
126
+ header: TextTableCellContent[]
127
+ baseRows: TextTableCellContent[][]
128
+ rowPool: TextTableCellContent[][]
129
+ maxRows: number
130
+ tableConfig: BenchmarkTableConfig
131
+ }
132
+
133
+ type SelectionScenarioPlan = {
134
+ kind: "selection"
135
+ name: string
136
+ description: string
137
+ iterations: number
138
+ warmupIterations: number
139
+ rows: number
140
+ cols: number
141
+ content: TextTableContent
142
+ dragSteps: number
143
+ tableConfig: BenchmarkTableConfig
144
+ }
145
+
146
+ type ScenarioPlan = ReplaceScenarioPlan | IncrementalScenarioPlan | SelectionScenarioPlan
147
+
148
+ type BenchmarkTableConfig = {
149
+ wrapMode: "none" | "char" | "word"
150
+ columnWidthMode: TextTableColumnWidthMode
151
+ columnFitter: TextTableColumnFitter
152
+ }
153
+
154
+ type RunContext = {
155
+ renderer: CliRenderer
156
+ table: TextTableRenderable
157
+ renderOnce: () => Promise<void>
158
+ memSampleEvery: number
159
+ }
160
+
161
+ type SuiteConfig = {
162
+ iterations: number
163
+ warmupIterations: number
164
+ longIterations: number
165
+ scale: number
166
+ }
167
+
168
+ type OutputMeta = {
169
+ suiteName: string
170
+ width: number
171
+ height: number
172
+ iterations: number
173
+ warmupIterations: number
174
+ longIterations: number
175
+ scale: number
176
+ seed: number
177
+ memSampleEvery: number
178
+ }
179
+
180
+ type IncrementalState = {
181
+ rows: TextTableCellContent[][]
182
+ cursor: number
183
+ maxRowsSeen: number
184
+ }
185
+
186
+ const program = new Command()
187
+ program
188
+ .name("text-table-benchmark")
189
+ .description("TextTableRenderable benchmark scenarios")
190
+ .option("-s, --suite <name>", "benchmark suite: quick, default, long", "default")
191
+ .option("-i, --iterations <count>", "iterations per scenario", "800")
192
+ .option("--warmup-iterations <count>", "warmup iterations per scenario", "80")
193
+ .option("--long-iterations <count>", "iterations for long suite", "3000")
194
+ .option("--scale <n>", "scale rows and dataset size", "1")
195
+ .option("--seed <n>", "seed for deterministic content", "1337")
196
+ .option("--width <n>", "test renderer width", "140")
197
+ .option("--height <n>", "test renderer height", "48")
198
+ .option("--mem-sample-every <count>", "sample memory every N iterations (0 disables)", "10")
199
+ .option("--scenario <name>", "run a single scenario")
200
+ .option("--json [path]", "write JSON results to file")
201
+ .option("--no-output", "suppress stdout output")
202
+ .parse(process.argv)
203
+
204
+ const options = program.opts()
205
+
206
+ const suiteName = String(options.suite)
207
+ const iterations = Math.max(1, Math.floor(toNumber(options.iterations, 800)))
208
+ const warmupIterations = Math.max(0, Math.floor(toNumber(options.warmupIterations, 80)))
209
+ const longIterations = Math.max(iterations, Math.floor(toNumber(options.longIterations, 3000)))
210
+ const scale = Math.max(0.25, toNumber(options.scale, 1))
211
+ const seed = Math.max(1, Math.floor(toNumber(options.seed, 1337)))
212
+ const width = Math.max(40, Math.floor(toNumber(options.width, 140)))
213
+ const height = Math.max(12, Math.floor(toNumber(options.height, 48)))
214
+ const memSampleEvery = Math.max(0, Math.floor(toNumber(options.memSampleEvery, 10)))
215
+ const scenarioFilter = options.scenario ? String(options.scenario) : null
216
+ const outputEnabled = options.output !== false
217
+
218
+ const PROPORTIONAL_TABLE_CONFIG: BenchmarkTableConfig = {
219
+ wrapMode: "word",
220
+ columnWidthMode: "full",
221
+ columnFitter: "proportional",
222
+ }
223
+
224
+ const BALANCED_TABLE_CONFIG: BenchmarkTableConfig = {
225
+ wrapMode: "word",
226
+ columnWidthMode: "full",
227
+ columnFitter: "balanced",
228
+ }
229
+
230
+ const jsonArg = options.json
231
+ const jsonPath =
232
+ typeof jsonArg === "string"
233
+ ? path.resolve(process.cwd(), jsonArg)
234
+ : jsonArg
235
+ ? path.resolve(process.cwd(), "latest-text-table-bench-run.json")
236
+ : null
237
+
238
+ if (jsonPath) {
239
+ const dir = path.dirname(jsonPath)
240
+ if (!existsSync(dir)) {
241
+ await mkdir(dir, { recursive: true })
242
+ }
243
+ if (existsSync(jsonPath)) {
244
+ console.error(`Error: output file already exists: ${jsonPath}`)
245
+ process.exit(1)
246
+ }
247
+ }
248
+
249
+ const scenarios = createScenarios(
250
+ suiteName,
251
+ {
252
+ iterations,
253
+ warmupIterations,
254
+ longIterations,
255
+ scale,
256
+ },
257
+ seed,
258
+ )
259
+
260
+ if (scenarios.length === 0) {
261
+ console.error(`Unknown suite: ${suiteName}`)
262
+ process.exit(1)
263
+ }
264
+
265
+ const filteredScenarios = scenarioFilter ? scenarios.filter((scenario) => scenario.name === scenarioFilter) : scenarios
266
+
267
+ if (scenarioFilter && filteredScenarios.length === 0) {
268
+ writeLine(`Unknown scenario: ${scenarioFilter}`)
269
+ process.exit(1)
270
+ }
271
+
272
+ const { renderer, renderOnce } = await createTestRenderer({
273
+ width,
274
+ height,
275
+ screenMode: "main-screen",
276
+ externalOutputMode: "passthrough",
277
+ consoleMode: "disabled",
278
+ })
279
+
280
+ renderer.requestRender = () => {}
281
+
282
+ const table = new TextTableRenderable(renderer, {
283
+ id: "text-table-bench",
284
+ width: "100%",
285
+ wrapMode: PROPORTIONAL_TABLE_CONFIG.wrapMode,
286
+ columnWidthMode: PROPORTIONAL_TABLE_CONFIG.columnWidthMode,
287
+ columnFitter: PROPORTIONAL_TABLE_CONFIG.columnFitter,
288
+ content: [],
289
+ })
290
+
291
+ renderer.root.add(table)
292
+ await renderOnce()
293
+
294
+ const ctx: RunContext = {
295
+ renderer,
296
+ table,
297
+ renderOnce,
298
+ memSampleEvery,
299
+ }
300
+
301
+ const results: ScenarioResult[] = []
302
+ const scenarioLines: string[] = []
303
+
304
+ try {
305
+ for (const plan of filteredScenarios) {
306
+ const result = await runScenario(plan, ctx)
307
+ results.push(result)
308
+ scenarioLines.push(formatScenarioResult(result))
309
+ }
310
+ } finally {
311
+ renderer.destroy()
312
+ }
313
+
314
+ await outputResults(
315
+ {
316
+ suiteName,
317
+ width,
318
+ height,
319
+ iterations,
320
+ warmupIterations,
321
+ longIterations,
322
+ scale,
323
+ seed,
324
+ memSampleEvery,
325
+ },
326
+ results,
327
+ scenarioLines,
328
+ outputEnabled,
329
+ jsonPath,
330
+ )
331
+
332
+ function createScenarios(suite: string, config: SuiteConfig, runSeed: number): ScenarioPlan[] {
333
+ const quick = {
334
+ replaceRows: scaled(24, config.scale),
335
+ replaceCols: 4,
336
+ replaceVariants: scaled(6, config.scale),
337
+ incrementalCols: 4,
338
+ incrementalBaseRows: scaled(8, config.scale),
339
+ incrementalPoolRows: scaled(220, config.scale),
340
+ incrementalMaxRows: scaled(120, config.scale),
341
+ }
342
+
343
+ const defaultSuite = {
344
+ replaceRows: scaled(72, config.scale),
345
+ replaceCols: 6,
346
+ replaceVariants: scaled(10, config.scale),
347
+ incrementalCols: 6,
348
+ incrementalBaseRows: scaled(16, config.scale),
349
+ incrementalPoolRows: scaled(480, config.scale),
350
+ incrementalMaxRows: scaled(320, config.scale),
351
+ }
352
+
353
+ const long = {
354
+ replaceRows: scaled(140, config.scale),
355
+ replaceCols: 8,
356
+ replaceVariants: scaled(14, config.scale),
357
+ incrementalCols: 8,
358
+ incrementalBaseRows: scaled(24, config.scale),
359
+ incrementalPoolRows: scaled(960, config.scale),
360
+ incrementalMaxRows: scaled(720, config.scale),
361
+ }
362
+
363
+ let shape: typeof quick
364
+ let runIterations = config.iterations
365
+
366
+ if (suite === "quick") {
367
+ shape = quick
368
+ } else if (suite === "default") {
369
+ shape = defaultSuite
370
+ } else if (suite === "long") {
371
+ shape = long
372
+ runIterations = config.longIterations
373
+ } else {
374
+ return []
375
+ }
376
+
377
+ const replaceRng = createRng((runSeed ^ 0x9e3779b9) >>> 0)
378
+ const variants: TextTableContent[] = []
379
+ for (let i = 0; i < shape.replaceVariants; i += 1) {
380
+ variants.push(buildTableContent(replaceRng, shape.replaceRows, shape.replaceCols))
381
+ }
382
+
383
+ const incrementalRng = createRng((runSeed ^ 0x85ebca6b) >>> 0)
384
+ const header = makeHeader(shape.incrementalCols)
385
+ const baseRows = buildRows(incrementalRng, shape.incrementalBaseRows, shape.incrementalCols, 0)
386
+ const rowPool = buildRows(
387
+ incrementalRng,
388
+ Math.max(shape.incrementalPoolRows, shape.incrementalBaseRows + 1),
389
+ shape.incrementalCols,
390
+ shape.incrementalBaseRows,
391
+ )
392
+
393
+ const replaceScenario: ReplaceScenarioPlan = {
394
+ kind: "replace",
395
+ name: "replace_tables",
396
+ description: "Replace full table content with prebuilt variants",
397
+ iterations: runIterations,
398
+ warmupIterations: config.warmupIterations,
399
+ rows: shape.replaceRows,
400
+ cols: shape.replaceCols,
401
+ variants,
402
+ tableConfig: PROPORTIONAL_TABLE_CONFIG,
403
+ }
404
+
405
+ const balancedFitterReplaceScenario: ReplaceScenarioPlan = {
406
+ kind: "replace",
407
+ name: "replace_tables_balanced_fitter",
408
+ description: "Replace full table content with prebuilt variants (balanced fitter)",
409
+ iterations: runIterations,
410
+ warmupIterations: config.warmupIterations,
411
+ rows: shape.replaceRows,
412
+ cols: shape.replaceCols,
413
+ variants,
414
+ tableConfig: BALANCED_TABLE_CONFIG,
415
+ }
416
+
417
+ const incrementalScenario: IncrementalScenarioPlan = {
418
+ kind: "incremental",
419
+ name: "incremental_table_rows",
420
+ description: "Append table rows and periodically reset to base size",
421
+ iterations: runIterations,
422
+ warmupIterations: config.warmupIterations,
423
+ cols: shape.incrementalCols,
424
+ header,
425
+ baseRows,
426
+ rowPool,
427
+ maxRows: Math.max(shape.incrementalMaxRows, shape.incrementalBaseRows + 1),
428
+ tableConfig: PROPORTIONAL_TABLE_CONFIG,
429
+ }
430
+
431
+ const balancedFitterIncrementalScenario: IncrementalScenarioPlan = {
432
+ kind: "incremental",
433
+ name: "incremental_table_rows_balanced_fitter",
434
+ description: "Append table rows and periodically reset to base size (balanced fitter)",
435
+ iterations: runIterations,
436
+ warmupIterations: config.warmupIterations,
437
+ cols: shape.incrementalCols,
438
+ header,
439
+ baseRows,
440
+ rowPool,
441
+ maxRows: Math.max(shape.incrementalMaxRows, shape.incrementalBaseRows + 1),
442
+ tableConfig: BALANCED_TABLE_CONFIG,
443
+ }
444
+
445
+ const selectionRng = createRng((runSeed ^ 0xa2f9c6d1) >>> 0)
446
+ const selectionContent = buildTableContent(selectionRng, shape.replaceRows, shape.replaceCols)
447
+
448
+ const selectionScenario: SelectionScenarioPlan = {
449
+ kind: "selection",
450
+ name: "selection_update",
451
+ description: "Update selection focus across rows and render",
452
+ iterations: runIterations,
453
+ warmupIterations: config.warmupIterations,
454
+ rows: shape.replaceRows,
455
+ cols: shape.replaceCols,
456
+ content: selectionContent,
457
+ dragSteps: 5,
458
+ tableConfig: PROPORTIONAL_TABLE_CONFIG,
459
+ }
460
+
461
+ const balancedFitterSelectionScenario: SelectionScenarioPlan = {
462
+ kind: "selection",
463
+ name: "selection_update_balanced_fitter",
464
+ description: "Update selection focus across rows and render (balanced fitter)",
465
+ iterations: runIterations,
466
+ warmupIterations: config.warmupIterations,
467
+ rows: shape.replaceRows,
468
+ cols: shape.replaceCols,
469
+ content: selectionContent,
470
+ dragSteps: 5,
471
+ tableConfig: BALANCED_TABLE_CONFIG,
472
+ }
473
+
474
+ return [
475
+ replaceScenario,
476
+ balancedFitterReplaceScenario,
477
+ incrementalScenario,
478
+ balancedFitterIncrementalScenario,
479
+ selectionScenario,
480
+ balancedFitterSelectionScenario,
481
+ ]
482
+ }
483
+
484
+ async function runScenario(plan: ScenarioPlan, ctx: RunContext): Promise<ScenarioResult> {
485
+ if (plan.kind === "replace") {
486
+ return runReplaceScenario(plan, ctx)
487
+ }
488
+ if (plan.kind === "incremental") {
489
+ return runIncrementalScenario(plan, ctx)
490
+ }
491
+ return runSelectionScenario(plan, ctx)
492
+ }
493
+
494
+ function applyTableConfig(table: TextTableRenderable, config: BenchmarkTableConfig): void {
495
+ table.wrapMode = config.wrapMode
496
+ table.columnWidthMode = config.columnWidthMode
497
+ table.columnFitter = config.columnFitter
498
+ }
499
+
500
+ async function runReplaceScenario(plan: ReplaceScenarioPlan, ctx: RunContext): Promise<ScenarioResult> {
501
+ applyTableConfig(ctx.table, plan.tableConfig)
502
+
503
+ for (let i = 0; i < plan.warmupIterations; i += 1) {
504
+ const variant = plan.variants[i % plan.variants.length]
505
+ ctx.table.content = variant
506
+ await ctx.renderOnce()
507
+ }
508
+
509
+ const durations: number[] = []
510
+ const measurementStart = Date.now()
511
+ const memStart = shouldSampleMemory(ctx.memSampleEvery) ? readMemorySample() : null
512
+ const memSamples: MemorySample[] = []
513
+
514
+ for (let i = 0; i < plan.iterations; i += 1) {
515
+ const variant = plan.variants[i % plan.variants.length]
516
+ const start = performance.now()
517
+ ctx.table.content = variant
518
+ await ctx.renderOnce()
519
+ durations.push(performance.now() - start)
520
+
521
+ if (ctx.memSampleEvery > 0 && (i + 1) % ctx.memSampleEvery === 0) {
522
+ memSamples.push(readMemorySample())
523
+ }
524
+ }
525
+
526
+ const elapsedMs = Date.now() - measurementStart
527
+ const memEnd = shouldSampleMemory(ctx.memSampleEvery) ? readMemorySample() : null
528
+
529
+ return {
530
+ name: plan.name,
531
+ description: plan.description,
532
+ category: "replace",
533
+ timingMode: "content-set-and-render",
534
+ iterations: plan.iterations,
535
+ warmupIterations: plan.warmupIterations,
536
+ elapsedMs,
537
+ updateStats: computeTimingStats(durations),
538
+ memoryStats: memStart && memEnd ? computeMemoryStats(memSamples, memStart, memEnd) : undefined,
539
+ tableStats: {
540
+ initialRows: plan.rows,
541
+ finalRows: plan.rows,
542
+ maxRows: plan.rows,
543
+ columns: plan.cols,
544
+ updates: plan.iterations,
545
+ datasetVariants: plan.variants.length,
546
+ },
547
+ settings: {
548
+ rows: plan.rows,
549
+ cols: plan.cols,
550
+ variants: plan.variants.length,
551
+ mode: "replace",
552
+ wrapMode: plan.tableConfig.wrapMode,
553
+ columnWidthMode: plan.tableConfig.columnWidthMode,
554
+ columnFitter: plan.tableConfig.columnFitter,
555
+ },
556
+ }
557
+ }
558
+
559
+ async function runIncrementalScenario(plan: IncrementalScenarioPlan, ctx: RunContext): Promise<ScenarioResult> {
560
+ applyTableConfig(ctx.table, plan.tableConfig)
561
+
562
+ const state: IncrementalState = {
563
+ rows: [...plan.baseRows],
564
+ cursor: 0,
565
+ maxRowsSeen: plan.baseRows.length,
566
+ }
567
+
568
+ ctx.table.content = [plan.header, ...state.rows]
569
+ await ctx.renderOnce()
570
+
571
+ for (let i = 0; i < plan.warmupIterations; i += 1) {
572
+ const next = nextIncrementalContent(plan, state)
573
+ ctx.table.content = next
574
+ await ctx.renderOnce()
575
+ }
576
+
577
+ const durations: number[] = []
578
+ const measurementStart = Date.now()
579
+ const memStart = shouldSampleMemory(ctx.memSampleEvery) ? readMemorySample() : null
580
+ const memSamples: MemorySample[] = []
581
+
582
+ for (let i = 0; i < plan.iterations; i += 1) {
583
+ const next = nextIncrementalContent(plan, state)
584
+
585
+ const start = performance.now()
586
+ ctx.table.content = next
587
+ await ctx.renderOnce()
588
+ durations.push(performance.now() - start)
589
+
590
+ if (ctx.memSampleEvery > 0 && (i + 1) % ctx.memSampleEvery === 0) {
591
+ memSamples.push(readMemorySample())
592
+ }
593
+ }
594
+
595
+ const elapsedMs = Date.now() - measurementStart
596
+ const memEnd = shouldSampleMemory(ctx.memSampleEvery) ? readMemorySample() : null
597
+
598
+ return {
599
+ name: plan.name,
600
+ description: plan.description,
601
+ category: "incremental",
602
+ timingMode: "content-set-and-render",
603
+ iterations: plan.iterations,
604
+ warmupIterations: plan.warmupIterations,
605
+ elapsedMs,
606
+ updateStats: computeTimingStats(durations),
607
+ memoryStats: memStart && memEnd ? computeMemoryStats(memSamples, memStart, memEnd) : undefined,
608
+ tableStats: {
609
+ initialRows: plan.baseRows.length,
610
+ finalRows: state.rows.length,
611
+ maxRows: state.maxRowsSeen,
612
+ columns: plan.cols,
613
+ updates: plan.iterations,
614
+ datasetVariants: plan.rowPool.length,
615
+ },
616
+ settings: {
617
+ cols: plan.cols,
618
+ baseRows: plan.baseRows.length,
619
+ rowPool: plan.rowPool.length,
620
+ maxRows: plan.maxRows,
621
+ mode: "incremental",
622
+ wrapMode: plan.tableConfig.wrapMode,
623
+ columnWidthMode: plan.tableConfig.columnWidthMode,
624
+ columnFitter: plan.tableConfig.columnFitter,
625
+ },
626
+ }
627
+ }
628
+
629
+ async function runSelectionScenario(plan: SelectionScenarioPlan, ctx: RunContext): Promise<ScenarioResult> {
630
+ applyTableConfig(ctx.table, plan.tableConfig)
631
+ ctx.table.content = plan.content
632
+ await ctx.renderOnce()
633
+
634
+ const tableX = ctx.table.x
635
+ const tableY = ctx.table.y
636
+ const tableH = ctx.table.height
637
+
638
+ const anchorX = tableX + 2
639
+ const anchorY = tableY + 2
640
+
641
+ const maxFocusY = tableY + tableH - 2
642
+ const focusRange = Math.max(1, maxFocusY - anchorY)
643
+
644
+ for (let i = 0; i < plan.warmupIterations; i += 1) {
645
+ const focusY = anchorY + (i % focusRange)
646
+ ctx.renderer.startSelection(ctx.table, anchorX, anchorY)
647
+ for (let step = 1; step <= plan.dragSteps; step += 1) {
648
+ const stepY = anchorY + Math.round(((focusY - anchorY) * step) / plan.dragSteps)
649
+ ctx.renderer.updateSelection(ctx.table, anchorX + 4, stepY)
650
+ }
651
+ await ctx.renderOnce()
652
+ ctx.renderer.clearSelection()
653
+ await ctx.renderOnce()
654
+ }
655
+
656
+ const durations: number[] = []
657
+ const measurementStart = Date.now()
658
+ const memStart = shouldSampleMemory(ctx.memSampleEvery) ? readMemorySample() : null
659
+ const memSamples: MemorySample[] = []
660
+
661
+ for (let i = 0; i < plan.iterations; i += 1) {
662
+ const focusY = anchorY + (i % focusRange)
663
+
664
+ const start = performance.now()
665
+
666
+ ctx.renderer.startSelection(ctx.table, anchorX, anchorY)
667
+ for (let step = 1; step <= plan.dragSteps; step += 1) {
668
+ const stepY = anchorY + Math.round(((focusY - anchorY) * step) / plan.dragSteps)
669
+ ctx.renderer.updateSelection(ctx.table, anchorX + 4, stepY)
670
+ }
671
+ await ctx.renderOnce()
672
+ ctx.renderer.clearSelection()
673
+ await ctx.renderOnce()
674
+
675
+ durations.push(performance.now() - start)
676
+
677
+ if (ctx.memSampleEvery > 0 && (i + 1) % ctx.memSampleEvery === 0) {
678
+ memSamples.push(readMemorySample())
679
+ }
680
+ }
681
+
682
+ const elapsedMs = Date.now() - measurementStart
683
+ const memEnd = shouldSampleMemory(ctx.memSampleEvery) ? readMemorySample() : null
684
+
685
+ return {
686
+ name: plan.name,
687
+ description: plan.description,
688
+ category: "selection",
689
+ timingMode: "selection-update-and-render",
690
+ iterations: plan.iterations,
691
+ warmupIterations: plan.warmupIterations,
692
+ elapsedMs,
693
+ updateStats: computeTimingStats(durations),
694
+ memoryStats: memStart && memEnd ? computeMemoryStats(memSamples, memStart, memEnd) : undefined,
695
+ tableStats: {
696
+ initialRows: plan.rows,
697
+ finalRows: plan.rows,
698
+ maxRows: plan.rows,
699
+ columns: plan.cols,
700
+ updates: plan.iterations * (plan.dragSteps + 1),
701
+ datasetVariants: 1,
702
+ },
703
+ settings: {
704
+ rows: plan.rows,
705
+ cols: plan.cols,
706
+ dragSteps: plan.dragSteps,
707
+ mode: "selection",
708
+ wrapMode: plan.tableConfig.wrapMode,
709
+ columnWidthMode: plan.tableConfig.columnWidthMode,
710
+ columnFitter: plan.tableConfig.columnFitter,
711
+ },
712
+ }
713
+ }
714
+
715
+ function nextIncrementalContent(plan: IncrementalScenarioPlan, state: IncrementalState): TextTableContent {
716
+ if (state.rows.length >= plan.maxRows) {
717
+ state.rows = [...plan.baseRows]
718
+ }
719
+
720
+ const fallbackRow = plan.rowPool[0] ?? makeDataRow(createRng(1), 0, plan.cols)
721
+ const nextRow = plan.rowPool[state.cursor] ?? fallbackRow
722
+
723
+ state.cursor += 1
724
+ if (state.cursor >= plan.rowPool.length) {
725
+ state.cursor = 0
726
+ }
727
+
728
+ state.rows = [...state.rows, nextRow]
729
+ state.maxRowsSeen = Math.max(state.maxRowsSeen, state.rows.length)
730
+
731
+ return [plan.header, ...state.rows]
732
+ }
733
+
734
+ function makeHeader(cols: number): TextTableCellContent[] {
735
+ const header: TextTableCellContent[] = []
736
+ for (let c = 0; c < cols; c += 1) {
737
+ header.push(chunkCell(`Column ${c + 1}`))
738
+ }
739
+ return header
740
+ }
741
+
742
+ function buildTableContent(rng: () => number, rows: number, cols: number): TextTableContent {
743
+ return [makeHeader(cols), ...buildRows(rng, rows, cols, 0)]
744
+ }
745
+
746
+ function buildRows(rng: () => number, rows: number, cols: number, rowOffset: number): TextTableCellContent[][] {
747
+ const out: TextTableCellContent[][] = []
748
+ for (let r = 0; r < rows; r += 1) {
749
+ out.push(makeDataRow(rng, rowOffset + r, cols))
750
+ }
751
+ return out
752
+ }
753
+
754
+ function makeDataRow(rng: () => number, rowIndex: number, cols: number): TextTableCellContent[] {
755
+ const row: TextTableCellContent[] = []
756
+ for (let c = 0; c < cols; c += 1) {
757
+ row.push(chunkCell(makeCellText(rng, rowIndex, c)))
758
+ }
759
+ return row
760
+ }
761
+
762
+ function chunkCell(text: string): TextTableCellContent {
763
+ return [
764
+ {
765
+ __isChunk: true,
766
+ text,
767
+ },
768
+ ]
769
+ }
770
+
771
+ function makeCellText(rng: () => number, row: number, col: number): string {
772
+ const a = pick(rng, WORDS)
773
+ const b = pick(rng, WORDS)
774
+ const roll = rng()
775
+
776
+ if (roll < 0.2) {
777
+ return `${a}-${b}-${row + col}`
778
+ }
779
+ if (roll < 0.4) {
780
+ return `${a} ${Math.floor(rng() * 1000)}`
781
+ }
782
+ if (roll < 0.6) {
783
+ return `${a} ${b} ${pick(rng, WORDS)}`
784
+ }
785
+ if (roll < 0.8) {
786
+ return `${a}_${b}_${Math.floor(rng() * 100)}`
787
+ }
788
+ return `${a} ${b} r${row}c${col}`
789
+ }
790
+
791
+ function pick<T>(rng: () => number, list: T[]): T {
792
+ return list[Math.floor(rng() * list.length)]
793
+ }
794
+
795
+ function createRng(initialSeed: number): () => number {
796
+ let state = initialSeed >>> 0
797
+ return () => {
798
+ state = (state * 1664525 + 1013904223) >>> 0
799
+ return state / 0x100000000
800
+ }
801
+ }
802
+
803
+ function scaled(value: number, scaleValue: number): number {
804
+ return Math.max(1, Math.round(value * scaleValue))
805
+ }
806
+
807
+ function toNumber(value: unknown, fallback: number): number {
808
+ if (typeof value === "number" && Number.isFinite(value)) return value
809
+ if (typeof value === "string") {
810
+ const parsed = Number(value)
811
+ if (Number.isFinite(parsed)) return parsed
812
+ }
813
+ return fallback
814
+ }
815
+
816
+ function shouldSampleMemory(memSampleEvery: number): boolean {
817
+ return memSampleEvery > 0
818
+ }
819
+
820
+ function readMemorySample(): MemorySample {
821
+ const usage = process.memoryUsage()
822
+ return {
823
+ rss: usage.rss ?? 0,
824
+ heapTotal: usage.heapTotal ?? 0,
825
+ heapUsed: usage.heapUsed ?? 0,
826
+ external: usage.external ?? 0,
827
+ arrayBuffers: usage.arrayBuffers ?? 0,
828
+ }
829
+ }
830
+
831
+ function computeMemoryStats(samples: MemorySample[], start: MemorySample, end: MemorySample): MemoryStats {
832
+ const all = [start, ...samples, end]
833
+ const peak = { ...start }
834
+
835
+ for (const sample of all) {
836
+ peak.rss = Math.max(peak.rss, sample.rss)
837
+ peak.heapTotal = Math.max(peak.heapTotal, sample.heapTotal)
838
+ peak.heapUsed = Math.max(peak.heapUsed, sample.heapUsed)
839
+ peak.external = Math.max(peak.external, sample.external)
840
+ peak.arrayBuffers = Math.max(peak.arrayBuffers, sample.arrayBuffers)
841
+ }
842
+
843
+ return {
844
+ samples: all.length,
845
+ start,
846
+ end,
847
+ delta: diffMemory(start, end),
848
+ peak,
849
+ }
850
+ }
851
+
852
+ function diffMemory(start: MemorySample, end: MemorySample): MemorySample {
853
+ return {
854
+ rss: end.rss - start.rss,
855
+ heapTotal: end.heapTotal - start.heapTotal,
856
+ heapUsed: end.heapUsed - start.heapUsed,
857
+ external: end.external - start.external,
858
+ arrayBuffers: end.arrayBuffers - start.arrayBuffers,
859
+ }
860
+ }
861
+
862
+ function computeTimingStats(durations: number[]): TimingStats {
863
+ const sorted = [...durations].sort((a, b) => a - b)
864
+ const count = sorted.length
865
+ const sum = sorted.reduce((acc, value) => acc + value, 0)
866
+ const average = count > 0 ? sum / count : 0
867
+ const min = sorted[0] ?? 0
868
+ const max = sorted[count - 1] ?? 0
869
+ const median = count > 0 ? (sorted[Math.floor(count / 2)] ?? 0) : 0
870
+ const p95 = count > 0 ? (sorted[Math.floor(count * 0.95)] ?? 0) : 0
871
+ const stdDev = count > 0 ? Math.sqrt(sorted.reduce((acc, v) => acc + Math.pow(v - average, 2), 0) / count) : 0
872
+
873
+ return {
874
+ count,
875
+ averageMs: average,
876
+ medianMs: median,
877
+ p95Ms: p95,
878
+ minMs: min,
879
+ maxMs: max,
880
+ stdDevMs: stdDev,
881
+ }
882
+ }
883
+
884
+ async function outputResults(
885
+ meta: OutputMeta,
886
+ results: ScenarioResult[],
887
+ scenarioLines: string[],
888
+ outputEnabled: boolean,
889
+ outputPath: string | null,
890
+ ): Promise<void> {
891
+ const runId = new Date().toISOString()
892
+ const payload = {
893
+ runId,
894
+ suite: meta.suiteName,
895
+ config: {
896
+ width: meta.width,
897
+ height: meta.height,
898
+ iterations: meta.iterations,
899
+ warmupIterations: meta.warmupIterations,
900
+ longIterations: meta.longIterations,
901
+ scale: meta.scale,
902
+ seed: meta.seed,
903
+ memSampleEvery: meta.memSampleEvery,
904
+ },
905
+ results,
906
+ }
907
+
908
+ if (outputEnabled) {
909
+ writeLine(
910
+ `text-table-benchmark suite=${meta.suiteName} mode=content-set-and-render iters=${meta.iterations} warmup=${meta.warmupIterations}`,
911
+ )
912
+ for (const line of scenarioLines) {
913
+ writeLine(line)
914
+ }
915
+ }
916
+
917
+ if (outputPath) {
918
+ try {
919
+ const json = JSON.stringify(payload, null, 2)
920
+ await Bun.write(outputPath, json)
921
+ } catch (error: any) {
922
+ writeLine(`Error writing results to ${outputPath}: ${error.message}`)
923
+ }
924
+ }
925
+ }
926
+
927
+ function formatBytes(value: number): string {
928
+ return `${(value / (1024 * 1024)).toFixed(2)}MB`
929
+ }
930
+
931
+ function formatScenarioResult(result: ScenarioResult): string {
932
+ const mem = result.memoryStats
933
+ const memSummary = mem
934
+ ? ` memDeltaRss=${formatBytes(mem.delta.rss)}` +
935
+ ` memDeltaHeap=${formatBytes(mem.delta.heapUsed)}` +
936
+ ` memDeltaExt=${formatBytes(mem.delta.external)}` +
937
+ ` memDeltaAB=${formatBytes(mem.delta.arrayBuffers)}` +
938
+ ` memPeakRss=${formatBytes(mem.peak.rss)}`
939
+ : ""
940
+
941
+ const fitter = typeof result.settings.columnFitter === "string" ? result.settings.columnFitter : "unknown"
942
+
943
+ return `scenario=${result.name} category=${result.category} mode=${result.timingMode} fitter=${fitter} iters=${result.updateStats.count} elapsedMs=${result.elapsedMs} avgMs=${result.updateStats.averageMs.toFixed(3)} medianMs=${result.updateStats.medianMs.toFixed(3)} p95Ms=${result.updateStats.p95Ms.toFixed(3)} minMs=${result.updateStats.minMs.toFixed(3)} maxMs=${result.updateStats.maxMs.toFixed(3)} rows=${result.tableStats.finalRows} maxRows=${result.tableStats.maxRows} cols=${result.tableStats.columns}${memSummary}`
944
+ }
945
+
946
+ function writeLine(line: string): void {
947
+ realStdoutWrite(`${line}\n`)
948
+ }