@xincli/opentui-core 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (476) hide show
  1. package/README.md +63 -0
  2. package/dev/keypress-debug-renderer.ts +148 -0
  3. package/dev/keypress-debug.ts +43 -0
  4. package/dev/print-env-vars.ts +32 -0
  5. package/dev/test-tmux-graphics-334.sh +68 -0
  6. package/dev/test-tmux-theme-queries.sh +151 -0
  7. package/dev/thai-debug-test.ts +68 -0
  8. package/dev/theme-mode.ts +290 -0
  9. package/docs/development.md +144 -0
  10. package/package.json +102 -0
  11. package/prebuilt/aarch64-android/libopentui.so +0 -0
  12. package/scripts/build-android.ts +226 -0
  13. package/scripts/build-native-termux.sh +525 -0
  14. package/scripts/build.ts +530 -0
  15. package/scripts/dist-test.ts +312 -0
  16. package/scripts/package-prebuilt.ts +110 -0
  17. package/scripts/publish.ts +60 -0
  18. package/scripts/test-node-hook.mjs +16 -0
  19. package/scripts/test-node.ts +241 -0
  20. package/scripts/vendor-deps.sh +73 -0
  21. package/src/NativeSpanFeed.ts +375 -0
  22. package/src/Renderable.ts +1878 -0
  23. package/src/__snapshots__/buffer.test.ts.snap +28 -0
  24. package/src/animation/Timeline.test.ts +2709 -0
  25. package/src/animation/Timeline.ts +598 -0
  26. package/src/ansi.ts +22 -0
  27. package/src/audio.ts +479 -0
  28. package/src/benchmark/attenuation-benchmark.ts +81 -0
  29. package/src/benchmark/audio-playback-benchmark.ts +344 -0
  30. package/src/benchmark/box-draw-benchmark.ts +1042 -0
  31. package/src/benchmark/colormatrix-benchmark.ts +123 -0
  32. package/src/benchmark/gain-benchmark.ts +80 -0
  33. package/src/benchmark/latest-all-bench-run.json +707 -0
  34. package/src/benchmark/latest-async-bench-run.json +336 -0
  35. package/src/benchmark/latest-default-bench-run.json +657 -0
  36. package/src/benchmark/latest-large-bench-run.json +707 -0
  37. package/src/benchmark/latest-quick-bench-run.json +207 -0
  38. package/src/benchmark/layout-benchmark.ts +2547 -0
  39. package/src/benchmark/markdown-benchmark.ts +1796 -0
  40. package/src/benchmark/native-span-feed-async-benchmark.ts +355 -0
  41. package/src/benchmark/native-span-feed-benchmark.md +56 -0
  42. package/src/benchmark/native-span-feed-benchmark.ts +596 -0
  43. package/src/benchmark/native-span-feed-compare.ts +280 -0
  44. package/src/benchmark/render-traversal-benchmark.ts +928 -0
  45. package/src/benchmark/text-buffer-render-benchmark.ts +874 -0
  46. package/src/benchmark/text-table-benchmark.ts +948 -0
  47. package/src/buffer.test.ts +292 -0
  48. package/src/buffer.ts +564 -0
  49. package/src/console.test.ts +629 -0
  50. package/src/console.ts +1253 -0
  51. package/src/edit-buffer.test.ts +1852 -0
  52. package/src/edit-buffer.ts +417 -0
  53. package/src/editor-view.test.ts +1056 -0
  54. package/src/editor-view.ts +290 -0
  55. package/src/index.ts +25 -0
  56. package/src/lib/KeyHandler.integration.test.ts +292 -0
  57. package/src/lib/KeyHandler.stopPropagation.test.ts +289 -0
  58. package/src/lib/KeyHandler.test.ts +662 -0
  59. package/src/lib/KeyHandler.ts +222 -0
  60. package/src/lib/RGBA.test.ts +60 -0
  61. package/src/lib/RGBA.ts +343 -0
  62. package/src/lib/ascii.font.ts +330 -0
  63. package/src/lib/border.test.ts +83 -0
  64. package/src/lib/border.ts +170 -0
  65. package/src/lib/bunfs.test.ts +27 -0
  66. package/src/lib/bunfs.ts +18 -0
  67. package/src/lib/clipboard.test.ts +63 -0
  68. package/src/lib/clipboard.ts +41 -0
  69. package/src/lib/clock.ts +35 -0
  70. package/src/lib/data-paths.test.ts +133 -0
  71. package/src/lib/data-paths.ts +109 -0
  72. package/src/lib/debounce.ts +106 -0
  73. package/src/lib/detect-links.test.ts +98 -0
  74. package/src/lib/detect-links.ts +56 -0
  75. package/src/lib/env.test.ts +228 -0
  76. package/src/lib/env.ts +209 -0
  77. package/src/lib/extmarks-history.ts +51 -0
  78. package/src/lib/extmarks-multiwidth.test.ts +324 -0
  79. package/src/lib/extmarks.test.ts +3547 -0
  80. package/src/lib/extmarks.ts +861 -0
  81. package/src/lib/fonts/block.json +405 -0
  82. package/src/lib/fonts/grid.json +265 -0
  83. package/src/lib/fonts/huge.json +741 -0
  84. package/src/lib/fonts/pallet.json +314 -0
  85. package/src/lib/fonts/shade.json +591 -0
  86. package/src/lib/fonts/slick.json +321 -0
  87. package/src/lib/fonts/tiny.json +69 -0
  88. package/src/lib/hast-styled-text.ts +59 -0
  89. package/src/lib/index.ts +22 -0
  90. package/src/lib/keybinding.internal.test.ts +342 -0
  91. package/src/lib/keybinding.internal.ts +182 -0
  92. package/src/lib/objects-in-viewport.test.ts +789 -0
  93. package/src/lib/objects-in-viewport.ts +153 -0
  94. package/src/lib/output.capture.ts +58 -0
  95. package/src/lib/parse.keypress-kitty.protocol.test.ts +347 -0
  96. package/src/lib/parse.keypress-kitty.test.ts +712 -0
  97. package/src/lib/parse.keypress-kitty.ts +470 -0
  98. package/src/lib/parse.keypress.test.ts +2020 -0
  99. package/src/lib/parse.keypress.ts +480 -0
  100. package/src/lib/parse.mouse.test.ts +552 -0
  101. package/src/lib/parse.mouse.ts +232 -0
  102. package/src/lib/paste.ts +18 -0
  103. package/src/lib/queue.ts +65 -0
  104. package/src/lib/render-geometry.ts +36 -0
  105. package/src/lib/renderable.validations.test.ts +87 -0
  106. package/src/lib/renderable.validations.ts +83 -0
  107. package/src/lib/scroll-acceleration.ts +98 -0
  108. package/src/lib/selection.ts +261 -0
  109. package/src/lib/singleton.ts +34 -0
  110. package/src/lib/stdin-parser.test.ts +2439 -0
  111. package/src/lib/stdin-parser.ts +2007 -0
  112. package/src/lib/styled-text.ts +178 -0
  113. package/src/lib/terminal-capability-detection.test.ts +226 -0
  114. package/src/lib/terminal-capability-detection.ts +98 -0
  115. package/src/lib/terminal-palette.test.ts +954 -0
  116. package/src/lib/terminal-palette.ts +441 -0
  117. package/src/lib/tree-sitter/assets/README.md +119 -0
  118. package/src/lib/tree-sitter/assets/javascript/highlights.scm +205 -0
  119. package/src/lib/tree-sitter/assets/javascript/tree-sitter-javascript.wasm +0 -0
  120. package/src/lib/tree-sitter/assets/markdown/highlights.scm +150 -0
  121. package/src/lib/tree-sitter/assets/markdown/injections.scm +27 -0
  122. package/src/lib/tree-sitter/assets/markdown/tree-sitter-markdown.wasm +0 -0
  123. package/src/lib/tree-sitter/assets/markdown_inline/highlights.scm +115 -0
  124. package/src/lib/tree-sitter/assets/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
  125. package/src/lib/tree-sitter/assets/typescript/highlights.scm +604 -0
  126. package/src/lib/tree-sitter/assets/typescript/tree-sitter-typescript.wasm +0 -0
  127. package/src/lib/tree-sitter/assets/update.ts +360 -0
  128. package/src/lib/tree-sitter/assets/zig/highlights.scm +284 -0
  129. package/src/lib/tree-sitter/assets/zig/tree-sitter-zig.wasm +0 -0
  130. package/src/lib/tree-sitter/assets.d.ts +9 -0
  131. package/src/lib/tree-sitter/cache.test.ts +291 -0
  132. package/src/lib/tree-sitter/client.test.ts +1434 -0
  133. package/src/lib/tree-sitter/client.ts +847 -0
  134. package/src/lib/tree-sitter/default-parsers.ts +133 -0
  135. package/src/lib/tree-sitter/download-utils.ts +148 -0
  136. package/src/lib/tree-sitter/index.ts +75 -0
  137. package/src/lib/tree-sitter/parser.worker.ts +1098 -0
  138. package/src/lib/tree-sitter/parsers-config.ts +81 -0
  139. package/src/lib/tree-sitter/resolve-ft.test.ts +55 -0
  140. package/src/lib/tree-sitter/resolve-ft.ts +189 -0
  141. package/src/lib/tree-sitter/types.ts +132 -0
  142. package/src/lib/tree-sitter/update-assets.ts +10 -0
  143. package/src/lib/tree-sitter-styled-text.test.ts +1253 -0
  144. package/src/lib/tree-sitter-styled-text.ts +314 -0
  145. package/src/lib/validate-dir-name.ts +55 -0
  146. package/src/lib/yoga.options.test.ts +628 -0
  147. package/src/lib/yoga.options.ts +346 -0
  148. package/src/native-event-worker-repro.test.ts +48 -0
  149. package/src/native-event-worker-repro.worker.ts +9 -0
  150. package/src/native-handle.test.ts +221 -0
  151. package/src/platform/ffi.test.ts +516 -0
  152. package/src/platform/ffi.ts +653 -0
  153. package/src/platform/runtime.test.ts +52 -0
  154. package/src/platform/runtime.ts +121 -0
  155. package/src/platform/test.ts +45 -0
  156. package/src/platform/worker-handler-cleanup.fixture.ts +12 -0
  157. package/src/platform/worker-onmessage-startup.fixture.ts +10 -0
  158. package/src/platform/worker-startup.fixture.ts +9 -0
  159. package/src/platform/worker.node-test.ts +153 -0
  160. package/src/platform/worker.ts +425 -0
  161. package/src/plugins/core-slot.ts +579 -0
  162. package/src/plugins/registry.ts +402 -0
  163. package/src/plugins/types.ts +46 -0
  164. package/src/post/effects.ts +948 -0
  165. package/src/post/filters.ts +512 -0
  166. package/src/post/matrices.ts +288 -0
  167. package/src/renderables/ASCIIFont.ts +219 -0
  168. package/src/renderables/Box.test.ts +474 -0
  169. package/src/renderables/Box.ts +353 -0
  170. package/src/renderables/Code.test.ts +2388 -0
  171. package/src/renderables/Code.ts +555 -0
  172. package/src/renderables/Diff.regression.test.ts +381 -0
  173. package/src/renderables/Diff.test.ts +3212 -0
  174. package/src/renderables/Diff.ts +1278 -0
  175. package/src/renderables/EditBufferRenderable.test.ts +288 -0
  176. package/src/renderables/EditBufferRenderable.ts +1152 -0
  177. package/src/renderables/FrameBuffer.ts +47 -0
  178. package/src/renderables/Input.test.ts +1310 -0
  179. package/src/renderables/Input.ts +273 -0
  180. package/src/renderables/LineNumberRenderable.ts +725 -0
  181. package/src/renderables/Markdown.ts +2144 -0
  182. package/src/renderables/ScrollBar.ts +423 -0
  183. package/src/renderables/ScrollBox.ts +903 -0
  184. package/src/renderables/Select.test.ts +1134 -0
  185. package/src/renderables/Select.ts +535 -0
  186. package/src/renderables/Slider.test.ts +456 -0
  187. package/src/renderables/Slider.ts +342 -0
  188. package/src/renderables/TabSelect.test.ts +238 -0
  189. package/src/renderables/TabSelect.ts +447 -0
  190. package/src/renderables/Text.selection-buffer.test.ts +118 -0
  191. package/src/renderables/Text.test.ts +2662 -0
  192. package/src/renderables/Text.ts +147 -0
  193. package/src/renderables/TextBufferRenderable.ts +504 -0
  194. package/src/renderables/TextNode.test.ts +1137 -0
  195. package/src/renderables/TextNode.ts +379 -0
  196. package/src/renderables/TextTable.test.ts +1411 -0
  197. package/src/renderables/TextTable.ts +1434 -0
  198. package/src/renderables/Textarea.ts +425 -0
  199. package/src/renderables/TimeToFirstDraw.ts +89 -0
  200. package/src/renderables/__snapshots__/Code.test.ts.snap +13 -0
  201. package/src/renderables/__snapshots__/Diff.test.ts.snap +785 -0
  202. package/src/renderables/__snapshots__/Text.test.ts.snap +421 -0
  203. package/src/renderables/__snapshots__/TextTable.test.ts.snap +215 -0
  204. package/src/renderables/__tests__/Code.test.ts +55 -0
  205. package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +144 -0
  206. package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +827 -0
  207. package/src/renderables/__tests__/LineNumberRenderable.test.ts +1906 -0
  208. package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +85 -0
  209. package/src/renderables/__tests__/Markdown.code-colors.test.ts +288 -0
  210. package/src/renderables/__tests__/Markdown.test.ts +4175 -0
  211. package/src/renderables/__tests__/MultiRenderable.selection.test.ts +87 -0
  212. package/src/renderables/__tests__/Textarea.buffer.test.ts +683 -0
  213. package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +675 -0
  214. package/src/renderables/__tests__/Textarea.editing.test.ts +2120 -0
  215. package/src/renderables/__tests__/Textarea.error-handling.test.ts +35 -0
  216. package/src/renderables/__tests__/Textarea.events.test.ts +738 -0
  217. package/src/renderables/__tests__/Textarea.highlights.test.ts +590 -0
  218. package/src/renderables/__tests__/Textarea.keybinding.test.ts +3275 -0
  219. package/src/renderables/__tests__/Textarea.paste.test.ts +357 -0
  220. package/src/renderables/__tests__/Textarea.rendering.test.ts +1927 -0
  221. package/src/renderables/__tests__/Textarea.scroll.test.ts +733 -0
  222. package/src/renderables/__tests__/Textarea.selection.test.ts +1589 -0
  223. package/src/renderables/__tests__/Textarea.stress.test.ts +670 -0
  224. package/src/renderables/__tests__/Textarea.undo-redo.test.ts +383 -0
  225. package/src/renderables/__tests__/Textarea.visual-lines.test.ts +310 -0
  226. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +221 -0
  227. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +89 -0
  228. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +457 -0
  229. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +158 -0
  230. package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +387 -0
  231. package/src/renderables/__tests__/markdown-parser.test.ts +241 -0
  232. package/src/renderables/__tests__/renderable-test-utils.ts +60 -0
  233. package/src/renderables/composition/README.md +8 -0
  234. package/src/renderables/composition/VRenderable.ts +32 -0
  235. package/src/renderables/composition/constructs.ts +127 -0
  236. package/src/renderables/composition/vnode.ts +277 -0
  237. package/src/renderables/index.ts +23 -0
  238. package/src/renderables/markdown-parser.ts +79 -0
  239. package/src/renderer-theme-mode.ts +199 -0
  240. package/src/renderer.ts +4975 -0
  241. package/src/runtime-plugin-support-configure.ts +63 -0
  242. package/src/runtime-plugin-support.ts +13 -0
  243. package/src/runtime-plugin.ts +626 -0
  244. package/src/specs/terminal-startup.md +64 -0
  245. package/src/syntax-style.test.ts +841 -0
  246. package/src/syntax-style.ts +274 -0
  247. package/src/testing/README.md +219 -0
  248. package/src/testing/bun-test-node.ts +880 -0
  249. package/src/testing/capture-spans.test.ts +216 -0
  250. package/src/testing/integration.test.ts +276 -0
  251. package/src/testing/manual-clock.ts +117 -0
  252. package/src/testing/mock-keys.test.ts +1378 -0
  253. package/src/testing/mock-keys.ts +457 -0
  254. package/src/testing/mock-mouse.test.ts +218 -0
  255. package/src/testing/mock-mouse.ts +247 -0
  256. package/src/testing/mock-tree-sitter-client.ts +81 -0
  257. package/src/testing/spy.ts +13 -0
  258. package/src/testing/terminal-capabilities.ts +47 -0
  259. package/src/testing/test-recorder.test.ts +410 -0
  260. package/src/testing/test-recorder.ts +133 -0
  261. package/src/testing/test-renderer.ts +375 -0
  262. package/src/testing/test-renderer.wait.test.ts +278 -0
  263. package/src/testing/test-streams.ts +31 -0
  264. package/src/testing.ts +9 -0
  265. package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +481 -0
  266. package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +19 -0
  267. package/src/tests/__snapshots__/scrollbox.test.ts.snap +29 -0
  268. package/src/tests/absolute-positioning.snapshot.test.ts +638 -0
  269. package/src/tests/allocator-stats.test.ts +53 -0
  270. package/src/tests/audio.test.ts +303 -0
  271. package/src/tests/destroy-during-render.test.ts +200 -0
  272. package/src/tests/destroy-on-exit.fixture.ts +36 -0
  273. package/src/tests/destroy-on-exit.test.ts +62 -0
  274. package/src/tests/ffi-borrowed-pointer-callsites.test.ts +153 -0
  275. package/src/tests/hover-cursor.test.ts +98 -0
  276. package/src/tests/native-backed-measurement-lifecycle.test.ts +133 -0
  277. package/src/tests/native-backed-measurement-parity.test.ts +322 -0
  278. package/src/tests/native-span-feed-async.test.ts +173 -0
  279. package/src/tests/native-span-feed-close.test.ts +120 -0
  280. package/src/tests/native-span-feed-coverage.test.ts +227 -0
  281. package/src/tests/native-span-feed-edge-cases.test.ts +384 -0
  282. package/src/tests/native-span-feed-use-after-free.test.ts +45 -0
  283. package/src/tests/opacity.test.ts +123 -0
  284. package/src/tests/renderable.snapshot.test.ts +524 -0
  285. package/src/tests/renderable.test.ts +1851 -0
  286. package/src/tests/renderer.clock.test.ts +270 -0
  287. package/src/tests/renderer.console-startup.test.ts +2151 -0
  288. package/src/tests/renderer.control.test.ts +458 -0
  289. package/src/tests/renderer.core-slot-binding.test.ts +962 -0
  290. package/src/tests/renderer.cursor.test.ts +26 -0
  291. package/src/tests/renderer.custom-stdout.test.ts +1112 -0
  292. package/src/tests/renderer.destroy-during-render.test.ts +147 -0
  293. package/src/tests/renderer.focus-restore.test.ts +257 -0
  294. package/src/tests/renderer.focus.test.ts +329 -0
  295. package/src/tests/renderer.idle.test.ts +219 -0
  296. package/src/tests/renderer.input.test.ts +2615 -0
  297. package/src/tests/renderer.kitty-flags.test.ts +195 -0
  298. package/src/tests/renderer.lifecycle.test.ts +34 -0
  299. package/src/tests/renderer.mouse.test.ts +1315 -0
  300. package/src/tests/renderer.notifications.test.ts +27 -0
  301. package/src/tests/renderer.palette.test.ts +1199 -0
  302. package/src/tests/renderer.remote.test.ts +247 -0
  303. package/src/tests/renderer.render-stats.test.ts +146 -0
  304. package/src/tests/renderer.scrollback-surface.test.ts +474 -0
  305. package/src/tests/renderer.selection.test.ts +138 -0
  306. package/src/tests/renderer.slot-registry.test.ts +684 -0
  307. package/src/tests/renderer.tracker.test.ts +202 -0
  308. package/src/tests/renderer.useMouse.test.ts +83 -0
  309. package/src/tests/runtime-plugin-node-modules-cycle.fixture.ts +76 -0
  310. package/src/tests/runtime-plugin-node-modules-import-like-string.fixture.ts +48 -0
  311. package/src/tests/runtime-plugin-node-modules-mjs.fixture.ts +43 -0
  312. package/src/tests/runtime-plugin-node-modules-no-bare-rewrite.fixture.ts +67 -0
  313. package/src/tests/runtime-plugin-node-modules-package-type-cache.fixture.ts +72 -0
  314. package/src/tests/runtime-plugin-node-modules-runtime-specifier.fixture.ts +44 -0
  315. package/src/tests/runtime-plugin-node-modules-scoped-package-bare-rewrite.fixture.ts +85 -0
  316. package/src/tests/runtime-plugin-path-alias.fixture.ts +43 -0
  317. package/src/tests/runtime-plugin-resolve-roots.fixture.ts +65 -0
  318. package/src/tests/runtime-plugin-support-configure.fixture.ts +31 -0
  319. package/src/tests/runtime-plugin-support-late-addition.fixture.ts +17 -0
  320. package/src/tests/runtime-plugin-support.fixture.ts +11 -0
  321. package/src/tests/runtime-plugin-support.test.ts +54 -0
  322. package/src/tests/runtime-plugin-windows-file-url.fixture.ts +30 -0
  323. package/src/tests/runtime-plugin.fixture.ts +40 -0
  324. package/src/tests/runtime-plugin.test.ts +369 -0
  325. package/src/tests/scrollbox-culling-bug.test.ts +114 -0
  326. package/src/tests/scrollbox-hitgrid-resize.test.ts +136 -0
  327. package/src/tests/scrollbox-hitgrid.test.ts +909 -0
  328. package/src/tests/scrollbox.test.ts +1895 -0
  329. package/src/tests/wrap-resize-perf.test.ts +276 -0
  330. package/src/tests/yoga-api-coverage.test.ts +409 -0
  331. package/src/tests/yoga-callback-stress.test.ts +122 -0
  332. package/src/tests/yoga-ffi.test.ts +117 -0
  333. package/src/tests/yoga-setters.test.ts +921 -0
  334. package/src/tests/yoga-upstream/YGAlignBaselineTest.test.ts +124 -0
  335. package/src/tests/yoga-upstream/YGComputedBorderTest.test.ts +27 -0
  336. package/src/tests/yoga-upstream/YGComputedMarginTest.test.ts +27 -0
  337. package/src/tests/yoga-upstream/YGComputedPaddingTest.test.ts +27 -0
  338. package/src/tests/yoga-upstream/YGDirtiedTest.test.ts +157 -0
  339. package/src/tests/yoga-upstream/YGErrataTest.test.ts +40 -0
  340. package/src/tests/yoga-upstream/YGFlexBasisAuto.test.ts +23 -0
  341. package/src/tests/yoga-upstream/YGHasNewLayout.test.ts +81 -0
  342. package/src/tests/yoga-upstream/YGMeasureCacheTest.test.ts +31 -0
  343. package/src/tests/yoga-upstream/YGMeasureTest.test.ts +70 -0
  344. package/src/tests/yoga-upstream/generated/YGAbsolutePositionTest.test.ts +1864 -0
  345. package/src/tests/yoga-upstream/generated/YGAlignContentTest.test.ts +5829 -0
  346. package/src/tests/yoga-upstream/generated/YGAlignItemsTest.test.ts +2444 -0
  347. package/src/tests/yoga-upstream/generated/YGAlignSelfTest.test.ts +293 -0
  348. package/src/tests/yoga-upstream/generated/YGAndroidNewsFeed.test.ts +315 -0
  349. package/src/tests/yoga-upstream/generated/YGAspectRatioTest.test.ts +231 -0
  350. package/src/tests/yoga-upstream/generated/YGAutoTest.test.ts +347 -0
  351. package/src/tests/yoga-upstream/generated/YGBorderTest.test.ts +256 -0
  352. package/src/tests/yoga-upstream/generated/YGBoxSizingTest.test.ts +2747 -0
  353. package/src/tests/yoga-upstream/generated/YGDimensionTest.test.ts +129 -0
  354. package/src/tests/yoga-upstream/generated/YGDisplayContentsTest.test.ts +109 -0
  355. package/src/tests/yoga-upstream/generated/YGDisplayTest.test.ts +1097 -0
  356. package/src/tests/yoga-upstream/generated/YGFlexDirectionTest.test.ts +4562 -0
  357. package/src/tests/yoga-upstream/generated/YGFlexTest.test.ts +677 -0
  358. package/src/tests/yoga-upstream/generated/YGFlexWrapTest.test.ts +2071 -0
  359. package/src/tests/yoga-upstream/generated/YGGapTest.test.ts +3303 -0
  360. package/src/tests/yoga-upstream/generated/YGIntrinsicSizeTest.test.ts +582 -0
  361. package/src/tests/yoga-upstream/generated/YGJustifyContentTest.test.ts +2150 -0
  362. package/src/tests/yoga-upstream/generated/YGMarginTest.test.ts +1963 -0
  363. package/src/tests/yoga-upstream/generated/YGMinMaxDimensionTest.test.ts +1446 -0
  364. package/src/tests/yoga-upstream/generated/YGPaddingTest.test.ts +354 -0
  365. package/src/tests/yoga-upstream/generated/YGPercentageTest.test.ts +1760 -0
  366. package/src/tests/yoga-upstream/generated/YGRoundingTest.test.ts +1161 -0
  367. package/src/tests/yoga-upstream/generated/YGSizeOverflowTest.test.ts +207 -0
  368. package/src/tests/yoga-upstream/generated/YGStaticPositionTest.test.ts +6338 -0
  369. package/src/tests/yoga-upstream/tools/MeasureCounter.ts +54 -0
  370. package/src/tests/yoga-upstream/tools/utils.ts +81 -0
  371. package/src/text-buffer-view.test.ts +722 -0
  372. package/src/text-buffer-view.ts +193 -0
  373. package/src/text-buffer.test.ts +512 -0
  374. package/src/text-buffer.ts +250 -0
  375. package/src/types.ts +199 -0
  376. package/src/utils.ts +88 -0
  377. package/src/yoga.ts +1146 -0
  378. package/src/zig/ansi.zig +502 -0
  379. package/src/zig/audio.zig +1206 -0
  380. package/src/zig/bench/README.md +50 -0
  381. package/src/zig/bench/buffer-color-blending_bench.zig +350 -0
  382. package/src/zig/bench/buffer-draw-box_bench.zig +786 -0
  383. package/src/zig/bench/buffer-draw-text-buffer_bench.zig +894 -0
  384. package/src/zig/bench/edit-buffer_bench.zig +476 -0
  385. package/src/zig/bench/editor-view_bench.zig +169 -0
  386. package/src/zig/bench/native-span-feed_bench.zig +107 -0
  387. package/src/zig/bench/rope-markers_bench.zig +713 -0
  388. package/src/zig/bench/rope_bench.zig +514 -0
  389. package/src/zig/bench/styled-text_bench.zig +476 -0
  390. package/src/zig/bench/text-buffer-coords_bench.zig +362 -0
  391. package/src/zig/bench/text-buffer-view_bench.zig +459 -0
  392. package/src/zig/bench/text-chunk-graphemes_bench.zig +272 -0
  393. package/src/zig/bench/utf8_bench.zig +799 -0
  394. package/src/zig/bench-utils.zig +431 -0
  395. package/src/zig/bench.zig +223 -0
  396. package/src/zig/buffer-methods.zig +215 -0
  397. package/src/zig/buffer.zig +2515 -0
  398. package/src/zig/build.zig +700 -0
  399. package/src/zig/build.zig.zon +19 -0
  400. package/src/zig/edit-buffer.zig +900 -0
  401. package/src/zig/editor-view.zig +812 -0
  402. package/src/zig/event-bus.zig +28 -0
  403. package/src/zig/event-emitter.zig +65 -0
  404. package/src/zig/file-logger.zig +92 -0
  405. package/src/zig/grapheme.zig +838 -0
  406. package/src/zig/handles.zig +267 -0
  407. package/src/zig/lib.zig +2813 -0
  408. package/src/zig/link.zig +349 -0
  409. package/src/zig/logger.zig +43 -0
  410. package/src/zig/mem-registry.zig +126 -0
  411. package/src/zig/miniaudio_shim.c +21 -0
  412. package/src/zig/native-renderable.zig +112 -0
  413. package/src/zig/native-span-feed-bench-lib.zig +7 -0
  414. package/src/zig/native-span-feed.zig +747 -0
  415. package/src/zig/renderer-output.zig +677 -0
  416. package/src/zig/renderer.zig +2106 -0
  417. package/src/zig/rope.zig +1220 -0
  418. package/src/zig/split-scrollback.zig +88 -0
  419. package/src/zig/syntax-style.zig +174 -0
  420. package/src/zig/terminal.zig +1652 -0
  421. package/src/zig/test.zig +82 -0
  422. package/src/zig/tests/README.md +18 -0
  423. package/src/zig/tests/ansi_test.zig +18 -0
  424. package/src/zig/tests/audio_test.zig +439 -0
  425. package/src/zig/tests/buffer-methods_test.zig +1111 -0
  426. package/src/zig/tests/buffer_test.zig +2926 -0
  427. package/src/zig/tests/edit-buffer-history_test.zig +299 -0
  428. package/src/zig/tests/edit-buffer_test.zig +1688 -0
  429. package/src/zig/tests/editor-view_test.zig +3454 -0
  430. package/src/zig/tests/event-emitter_test.zig +249 -0
  431. package/src/zig/tests/grapheme_test.zig +1329 -0
  432. package/src/zig/tests/handles_test.zig +123 -0
  433. package/src/zig/tests/link_test.zig +220 -0
  434. package/src/zig/tests/mem-registry_test.zig +473 -0
  435. package/src/zig/tests/memory_leak_regression_test.zig +234 -0
  436. package/src/zig/tests/native-renderable_test.zig +16 -0
  437. package/src/zig/tests/native-span-feed_test.zig +1266 -0
  438. package/src/zig/tests/renderer_test.zig +2604 -0
  439. package/src/zig/tests/rope-nested_test.zig +712 -0
  440. package/src/zig/tests/rope_fuzz_test.zig +238 -0
  441. package/src/zig/tests/rope_test.zig +2362 -0
  442. package/src/zig/tests/segment-merge.test.zig +148 -0
  443. package/src/zig/tests/split-scrollback_test.zig +70 -0
  444. package/src/zig/tests/syntax-style_test.zig +558 -0
  445. package/src/zig/tests/terminal_test.zig +1489 -0
  446. package/src/zig/tests/test-renderer.zig +126 -0
  447. package/src/zig/tests/text-buffer-drawing_test.zig +3291 -0
  448. package/src/zig/tests/text-buffer-highlights_test.zig +706 -0
  449. package/src/zig/tests/text-buffer-iterators_test.zig +776 -0
  450. package/src/zig/tests/text-buffer-segment_test.zig +320 -0
  451. package/src/zig/tests/text-buffer-selection_test.zig +1056 -0
  452. package/src/zig/tests/text-buffer-selection_viewport_test.zig +358 -0
  453. package/src/zig/tests/text-buffer-view_test.zig +3672 -0
  454. package/src/zig/tests/text-buffer_test.zig +2191 -0
  455. package/src/zig/tests/unicode-width-map.zon +3909 -0
  456. package/src/zig/tests/utf8_no_zwj_test.zig +260 -0
  457. package/src/zig/tests/utf8_test.zig +4060 -0
  458. package/src/zig/tests/utf8_wcwidth_cursor_test.zig +267 -0
  459. package/src/zig/tests/utf8_wcwidth_test.zig +357 -0
  460. package/src/zig/tests/word-wrap-editing_test.zig +498 -0
  461. package/src/zig/tests/wrap-cache-perf_test.zig +113 -0
  462. package/src/zig/tests/yoga_test.zig +65 -0
  463. package/src/zig/text-buffer-iterators.zig +496 -0
  464. package/src/zig/text-buffer-segment.zig +403 -0
  465. package/src/zig/text-buffer-view.zig +1428 -0
  466. package/src/zig/text-buffer.zig +1272 -0
  467. package/src/zig/utf8.zig +1954 -0
  468. package/src/zig/utils.zig +12 -0
  469. package/src/zig/uucode-stub.zig +76 -0
  470. package/src/zig/vendor/miniaudio/miniaudio.h +93468 -0
  471. package/src/zig/yoga.zig +658 -0
  472. package/src/zig-structs.ts +381 -0
  473. package/src/zig.ts +5260 -0
  474. package/tsconfig.build.json +17 -0
  475. package/tsconfig.json +27 -0
  476. package/tsconfig.node-test.json +138 -0
