@fairyhunter13/opentui-core 0.1.91 → 0.1.94
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/3d/SpriteResourceManager.d.ts +74 -0
- package/3d/SpriteUtils.d.ts +13 -0
- package/3d/TextureUtils.d.ts +24 -0
- package/3d/ThreeRenderable.d.ts +40 -0
- package/3d/WGPURenderer.d.ts +61 -0
- package/3d/animation/ExplodingSpriteEffect.d.ts +71 -0
- package/3d/animation/PhysicsExplodingSpriteEffect.d.ts +76 -0
- package/3d/animation/SpriteAnimator.d.ts +124 -0
- package/3d/animation/SpriteParticleGenerator.d.ts +62 -0
- package/3d/canvas.d.ts +44 -0
- package/3d/index.d.ts +12 -0
- package/3d/physics/PlanckPhysicsAdapter.d.ts +19 -0
- package/3d/physics/RapierPhysicsAdapter.d.ts +19 -0
- package/3d/physics/physics-interface.d.ts +27 -0
- package/3d.d.ts +2 -0
- package/3d.js +34042 -0
- package/3d.js.map +155 -0
- package/LICENSE +21 -0
- package/NativeSpanFeed.d.ts +41 -0
- package/Renderable.d.ts +334 -0
- package/animation/Timeline.d.ts +126 -0
- package/ansi.d.ts +13 -0
- package/buffer.d.ts +107 -0
- package/console.d.ts +143 -0
- package/edit-buffer.d.ts +98 -0
- package/editor-view.d.ts +73 -0
- package/index-e6ec7apq.js +18415 -0
- package/index-e6ec7apq.js.map +64 -0
- package/index-h066zmrb.js +12619 -0
- package/index-h066zmrb.js.map +43 -0
- package/index-ynzawt3n.js +113 -0
- package/index-ynzawt3n.js.map +10 -0
- package/index.d.ts +21 -0
- package/index.js +430 -0
- package/index.js.map +9 -0
- package/lib/KeyHandler.d.ts +61 -0
- package/lib/RGBA.d.ts +25 -0
- package/lib/ascii.font.d.ts +508 -0
- package/lib/border.d.ts +49 -0
- package/lib/bunfs.d.ts +7 -0
- package/lib/clipboard.d.ts +17 -0
- package/lib/clock.d.ts +15 -0
- package/lib/data-paths.d.ts +26 -0
- package/lib/debounce.d.ts +42 -0
- package/lib/detect-links.d.ts +6 -0
- package/lib/env.d.ts +42 -0
- package/lib/extmarks-history.d.ts +17 -0
- package/lib/extmarks.d.ts +89 -0
- package/lib/hast-styled-text.d.ts +17 -0
- package/lib/index.d.ts +21 -0
- package/lib/keymapping.d.ts +25 -0
- package/lib/objects-in-viewport.d.ts +24 -0
- package/lib/output.capture.d.ts +24 -0
- package/lib/parse.keypress-kitty.d.ts +2 -0
- package/lib/parse.keypress.d.ts +26 -0
- package/lib/parse.mouse.d.ts +30 -0
- package/lib/paste.d.ts +7 -0
- package/lib/queue.d.ts +15 -0
- package/lib/renderable.validations.d.ts +12 -0
- package/lib/scroll-acceleration.d.ts +43 -0
- package/lib/selection.d.ts +63 -0
- package/lib/singleton.d.ts +7 -0
- package/lib/stdin-parser.d.ts +76 -0
- package/lib/styled-text.d.ts +63 -0
- package/lib/terminal-capability-detection.d.ts +30 -0
- package/lib/terminal-palette.d.ts +50 -0
- package/lib/tree-sitter/assets/update.d.ts +11 -0
- package/lib/tree-sitter/client.d.ts +47 -0
- package/lib/tree-sitter/default-parsers.d.ts +2 -0
- package/lib/tree-sitter/download-utils.d.ts +21 -0
- package/lib/tree-sitter/index.d.ts +8 -0
- package/lib/tree-sitter/parser.worker.d.ts +1 -0
- package/lib/tree-sitter/parsers-config.d.ts +38 -0
- package/lib/tree-sitter/resolve-ft.d.ts +2 -0
- package/lib/tree-sitter/types.d.ts +81 -0
- package/lib/tree-sitter-styled-text.d.ts +14 -0
- package/lib/validate-dir-name.d.ts +1 -0
- package/lib/yoga.options.d.ts +32 -0
- package/package.json +51 -63
- package/parser.worker.js +869 -0
- package/parser.worker.js.map +12 -0
- package/plugins/core-slot.d.ts +72 -0
- package/plugins/registry.d.ts +38 -0
- package/plugins/types.d.ts +34 -0
- package/post/filters.d.ts +105 -0
- package/renderables/ASCIIFont.d.ts +52 -0
- package/renderables/Box.d.ts +72 -0
- package/renderables/Code.d.ts +78 -0
- package/renderables/Diff.d.ts +142 -0
- package/renderables/EditBufferRenderable.d.ts +162 -0
- package/renderables/FrameBuffer.d.ts +16 -0
- package/renderables/Input.d.ts +67 -0
- package/renderables/LineNumberRenderable.d.ts +74 -0
- package/renderables/Markdown.d.ts +173 -0
- package/renderables/ScrollBar.d.ts +77 -0
- package/renderables/ScrollBox.d.ts +124 -0
- package/renderables/Select.d.ts +115 -0
- package/renderables/Slider.d.ts +44 -0
- package/renderables/TabSelect.d.ts +96 -0
- package/renderables/Text.d.ts +36 -0
- package/renderables/TextBufferRenderable.d.ts +105 -0
- package/renderables/TextNode.d.ts +91 -0
- package/renderables/TextTable.d.ts +140 -0
- package/renderables/Textarea.d.ts +114 -0
- package/renderables/TimeToFirstDraw.d.ts +24 -0
- package/renderables/__tests__/renderable-test-utils.d.ts +12 -0
- package/renderables/composition/VRenderable.d.ts +16 -0
- package/renderables/composition/constructs.d.ts +35 -0
- package/renderables/composition/vnode.d.ts +46 -0
- package/renderables/index.d.ts +22 -0
- package/renderables/markdown-parser.d.ts +10 -0
- package/renderer.d.ts +388 -0
- package/runtime-plugin-support.d.ts +3 -0
- package/runtime-plugin-support.js +29 -0
- package/runtime-plugin-support.js.map +10 -0
- package/runtime-plugin.d.ts +11 -0
- package/runtime-plugin.js +16 -0
- package/runtime-plugin.js.map +9 -0
- package/syntax-style.d.ts +54 -0
- package/testing/manual-clock.d.ts +16 -0
- package/testing/mock-keys.d.ts +81 -0
- package/testing/mock-mouse.d.ts +38 -0
- package/testing/mock-tree-sitter-client.d.ts +23 -0
- package/testing/spy.d.ts +7 -0
- package/testing/test-recorder.d.ts +61 -0
- package/testing/test-renderer.d.ts +23 -0
- package/testing.d.ts +6 -0
- package/testing.js +675 -0
- package/testing.js.map +15 -0
- package/text-buffer-view.d.ts +42 -0
- package/text-buffer.d.ts +67 -0
- package/types.d.ts +131 -0
- package/utils.d.ts +14 -0
- package/zig-structs.d.ts +155 -0
- package/zig.d.ts +351 -0
- package/dev/keypress-debug-renderer.ts +0 -148
- package/dev/keypress-debug.ts +0 -43
- package/dev/print-env-vars.ts +0 -32
- package/dev/test-tmux-graphics-334.sh +0 -68
- package/dev/thai-debug-test.ts +0 -68
- package/docs/development.md +0 -141
- package/docs/env-vars.md +0 -140
- package/docs/getting-started.md +0 -353
- package/docs/renderables-vs-constructs.md +0 -159
- package/docs/tree-sitter.md +0 -311
- package/scripts/build.ts +0 -400
- package/scripts/publish.ts +0 -60
- package/src/3d/SpriteResourceManager.ts +0 -286
- package/src/3d/SpriteUtils.ts +0 -71
- package/src/3d/TextureUtils.ts +0 -196
- package/src/3d/ThreeRenderable.ts +0 -197
- package/src/3d/WGPURenderer.ts +0 -294
- package/src/3d/animation/ExplodingSpriteEffect.ts +0 -513
- package/src/3d/animation/PhysicsExplodingSpriteEffect.ts +0 -429
- package/src/3d/animation/SpriteAnimator.ts +0 -633
- package/src/3d/animation/SpriteParticleGenerator.ts +0 -435
- package/src/3d/canvas.ts +0 -464
- package/src/3d/index.ts +0 -12
- package/src/3d/physics/PlanckPhysicsAdapter.ts +0 -72
- package/src/3d/physics/RapierPhysicsAdapter.ts +0 -66
- package/src/3d/physics/physics-interface.ts +0 -31
- package/src/3d/shaders/supersampling.wgsl +0 -201
- package/src/3d.ts +0 -3
- package/src/NativeSpanFeed.ts +0 -300
- package/src/Renderable.ts +0 -1698
- package/src/__snapshots__/buffer.test.ts.snap +0 -28
- package/src/animation/Timeline.test.ts +0 -2709
- package/src/animation/Timeline.ts +0 -598
- package/src/ansi.ts +0 -18
- package/src/benchmark/latest-all-bench-run.json +0 -707
- package/src/benchmark/latest-async-bench-run.json +0 -336
- package/src/benchmark/latest-default-bench-run.json +0 -657
- package/src/benchmark/latest-large-bench-run.json +0 -707
- package/src/benchmark/latest-quick-bench-run.json +0 -207
- package/src/benchmark/markdown-benchmark.ts +0 -1804
- package/src/benchmark/native-span-feed-async-benchmark.ts +0 -355
- package/src/benchmark/native-span-feed-benchmark.md +0 -56
- package/src/benchmark/native-span-feed-benchmark.ts +0 -596
- package/src/benchmark/native-span-feed-compare.ts +0 -280
- package/src/benchmark/renderer-benchmark.ts +0 -754
- package/src/benchmark/text-table-benchmark.ts +0 -947
- package/src/buffer.test.ts +0 -291
- package/src/buffer.ts +0 -519
- package/src/console.test.ts +0 -612
- package/src/console.ts +0 -1255
- package/src/edit-buffer.test.ts +0 -1769
- package/src/edit-buffer.ts +0 -411
- package/src/editor-view.test.ts +0 -1032
- package/src/editor-view.ts +0 -284
- package/src/examples/ascii-font-selection-demo.ts +0 -245
- 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 +0 -1018
- 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 +0 -115
- package/src/examples/code-demo.ts +0 -584
- package/src/examples/console-demo.ts +0 -358
- package/src/examples/core-plugin-slots-demo.ts +0 -759
- package/src/examples/diff-demo.ts +0 -699
- package/src/examples/draggable-three-demo.ts +0 -259
- package/src/examples/editor-demo.ts +0 -322
- package/src/examples/extmarks-demo.ts +0 -204
- package/src/examples/focus-restore-demo.ts +0 -310
- package/src/examples/fonts.ts +0 -245
- package/src/examples/fractal-shader-demo.ts +0 -268
- package/src/examples/framebuffer-demo.ts +0 -674
- package/src/examples/full-unicode-demo.ts +0 -181
- package/src/examples/golden-star-demo.ts +0 -933
- package/src/examples/grayscale-buffer-demo.ts +0 -249
- package/src/examples/hast-syntax-highlighting-demo.ts +0 -129
- package/src/examples/index.ts +0 -925
- package/src/examples/input-demo.ts +0 -377
- package/src/examples/input-select-layout-demo.ts +0 -425
- package/src/examples/install.sh +0 -143
- package/src/examples/keypress-debug-demo.ts +0 -452
- package/src/examples/lib/HexList.ts +0 -122
- package/src/examples/lib/PaletteGrid.ts +0 -125
- package/src/examples/lib/standalone-keys.ts +0 -25
- package/src/examples/lib/tab-controller.ts +0 -243
- package/src/examples/lights-phong-demo.ts +0 -290
- package/src/examples/link-demo.ts +0 -220
- package/src/examples/live-state-demo.ts +0 -480
- package/src/examples/markdown-demo.ts +0 -620
- package/src/examples/mouse-interaction-demo.ts +0 -428
- package/src/examples/nested-zindex-demo.ts +0 -357
- package/src/examples/opacity-example.ts +0 -235
- package/src/examples/opentui-demo.ts +0 -1057
- package/src/examples/physx-planck-2d-demo.ts +0 -507
- package/src/examples/physx-rapier-2d-demo.ts +0 -526
- package/src/examples/relative-positioning-demo.ts +0 -323
- package/src/examples/scroll-example.ts +0 -214
- package/src/examples/scrollbox-mouse-test.ts +0 -112
- package/src/examples/scrollbox-overlay-hit-test.ts +0 -206
- package/src/examples/select-demo.ts +0 -237
- package/src/examples/shader-cube-demo.ts +0 -772
- package/src/examples/simple-layout-example.ts +0 -591
- package/src/examples/slider-demo.ts +0 -617
- package/src/examples/split-mode-demo.ts +0 -445
- package/src/examples/sprite-animation-demo.ts +0 -443
- package/src/examples/sprite-particle-generator-demo.ts +0 -486
- package/src/examples/static-sprite-demo.ts +0 -193
- package/src/examples/sticky-scroll-example.ts +0 -308
- package/src/examples/styled-text-demo.ts +0 -282
- package/src/examples/tab-select-demo.ts +0 -219
- package/src/examples/terminal-title.ts +0 -29
- package/src/examples/terminal.ts +0 -305
- package/src/examples/text-node-demo.ts +0 -416
- package/src/examples/text-selection-demo.ts +0 -377
- package/src/examples/text-table-demo.ts +0 -503
- package/src/examples/text-truncation-demo.ts +0 -481
- package/src/examples/text-wrap.ts +0 -757
- package/src/examples/texture-loading-demo.ts +0 -259
- package/src/examples/timeline-example.ts +0 -670
- package/src/examples/transparency-demo.ts +0 -241
- package/src/examples/vnode-composition-demo.ts +0 -404
- package/src/index.ts +0 -22
- package/src/lib/KeyHandler.integration.test.ts +0 -292
- package/src/lib/KeyHandler.stopPropagation.test.ts +0 -289
- package/src/lib/KeyHandler.test.ts +0 -662
- package/src/lib/KeyHandler.ts +0 -222
- package/src/lib/RGBA.test.ts +0 -984
- package/src/lib/RGBA.ts +0 -204
- package/src/lib/ascii.font.ts +0 -330
- package/src/lib/border.test.ts +0 -83
- package/src/lib/border.ts +0 -168
- package/src/lib/bunfs.test.ts +0 -27
- package/src/lib/bunfs.ts +0 -18
- package/src/lib/clipboard.test.ts +0 -41
- package/src/lib/clipboard.ts +0 -47
- package/src/lib/clock.ts +0 -31
- package/src/lib/data-paths.test.ts +0 -133
- package/src/lib/data-paths.ts +0 -109
- package/src/lib/debounce.ts +0 -106
- package/src/lib/detect-links.test.ts +0 -98
- package/src/lib/detect-links.ts +0 -56
- package/src/lib/env.test.ts +0 -228
- package/src/lib/env.ts +0 -209
- package/src/lib/extmarks-history.ts +0 -51
- package/src/lib/extmarks-multiwidth.test.ts +0 -322
- package/src/lib/extmarks.test.ts +0 -3457
- package/src/lib/extmarks.ts +0 -843
- package/src/lib/fonts/block.json +0 -405
- package/src/lib/fonts/grid.json +0 -265
- package/src/lib/fonts/huge.json +0 -741
- package/src/lib/fonts/pallet.json +0 -314
- package/src/lib/fonts/shade.json +0 -591
- package/src/lib/fonts/slick.json +0 -321
- package/src/lib/fonts/tiny.json +0 -69
- package/src/lib/hast-styled-text.ts +0 -59
- package/src/lib/index.ts +0 -21
- package/src/lib/keymapping.test.ts +0 -280
- package/src/lib/keymapping.ts +0 -87
- package/src/lib/objects-in-viewport.test.ts +0 -787
- package/src/lib/objects-in-viewport.ts +0 -153
- package/src/lib/output.capture.ts +0 -58
- package/src/lib/parse.keypress-kitty.protocol.test.ts +0 -340
- package/src/lib/parse.keypress-kitty.test.ts +0 -663
- package/src/lib/parse.keypress-kitty.ts +0 -439
- package/src/lib/parse.keypress.test.ts +0 -1849
- package/src/lib/parse.keypress.ts +0 -397
- package/src/lib/parse.mouse.test.ts +0 -552
- package/src/lib/parse.mouse.ts +0 -232
- package/src/lib/paste.ts +0 -16
- package/src/lib/queue.ts +0 -65
- package/src/lib/renderable.validations.test.ts +0 -87
- package/src/lib/renderable.validations.ts +0 -83
- package/src/lib/scroll-acceleration.ts +0 -98
- package/src/lib/selection.ts +0 -240
- package/src/lib/singleton.ts +0 -28
- package/src/lib/stdin-parser.test.ts +0 -1676
- package/src/lib/stdin-parser.ts +0 -1248
- package/src/lib/styled-text.ts +0 -178
- package/src/lib/terminal-capability-detection.test.ts +0 -202
- package/src/lib/terminal-capability-detection.ts +0 -79
- package/src/lib/terminal-palette.test.ts +0 -878
- package/src/lib/terminal-palette.ts +0 -383
- package/src/lib/tree-sitter/assets/README.md +0 -118
- package/src/lib/tree-sitter/assets/update.ts +0 -331
- package/src/lib/tree-sitter/assets.d.ts +0 -9
- package/src/lib/tree-sitter/cache.test.ts +0 -270
- package/src/lib/tree-sitter/client.test.ts +0 -1061
- package/src/lib/tree-sitter/client.ts +0 -615
- package/src/lib/tree-sitter/default-parsers.ts +0 -80
- package/src/lib/tree-sitter/download-utils.ts +0 -148
- package/src/lib/tree-sitter/index.ts +0 -28
- package/src/lib/tree-sitter/parser.worker.ts +0 -1001
- package/src/lib/tree-sitter/parsers-config.ts +0 -75
- package/src/lib/tree-sitter/resolve-ft.ts +0 -62
- package/src/lib/tree-sitter/types.ts +0 -81
- package/src/lib/tree-sitter-styled-text.test.ts +0 -1253
- package/src/lib/tree-sitter-styled-text.ts +0 -306
- package/src/lib/validate-dir-name.ts +0 -55
- package/src/lib/yoga.options.test.ts +0 -628
- package/src/lib/yoga.options.ts +0 -346
- package/src/plugins/core-slot.ts +0 -579
- package/src/plugins/registry.ts +0 -377
- package/src/plugins/types.ts +0 -46
- package/src/post/filters.ts +0 -888
- package/src/renderables/ASCIIFont.ts +0 -219
- package/src/renderables/Box.test.ts +0 -160
- package/src/renderables/Box.ts +0 -295
- package/src/renderables/Code.test.ts +0 -2062
- package/src/renderables/Code.ts +0 -357
- package/src/renderables/Diff.regression.test.ts +0 -226
- package/src/renderables/Diff.test.ts +0 -3027
- package/src/renderables/Diff.ts +0 -1209
- package/src/renderables/EditBufferRenderable.ts +0 -764
- package/src/renderables/FrameBuffer.ts +0 -47
- package/src/renderables/Input.test.ts +0 -1228
- package/src/renderables/Input.ts +0 -245
- package/src/renderables/LineNumberRenderable.ts +0 -675
- package/src/renderables/Markdown.ts +0 -1106
- package/src/renderables/ScrollBar.ts +0 -422
- package/src/renderables/ScrollBox.ts +0 -883
- package/src/renderables/Select.test.ts +0 -1010
- package/src/renderables/Select.ts +0 -523
- package/src/renderables/Slider.test.ts +0 -456
- package/src/renderables/Slider.ts +0 -347
- package/src/renderables/TabSelect.test.ts +0 -197
- package/src/renderables/TabSelect.ts +0 -455
- package/src/renderables/Text.selection-buffer.test.ts +0 -123
- package/src/renderables/Text.test.ts +0 -2660
- package/src/renderables/Text.ts +0 -147
- package/src/renderables/TextBufferRenderable.ts +0 -518
- package/src/renderables/TextNode.test.ts +0 -1058
- package/src/renderables/TextNode.ts +0 -325
- package/src/renderables/TextTable.test.ts +0 -1421
- package/src/renderables/TextTable.ts +0 -1344
- package/src/renderables/Textarea.ts +0 -732
- package/src/renderables/TimeToFirstDraw.ts +0 -89
- package/src/renderables/__snapshots__/Code.test.ts.snap +0 -13
- package/src/renderables/__snapshots__/Diff.test.ts.snap +0 -785
- package/src/renderables/__snapshots__/Text.test.ts.snap +0 -421
- package/src/renderables/__snapshots__/TextTable.test.ts.snap +0 -215
- package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +0 -144
- package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +0 -816
- package/src/renderables/__tests__/LineNumberRenderable.test.ts +0 -1787
- package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +0 -85
- package/src/renderables/__tests__/Markdown.test.ts +0 -2287
- package/src/renderables/__tests__/MultiRenderable.selection.test.ts +0 -87
- package/src/renderables/__tests__/Textarea.buffer.test.ts +0 -682
- package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +0 -675
- package/src/renderables/__tests__/Textarea.editing.test.ts +0 -2041
- package/src/renderables/__tests__/Textarea.error-handling.test.ts +0 -35
- package/src/renderables/__tests__/Textarea.events.test.ts +0 -738
- package/src/renderables/__tests__/Textarea.highlights.test.ts +0 -590
- package/src/renderables/__tests__/Textarea.keybinding.test.ts +0 -3149
- package/src/renderables/__tests__/Textarea.paste.test.ts +0 -357
- package/src/renderables/__tests__/Textarea.rendering.test.ts +0 -1864
- package/src/renderables/__tests__/Textarea.scroll.test.ts +0 -733
- package/src/renderables/__tests__/Textarea.selection.test.ts +0 -1590
- package/src/renderables/__tests__/Textarea.stress.test.ts +0 -670
- package/src/renderables/__tests__/Textarea.undo-redo.test.ts +0 -383
- package/src/renderables/__tests__/Textarea.visual-lines.test.ts +0 -310
- package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +0 -221
- package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +0 -89
- package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +0 -457
- package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +0 -158
- package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +0 -387
- package/src/renderables/__tests__/markdown-parser.test.ts +0 -217
- package/src/renderables/__tests__/renderable-test-utils.ts +0 -60
- package/src/renderables/composition/README.md +0 -8
- package/src/renderables/composition/VRenderable.ts +0 -32
- package/src/renderables/composition/constructs.ts +0 -127
- package/src/renderables/composition/vnode.ts +0 -289
- package/src/renderables/index.ts +0 -22
- package/src/renderables/markdown-parser.ts +0 -66
- package/src/renderer.ts +0 -2363
- package/src/runtime-plugin-support.ts +0 -39
- package/src/runtime-plugin.ts +0 -144
- package/src/syntax-style.test.ts +0 -841
- package/src/syntax-style.ts +0 -264
- package/src/testing/README.md +0 -210
- package/src/testing/capture-spans.test.ts +0 -194
- package/src/testing/integration.test.ts +0 -276
- package/src/testing/manual-clock.ts +0 -106
- package/src/testing/mock-keys.test.ts +0 -1356
- package/src/testing/mock-keys.ts +0 -449
- package/src/testing/mock-mouse.test.ts +0 -218
- package/src/testing/mock-mouse.ts +0 -247
- package/src/testing/mock-tree-sitter-client.ts +0 -73
- package/src/testing/spy.ts +0 -13
- package/src/testing/test-recorder.test.ts +0 -415
- package/src/testing/test-recorder.ts +0 -145
- package/src/testing/test-renderer.ts +0 -116
- package/src/testing.ts +0 -7
- package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +0 -481
- package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +0 -19
- package/src/tests/__snapshots__/scrollbox.test.ts.snap +0 -29
- package/src/tests/absolute-positioning.snapshot.test.ts +0 -638
- package/src/tests/allocator-stats.test.ts +0 -38
- package/src/tests/destroy-during-render.test.ts +0 -200
- package/src/tests/hover-cursor.test.ts +0 -98
- package/src/tests/native-span-feed-async.test.ts +0 -173
- package/src/tests/native-span-feed-close.test.ts +0 -120
- package/src/tests/native-span-feed-coverage.test.ts +0 -227
- package/src/tests/native-span-feed-edge-cases.test.ts +0 -352
- package/src/tests/native-span-feed-use-after-free.test.ts +0 -45
- package/src/tests/opacity.test.ts +0 -123
- package/src/tests/renderable.snapshot.test.ts +0 -524
- package/src/tests/renderable.test.ts +0 -1281
- package/src/tests/renderer.console-startup.test.ts +0 -65
- package/src/tests/renderer.control.test.ts +0 -364
- package/src/tests/renderer.core-slot-binding.test.ts +0 -952
- package/src/tests/renderer.cursor.test.ts +0 -26
- package/src/tests/renderer.destroy-during-render.test.ts +0 -110
- package/src/tests/renderer.focus-restore.test.ts +0 -228
- package/src/tests/renderer.focus.test.ts +0 -251
- package/src/tests/renderer.idle.test.ts +0 -219
- package/src/tests/renderer.input.test.ts +0 -2145
- package/src/tests/renderer.kitty-flags.test.ts +0 -195
- package/src/tests/renderer.mouse.test.ts +0 -1269
- package/src/tests/renderer.palette.test.ts +0 -629
- package/src/tests/renderer.selection.test.ts +0 -49
- package/src/tests/renderer.slot-registry.test.ts +0 -649
- package/src/tests/renderer.useMouse.test.ts +0 -50
- package/src/tests/runtime-plugin-support.fixture.ts +0 -11
- package/src/tests/runtime-plugin-support.test.ts +0 -28
- package/src/tests/runtime-plugin.fixture.ts +0 -40
- package/src/tests/runtime-plugin.test.ts +0 -190
- package/src/tests/scrollbox-culling-bug.test.ts +0 -114
- package/src/tests/scrollbox-hitgrid-resize.test.ts +0 -136
- package/src/tests/scrollbox-hitgrid.test.ts +0 -909
- package/src/tests/scrollbox.test.ts +0 -1530
- package/src/tests/wrap-resize-perf.test.ts +0 -229
- package/src/tests/yoga-setters.test.ts +0 -921
- package/src/text-buffer-view.test.ts +0 -705
- package/src/text-buffer-view.ts +0 -189
- package/src/text-buffer.test.ts +0 -347
- package/src/text-buffer.ts +0 -250
- package/src/types.ts +0 -152
- package/src/utils.ts +0 -88
- package/src/zig/ansi.zig +0 -268
- package/src/zig/bench/README.md +0 -50
- package/src/zig/bench/buffer-draw-text-buffer_bench.zig +0 -887
- package/src/zig/bench/edit-buffer_bench.zig +0 -476
- package/src/zig/bench/native-span-feed_bench.zig +0 -100
- package/src/zig/bench/rope-markers_bench.zig +0 -713
- package/src/zig/bench/rope_bench.zig +0 -514
- package/src/zig/bench/styled-text_bench.zig +0 -470
- package/src/zig/bench/text-buffer-coords_bench.zig +0 -362
- package/src/zig/bench/text-buffer-view_bench.zig +0 -459
- package/src/zig/bench/text-chunk-graphemes_bench.zig +0 -273
- package/src/zig/bench/utf8_bench.zig +0 -799
- package/src/zig/bench-utils.zig +0 -431
- package/src/zig/bench.zig +0 -217
- package/src/zig/buffer.zig +0 -2223
- package/src/zig/build.zig +0 -289
- package/src/zig/build.zig.zon +0 -16
- package/src/zig/edit-buffer.zig +0 -825
- package/src/zig/editor-view.zig +0 -802
- package/src/zig/event-bus.zig +0 -13
- package/src/zig/event-emitter.zig +0 -65
- package/src/zig/file-logger.zig +0 -92
- package/src/zig/grapheme.zig +0 -599
- package/src/zig/lib.zig +0 -1834
- package/src/zig/link.zig +0 -333
- package/src/zig/logger.zig +0 -43
- package/src/zig/mem-registry.zig +0 -125
- package/src/zig/native-span-feed-bench-lib.zig +0 -7
- package/src/zig/native-span-feed.zig +0 -708
- package/src/zig/renderer.zig +0 -1386
- package/src/zig/rope.zig +0 -1220
- package/src/zig/syntax-style.zig +0 -161
- package/src/zig/terminal.zig +0 -975
- package/src/zig/test.zig +0 -70
- package/src/zig/tests/README.md +0 -18
- package/src/zig/tests/buffer_test.zig +0 -2526
- package/src/zig/tests/edit-buffer-history_test.zig +0 -271
- package/src/zig/tests/edit-buffer_test.zig +0 -1689
- package/src/zig/tests/editor-view_test.zig +0 -3299
- package/src/zig/tests/event-emitter_test.zig +0 -249
- package/src/zig/tests/grapheme_test.zig +0 -1304
- package/src/zig/tests/link_test.zig +0 -190
- package/src/zig/tests/mem-registry_test.zig +0 -473
- package/src/zig/tests/memory_leak_regression_test.zig +0 -159
- package/src/zig/tests/native-span-feed_test.zig +0 -1264
- package/src/zig/tests/renderer_test.zig +0 -1010
- package/src/zig/tests/rope-nested_test.zig +0 -712
- package/src/zig/tests/rope_fuzz_test.zig +0 -238
- package/src/zig/tests/rope_test.zig +0 -2362
- package/src/zig/tests/segment-merge.test.zig +0 -148
- package/src/zig/tests/syntax-style_test.zig +0 -557
- package/src/zig/tests/terminal_test.zig +0 -719
- package/src/zig/tests/text-buffer-drawing_test.zig +0 -3237
- package/src/zig/tests/text-buffer-highlights_test.zig +0 -666
- package/src/zig/tests/text-buffer-iterators_test.zig +0 -776
- package/src/zig/tests/text-buffer-segment_test.zig +0 -320
- package/src/zig/tests/text-buffer-selection_test.zig +0 -1035
- package/src/zig/tests/text-buffer-selection_viewport_test.zig +0 -358
- package/src/zig/tests/text-buffer-view_test.zig +0 -3649
- package/src/zig/tests/text-buffer_test.zig +0 -2191
- package/src/zig/tests/unicode-width-map.zon +0 -3909
- package/src/zig/tests/utf8_no_zwj_test.zig +0 -260
- package/src/zig/tests/utf8_test.zig +0 -4057
- package/src/zig/tests/utf8_wcwidth_cursor_test.zig +0 -267
- package/src/zig/tests/utf8_wcwidth_test.zig +0 -357
- package/src/zig/tests/word-wrap-editing_test.zig +0 -498
- package/src/zig/tests/wrap-cache-perf_test.zig +0 -113
- package/src/zig/text-buffer-iterators.zig +0 -499
- package/src/zig/text-buffer-segment.zig +0 -404
- package/src/zig/text-buffer-view.zig +0 -1371
- package/src/zig/text-buffer.zig +0 -1180
- package/src/zig/utf8.zig +0 -1948
- package/src/zig/utils.zig +0 -9
- package/src/zig-structs.ts +0 -261
- package/src/zig.ts +0 -3843
- package/tsconfig.build.json +0 -22
- package/tsconfig.json +0 -28
- /package/{src/lib/tree-sitter/assets → assets}/javascript/highlights.scm +0 -0
- /package/{src/lib/tree-sitter/assets → assets}/javascript/tree-sitter-javascript.wasm +0 -0
- /package/{src/lib/tree-sitter/assets → assets}/markdown/highlights.scm +0 -0
- /package/{src/lib/tree-sitter/assets → assets}/markdown/injections.scm +0 -0
- /package/{src/lib/tree-sitter/assets → assets}/markdown/tree-sitter-markdown.wasm +0 -0
- /package/{src/lib/tree-sitter/assets → assets}/markdown_inline/highlights.scm +0 -0
- /package/{src/lib/tree-sitter/assets → assets}/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
- /package/{src/lib/tree-sitter/assets → assets}/typescript/highlights.scm +0 -0
- /package/{src/lib/tree-sitter/assets → assets}/typescript/tree-sitter-typescript.wasm +0 -0
- /package/{src/lib/tree-sitter/assets → assets}/zig/highlights.scm +0 -0
- /package/{src/lib/tree-sitter/assets → assets}/zig/tree-sitter-zig.wasm +0 -0
|
@@ -1,1849 +0,0 @@
|
|
|
1
|
-
import { test, expect } from "bun:test"
|
|
2
|
-
import { parseKeypress, nonAlphanumericKeys, type ParsedKey, type KeyEventType } from "./parse.keypress.js"
|
|
3
|
-
import { Buffer } from "node:buffer"
|
|
4
|
-
|
|
5
|
-
test("parseKeypress - basic letters", () => {
|
|
6
|
-
expect(parseKeypress("a")).toEqual({
|
|
7
|
-
name: "a",
|
|
8
|
-
ctrl: false,
|
|
9
|
-
meta: false,
|
|
10
|
-
shift: false,
|
|
11
|
-
option: false,
|
|
12
|
-
number: false,
|
|
13
|
-
sequence: "a",
|
|
14
|
-
raw: "a",
|
|
15
|
-
eventType: "press",
|
|
16
|
-
source: "raw",
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
expect(parseKeypress("A")).toEqual({
|
|
20
|
-
eventType: "press",
|
|
21
|
-
name: "a",
|
|
22
|
-
ctrl: false,
|
|
23
|
-
meta: false,
|
|
24
|
-
shift: true,
|
|
25
|
-
option: false,
|
|
26
|
-
number: false,
|
|
27
|
-
sequence: "A",
|
|
28
|
-
raw: "A",
|
|
29
|
-
source: "raw",
|
|
30
|
-
})
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
test("parseKeypress - numbers", () => {
|
|
34
|
-
expect(parseKeypress("1")).toEqual({
|
|
35
|
-
eventType: "press",
|
|
36
|
-
name: "1",
|
|
37
|
-
ctrl: false,
|
|
38
|
-
meta: false,
|
|
39
|
-
shift: false,
|
|
40
|
-
option: false,
|
|
41
|
-
number: true,
|
|
42
|
-
sequence: "1",
|
|
43
|
-
raw: "1",
|
|
44
|
-
source: "raw",
|
|
45
|
-
})
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
test("parseKeypress - special keys", () => {
|
|
49
|
-
expect(parseKeypress("\r")).toEqual({
|
|
50
|
-
eventType: "press",
|
|
51
|
-
name: "return",
|
|
52
|
-
ctrl: false,
|
|
53
|
-
meta: false,
|
|
54
|
-
shift: false,
|
|
55
|
-
option: false,
|
|
56
|
-
number: false,
|
|
57
|
-
sequence: "\r",
|
|
58
|
-
raw: "\r",
|
|
59
|
-
source: "raw",
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
expect(parseKeypress("\n")).toEqual({
|
|
63
|
-
eventType: "press",
|
|
64
|
-
name: "linefeed",
|
|
65
|
-
ctrl: false,
|
|
66
|
-
meta: false,
|
|
67
|
-
shift: false,
|
|
68
|
-
option: false,
|
|
69
|
-
number: false,
|
|
70
|
-
sequence: "\n",
|
|
71
|
-
raw: "\n",
|
|
72
|
-
source: "raw",
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
expect(parseKeypress("\x1b\r")).toEqual({
|
|
76
|
-
eventType: "press",
|
|
77
|
-
name: "return",
|
|
78
|
-
ctrl: false,
|
|
79
|
-
meta: true,
|
|
80
|
-
shift: false,
|
|
81
|
-
option: false,
|
|
82
|
-
number: false,
|
|
83
|
-
sequence: "\x1b\r",
|
|
84
|
-
raw: "\x1b\r",
|
|
85
|
-
source: "raw",
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
expect(parseKeypress("\x1b\n")).toEqual({
|
|
89
|
-
eventType: "press",
|
|
90
|
-
name: "linefeed",
|
|
91
|
-
ctrl: false,
|
|
92
|
-
meta: true,
|
|
93
|
-
shift: false,
|
|
94
|
-
option: false,
|
|
95
|
-
number: false,
|
|
96
|
-
sequence: "\x1b\n",
|
|
97
|
-
raw: "\x1b\n",
|
|
98
|
-
source: "raw",
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
expect(parseKeypress("\t")).toEqual({
|
|
102
|
-
eventType: "press",
|
|
103
|
-
name: "tab",
|
|
104
|
-
ctrl: false,
|
|
105
|
-
meta: false,
|
|
106
|
-
shift: false,
|
|
107
|
-
option: false,
|
|
108
|
-
number: false,
|
|
109
|
-
sequence: "\t",
|
|
110
|
-
raw: "\t",
|
|
111
|
-
source: "raw",
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
expect(parseKeypress("\b")).toEqual({
|
|
115
|
-
eventType: "press",
|
|
116
|
-
name: "backspace",
|
|
117
|
-
ctrl: false,
|
|
118
|
-
meta: false,
|
|
119
|
-
shift: false,
|
|
120
|
-
option: false,
|
|
121
|
-
number: false,
|
|
122
|
-
sequence: "\b",
|
|
123
|
-
raw: "\b",
|
|
124
|
-
source: "raw",
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
expect(parseKeypress("\x1b")).toEqual({
|
|
128
|
-
name: "escape",
|
|
129
|
-
ctrl: false,
|
|
130
|
-
meta: false,
|
|
131
|
-
shift: false,
|
|
132
|
-
option: false,
|
|
133
|
-
number: false,
|
|
134
|
-
sequence: "\x1b",
|
|
135
|
-
raw: "\x1b",
|
|
136
|
-
eventType: "press",
|
|
137
|
-
source: "raw",
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
expect(parseKeypress(" ")).toEqual({
|
|
141
|
-
eventType: "press",
|
|
142
|
-
name: "space",
|
|
143
|
-
ctrl: false,
|
|
144
|
-
meta: false,
|
|
145
|
-
shift: false,
|
|
146
|
-
option: false,
|
|
147
|
-
number: false,
|
|
148
|
-
sequence: " ",
|
|
149
|
-
raw: " ",
|
|
150
|
-
source: "raw",
|
|
151
|
-
})
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
test("parseKeypress - ctrl+letter combinations", () => {
|
|
155
|
-
expect(parseKeypress("\x01")).toEqual({
|
|
156
|
-
eventType: "press",
|
|
157
|
-
name: "a",
|
|
158
|
-
ctrl: true,
|
|
159
|
-
meta: false,
|
|
160
|
-
shift: false,
|
|
161
|
-
option: false,
|
|
162
|
-
number: false,
|
|
163
|
-
sequence: "\x01",
|
|
164
|
-
raw: "\x01",
|
|
165
|
-
source: "raw",
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
expect(parseKeypress("\x1a")).toEqual({
|
|
169
|
-
eventType: "press",
|
|
170
|
-
name: "z",
|
|
171
|
-
ctrl: true,
|
|
172
|
-
meta: false,
|
|
173
|
-
shift: false,
|
|
174
|
-
option: false,
|
|
175
|
-
number: false,
|
|
176
|
-
sequence: "\x1a",
|
|
177
|
-
raw: "\x1a",
|
|
178
|
-
source: "raw",
|
|
179
|
-
})
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
test("parseKeypress - ctrl+space and alt+space", () => {
|
|
183
|
-
// Ctrl+Space sends \x00 (null character)
|
|
184
|
-
expect(parseKeypress("\x00")).toEqual({
|
|
185
|
-
eventType: "press",
|
|
186
|
-
name: "space",
|
|
187
|
-
ctrl: true,
|
|
188
|
-
meta: false,
|
|
189
|
-
shift: false,
|
|
190
|
-
option: false,
|
|
191
|
-
number: false,
|
|
192
|
-
sequence: "\x00",
|
|
193
|
-
raw: "\x00",
|
|
194
|
-
source: "raw",
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
// Also test with unicode escape notation
|
|
198
|
-
expect(parseKeypress("\u0000")).toEqual({
|
|
199
|
-
eventType: "press",
|
|
200
|
-
name: "space",
|
|
201
|
-
ctrl: true,
|
|
202
|
-
meta: false,
|
|
203
|
-
shift: false,
|
|
204
|
-
option: false,
|
|
205
|
-
number: false,
|
|
206
|
-
sequence: "\u0000",
|
|
207
|
-
raw: "\u0000",
|
|
208
|
-
source: "raw",
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
// Alt+Space / Option+Space sends ESC + space (\x1b or \u001b followed by space)
|
|
212
|
-
// Note: meta=true indicates Alt/Option was pressed, but option=false because
|
|
213
|
-
// this is a simple ESC-prefix sequence (not an ANSI sequence with modifier bits)
|
|
214
|
-
expect(parseKeypress("\x1b ")).toEqual({
|
|
215
|
-
eventType: "press",
|
|
216
|
-
name: "space",
|
|
217
|
-
ctrl: false,
|
|
218
|
-
meta: true,
|
|
219
|
-
shift: false,
|
|
220
|
-
option: false,
|
|
221
|
-
number: false,
|
|
222
|
-
sequence: "\x1b ",
|
|
223
|
-
raw: "\x1b ",
|
|
224
|
-
source: "raw",
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
// Test with \u001b notation as well
|
|
228
|
-
expect(parseKeypress("\u001b ")).toEqual({
|
|
229
|
-
eventType: "press",
|
|
230
|
-
name: "space",
|
|
231
|
-
ctrl: false,
|
|
232
|
-
meta: true,
|
|
233
|
-
shift: false,
|
|
234
|
-
option: false,
|
|
235
|
-
number: false,
|
|
236
|
-
sequence: "\u001b ",
|
|
237
|
-
raw: "\u001b ",
|
|
238
|
-
source: "raw",
|
|
239
|
-
})
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
test("parseKeypress - meta+character combinations", () => {
|
|
243
|
-
// Simple ESC+character sequences (like ESC+a) set meta=true but option=false
|
|
244
|
-
// These sequences are typically generated by Alt/Option+key on many terminals
|
|
245
|
-
// but the simple ESC prefix doesn't distinguish between Alt/Option and Meta/Cmd
|
|
246
|
-
// so option flag is NOT set (unlike ANSI sequences with explicit modifier bits)
|
|
247
|
-
expect(parseKeypress("\x1ba")).toEqual({
|
|
248
|
-
eventType: "press",
|
|
249
|
-
name: "a",
|
|
250
|
-
ctrl: false,
|
|
251
|
-
meta: true,
|
|
252
|
-
shift: false,
|
|
253
|
-
option: false, // Note: option is NOT set for simple ESC+char sequences
|
|
254
|
-
number: false,
|
|
255
|
-
sequence: "\x1ba",
|
|
256
|
-
raw: "\x1ba",
|
|
257
|
-
source: "raw",
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
expect(parseKeypress("\x1bA")).toEqual({
|
|
261
|
-
eventType: "press",
|
|
262
|
-
name: "A",
|
|
263
|
-
ctrl: false,
|
|
264
|
-
meta: true,
|
|
265
|
-
shift: true,
|
|
266
|
-
option: false, // Note: option is NOT set for simple ESC+char sequences
|
|
267
|
-
number: false,
|
|
268
|
-
sequence: "\x1bA",
|
|
269
|
-
raw: "\x1bA",
|
|
270
|
-
source: "raw",
|
|
271
|
-
})
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
test("parseKeypress - function keys", () => {
|
|
275
|
-
expect(parseKeypress("\x1bOP")).toEqual({
|
|
276
|
-
eventType: "press",
|
|
277
|
-
name: "f1",
|
|
278
|
-
ctrl: false,
|
|
279
|
-
meta: false,
|
|
280
|
-
shift: false,
|
|
281
|
-
option: false,
|
|
282
|
-
number: false,
|
|
283
|
-
sequence: "\x1bOP",
|
|
284
|
-
raw: "\x1bOP",
|
|
285
|
-
code: "OP",
|
|
286
|
-
super: false,
|
|
287
|
-
hyper: false,
|
|
288
|
-
source: "raw",
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
expect(parseKeypress("\x1b[11~")).toEqual({
|
|
292
|
-
eventType: "press",
|
|
293
|
-
name: "f1",
|
|
294
|
-
ctrl: false,
|
|
295
|
-
meta: false,
|
|
296
|
-
shift: false,
|
|
297
|
-
option: false,
|
|
298
|
-
number: false,
|
|
299
|
-
sequence: "\x1b[11~",
|
|
300
|
-
raw: "\x1b[11~",
|
|
301
|
-
code: "[11~",
|
|
302
|
-
super: false,
|
|
303
|
-
hyper: false,
|
|
304
|
-
source: "raw",
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
expect(parseKeypress("\x1b[24~")).toEqual({
|
|
308
|
-
eventType: "press",
|
|
309
|
-
name: "f12",
|
|
310
|
-
ctrl: false,
|
|
311
|
-
meta: false,
|
|
312
|
-
shift: false,
|
|
313
|
-
option: false,
|
|
314
|
-
number: false,
|
|
315
|
-
sequence: "\x1b[24~",
|
|
316
|
-
raw: "\x1b[24~",
|
|
317
|
-
code: "[24~",
|
|
318
|
-
super: false,
|
|
319
|
-
hyper: false,
|
|
320
|
-
source: "raw",
|
|
321
|
-
})
|
|
322
|
-
})
|
|
323
|
-
|
|
324
|
-
test("parseKeypress - arrow keys", () => {
|
|
325
|
-
expect(parseKeypress("\x1b[A")).toEqual({
|
|
326
|
-
eventType: "press",
|
|
327
|
-
name: "up",
|
|
328
|
-
ctrl: false,
|
|
329
|
-
meta: false,
|
|
330
|
-
shift: false,
|
|
331
|
-
option: false,
|
|
332
|
-
number: false,
|
|
333
|
-
sequence: "\x1b[A",
|
|
334
|
-
raw: "\x1b[A",
|
|
335
|
-
code: "[A",
|
|
336
|
-
super: false,
|
|
337
|
-
hyper: false,
|
|
338
|
-
source: "raw",
|
|
339
|
-
})
|
|
340
|
-
|
|
341
|
-
expect(parseKeypress("\x1b[B")).toEqual({
|
|
342
|
-
eventType: "press",
|
|
343
|
-
name: "down",
|
|
344
|
-
ctrl: false,
|
|
345
|
-
meta: false,
|
|
346
|
-
shift: false,
|
|
347
|
-
option: false,
|
|
348
|
-
number: false,
|
|
349
|
-
sequence: "\x1b[B",
|
|
350
|
-
raw: "\x1b[B",
|
|
351
|
-
code: "[B",
|
|
352
|
-
super: false,
|
|
353
|
-
hyper: false,
|
|
354
|
-
source: "raw",
|
|
355
|
-
})
|
|
356
|
-
|
|
357
|
-
expect(parseKeypress("\x1b[C")).toEqual({
|
|
358
|
-
eventType: "press",
|
|
359
|
-
name: "right",
|
|
360
|
-
ctrl: false,
|
|
361
|
-
meta: false,
|
|
362
|
-
shift: false,
|
|
363
|
-
option: false,
|
|
364
|
-
number: false,
|
|
365
|
-
sequence: "\x1b[C",
|
|
366
|
-
raw: "\x1b[C",
|
|
367
|
-
code: "[C",
|
|
368
|
-
super: false,
|
|
369
|
-
hyper: false,
|
|
370
|
-
source: "raw",
|
|
371
|
-
})
|
|
372
|
-
|
|
373
|
-
expect(parseKeypress("\x1b[D")).toEqual({
|
|
374
|
-
eventType: "press",
|
|
375
|
-
name: "left",
|
|
376
|
-
ctrl: false,
|
|
377
|
-
meta: false,
|
|
378
|
-
shift: false,
|
|
379
|
-
option: false,
|
|
380
|
-
number: false,
|
|
381
|
-
sequence: "\x1b[D",
|
|
382
|
-
raw: "\x1b[D",
|
|
383
|
-
code: "[D",
|
|
384
|
-
super: false,
|
|
385
|
-
hyper: false,
|
|
386
|
-
source: "raw",
|
|
387
|
-
})
|
|
388
|
-
})
|
|
389
|
-
|
|
390
|
-
test("parseKeypress - navigation keys", () => {
|
|
391
|
-
expect(parseKeypress("\x1b[H")).toEqual({
|
|
392
|
-
eventType: "press",
|
|
393
|
-
name: "home",
|
|
394
|
-
ctrl: false,
|
|
395
|
-
meta: false,
|
|
396
|
-
shift: false,
|
|
397
|
-
option: false,
|
|
398
|
-
number: false,
|
|
399
|
-
sequence: "\x1b[H",
|
|
400
|
-
raw: "\x1b[H",
|
|
401
|
-
code: "[H",
|
|
402
|
-
super: false,
|
|
403
|
-
hyper: false,
|
|
404
|
-
source: "raw",
|
|
405
|
-
})
|
|
406
|
-
|
|
407
|
-
expect(parseKeypress("\x1b[F")).toEqual({
|
|
408
|
-
eventType: "press",
|
|
409
|
-
name: "end",
|
|
410
|
-
ctrl: false,
|
|
411
|
-
meta: false,
|
|
412
|
-
shift: false,
|
|
413
|
-
option: false,
|
|
414
|
-
number: false,
|
|
415
|
-
sequence: "\x1b[F",
|
|
416
|
-
raw: "\x1b[F",
|
|
417
|
-
code: "[F",
|
|
418
|
-
super: false,
|
|
419
|
-
hyper: false,
|
|
420
|
-
source: "raw",
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
expect(parseKeypress("\x1b[5~")).toEqual({
|
|
424
|
-
eventType: "press",
|
|
425
|
-
name: "pageup",
|
|
426
|
-
ctrl: false,
|
|
427
|
-
meta: false,
|
|
428
|
-
shift: false,
|
|
429
|
-
option: false,
|
|
430
|
-
number: false,
|
|
431
|
-
sequence: "\x1b[5~",
|
|
432
|
-
raw: "\x1b[5~",
|
|
433
|
-
code: "[5~",
|
|
434
|
-
super: false,
|
|
435
|
-
hyper: false,
|
|
436
|
-
source: "raw",
|
|
437
|
-
})
|
|
438
|
-
|
|
439
|
-
expect(parseKeypress("\x1b[6~")).toEqual({
|
|
440
|
-
eventType: "press",
|
|
441
|
-
name: "pagedown",
|
|
442
|
-
ctrl: false,
|
|
443
|
-
meta: false,
|
|
444
|
-
shift: false,
|
|
445
|
-
option: false,
|
|
446
|
-
number: false,
|
|
447
|
-
sequence: "\x1b[6~",
|
|
448
|
-
raw: "\x1b[6~",
|
|
449
|
-
code: "[6~",
|
|
450
|
-
super: false,
|
|
451
|
-
hyper: false,
|
|
452
|
-
source: "raw",
|
|
453
|
-
})
|
|
454
|
-
})
|
|
455
|
-
|
|
456
|
-
test("parseKeypress - modifier combinations", () => {
|
|
457
|
-
// Shift only: modifier value 2 = bits 1 (0b0001)
|
|
458
|
-
expect(parseKeypress("\x1b[1;2A")).toEqual({
|
|
459
|
-
eventType: "press",
|
|
460
|
-
name: "up",
|
|
461
|
-
ctrl: false,
|
|
462
|
-
meta: false,
|
|
463
|
-
shift: true,
|
|
464
|
-
option: false,
|
|
465
|
-
number: false,
|
|
466
|
-
sequence: "\x1b[1;2A",
|
|
467
|
-
raw: "\x1b[1;2A",
|
|
468
|
-
code: "[A",
|
|
469
|
-
super: false,
|
|
470
|
-
hyper: false,
|
|
471
|
-
source: "raw",
|
|
472
|
-
})
|
|
473
|
-
|
|
474
|
-
// Alt/Option key: modifier value 3 = bits 2 (0b0010)
|
|
475
|
-
// Note: Alt/Option (same key) sets both meta and option flags
|
|
476
|
-
expect(parseKeypress("\x1b[1;3A")).toEqual({
|
|
477
|
-
eventType: "press",
|
|
478
|
-
name: "up",
|
|
479
|
-
ctrl: false,
|
|
480
|
-
meta: true,
|
|
481
|
-
shift: false,
|
|
482
|
-
option: true,
|
|
483
|
-
number: false,
|
|
484
|
-
sequence: "\x1b[1;3A",
|
|
485
|
-
raw: "\x1b[1;3A",
|
|
486
|
-
code: "[A",
|
|
487
|
-
super: false,
|
|
488
|
-
hyper: false,
|
|
489
|
-
source: "raw",
|
|
490
|
-
})
|
|
491
|
-
|
|
492
|
-
// Shift+Alt/Option: modifier value 4 = bits 3 (0b0011 = Shift(1) + Alt/Option(2))
|
|
493
|
-
expect(parseKeypress("\x1b[1;4A")).toEqual({
|
|
494
|
-
eventType: "press",
|
|
495
|
-
name: "up",
|
|
496
|
-
ctrl: false,
|
|
497
|
-
meta: true,
|
|
498
|
-
shift: true,
|
|
499
|
-
option: true,
|
|
500
|
-
number: false,
|
|
501
|
-
sequence: "\x1b[1;4A",
|
|
502
|
-
raw: "\x1b[1;4A",
|
|
503
|
-
code: "[A",
|
|
504
|
-
super: false,
|
|
505
|
-
hyper: false,
|
|
506
|
-
source: "raw",
|
|
507
|
-
})
|
|
508
|
-
|
|
509
|
-
// Ctrl only: modifier value 5 = bits 4 (0b0100)
|
|
510
|
-
expect(parseKeypress("\x1b[1;5A")).toEqual({
|
|
511
|
-
eventType: "press",
|
|
512
|
-
name: "up",
|
|
513
|
-
ctrl: true,
|
|
514
|
-
meta: false,
|
|
515
|
-
shift: false,
|
|
516
|
-
option: false,
|
|
517
|
-
number: false,
|
|
518
|
-
sequence: "\x1b[1;5A",
|
|
519
|
-
raw: "\x1b[1;5A",
|
|
520
|
-
code: "[A",
|
|
521
|
-
super: false,
|
|
522
|
-
hyper: false,
|
|
523
|
-
source: "raw",
|
|
524
|
-
})
|
|
525
|
-
|
|
526
|
-
// Shift+Alt/Option+Ctrl: modifier value 8 = bits 7 (0b0111 = Shift(1) + Alt/Option(2) + Ctrl(4))
|
|
527
|
-
// Note: meta is true because Alt/Option is pressed, NOT because Meta/Cmd bit is set
|
|
528
|
-
expect(parseKeypress("\x1b[1;8A")).toEqual({
|
|
529
|
-
eventType: "press",
|
|
530
|
-
name: "up",
|
|
531
|
-
ctrl: true,
|
|
532
|
-
meta: true,
|
|
533
|
-
shift: true,
|
|
534
|
-
option: true,
|
|
535
|
-
number: false,
|
|
536
|
-
sequence: "\x1b[1;8A",
|
|
537
|
-
raw: "\x1b[1;8A",
|
|
538
|
-
code: "[A",
|
|
539
|
-
super: false,
|
|
540
|
-
hyper: false,
|
|
541
|
-
source: "raw",
|
|
542
|
-
})
|
|
543
|
-
|
|
544
|
-
// Super modifier bit only: modifier value 9 = bits 8 (0b1000)
|
|
545
|
-
// NOTE: This is bit 8 which is the Super key
|
|
546
|
-
expect(parseKeypress("\x1b[1;9A")).toEqual({
|
|
547
|
-
eventType: "press",
|
|
548
|
-
name: "up",
|
|
549
|
-
ctrl: false,
|
|
550
|
-
meta: false,
|
|
551
|
-
shift: false,
|
|
552
|
-
option: false,
|
|
553
|
-
number: false,
|
|
554
|
-
sequence: "\x1b[1;9A",
|
|
555
|
-
raw: "\x1b[1;9A",
|
|
556
|
-
code: "[A",
|
|
557
|
-
super: true,
|
|
558
|
-
hyper: false,
|
|
559
|
-
source: "raw",
|
|
560
|
-
})
|
|
561
|
-
|
|
562
|
-
// Shift+Super: modifier value 10 = bits 9 (0b1001 = Shift(1) + Super(8))
|
|
563
|
-
expect(parseKeypress("\x1b[1;10A")).toEqual({
|
|
564
|
-
eventType: "press",
|
|
565
|
-
name: "up",
|
|
566
|
-
ctrl: false,
|
|
567
|
-
meta: false,
|
|
568
|
-
shift: true,
|
|
569
|
-
option: false,
|
|
570
|
-
number: false,
|
|
571
|
-
sequence: "\x1b[1;10A",
|
|
572
|
-
raw: "\x1b[1;10A",
|
|
573
|
-
code: "[A",
|
|
574
|
-
super: true,
|
|
575
|
-
hyper: false,
|
|
576
|
-
source: "raw",
|
|
577
|
-
})
|
|
578
|
-
|
|
579
|
-
// Alt/Option+Super: modifier value 11 = bits 10 (0b1010 = Alt/Option(2) + Super(8))
|
|
580
|
-
expect(parseKeypress("\x1b[1;11A")).toEqual({
|
|
581
|
-
eventType: "press",
|
|
582
|
-
name: "up",
|
|
583
|
-
ctrl: false,
|
|
584
|
-
meta: true,
|
|
585
|
-
shift: false,
|
|
586
|
-
option: true,
|
|
587
|
-
number: false,
|
|
588
|
-
sequence: "\x1b[1;11A",
|
|
589
|
-
raw: "\x1b[1;11A",
|
|
590
|
-
code: "[A",
|
|
591
|
-
super: true,
|
|
592
|
-
hyper: false,
|
|
593
|
-
source: "raw",
|
|
594
|
-
})
|
|
595
|
-
|
|
596
|
-
// All ANSI modifier bits: modifier value 16 = bits 15 (0b1111 = Shift(1) + Alt(2) + Ctrl(4) + Super(8))
|
|
597
|
-
expect(parseKeypress("\x1b[1;16A")).toEqual({
|
|
598
|
-
eventType: "press",
|
|
599
|
-
name: "up",
|
|
600
|
-
ctrl: true,
|
|
601
|
-
meta: true,
|
|
602
|
-
shift: true,
|
|
603
|
-
option: true,
|
|
604
|
-
number: false,
|
|
605
|
-
sequence: "\x1b[1;16A",
|
|
606
|
-
raw: "\x1b[1;16A",
|
|
607
|
-
code: "[A",
|
|
608
|
-
super: true,
|
|
609
|
-
hyper: false,
|
|
610
|
-
source: "raw",
|
|
611
|
-
})
|
|
612
|
-
})
|
|
613
|
-
|
|
614
|
-
test("parseKeypress - delete key", () => {
|
|
615
|
-
expect(parseKeypress("\x1b[3~")).toEqual({
|
|
616
|
-
eventType: "press",
|
|
617
|
-
name: "delete",
|
|
618
|
-
ctrl: false,
|
|
619
|
-
meta: false,
|
|
620
|
-
shift: false,
|
|
621
|
-
option: false,
|
|
622
|
-
number: false,
|
|
623
|
-
sequence: "\x1b[3~",
|
|
624
|
-
raw: "\x1b[3~",
|
|
625
|
-
code: "[3~",
|
|
626
|
-
super: false,
|
|
627
|
-
hyper: false,
|
|
628
|
-
source: "raw",
|
|
629
|
-
})
|
|
630
|
-
})
|
|
631
|
-
|
|
632
|
-
test("parseKeypress - delete key with modifiers (modifyOtherKeys format)", () => {
|
|
633
|
-
// Delete key without modifiers: \x1b[3~
|
|
634
|
-
const plainDelete = parseKeypress("\x1b[3~")!
|
|
635
|
-
expect(plainDelete.name).toBe("delete")
|
|
636
|
-
expect(plainDelete.shift).toBe(false)
|
|
637
|
-
expect(plainDelete.ctrl).toBe(false)
|
|
638
|
-
expect(plainDelete.meta).toBe(false)
|
|
639
|
-
expect(plainDelete.option).toBe(false)
|
|
640
|
-
|
|
641
|
-
// Shift+Delete: \x1b[3;2~
|
|
642
|
-
const shiftDelete = parseKeypress("\x1b[3;2~")!
|
|
643
|
-
expect(shiftDelete.name).toBe("delete")
|
|
644
|
-
expect(shiftDelete.shift).toBe(true)
|
|
645
|
-
expect(shiftDelete.ctrl).toBe(false)
|
|
646
|
-
expect(shiftDelete.meta).toBe(false)
|
|
647
|
-
expect(shiftDelete.option).toBe(false)
|
|
648
|
-
expect(shiftDelete.sequence).toBe("\x1b[3;2~")
|
|
649
|
-
expect(shiftDelete.code).toBe("[3~")
|
|
650
|
-
|
|
651
|
-
// Option/Meta+Delete: \x1b[3;3~
|
|
652
|
-
const metaDelete = parseKeypress("\x1b[3;3~")!
|
|
653
|
-
expect(metaDelete.name).toBe("delete")
|
|
654
|
-
expect(metaDelete.meta).toBe(true)
|
|
655
|
-
expect(metaDelete.option).toBe(true)
|
|
656
|
-
expect(metaDelete.ctrl).toBe(false)
|
|
657
|
-
expect(metaDelete.shift).toBe(false)
|
|
658
|
-
expect(metaDelete.sequence).toBe("\x1b[3;3~")
|
|
659
|
-
expect(metaDelete.code).toBe("[3~")
|
|
660
|
-
|
|
661
|
-
// Ctrl+Delete: \x1b[3;5~
|
|
662
|
-
const ctrlDelete = parseKeypress("\x1b[3;5~")!
|
|
663
|
-
expect(ctrlDelete.name).toBe("delete")
|
|
664
|
-
expect(ctrlDelete.ctrl).toBe(true)
|
|
665
|
-
expect(ctrlDelete.shift).toBe(false)
|
|
666
|
-
expect(ctrlDelete.meta).toBe(false)
|
|
667
|
-
expect(ctrlDelete.option).toBe(false)
|
|
668
|
-
expect(ctrlDelete.sequence).toBe("\x1b[3;5~")
|
|
669
|
-
expect(ctrlDelete.code).toBe("[3~")
|
|
670
|
-
|
|
671
|
-
// Shift+Option+Delete: \x1b[3;4~
|
|
672
|
-
const shiftMetaDelete = parseKeypress("\x1b[3;4~")!
|
|
673
|
-
expect(shiftMetaDelete.name).toBe("delete")
|
|
674
|
-
expect(shiftMetaDelete.shift).toBe(true)
|
|
675
|
-
expect(shiftMetaDelete.meta).toBe(true)
|
|
676
|
-
expect(shiftMetaDelete.option).toBe(true)
|
|
677
|
-
expect(shiftMetaDelete.ctrl).toBe(false)
|
|
678
|
-
expect(shiftMetaDelete.sequence).toBe("\x1b[3;4~")
|
|
679
|
-
expect(shiftMetaDelete.code).toBe("[3~")
|
|
680
|
-
|
|
681
|
-
// Ctrl+Option+Delete: \x1b[3;7~
|
|
682
|
-
const ctrlMetaDelete = parseKeypress("\x1b[3;7~")!
|
|
683
|
-
expect(ctrlMetaDelete.name).toBe("delete")
|
|
684
|
-
expect(ctrlMetaDelete.ctrl).toBe(true)
|
|
685
|
-
expect(ctrlMetaDelete.meta).toBe(true)
|
|
686
|
-
expect(ctrlMetaDelete.option).toBe(true)
|
|
687
|
-
expect(ctrlMetaDelete.shift).toBe(false)
|
|
688
|
-
expect(ctrlMetaDelete.sequence).toBe("\x1b[3;7~")
|
|
689
|
-
expect(ctrlMetaDelete.code).toBe("[3~")
|
|
690
|
-
})
|
|
691
|
-
|
|
692
|
-
test("parseKeypress - delete key with modifiers (Kitty keyboard protocol)", () => {
|
|
693
|
-
// Delete key in Kitty protocol uses code 57349
|
|
694
|
-
// Without modifiers: \x1b[57349u
|
|
695
|
-
const plainDelete = parseKeypress("\x1b[57349u", { useKittyKeyboard: true })!
|
|
696
|
-
expect(plainDelete.name).toBe("delete")
|
|
697
|
-
expect(plainDelete.shift).toBe(false)
|
|
698
|
-
expect(plainDelete.ctrl).toBe(false)
|
|
699
|
-
expect(plainDelete.meta).toBe(false)
|
|
700
|
-
expect(plainDelete.source).toBe("kitty")
|
|
701
|
-
|
|
702
|
-
// Shift+Delete: \x1b[57349;2u
|
|
703
|
-
const shiftDelete = parseKeypress("\x1b[57349;2u", { useKittyKeyboard: true })!
|
|
704
|
-
expect(shiftDelete.name).toBe("delete")
|
|
705
|
-
expect(shiftDelete.shift).toBe(true)
|
|
706
|
-
expect(shiftDelete.ctrl).toBe(false)
|
|
707
|
-
expect(shiftDelete.meta).toBe(false)
|
|
708
|
-
expect(shiftDelete.source).toBe("kitty")
|
|
709
|
-
|
|
710
|
-
// Option/Meta+Delete: \x1b[57349;3u
|
|
711
|
-
const metaDelete = parseKeypress("\x1b[57349;3u", { useKittyKeyboard: true })!
|
|
712
|
-
expect(metaDelete.name).toBe("delete")
|
|
713
|
-
expect(metaDelete.meta).toBe(true)
|
|
714
|
-
expect(metaDelete.ctrl).toBe(false)
|
|
715
|
-
expect(metaDelete.shift).toBe(false)
|
|
716
|
-
expect(metaDelete.source).toBe("kitty")
|
|
717
|
-
|
|
718
|
-
// Ctrl+Delete: \x1b[57349;5u
|
|
719
|
-
const ctrlDelete = parseKeypress("\x1b[57349;5u", { useKittyKeyboard: true })!
|
|
720
|
-
expect(ctrlDelete.name).toBe("delete")
|
|
721
|
-
expect(ctrlDelete.ctrl).toBe(true)
|
|
722
|
-
expect(ctrlDelete.shift).toBe(false)
|
|
723
|
-
expect(ctrlDelete.meta).toBe(false)
|
|
724
|
-
expect(ctrlDelete.source).toBe("kitty")
|
|
725
|
-
})
|
|
726
|
-
|
|
727
|
-
test("parseKeypress - backspace key with modifiers (modifyOtherKeys format)", () => {
|
|
728
|
-
// Backspace is typically \x7f or \b, but with modifiers uses modifyOtherKeys format
|
|
729
|
-
|
|
730
|
-
// Shift+Backspace: \x1b[27;2;127~ (using charcode 127)
|
|
731
|
-
const shiftBackspace = parseKeypress("\x1b[27;2;127~")!
|
|
732
|
-
expect(shiftBackspace.name).toBe("backspace")
|
|
733
|
-
expect(shiftBackspace.shift).toBe(true)
|
|
734
|
-
expect(shiftBackspace.ctrl).toBe(false)
|
|
735
|
-
expect(shiftBackspace.meta).toBe(false)
|
|
736
|
-
expect(shiftBackspace.option).toBe(false)
|
|
737
|
-
|
|
738
|
-
// Ctrl+Backspace: \x1b[27;5;127~
|
|
739
|
-
const ctrlBackspace = parseKeypress("\x1b[27;5;127~")!
|
|
740
|
-
expect(ctrlBackspace.name).toBe("backspace")
|
|
741
|
-
expect(ctrlBackspace.ctrl).toBe(true)
|
|
742
|
-
expect(ctrlBackspace.shift).toBe(false)
|
|
743
|
-
expect(ctrlBackspace.meta).toBe(false)
|
|
744
|
-
expect(ctrlBackspace.option).toBe(false)
|
|
745
|
-
|
|
746
|
-
// Option/Meta+Backspace: \x1b[27;3;127~
|
|
747
|
-
const metaBackspace = parseKeypress("\x1b[27;3;127~")!
|
|
748
|
-
expect(metaBackspace.name).toBe("backspace")
|
|
749
|
-
expect(metaBackspace.meta).toBe(true)
|
|
750
|
-
expect(metaBackspace.option).toBe(true)
|
|
751
|
-
expect(metaBackspace.ctrl).toBe(false)
|
|
752
|
-
expect(metaBackspace.shift).toBe(false)
|
|
753
|
-
})
|
|
754
|
-
|
|
755
|
-
test("parseKeypress - backspace key with modifiers (Kitty keyboard protocol)", () => {
|
|
756
|
-
// Backspace key in Kitty protocol uses code 127
|
|
757
|
-
// Ctrl+Backspace: \x1b[127;5u
|
|
758
|
-
const ctrlBackspace = parseKeypress("\x1b[127;5u", { useKittyKeyboard: true })!
|
|
759
|
-
expect(ctrlBackspace.name).toBe("backspace")
|
|
760
|
-
expect(ctrlBackspace.ctrl).toBe(true)
|
|
761
|
-
expect(ctrlBackspace.shift).toBe(false)
|
|
762
|
-
expect(ctrlBackspace.meta).toBe(false)
|
|
763
|
-
expect(ctrlBackspace.source).toBe("kitty")
|
|
764
|
-
|
|
765
|
-
// Option/Meta+Backspace: \x1b[127;3u
|
|
766
|
-
const metaBackspace = parseKeypress("\x1b[127;3u", { useKittyKeyboard: true })!
|
|
767
|
-
expect(metaBackspace.name).toBe("backspace")
|
|
768
|
-
expect(metaBackspace.meta).toBe(true)
|
|
769
|
-
expect(metaBackspace.ctrl).toBe(false)
|
|
770
|
-
expect(metaBackspace.shift).toBe(false)
|
|
771
|
-
expect(metaBackspace.source).toBe("kitty")
|
|
772
|
-
|
|
773
|
-
// Shift+Backspace: \x1b[127;2u
|
|
774
|
-
const shiftBackspace = parseKeypress("\x1b[127;2u", { useKittyKeyboard: true })!
|
|
775
|
-
expect(shiftBackspace.name).toBe("backspace")
|
|
776
|
-
expect(shiftBackspace.shift).toBe(true)
|
|
777
|
-
expect(shiftBackspace.ctrl).toBe(false)
|
|
778
|
-
expect(shiftBackspace.meta).toBe(false)
|
|
779
|
-
expect(shiftBackspace.source).toBe("kitty")
|
|
780
|
-
})
|
|
781
|
-
|
|
782
|
-
test("parseKeypress - Buffer input", () => {
|
|
783
|
-
const buf = Buffer.from("a")
|
|
784
|
-
expect(parseKeypress(buf)).toEqual({
|
|
785
|
-
eventType: "press",
|
|
786
|
-
name: "a",
|
|
787
|
-
ctrl: false,
|
|
788
|
-
meta: false,
|
|
789
|
-
shift: false,
|
|
790
|
-
option: false,
|
|
791
|
-
number: false,
|
|
792
|
-
sequence: "a",
|
|
793
|
-
raw: "a",
|
|
794
|
-
source: "raw",
|
|
795
|
-
})
|
|
796
|
-
})
|
|
797
|
-
|
|
798
|
-
test("parseKeypress - high byte buffer handling", () => {
|
|
799
|
-
const buf = Buffer.from([160]) // 128 + 32, should become \x1b + " "
|
|
800
|
-
expect(parseKeypress(buf)).toEqual({
|
|
801
|
-
eventType: "press",
|
|
802
|
-
name: "space",
|
|
803
|
-
ctrl: false,
|
|
804
|
-
meta: true,
|
|
805
|
-
shift: false,
|
|
806
|
-
option: false,
|
|
807
|
-
number: false,
|
|
808
|
-
sequence: "\x1b ",
|
|
809
|
-
raw: "\x1b ",
|
|
810
|
-
source: "raw",
|
|
811
|
-
})
|
|
812
|
-
})
|
|
813
|
-
|
|
814
|
-
test("parseKeypress - empty input", () => {
|
|
815
|
-
expect(parseKeypress("")).toEqual({
|
|
816
|
-
eventType: "press",
|
|
817
|
-
name: "",
|
|
818
|
-
ctrl: false,
|
|
819
|
-
meta: false,
|
|
820
|
-
shift: false,
|
|
821
|
-
option: false,
|
|
822
|
-
number: false,
|
|
823
|
-
sequence: "",
|
|
824
|
-
raw: "",
|
|
825
|
-
source: "raw",
|
|
826
|
-
})
|
|
827
|
-
|
|
828
|
-
expect(parseKeypress()).toEqual({
|
|
829
|
-
eventType: "press",
|
|
830
|
-
name: "",
|
|
831
|
-
ctrl: false,
|
|
832
|
-
meta: false,
|
|
833
|
-
shift: false,
|
|
834
|
-
option: false,
|
|
835
|
-
number: false,
|
|
836
|
-
sequence: "",
|
|
837
|
-
raw: "",
|
|
838
|
-
source: "raw",
|
|
839
|
-
})
|
|
840
|
-
})
|
|
841
|
-
|
|
842
|
-
test("parseKeypress - special characters", () => {
|
|
843
|
-
expect(parseKeypress("!")).toEqual({
|
|
844
|
-
eventType: "press",
|
|
845
|
-
name: "!",
|
|
846
|
-
ctrl: false,
|
|
847
|
-
meta: false,
|
|
848
|
-
shift: false,
|
|
849
|
-
option: false,
|
|
850
|
-
number: false,
|
|
851
|
-
sequence: "!",
|
|
852
|
-
raw: "!",
|
|
853
|
-
source: "raw",
|
|
854
|
-
})
|
|
855
|
-
|
|
856
|
-
expect(parseKeypress("@")).toEqual({
|
|
857
|
-
eventType: "press",
|
|
858
|
-
name: "@",
|
|
859
|
-
ctrl: false,
|
|
860
|
-
meta: false,
|
|
861
|
-
shift: false,
|
|
862
|
-
option: false,
|
|
863
|
-
number: false,
|
|
864
|
-
sequence: "@",
|
|
865
|
-
raw: "@",
|
|
866
|
-
source: "raw",
|
|
867
|
-
})
|
|
868
|
-
})
|
|
869
|
-
|
|
870
|
-
test("parseKeypress - meta space and escape combinations", () => {
|
|
871
|
-
expect(parseKeypress("\x1b ")).toEqual({
|
|
872
|
-
eventType: "press",
|
|
873
|
-
name: "space",
|
|
874
|
-
ctrl: false,
|
|
875
|
-
meta: true,
|
|
876
|
-
shift: false,
|
|
877
|
-
option: false,
|
|
878
|
-
number: false,
|
|
879
|
-
sequence: "\x1b ",
|
|
880
|
-
raw: "\x1b ",
|
|
881
|
-
source: "raw",
|
|
882
|
-
})
|
|
883
|
-
|
|
884
|
-
expect(parseKeypress("\x1b\x1b")).toEqual({
|
|
885
|
-
eventType: "press",
|
|
886
|
-
name: "escape",
|
|
887
|
-
ctrl: false,
|
|
888
|
-
meta: true,
|
|
889
|
-
shift: false,
|
|
890
|
-
option: false,
|
|
891
|
-
number: false,
|
|
892
|
-
sequence: "\x1b\x1b",
|
|
893
|
-
raw: "\x1b\x1b",
|
|
894
|
-
source: "raw",
|
|
895
|
-
})
|
|
896
|
-
})
|
|
897
|
-
|
|
898
|
-
test("parseKeypress - rxvt style arrow keys with modifiers", () => {
|
|
899
|
-
expect(parseKeypress("\x1b[a")).toEqual({
|
|
900
|
-
eventType: "press",
|
|
901
|
-
name: "up",
|
|
902
|
-
ctrl: false,
|
|
903
|
-
meta: false,
|
|
904
|
-
shift: true,
|
|
905
|
-
option: false,
|
|
906
|
-
number: false,
|
|
907
|
-
sequence: "\x1b[a",
|
|
908
|
-
raw: "\x1b[a",
|
|
909
|
-
code: "[a",
|
|
910
|
-
super: false,
|
|
911
|
-
hyper: false,
|
|
912
|
-
source: "raw",
|
|
913
|
-
})
|
|
914
|
-
|
|
915
|
-
expect(parseKeypress("\x1b[2$")).toEqual({
|
|
916
|
-
eventType: "press",
|
|
917
|
-
name: "insert",
|
|
918
|
-
ctrl: false,
|
|
919
|
-
meta: false,
|
|
920
|
-
shift: true,
|
|
921
|
-
option: false,
|
|
922
|
-
number: false,
|
|
923
|
-
sequence: "\x1b[2$",
|
|
924
|
-
raw: "\x1b[2$",
|
|
925
|
-
code: "[2$",
|
|
926
|
-
super: false,
|
|
927
|
-
hyper: false,
|
|
928
|
-
source: "raw",
|
|
929
|
-
})
|
|
930
|
-
})
|
|
931
|
-
|
|
932
|
-
test("parseKeypress - ctrl modifier keys", () => {
|
|
933
|
-
expect(parseKeypress("\x1bOa")).toEqual({
|
|
934
|
-
eventType: "press",
|
|
935
|
-
name: "up",
|
|
936
|
-
ctrl: true,
|
|
937
|
-
meta: false,
|
|
938
|
-
shift: false,
|
|
939
|
-
option: false,
|
|
940
|
-
number: false,
|
|
941
|
-
sequence: "\x1bOa",
|
|
942
|
-
raw: "\x1bOa",
|
|
943
|
-
code: "Oa",
|
|
944
|
-
super: false,
|
|
945
|
-
hyper: false,
|
|
946
|
-
source: "raw",
|
|
947
|
-
})
|
|
948
|
-
|
|
949
|
-
expect(parseKeypress("\x1b[2^")).toEqual({
|
|
950
|
-
eventType: "press",
|
|
951
|
-
name: "insert",
|
|
952
|
-
ctrl: true,
|
|
953
|
-
meta: false,
|
|
954
|
-
shift: false,
|
|
955
|
-
option: false,
|
|
956
|
-
number: false,
|
|
957
|
-
sequence: "\x1b[2^",
|
|
958
|
-
raw: "\x1b[2^",
|
|
959
|
-
code: "[2^",
|
|
960
|
-
super: false,
|
|
961
|
-
hyper: false,
|
|
962
|
-
source: "raw",
|
|
963
|
-
})
|
|
964
|
-
})
|
|
965
|
-
|
|
966
|
-
test("nonAlphanumericKeys export", () => {
|
|
967
|
-
expect(Array.isArray(nonAlphanumericKeys)).toBe(true)
|
|
968
|
-
expect(nonAlphanumericKeys.length).toBeGreaterThan(0)
|
|
969
|
-
expect(nonAlphanumericKeys).toContain("up")
|
|
970
|
-
expect(nonAlphanumericKeys).toContain("down")
|
|
971
|
-
expect(nonAlphanumericKeys).toContain("f1")
|
|
972
|
-
expect(nonAlphanumericKeys).toContain("backspace")
|
|
973
|
-
expect(nonAlphanumericKeys).toContain("tab")
|
|
974
|
-
expect(nonAlphanumericKeys).toContain("left")
|
|
975
|
-
expect(nonAlphanumericKeys).toContain("right")
|
|
976
|
-
})
|
|
977
|
-
|
|
978
|
-
// Tests for modifier bit calculations and meta/option relationship
|
|
979
|
-
// Terminal modifier bits (ANSI standard): Shift=1, Alt/Option=2, Ctrl=4, Meta=8
|
|
980
|
-
//
|
|
981
|
-
// IMPORTANT REALITY CHECK:
|
|
982
|
-
// - Alt and Option are THE SAME PHYSICAL KEY (macOS calls it Option, others call it Alt)
|
|
983
|
-
// - Cmd (Mac), Win (Windows), and many Ctrl combos DON'T reach the terminal - OS intercepts them
|
|
984
|
-
// - The "Meta" modifier bit (8) exists in ANSI standard but is THEORETICAL
|
|
985
|
-
// - In practice, only Alt/Option generates modifier sequences that reach the terminal
|
|
986
|
-
//
|
|
987
|
-
// The `option` flag: true when ANSI escape sequence has explicit Alt modifier bit (bit 2)
|
|
988
|
-
// The `meta` flag: true when ESC prefix is detected OR ANSI Alt/Meta bits are set (legacy naming)
|
|
989
|
-
//
|
|
990
|
-
// Real terminal behavior on macOS (see key-results file):
|
|
991
|
-
// - Alt+letter: sends ESC+char (e.g., "\x1ba") → meta=true, option=false
|
|
992
|
-
// - Alt+arrow: sends ANSI sequence (e.g., "\x1b[1;3A") → meta=true, option=true
|
|
993
|
-
// - Cmd+anything: NO EVENT reaches terminal (OS intercepts)
|
|
994
|
-
// - Ctrl+arrow: NO EVENT reaches terminal on Mac (OS intercepts)
|
|
995
|
-
test("parseKeypress - modifier bit calculations and meta/option relationship", () => {
|
|
996
|
-
// Individual modifiers to establish the baseline
|
|
997
|
-
|
|
998
|
-
// Shift modifier is bit 0 (value 1), so modifier value 2 = 1 + 1
|
|
999
|
-
const shiftOnly = parseKeypress("\x1b[1;2A")!
|
|
1000
|
-
expect(shiftOnly.name).toBe("up")
|
|
1001
|
-
expect(shiftOnly.shift).toBe(true)
|
|
1002
|
-
expect(shiftOnly.ctrl).toBe(false)
|
|
1003
|
-
expect(shiftOnly.meta).toBe(false)
|
|
1004
|
-
expect(shiftOnly.option).toBe(false)
|
|
1005
|
-
|
|
1006
|
-
// Alt/Option modifier is bit 1 (value 2), so modifier value 3 = 2 + 1
|
|
1007
|
-
// IMPORTANT: Alt/Option (same key, different names) sets BOTH meta and option flags
|
|
1008
|
-
const altOnly = parseKeypress("\x1b[1;3A")!
|
|
1009
|
-
expect(altOnly.name).toBe("up")
|
|
1010
|
-
expect(altOnly.meta).toBe(true) // Alt/Option sets meta flag
|
|
1011
|
-
expect(altOnly.option).toBe(true) // Alt/Option sets option flag
|
|
1012
|
-
expect(altOnly.ctrl).toBe(false)
|
|
1013
|
-
expect(altOnly.shift).toBe(false)
|
|
1014
|
-
|
|
1015
|
-
// Ctrl modifier is bit 2 (value 4), so modifier value 5 = 4 + 1
|
|
1016
|
-
const ctrlOnly = parseKeypress("\x1b[1;5A")!
|
|
1017
|
-
expect(ctrlOnly.name).toBe("up")
|
|
1018
|
-
expect(ctrlOnly.ctrl).toBe(true)
|
|
1019
|
-
expect(ctrlOnly.meta).toBe(false)
|
|
1020
|
-
expect(ctrlOnly.shift).toBe(false)
|
|
1021
|
-
expect(ctrlOnly.option).toBe(false)
|
|
1022
|
-
|
|
1023
|
-
// Super modifier is bit 3 (value 8), so modifier value 9 = 8 + 1
|
|
1024
|
-
// Super is the Command/Windows key
|
|
1025
|
-
const superOnly = parseKeypress("\x1b[1;9A")!
|
|
1026
|
-
expect(superOnly.name).toBe("up")
|
|
1027
|
-
expect(superOnly.meta).toBe(false)
|
|
1028
|
-
expect(superOnly.option).toBe(false)
|
|
1029
|
-
expect(superOnly.ctrl).toBe(false)
|
|
1030
|
-
expect(superOnly.shift).toBe(false)
|
|
1031
|
-
expect(superOnly.super).toBe(true)
|
|
1032
|
-
expect(superOnly.hyper).toBe(false)
|
|
1033
|
-
|
|
1034
|
-
// Combined modifiers to test the relationships
|
|
1035
|
-
|
|
1036
|
-
// Ctrl+Super = 4 + 8 = 12, so modifier value 13 = 12 + 1
|
|
1037
|
-
const ctrlSuper = parseKeypress("\x1b[1;13A")!
|
|
1038
|
-
expect(ctrlSuper.name).toBe("up")
|
|
1039
|
-
expect(ctrlSuper.ctrl).toBe(true)
|
|
1040
|
-
expect(ctrlSuper.meta).toBe(false)
|
|
1041
|
-
expect(ctrlSuper.shift).toBe(false)
|
|
1042
|
-
expect(ctrlSuper.option).toBe(false)
|
|
1043
|
-
expect(ctrlSuper.super).toBe(true)
|
|
1044
|
-
expect(ctrlSuper.hyper).toBe(false)
|
|
1045
|
-
|
|
1046
|
-
// Shift+Alt/Option = 1 + 2 = 3, so modifier value 4 = 3 + 1
|
|
1047
|
-
// Should have meta=true, option=true (Alt/Option key is pressed)
|
|
1048
|
-
const shiftAlt = parseKeypress("\x1b[1;4A")!
|
|
1049
|
-
expect(shiftAlt.name).toBe("up")
|
|
1050
|
-
expect(shiftAlt.shift).toBe(true)
|
|
1051
|
-
expect(shiftAlt.option).toBe(true) // Alt/Option sets option
|
|
1052
|
-
expect(shiftAlt.meta).toBe(true) // Alt/Option also sets meta
|
|
1053
|
-
expect(shiftAlt.ctrl).toBe(false)
|
|
1054
|
-
|
|
1055
|
-
// Alt/Option+Meta/Cmd = 2 + 8 = 10, so modifier value 11 = 10 + 1
|
|
1056
|
-
// Both physical keys pressed: Alt/Option key AND Meta/Cmd key
|
|
1057
|
-
const altMeta = parseKeypress("\x1b[1;11A")!
|
|
1058
|
-
expect(altMeta.name).toBe("up")
|
|
1059
|
-
expect(altMeta.meta).toBe(true) // Both Alt/Option and Meta/Cmd set meta flag
|
|
1060
|
-
expect(altMeta.option).toBe(true) // Alt/Option sets option flag
|
|
1061
|
-
expect(altMeta.ctrl).toBe(false)
|
|
1062
|
-
expect(altMeta.shift).toBe(false)
|
|
1063
|
-
|
|
1064
|
-
// Ctrl+Alt/Option = 4 + 2 = 6, so modifier value 7 = 6 + 1
|
|
1065
|
-
// Should have meta=true, option=true (Alt/Option key is pressed)
|
|
1066
|
-
const ctrlAlt = parseKeypress("\x1b[1;7A")!
|
|
1067
|
-
expect(ctrlAlt.name).toBe("up")
|
|
1068
|
-
expect(ctrlAlt.ctrl).toBe(true)
|
|
1069
|
-
expect(ctrlAlt.meta).toBe(true) // Alt/Option sets meta
|
|
1070
|
-
expect(ctrlAlt.option).toBe(true) // Alt/Option sets option
|
|
1071
|
-
expect(ctrlAlt.shift).toBe(false)
|
|
1072
|
-
|
|
1073
|
-
// All modifiers: Shift(1) + Alt(2) + Ctrl(4) + Meta(8) = 15, so modifier value 16 = 15 + 1
|
|
1074
|
-
const allMods = parseKeypress("\x1b[1;16A")!
|
|
1075
|
-
expect(allMods.name).toBe("up")
|
|
1076
|
-
expect(allMods.shift).toBe(true)
|
|
1077
|
-
expect(allMods.option).toBe(true) // Alt is present
|
|
1078
|
-
expect(allMods.ctrl).toBe(true)
|
|
1079
|
-
expect(allMods.meta).toBe(true) // Both Alt and Meta are present
|
|
1080
|
-
})
|
|
1081
|
-
|
|
1082
|
-
test("parseKeypress - distinguishing between Alt/Option and theoretical Meta modifier", () => {
|
|
1083
|
-
// IMPORTANT REALITY:
|
|
1084
|
-
// - Alt and Option are THE SAME PHYSICAL KEY (macOS calls it Option, others call it Alt)
|
|
1085
|
-
// - This is the ONLY modifier key that reliably reaches the terminal
|
|
1086
|
-
// - Cmd (Mac) and Win (Windows) keys are intercepted by the OS and DON'T reach the terminal
|
|
1087
|
-
// - The ANSI "Meta" modifier bit (8) is part of the standard but rarely/never seen in practice
|
|
1088
|
-
//
|
|
1089
|
-
// Real terminal behavior (see key-results file):
|
|
1090
|
-
// - Alt+letter: "\x1ba" → meta=true, option=false (simple ESC prefix)
|
|
1091
|
-
// - Alt+arrow: "\x1b[1;3A" → meta=true, option=true (ANSI with Alt bit)
|
|
1092
|
-
// - Cmd+anything: NO EVENT (OS intercepts)
|
|
1093
|
-
|
|
1094
|
-
// Alt/Option key with arrow (ANSI sequence with modifier bit 2)
|
|
1095
|
-
const altArrow = parseKeypress("\x1b[1;3C")! // Real: Alt/Option+Right
|
|
1096
|
-
expect(altArrow.name).toBe("right")
|
|
1097
|
-
expect(altArrow.meta).toBe(true)
|
|
1098
|
-
expect(altArrow.option).toBe(true)
|
|
1099
|
-
expect(altArrow.ctrl).toBe(false)
|
|
1100
|
-
expect(altArrow.shift).toBe(false)
|
|
1101
|
-
|
|
1102
|
-
// Super key: bit 8
|
|
1103
|
-
const superArrow = parseKeypress("\x1b[1;9C")! // Super bit only
|
|
1104
|
-
expect(superArrow.name).toBe("right")
|
|
1105
|
-
expect(superArrow.meta).toBe(false)
|
|
1106
|
-
expect(superArrow.option).toBe(false)
|
|
1107
|
-
expect(superArrow.ctrl).toBe(false)
|
|
1108
|
-
expect(superArrow.shift).toBe(false)
|
|
1109
|
-
expect(superArrow.super).toBe(true)
|
|
1110
|
-
expect(superArrow.hyper).toBe(false)
|
|
1111
|
-
|
|
1112
|
-
// To detect if Alt/Option was pressed in ANSI sequences: check option=true
|
|
1113
|
-
expect(altArrow.option).toBe(true)
|
|
1114
|
-
|
|
1115
|
-
// Both Alt and Super bits set
|
|
1116
|
-
const altSuperArrow = parseKeypress("\x1b[1;11C")! // Alt+Super bits
|
|
1117
|
-
expect(altSuperArrow.meta).toBe(true)
|
|
1118
|
-
expect(altSuperArrow.option).toBe(true)
|
|
1119
|
-
expect(altSuperArrow.super).toBe(true)
|
|
1120
|
-
expect(altSuperArrow.hyper).toBe(false)
|
|
1121
|
-
})
|
|
1122
|
-
|
|
1123
|
-
test("parseKeypress - modifier combinations with function keys", () => {
|
|
1124
|
-
// Ctrl+F1 - may work depending on OS/terminal configuration
|
|
1125
|
-
const ctrlF1 = parseKeypress("\x1b[11;5~")!
|
|
1126
|
-
expect(ctrlF1.name).toBe("f1")
|
|
1127
|
-
expect(ctrlF1.ctrl).toBe(true)
|
|
1128
|
-
expect(ctrlF1.meta).toBe(false)
|
|
1129
|
-
expect(ctrlF1.option).toBe(false)
|
|
1130
|
-
expect(ctrlF1.eventType).toBe("press")
|
|
1131
|
-
|
|
1132
|
-
// Alt/Option+F1 - real key combination that reaches terminal
|
|
1133
|
-
const altF1 = parseKeypress("\x1b[11;3~")!
|
|
1134
|
-
expect(altF1.name).toBe("f1")
|
|
1135
|
-
expect(altF1.meta).toBe(true)
|
|
1136
|
-
expect(altF1.option).toBe(true)
|
|
1137
|
-
expect(altF1.ctrl).toBe(false)
|
|
1138
|
-
expect(altF1.eventType).toBe("press")
|
|
1139
|
-
|
|
1140
|
-
// Super key (bit 8)
|
|
1141
|
-
const superF1 = parseKeypress("\x1b[11;9~")!
|
|
1142
|
-
expect(superF1.name).toBe("f1")
|
|
1143
|
-
expect(superF1.meta).toBe(false)
|
|
1144
|
-
expect(superF1.option).toBe(false)
|
|
1145
|
-
expect(superF1.ctrl).toBe(false)
|
|
1146
|
-
expect(superF1.super).toBe(true)
|
|
1147
|
-
expect(superF1.hyper).toBe(false)
|
|
1148
|
-
expect(superF1.eventType).toBe("press")
|
|
1149
|
-
|
|
1150
|
-
// Shift+Ctrl+F1 - may work depending on OS/terminal configuration
|
|
1151
|
-
const shiftCtrlF1 = parseKeypress("\x1b[11;6~")!
|
|
1152
|
-
expect(shiftCtrlF1.name).toBe("f1")
|
|
1153
|
-
expect(shiftCtrlF1.shift).toBe(true)
|
|
1154
|
-
expect(shiftCtrlF1.ctrl).toBe(true)
|
|
1155
|
-
expect(shiftCtrlF1.meta).toBe(false)
|
|
1156
|
-
expect(shiftCtrlF1.option).toBe(false)
|
|
1157
|
-
expect(shiftCtrlF1.eventType).toBe("press")
|
|
1158
|
-
})
|
|
1159
|
-
|
|
1160
|
-
test("parseKeypress - regular parsing always defaults to press event type", () => {
|
|
1161
|
-
// Test various regular key sequences to ensure they all default to "press"
|
|
1162
|
-
const keys = [
|
|
1163
|
-
"a",
|
|
1164
|
-
"A",
|
|
1165
|
-
"1",
|
|
1166
|
-
"!",
|
|
1167
|
-
"\t",
|
|
1168
|
-
"\r",
|
|
1169
|
-
"\n",
|
|
1170
|
-
" ",
|
|
1171
|
-
"\x1b",
|
|
1172
|
-
"\x01", // Ctrl+A
|
|
1173
|
-
"\x1ba", // Alt+A
|
|
1174
|
-
"\x1b[A", // Up arrow
|
|
1175
|
-
"\x1b[11~", // F1
|
|
1176
|
-
"\x1b[1;2A", // Shift+Up
|
|
1177
|
-
"\x1b[3~", // Delete
|
|
1178
|
-
]
|
|
1179
|
-
|
|
1180
|
-
for (const keySeq of keys) {
|
|
1181
|
-
const result = parseKeypress(keySeq)!
|
|
1182
|
-
expect(result.eventType).toBe("press")
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
// Test with Buffer input too
|
|
1186
|
-
const bufResult = parseKeypress(Buffer.from("x"))
|
|
1187
|
-
expect(bufResult?.eventType).toBe("press")
|
|
1188
|
-
})
|
|
1189
|
-
|
|
1190
|
-
test("KeyEventType type validation", () => {
|
|
1191
|
-
// Test that KeyEventType only allows valid values
|
|
1192
|
-
const validEventTypes: KeyEventType[] = ["press", "repeat", "release"]
|
|
1193
|
-
|
|
1194
|
-
for (const eventType of validEventTypes) {
|
|
1195
|
-
// This should compile without errors
|
|
1196
|
-
const mockKey: ParsedKey = {
|
|
1197
|
-
name: "test",
|
|
1198
|
-
ctrl: false,
|
|
1199
|
-
meta: false,
|
|
1200
|
-
shift: false,
|
|
1201
|
-
option: false,
|
|
1202
|
-
sequence: "test",
|
|
1203
|
-
raw: "test",
|
|
1204
|
-
number: false,
|
|
1205
|
-
eventType: eventType,
|
|
1206
|
-
source: "raw",
|
|
1207
|
-
}
|
|
1208
|
-
expect(mockKey.eventType).toBe(eventType)
|
|
1209
|
-
}
|
|
1210
|
-
})
|
|
1211
|
-
|
|
1212
|
-
test("parseKeypress - ctrl+option+letter combinations", () => {
|
|
1213
|
-
// This is ESC (\x1b) followed by \x15 (which is Ctrl+U)
|
|
1214
|
-
const ctrlOptionU = parseKeypress("\u001b\u0015")!
|
|
1215
|
-
|
|
1216
|
-
// The sequence should be parsed as meta+ctrl+u
|
|
1217
|
-
expect(ctrlOptionU?.name).toBe("u")
|
|
1218
|
-
expect(ctrlOptionU?.ctrl).toBe(true)
|
|
1219
|
-
expect(ctrlOptionU?.meta).toBe(true) // ESC prefix indicates meta/alt/option
|
|
1220
|
-
expect(ctrlOptionU?.shift).toBe(false)
|
|
1221
|
-
expect(ctrlOptionU?.option).toBe(false) // Note: option flag is separate from meta
|
|
1222
|
-
expect(ctrlOptionU?.sequence).toBe("\u001b\u0015")
|
|
1223
|
-
expect(ctrlOptionU?.raw).toBe("\u001b\u0015")
|
|
1224
|
-
expect(ctrlOptionU?.eventType).toBe("press")
|
|
1225
|
-
|
|
1226
|
-
// Test other meta+ctrl combinations
|
|
1227
|
-
const metaCtrlA = parseKeypress("\x1b\x01") // ESC + Ctrl+A
|
|
1228
|
-
expect(metaCtrlA?.name).toBe("a")
|
|
1229
|
-
expect(metaCtrlA?.ctrl).toBe(true)
|
|
1230
|
-
expect(metaCtrlA?.meta).toBe(true)
|
|
1231
|
-
expect(metaCtrlA?.shift).toBe(false)
|
|
1232
|
-
expect(metaCtrlA?.option).toBe(false)
|
|
1233
|
-
|
|
1234
|
-
const metaCtrlZ = parseKeypress("\x1b\x1a") // ESC + Ctrl+Z
|
|
1235
|
-
expect(metaCtrlZ?.name).toBe("z")
|
|
1236
|
-
expect(metaCtrlZ?.ctrl).toBe(true)
|
|
1237
|
-
expect(metaCtrlZ?.meta).toBe(true)
|
|
1238
|
-
expect(metaCtrlZ?.shift).toBe(false)
|
|
1239
|
-
expect(metaCtrlZ?.option).toBe(false)
|
|
1240
|
-
|
|
1241
|
-
// Test option+shift+u for comparison (this reportedly works)
|
|
1242
|
-
// Option+Shift+U generates ESC + U (uppercase)
|
|
1243
|
-
const optionShiftU = parseKeypress("\x1bU")!
|
|
1244
|
-
expect(optionShiftU?.name).toBe("U")
|
|
1245
|
-
expect(optionShiftU?.meta).toBe(true)
|
|
1246
|
-
expect(optionShiftU?.shift).toBe(true)
|
|
1247
|
-
expect(optionShiftU?.ctrl).toBe(false)
|
|
1248
|
-
expect(optionShiftU?.option).toBe(false)
|
|
1249
|
-
|
|
1250
|
-
// Edge case: ensure we don't match beyond \x1a (26, which is Ctrl+Z)
|
|
1251
|
-
const invalidCtrlSeq = parseKeypress("\x1b\x1b") // ESC + ESC (not a ctrl char)
|
|
1252
|
-
expect(invalidCtrlSeq?.name).toBe("escape")
|
|
1253
|
-
expect(invalidCtrlSeq?.meta).toBe(true)
|
|
1254
|
-
expect(invalidCtrlSeq?.ctrl).toBe(false)
|
|
1255
|
-
|
|
1256
|
-
// Edge case: test boundary at \x1a
|
|
1257
|
-
const metaCtrlAtBoundary = parseKeypress("\x1b\x1a") // ESC + Ctrl+Z
|
|
1258
|
-
expect(metaCtrlAtBoundary?.name).toBe("z")
|
|
1259
|
-
expect(metaCtrlAtBoundary?.ctrl).toBe(true)
|
|
1260
|
-
expect(metaCtrlAtBoundary?.meta).toBe(true)
|
|
1261
|
-
})
|
|
1262
|
-
|
|
1263
|
-
test("parseKeypress - filters out SGR mouse events", () => {
|
|
1264
|
-
const mouseDown = parseKeypress("\x1b[<0;10;5M")!
|
|
1265
|
-
expect(mouseDown).toBeNull()
|
|
1266
|
-
|
|
1267
|
-
const mouseUp = parseKeypress("\x1b[<0;10;5m")!
|
|
1268
|
-
expect(mouseUp).toBeNull()
|
|
1269
|
-
|
|
1270
|
-
const mouseDrag = parseKeypress("\x1b[<32;15;8M")!
|
|
1271
|
-
expect(mouseDrag).toBeNull()
|
|
1272
|
-
|
|
1273
|
-
const mouseScroll = parseKeypress("\x1b[<64;20;10M")!
|
|
1274
|
-
expect(mouseScroll).toBeNull()
|
|
1275
|
-
})
|
|
1276
|
-
|
|
1277
|
-
test("parseKeypress - filters out incomplete/partial SGR mouse sequences", () => {
|
|
1278
|
-
// These are flushed by the zig parser when a new ESC arrives mid-sequence
|
|
1279
|
-
expect(parseKeypress("\x1b[<35;")).toBeNull()
|
|
1280
|
-
expect(parseKeypress("\x1b[<35;20")).toBeNull()
|
|
1281
|
-
expect(parseKeypress("\x1b[<35;20;")).toBeNull()
|
|
1282
|
-
expect(parseKeypress("\x1b[<35;20;5")).toBeNull()
|
|
1283
|
-
expect(parseKeypress("\x1b[<")).toBeNull()
|
|
1284
|
-
expect(parseKeypress("\x1b[<0")).toBeNull()
|
|
1285
|
-
expect(parseKeypress("\x1b[<64;20;10")).toBeNull()
|
|
1286
|
-
})
|
|
1287
|
-
|
|
1288
|
-
test("parseKeypress - filters out SGR mouse continuations without ESC", () => {
|
|
1289
|
-
// These can occur if ESC is flushed on timeout before the rest of the sequence arrives.
|
|
1290
|
-
expect(parseKeypress("[<35;20;5m")).toBeNull()
|
|
1291
|
-
expect(parseKeypress("[<0;10;5M")).toBeNull()
|
|
1292
|
-
expect(parseKeypress("[<35;")).toBeNull()
|
|
1293
|
-
expect(parseKeypress("[<35;20")).toBeNull()
|
|
1294
|
-
expect(parseKeypress("[<35;20;")).toBeNull()
|
|
1295
|
-
expect(parseKeypress("[<")).toBeNull()
|
|
1296
|
-
expect(parseKeypress("[<64;20;10")).toBeNull()
|
|
1297
|
-
})
|
|
1298
|
-
|
|
1299
|
-
test("parseKeypress - filters out basic mouse events", () => {
|
|
1300
|
-
const basicMouse = parseKeypress("\x1b[M abc")!
|
|
1301
|
-
expect(basicMouse).toBeNull()
|
|
1302
|
-
})
|
|
1303
|
-
|
|
1304
|
-
test("parseKeypress - filters out terminal response sequences", () => {
|
|
1305
|
-
// Window/cell size reports - Format: ESC[4;height;width t or ESC[8;rows;cols t
|
|
1306
|
-
// Example: resolution query response "\u001b[4;1782;3012t"
|
|
1307
|
-
const windowSize1 = parseKeypress("\u001b[4;1782;3012t")
|
|
1308
|
-
expect(windowSize1).toBeNull()
|
|
1309
|
-
|
|
1310
|
-
const windowSize2 = parseKeypress("\x1b[4;800;600t")
|
|
1311
|
-
expect(windowSize2).toBeNull()
|
|
1312
|
-
|
|
1313
|
-
const cellSize = parseKeypress("\x1b[8;24;80t")
|
|
1314
|
-
expect(cellSize).toBeNull()
|
|
1315
|
-
|
|
1316
|
-
// Cursor position reports - Format: ESC[row;col R
|
|
1317
|
-
// Response to DSR (Device Status Report) query
|
|
1318
|
-
const cursorPos1 = parseKeypress("\x1b[10;25R")
|
|
1319
|
-
expect(cursorPos1).toBeNull()
|
|
1320
|
-
|
|
1321
|
-
const cursorPos2 = parseKeypress("\u001b[1;1R")
|
|
1322
|
-
expect(cursorPos2).toBeNull()
|
|
1323
|
-
|
|
1324
|
-
// Device Attributes (DA) responses - Format: ESC[?...c
|
|
1325
|
-
// Response to terminal identification query
|
|
1326
|
-
const deviceAttrs1 = parseKeypress("\x1b[?1;2c")
|
|
1327
|
-
expect(deviceAttrs1).toBeNull()
|
|
1328
|
-
|
|
1329
|
-
const deviceAttrs2 = parseKeypress("\x1b[?62;c")
|
|
1330
|
-
expect(deviceAttrs2).toBeNull()
|
|
1331
|
-
|
|
1332
|
-
const deviceAttrs3 = parseKeypress("\x1b[?1;0;6;9;15c")
|
|
1333
|
-
expect(deviceAttrs3).toBeNull()
|
|
1334
|
-
|
|
1335
|
-
// Mode reports - Format: ESC[?...;...$y
|
|
1336
|
-
// Response to DECRQM (Request Mode) query
|
|
1337
|
-
const modeReport1 = parseKeypress("\x1b[?1;2$y")
|
|
1338
|
-
expect(modeReport1).toBeNull()
|
|
1339
|
-
|
|
1340
|
-
const modeReport2 = parseKeypress("\x1b[?25;1$y")
|
|
1341
|
-
expect(modeReport2).toBeNull()
|
|
1342
|
-
|
|
1343
|
-
// Focus events
|
|
1344
|
-
const focusIn = parseKeypress("\x1b[I")
|
|
1345
|
-
expect(focusIn).toBeNull()
|
|
1346
|
-
|
|
1347
|
-
const focusOut = parseKeypress("\x1b[O")
|
|
1348
|
-
expect(focusOut).toBeNull()
|
|
1349
|
-
|
|
1350
|
-
// OSC (Operating System Command) responses - color/style queries
|
|
1351
|
-
// Format: ESC]...ESC\ or ESC]...BEL
|
|
1352
|
-
// Must be complete sequences with proper terminators to be filtered
|
|
1353
|
-
const oscResponse1 = parseKeypress("\x1b]11;rgb:0000/0000/0000\x1b\\")
|
|
1354
|
-
expect(oscResponse1).toBeNull()
|
|
1355
|
-
|
|
1356
|
-
const oscResponse2 = parseKeypress("\x1b]10;rgb:ffff/ffff/ffff\x07")
|
|
1357
|
-
expect(oscResponse2).toBeNull()
|
|
1358
|
-
|
|
1359
|
-
// Incomplete OSC sequences should NOT be filtered
|
|
1360
|
-
// The stdin parser will either complete them or timeout and flush them
|
|
1361
|
-
const incompleteOsc = parseKeypress("\x1b]11;rgb:0000")
|
|
1362
|
-
expect(incompleteOsc).not.toBeNull()
|
|
1363
|
-
expect(incompleteOsc?.name).toBe("") // Unknown sequence, but not filtered
|
|
1364
|
-
})
|
|
1365
|
-
|
|
1366
|
-
test("parseKeypress - does not filter valid key sequences that might look similar", () => {
|
|
1367
|
-
// Make sure we don't accidentally filter out valid keys
|
|
1368
|
-
|
|
1369
|
-
// F1-F12 should still work (e.g., [11~, [24~)
|
|
1370
|
-
const f1 = parseKeypress("\x1b[11~")
|
|
1371
|
-
expect(f1).not.toBeNull()
|
|
1372
|
-
expect(f1?.name).toBe("f1")
|
|
1373
|
-
|
|
1374
|
-
const f12 = parseKeypress("\x1b[24~")
|
|
1375
|
-
expect(f12).not.toBeNull()
|
|
1376
|
-
expect(f12?.name).toBe("f12")
|
|
1377
|
-
|
|
1378
|
-
// Arrow keys with O prefix should still work (SS3 sequences)
|
|
1379
|
-
const arrowUp = parseKeypress("\x1bOA")
|
|
1380
|
-
expect(arrowUp).not.toBeNull()
|
|
1381
|
-
expect(arrowUp?.name).toBe("up")
|
|
1382
|
-
|
|
1383
|
-
// Other SS3 sequences
|
|
1384
|
-
const ss3Down = parseKeypress("\x1bOB")
|
|
1385
|
-
expect(ss3Down).not.toBeNull()
|
|
1386
|
-
expect(ss3Down?.name).toBe("down")
|
|
1387
|
-
|
|
1388
|
-
// Note: ESC[O without a following character is filtered (focus out event)
|
|
1389
|
-
const focusOutFiltered = parseKeypress("\x1b[O")
|
|
1390
|
-
expect(focusOutFiltered).toBeNull()
|
|
1391
|
-
|
|
1392
|
-
// Standard arrow keys should still work
|
|
1393
|
-
const arrowLeft = parseKeypress("\x1b[D")
|
|
1394
|
-
expect(arrowLeft).not.toBeNull()
|
|
1395
|
-
expect(arrowLeft?.name).toBe("left")
|
|
1396
|
-
|
|
1397
|
-
// Modified keys should still work
|
|
1398
|
-
const ctrlUp = parseKeypress("\x1b[1;5A")
|
|
1399
|
-
expect(ctrlUp).not.toBeNull()
|
|
1400
|
-
expect(ctrlUp?.name).toBe("up")
|
|
1401
|
-
expect(ctrlUp?.ctrl).toBe(true)
|
|
1402
|
-
|
|
1403
|
-
// Delete, insert, page up/down should still work
|
|
1404
|
-
const deleteKey = parseKeypress("\x1b[3~")
|
|
1405
|
-
expect(deleteKey).not.toBeNull()
|
|
1406
|
-
expect(deleteKey?.name).toBe("delete")
|
|
1407
|
-
|
|
1408
|
-
const insertKey = parseKeypress("\x1b[2~")
|
|
1409
|
-
expect(insertKey).not.toBeNull()
|
|
1410
|
-
expect(insertKey?.name).toBe("insert")
|
|
1411
|
-
|
|
1412
|
-
const pageUp = parseKeypress("\x1b[5~")
|
|
1413
|
-
expect(pageUp).not.toBeNull()
|
|
1414
|
-
expect(pageUp?.name).toBe("pageup")
|
|
1415
|
-
|
|
1416
|
-
// Kitty keyboard protocol sequences should still work
|
|
1417
|
-
const kittyA = parseKeypress("\x1b[97u", { useKittyKeyboard: true })
|
|
1418
|
-
expect(kittyA).not.toBeNull()
|
|
1419
|
-
expect(kittyA?.name).toBe("a")
|
|
1420
|
-
expect(kittyA?.source).toBe("kitty")
|
|
1421
|
-
|
|
1422
|
-
const kittyArrow = parseKeypress("\x1b[57352u", { useKittyKeyboard: true })
|
|
1423
|
-
expect(kittyArrow).not.toBeNull()
|
|
1424
|
-
expect(kittyArrow?.name).toBe("up")
|
|
1425
|
-
expect(kittyArrow?.source).toBe("kitty")
|
|
1426
|
-
|
|
1427
|
-
// Bracketed paste markers should be filtered
|
|
1428
|
-
// They're handled by KeyHandler before parseKeypress is called,
|
|
1429
|
-
// but should return null for defense-in-depth
|
|
1430
|
-
const pasteStart = parseKeypress("\x1b[200~")
|
|
1431
|
-
expect(pasteStart).toBeNull()
|
|
1432
|
-
|
|
1433
|
-
const pasteEnd = parseKeypress("\x1b[201~")
|
|
1434
|
-
expect(pasteEnd).toBeNull()
|
|
1435
|
-
|
|
1436
|
-
// Control characters should still work (including BEL which is Ctrl+G)
|
|
1437
|
-
const bel = parseKeypress("\x07")
|
|
1438
|
-
expect(bel).not.toBeNull()
|
|
1439
|
-
expect(bel?.name).toBe("g")
|
|
1440
|
-
expect(bel?.ctrl).toBe(true)
|
|
1441
|
-
|
|
1442
|
-
const backspace = parseKeypress("\b")
|
|
1443
|
-
expect(backspace).not.toBeNull()
|
|
1444
|
-
expect(backspace?.name).toBe("backspace")
|
|
1445
|
-
|
|
1446
|
-
const backspace2 = parseKeypress("\x7f")
|
|
1447
|
-
expect(backspace2).not.toBeNull()
|
|
1448
|
-
expect(backspace2?.name).toBe("backspace")
|
|
1449
|
-
})
|
|
1450
|
-
|
|
1451
|
-
test("parseKeypress - source field is always 'raw' for non-Kitty parsing", () => {
|
|
1452
|
-
// Test various key types to ensure they all have source: "raw"
|
|
1453
|
-
const letter = parseKeypress("a")
|
|
1454
|
-
expect(letter?.source).toBe("raw")
|
|
1455
|
-
|
|
1456
|
-
const shiftLetter = parseKeypress("A")
|
|
1457
|
-
expect(shiftLetter?.source).toBe("raw")
|
|
1458
|
-
|
|
1459
|
-
const number = parseKeypress("5")
|
|
1460
|
-
expect(number?.source).toBe("raw")
|
|
1461
|
-
|
|
1462
|
-
const ctrlKey = parseKeypress("\x01") // Ctrl+A
|
|
1463
|
-
expect(ctrlKey?.source).toBe("raw")
|
|
1464
|
-
|
|
1465
|
-
const metaKey = parseKeypress("\x1ba") // Alt+A
|
|
1466
|
-
expect(metaKey?.source).toBe("raw")
|
|
1467
|
-
|
|
1468
|
-
const arrowKey = parseKeypress("\x1b[A") // Up arrow
|
|
1469
|
-
expect(arrowKey?.source).toBe("raw")
|
|
1470
|
-
|
|
1471
|
-
const functionKey = parseKeypress("\x1bOP") // F1
|
|
1472
|
-
expect(functionKey?.source).toBe("raw")
|
|
1473
|
-
|
|
1474
|
-
const modifiedArrow = parseKeypress("\x1b[1;5A") // Ctrl+Up
|
|
1475
|
-
expect(modifiedArrow?.source).toBe("raw")
|
|
1476
|
-
|
|
1477
|
-
const deleteKey = parseKeypress("\x1b[3~")
|
|
1478
|
-
expect(deleteKey?.source).toBe("raw")
|
|
1479
|
-
|
|
1480
|
-
const returnKey = parseKeypress("\r")
|
|
1481
|
-
expect(returnKey?.source).toBe("raw")
|
|
1482
|
-
|
|
1483
|
-
const tabKey = parseKeypress("\t")
|
|
1484
|
-
expect(tabKey?.source).toBe("raw")
|
|
1485
|
-
|
|
1486
|
-
const escapeKey = parseKeypress("\x1b")
|
|
1487
|
-
expect(escapeKey?.source).toBe("raw")
|
|
1488
|
-
})
|
|
1489
|
-
|
|
1490
|
-
test("parseKeypress - source field is 'kitty' when Kitty keyboard protocol is used", () => {
|
|
1491
|
-
// Test Kitty keyboard protocol sequences
|
|
1492
|
-
const kittyA = parseKeypress("\x1b[97u", { useKittyKeyboard: true })
|
|
1493
|
-
expect(kittyA?.source).toBe("kitty")
|
|
1494
|
-
expect(kittyA?.name).toBe("a")
|
|
1495
|
-
|
|
1496
|
-
const kittyArrow = parseKeypress("\x1b[57352u", { useKittyKeyboard: true }) // Up arrow
|
|
1497
|
-
expect(kittyArrow?.source).toBe("kitty")
|
|
1498
|
-
expect(kittyArrow?.name).toBe("up")
|
|
1499
|
-
|
|
1500
|
-
const kittyF1 = parseKeypress("\x1b[57364u", { useKittyKeyboard: true }) // F1
|
|
1501
|
-
expect(kittyF1?.source).toBe("kitty")
|
|
1502
|
-
expect(kittyF1?.name).toBe("f1")
|
|
1503
|
-
|
|
1504
|
-
const kittyCtrlA = parseKeypress("\x1b[97;5u", { useKittyKeyboard: true }) // Ctrl+A
|
|
1505
|
-
expect(kittyCtrlA?.source).toBe("kitty")
|
|
1506
|
-
expect(kittyCtrlA?.name).toBe("a")
|
|
1507
|
-
expect(kittyCtrlA?.ctrl).toBe(true)
|
|
1508
|
-
})
|
|
1509
|
-
|
|
1510
|
-
test("parseKeypress - fallback to raw parsing when Kitty option is enabled but sequence is not Kitty", () => {
|
|
1511
|
-
// Even with useKittyKeyboard enabled, non-Kitty sequences should use raw parsing
|
|
1512
|
-
const normalArrow = parseKeypress("\x1b[A", { useKittyKeyboard: true })
|
|
1513
|
-
expect(normalArrow?.source).toBe("raw")
|
|
1514
|
-
expect(normalArrow?.name).toBe("up")
|
|
1515
|
-
|
|
1516
|
-
const normalLetter = parseKeypress("a", { useKittyKeyboard: true })
|
|
1517
|
-
expect(normalLetter?.source).toBe("raw")
|
|
1518
|
-
expect(normalLetter?.name).toBe("a")
|
|
1519
|
-
|
|
1520
|
-
const normalCtrl = parseKeypress("\x01", { useKittyKeyboard: true })
|
|
1521
|
-
expect(normalCtrl?.source).toBe("raw")
|
|
1522
|
-
expect(normalCtrl?.name).toBe("a")
|
|
1523
|
-
expect(normalCtrl?.ctrl).toBe(true)
|
|
1524
|
-
})
|
|
1525
|
-
|
|
1526
|
-
test("parseKeypress - modifyOtherKeys digits", () => {
|
|
1527
|
-
const shiftOne = parseKeypress("\x1b[27;2;49~")!
|
|
1528
|
-
expect(shiftOne.name).toBe("1")
|
|
1529
|
-
expect(shiftOne.shift).toBe(true)
|
|
1530
|
-
expect(shiftOne.ctrl).toBe(false)
|
|
1531
|
-
expect(shiftOne.meta).toBe(false)
|
|
1532
|
-
expect(shiftOne.option).toBe(false)
|
|
1533
|
-
expect(shiftOne.number).toBe(true)
|
|
1534
|
-
expect(shiftOne.sequence).toBe("1")
|
|
1535
|
-
expect(shiftOne.raw).toBe("\x1b[27;2;49~")
|
|
1536
|
-
expect(shiftOne.eventType).toBe("press")
|
|
1537
|
-
expect(shiftOne.source).toBe("raw")
|
|
1538
|
-
})
|
|
1539
|
-
|
|
1540
|
-
test("parseKeypress - modifyOtherKeys modified enter keys", () => {
|
|
1541
|
-
// Terminals with modifyOtherKeys mode enabled send special escape sequences for modified keys
|
|
1542
|
-
// Format: CSI 27 ; modifier ; code ~ where code 13 is enter/return
|
|
1543
|
-
// This is part of the CSI u protocol and is sent by xterm, iTerm2, Ghostty, etc.
|
|
1544
|
-
|
|
1545
|
-
// Shift+Enter: CSI 27;2;13~ (modifier 2 = shift bit 1)
|
|
1546
|
-
const shiftEnter = parseKeypress("\u001b[27;2;13~")!
|
|
1547
|
-
expect(shiftEnter.name).toBe("return")
|
|
1548
|
-
expect(shiftEnter.shift).toBe(true)
|
|
1549
|
-
expect(shiftEnter.ctrl).toBe(false)
|
|
1550
|
-
expect(shiftEnter.meta).toBe(false)
|
|
1551
|
-
expect(shiftEnter.option).toBe(false)
|
|
1552
|
-
expect(shiftEnter.sequence).toBe("\u001b[27;2;13~")
|
|
1553
|
-
expect(shiftEnter.raw).toBe("\u001b[27;2;13~")
|
|
1554
|
-
expect(shiftEnter.eventType).toBe("press")
|
|
1555
|
-
expect(shiftEnter.source).toBe("raw")
|
|
1556
|
-
|
|
1557
|
-
// Test with \x1b notation as well
|
|
1558
|
-
const shiftEnter2 = parseKeypress("\x1b[27;2;13~")!
|
|
1559
|
-
expect(shiftEnter2.name).toBe("return")
|
|
1560
|
-
expect(shiftEnter2.shift).toBe(true)
|
|
1561
|
-
expect(shiftEnter2.ctrl).toBe(false)
|
|
1562
|
-
expect(shiftEnter2.meta).toBe(false)
|
|
1563
|
-
expect(shiftEnter2.option).toBe(false)
|
|
1564
|
-
|
|
1565
|
-
// Ctrl+Enter: CSI 27;5;13~ (modifier 5 = ctrl bit 4)
|
|
1566
|
-
const ctrlEnter = parseKeypress("\u001b[27;5;13~")!
|
|
1567
|
-
expect(ctrlEnter.name).toBe("return")
|
|
1568
|
-
expect(ctrlEnter.ctrl).toBe(true)
|
|
1569
|
-
expect(ctrlEnter.shift).toBe(false)
|
|
1570
|
-
expect(ctrlEnter.meta).toBe(false)
|
|
1571
|
-
expect(ctrlEnter.option).toBe(false)
|
|
1572
|
-
expect(ctrlEnter.sequence).toBe("\u001b[27;5;13~")
|
|
1573
|
-
expect(ctrlEnter.raw).toBe("\u001b[27;5;13~")
|
|
1574
|
-
expect(ctrlEnter.eventType).toBe("press")
|
|
1575
|
-
expect(ctrlEnter.source).toBe("raw")
|
|
1576
|
-
|
|
1577
|
-
// Test with \x1b notation
|
|
1578
|
-
const ctrlEnter2 = parseKeypress("\x1b[27;5;13~")!
|
|
1579
|
-
expect(ctrlEnter2.name).toBe("return")
|
|
1580
|
-
expect(ctrlEnter2.ctrl).toBe(true)
|
|
1581
|
-
expect(ctrlEnter2.shift).toBe(false)
|
|
1582
|
-
expect(ctrlEnter2.meta).toBe(false)
|
|
1583
|
-
expect(ctrlEnter2.option).toBe(false)
|
|
1584
|
-
|
|
1585
|
-
// Alt/Option+Enter: CSI 27;3;13~ (modifier 3 = alt/option bit 2)
|
|
1586
|
-
const altEnter = parseKeypress("\u001b[27;3;13~")!
|
|
1587
|
-
expect(altEnter.name).toBe("return")
|
|
1588
|
-
expect(altEnter.meta).toBe(true)
|
|
1589
|
-
expect(altEnter.option).toBe(true)
|
|
1590
|
-
expect(altEnter.ctrl).toBe(false)
|
|
1591
|
-
expect(altEnter.shift).toBe(false)
|
|
1592
|
-
expect(altEnter.sequence).toBe("\u001b[27;3;13~")
|
|
1593
|
-
expect(altEnter.raw).toBe("\u001b[27;3;13~")
|
|
1594
|
-
expect(altEnter.eventType).toBe("press")
|
|
1595
|
-
expect(altEnter.source).toBe("raw")
|
|
1596
|
-
|
|
1597
|
-
// Shift+Ctrl+Enter: CSI 27;6;13~ (modifier 6 = shift(1) + ctrl(4) = bits 5)
|
|
1598
|
-
const shiftCtrlEnter = parseKeypress("\u001b[27;6;13~")!
|
|
1599
|
-
expect(shiftCtrlEnter.name).toBe("return")
|
|
1600
|
-
expect(shiftCtrlEnter.shift).toBe(true)
|
|
1601
|
-
expect(shiftCtrlEnter.ctrl).toBe(true)
|
|
1602
|
-
expect(shiftCtrlEnter.meta).toBe(false)
|
|
1603
|
-
expect(shiftCtrlEnter.option).toBe(false)
|
|
1604
|
-
expect(shiftCtrlEnter.sequence).toBe("\u001b[27;6;13~")
|
|
1605
|
-
expect(shiftCtrlEnter.raw).toBe("\u001b[27;6;13~")
|
|
1606
|
-
expect(shiftCtrlEnter.eventType).toBe("press")
|
|
1607
|
-
expect(shiftCtrlEnter.source).toBe("raw")
|
|
1608
|
-
|
|
1609
|
-
// Shift+Alt+Enter: CSI 27;4;13~ (modifier 4 = shift(1) + alt(2) = bits 3)
|
|
1610
|
-
const shiftAltEnter = parseKeypress("\u001b[27;4;13~")!
|
|
1611
|
-
expect(shiftAltEnter.name).toBe("return")
|
|
1612
|
-
expect(shiftAltEnter.shift).toBe(true)
|
|
1613
|
-
expect(shiftAltEnter.meta).toBe(true)
|
|
1614
|
-
expect(shiftAltEnter.option).toBe(true)
|
|
1615
|
-
expect(shiftAltEnter.ctrl).toBe(false)
|
|
1616
|
-
expect(shiftAltEnter.sequence).toBe("\u001b[27;4;13~")
|
|
1617
|
-
expect(shiftAltEnter.raw).toBe("\u001b[27;4;13~")
|
|
1618
|
-
expect(shiftAltEnter.eventType).toBe("press")
|
|
1619
|
-
expect(shiftAltEnter.source).toBe("raw")
|
|
1620
|
-
|
|
1621
|
-
// Ctrl+Alt+Enter: CSI 27;7;13~ (modifier 7 = alt(2) + ctrl(4) = bits 6)
|
|
1622
|
-
const ctrlAltEnter = parseKeypress("\u001b[27;7;13~")!
|
|
1623
|
-
expect(ctrlAltEnter.name).toBe("return")
|
|
1624
|
-
expect(ctrlAltEnter.ctrl).toBe(true)
|
|
1625
|
-
expect(ctrlAltEnter.meta).toBe(true)
|
|
1626
|
-
expect(ctrlAltEnter.option).toBe(true)
|
|
1627
|
-
expect(ctrlAltEnter.shift).toBe(false)
|
|
1628
|
-
expect(ctrlAltEnter.sequence).toBe("\u001b[27;7;13~")
|
|
1629
|
-
expect(ctrlAltEnter.raw).toBe("\u001b[27;7;13~")
|
|
1630
|
-
expect(ctrlAltEnter.eventType).toBe("press")
|
|
1631
|
-
expect(ctrlAltEnter.source).toBe("raw")
|
|
1632
|
-
|
|
1633
|
-
// Shift+Ctrl+Alt+Enter: CSI 27;8;13~ (modifier 8 = shift(1) + alt(2) + ctrl(4) = bits 7)
|
|
1634
|
-
const allModsEnter = parseKeypress("\u001b[27;8;13~")!
|
|
1635
|
-
expect(allModsEnter.name).toBe("return")
|
|
1636
|
-
expect(allModsEnter.shift).toBe(true)
|
|
1637
|
-
expect(allModsEnter.ctrl).toBe(true)
|
|
1638
|
-
expect(allModsEnter.meta).toBe(true)
|
|
1639
|
-
expect(allModsEnter.option).toBe(true)
|
|
1640
|
-
expect(allModsEnter.sequence).toBe("\u001b[27;8;13~")
|
|
1641
|
-
expect(allModsEnter.raw).toBe("\u001b[27;8;13~")
|
|
1642
|
-
expect(allModsEnter.eventType).toBe("press")
|
|
1643
|
-
expect(allModsEnter.source).toBe("raw")
|
|
1644
|
-
})
|
|
1645
|
-
|
|
1646
|
-
test("parseKeypress - modifyOtherKeys modified escape keys", () => {
|
|
1647
|
-
// Terminals with modifyOtherKeys mode enabled also send modified escape key sequences
|
|
1648
|
-
// Format: CSI 27 ; modifier ; 27 ~ where code 27 is escape
|
|
1649
|
-
|
|
1650
|
-
// Ctrl+Escape: CSI 27;5;27~ (modifier 5 = ctrl bit 4)
|
|
1651
|
-
const ctrlEscape = parseKeypress("\u001b[27;5;27~")!
|
|
1652
|
-
expect(ctrlEscape.name).toBe("escape")
|
|
1653
|
-
expect(ctrlEscape.ctrl).toBe(true)
|
|
1654
|
-
expect(ctrlEscape.shift).toBe(false)
|
|
1655
|
-
expect(ctrlEscape.meta).toBe(false)
|
|
1656
|
-
expect(ctrlEscape.option).toBe(false)
|
|
1657
|
-
expect(ctrlEscape.sequence).toBe("\u001b[27;5;27~")
|
|
1658
|
-
expect(ctrlEscape.raw).toBe("\u001b[27;5;27~")
|
|
1659
|
-
expect(ctrlEscape.eventType).toBe("press")
|
|
1660
|
-
expect(ctrlEscape.source).toBe("raw")
|
|
1661
|
-
|
|
1662
|
-
// Test with \x1b notation as well
|
|
1663
|
-
const ctrlEscape2 = parseKeypress("\x1b[27;5;27~")!
|
|
1664
|
-
expect(ctrlEscape2.name).toBe("escape")
|
|
1665
|
-
expect(ctrlEscape2.ctrl).toBe(true)
|
|
1666
|
-
expect(ctrlEscape2.shift).toBe(false)
|
|
1667
|
-
expect(ctrlEscape2.meta).toBe(false)
|
|
1668
|
-
expect(ctrlEscape2.option).toBe(false)
|
|
1669
|
-
|
|
1670
|
-
// Shift+Escape: CSI 27;2;27~ (modifier 2 = shift bit 1)
|
|
1671
|
-
const shiftEscape = parseKeypress("\u001b[27;2;27~")!
|
|
1672
|
-
expect(shiftEscape.name).toBe("escape")
|
|
1673
|
-
expect(shiftEscape.shift).toBe(true)
|
|
1674
|
-
expect(shiftEscape.ctrl).toBe(false)
|
|
1675
|
-
expect(shiftEscape.meta).toBe(false)
|
|
1676
|
-
expect(shiftEscape.option).toBe(false)
|
|
1677
|
-
expect(shiftEscape.sequence).toBe("\u001b[27;2;27~")
|
|
1678
|
-
expect(shiftEscape.raw).toBe("\u001b[27;2;27~")
|
|
1679
|
-
expect(shiftEscape.eventType).toBe("press")
|
|
1680
|
-
expect(shiftEscape.source).toBe("raw")
|
|
1681
|
-
|
|
1682
|
-
// Alt+Escape: CSI 27;3;27~ (modifier 3 = alt/option bit 2)
|
|
1683
|
-
const altEscape = parseKeypress("\u001b[27;3;27~")!
|
|
1684
|
-
expect(altEscape.name).toBe("escape")
|
|
1685
|
-
expect(altEscape.meta).toBe(true)
|
|
1686
|
-
expect(altEscape.option).toBe(true)
|
|
1687
|
-
expect(altEscape.ctrl).toBe(false)
|
|
1688
|
-
expect(altEscape.shift).toBe(false)
|
|
1689
|
-
expect(altEscape.sequence).toBe("\u001b[27;3;27~")
|
|
1690
|
-
expect(altEscape.raw).toBe("\u001b[27;3;27~")
|
|
1691
|
-
expect(altEscape.eventType).toBe("press")
|
|
1692
|
-
expect(altEscape.source).toBe("raw")
|
|
1693
|
-
|
|
1694
|
-
// Shift+Ctrl+Escape: CSI 27;6;27~ (modifier 6 = shift(1) + ctrl(4) = bits 5)
|
|
1695
|
-
const shiftCtrlEscape = parseKeypress("\u001b[27;6;27~")!
|
|
1696
|
-
expect(shiftCtrlEscape.name).toBe("escape")
|
|
1697
|
-
expect(shiftCtrlEscape.shift).toBe(true)
|
|
1698
|
-
expect(shiftCtrlEscape.ctrl).toBe(true)
|
|
1699
|
-
expect(shiftCtrlEscape.meta).toBe(false)
|
|
1700
|
-
expect(shiftCtrlEscape.option).toBe(false)
|
|
1701
|
-
expect(shiftCtrlEscape.sequence).toBe("\u001b[27;6;27~")
|
|
1702
|
-
expect(shiftCtrlEscape.raw).toBe("\u001b[27;6;27~")
|
|
1703
|
-
expect(shiftCtrlEscape.eventType).toBe("press")
|
|
1704
|
-
expect(shiftCtrlEscape.source).toBe("raw")
|
|
1705
|
-
})
|
|
1706
|
-
|
|
1707
|
-
test("parseKeypress - modifyOtherKeys modified tab, space, and backspace keys", () => {
|
|
1708
|
-
// Tab key: charcode 9
|
|
1709
|
-
const ctrlTab = parseKeypress("\u001b[27;5;9~")!
|
|
1710
|
-
expect(ctrlTab.name).toBe("tab")
|
|
1711
|
-
expect(ctrlTab.ctrl).toBe(true)
|
|
1712
|
-
expect(ctrlTab.shift).toBe(false)
|
|
1713
|
-
expect(ctrlTab.meta).toBe(false)
|
|
1714
|
-
expect(ctrlTab.option).toBe(false)
|
|
1715
|
-
|
|
1716
|
-
const shiftTab = parseKeypress("\u001b[27;2;9~")!
|
|
1717
|
-
expect(shiftTab.name).toBe("tab")
|
|
1718
|
-
expect(shiftTab.shift).toBe(true)
|
|
1719
|
-
expect(shiftTab.ctrl).toBe(false)
|
|
1720
|
-
expect(shiftTab.meta).toBe(false)
|
|
1721
|
-
expect(shiftTab.option).toBe(false)
|
|
1722
|
-
|
|
1723
|
-
// Space key: charcode 32
|
|
1724
|
-
const ctrlSpace = parseKeypress("\u001b[27;5;32~")!
|
|
1725
|
-
expect(ctrlSpace.name).toBe("space")
|
|
1726
|
-
expect(ctrlSpace.ctrl).toBe(true)
|
|
1727
|
-
expect(ctrlSpace.shift).toBe(false)
|
|
1728
|
-
expect(ctrlSpace.meta).toBe(false)
|
|
1729
|
-
expect(ctrlSpace.option).toBe(false)
|
|
1730
|
-
|
|
1731
|
-
const shiftSpace = parseKeypress("\u001b[27;2;32~")!
|
|
1732
|
-
expect(shiftSpace.name).toBe("space")
|
|
1733
|
-
expect(shiftSpace.shift).toBe(true)
|
|
1734
|
-
expect(shiftSpace.ctrl).toBe(false)
|
|
1735
|
-
expect(shiftSpace.meta).toBe(false)
|
|
1736
|
-
expect(shiftSpace.option).toBe(false)
|
|
1737
|
-
|
|
1738
|
-
const altSpace = parseKeypress("\u001b[27;3;32~")!
|
|
1739
|
-
expect(altSpace.name).toBe("space")
|
|
1740
|
-
expect(altSpace.meta).toBe(true)
|
|
1741
|
-
expect(altSpace.option).toBe(true)
|
|
1742
|
-
expect(altSpace.ctrl).toBe(false)
|
|
1743
|
-
expect(altSpace.shift).toBe(false)
|
|
1744
|
-
|
|
1745
|
-
// Backspace key: charcode 127 (or 8)
|
|
1746
|
-
const ctrlBackspace = parseKeypress("\u001b[27;5;127~")!
|
|
1747
|
-
expect(ctrlBackspace.name).toBe("backspace")
|
|
1748
|
-
expect(ctrlBackspace.ctrl).toBe(true)
|
|
1749
|
-
expect(ctrlBackspace.shift).toBe(false)
|
|
1750
|
-
expect(ctrlBackspace.meta).toBe(false)
|
|
1751
|
-
expect(ctrlBackspace.option).toBe(false)
|
|
1752
|
-
|
|
1753
|
-
const shiftBackspace = parseKeypress("\u001b[27;2;127~")!
|
|
1754
|
-
expect(shiftBackspace.name).toBe("backspace")
|
|
1755
|
-
expect(shiftBackspace.shift).toBe(true)
|
|
1756
|
-
expect(shiftBackspace.ctrl).toBe(false)
|
|
1757
|
-
expect(shiftBackspace.meta).toBe(false)
|
|
1758
|
-
expect(shiftBackspace.option).toBe(false)
|
|
1759
|
-
|
|
1760
|
-
// Test charcode 8 variant
|
|
1761
|
-
const ctrlBackspace8 = parseKeypress("\u001b[27;5;8~")!
|
|
1762
|
-
expect(ctrlBackspace8.name).toBe("backspace")
|
|
1763
|
-
expect(ctrlBackspace8.ctrl).toBe(true)
|
|
1764
|
-
})
|
|
1765
|
-
|
|
1766
|
-
test("parseKeypress - meta+arrow keys with uppercase F and B (old style)", () => {
|
|
1767
|
-
// Some terminals send ESC followed by uppercase F/B for meta+arrow keys
|
|
1768
|
-
// ONLY uppercase F and B map to arrow keys (not P/N which require actual shift)
|
|
1769
|
-
// Lowercase f/b are just regular meta+letter combinations
|
|
1770
|
-
|
|
1771
|
-
// Meta+Right (uppercase F)
|
|
1772
|
-
const metaRight = parseKeypress("\u001BF")!
|
|
1773
|
-
expect(metaRight.name).toBe("right")
|
|
1774
|
-
expect(metaRight.meta).toBe(true)
|
|
1775
|
-
expect(metaRight.shift).toBe(false)
|
|
1776
|
-
expect(metaRight.ctrl).toBe(false)
|
|
1777
|
-
expect(metaRight.option).toBe(false)
|
|
1778
|
-
expect(metaRight.sequence).toBe("\u001BF")
|
|
1779
|
-
expect(metaRight.raw).toBe("\u001BF")
|
|
1780
|
-
|
|
1781
|
-
// Meta+Left (uppercase B)
|
|
1782
|
-
const metaLeft = parseKeypress("\u001BB")!
|
|
1783
|
-
expect(metaLeft.name).toBe("left")
|
|
1784
|
-
expect(metaLeft.meta).toBe(true)
|
|
1785
|
-
expect(metaLeft.shift).toBe(false)
|
|
1786
|
-
expect(metaLeft.ctrl).toBe(false)
|
|
1787
|
-
expect(metaLeft.option).toBe(false)
|
|
1788
|
-
expect(metaLeft.sequence).toBe("\u001BB")
|
|
1789
|
-
expect(metaLeft.raw).toBe("\u001BB")
|
|
1790
|
-
|
|
1791
|
-
// Uppercase P should be meta+shift+p (not arrow up)
|
|
1792
|
-
const metaShiftP = parseKeypress("\u001BP")!
|
|
1793
|
-
expect(metaShiftP.name).toBe("P")
|
|
1794
|
-
expect(metaShiftP.meta).toBe(true)
|
|
1795
|
-
expect(metaShiftP.shift).toBe(true)
|
|
1796
|
-
expect(metaShiftP.ctrl).toBe(false)
|
|
1797
|
-
expect(metaShiftP.option).toBe(false)
|
|
1798
|
-
|
|
1799
|
-
// Uppercase N should be meta+shift+n (not arrow down)
|
|
1800
|
-
const metaShiftN = parseKeypress("\u001BN")!
|
|
1801
|
-
expect(metaShiftN.name).toBe("N")
|
|
1802
|
-
expect(metaShiftN.meta).toBe(true)
|
|
1803
|
-
expect(metaShiftN.shift).toBe(true)
|
|
1804
|
-
expect(metaShiftN.ctrl).toBe(false)
|
|
1805
|
-
expect(metaShiftN.option).toBe(false)
|
|
1806
|
-
|
|
1807
|
-
// Lowercase versions should NOT map to arrow keys - they're just meta+letter
|
|
1808
|
-
// Meta+f (lowercase f) should be just meta+f, NOT meta+right
|
|
1809
|
-
const metaF = parseKeypress("\u001Bf")!
|
|
1810
|
-
expect(metaF.name).toBe("f")
|
|
1811
|
-
expect(metaF.meta).toBe(true)
|
|
1812
|
-
expect(metaF.shift).toBe(false)
|
|
1813
|
-
expect(metaF.ctrl).toBe(false)
|
|
1814
|
-
|
|
1815
|
-
// Meta+b (lowercase b) should be just meta+b, NOT meta+left
|
|
1816
|
-
const metaB = parseKeypress("\u001Bb")!
|
|
1817
|
-
expect(metaB.name).toBe("b")
|
|
1818
|
-
expect(metaB.meta).toBe(true)
|
|
1819
|
-
expect(metaB.shift).toBe(false)
|
|
1820
|
-
expect(metaB.ctrl).toBe(false)
|
|
1821
|
-
})
|
|
1822
|
-
|
|
1823
|
-
test("parseKeypress - double ESC preserves meta state when fn-key modifiers are parsed", () => {
|
|
1824
|
-
const metaUp = parseKeypress("\x1b\x1b[A")!
|
|
1825
|
-
expect(metaUp.name).toBe("up")
|
|
1826
|
-
expect(metaUp.meta).toBe(true)
|
|
1827
|
-
expect(metaUp.option).toBe(true)
|
|
1828
|
-
expect(metaUp.ctrl).toBe(false)
|
|
1829
|
-
expect(metaUp.shift).toBe(false)
|
|
1830
|
-
|
|
1831
|
-
const metaCtrlUp = parseKeypress("\x1b\x1b[1;5A")!
|
|
1832
|
-
expect(metaCtrlUp.name).toBe("up")
|
|
1833
|
-
expect(metaCtrlUp.meta).toBe(true)
|
|
1834
|
-
expect(metaCtrlUp.option).toBe(true)
|
|
1835
|
-
expect(metaCtrlUp.ctrl).toBe(true)
|
|
1836
|
-
expect(metaCtrlUp.shift).toBe(false)
|
|
1837
|
-
})
|
|
1838
|
-
|
|
1839
|
-
test("parseKeypress - preserves printable Unicode characters including non-BMP", () => {
|
|
1840
|
-
for (const char of ["é", "中", "👍"]) {
|
|
1841
|
-
const key = parseKeypress(char)!
|
|
1842
|
-
expect(key.name).toBe(char)
|
|
1843
|
-
expect(key.raw).toBe(char)
|
|
1844
|
-
expect(key.sequence).toBe(char)
|
|
1845
|
-
expect(key.meta).toBe(false)
|
|
1846
|
-
expect(key.ctrl).toBe(false)
|
|
1847
|
-
expect(key.shift).toBe(false)
|
|
1848
|
-
}
|
|
1849
|
-
})
|