@fairyhunter13/opentui-core 0.1.112 → 0.1.114
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/thai-debug-test.ts +68 -0
- package/docs/development.md +144 -0
- package/package.json +63 -51
- package/scripts/build.ts +400 -0
- package/scripts/publish.ts +60 -0
- package/src/3d/SpriteResourceManager.ts +286 -0
- package/src/3d/SpriteUtils.ts +70 -0
- package/src/3d/TextureUtils.ts +196 -0
- package/src/3d/ThreeRenderable.ts +197 -0
- package/src/3d/WGPURenderer.ts +294 -0
- package/src/3d/animation/ExplodingSpriteEffect.ts +513 -0
- package/src/3d/animation/PhysicsExplodingSpriteEffect.ts +429 -0
- package/src/3d/animation/SpriteAnimator.ts +633 -0
- package/src/3d/animation/SpriteParticleGenerator.ts +435 -0
- package/src/3d/canvas.ts +464 -0
- package/src/3d/index.ts +12 -0
- package/src/3d/physics/PlanckPhysicsAdapter.ts +72 -0
- package/src/3d/physics/RapierPhysicsAdapter.ts +66 -0
- package/src/3d/physics/physics-interface.ts +31 -0
- package/src/3d/shaders/supersampling.wgsl +201 -0
- package/src/3d.ts +3 -0
- package/src/NativeSpanFeed.ts +300 -0
- package/src/Renderable.ts +1704 -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 +18 -0
- package/src/benchmark/attenuation-benchmark.ts +81 -0
- package/src/benchmark/colormatrix-benchmark.ts +128 -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/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/renderer-benchmark.ts +754 -0
- package/src/benchmark/text-table-benchmark.ts +948 -0
- package/src/buffer.test.ts +291 -0
- package/src/buffer.ts +554 -0
- package/src/console.test.ts +612 -0
- package/src/console.ts +1254 -0
- package/src/edit-buffer.test.ts +1769 -0
- package/src/edit-buffer.ts +411 -0
- package/src/editor-view.test.ts +1032 -0
- package/src/editor-view.ts +284 -0
- package/src/examples/ascii-font-selection-demo.ts +245 -0
- package/src/examples/assets/Water_2_M_Normal.jpg +0 -0
- package/src/examples/assets/concrete.png +0 -0
- package/src/examples/assets/crate.png +0 -0
- package/src/examples/assets/crate_emissive.png +0 -0
- package/src/examples/assets/forrest_background.png +0 -0
- package/src/examples/assets/hast-example.json +1018 -0
- package/src/examples/assets/heart.png +0 -0
- package/src/examples/assets/main_char_heavy_attack.png +0 -0
- package/src/examples/assets/main_char_idle.png +0 -0
- package/src/examples/assets/main_char_jump_end.png +0 -0
- package/src/examples/assets/main_char_jump_landing.png +0 -0
- package/src/examples/assets/main_char_jump_start.png +0 -0
- package/src/examples/assets/main_char_run_loop.png +0 -0
- package/src/examples/assets/roughness_map.jpg +0 -0
- package/src/examples/build.ts +115 -0
- package/src/examples/code-demo.ts +924 -0
- package/src/examples/console-demo.ts +358 -0
- package/src/examples/core-plugin-slots-demo.ts +759 -0
- package/src/examples/diff-demo.ts +701 -0
- package/src/examples/draggable-three-demo.ts +259 -0
- package/src/examples/editor-demo.ts +322 -0
- package/src/examples/extmarks-demo.ts +196 -0
- package/src/examples/focus-restore-demo.ts +310 -0
- package/src/examples/fonts.ts +245 -0
- package/src/examples/fractal-shader-demo.ts +268 -0
- package/src/examples/framebuffer-demo.ts +674 -0
- package/src/examples/full-unicode-demo.ts +241 -0
- package/src/examples/golden-star-demo.ts +933 -0
- package/src/examples/grayscale-buffer-demo.ts +249 -0
- package/src/examples/hast-syntax-highlighting-demo.ts +129 -0
- package/src/examples/index.ts +926 -0
- package/src/examples/input-demo.ts +377 -0
- package/src/examples/input-select-layout-demo.ts +425 -0
- package/src/examples/install.sh +143 -0
- package/src/examples/keypress-debug-demo.ts +452 -0
- package/src/examples/lib/HexList.ts +122 -0
- package/src/examples/lib/PaletteGrid.ts +125 -0
- package/src/examples/lib/standalone-keys.ts +25 -0
- package/src/examples/lib/tab-controller.ts +243 -0
- package/src/examples/lights-phong-demo.ts +290 -0
- package/src/examples/link-demo.ts +220 -0
- package/src/examples/live-state-demo.ts +480 -0
- package/src/examples/markdown-demo.ts +725 -0
- package/src/examples/mouse-interaction-demo.ts +428 -0
- package/src/examples/nested-zindex-demo.ts +357 -0
- package/src/examples/opacity-example.ts +235 -0
- package/src/examples/opentui-demo.ts +1057 -0
- package/src/examples/physx-planck-2d-demo.ts +623 -0
- package/src/examples/physx-rapier-2d-demo.ts +655 -0
- package/src/examples/relative-positioning-demo.ts +323 -0
- package/src/examples/scroll-example.ts +214 -0
- package/src/examples/scrollbox-mouse-test.ts +112 -0
- package/src/examples/scrollbox-overlay-hit-test.ts +206 -0
- package/src/examples/select-demo.ts +237 -0
- package/src/examples/shader-cube-demo.ts +1015 -0
- package/src/examples/simple-layout-example.ts +591 -0
- package/src/examples/slider-demo.ts +617 -0
- package/src/examples/split-mode-demo.ts +453 -0
- package/src/examples/sprite-animation-demo.ts +443 -0
- package/src/examples/sprite-particle-generator-demo.ts +486 -0
- package/src/examples/static-sprite-demo.ts +193 -0
- package/src/examples/sticky-scroll-example.ts +308 -0
- package/src/examples/styled-text-demo.ts +282 -0
- package/src/examples/tab-select-demo.ts +219 -0
- package/src/examples/terminal-title.ts +29 -0
- package/src/examples/terminal.ts +305 -0
- package/src/examples/text-node-demo.ts +416 -0
- package/src/examples/text-selection-demo.ts +377 -0
- package/src/examples/text-table-demo.ts +503 -0
- package/src/examples/text-truncation-demo.ts +481 -0
- package/src/examples/text-wrap.ts +757 -0
- package/src/examples/texture-loading-demo.ts +259 -0
- package/src/examples/timeline-example.ts +670 -0
- package/src/examples/transparency-demo.ts +400 -0
- package/src/examples/vnode-composition-demo.ts +404 -0
- package/src/examples/wide-grapheme-overlay-demo.ts +280 -0
- package/src/index.ts +24 -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 +984 -0
- package/src/lib/RGBA.ts +204 -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 +41 -0
- package/src/lib/clipboard.ts +47 -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 +322 -0
- package/src/lib/extmarks.test.ts +3457 -0
- package/src/lib/extmarks.ts +843 -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 +21 -0
- package/src/lib/keymapping.test.ts +317 -0
- package/src/lib/keymapping.ts +115 -0
- package/src/lib/objects-in-viewport.test.ts +787 -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 +340 -0
- package/src/lib/parse.keypress-kitty.test.ts +663 -0
- package/src/lib/parse.keypress-kitty.ts +439 -0
- package/src/lib/parse.keypress.test.ts +1849 -0
- package/src/lib/parse.keypress.ts +397 -0
- package/src/lib/parse.mouse.test.ts +552 -0
- package/src/lib/parse.mouse.ts +232 -0
- package/src/lib/paste.ts +16 -0
- package/src/lib/queue.ts +65 -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 +240 -0
- package/src/lib/singleton.ts +28 -0
- package/src/lib/stdin-parser.test.ts +2290 -0
- package/src/lib/stdin-parser.ts +1810 -0
- package/src/lib/styled-text.ts +178 -0
- package/src/lib/terminal-capability-detection.test.ts +202 -0
- package/src/lib/terminal-capability-detection.ts +79 -0
- package/src/lib/terminal-palette.test.ts +878 -0
- package/src/lib/terminal-palette.ts +383 -0
- package/src/lib/tree-sitter/assets/README.md +118 -0
- package/src/lib/tree-sitter/assets/update.ts +334 -0
- package/src/lib/tree-sitter/assets.d.ts +9 -0
- package/src/lib/tree-sitter/cache.test.ts +273 -0
- package/src/lib/tree-sitter/client.test.ts +1165 -0
- package/src/lib/tree-sitter/client.ts +607 -0
- package/src/lib/tree-sitter/default-parsers.ts +86 -0
- package/src/lib/tree-sitter/download-utils.ts +148 -0
- package/src/lib/tree-sitter/index.ts +28 -0
- package/src/lib/tree-sitter/parser.worker.ts +1042 -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 +82 -0
- package/src/lib/tree-sitter-styled-text.test.ts +1253 -0
- package/src/lib/tree-sitter-styled-text.ts +306 -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/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 +930 -0
- package/src/post/filters.ts +489 -0
- package/src/post/matrices.ts +288 -0
- package/src/renderables/ASCIIFont.ts +219 -0
- package/src/renderables/Box.test.ts +205 -0
- package/src/renderables/Box.ts +326 -0
- package/src/renderables/Code.test.ts +2062 -0
- package/src/renderables/Code.ts +357 -0
- package/src/renderables/Diff.regression.test.ts +226 -0
- package/src/renderables/Diff.test.ts +3101 -0
- package/src/renderables/Diff.ts +1211 -0
- package/src/renderables/EditBufferRenderable.test.ts +288 -0
- package/src/renderables/EditBufferRenderable.ts +1166 -0
- package/src/renderables/FrameBuffer.ts +47 -0
- package/src/renderables/Input.test.ts +1228 -0
- package/src/renderables/Input.ts +247 -0
- package/src/renderables/LineNumberRenderable.ts +724 -0
- package/src/renderables/Markdown.ts +1393 -0
- package/src/renderables/ScrollBar.ts +422 -0
- package/src/renderables/ScrollBox.ts +883 -0
- package/src/renderables/Select.test.ts +1033 -0
- package/src/renderables/Select.ts +524 -0
- package/src/renderables/Slider.test.ts +456 -0
- package/src/renderables/Slider.ts +342 -0
- package/src/renderables/TabSelect.test.ts +197 -0
- package/src/renderables/TabSelect.ts +455 -0
- package/src/renderables/Text.selection-buffer.test.ts +123 -0
- package/src/renderables/Text.test.ts +2660 -0
- package/src/renderables/Text.ts +147 -0
- package/src/renderables/TextBufferRenderable.ts +518 -0
- package/src/renderables/TextNode.test.ts +1058 -0
- package/src/renderables/TextNode.ts +325 -0
- package/src/renderables/TextTable.test.ts +1421 -0
- package/src/renderables/TextTable.ts +1344 -0
- package/src/renderables/Textarea.ts +430 -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__/LineNumberRenderable.scrollbox-simple.test.ts +144 -0
- package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +816 -0
- package/src/renderables/__tests__/LineNumberRenderable.test.ts +1865 -0
- package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +85 -0
- package/src/renderables/__tests__/Markdown.code-colors.test.ts +242 -0
- package/src/renderables/__tests__/Markdown.test.ts +2518 -0
- package/src/renderables/__tests__/MultiRenderable.selection.test.ts +87 -0
- package/src/renderables/__tests__/Textarea.buffer.test.ts +682 -0
- package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +675 -0
- package/src/renderables/__tests__/Textarea.editing.test.ts +2041 -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 +3149 -0
- package/src/renderables/__tests__/Textarea.paste.test.ts +357 -0
- package/src/renderables/__tests__/Textarea.rendering.test.ts +1866 -0
- package/src/renderables/__tests__/Textarea.scroll.test.ts +733 -0
- package/src/renderables/__tests__/Textarea.selection.test.ts +1590 -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 +217 -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 +289 -0
- package/src/renderables/index.ts +23 -0
- package/src/renderables/markdown-parser.ts +66 -0
- package/src/renderer.ts +2681 -0
- package/src/runtime-plugin-support.ts +39 -0
- package/src/runtime-plugin.ts +615 -0
- package/src/syntax-style.test.ts +841 -0
- package/src/syntax-style.ts +257 -0
- package/src/testing/README.md +210 -0
- package/src/testing/capture-spans.test.ts +194 -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 +73 -0
- package/src/testing/spy.ts +13 -0
- package/src/testing/test-recorder.test.ts +415 -0
- package/src/testing/test-recorder.ts +145 -0
- package/src/testing/test-renderer.ts +132 -0
- package/src/testing.ts +7 -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 +38 -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 +41 -0
- package/src/tests/hover-cursor.test.ts +98 -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 +352 -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 +1281 -0
- package/src/tests/renderer.clock.test.ts +158 -0
- package/src/tests/renderer.console-startup.test.ts +185 -0
- package/src/tests/renderer.control.test.ts +425 -0
- package/src/tests/renderer.core-slot-binding.test.ts +952 -0
- package/src/tests/renderer.cursor.test.ts +26 -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 +294 -0
- package/src/tests/renderer.idle.test.ts +219 -0
- package/src/tests/renderer.input.test.ts +2237 -0
- package/src/tests/renderer.kitty-flags.test.ts +195 -0
- package/src/tests/renderer.mouse.test.ts +1274 -0
- package/src/tests/renderer.palette.test.ts +629 -0
- package/src/tests/renderer.selection.test.ts +49 -0
- package/src/tests/renderer.slot-registry.test.ts +684 -0
- package/src/tests/renderer.useMouse.test.ts +47 -0
- package/src/tests/runtime-plugin-node-modules-cycle.fixture.ts +76 -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.fixture.ts +11 -0
- package/src/tests/runtime-plugin-support.test.ts +19 -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 +354 -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 +1530 -0
- package/src/tests/wrap-resize-perf.test.ts +276 -0
- package/src/tests/yoga-setters.test.ts +921 -0
- package/src/text-buffer-view.test.ts +705 -0
- package/src/text-buffer-view.ts +189 -0
- package/src/text-buffer.test.ts +347 -0
- package/src/text-buffer.ts +250 -0
- package/src/types.ts +161 -0
- package/src/utils.ts +88 -0
- package/src/zig/ansi.zig +268 -0
- package/src/zig/bench/README.md +50 -0
- package/src/zig/bench/buffer-draw-text-buffer_bench.zig +887 -0
- package/src/zig/bench/edit-buffer_bench.zig +476 -0
- package/src/zig/bench/native-span-feed_bench.zig +100 -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 +470 -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 +273 -0
- package/src/zig/bench/utf8_bench.zig +799 -0
- package/src/zig/bench-utils.zig +431 -0
- package/src/zig/bench.zig +217 -0
- package/src/zig/buffer-methods.zig +211 -0
- package/src/zig/buffer.zig +2281 -0
- package/src/zig/build.zig +289 -0
- package/src/zig/build.zig.zon +16 -0
- package/src/zig/edit-buffer.zig +825 -0
- package/src/zig/editor-view.zig +802 -0
- package/src/zig/event-bus.zig +13 -0
- package/src/zig/event-emitter.zig +65 -0
- package/src/zig/file-logger.zig +92 -0
- package/src/zig/grapheme.zig +599 -0
- package/src/zig/lib.zig +1854 -0
- package/src/zig/link.zig +333 -0
- package/src/zig/logger.zig +43 -0
- package/src/zig/mem-registry.zig +125 -0
- package/src/zig/native-span-feed-bench-lib.zig +7 -0
- package/src/zig/native-span-feed.zig +708 -0
- package/src/zig/renderer.zig +1393 -0
- package/src/zig/rope.zig +1220 -0
- package/src/zig/syntax-style.zig +161 -0
- package/src/zig/terminal.zig +987 -0
- package/src/zig/test.zig +72 -0
- package/src/zig/tests/README.md +18 -0
- package/src/zig/tests/buffer-methods_test.zig +1109 -0
- package/src/zig/tests/buffer_test.zig +2557 -0
- package/src/zig/tests/edit-buffer-history_test.zig +271 -0
- package/src/zig/tests/edit-buffer_test.zig +1689 -0
- package/src/zig/tests/editor-view_test.zig +3299 -0
- package/src/zig/tests/event-emitter_test.zig +249 -0
- package/src/zig/tests/grapheme_test.zig +1304 -0
- package/src/zig/tests/link_test.zig +190 -0
- package/src/zig/tests/mem-registry_test.zig +473 -0
- package/src/zig/tests/memory_leak_regression_test.zig +159 -0
- package/src/zig/tests/native-span-feed_test.zig +1264 -0
- package/src/zig/tests/renderer_test.zig +1017 -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/syntax-style_test.zig +557 -0
- package/src/zig/tests/terminal_test.zig +754 -0
- package/src/zig/tests/text-buffer-drawing_test.zig +3237 -0
- package/src/zig/tests/text-buffer-highlights_test.zig +666 -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 +1035 -0
- package/src/zig/tests/text-buffer-selection_viewport_test.zig +358 -0
- package/src/zig/tests/text-buffer-view_test.zig +3649 -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 +4057 -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/text-buffer-iterators.zig +499 -0
- package/src/zig/text-buffer-segment.zig +404 -0
- package/src/zig/text-buffer-view.zig +1371 -0
- package/src/zig/text-buffer.zig +1180 -0
- package/src/zig/utf8.zig +1948 -0
- package/src/zig/utils.zig +9 -0
- package/src/zig-structs.ts +261 -0
- package/src/zig.ts +3884 -0
- package/tsconfig.build.json +24 -0
- package/tsconfig.json +27 -0
- package/3d/SpriteResourceManager.d.ts +0 -74
- package/3d/SpriteUtils.d.ts +0 -13
- package/3d/TextureUtils.d.ts +0 -24
- package/3d/ThreeRenderable.d.ts +0 -40
- package/3d/WGPURenderer.d.ts +0 -61
- package/3d/animation/ExplodingSpriteEffect.d.ts +0 -71
- package/3d/animation/PhysicsExplodingSpriteEffect.d.ts +0 -76
- package/3d/animation/SpriteAnimator.d.ts +0 -124
- package/3d/animation/SpriteParticleGenerator.d.ts +0 -62
- package/3d/canvas.d.ts +0 -44
- package/3d/index.d.ts +0 -12
- package/3d/physics/PlanckPhysicsAdapter.d.ts +0 -19
- package/3d/physics/RapierPhysicsAdapter.d.ts +0 -19
- package/3d/physics/physics-interface.d.ts +0 -27
- package/3d.d.ts +0 -2
- package/3d.js +0 -34041
- package/3d.js.map +0 -155
- package/LICENSE +0 -21
- package/NativeSpanFeed.d.ts +0 -41
- package/Renderable.d.ts +0 -334
- package/animation/Timeline.d.ts +0 -126
- package/ansi.d.ts +0 -13
- package/buffer.d.ts +0 -111
- package/console.d.ts +0 -144
- package/edit-buffer.d.ts +0 -98
- package/editor-view.d.ts +0 -73
- package/index-8fks7yv1.js +0 -411
- package/index-8fks7yv1.js.map +0 -10
- package/index-egy5e2rs.js +0 -12267
- package/index-egy5e2rs.js.map +0 -42
- package/index-tse8gzh0.js +0 -20614
- package/index-tse8gzh0.js.map +0 -67
- package/index.d.ts +0 -23
- package/index.js +0 -478
- package/index.js.map +0 -9
- package/lib/KeyHandler.d.ts +0 -61
- package/lib/RGBA.d.ts +0 -25
- package/lib/ascii.font.d.ts +0 -508
- package/lib/border.d.ts +0 -51
- package/lib/bunfs.d.ts +0 -7
- package/lib/clipboard.d.ts +0 -17
- package/lib/clock.d.ts +0 -15
- package/lib/data-paths.d.ts +0 -26
- package/lib/debounce.d.ts +0 -42
- package/lib/detect-links.d.ts +0 -6
- package/lib/env.d.ts +0 -42
- package/lib/extmarks-history.d.ts +0 -17
- package/lib/extmarks.d.ts +0 -89
- package/lib/hast-styled-text.d.ts +0 -17
- package/lib/index.d.ts +0 -21
- package/lib/keymapping.d.ts +0 -25
- package/lib/objects-in-viewport.d.ts +0 -24
- package/lib/output.capture.d.ts +0 -24
- package/lib/parse.keypress-kitty.d.ts +0 -2
- package/lib/parse.keypress.d.ts +0 -26
- package/lib/parse.mouse.d.ts +0 -30
- package/lib/paste.d.ts +0 -7
- package/lib/queue.d.ts +0 -15
- package/lib/renderable.validations.d.ts +0 -12
- package/lib/scroll-acceleration.d.ts +0 -43
- package/lib/selection.d.ts +0 -63
- package/lib/singleton.d.ts +0 -7
- package/lib/stdin-parser.d.ts +0 -87
- package/lib/styled-text.d.ts +0 -63
- package/lib/terminal-capability-detection.d.ts +0 -30
- package/lib/terminal-palette.d.ts +0 -50
- package/lib/tree-sitter/assets/update.d.ts +0 -11
- package/lib/tree-sitter/client.d.ts +0 -47
- package/lib/tree-sitter/default-parsers.d.ts +0 -2
- package/lib/tree-sitter/download-utils.d.ts +0 -21
- package/lib/tree-sitter/index.d.ts +0 -8
- package/lib/tree-sitter/parser.worker.d.ts +0 -1
- package/lib/tree-sitter/parsers-config.d.ts +0 -53
- package/lib/tree-sitter/resolve-ft.d.ts +0 -5
- package/lib/tree-sitter/types.d.ts +0 -82
- package/lib/tree-sitter-styled-text.d.ts +0 -14
- package/lib/validate-dir-name.d.ts +0 -1
- package/lib/yoga.options.d.ts +0 -32
- package/parser.worker.js +0 -899
- package/parser.worker.js.map +0 -12
- package/plugins/core-slot.d.ts +0 -72
- package/plugins/registry.d.ts +0 -42
- package/plugins/types.d.ts +0 -34
- package/post/effects.d.ts +0 -147
- package/post/filters.d.ts +0 -65
- package/post/matrices.d.ts +0 -20
- package/renderables/ASCIIFont.d.ts +0 -52
- package/renderables/Box.d.ts +0 -81
- package/renderables/Code.d.ts +0 -78
- package/renderables/Diff.d.ts +0 -142
- package/renderables/EditBufferRenderable.d.ts +0 -237
- package/renderables/FrameBuffer.d.ts +0 -16
- package/renderables/Input.d.ts +0 -67
- package/renderables/LineNumberRenderable.d.ts +0 -78
- package/renderables/Markdown.d.ts +0 -185
- package/renderables/ScrollBar.d.ts +0 -77
- package/renderables/ScrollBox.d.ts +0 -124
- package/renderables/Select.d.ts +0 -115
- package/renderables/Slider.d.ts +0 -47
- package/renderables/TabSelect.d.ts +0 -96
- package/renderables/Text.d.ts +0 -36
- package/renderables/TextBufferRenderable.d.ts +0 -105
- package/renderables/TextNode.d.ts +0 -91
- package/renderables/TextTable.d.ts +0 -140
- package/renderables/Textarea.d.ts +0 -63
- package/renderables/TimeToFirstDraw.d.ts +0 -24
- package/renderables/__tests__/renderable-test-utils.d.ts +0 -12
- package/renderables/composition/VRenderable.d.ts +0 -16
- package/renderables/composition/constructs.d.ts +0 -35
- package/renderables/composition/vnode.d.ts +0 -46
- package/renderables/index.d.ts +0 -23
- package/renderables/markdown-parser.d.ts +0 -10
- package/renderer.d.ts +0 -419
- package/runtime-plugin-support.d.ts +0 -3
- package/runtime-plugin-support.js +0 -29
- package/runtime-plugin-support.js.map +0 -10
- package/runtime-plugin.d.ts +0 -16
- package/runtime-plugin.js +0 -16
- package/runtime-plugin.js.map +0 -9
- package/syntax-style.d.ts +0 -54
- package/testing/manual-clock.d.ts +0 -17
- package/testing/mock-keys.d.ts +0 -81
- package/testing/mock-mouse.d.ts +0 -38
- package/testing/mock-tree-sitter-client.d.ts +0 -23
- package/testing/spy.d.ts +0 -7
- package/testing/test-recorder.d.ts +0 -61
- package/testing/test-renderer.d.ts +0 -23
- package/testing.d.ts +0 -6
- package/testing.js +0 -697
- package/testing.js.map +0 -15
- package/text-buffer-view.d.ts +0 -42
- package/text-buffer.d.ts +0 -67
- package/types.d.ts +0 -139
- package/utils.d.ts +0 -14
- package/zig-structs.d.ts +0 -155
- package/zig.d.ts +0 -353
- /package/{assets → src/lib/tree-sitter/assets}/javascript/highlights.scm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/javascript/tree-sitter-javascript.wasm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/markdown/highlights.scm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/markdown/injections.scm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/markdown/tree-sitter-markdown.wasm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/markdown_inline/highlights.scm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/typescript/highlights.scm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/typescript/tree-sitter-typescript.wasm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/zig/highlights.scm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/zig/tree-sitter-zig.wasm +0 -0
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach, afterEach } from "bun:test"
|
|
2
|
+
import { createTestRenderer, type TestRenderer, type MockMouse } from "../../testing/test-renderer.js"
|
|
3
|
+
import { createTextareaRenderable, simulateFrames as _simulateFrames } from "./renderable-test-utils.js"
|
|
4
|
+
import { TestRecorder } from "../../testing/test-recorder.js"
|
|
5
|
+
import { RGBA } from "../../lib/RGBA.js"
|
|
6
|
+
import { ManualClock } from "../../testing/manual-clock.js"
|
|
7
|
+
|
|
8
|
+
let currentRenderer: TestRenderer
|
|
9
|
+
let renderOnce: () => Promise<void>
|
|
10
|
+
let currentMouse: MockMouse
|
|
11
|
+
let clock: ManualClock
|
|
12
|
+
|
|
13
|
+
const simulateFrames = (ms: number, frameInterval?: number) => _simulateFrames(clock, renderOnce, ms, frameInterval)
|
|
14
|
+
|
|
15
|
+
describe("Textarea - Scroll Tests", () => {
|
|
16
|
+
beforeEach(async () => {
|
|
17
|
+
clock = new ManualClock()
|
|
18
|
+
;({
|
|
19
|
+
renderer: currentRenderer,
|
|
20
|
+
renderOnce,
|
|
21
|
+
mockMouse: currentMouse,
|
|
22
|
+
} = await createTestRenderer({
|
|
23
|
+
width: 80,
|
|
24
|
+
height: 24,
|
|
25
|
+
clock,
|
|
26
|
+
}))
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
currentRenderer.destroy()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
describe("Mouse Selection Auto-Scroll", () => {
|
|
34
|
+
it("should auto-scroll down when dragging selection below viewport", async () => {
|
|
35
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
36
|
+
initialValue: Array.from({ length: 50 }, (_, i) => `Line ${i}`).join("\n"),
|
|
37
|
+
width: 40,
|
|
38
|
+
height: 10,
|
|
39
|
+
selectable: true,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
// Position at top
|
|
43
|
+
editor.editBuffer.gotoLine(0)
|
|
44
|
+
await renderOnce()
|
|
45
|
+
|
|
46
|
+
const viewportBefore = editor.editorView.getViewport()
|
|
47
|
+
expect(viewportBefore.offsetY).toBe(0)
|
|
48
|
+
|
|
49
|
+
// Start dragging from top
|
|
50
|
+
await currentMouse.pressDown(editor.x, editor.y)
|
|
51
|
+
|
|
52
|
+
// Move to bottom edge to trigger auto-scroll (keep button pressed)
|
|
53
|
+
await currentMouse.moveTo(editor.x + 5, editor.y + editor.height - 1)
|
|
54
|
+
|
|
55
|
+
// Simulate 1 second of frames for auto-scroll
|
|
56
|
+
await simulateFrames(1000)
|
|
57
|
+
|
|
58
|
+
const viewportAfter = editor.editorView.getViewport()
|
|
59
|
+
|
|
60
|
+
// Release mouse
|
|
61
|
+
await currentMouse.release(editor.x + 5, editor.y + editor.height - 1)
|
|
62
|
+
|
|
63
|
+
// Viewport should have scrolled down significantly
|
|
64
|
+
expect(viewportAfter.offsetY).toBeGreaterThan(viewportBefore.offsetY)
|
|
65
|
+
|
|
66
|
+
editor.destroy()
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it("should set cursor to selection focus when selecting", async () => {
|
|
70
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
71
|
+
initialValue: Array.from({ length: 50 }, (_, i) => `Line ${i}`).join("\n"),
|
|
72
|
+
width: 40,
|
|
73
|
+
height: 10,
|
|
74
|
+
selectable: true,
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
editor.editBuffer.gotoLine(0)
|
|
78
|
+
await renderOnce()
|
|
79
|
+
|
|
80
|
+
const cursorBefore = editor.logicalCursor
|
|
81
|
+
|
|
82
|
+
// Start selection and drag
|
|
83
|
+
await currentMouse.drag(editor.x, editor.y, editor.x + 10, editor.y + 5)
|
|
84
|
+
await renderOnce()
|
|
85
|
+
|
|
86
|
+
const cursorAfter = editor.logicalCursor
|
|
87
|
+
|
|
88
|
+
// Cursor should have moved to the selection focus position
|
|
89
|
+
expect(cursorAfter.row).toBeGreaterThan(cursorBefore.row)
|
|
90
|
+
|
|
91
|
+
editor.destroy()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it("should auto-scroll up when dragging selection above viewport", async () => {
|
|
95
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
96
|
+
initialValue: Array.from({ length: 100 }, (_, i) => `Line ${i}`).join("\n"),
|
|
97
|
+
width: 40,
|
|
98
|
+
height: 10,
|
|
99
|
+
selectable: true,
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
// Start somewhere in the middle so we can scroll up
|
|
103
|
+
editor.editBuffer.gotoLine(40)
|
|
104
|
+
await renderOnce()
|
|
105
|
+
|
|
106
|
+
const viewportBefore = editor.editorView.getViewport()
|
|
107
|
+
expect(viewportBefore.offsetY).toBeGreaterThan(0)
|
|
108
|
+
|
|
109
|
+
// Start dragging from within viewport
|
|
110
|
+
await currentMouse.pressDown(editor.x + 2, editor.y + 5)
|
|
111
|
+
// Drag to the top edge (within bounds) to trigger upward auto-scroll
|
|
112
|
+
await currentMouse.moveTo(editor.x + 2, editor.y)
|
|
113
|
+
|
|
114
|
+
// Simulate 1 second of frames for auto-scroll
|
|
115
|
+
await simulateFrames(1000)
|
|
116
|
+
|
|
117
|
+
const viewportAfter = editor.editorView.getViewport()
|
|
118
|
+
|
|
119
|
+
await currentMouse.release(editor.x + 2, editor.y)
|
|
120
|
+
|
|
121
|
+
expect(viewportAfter.offsetY).toBeLessThan(viewportBefore.offsetY)
|
|
122
|
+
|
|
123
|
+
editor.destroy()
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it("should stop auto-scroll when selection ends", async () => {
|
|
127
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
128
|
+
initialValue: Array.from({ length: 100 }, (_, i) => `Line ${i}`).join("\n"),
|
|
129
|
+
width: 40,
|
|
130
|
+
height: 10,
|
|
131
|
+
selectable: true,
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
editor.editBuffer.gotoLine(0)
|
|
135
|
+
await renderOnce()
|
|
136
|
+
|
|
137
|
+
await currentMouse.pressDown(editor.x + 2, editor.y)
|
|
138
|
+
await currentMouse.moveTo(editor.x + 2, editor.y + editor.height - 1)
|
|
139
|
+
|
|
140
|
+
// Simulate 1 second of auto-scroll
|
|
141
|
+
await simulateFrames(1000)
|
|
142
|
+
|
|
143
|
+
// End selection (mouse up) and render a few more frames
|
|
144
|
+
await currentMouse.release(editor.x + 2, editor.y + editor.height - 1)
|
|
145
|
+
await simulateFrames(200)
|
|
146
|
+
|
|
147
|
+
const viewportAfterRelease = editor.editorView.getViewport()
|
|
148
|
+
|
|
149
|
+
// If selection-end notifications work, viewport should remain stable
|
|
150
|
+
await simulateFrames(1000)
|
|
151
|
+
|
|
152
|
+
const viewportFinal = editor.editorView.getViewport()
|
|
153
|
+
|
|
154
|
+
expect(viewportFinal.offsetY).toBe(viewportAfterRelease.offsetY)
|
|
155
|
+
|
|
156
|
+
editor.destroy()
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
describe("Selection Focus Clamping", () => {
|
|
161
|
+
it("should clamp cursor when dragging selection focus beyond buffer bounds", async () => {
|
|
162
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
163
|
+
initialValue: Array.from({ length: 10 }, (_, i) => `Line ${i}`).join("\n"),
|
|
164
|
+
width: 40,
|
|
165
|
+
height: 10,
|
|
166
|
+
selectable: true,
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
await renderOnce()
|
|
170
|
+
|
|
171
|
+
// Start selection at the top of the buffer
|
|
172
|
+
await currentMouse.pressDown(editor.x, editor.y)
|
|
173
|
+
await renderOnce()
|
|
174
|
+
|
|
175
|
+
// Drag selection far below the renderable's bounds (focusY way beyond buffer)
|
|
176
|
+
await currentMouse.moveTo(editor.x + 2, editor.y + 200)
|
|
177
|
+
await renderOnce()
|
|
178
|
+
|
|
179
|
+
const cursor = editor.logicalCursor
|
|
180
|
+
expect(cursor.row).toBe(9)
|
|
181
|
+
|
|
182
|
+
await currentMouse.release(editor.x + 2, editor.y + 200)
|
|
183
|
+
await renderOnce()
|
|
184
|
+
|
|
185
|
+
editor.destroy()
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
describe("Mouse Click Cursor Positioning", () => {
|
|
190
|
+
it("should set cursor when clicking without dragging", async () => {
|
|
191
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
192
|
+
initialValue: "Line 0\nLine 1\nLine 2\nLine 3\nLine 4",
|
|
193
|
+
width: 40,
|
|
194
|
+
height: 10,
|
|
195
|
+
selectable: true,
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
editor.editBuffer.gotoLine(0)
|
|
199
|
+
await renderOnce()
|
|
200
|
+
|
|
201
|
+
const cursorBefore = editor.logicalCursor
|
|
202
|
+
expect(cursorBefore.row).toBe(0)
|
|
203
|
+
expect(cursorBefore.col).toBe(0)
|
|
204
|
+
|
|
205
|
+
// Click on line 2, column 3
|
|
206
|
+
await currentMouse.click(editor.x + 3, editor.y + 2)
|
|
207
|
+
await renderOnce()
|
|
208
|
+
|
|
209
|
+
const cursorAfter = editor.logicalCursor
|
|
210
|
+
expect(cursorAfter.row).toBe(2)
|
|
211
|
+
expect(cursorAfter.col).toBe(3)
|
|
212
|
+
|
|
213
|
+
editor.destroy()
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it("should set cursor when clicking on empty line", async () => {
|
|
217
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
218
|
+
initialValue: "Line 0\n\nLine 2\n\nLine 4",
|
|
219
|
+
width: 40,
|
|
220
|
+
height: 10,
|
|
221
|
+
selectable: true,
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
await renderOnce()
|
|
225
|
+
|
|
226
|
+
// Click on empty line 1
|
|
227
|
+
await currentMouse.click(editor.x + 5, editor.y + 1)
|
|
228
|
+
await renderOnce()
|
|
229
|
+
|
|
230
|
+
const cursor1 = editor.logicalCursor
|
|
231
|
+
expect(cursor1.row).toBe(1)
|
|
232
|
+
expect(cursor1.col).toBe(0) // Empty line, cursor at column 0
|
|
233
|
+
|
|
234
|
+
// Click on empty line 3
|
|
235
|
+
await currentMouse.click(editor.x + 10, editor.y + 3)
|
|
236
|
+
await renderOnce()
|
|
237
|
+
|
|
238
|
+
const cursor2 = editor.logicalCursor
|
|
239
|
+
expect(cursor2.row).toBe(3)
|
|
240
|
+
expect(cursor2.col).toBe(0)
|
|
241
|
+
|
|
242
|
+
editor.destroy()
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it("should clamp cursor when clicking beyond line end", async () => {
|
|
246
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
247
|
+
initialValue: "Short\nMedium line\nVery long line here",
|
|
248
|
+
width: 40,
|
|
249
|
+
height: 10,
|
|
250
|
+
selectable: true,
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
await renderOnce()
|
|
254
|
+
|
|
255
|
+
// Click way beyond the end of "Short" (5 chars)
|
|
256
|
+
await currentMouse.click(editor.x + 20, editor.y)
|
|
257
|
+
await renderOnce()
|
|
258
|
+
|
|
259
|
+
const cursor = editor.logicalCursor
|
|
260
|
+
expect(cursor.row).toBe(0)
|
|
261
|
+
expect(cursor.col).toBeLessThanOrEqual(5) // Clamped to line end
|
|
262
|
+
|
|
263
|
+
editor.destroy()
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it("should set cursor when clicking with scrolled viewport", async () => {
|
|
267
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
268
|
+
initialValue: Array.from({ length: 50 }, (_, i) => `Line ${i}`).join("\n"),
|
|
269
|
+
width: 40,
|
|
270
|
+
height: 10,
|
|
271
|
+
selectable: true,
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
// Scroll to middle
|
|
275
|
+
editor.editBuffer.gotoLine(25)
|
|
276
|
+
await renderOnce()
|
|
277
|
+
|
|
278
|
+
const viewport = editor.editorView.getViewport()
|
|
279
|
+
expect(viewport.offsetY).toBeGreaterThan(10)
|
|
280
|
+
|
|
281
|
+
const offsetYBefore = viewport.offsetY
|
|
282
|
+
|
|
283
|
+
// Click on first visible line (which is line offsetY in absolute terms)
|
|
284
|
+
await currentMouse.click(editor.x + 3, editor.y)
|
|
285
|
+
await renderOnce()
|
|
286
|
+
|
|
287
|
+
const cursor = editor.logicalCursor
|
|
288
|
+
expect(cursor.row).toBe(offsetYBefore) // Should be the first visible line
|
|
289
|
+
expect(cursor.col).toBe(3)
|
|
290
|
+
|
|
291
|
+
editor.destroy()
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
describe("Mouse Wheel Scrolling", () => {
|
|
296
|
+
it("should scroll down on mouse wheel down", async () => {
|
|
297
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
298
|
+
initialValue: Array.from({ length: 50 }, (_, i) => `Line ${i}`).join("\n"),
|
|
299
|
+
width: 40,
|
|
300
|
+
height: 10,
|
|
301
|
+
selectable: true,
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
editor.editBuffer.gotoLine(0)
|
|
305
|
+
await renderOnce()
|
|
306
|
+
|
|
307
|
+
const viewportBefore = editor.editorView.getViewport()
|
|
308
|
+
expect(viewportBefore.offsetY).toBe(0)
|
|
309
|
+
|
|
310
|
+
// Scroll down by 3 lines
|
|
311
|
+
for (let i = 0; i < 3; i++) {
|
|
312
|
+
await currentMouse.scroll(editor.x + 5, editor.y + 5, "down")
|
|
313
|
+
}
|
|
314
|
+
await renderOnce()
|
|
315
|
+
|
|
316
|
+
const viewportAfter = editor.editorView.getViewport()
|
|
317
|
+
expect(viewportAfter.offsetY).toBe(3)
|
|
318
|
+
|
|
319
|
+
editor.destroy()
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it("should move cursor into the viewport when wheel scrolling", async () => {
|
|
323
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
324
|
+
initialValue: Array.from({ length: 50 }, (_, i) => `Line ${i}`).join("\n"),
|
|
325
|
+
width: 40,
|
|
326
|
+
height: 10,
|
|
327
|
+
selectable: true,
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
editor.editBuffer.gotoLine(0)
|
|
331
|
+
await renderOnce()
|
|
332
|
+
|
|
333
|
+
const cursorBefore = editor.logicalCursor
|
|
334
|
+
expect(cursorBefore.row).toBe(0)
|
|
335
|
+
|
|
336
|
+
// Scroll down a few lines
|
|
337
|
+
for (let i = 0; i < 3; i++) {
|
|
338
|
+
await currentMouse.scroll(editor.x + 5, editor.y + 5, "down")
|
|
339
|
+
}
|
|
340
|
+
await renderOnce()
|
|
341
|
+
|
|
342
|
+
const viewportAfter = editor.editorView.getViewport()
|
|
343
|
+
const cursorAfter = editor.logicalCursor
|
|
344
|
+
|
|
345
|
+
// Wheel scrolling uses setViewport(..., moveCursor=true), which moves the cursor to stay visible
|
|
346
|
+
expect(cursorAfter.row).toBeGreaterThan(cursorBefore.row)
|
|
347
|
+
expect(cursorAfter.row).toBeGreaterThanOrEqual(viewportAfter.offsetY)
|
|
348
|
+
expect(cursorAfter.row).toBeLessThan(viewportAfter.offsetY + viewportAfter.height)
|
|
349
|
+
|
|
350
|
+
editor.destroy()
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
it("should scroll up on mouse wheel up", async () => {
|
|
354
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
355
|
+
initialValue: Array.from({ length: 50 }, (_, i) => `Line ${i}`).join("\n"),
|
|
356
|
+
width: 40,
|
|
357
|
+
height: 10,
|
|
358
|
+
selectable: true,
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
// Start at line 20
|
|
362
|
+
editor.editBuffer.gotoLine(20)
|
|
363
|
+
await renderOnce()
|
|
364
|
+
|
|
365
|
+
const viewportBefore = editor.editorView.getViewport()
|
|
366
|
+
expect(viewportBefore.offsetY).toBeGreaterThan(10)
|
|
367
|
+
const offsetBefore = viewportBefore.offsetY
|
|
368
|
+
|
|
369
|
+
// Scroll up by 5 lines
|
|
370
|
+
for (let i = 0; i < 5; i++) {
|
|
371
|
+
await currentMouse.scroll(editor.x + 5, editor.y + 5, "up")
|
|
372
|
+
}
|
|
373
|
+
await renderOnce()
|
|
374
|
+
|
|
375
|
+
const viewportAfter = editor.editorView.getViewport()
|
|
376
|
+
expect(viewportAfter.offsetY).toBe(offsetBefore - 5)
|
|
377
|
+
|
|
378
|
+
editor.destroy()
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
it("should not scroll beyond top", async () => {
|
|
382
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
383
|
+
initialValue: Array.from({ length: 50 }, (_, i) => `Line ${i}`).join("\n"),
|
|
384
|
+
width: 40,
|
|
385
|
+
height: 10,
|
|
386
|
+
selectable: true,
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
editor.editBuffer.gotoLine(2)
|
|
390
|
+
await renderOnce()
|
|
391
|
+
|
|
392
|
+
// Scroll up by 100 lines (should clamp to 0)
|
|
393
|
+
for (let i = 0; i < 100; i++) {
|
|
394
|
+
await currentMouse.scroll(editor.x + 5, editor.y + 5, "up")
|
|
395
|
+
}
|
|
396
|
+
await renderOnce()
|
|
397
|
+
|
|
398
|
+
const viewport = editor.editorView.getViewport()
|
|
399
|
+
expect(viewport.offsetY).toBe(0)
|
|
400
|
+
|
|
401
|
+
editor.destroy()
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
it("should not scroll beyond bottom", async () => {
|
|
405
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
406
|
+
initialValue: Array.from({ length: 20 }, (_, i) => `Line ${i}`).join("\n"),
|
|
407
|
+
width: 40,
|
|
408
|
+
height: 10,
|
|
409
|
+
selectable: true,
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
await renderOnce()
|
|
413
|
+
|
|
414
|
+
// Scroll down by 100 lines (should clamp to maxOffsetY = 20 - 10 = 10)
|
|
415
|
+
for (let i = 0; i < 100; i++) {
|
|
416
|
+
await currentMouse.scroll(editor.x + 5, editor.y + 5, "down")
|
|
417
|
+
}
|
|
418
|
+
await renderOnce()
|
|
419
|
+
|
|
420
|
+
const viewport = editor.editorView.getViewport()
|
|
421
|
+
expect(viewport.offsetY).toBe(10) // 20 lines - 10 viewport height
|
|
422
|
+
|
|
423
|
+
editor.destroy()
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
it("should allow mouse wheel scroll after selection auto-scroll", async () => {
|
|
427
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
428
|
+
initialValue: Array.from({ length: 100 }, (_, i) => `Line ${i}`).join("\n"),
|
|
429
|
+
width: 40,
|
|
430
|
+
height: 10,
|
|
431
|
+
selectable: true,
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
// Position at top
|
|
435
|
+
editor.editBuffer.gotoLine(0)
|
|
436
|
+
await renderOnce()
|
|
437
|
+
|
|
438
|
+
const viewportInitial = editor.editorView.getViewport()
|
|
439
|
+
expect(viewportInitial.offsetY).toBe(0)
|
|
440
|
+
|
|
441
|
+
// Drag selection from top to way below viewport to trigger auto-scroll to bottom
|
|
442
|
+
await currentMouse.pressDown(editor.x, editor.y)
|
|
443
|
+
await currentMouse.moveTo(editor.x + 5, editor.y + editor.height - 1)
|
|
444
|
+
|
|
445
|
+
// Simulate 2 seconds for auto-scroll to reach near the end
|
|
446
|
+
await simulateFrames(2000)
|
|
447
|
+
|
|
448
|
+
// Release mouse to complete selection
|
|
449
|
+
await currentMouse.release(editor.x + 5, editor.y + editor.height - 1)
|
|
450
|
+
|
|
451
|
+
const viewportAfterSelection = editor.editorView.getViewport()
|
|
452
|
+
|
|
453
|
+
// Should have scrolled down significantly
|
|
454
|
+
expect(viewportAfterSelection.offsetY).toBeGreaterThan(20)
|
|
455
|
+
|
|
456
|
+
// Now use mouse wheel to scroll all the way back up
|
|
457
|
+
for (let i = 0; i < 100; i++) {
|
|
458
|
+
await currentMouse.scroll(editor.x + 5, editor.y + 5, "up")
|
|
459
|
+
}
|
|
460
|
+
await renderOnce()
|
|
461
|
+
|
|
462
|
+
const viewportFinal = editor.editorView.getViewport()
|
|
463
|
+
|
|
464
|
+
// Should have scrolled all the way back to top
|
|
465
|
+
expect(viewportFinal.offsetY).toBe(0)
|
|
466
|
+
|
|
467
|
+
editor.destroy()
|
|
468
|
+
})
|
|
469
|
+
})
|
|
470
|
+
|
|
471
|
+
describe("Mouse Wheel Horizontal Scrolling", () => {
|
|
472
|
+
it("should scroll horizontally with wheel when wrapping is disabled", async () => {
|
|
473
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
474
|
+
initialValue: "A".repeat(200),
|
|
475
|
+
width: 20,
|
|
476
|
+
height: 5,
|
|
477
|
+
wrapMode: "none",
|
|
478
|
+
selectable: true,
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
await renderOnce()
|
|
482
|
+
|
|
483
|
+
// Keep a selection active so native updateBeforeRender doesn't auto-scroll viewport back to cursor
|
|
484
|
+
await currentMouse.drag(editor.x, editor.y, editor.x + 1, editor.y)
|
|
485
|
+
await renderOnce()
|
|
486
|
+
|
|
487
|
+
const viewportBefore = editor.editorView.getViewport()
|
|
488
|
+
expect(viewportBefore.offsetX).toBe(0)
|
|
489
|
+
|
|
490
|
+
for (let i = 0; i < 5; i++) {
|
|
491
|
+
await currentMouse.scroll(editor.x + 2, editor.y + 2, "right")
|
|
492
|
+
}
|
|
493
|
+
await renderOnce()
|
|
494
|
+
|
|
495
|
+
const viewportAfterRight = editor.editorView.getViewport()
|
|
496
|
+
expect(viewportAfterRight.offsetX).toBe(5)
|
|
497
|
+
|
|
498
|
+
for (let i = 0; i < 3; i++) {
|
|
499
|
+
await currentMouse.scroll(editor.x + 2, editor.y + 2, "left")
|
|
500
|
+
}
|
|
501
|
+
await renderOnce()
|
|
502
|
+
|
|
503
|
+
const viewportAfterLeft = editor.editorView.getViewport()
|
|
504
|
+
expect(viewportAfterLeft.offsetX).toBe(2)
|
|
505
|
+
|
|
506
|
+
editor.destroy()
|
|
507
|
+
})
|
|
508
|
+
|
|
509
|
+
it("should not scroll horizontally with wheel when wrapping is enabled", async () => {
|
|
510
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
511
|
+
initialValue: "A".repeat(200),
|
|
512
|
+
width: 20,
|
|
513
|
+
height: 5,
|
|
514
|
+
wrapMode: "word",
|
|
515
|
+
selectable: true,
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
await renderOnce()
|
|
519
|
+
|
|
520
|
+
// Keep selection active to avoid cursor-driven viewport changes
|
|
521
|
+
await currentMouse.drag(editor.x, editor.y, editor.x + 1, editor.y)
|
|
522
|
+
await renderOnce()
|
|
523
|
+
|
|
524
|
+
const viewportBefore = editor.editorView.getViewport()
|
|
525
|
+
expect(viewportBefore.offsetX).toBe(0)
|
|
526
|
+
|
|
527
|
+
for (let i = 0; i < 5; i++) {
|
|
528
|
+
await currentMouse.scroll(editor.x + 2, editor.y + 2, "right")
|
|
529
|
+
}
|
|
530
|
+
await renderOnce()
|
|
531
|
+
|
|
532
|
+
const viewportAfter = editor.editorView.getViewport()
|
|
533
|
+
expect(viewportAfter.offsetX).toBe(0)
|
|
534
|
+
|
|
535
|
+
editor.destroy()
|
|
536
|
+
})
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
describe("Viewport Offset After Resize", () => {
|
|
540
|
+
it("should keep content at bottom when resizing from narrow wrapped to wide unwrapped", async () => {
|
|
541
|
+
const { textarea: editor, root } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
542
|
+
initialValue: Array.from(
|
|
543
|
+
{ length: 15 },
|
|
544
|
+
(_, i) => `This is line ${i.toString().padStart(2, "0")} with enough text to wrap when narrow`,
|
|
545
|
+
).join("\n"),
|
|
546
|
+
width: 10,
|
|
547
|
+
height: 10,
|
|
548
|
+
wrapMode: "word",
|
|
549
|
+
selectable: true,
|
|
550
|
+
})
|
|
551
|
+
|
|
552
|
+
await renderOnce()
|
|
553
|
+
|
|
554
|
+
editor.focus()
|
|
555
|
+
|
|
556
|
+
// Scroll to the very bottom
|
|
557
|
+
editor.editBuffer.gotoLine(999)
|
|
558
|
+
await renderOnce()
|
|
559
|
+
|
|
560
|
+
const viewportAtBottom = editor.editorView.getViewport()
|
|
561
|
+
const totalVirtualLinesNarrow = editor.editorView.getTotalVirtualLineCount()
|
|
562
|
+
|
|
563
|
+
expect(viewportAtBottom.offsetY).toBeGreaterThan(10)
|
|
564
|
+
|
|
565
|
+
// Resize to much wider - this will unwrap most lines
|
|
566
|
+
editor.width = 80
|
|
567
|
+
root.yogaNode.calculateLayout(80, 24)
|
|
568
|
+
await renderOnce()
|
|
569
|
+
|
|
570
|
+
const viewportAfterResize = editor.editorView.getViewport()
|
|
571
|
+
const totalVirtualLinesWide = editor.editorView.getTotalVirtualLineCount()
|
|
572
|
+
|
|
573
|
+
// After unwrapping, total lines should be much less (close to 15 logical lines)
|
|
574
|
+
expect(totalVirtualLinesWide).toBeLessThan(totalVirtualLinesNarrow)
|
|
575
|
+
|
|
576
|
+
// Content should still be at the bottom of the viewport
|
|
577
|
+
// The last line should be visible at the bottom
|
|
578
|
+
const maxOffsetYWide = Math.max(0, totalVirtualLinesWide - viewportAfterResize.height)
|
|
579
|
+
expect(viewportAfterResize.offsetY).toBe(maxOffsetYWide)
|
|
580
|
+
|
|
581
|
+
editor.destroy()
|
|
582
|
+
})
|
|
583
|
+
|
|
584
|
+
it("should clamp horizontal viewport offset when resizing wider with no wrap", async () => {
|
|
585
|
+
const { textarea: editor, root } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
586
|
+
initialValue: "A".repeat(200),
|
|
587
|
+
width: 20,
|
|
588
|
+
height: 10,
|
|
589
|
+
wrapMode: "none",
|
|
590
|
+
selectable: true,
|
|
591
|
+
})
|
|
592
|
+
|
|
593
|
+
await renderOnce()
|
|
594
|
+
|
|
595
|
+
// Scroll horizontally to the far right
|
|
596
|
+
editor.focus()
|
|
597
|
+
for (let i = 0; i < 100; i++) {
|
|
598
|
+
editor.moveCursorRight()
|
|
599
|
+
}
|
|
600
|
+
await renderOnce()
|
|
601
|
+
|
|
602
|
+
const viewportNarrow = editor.editorView.getViewport()
|
|
603
|
+
|
|
604
|
+
expect(viewportNarrow.offsetX).toBeGreaterThan(50)
|
|
605
|
+
|
|
606
|
+
// Resize to much wider - viewport offsetX might now exceed valid range
|
|
607
|
+
editor.width = 250
|
|
608
|
+
root.yogaNode.calculateLayout(80, 24)
|
|
609
|
+
await renderOnce()
|
|
610
|
+
|
|
611
|
+
const viewportWide = editor.editorView.getViewport()
|
|
612
|
+
const totalLineWidthWide = editor.lineInfo.lineWidthColsMax
|
|
613
|
+
const maxOffsetXWide = Math.max(0, totalLineWidthWide - viewportWide.width)
|
|
614
|
+
|
|
615
|
+
expect(viewportWide.offsetX).toBeLessThanOrEqual(maxOffsetXWide)
|
|
616
|
+
|
|
617
|
+
editor.destroy()
|
|
618
|
+
})
|
|
619
|
+
|
|
620
|
+
it("should allow scrolling and selecting last line immediately after resize from wide to narrow", async () => {
|
|
621
|
+
const { textarea: editor, root } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
622
|
+
initialValue: Array.from(
|
|
623
|
+
{ length: 20 },
|
|
624
|
+
(_, i) =>
|
|
625
|
+
`Line ${i.toString().padStart(2, "0")} with enough text content to cause wrapping when viewport becomes narrow`,
|
|
626
|
+
).join("\n"),
|
|
627
|
+
width: 80,
|
|
628
|
+
height: 10,
|
|
629
|
+
wrapMode: "word",
|
|
630
|
+
selectable: true,
|
|
631
|
+
})
|
|
632
|
+
|
|
633
|
+
await renderOnce()
|
|
634
|
+
|
|
635
|
+
// Resize to very narrow - this will cause heavy wrapping
|
|
636
|
+
editor.width = 10
|
|
637
|
+
root.yogaNode.calculateLayout(80, 24)
|
|
638
|
+
await renderOnce()
|
|
639
|
+
|
|
640
|
+
const viewportAfterResize = editor.editorView.getViewport()
|
|
641
|
+
const totalVirtualLinesNarrow = editor.editorView.getTotalVirtualLineCount()
|
|
642
|
+
|
|
643
|
+
expect(totalVirtualLinesNarrow).toBeGreaterThan(20)
|
|
644
|
+
|
|
645
|
+
// Immediately try to scroll down to the bottom with mouse wheel
|
|
646
|
+
const maxOffsetY = Math.max(0, totalVirtualLinesNarrow - viewportAfterResize.height)
|
|
647
|
+
|
|
648
|
+
for (let i = 0; i < maxOffsetY + 20; i++) {
|
|
649
|
+
await currentMouse.scroll(editor.x + 2, editor.y + 2, "down")
|
|
650
|
+
}
|
|
651
|
+
await renderOnce()
|
|
652
|
+
|
|
653
|
+
const viewportAfterScroll = editor.editorView.getViewport()
|
|
654
|
+
|
|
655
|
+
// Should have scrolled close to the bottom (within scroll margin tolerance)
|
|
656
|
+
expect(viewportAfterScroll.offsetY).toBeGreaterThan(maxOffsetY - 5)
|
|
657
|
+
expect(viewportAfterScroll.offsetY).toBeLessThanOrEqual(maxOffsetY)
|
|
658
|
+
|
|
659
|
+
// Now try to select text on the last visible line
|
|
660
|
+
await currentMouse.drag(editor.x, editor.y + editor.height - 1, editor.x + 8, editor.y + editor.height - 1)
|
|
661
|
+
await renderOnce()
|
|
662
|
+
|
|
663
|
+
const selectedText = editor.getSelectedText()
|
|
664
|
+
|
|
665
|
+
expect(editor.hasSelection()).toBe(true)
|
|
666
|
+
expect(selectedText.length).toBeGreaterThan(0)
|
|
667
|
+
|
|
668
|
+
editor.destroy()
|
|
669
|
+
})
|
|
670
|
+
|
|
671
|
+
it("should continuously update selection during auto-scroll without mouse movement", async () => {
|
|
672
|
+
const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
|
|
673
|
+
initialValue: Array.from({ length: 100 }, (_, i) => `Line ${i.toString().padStart(2, "0")}`).join("\n"),
|
|
674
|
+
width: 40,
|
|
675
|
+
height: 10,
|
|
676
|
+
selectable: true,
|
|
677
|
+
selectionBg: RGBA.fromValues(0, 1, 0, 1), // Bright green for easy detection
|
|
678
|
+
})
|
|
679
|
+
|
|
680
|
+
await renderOnce()
|
|
681
|
+
|
|
682
|
+
const recorder = new TestRecorder(currentRenderer, { recordBuffers: { bg: true } })
|
|
683
|
+
|
|
684
|
+
editor.editBuffer.gotoLine(0)
|
|
685
|
+
await renderOnce()
|
|
686
|
+
|
|
687
|
+
recorder.rec()
|
|
688
|
+
|
|
689
|
+
await currentMouse.pressDown(editor.x + 2, editor.y)
|
|
690
|
+
await currentMouse.moveTo(editor.x + 2, editor.y + editor.height - 1)
|
|
691
|
+
|
|
692
|
+
// Simulate 2 seconds of auto-scroll WITHOUT moving mouse
|
|
693
|
+
await simulateFrames(2000)
|
|
694
|
+
|
|
695
|
+
await currentMouse.release(editor.x + 2, editor.y + editor.height - 1)
|
|
696
|
+
await renderOnce()
|
|
697
|
+
recorder.stop()
|
|
698
|
+
|
|
699
|
+
const frames = recorder.recordedFrames
|
|
700
|
+
expect(frames.length).toBeGreaterThan(10)
|
|
701
|
+
|
|
702
|
+
const bufferWidth = currentRenderer.width
|
|
703
|
+
const selectionCellCounts: number[] = []
|
|
704
|
+
|
|
705
|
+
for (const frame of frames) {
|
|
706
|
+
if (!frame.buffers?.bg) continue
|
|
707
|
+
|
|
708
|
+
let selectedCells = 0
|
|
709
|
+
for (let y = editor.y; y < editor.y + editor.height; y++) {
|
|
710
|
+
for (let x = editor.x; x < editor.x + editor.width; x++) {
|
|
711
|
+
const bufferIdx = y * bufferWidth + x
|
|
712
|
+
const bgG = frame.buffers.bg[bufferIdx * 4 + 1]
|
|
713
|
+
if (Math.abs(bgG - 1.0) < 0.01) {
|
|
714
|
+
selectedCells++
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
selectionCellCounts.push(selectedCells)
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const firstFrameSelection = selectionCellCounts[0] || 0
|
|
722
|
+
const lastFrameSelection = selectionCellCounts[selectionCellCounts.length - 1] || 0
|
|
723
|
+
|
|
724
|
+
const framesWithoutSelection = selectionCellCounts.filter((count, i) => i > 0 && count === 0).length
|
|
725
|
+
|
|
726
|
+
// Selection should expand and be continuously visible (no flicker)
|
|
727
|
+
expect(lastFrameSelection).toBeGreaterThan(firstFrameSelection)
|
|
728
|
+
expect(framesWithoutSelection).toBe(0)
|
|
729
|
+
|
|
730
|
+
editor.destroy()
|
|
731
|
+
})
|
|
732
|
+
})
|
|
733
|
+
})
|