@@ -0,0 +1,193 @@
1
+ import { RGBA } from "./lib/RGBA.js"
2
+ import { resolveRenderLib, type LineInfo, type RenderLib, type TextBufferViewHandle } from "./zig.js"
3
+ import type { TextBuffer } from "./text-buffer.js"
4
+
5
+ export class TextBufferView {
6
+ private lib: RenderLib
7
+ private viewPtr: TextBufferViewHandle
8
+ private textBuffer: TextBuffer
9
+ private _destroyed: boolean = false
10
+
11
+ constructor(lib: RenderLib, ptr: TextBufferViewHandle, textBuffer: TextBuffer) {
12
+ this.lib = lib
13
+ this.viewPtr = ptr
14
+ this.textBuffer = textBuffer
15
+ }
16
+
17
+ static create(textBuffer: TextBuffer): TextBufferView {
18
+ const lib = resolveRenderLib()
19
+ const viewPtr = lib.createTextBufferView(textBuffer.ptr)
20
+ return new TextBufferView(lib, viewPtr, textBuffer)
21
+ }
22
+
23
+ // Fail loud and clear
24
+ private guard(): void {
25
+ if (this._destroyed) throw new Error("TextBufferView is destroyed")
26
+ }
27
+
28
+ public get ptr(): TextBufferViewHandle {
29
+ this.guard()
30
+ return this.viewPtr
31
+ }
32
+
33
+ public setSelection(start: number, end: number, bgColor?: RGBA, fgColor?: RGBA): void {
34
+ this.guard()
35
+ this.lib.textBufferViewSetSelection(this.viewPtr, start, end, bgColor || null, fgColor || null)
36
+ }
37
+
38
+ public updateSelection(end: number, bgColor?: RGBA, fgColor?: RGBA): void {
39
+ this.guard()
40
+ this.lib.textBufferViewUpdateSelection(this.viewPtr, end, bgColor || null, fgColor || null)
41
+ }
42
+
43
+ public resetSelection(): void {
44
+ this.guard()
45
+ this.lib.textBufferViewResetSelection(this.viewPtr)
46
+ }
47
+
48
+ public getSelection(): { start: number; end: number } | null {
49
+ this.guard()
50
+ return this.lib.textBufferViewGetSelection(this.viewPtr)
51
+ }
52
+
53
+ public hasSelection(): boolean {
54
+ this.guard()
55
+ return this.getSelection() !== null
56
+ }
57
+
58
+ public setLocalSelection(
59
+ anchorX: number,
60
+ anchorY: number,
61
+ focusX: number,
62
+ focusY: number,
63
+ bgColor?: RGBA,
64
+ fgColor?: RGBA,
65
+ ): boolean {
66
+ this.guard()
67
+ return this.lib.textBufferViewSetLocalSelection(
68
+ this.viewPtr,
69
+ anchorX,
70
+ anchorY,
71
+ focusX,
72
+ focusY,
73
+ bgColor || null,
74
+ fgColor || null,
75
+ )
76
+ }
77
+
78
+ public updateLocalSelection(
79
+ anchorX: number,
80
+ anchorY: number,
81
+ focusX: number,
82
+ focusY: number,
83
+ bgColor?: RGBA,
84
+ fgColor?: RGBA,
85
+ ): boolean {
86
+ this.guard()
87
+ return this.lib.textBufferViewUpdateLocalSelection(
88
+ this.viewPtr,
89
+ anchorX,
90
+ anchorY,
91
+ focusX,
92
+ focusY,
93
+ bgColor || null,
94
+ fgColor || null,
95
+ )
96
+ }
97
+
98
+ public resetLocalSelection(): void {
99
+ this.guard()
100
+ this.lib.textBufferViewResetLocalSelection(this.viewPtr)
101
+ }
102
+
103
+ public setWrapWidth(width: number | null): void {
104
+ this.guard()
105
+ this.lib.textBufferViewSetWrapWidth(this.viewPtr, width ?? 0)
106
+ }
107
+
108
+ public setWrapMode(mode: "none" | "char" | "word"): void {
109
+ this.guard()
110
+ this.lib.textBufferViewSetWrapMode(this.viewPtr, mode)
111
+ }
112
+
113
+ public setFirstLineOffset(offset: number): void {
114
+ this.guard()
115
+ this.lib.textBufferViewSetFirstLineOffset(this.viewPtr, offset)
116
+ }
117
+
118
+ public setViewportSize(width: number, height: number): void {
119
+ this.guard()
120
+ this.lib.textBufferViewSetViewportSize(this.viewPtr, width, height)
121
+ }
122
+
123
+ public setViewport(x: number, y: number, width: number, height: number): void {
124
+ this.guard()
125
+ this.lib.textBufferViewSetViewport(this.viewPtr, x, y, width, height)
126
+ }
127
+
128
+ public get lineInfo(): LineInfo {
129
+ this.guard()
130
+ return this.lib.textBufferViewGetLineInfo(this.viewPtr)
131
+ }
132
+
133
+ public get logicalLineInfo(): LineInfo {
134
+ this.guard()
135
+ return this.lib.textBufferViewGetLogicalLineInfo(this.viewPtr)
136
+ }
137
+
138
+ public getSelectedText(): string {
139
+ this.guard()
140
+ const byteSize = this.textBuffer.byteSize
141
+ if (byteSize === 0) return ""
142
+
143
+ const selectedBytes = this.lib.textBufferViewGetSelectedTextBytes(this.viewPtr, byteSize)
144
+
145
+ if (!selectedBytes) return ""
146
+
147
+ return this.lib.decoder.decode(selectedBytes)
148
+ }
149
+
150
+ public getPlainText(): string {
151
+ this.guard()
152
+ const byteSize = this.textBuffer.byteSize
153
+ if (byteSize === 0) return ""
154
+
155
+ const plainBytes = this.lib.textBufferViewGetPlainTextBytes(this.viewPtr, byteSize)
156
+
157
+ if (!plainBytes) return ""
158
+
159
+ return this.lib.decoder.decode(plainBytes)
160
+ }
161
+
162
+ public setTabIndicator(indicator: string | number): void {
163
+ this.guard()
164
+ const codePoint = typeof indicator === "string" ? (indicator.codePointAt(0) ?? 0) : indicator
165
+ this.lib.textBufferViewSetTabIndicator(this.viewPtr, codePoint)
166
+ }
167
+
168
+ public setTabIndicatorColor(color: RGBA): void {
169
+ this.guard()
170
+ this.lib.textBufferViewSetTabIndicatorColor(this.viewPtr, color)
171
+ }
172
+
173
+ public setTruncate(truncate: boolean): void {
174
+ this.guard()
175
+ this.lib.textBufferViewSetTruncate(this.viewPtr, truncate)
176
+ }
177
+
178
+ public measureForDimensions(width: number, height: number): { lineCount: number; widthColsMax: number } | null {
179
+ this.guard()
180
+ return this.lib.textBufferViewMeasureForDimensions(this.viewPtr, width, height)
181
+ }
182
+
183
+ public getVirtualLineCount(): number {
184
+ this.guard()
185
+ return this.lib.textBufferViewGetVirtualLineCount(this.viewPtr)
186
+ }
187
+
188
+ public destroy(): void {
189
+ if (this._destroyed) return
190
+ this._destroyed = true
191
+ this.lib.destroyTextBufferView(this.viewPtr)
192
+ }
193
+ }
@@ -0,0 +1,512 @@
1
+ import { describe, expect, it, beforeEach, afterEach } from "bun:test"
2
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs"
3
+ import { tmpdir } from "node:os"
4
+ import { join } from "node:path"
5
+ import { TextBuffer } from "./text-buffer.js"
6
+ import { resolveRenderLib } from "./zig.js"
7
+ import { StyledText, stringToStyledText } from "./lib/styled-text.js"
8
+ import { RGBA } from "./lib/RGBA.js"
9
+ import { SyntaxStyle } from "./syntax-style.js"
10
+
11
+ const MALFORMED_UTF8_ABOVE_UNICODE_RANGE = new Uint8Array([0x41, 0xf4, 0x90, 0x80, 0x80, 0x42])
12
+ const MALFORMED_UTF8_TEXT = "A" + "\uFFFD".repeat(4) + "B"
13
+
14
+ describe("TextBuffer", () => {
15
+ let buffer: TextBuffer
16
+
17
+ beforeEach(() => {
18
+ buffer = TextBuffer.create("wcwidth")
19
+ })
20
+
21
+ afterEach(() => {
22
+ buffer.destroy()
23
+ })
24
+
25
+ describe("setText and setStyledText", () => {
26
+ it("should set text content", () => {
27
+ const text = "Hello World"
28
+ buffer.setText(text)
29
+
30
+ expect(buffer.length).toBe(11)
31
+ expect(buffer.byteSize).toBeGreaterThan(0)
32
+ })
33
+
34
+ it("should set styled text", () => {
35
+ const styledText = stringToStyledText("Hello World")
36
+ buffer.setStyledText(styledText)
37
+
38
+ expect(buffer.length).toBe(11)
39
+ })
40
+
41
+ it("should handle empty text", () => {
42
+ buffer.setText("")
43
+
44
+ expect(buffer.length).toBe(0)
45
+ expect(buffer.getPlainText()).toBe("")
46
+ })
47
+
48
+ it("should replace existing text with empty text", () => {
49
+ buffer.setText("Hello World")
50
+ buffer.setText("")
51
+
52
+ expect(buffer.length).toBe(0)
53
+ expect(buffer.getPlainText()).toBe("")
54
+ })
55
+
56
+ it("should handle empty styled text", () => {
57
+ const emptyText = stringToStyledText("")
58
+ buffer.setStyledText(emptyText)
59
+
60
+ expect(buffer.length).toBe(0)
61
+ })
62
+
63
+ it("should handle text with newlines", () => {
64
+ const text = "Line 1\nLine 2\nLine 3"
65
+ buffer.setText(text)
66
+
67
+ expect(buffer.length).toBe(18) // 6 + 6 + 6 chars (newlines not counted)
68
+ })
69
+ })
70
+
71
+ describe("getPlainText", () => {
72
+ it("should return empty string for empty buffer", () => {
73
+ const emptyText = stringToStyledText("")
74
+ buffer.setStyledText(emptyText)
75
+
76
+ const plainText = buffer.getPlainText()
77
+ expect(plainText).toBe("")
78
+ })
79
+
80
+ it("should return plain text without styling", () => {
81
+ const styledText = stringToStyledText("Hello World")
82
+ buffer.setStyledText(styledText)
83
+
84
+ const plainText = buffer.getPlainText()
85
+ expect(plainText).toBe("Hello World")
86
+ })
87
+
88
+ it("should handle text with newlines", () => {
89
+ const styledText = stringToStyledText("Line 1\nLine 2\nLine 3")
90
+ buffer.setStyledText(styledText)
91
+
92
+ const plainText = buffer.getPlainText()
93
+ expect(plainText).toBe("Line 1\nLine 2\nLine 3")
94
+ })
95
+
96
+ it("should handle Unicode characters correctly", () => {
97
+ const styledText = stringToStyledText("Hello δΈ–η•Œ 🌟")
98
+ buffer.setStyledText(styledText)
99
+
100
+ const plainText = buffer.getPlainText()
101
+ expect(plainText).toBe("Hello δΈ–η•Œ 🌟")
102
+ })
103
+
104
+ it("should handle styled text with colors and attributes", () => {
105
+ const redChunk = {
106
+ __isChunk: true as const,
107
+ text: "Red",
108
+ fg: RGBA.fromValues(1, 0, 0, 1),
109
+ }
110
+ const newlineChunk = {
111
+ __isChunk: true as const,
112
+ text: "\n",
113
+ }
114
+ const blueChunk = {
115
+ __isChunk: true as const,
116
+ text: "Blue",
117
+ fg: RGBA.fromValues(0, 0, 1, 1),
118
+ }
119
+
120
+ const styledText = new StyledText([redChunk, newlineChunk, blueChunk])
121
+ buffer.setStyledText(styledText)
122
+
123
+ const plainText = buffer.getPlainText()
124
+ expect(plainText).toBe("Red\nBlue")
125
+ })
126
+
127
+ it("should return null bytes for zero-length plain-text output buffer", () => {
128
+ buffer.setText("Hello World")
129
+
130
+ const plainBytes = (buffer as any).lib.getPlainTextBytes(buffer.ptr, 0)
131
+
132
+ expect(plainBytes).toBeNull()
133
+ })
134
+ })
135
+
136
+ describe("getTextRange", () => {
137
+ it("should return null bytes for zero-length range output buffers", () => {
138
+ buffer.setText("Hello World")
139
+
140
+ const lib = (buffer as any).lib
141
+
142
+ expect(lib.textBufferGetTextRange(buffer.ptr, 0, 5, 0)).toBeNull()
143
+ expect(lib.textBufferGetTextRangeByCoords(buffer.ptr, 0, 0, 0, 5, 0)).toBeNull()
144
+ })
145
+ })
146
+
147
+ describe("line highlights", () => {
148
+ it("should return an empty list when a line has no highlights", () => {
149
+ buffer.setText("Hello\nWorld")
150
+
151
+ expect(buffer.getLineHighlights(0)).toEqual([])
152
+ expect(buffer.getLineHighlights(1)).toEqual([])
153
+ })
154
+
155
+ it("should round-trip line highlights through the native highlight buffer", () => {
156
+ const style = SyntaxStyle.create()
157
+
158
+ try {
159
+ const styleId = style.registerStyle("highlight", {
160
+ fg: RGBA.fromValues(1, 0, 0, 1),
161
+ })
162
+
163
+ buffer.setSyntaxStyle(style)
164
+ buffer.setText("Hello World")
165
+ buffer.addHighlight(0, { start: 0, end: 5, styleId, priority: 7, hlRef: 42 })
166
+
167
+ expect(buffer.getLineHighlights(0)).toEqual([{ start: 0, end: 5, styleId, priority: 7, hlRef: 42 }])
168
+ } finally {
169
+ style.destroy()
170
+ }
171
+ })
172
+ })
173
+
174
+ describe("length property", () => {
175
+ it("should return correct length for simple text", () => {
176
+ const styledText = stringToStyledText("Hello World")
177
+ buffer.setStyledText(styledText)
178
+
179
+ expect(buffer.length).toBe(11)
180
+ })
181
+
182
+ it("should return 0 for empty buffer", () => {
183
+ const emptyText = stringToStyledText("")
184
+ buffer.setStyledText(emptyText)
185
+
186
+ expect(buffer.length).toBe(0)
187
+ })
188
+
189
+ it("should handle text with newlines correctly", () => {
190
+ const styledText = stringToStyledText("Line 1\nLine 2\nLine 3")
191
+ buffer.setStyledText(styledText)
192
+
193
+ expect(buffer.length).toBe(18) // 6 + 6 + 6 chars (newlines not counted)
194
+ })
195
+
196
+ it("should handle Unicode characters correctly", () => {
197
+ const styledText = stringToStyledText("Hello δΈ–η•Œ 🌟")
198
+ buffer.setStyledText(styledText)
199
+
200
+ expect(buffer.length).toBe(13)
201
+ })
202
+ })
203
+
204
+ describe("default styles", () => {
205
+ it("should set and reset default foreground color", () => {
206
+ const fg = RGBA.fromValues(1, 0, 0, 1)
207
+ buffer.setDefaultFg(fg)
208
+ buffer.resetDefaults()
209
+
210
+ // No error should be thrown
211
+ expect(true).toBe(true)
212
+ })
213
+
214
+ it("should set and reset default background color", () => {
215
+ const bg = RGBA.fromValues(0, 0, 1, 1)
216
+ buffer.setDefaultBg(bg)
217
+ buffer.resetDefaults()
218
+
219
+ // No error should be thrown
220
+ expect(true).toBe(true)
221
+ })
222
+
223
+ it("should set and reset default attributes", () => {
224
+ buffer.setDefaultAttributes(1)
225
+ buffer.resetDefaults()
226
+
227
+ // No error should be thrown
228
+ expect(true).toBe(true)
229
+ })
230
+ })
231
+
232
+ describe("clear() vs reset()", () => {
233
+ it("clear() should empty buffer but preserve text across setText calls", () => {
234
+ // Set initial text
235
+ buffer.setText("First text")
236
+ expect(buffer.length).toBe(10)
237
+
238
+ // Set new text (which calls clear() internally)
239
+ buffer.setText("Second text")
240
+ expect(buffer.length).toBe(11)
241
+ expect(buffer.getPlainText()).toBe("Second text")
242
+
243
+ // Explicit clear
244
+ buffer.clear()
245
+ expect(buffer.length).toBe(0)
246
+ expect(buffer.getPlainText()).toBe("")
247
+ })
248
+
249
+ it("reset() should fully reset the buffer", () => {
250
+ buffer.setText("Some text")
251
+ expect(buffer.length).toBe(9)
252
+
253
+ buffer.reset()
254
+ expect(buffer.length).toBe(0)
255
+ expect(buffer.getPlainText()).toBe("")
256
+
257
+ // Should be able to use buffer after reset
258
+ buffer.setText("New text")
259
+ expect(buffer.length).toBe(8)
260
+ })
261
+
262
+ it("setText should preserve highlights (use clear() not reset())", () => {
263
+ // This test verifies that setText now uses clear() internally
264
+ // and doesn't clear highlights
265
+ buffer.setText("Hello World")
266
+
267
+ // Note: We can't easily test highlight preservation from TypeScript
268
+ // without a SyntaxStyle, but we verify the buffer still works
269
+ expect(buffer.length).toBe(11)
270
+
271
+ buffer.setText("New Text")
272
+ expect(buffer.length).toBe(8)
273
+ expect(buffer.getPlainText()).toBe("New Text")
274
+ })
275
+
276
+ it("setText should clear styled text chunk highlights", () => {
277
+ const syntaxStyle = SyntaxStyle.create()
278
+ buffer.setSyntaxStyle(syntaxStyle)
279
+ buffer.setStyledText(
280
+ new StyledText([
281
+ {
282
+ __isChunk: true,
283
+ text: "Styled",
284
+ fg: RGBA.fromValues(1, 0, 0, 1),
285
+ },
286
+ ]),
287
+ )
288
+
289
+ expect(buffer.getHighlightCount()).toBe(1)
290
+
291
+ buffer.setText("Plain")
292
+
293
+ expect(buffer.getPlainText()).toBe("Plain")
294
+ expect(buffer.getHighlightCount()).toBe(0)
295
+
296
+ syntaxStyle.destroy()
297
+ })
298
+
299
+ it("setText should preserve user highlights including max hlRef", () => {
300
+ const syntaxStyle = SyntaxStyle.create()
301
+ const styleId = syntaxStyle.registerStyle("user-highlight", { fg: RGBA.fromValues(0, 1, 0, 1) })
302
+ buffer.setSyntaxStyle(syntaxStyle)
303
+ buffer.setText("Hello World")
304
+ buffer.addHighlight(0, { start: 0, end: 5, styleId, priority: 0, hlRef: 65535 })
305
+
306
+ expect(buffer.getHighlightCount()).toBe(1)
307
+
308
+ buffer.setText("New Text")
309
+
310
+ expect(buffer.getPlainText()).toBe("New Text")
311
+ expect(buffer.getHighlightCount()).toBe(1)
312
+ expect(buffer.getLineHighlights(0)[0]?.hlRef).toBe(65535)
313
+
314
+ syntaxStyle.destroy()
315
+ })
316
+
317
+ it("setStyledText should preserve content across calls", () => {
318
+ const firstText = stringToStyledText("First")
319
+ buffer.setStyledText(firstText)
320
+ expect(buffer.length).toBe(5)
321
+
322
+ const secondText = stringToStyledText("Second")
323
+ buffer.setStyledText(secondText)
324
+ expect(buffer.length).toBe(6)
325
+ expect(buffer.getPlainText()).toBe("Second")
326
+ })
327
+
328
+ it("multiple setText calls should work correctly with clear()", () => {
329
+ buffer.setText("Text 1")
330
+ expect(buffer.length).toBe(6)
331
+
332
+ buffer.setText("Text 2")
333
+ expect(buffer.length).toBe(6)
334
+
335
+ buffer.setText("Text 3")
336
+ expect(buffer.length).toBe(6)
337
+
338
+ expect(buffer.getPlainText()).toBe("Text 3")
339
+ })
340
+
341
+ it("clear() followed by setText should work", () => {
342
+ buffer.setText("Initial")
343
+ expect(buffer.length).toBe(7)
344
+
345
+ buffer.clear()
346
+ expect(buffer.length).toBe(0)
347
+
348
+ buffer.setText("After clear")
349
+ expect(buffer.length).toBe(11)
350
+ expect(buffer.getPlainText()).toBe("After clear")
351
+ })
352
+
353
+ it("reset() followed by setText should work", () => {
354
+ buffer.setText("Initial")
355
+ expect(buffer.length).toBe(7)
356
+
357
+ buffer.reset()
358
+ expect(buffer.length).toBe(0)
359
+
360
+ buffer.setText("After reset")
361
+ expect(buffer.length).toBe(11)
362
+ expect(buffer.getPlainText()).toBe("After reset")
363
+ })
364
+
365
+ it("recovers if the native memory registry entry was cleared", () => {
366
+ buffer.setText("Hello World")
367
+
368
+ resolveRenderLib().textBufferClearMemRegistry(buffer.ptr)
369
+
370
+ buffer.setText("Recovered")
371
+
372
+ expect(buffer.length).toBe(9)
373
+ expect(buffer.byteSize).toBe(9)
374
+ expect(buffer.getPlainText()).toBe("Recovered")
375
+ })
376
+ })
377
+
378
+ describe("malformed UTF-8 bytes", () => {
379
+ it("loadFile should preserve malformed UTF-8 bytes without panicking", () => {
380
+ const dir = mkdtempSync(join(process.env.OTUI_TEXT_BUFFER_TEST_TMPDIR ?? tmpdir(), "opentui-text-buffer-"))
381
+ const path = join(dir, "malformed.txt")
382
+ const unicodeBuffer = TextBuffer.create("unicode")
383
+
384
+ try {
385
+ writeFileSync(path, MALFORMED_UTF8_ABOVE_UNICODE_RANGE)
386
+
387
+ unicodeBuffer.loadFile(path)
388
+
389
+ expect(unicodeBuffer.byteSize).toBe(6)
390
+ expect(unicodeBuffer.length).toBe(2)
391
+ expect(unicodeBuffer.getLineCount()).toBe(1)
392
+ expect(unicodeBuffer.getPlainText()).toBe(MALFORMED_UTF8_TEXT)
393
+ } finally {
394
+ unicodeBuffer.destroy()
395
+ rmSync(dir, { recursive: true, force: true })
396
+ }
397
+ })
398
+
399
+ it("textBufferAppend should preserve malformed UTF-8 bytes without panicking", () => {
400
+ const lib = resolveRenderLib()
401
+ const unicodeBuffer = TextBuffer.create("unicode")
402
+
403
+ try {
404
+ lib.textBufferAppend(unicodeBuffer.ptr, MALFORMED_UTF8_ABOVE_UNICODE_RANGE)
405
+
406
+ expect(lib.textBufferGetByteSize(unicodeBuffer.ptr)).toBe(6)
407
+ expect(lib.textBufferGetLength(unicodeBuffer.ptr)).toBe(2)
408
+ } finally {
409
+ unicodeBuffer.destroy()
410
+ }
411
+ })
412
+ })
413
+
414
+ describe("append()", () => {
415
+ it("should append text to empty buffer", () => {
416
+ buffer.append("Hello")
417
+ expect(buffer.length).toBe(5)
418
+ expect(buffer.getPlainText()).toBe("Hello")
419
+ })
420
+
421
+ it("should append text to existing content", () => {
422
+ buffer.setText("Hello")
423
+ buffer.append(" World")
424
+ expect(buffer.length).toBe(11)
425
+ expect(buffer.getPlainText()).toBe("Hello World")
426
+ })
427
+
428
+ it("should append text with newlines", () => {
429
+ buffer.setText("Line 1")
430
+ buffer.append("\nLine 2")
431
+ expect(buffer.getPlainText()).toBe("Line 1\nLine 2")
432
+ })
433
+
434
+ it("should append multiple times", () => {
435
+ buffer.setText("Start")
436
+ buffer.append(" middle")
437
+ buffer.append(" end")
438
+ expect(buffer.getPlainText()).toBe("Start middle end")
439
+ })
440
+
441
+ it("should handle appending empty string", () => {
442
+ buffer.setText("Hello")
443
+ const lengthBefore = buffer.length
444
+ buffer.append("")
445
+ expect(buffer.length).toBe(lengthBefore)
446
+ expect(buffer.getPlainText()).toBe("Hello")
447
+ })
448
+
449
+ it("should append empty string to an empty buffer", () => {
450
+ buffer.append("")
451
+
452
+ expect(buffer.length).toBe(0)
453
+ expect(buffer.getPlainText()).toBe("")
454
+ })
455
+
456
+ it("should append unicode content", () => {
457
+ buffer.setText("Hello ")
458
+ buffer.append("δΈ–η•Œ 🌟")
459
+ expect(buffer.getPlainText()).toBe("Hello δΈ–η•Œ 🌟")
460
+ })
461
+
462
+ it("should handle streaming chunks", () => {
463
+ buffer.append("First")
464
+ buffer.append("\nLine2")
465
+ buffer.append("\n")
466
+ buffer.append("Line3")
467
+ buffer.append(" end")
468
+ expect(buffer.getPlainText()).toBe("First\nLine2\nLine3 end")
469
+ })
470
+
471
+ it("should handle CRLF line endings in append", () => {
472
+ buffer.append("Line1\r\n")
473
+ buffer.append("Line2\r\n")
474
+ buffer.append("Line3")
475
+ // CRLF should be normalized to LF
476
+ expect(buffer.getPlainText()).toBe("Line1\nLine2\nLine3")
477
+ })
478
+
479
+ it("should work with clear and append", () => {
480
+ buffer.setText("Initial")
481
+ buffer.clear()
482
+ buffer.append("After clear")
483
+ expect(buffer.getPlainText()).toBe("After clear")
484
+ })
485
+
486
+ it("should work with reset and append", () => {
487
+ buffer.setText("Initial")
488
+ buffer.reset()
489
+ buffer.append("After reset")
490
+ expect(buffer.getPlainText()).toBe("After reset")
491
+ })
492
+
493
+ it("should handle large streaming append", () => {
494
+ for (let i = 0; i < 100; i++) {
495
+ buffer.append(`Line ${i}\n`)
496
+ }
497
+ const result = buffer.getPlainText()
498
+ expect(result).toContain("Line 0")
499
+ expect(result).toContain("Line 99")
500
+ })
501
+
502
+ it("should mix setText and append", () => {
503
+ buffer.setText("First")
504
+ buffer.append(" appended")
505
+ expect(buffer.getPlainText()).toBe("First appended")
506
+
507
+ buffer.setText("Reset")
508
+ buffer.append(" again")
509
+ expect(buffer.getPlainText()).toBe("Reset again")
510
+ })
511
+ })
512
+ })