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