@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,954 @@
|
|
|
1
|
+
import { test, expect } from "bun:test"
|
|
2
|
+
import { TerminalPalette, normalizeTerminalPalette } from "./terminal-palette.js"
|
|
3
|
+
import { EventEmitter } from "events"
|
|
4
|
+
import { Buffer } from "node:buffer"
|
|
5
|
+
import { ManualClock } from "../testing/manual-clock.js"
|
|
6
|
+
|
|
7
|
+
class MockStream extends EventEmitter {
|
|
8
|
+
isTTY = true
|
|
9
|
+
isRaw = false
|
|
10
|
+
isPaused() {
|
|
11
|
+
return false
|
|
12
|
+
}
|
|
13
|
+
write(_data: string) {
|
|
14
|
+
return true
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function createPaletteHarness(
|
|
19
|
+
options: {
|
|
20
|
+
writeFn?: (data: string | Buffer) => boolean
|
|
21
|
+
isLegacyTmux?: boolean
|
|
22
|
+
isTmux?: boolean
|
|
23
|
+
oscSource?: {
|
|
24
|
+
subscribeOsc(handler: (sequence: string) => void): () => void
|
|
25
|
+
}
|
|
26
|
+
} = {},
|
|
27
|
+
) {
|
|
28
|
+
const stdin = new MockStream() as any
|
|
29
|
+
const stdout = new MockStream() as any
|
|
30
|
+
const clock = new ManualClock()
|
|
31
|
+
const palette = new TerminalPalette({
|
|
32
|
+
stdin,
|
|
33
|
+
stdout,
|
|
34
|
+
writeFn: options.writeFn,
|
|
35
|
+
isLegacyTmux: options.isLegacyTmux ?? false,
|
|
36
|
+
isTmux: options.isTmux,
|
|
37
|
+
oscSource: options.oscSource,
|
|
38
|
+
clock,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
return { stdin, stdout, clock, palette }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function flushAsync(): Promise<void> {
|
|
45
|
+
await Promise.resolve()
|
|
46
|
+
await Promise.resolve()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function startPaletteDetection(
|
|
50
|
+
options: {
|
|
51
|
+
timeout?: number
|
|
52
|
+
size?: number
|
|
53
|
+
writeFn?: (data: string | Buffer) => boolean
|
|
54
|
+
isLegacyTmux?: boolean
|
|
55
|
+
isTmux?: boolean
|
|
56
|
+
} = {},
|
|
57
|
+
) {
|
|
58
|
+
const timeout = options.timeout ?? 2000
|
|
59
|
+
const harness = createPaletteHarness({
|
|
60
|
+
writeFn: options.writeFn,
|
|
61
|
+
isLegacyTmux: options.isLegacyTmux,
|
|
62
|
+
isTmux: options.isTmux,
|
|
63
|
+
})
|
|
64
|
+
const detectPromise = harness.palette.detect({
|
|
65
|
+
timeout,
|
|
66
|
+
size: options.size ?? 256,
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
harness.stdin.emit("data", Buffer.from("\x1b]4;0;#000000\x07"))
|
|
70
|
+
await flushAsync()
|
|
71
|
+
|
|
72
|
+
return { ...harness, detectPromise, timeout }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function advanceClock(clock: ManualClock, ms: number): Promise<void> {
|
|
76
|
+
await flushAsync()
|
|
77
|
+
// Flush queued 0ms mock terminal responses before advancing the real timeout window.
|
|
78
|
+
clock.advance(0)
|
|
79
|
+
await flushAsync()
|
|
80
|
+
clock.advance(ms)
|
|
81
|
+
await flushAsync()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
test("normalizeTerminalPalette returns detached fallback colors", () => {
|
|
85
|
+
const normalized = normalizeTerminalPalette(null)
|
|
86
|
+
normalized.palette[0].r = 1
|
|
87
|
+
normalized.defaultForeground.g = 0
|
|
88
|
+
|
|
89
|
+
const fresh = normalizeTerminalPalette(null)
|
|
90
|
+
|
|
91
|
+
expect(fresh.palette[0].toInts()).toEqual([0, 0, 0, 255])
|
|
92
|
+
expect(fresh.defaultForeground.toInts()).toEqual([255, 255, 255, 255])
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test("TerminalPalette detectOSCSupport returns true on response", async () => {
|
|
96
|
+
const { stdin, clock, palette } = createPaletteHarness()
|
|
97
|
+
|
|
98
|
+
const detectPromise = palette.detectOSCSupport(500)
|
|
99
|
+
|
|
100
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#ff0000\x07"))
|
|
101
|
+
|
|
102
|
+
await advanceClock(clock, 500)
|
|
103
|
+
|
|
104
|
+
const result = await detectPromise
|
|
105
|
+
|
|
106
|
+
expect(result).toBe(true)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test("TerminalPalette detectOSCSupport returns false on timeout", async () => {
|
|
110
|
+
const { clock, palette } = createPaletteHarness()
|
|
111
|
+
|
|
112
|
+
const detectPromise = palette.detectOSCSupport(100)
|
|
113
|
+
|
|
114
|
+
await advanceClock(clock, 300)
|
|
115
|
+
|
|
116
|
+
const result = await detectPromise
|
|
117
|
+
|
|
118
|
+
expect(result).toBe(false)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test("TerminalPalette parses OSC 4 hex format correctly", async () => {
|
|
122
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
123
|
+
|
|
124
|
+
clock.setTimeout(() => {
|
|
125
|
+
for (let i = 0; i < 256; i++) {
|
|
126
|
+
const color = i === 0 ? "#ff00aa" : i === 1 ? "#00ff00" : i === 2 ? "#0000ff" : "#000000"
|
|
127
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};${color}\x07`))
|
|
128
|
+
}
|
|
129
|
+
stdin.emit("data", Buffer.from("\x1b]10;#aabbcc\x07"))
|
|
130
|
+
stdin.emit("data", Buffer.from("\x1b]11;#ddeeff\x07"))
|
|
131
|
+
}, 0)
|
|
132
|
+
|
|
133
|
+
await advanceClock(clock, timeout)
|
|
134
|
+
|
|
135
|
+
const result = await detectPromise
|
|
136
|
+
|
|
137
|
+
expect(result.palette[0]).toBe("#ff00aa")
|
|
138
|
+
expect(result.palette[1]).toBe("#00ff00")
|
|
139
|
+
expect(result.palette[2]).toBe("#0000ff")
|
|
140
|
+
expect(result.defaultForeground).toBe("#aabbcc")
|
|
141
|
+
expect(result.defaultBackground).toBe("#ddeeff")
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
test("TerminalPalette parses OSC 4 rgb format with 4 hex digits", async () => {
|
|
145
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
146
|
+
|
|
147
|
+
clock.setTimeout(() => {
|
|
148
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;rgb:ffff/0000/aaaa\x07"))
|
|
149
|
+
for (let i = 1; i < 256; i++) {
|
|
150
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
151
|
+
}
|
|
152
|
+
}, 0)
|
|
153
|
+
|
|
154
|
+
await advanceClock(clock, timeout)
|
|
155
|
+
|
|
156
|
+
const result = await detectPromise
|
|
157
|
+
|
|
158
|
+
expect(result.palette[0]).toMatch(/^#[0-9a-f]{6}$/)
|
|
159
|
+
expect(result.palette[0]).toBe("#ff00aa")
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
test("TerminalPalette parses OSC 4 rgb format with 2 hex digits", async () => {
|
|
163
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
164
|
+
|
|
165
|
+
clock.setTimeout(() => {
|
|
166
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;rgb:ff/00/aa\x07"))
|
|
167
|
+
for (let i = 1; i < 256; i++) {
|
|
168
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
169
|
+
}
|
|
170
|
+
}, 0)
|
|
171
|
+
|
|
172
|
+
await advanceClock(clock, timeout)
|
|
173
|
+
|
|
174
|
+
const result = await detectPromise
|
|
175
|
+
|
|
176
|
+
expect(result.palette[0]).toMatch(/^#[0-9a-f]{6}$/)
|
|
177
|
+
expect(result.palette[0]).toBe("#ff00aa")
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test("TerminalPalette handles multiple color responses in single buffer", async () => {
|
|
181
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
182
|
+
|
|
183
|
+
clock.setTimeout(() => {
|
|
184
|
+
stdin.emit(
|
|
185
|
+
"data",
|
|
186
|
+
Buffer.from(
|
|
187
|
+
"\x1b]4;0;rgb:0000/0000/0000\x07" +
|
|
188
|
+
"\x1b]4;1;rgb:aa00/0000/0000\x07" +
|
|
189
|
+
"\x1b]4;2;rgb:0000/aa00/0000\x07" +
|
|
190
|
+
"\x1b]4;3;rgb:aa00/aa00/0000\x07",
|
|
191
|
+
),
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
for (let i = 4; i < 256; i++) {
|
|
195
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
196
|
+
}
|
|
197
|
+
}, 0)
|
|
198
|
+
|
|
199
|
+
await advanceClock(clock, timeout)
|
|
200
|
+
|
|
201
|
+
const result = await detectPromise
|
|
202
|
+
|
|
203
|
+
expect(result.palette[0]).toBe("#000000")
|
|
204
|
+
expect(result.palette[1]).toBe("#a90000")
|
|
205
|
+
expect(result.palette[2]).toBe("#00a900")
|
|
206
|
+
expect(result.palette[3]).toBe("#a9a900")
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
test("TerminalPalette handles BEL terminator", async () => {
|
|
210
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
211
|
+
|
|
212
|
+
clock.setTimeout(() => {
|
|
213
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#ff0000\x07"))
|
|
214
|
+
for (let i = 1; i < 256; i++) {
|
|
215
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
216
|
+
}
|
|
217
|
+
}, 0)
|
|
218
|
+
|
|
219
|
+
await advanceClock(clock, timeout)
|
|
220
|
+
|
|
221
|
+
const result = await detectPromise
|
|
222
|
+
|
|
223
|
+
expect(result.palette[0]).toBe("#ff0000")
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
test("TerminalPalette handles ST terminator", async () => {
|
|
227
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
228
|
+
|
|
229
|
+
clock.setTimeout(() => {
|
|
230
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#00ff00\x1b\\"))
|
|
231
|
+
for (let i = 1; i < 256; i++) {
|
|
232
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
233
|
+
}
|
|
234
|
+
}, 0)
|
|
235
|
+
|
|
236
|
+
await advanceClock(clock, timeout)
|
|
237
|
+
|
|
238
|
+
const result = await detectPromise
|
|
239
|
+
|
|
240
|
+
expect(result.palette[0]).toBe("#00ff00")
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
test("TerminalPalette scales color components correctly", async () => {
|
|
244
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
245
|
+
|
|
246
|
+
clock.setTimeout(() => {
|
|
247
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;rgb:ffff/0000/0000\x07"))
|
|
248
|
+
for (let i = 1; i < 256; i++) {
|
|
249
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
250
|
+
}
|
|
251
|
+
}, 0)
|
|
252
|
+
|
|
253
|
+
await advanceClock(clock, timeout)
|
|
254
|
+
|
|
255
|
+
const result = await detectPromise
|
|
256
|
+
|
|
257
|
+
expect(result.palette[0]).toBe("#ff0000")
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
test("TerminalPalette returns null for colors that don't respond", async () => {
|
|
261
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection({ timeout: 1000 })
|
|
262
|
+
|
|
263
|
+
clock.setTimeout(() => {
|
|
264
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#ff0000\x07"))
|
|
265
|
+
}, 0)
|
|
266
|
+
|
|
267
|
+
await advanceClock(clock, timeout)
|
|
268
|
+
|
|
269
|
+
const result = await detectPromise
|
|
270
|
+
|
|
271
|
+
expect(result.palette[0]).toBe("#ff0000")
|
|
272
|
+
expect(result.palette.some((color: string | null) => color === null)).toBe(true)
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
test("TerminalPalette handles response split across chunks", async () => {
|
|
276
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
277
|
+
|
|
278
|
+
clock.setTimeout(() => {
|
|
279
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#ff"))
|
|
280
|
+
stdin.emit("data", Buffer.from("00aa\x07"))
|
|
281
|
+
|
|
282
|
+
stdin.emit("data", Buffer.from("\x1b]4;1;rgb:0000/"))
|
|
283
|
+
stdin.emit("data", Buffer.from("ffff/"))
|
|
284
|
+
stdin.emit("data", Buffer.from("0000\x07"))
|
|
285
|
+
|
|
286
|
+
for (let i = 2; i < 256; i++) {
|
|
287
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
288
|
+
}
|
|
289
|
+
}, 0)
|
|
290
|
+
|
|
291
|
+
await advanceClock(clock, timeout)
|
|
292
|
+
|
|
293
|
+
const result = await detectPromise
|
|
294
|
+
|
|
295
|
+
expect(result.palette[0]).toBe("#ff00aa")
|
|
296
|
+
expect(result.palette[1]).toBe("#00ff00")
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
test("TerminalPalette handles OSC response mixed with mouse events", async () => {
|
|
300
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
301
|
+
|
|
302
|
+
clock.setTimeout(() => {
|
|
303
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#ff00aa\x07"))
|
|
304
|
+
stdin.emit("data", Buffer.from("\x1b[<0;10;5M"))
|
|
305
|
+
stdin.emit("data", Buffer.from("\x1b]4;1;#00ff00\x07"))
|
|
306
|
+
stdin.emit("data", Buffer.from("\x1b[<0;11;5M"))
|
|
307
|
+
stdin.emit("data", Buffer.from("\x1b]4;2;#0000ff\x07"))
|
|
308
|
+
stdin.emit("data", Buffer.from("\x1b[<0;12;5m"))
|
|
309
|
+
|
|
310
|
+
for (let i = 3; i < 256; i++) {
|
|
311
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
312
|
+
}
|
|
313
|
+
}, 0)
|
|
314
|
+
|
|
315
|
+
await advanceClock(clock, timeout)
|
|
316
|
+
|
|
317
|
+
const result = await detectPromise
|
|
318
|
+
|
|
319
|
+
expect(result.palette[0]).toBe("#ff00aa")
|
|
320
|
+
expect(result.palette[1]).toBe("#00ff00")
|
|
321
|
+
expect(result.palette[2]).toBe("#0000ff")
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
test("TerminalPalette handles OSC response mixed with key events", async () => {
|
|
325
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
326
|
+
|
|
327
|
+
clock.setTimeout(() => {
|
|
328
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#ff00aa\x07"))
|
|
329
|
+
stdin.emit("data", Buffer.from("hello"))
|
|
330
|
+
stdin.emit("data", Buffer.from("\x1b]4;1;#00ff00\x07"))
|
|
331
|
+
stdin.emit("data", Buffer.from("\x1b[A"))
|
|
332
|
+
stdin.emit("data", Buffer.from("\x1b]4;2;#0000ff\x07"))
|
|
333
|
+
stdin.emit("data", Buffer.from("\x1b[B"))
|
|
334
|
+
stdin.emit("data", Buffer.from("\x1b]4;3;#ffff00\x07"))
|
|
335
|
+
|
|
336
|
+
for (let i = 4; i < 256; i++) {
|
|
337
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
338
|
+
}
|
|
339
|
+
}, 0)
|
|
340
|
+
|
|
341
|
+
await advanceClock(clock, timeout)
|
|
342
|
+
|
|
343
|
+
const result = await detectPromise
|
|
344
|
+
|
|
345
|
+
expect(result.palette[0]).toBe("#ff00aa")
|
|
346
|
+
expect(result.palette[1]).toBe("#00ff00")
|
|
347
|
+
expect(result.palette[2]).toBe("#0000ff")
|
|
348
|
+
expect(result.palette[3]).toBe("#ffff00")
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
test("TerminalPalette handles response split mid-escape sequence", async () => {
|
|
352
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
353
|
+
|
|
354
|
+
clock.setTimeout(() => {
|
|
355
|
+
stdin.emit("data", Buffer.from("\x1b"))
|
|
356
|
+
stdin.emit("data", Buffer.from("]4;0;#ff00aa\x07"))
|
|
357
|
+
|
|
358
|
+
stdin.emit("data", Buffer.from("\x1b]"))
|
|
359
|
+
stdin.emit("data", Buffer.from("4;1;#00ff00\x07"))
|
|
360
|
+
|
|
361
|
+
stdin.emit("data", Buffer.from("\x1b]4"))
|
|
362
|
+
stdin.emit("data", Buffer.from(";2;#0000ff\x07"))
|
|
363
|
+
|
|
364
|
+
stdin.emit("data", Buffer.from("\x1b]4;"))
|
|
365
|
+
stdin.emit("data", Buffer.from("3;#ffff00\x07"))
|
|
366
|
+
|
|
367
|
+
for (let i = 4; i < 256; i++) {
|
|
368
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
369
|
+
}
|
|
370
|
+
}, 0)
|
|
371
|
+
|
|
372
|
+
await advanceClock(clock, timeout)
|
|
373
|
+
|
|
374
|
+
const result = await detectPromise
|
|
375
|
+
|
|
376
|
+
expect(result.palette[0]).toBe("#ff00aa")
|
|
377
|
+
expect(result.palette[1]).toBe("#00ff00")
|
|
378
|
+
expect(result.palette[2]).toBe("#0000ff")
|
|
379
|
+
expect(result.palette[3]).toBe("#ffff00")
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
test("TerminalPalette handles mixed ANSI sequences and OSC responses", async () => {
|
|
383
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
384
|
+
|
|
385
|
+
clock.setTimeout(() => {
|
|
386
|
+
stdin.emit("data", Buffer.from("\x1b[2J"))
|
|
387
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#ff00aa\x07"))
|
|
388
|
+
stdin.emit("data", Buffer.from("\x1b[H"))
|
|
389
|
+
stdin.emit("data", Buffer.from("\x1b]4;1;#00ff00\x07"))
|
|
390
|
+
stdin.emit("data", Buffer.from("\x1b[31m"))
|
|
391
|
+
stdin.emit("data", Buffer.from("\x1b]4;2;#0000ff\x07"))
|
|
392
|
+
stdin.emit("data", Buffer.from("\x1b[0m"))
|
|
393
|
+
|
|
394
|
+
for (let i = 3; i < 256; i++) {
|
|
395
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
396
|
+
}
|
|
397
|
+
}, 0)
|
|
398
|
+
|
|
399
|
+
await advanceClock(clock, timeout)
|
|
400
|
+
|
|
401
|
+
const result = await detectPromise
|
|
402
|
+
|
|
403
|
+
expect(result.palette[0]).toBe("#ff00aa")
|
|
404
|
+
expect(result.palette[1]).toBe("#00ff00")
|
|
405
|
+
expect(result.palette[2]).toBe("#0000ff")
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
test("TerminalPalette handles complex chunking with partial responses", async () => {
|
|
409
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
410
|
+
|
|
411
|
+
clock.setTimeout(() => {
|
|
412
|
+
const response0 = "\x1b]4;0;rgb:ffff/0000/aaaa\x07"
|
|
413
|
+
for (let i = 0; i < response0.length; i += 3) {
|
|
414
|
+
stdin.emit("data", Buffer.from(response0.slice(i, i + 3)))
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
stdin.emit("data", Buffer.from("\x1b]4;1"))
|
|
418
|
+
stdin.emit("data", Buffer.from(";#00"))
|
|
419
|
+
stdin.emit("data", Buffer.from("some junk data"))
|
|
420
|
+
stdin.emit("data", Buffer.from("ff00"))
|
|
421
|
+
stdin.emit("data", Buffer.from("\x1b[D"))
|
|
422
|
+
stdin.emit("data", Buffer.from("\x07"))
|
|
423
|
+
|
|
424
|
+
stdin.emit("data", Buffer.from("\x1b]4;1;#00ff00\x07"))
|
|
425
|
+
|
|
426
|
+
for (let i = 2; i < 256; i++) {
|
|
427
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
428
|
+
}
|
|
429
|
+
}, 0)
|
|
430
|
+
|
|
431
|
+
await advanceClock(clock, timeout)
|
|
432
|
+
|
|
433
|
+
const result = await detectPromise
|
|
434
|
+
|
|
435
|
+
expect(result.palette[0]).toBe("#ff00aa")
|
|
436
|
+
expect(result.palette[1]).toBe("#00ff00")
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
test("TerminalPalette ignores malformed responses and waits for valid ones", async () => {
|
|
440
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
441
|
+
|
|
442
|
+
clock.setTimeout(() => {
|
|
443
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#ff00\x07"))
|
|
444
|
+
stdin.emit("data", Buffer.from("\x1b]4;1;rgb:gg00/0000/0000\x07"))
|
|
445
|
+
stdin.emit("data", Buffer.from("\x1b]4;2;#zzzzzz\x07"))
|
|
446
|
+
|
|
447
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#ff00aa\x07"))
|
|
448
|
+
stdin.emit("data", Buffer.from("\x1b]4;1;#00ff00\x07"))
|
|
449
|
+
stdin.emit("data", Buffer.from("\x1b]4;2;#0000ff\x07"))
|
|
450
|
+
|
|
451
|
+
for (let i = 3; i < 256; i++) {
|
|
452
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
453
|
+
}
|
|
454
|
+
}, 0)
|
|
455
|
+
|
|
456
|
+
await advanceClock(clock, timeout)
|
|
457
|
+
|
|
458
|
+
const result = await detectPromise
|
|
459
|
+
|
|
460
|
+
expect(result.palette[0]).toBe("#ff00aa")
|
|
461
|
+
expect(result.palette[1]).toBe("#00ff00")
|
|
462
|
+
expect(result.palette[2]).toBe("#0000ff")
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
test("TerminalPalette handles buffer overflow gracefully", async () => {
|
|
466
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
467
|
+
|
|
468
|
+
clock.setTimeout(() => {
|
|
469
|
+
const junkData = "x".repeat(10000)
|
|
470
|
+
stdin.emit("data", Buffer.from(junkData))
|
|
471
|
+
|
|
472
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#ff00aa\x07"))
|
|
473
|
+
stdin.emit("data", Buffer.from("\x1b]4;1;#00ff00\x07"))
|
|
474
|
+
|
|
475
|
+
for (let i = 2; i < 256; i++) {
|
|
476
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
477
|
+
}
|
|
478
|
+
}, 0)
|
|
479
|
+
|
|
480
|
+
await advanceClock(clock, timeout)
|
|
481
|
+
|
|
482
|
+
const result = await detectPromise
|
|
483
|
+
|
|
484
|
+
expect(result.palette[0]).toBe("#ff00aa")
|
|
485
|
+
expect(result.palette[1]).toBe("#00ff00")
|
|
486
|
+
})
|
|
487
|
+
|
|
488
|
+
test("TerminalPalette handles all 256 colors in a single blob", async () => {
|
|
489
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
490
|
+
|
|
491
|
+
clock.setTimeout(() => {
|
|
492
|
+
let blob = ""
|
|
493
|
+
for (let i = 0; i < 256; i++) {
|
|
494
|
+
const color = i === 0 ? "#ff0011" : i === 1 ? "#00ff22" : i === 255 ? "#aabbcc" : "#000000"
|
|
495
|
+
blob += `\x1b]4;${i};${color}\x07`
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
stdin.emit("data", Buffer.from(blob))
|
|
499
|
+
}, 0)
|
|
500
|
+
|
|
501
|
+
await advanceClock(clock, timeout)
|
|
502
|
+
|
|
503
|
+
const result = await detectPromise
|
|
504
|
+
|
|
505
|
+
expect(result.palette[0]).toBe("#ff0011")
|
|
506
|
+
expect(result.palette[1]).toBe("#00ff22")
|
|
507
|
+
expect(result.palette[255]).toBe("#aabbcc")
|
|
508
|
+
expect(result.palette.length).toBe(256)
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
test("TerminalPalette handles blob split across multiple chunks", async () => {
|
|
512
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
513
|
+
|
|
514
|
+
clock.setTimeout(() => {
|
|
515
|
+
let blob = ""
|
|
516
|
+
for (let i = 0; i < 256; i++) {
|
|
517
|
+
const color = i === 5 ? "#112233" : i === 100 ? "#445566" : i === 200 ? "#778899" : "#000000"
|
|
518
|
+
blob += `\x1b]4;${i};${color}\x07`
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const chunkSize = 500
|
|
522
|
+
for (let i = 0; i < blob.length; i += chunkSize) {
|
|
523
|
+
stdin.emit("data", Buffer.from(blob.slice(i, i + chunkSize)))
|
|
524
|
+
}
|
|
525
|
+
}, 0)
|
|
526
|
+
|
|
527
|
+
await advanceClock(clock, timeout)
|
|
528
|
+
|
|
529
|
+
const result = await detectPromise
|
|
530
|
+
|
|
531
|
+
expect(result.palette[5]).toBe("#112233")
|
|
532
|
+
expect(result.palette[100]).toBe("#445566")
|
|
533
|
+
expect(result.palette[200]).toBe("#778899")
|
|
534
|
+
expect(result.palette.length).toBe(256)
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
test("TerminalPalette handles blob with mixed junk data", async () => {
|
|
538
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
539
|
+
|
|
540
|
+
clock.setTimeout(() => {
|
|
541
|
+
let blob = ""
|
|
542
|
+
for (let i = 0; i < 256; i++) {
|
|
543
|
+
const color = i === 10 ? "#abcdef" : i === 50 ? "#fedcba" : "#000000"
|
|
544
|
+
blob += `\x1b]4;${i};${color}\x07`
|
|
545
|
+
|
|
546
|
+
if (i % 20 === 0) {
|
|
547
|
+
blob += "JUNK_DATA_HERE"
|
|
548
|
+
}
|
|
549
|
+
if (i % 30 === 0) {
|
|
550
|
+
blob += "\x1b[2J\x1b[H"
|
|
551
|
+
}
|
|
552
|
+
if (i % 40 === 0) {
|
|
553
|
+
blob += "\x1b[<0;10;5M"
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
stdin.emit("data", Buffer.from(blob))
|
|
558
|
+
}, 0)
|
|
559
|
+
|
|
560
|
+
await advanceClock(clock, timeout)
|
|
561
|
+
|
|
562
|
+
const result = await detectPromise
|
|
563
|
+
|
|
564
|
+
expect(result.palette[10]).toBe("#abcdef")
|
|
565
|
+
expect(result.palette[50]).toBe("#fedcba")
|
|
566
|
+
expect(result.palette.length).toBe(256)
|
|
567
|
+
})
|
|
568
|
+
|
|
569
|
+
test("TerminalPalette handles realistic terminal response pattern", async () => {
|
|
570
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
571
|
+
|
|
572
|
+
clock.setTimeout(() => {
|
|
573
|
+
let chunk1 = ""
|
|
574
|
+
for (let i = 0; i <= 5; i++) {
|
|
575
|
+
chunk1 += `\x1b]4;${i};#ff0000\x07`
|
|
576
|
+
}
|
|
577
|
+
stdin.emit("data", Buffer.from(chunk1))
|
|
578
|
+
|
|
579
|
+
let chunk2 = ""
|
|
580
|
+
for (let i = 6; i <= 50; i++) {
|
|
581
|
+
chunk2 += `\x1b]4;${i};#00ff00\x07`
|
|
582
|
+
}
|
|
583
|
+
stdin.emit("data", Buffer.from(chunk2.slice(0, 200)))
|
|
584
|
+
stdin.emit("data", Buffer.from(chunk2.slice(200)))
|
|
585
|
+
|
|
586
|
+
stdin.emit("data", Buffer.from("\x1b[<35;20;10M"))
|
|
587
|
+
let chunk3 = ""
|
|
588
|
+
for (let i = 51; i <= 150; i++) {
|
|
589
|
+
chunk3 += `\x1b]4;${i};#0000ff\x07`
|
|
590
|
+
}
|
|
591
|
+
stdin.emit("data", Buffer.from(chunk3))
|
|
592
|
+
|
|
593
|
+
let chunk4 = ""
|
|
594
|
+
for (let i = 151; i <= 255; i++) {
|
|
595
|
+
chunk4 += `\x1b]4;${i};#ffffff\x07`
|
|
596
|
+
}
|
|
597
|
+
stdin.emit("data", Buffer.from(chunk4))
|
|
598
|
+
}, 0)
|
|
599
|
+
|
|
600
|
+
await advanceClock(clock, timeout)
|
|
601
|
+
|
|
602
|
+
const result = await detectPromise
|
|
603
|
+
|
|
604
|
+
expect(result.palette[0]).toBe("#ff0000")
|
|
605
|
+
expect(result.palette[5]).toBe("#ff0000")
|
|
606
|
+
expect(result.palette[6]).toBe("#00ff00")
|
|
607
|
+
expect(result.palette[50]).toBe("#00ff00")
|
|
608
|
+
expect(result.palette[51]).toBe("#0000ff")
|
|
609
|
+
expect(result.palette[150]).toBe("#0000ff")
|
|
610
|
+
expect(result.palette[151]).toBe("#ffffff")
|
|
611
|
+
expect(result.palette[255]).toBe("#ffffff")
|
|
612
|
+
expect(result.palette.length).toBe(256)
|
|
613
|
+
})
|
|
614
|
+
|
|
615
|
+
test("TerminalPalette uses custom write function when provided", async () => {
|
|
616
|
+
const writtenData: string[] = []
|
|
617
|
+
|
|
618
|
+
const customWrite = (data: string | Buffer) => {
|
|
619
|
+
writtenData.push(data.toString())
|
|
620
|
+
return true
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const { stdin, clock, palette } = createPaletteHarness({ writeFn: customWrite })
|
|
624
|
+
|
|
625
|
+
const detectPromise = palette.detectOSCSupport(500)
|
|
626
|
+
|
|
627
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#ff0000\x07"))
|
|
628
|
+
|
|
629
|
+
await advanceClock(clock, 500)
|
|
630
|
+
|
|
631
|
+
const result = await detectPromise
|
|
632
|
+
|
|
633
|
+
expect(result).toBe(true)
|
|
634
|
+
expect(writtenData.length).toBe(1)
|
|
635
|
+
expect(writtenData[0]).toBe("\x1b]4;0;?\x07")
|
|
636
|
+
})
|
|
637
|
+
|
|
638
|
+
test("TerminalPalette uses custom write function for palette detection", async () => {
|
|
639
|
+
const writtenData: string[] = []
|
|
640
|
+
|
|
641
|
+
const customWrite = (data: string | Buffer) => {
|
|
642
|
+
writtenData.push(data.toString())
|
|
643
|
+
return true
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection({ writeFn: customWrite })
|
|
647
|
+
|
|
648
|
+
clock.setTimeout(() => {
|
|
649
|
+
for (let i = 0; i < 256; i++) {
|
|
650
|
+
const color = "#aabbcc"
|
|
651
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};${color}\x07`))
|
|
652
|
+
}
|
|
653
|
+
}, 0)
|
|
654
|
+
|
|
655
|
+
await advanceClock(clock, timeout)
|
|
656
|
+
|
|
657
|
+
await detectPromise
|
|
658
|
+
|
|
659
|
+
expect(writtenData.length).toBe(3)
|
|
660
|
+
expect(writtenData[0]).toBe("\x1b]4;0;?\x07")
|
|
661
|
+
|
|
662
|
+
const paletteQuery = writtenData[1]
|
|
663
|
+
for (let i = 0; i < 256; i++) {
|
|
664
|
+
expect(paletteQuery).toContain(`\x1b]4;${i};?\x07`)
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
const specialQuery = writtenData[2]
|
|
668
|
+
expect(specialQuery).toContain("\x1b]10;?\x07")
|
|
669
|
+
expect(specialQuery).toContain("\x1b]11;?\x07")
|
|
670
|
+
})
|
|
671
|
+
|
|
672
|
+
test("TerminalPalette falls back to stdout.write when no custom write function provided", async () => {
|
|
673
|
+
const clock = new ManualClock()
|
|
674
|
+
const stdin = new MockStream() as any
|
|
675
|
+
const writtenData: string[] = []
|
|
676
|
+
|
|
677
|
+
const stdout = new MockStream() as any
|
|
678
|
+
stdout.write = (data: string) => {
|
|
679
|
+
writtenData.push(data)
|
|
680
|
+
return true
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const palette = new TerminalPalette({
|
|
684
|
+
stdin,
|
|
685
|
+
stdout,
|
|
686
|
+
isLegacyTmux: false,
|
|
687
|
+
clock,
|
|
688
|
+
})
|
|
689
|
+
|
|
690
|
+
const detectPromise = palette.detectOSCSupport(500)
|
|
691
|
+
|
|
692
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#ff0000\x07"))
|
|
693
|
+
|
|
694
|
+
await advanceClock(clock, 500)
|
|
695
|
+
|
|
696
|
+
const result = await detectPromise
|
|
697
|
+
|
|
698
|
+
expect(result).toBe(true)
|
|
699
|
+
expect(writtenData.length).toBe(1)
|
|
700
|
+
expect(writtenData[0]).toBe("\x1b]4;0;?\x07")
|
|
701
|
+
})
|
|
702
|
+
|
|
703
|
+
test("TerminalPalette custom write function can intercept and modify output", async () => {
|
|
704
|
+
const interceptedWrites: string[] = []
|
|
705
|
+
let actualWrites = 0
|
|
706
|
+
|
|
707
|
+
const customWrite = (data: string | Buffer) => {
|
|
708
|
+
interceptedWrites.push(data.toString())
|
|
709
|
+
actualWrites++
|
|
710
|
+
return true
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
const { stdin, clock, palette } = createPaletteHarness({ writeFn: customWrite })
|
|
714
|
+
|
|
715
|
+
const detectPromise = palette.detectOSCSupport(500)
|
|
716
|
+
|
|
717
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#ff0000\x07"))
|
|
718
|
+
|
|
719
|
+
await advanceClock(clock, 500)
|
|
720
|
+
|
|
721
|
+
await detectPromise
|
|
722
|
+
|
|
723
|
+
expect(actualWrites).toBe(1)
|
|
724
|
+
expect(interceptedWrites.length).toBe(1)
|
|
725
|
+
expect(interceptedWrites[0]).toBe("\x1b]4;0;?\x07")
|
|
726
|
+
})
|
|
727
|
+
|
|
728
|
+
test("TerminalPalette detects all special OSC colors (10-19)", async () => {
|
|
729
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
730
|
+
|
|
731
|
+
clock.setTimeout(() => {
|
|
732
|
+
for (let i = 0; i < 256; i++) {
|
|
733
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
734
|
+
}
|
|
735
|
+
stdin.emit("data", Buffer.from("\x1b]10;#ff0001\x07"))
|
|
736
|
+
stdin.emit("data", Buffer.from("\x1b]11;#ff0002\x07"))
|
|
737
|
+
stdin.emit("data", Buffer.from("\x1b]12;#ff0003\x07"))
|
|
738
|
+
stdin.emit("data", Buffer.from("\x1b]13;#ff0004\x07"))
|
|
739
|
+
stdin.emit("data", Buffer.from("\x1b]14;#ff0005\x07"))
|
|
740
|
+
stdin.emit("data", Buffer.from("\x1b]15;#ff0006\x07"))
|
|
741
|
+
stdin.emit("data", Buffer.from("\x1b]16;#ff0007\x07"))
|
|
742
|
+
stdin.emit("data", Buffer.from("\x1b]17;#ff0008\x07"))
|
|
743
|
+
stdin.emit("data", Buffer.from("\x1b]19;#ff0009\x07"))
|
|
744
|
+
}, 0)
|
|
745
|
+
|
|
746
|
+
await advanceClock(clock, timeout)
|
|
747
|
+
|
|
748
|
+
const result = await detectPromise
|
|
749
|
+
|
|
750
|
+
expect(result.defaultForeground).toBe("#ff0001")
|
|
751
|
+
expect(result.defaultBackground).toBe("#ff0002")
|
|
752
|
+
expect(result.cursorColor).toBe("#ff0003")
|
|
753
|
+
expect(result.mouseForeground).toBe("#ff0004")
|
|
754
|
+
expect(result.mouseBackground).toBe("#ff0005")
|
|
755
|
+
expect(result.tekForeground).toBe("#ff0006")
|
|
756
|
+
expect(result.tekBackground).toBe("#ff0007")
|
|
757
|
+
expect(result.highlightBackground).toBe("#ff0008")
|
|
758
|
+
expect(result.highlightForeground).toBe("#ff0009")
|
|
759
|
+
})
|
|
760
|
+
|
|
761
|
+
test("TerminalPalette only wraps OSC 4 and only queries tmux-supported special colors in legacy tmux", async () => {
|
|
762
|
+
const writes: string[] = []
|
|
763
|
+
const { clock, detectPromise, timeout } = await startPaletteDetection({
|
|
764
|
+
isLegacyTmux: true,
|
|
765
|
+
isTmux: true,
|
|
766
|
+
writeFn: (data) => {
|
|
767
|
+
writes.push(data.toString())
|
|
768
|
+
return true
|
|
769
|
+
},
|
|
770
|
+
})
|
|
771
|
+
|
|
772
|
+
expect(writes.some((write) => write.startsWith("\x1bPtmux;\x1b\x1b]4;"))).toBe(true)
|
|
773
|
+
expect(writes).toContain("\x1b]10;?\x07\x1b]11;?\x07\x1b]12;?\x07")
|
|
774
|
+
expect(writes.some((write) => write.includes("\x1b]13;?"))).toBe(false)
|
|
775
|
+
|
|
776
|
+
await advanceClock(clock, timeout)
|
|
777
|
+
await detectPromise
|
|
778
|
+
})
|
|
779
|
+
|
|
780
|
+
test("TerminalPalette uses plain OSC 4 but limits special color queries in modern tmux", async () => {
|
|
781
|
+
const writes: string[] = []
|
|
782
|
+
const { clock, detectPromise, timeout } = await startPaletteDetection({
|
|
783
|
+
isTmux: true,
|
|
784
|
+
writeFn: (data) => {
|
|
785
|
+
writes.push(data.toString())
|
|
786
|
+
return true
|
|
787
|
+
},
|
|
788
|
+
})
|
|
789
|
+
|
|
790
|
+
expect(writes).toContain("\x1b]4;0;?\x07")
|
|
791
|
+
expect(writes.some((write) => write.startsWith("\x1bPtmux;"))).toBe(false)
|
|
792
|
+
expect(writes).toContain("\x1b]10;?\x07\x1b]11;?\x07\x1b]12;?\x07")
|
|
793
|
+
expect(writes.some((write) => write.includes("\x1b]13;?"))).toBe(false)
|
|
794
|
+
|
|
795
|
+
await advanceClock(clock, timeout)
|
|
796
|
+
await detectPromise
|
|
797
|
+
})
|
|
798
|
+
|
|
799
|
+
test("TerminalPalette handles special colors in rgb format", async () => {
|
|
800
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
801
|
+
|
|
802
|
+
clock.setTimeout(() => {
|
|
803
|
+
for (let i = 0; i < 256; i++) {
|
|
804
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
805
|
+
}
|
|
806
|
+
stdin.emit("data", Buffer.from("\x1b]10;rgb:ffff/0000/0000\x07"))
|
|
807
|
+
stdin.emit("data", Buffer.from("\x1b]11;rgb:0000/ffff/0000\x07"))
|
|
808
|
+
}, 0)
|
|
809
|
+
|
|
810
|
+
await advanceClock(clock, timeout)
|
|
811
|
+
|
|
812
|
+
const result = await detectPromise
|
|
813
|
+
|
|
814
|
+
expect(result.defaultForeground).toBe("#ff0000")
|
|
815
|
+
expect(result.defaultBackground).toBe("#00ff00")
|
|
816
|
+
})
|
|
817
|
+
|
|
818
|
+
test("TerminalPalette handles missing special colors gracefully", async () => {
|
|
819
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
820
|
+
|
|
821
|
+
clock.setTimeout(() => {
|
|
822
|
+
for (let i = 0; i < 256; i++) {
|
|
823
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
824
|
+
}
|
|
825
|
+
stdin.emit("data", Buffer.from("\x1b]10;#ff0001\x07"))
|
|
826
|
+
stdin.emit("data", Buffer.from("\x1b]11;#ff0002\x07"))
|
|
827
|
+
}, 0)
|
|
828
|
+
|
|
829
|
+
await advanceClock(clock, timeout)
|
|
830
|
+
|
|
831
|
+
const result = await detectPromise
|
|
832
|
+
|
|
833
|
+
expect(result.defaultForeground).toBe("#ff0001")
|
|
834
|
+
expect(result.defaultBackground).toBe("#ff0002")
|
|
835
|
+
expect(result.cursorColor).toBe(null)
|
|
836
|
+
expect(result.mouseForeground).toBe(null)
|
|
837
|
+
expect(result.mouseBackground).toBe(null)
|
|
838
|
+
})
|
|
839
|
+
|
|
840
|
+
test("TerminalPalette special colors with ST terminator", async () => {
|
|
841
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
842
|
+
|
|
843
|
+
clock.setTimeout(() => {
|
|
844
|
+
for (let i = 0; i < 256; i++) {
|
|
845
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
846
|
+
}
|
|
847
|
+
stdin.emit("data", Buffer.from("\x1b]10;#aabbcc\x1b\\"))
|
|
848
|
+
stdin.emit("data", Buffer.from("\x1b]11;#ddeeff\x1b\\"))
|
|
849
|
+
}, 0)
|
|
850
|
+
|
|
851
|
+
await advanceClock(clock, timeout)
|
|
852
|
+
|
|
853
|
+
const result = await detectPromise
|
|
854
|
+
|
|
855
|
+
expect(result.defaultForeground).toBe("#aabbcc")
|
|
856
|
+
expect(result.defaultBackground).toBe("#ddeeff")
|
|
857
|
+
})
|
|
858
|
+
|
|
859
|
+
test("TerminalPalette handles mixed palette and special color responses", async () => {
|
|
860
|
+
const { stdin, clock, detectPromise, timeout } = await startPaletteDetection()
|
|
861
|
+
|
|
862
|
+
clock.setTimeout(() => {
|
|
863
|
+
stdin.emit("data", Buffer.from("\x1b]4;0;#010203\x07"))
|
|
864
|
+
stdin.emit("data", Buffer.from("\x1b]10;#aabbcc\x07"))
|
|
865
|
+
stdin.emit("data", Buffer.from("\x1b]4;1;#040506\x07"))
|
|
866
|
+
stdin.emit("data", Buffer.from("\x1b]11;#ddeeff\x07"))
|
|
867
|
+
for (let i = 2; i < 256; i++) {
|
|
868
|
+
stdin.emit("data", Buffer.from(`\x1b]4;${i};#000000\x07`))
|
|
869
|
+
}
|
|
870
|
+
}, 0)
|
|
871
|
+
|
|
872
|
+
await advanceClock(clock, timeout)
|
|
873
|
+
|
|
874
|
+
const result = await detectPromise
|
|
875
|
+
|
|
876
|
+
expect(result.palette[0]).toBe("#010203")
|
|
877
|
+
expect(result.palette[1]).toBe("#040506")
|
|
878
|
+
expect(result.defaultForeground).toBe("#aabbcc")
|
|
879
|
+
expect(result.defaultBackground).toBe("#ddeeff")
|
|
880
|
+
})
|
|
881
|
+
|
|
882
|
+
test("TerminalPalette returns null special colors on non-TTY", async () => {
|
|
883
|
+
const { stdin, stdout, clock, palette } = createPaletteHarness()
|
|
884
|
+
stdin.isTTY = false
|
|
885
|
+
stdout.isTTY = false
|
|
886
|
+
|
|
887
|
+
const detectPromise = palette.detect({ timeout: 100 })
|
|
888
|
+
|
|
889
|
+
await advanceClock(clock, 100)
|
|
890
|
+
|
|
891
|
+
const result = await detectPromise
|
|
892
|
+
|
|
893
|
+
expect(result.defaultForeground).toBe(null)
|
|
894
|
+
expect(result.defaultBackground).toBe(null)
|
|
895
|
+
expect(result.cursorColor).toBe(null)
|
|
896
|
+
expect(result.palette.every((c: string | null) => c === null)).toBe(true)
|
|
897
|
+
})
|
|
898
|
+
|
|
899
|
+
test("TerminalPalette returns null special colors on OSC not supported", async () => {
|
|
900
|
+
const { clock, palette } = createPaletteHarness()
|
|
901
|
+
|
|
902
|
+
const detectPromise = palette.detect({ timeout: 100 })
|
|
903
|
+
|
|
904
|
+
await advanceClock(clock, 300)
|
|
905
|
+
|
|
906
|
+
const result = await detectPromise
|
|
907
|
+
|
|
908
|
+
expect(result.defaultForeground).toBe(null)
|
|
909
|
+
expect(result.defaultBackground).toBe(null)
|
|
910
|
+
expect(result.cursorColor).toBe(null)
|
|
911
|
+
expect(result.palette.every((c: string | null) => c === null)).toBe(true)
|
|
912
|
+
})
|
|
913
|
+
|
|
914
|
+
test("TerminalPalette can read OSC from router subscription source", async () => {
|
|
915
|
+
const stdin = new MockStream() as any
|
|
916
|
+
|
|
917
|
+
const handlers = new Set<(sequence: string) => void>()
|
|
918
|
+
let subscribeCount = 0
|
|
919
|
+
let unsubscribeCount = 0
|
|
920
|
+
|
|
921
|
+
const oscSource = {
|
|
922
|
+
subscribeOsc(handler: (sequence: string) => void) {
|
|
923
|
+
subscribeCount++
|
|
924
|
+
handlers.add(handler)
|
|
925
|
+
return () => {
|
|
926
|
+
unsubscribeCount++
|
|
927
|
+
handlers.delete(handler)
|
|
928
|
+
}
|
|
929
|
+
},
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
const clock = new ManualClock()
|
|
933
|
+
const stdout = new MockStream() as any
|
|
934
|
+
const palette = new TerminalPalette({
|
|
935
|
+
stdin,
|
|
936
|
+
stdout,
|
|
937
|
+
isLegacyTmux: false,
|
|
938
|
+
oscSource,
|
|
939
|
+
clock,
|
|
940
|
+
})
|
|
941
|
+
|
|
942
|
+
const detectPromise = palette.detectOSCSupport(500)
|
|
943
|
+
for (const handler of handlers) {
|
|
944
|
+
handler("\x1b]4;0;#ff0000\x07")
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
await advanceClock(clock, 500)
|
|
948
|
+
|
|
949
|
+
const supported = await detectPromise
|
|
950
|
+
expect(supported).toBe(true)
|
|
951
|
+
expect(subscribeCount).toBe(1)
|
|
952
|
+
expect(unsubscribeCount).toBe(1)
|
|
953
|
+
expect(stdin.listenerCount("data")).toBe(0)
|
|
954
|
+
})
|