@xincli/opentui-core 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (476) hide show
  1. package/README.md +63 -0
  2. package/dev/keypress-debug-renderer.ts +148 -0
  3. package/dev/keypress-debug.ts +43 -0
  4. package/dev/print-env-vars.ts +32 -0
  5. package/dev/test-tmux-graphics-334.sh +68 -0
  6. package/dev/test-tmux-theme-queries.sh +151 -0
  7. package/dev/thai-debug-test.ts +68 -0
  8. package/dev/theme-mode.ts +290 -0
  9. package/docs/development.md +144 -0
  10. package/package.json +102 -0
  11. package/prebuilt/aarch64-android/libopentui.so +0 -0
  12. package/scripts/build-android.ts +226 -0
  13. package/scripts/build-native-termux.sh +525 -0
  14. package/scripts/build.ts +530 -0
  15. package/scripts/dist-test.ts +312 -0
  16. package/scripts/package-prebuilt.ts +110 -0
  17. package/scripts/publish.ts +60 -0
  18. package/scripts/test-node-hook.mjs +16 -0
  19. package/scripts/test-node.ts +241 -0
  20. package/scripts/vendor-deps.sh +73 -0
  21. package/src/NativeSpanFeed.ts +375 -0
  22. package/src/Renderable.ts +1878 -0
  23. package/src/__snapshots__/buffer.test.ts.snap +28 -0
  24. package/src/animation/Timeline.test.ts +2709 -0
  25. package/src/animation/Timeline.ts +598 -0
  26. package/src/ansi.ts +22 -0
  27. package/src/audio.ts +479 -0
  28. package/src/benchmark/attenuation-benchmark.ts +81 -0
  29. package/src/benchmark/audio-playback-benchmark.ts +344 -0
  30. package/src/benchmark/box-draw-benchmark.ts +1042 -0
  31. package/src/benchmark/colormatrix-benchmark.ts +123 -0
  32. package/src/benchmark/gain-benchmark.ts +80 -0
  33. package/src/benchmark/latest-all-bench-run.json +707 -0
  34. package/src/benchmark/latest-async-bench-run.json +336 -0
  35. package/src/benchmark/latest-default-bench-run.json +657 -0
  36. package/src/benchmark/latest-large-bench-run.json +707 -0
  37. package/src/benchmark/latest-quick-bench-run.json +207 -0
  38. package/src/benchmark/layout-benchmark.ts +2547 -0
  39. package/src/benchmark/markdown-benchmark.ts +1796 -0
  40. package/src/benchmark/native-span-feed-async-benchmark.ts +355 -0
  41. package/src/benchmark/native-span-feed-benchmark.md +56 -0
  42. package/src/benchmark/native-span-feed-benchmark.ts +596 -0
  43. package/src/benchmark/native-span-feed-compare.ts +280 -0
  44. package/src/benchmark/render-traversal-benchmark.ts +928 -0
  45. package/src/benchmark/text-buffer-render-benchmark.ts +874 -0
  46. package/src/benchmark/text-table-benchmark.ts +948 -0
  47. package/src/buffer.test.ts +292 -0
  48. package/src/buffer.ts +564 -0
  49. package/src/console.test.ts +629 -0
  50. package/src/console.ts +1253 -0
  51. package/src/edit-buffer.test.ts +1852 -0
  52. package/src/edit-buffer.ts +417 -0
  53. package/src/editor-view.test.ts +1056 -0
  54. package/src/editor-view.ts +290 -0
  55. package/src/index.ts +25 -0
  56. package/src/lib/KeyHandler.integration.test.ts +292 -0
  57. package/src/lib/KeyHandler.stopPropagation.test.ts +289 -0
  58. package/src/lib/KeyHandler.test.ts +662 -0
  59. package/src/lib/KeyHandler.ts +222 -0
  60. package/src/lib/RGBA.test.ts +60 -0
  61. package/src/lib/RGBA.ts +343 -0
  62. package/src/lib/ascii.font.ts +330 -0
  63. package/src/lib/border.test.ts +83 -0
  64. package/src/lib/border.ts +170 -0
  65. package/src/lib/bunfs.test.ts +27 -0
  66. package/src/lib/bunfs.ts +18 -0
  67. package/src/lib/clipboard.test.ts +63 -0
  68. package/src/lib/clipboard.ts +41 -0
  69. package/src/lib/clock.ts +35 -0
  70. package/src/lib/data-paths.test.ts +133 -0
  71. package/src/lib/data-paths.ts +109 -0
  72. package/src/lib/debounce.ts +106 -0
  73. package/src/lib/detect-links.test.ts +98 -0
  74. package/src/lib/detect-links.ts +56 -0
  75. package/src/lib/env.test.ts +228 -0
  76. package/src/lib/env.ts +209 -0
  77. package/src/lib/extmarks-history.ts +51 -0
  78. package/src/lib/extmarks-multiwidth.test.ts +324 -0
  79. package/src/lib/extmarks.test.ts +3547 -0
  80. package/src/lib/extmarks.ts +861 -0
  81. package/src/lib/fonts/block.json +405 -0
  82. package/src/lib/fonts/grid.json +265 -0
  83. package/src/lib/fonts/huge.json +741 -0
  84. package/src/lib/fonts/pallet.json +314 -0
  85. package/src/lib/fonts/shade.json +591 -0
  86. package/src/lib/fonts/slick.json +321 -0
  87. package/src/lib/fonts/tiny.json +69 -0
  88. package/src/lib/hast-styled-text.ts +59 -0
  89. package/src/lib/index.ts +22 -0
  90. package/src/lib/keybinding.internal.test.ts +342 -0
  91. package/src/lib/keybinding.internal.ts +182 -0
  92. package/src/lib/objects-in-viewport.test.ts +789 -0
  93. package/src/lib/objects-in-viewport.ts +153 -0
  94. package/src/lib/output.capture.ts +58 -0
  95. package/src/lib/parse.keypress-kitty.protocol.test.ts +347 -0
  96. package/src/lib/parse.keypress-kitty.test.ts +712 -0
  97. package/src/lib/parse.keypress-kitty.ts +470 -0
  98. package/src/lib/parse.keypress.test.ts +2020 -0
  99. package/src/lib/parse.keypress.ts +480 -0
  100. package/src/lib/parse.mouse.test.ts +552 -0
  101. package/src/lib/parse.mouse.ts +232 -0
  102. package/src/lib/paste.ts +18 -0
  103. package/src/lib/queue.ts +65 -0
  104. package/src/lib/render-geometry.ts +36 -0
  105. package/src/lib/renderable.validations.test.ts +87 -0
  106. package/src/lib/renderable.validations.ts +83 -0
  107. package/src/lib/scroll-acceleration.ts +98 -0
  108. package/src/lib/selection.ts +261 -0
  109. package/src/lib/singleton.ts +34 -0
  110. package/src/lib/stdin-parser.test.ts +2439 -0
  111. package/src/lib/stdin-parser.ts +2007 -0
  112. package/src/lib/styled-text.ts +178 -0
  113. package/src/lib/terminal-capability-detection.test.ts +226 -0
  114. package/src/lib/terminal-capability-detection.ts +98 -0
  115. package/src/lib/terminal-palette.test.ts +954 -0
  116. package/src/lib/terminal-palette.ts +441 -0
  117. package/src/lib/tree-sitter/assets/README.md +119 -0
  118. package/src/lib/tree-sitter/assets/javascript/highlights.scm +205 -0
  119. package/src/lib/tree-sitter/assets/javascript/tree-sitter-javascript.wasm +0 -0
  120. package/src/lib/tree-sitter/assets/markdown/highlights.scm +150 -0
  121. package/src/lib/tree-sitter/assets/markdown/injections.scm +27 -0
  122. package/src/lib/tree-sitter/assets/markdown/tree-sitter-markdown.wasm +0 -0
  123. package/src/lib/tree-sitter/assets/markdown_inline/highlights.scm +115 -0
  124. package/src/lib/tree-sitter/assets/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
  125. package/src/lib/tree-sitter/assets/typescript/highlights.scm +604 -0
  126. package/src/lib/tree-sitter/assets/typescript/tree-sitter-typescript.wasm +0 -0
  127. package/src/lib/tree-sitter/assets/update.ts +360 -0
  128. package/src/lib/tree-sitter/assets/zig/highlights.scm +284 -0
  129. package/src/lib/tree-sitter/assets/zig/tree-sitter-zig.wasm +0 -0
  130. package/src/lib/tree-sitter/assets.d.ts +9 -0
  131. package/src/lib/tree-sitter/cache.test.ts +291 -0
  132. package/src/lib/tree-sitter/client.test.ts +1434 -0
  133. package/src/lib/tree-sitter/client.ts +847 -0
  134. package/src/lib/tree-sitter/default-parsers.ts +133 -0
  135. package/src/lib/tree-sitter/download-utils.ts +148 -0
  136. package/src/lib/tree-sitter/index.ts +75 -0
  137. package/src/lib/tree-sitter/parser.worker.ts +1098 -0
  138. package/src/lib/tree-sitter/parsers-config.ts +81 -0
  139. package/src/lib/tree-sitter/resolve-ft.test.ts +55 -0
  140. package/src/lib/tree-sitter/resolve-ft.ts +189 -0
  141. package/src/lib/tree-sitter/types.ts +132 -0
  142. package/src/lib/tree-sitter/update-assets.ts +10 -0
  143. package/src/lib/tree-sitter-styled-text.test.ts +1253 -0
  144. package/src/lib/tree-sitter-styled-text.ts +314 -0
  145. package/src/lib/validate-dir-name.ts +55 -0
  146. package/src/lib/yoga.options.test.ts +628 -0
  147. package/src/lib/yoga.options.ts +346 -0
  148. package/src/native-event-worker-repro.test.ts +48 -0
  149. package/src/native-event-worker-repro.worker.ts +9 -0
  150. package/src/native-handle.test.ts +221 -0
  151. package/src/platform/ffi.test.ts +516 -0
  152. package/src/platform/ffi.ts +653 -0
  153. package/src/platform/runtime.test.ts +52 -0
  154. package/src/platform/runtime.ts +121 -0
  155. package/src/platform/test.ts +45 -0
  156. package/src/platform/worker-handler-cleanup.fixture.ts +12 -0
  157. package/src/platform/worker-onmessage-startup.fixture.ts +10 -0
  158. package/src/platform/worker-startup.fixture.ts +9 -0
  159. package/src/platform/worker.node-test.ts +153 -0
  160. package/src/platform/worker.ts +425 -0
  161. package/src/plugins/core-slot.ts +579 -0
  162. package/src/plugins/registry.ts +402 -0
  163. package/src/plugins/types.ts +46 -0
  164. package/src/post/effects.ts +948 -0
  165. package/src/post/filters.ts +512 -0
  166. package/src/post/matrices.ts +288 -0
  167. package/src/renderables/ASCIIFont.ts +219 -0
  168. package/src/renderables/Box.test.ts +474 -0
  169. package/src/renderables/Box.ts +353 -0
  170. package/src/renderables/Code.test.ts +2388 -0
  171. package/src/renderables/Code.ts +555 -0
  172. package/src/renderables/Diff.regression.test.ts +381 -0
  173. package/src/renderables/Diff.test.ts +3212 -0
  174. package/src/renderables/Diff.ts +1278 -0
  175. package/src/renderables/EditBufferRenderable.test.ts +288 -0
  176. package/src/renderables/EditBufferRenderable.ts +1152 -0
  177. package/src/renderables/FrameBuffer.ts +47 -0
  178. package/src/renderables/Input.test.ts +1310 -0
  179. package/src/renderables/Input.ts +273 -0
  180. package/src/renderables/LineNumberRenderable.ts +725 -0
  181. package/src/renderables/Markdown.ts +2144 -0
  182. package/src/renderables/ScrollBar.ts +423 -0
  183. package/src/renderables/ScrollBox.ts +903 -0
  184. package/src/renderables/Select.test.ts +1134 -0
  185. package/src/renderables/Select.ts +535 -0
  186. package/src/renderables/Slider.test.ts +456 -0
  187. package/src/renderables/Slider.ts +342 -0
  188. package/src/renderables/TabSelect.test.ts +238 -0
  189. package/src/renderables/TabSelect.ts +447 -0
  190. package/src/renderables/Text.selection-buffer.test.ts +118 -0
  191. package/src/renderables/Text.test.ts +2662 -0
  192. package/src/renderables/Text.ts +147 -0
  193. package/src/renderables/TextBufferRenderable.ts +504 -0
  194. package/src/renderables/TextNode.test.ts +1137 -0
  195. package/src/renderables/TextNode.ts +379 -0
  196. package/src/renderables/TextTable.test.ts +1411 -0
  197. package/src/renderables/TextTable.ts +1434 -0
  198. package/src/renderables/Textarea.ts +425 -0
  199. package/src/renderables/TimeToFirstDraw.ts +89 -0
  200. package/src/renderables/__snapshots__/Code.test.ts.snap +13 -0
  201. package/src/renderables/__snapshots__/Diff.test.ts.snap +785 -0
  202. package/src/renderables/__snapshots__/Text.test.ts.snap +421 -0
  203. package/src/renderables/__snapshots__/TextTable.test.ts.snap +215 -0
  204. package/src/renderables/__tests__/Code.test.ts +55 -0
  205. package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +144 -0
  206. package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +827 -0
  207. package/src/renderables/__tests__/LineNumberRenderable.test.ts +1906 -0
  208. package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +85 -0
  209. package/src/renderables/__tests__/Markdown.code-colors.test.ts +288 -0
  210. package/src/renderables/__tests__/Markdown.test.ts +4175 -0
  211. package/src/renderables/__tests__/MultiRenderable.selection.test.ts +87 -0
  212. package/src/renderables/__tests__/Textarea.buffer.test.ts +683 -0
  213. package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +675 -0
  214. package/src/renderables/__tests__/Textarea.editing.test.ts +2120 -0
  215. package/src/renderables/__tests__/Textarea.error-handling.test.ts +35 -0
  216. package/src/renderables/__tests__/Textarea.events.test.ts +738 -0
  217. package/src/renderables/__tests__/Textarea.highlights.test.ts +590 -0
  218. package/src/renderables/__tests__/Textarea.keybinding.test.ts +3275 -0
  219. package/src/renderables/__tests__/Textarea.paste.test.ts +357 -0
  220. package/src/renderables/__tests__/Textarea.rendering.test.ts +1927 -0
  221. package/src/renderables/__tests__/Textarea.scroll.test.ts +733 -0
  222. package/src/renderables/__tests__/Textarea.selection.test.ts +1589 -0
  223. package/src/renderables/__tests__/Textarea.stress.test.ts +670 -0
  224. package/src/renderables/__tests__/Textarea.undo-redo.test.ts +383 -0
  225. package/src/renderables/__tests__/Textarea.visual-lines.test.ts +310 -0
  226. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +221 -0
  227. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +89 -0
  228. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +457 -0
  229. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +158 -0
  230. package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +387 -0
  231. package/src/renderables/__tests__/markdown-parser.test.ts +241 -0
  232. package/src/renderables/__tests__/renderable-test-utils.ts +60 -0
  233. package/src/renderables/composition/README.md +8 -0
  234. package/src/renderables/composition/VRenderable.ts +32 -0
  235. package/src/renderables/composition/constructs.ts +127 -0
  236. package/src/renderables/composition/vnode.ts +277 -0
  237. package/src/renderables/index.ts +23 -0
  238. package/src/renderables/markdown-parser.ts +79 -0
  239. package/src/renderer-theme-mode.ts +199 -0
  240. package/src/renderer.ts +4975 -0
  241. package/src/runtime-plugin-support-configure.ts +63 -0
  242. package/src/runtime-plugin-support.ts +13 -0
  243. package/src/runtime-plugin.ts +626 -0
  244. package/src/specs/terminal-startup.md +64 -0
  245. package/src/syntax-style.test.ts +841 -0
  246. package/src/syntax-style.ts +274 -0
  247. package/src/testing/README.md +219 -0
  248. package/src/testing/bun-test-node.ts +880 -0
  249. package/src/testing/capture-spans.test.ts +216 -0
  250. package/src/testing/integration.test.ts +276 -0
  251. package/src/testing/manual-clock.ts +117 -0
  252. package/src/testing/mock-keys.test.ts +1378 -0
  253. package/src/testing/mock-keys.ts +457 -0
  254. package/src/testing/mock-mouse.test.ts +218 -0
  255. package/src/testing/mock-mouse.ts +247 -0
  256. package/src/testing/mock-tree-sitter-client.ts +81 -0
  257. package/src/testing/spy.ts +13 -0
  258. package/src/testing/terminal-capabilities.ts +47 -0
  259. package/src/testing/test-recorder.test.ts +410 -0
  260. package/src/testing/test-recorder.ts +133 -0
  261. package/src/testing/test-renderer.ts +375 -0
  262. package/src/testing/test-renderer.wait.test.ts +278 -0
  263. package/src/testing/test-streams.ts +31 -0
  264. package/src/testing.ts +9 -0
  265. package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +481 -0
  266. package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +19 -0
  267. package/src/tests/__snapshots__/scrollbox.test.ts.snap +29 -0
  268. package/src/tests/absolute-positioning.snapshot.test.ts +638 -0
  269. package/src/tests/allocator-stats.test.ts +53 -0
  270. package/src/tests/audio.test.ts +303 -0
  271. package/src/tests/destroy-during-render.test.ts +200 -0
  272. package/src/tests/destroy-on-exit.fixture.ts +36 -0
  273. package/src/tests/destroy-on-exit.test.ts +62 -0
  274. package/src/tests/ffi-borrowed-pointer-callsites.test.ts +153 -0
  275. package/src/tests/hover-cursor.test.ts +98 -0
  276. package/src/tests/native-backed-measurement-lifecycle.test.ts +133 -0
  277. package/src/tests/native-backed-measurement-parity.test.ts +322 -0
  278. package/src/tests/native-span-feed-async.test.ts +173 -0
  279. package/src/tests/native-span-feed-close.test.ts +120 -0
  280. package/src/tests/native-span-feed-coverage.test.ts +227 -0
  281. package/src/tests/native-span-feed-edge-cases.test.ts +384 -0
  282. package/src/tests/native-span-feed-use-after-free.test.ts +45 -0
  283. package/src/tests/opacity.test.ts +123 -0
  284. package/src/tests/renderable.snapshot.test.ts +524 -0
  285. package/src/tests/renderable.test.ts +1851 -0
  286. package/src/tests/renderer.clock.test.ts +270 -0
  287. package/src/tests/renderer.console-startup.test.ts +2151 -0
  288. package/src/tests/renderer.control.test.ts +458 -0
  289. package/src/tests/renderer.core-slot-binding.test.ts +962 -0
  290. package/src/tests/renderer.cursor.test.ts +26 -0
  291. package/src/tests/renderer.custom-stdout.test.ts +1112 -0
  292. package/src/tests/renderer.destroy-during-render.test.ts +147 -0
  293. package/src/tests/renderer.focus-restore.test.ts +257 -0
  294. package/src/tests/renderer.focus.test.ts +329 -0
  295. package/src/tests/renderer.idle.test.ts +219 -0
  296. package/src/tests/renderer.input.test.ts +2615 -0
  297. package/src/tests/renderer.kitty-flags.test.ts +195 -0
  298. package/src/tests/renderer.lifecycle.test.ts +34 -0
  299. package/src/tests/renderer.mouse.test.ts +1315 -0
  300. package/src/tests/renderer.notifications.test.ts +27 -0
  301. package/src/tests/renderer.palette.test.ts +1199 -0
  302. package/src/tests/renderer.remote.test.ts +247 -0
  303. package/src/tests/renderer.render-stats.test.ts +146 -0
  304. package/src/tests/renderer.scrollback-surface.test.ts +474 -0
  305. package/src/tests/renderer.selection.test.ts +138 -0
  306. package/src/tests/renderer.slot-registry.test.ts +684 -0
  307. package/src/tests/renderer.tracker.test.ts +202 -0
  308. package/src/tests/renderer.useMouse.test.ts +83 -0
  309. package/src/tests/runtime-plugin-node-modules-cycle.fixture.ts +76 -0
  310. package/src/tests/runtime-plugin-node-modules-import-like-string.fixture.ts +48 -0
  311. package/src/tests/runtime-plugin-node-modules-mjs.fixture.ts +43 -0
  312. package/src/tests/runtime-plugin-node-modules-no-bare-rewrite.fixture.ts +67 -0
  313. package/src/tests/runtime-plugin-node-modules-package-type-cache.fixture.ts +72 -0
  314. package/src/tests/runtime-plugin-node-modules-runtime-specifier.fixture.ts +44 -0
  315. package/src/tests/runtime-plugin-node-modules-scoped-package-bare-rewrite.fixture.ts +85 -0
  316. package/src/tests/runtime-plugin-path-alias.fixture.ts +43 -0
  317. package/src/tests/runtime-plugin-resolve-roots.fixture.ts +65 -0
  318. package/src/tests/runtime-plugin-support-configure.fixture.ts +31 -0
  319. package/src/tests/runtime-plugin-support-late-addition.fixture.ts +17 -0
  320. package/src/tests/runtime-plugin-support.fixture.ts +11 -0
  321. package/src/tests/runtime-plugin-support.test.ts +54 -0
  322. package/src/tests/runtime-plugin-windows-file-url.fixture.ts +30 -0
  323. package/src/tests/runtime-plugin.fixture.ts +40 -0
  324. package/src/tests/runtime-plugin.test.ts +369 -0
  325. package/src/tests/scrollbox-culling-bug.test.ts +114 -0
  326. package/src/tests/scrollbox-hitgrid-resize.test.ts +136 -0
  327. package/src/tests/scrollbox-hitgrid.test.ts +909 -0
  328. package/src/tests/scrollbox.test.ts +1895 -0
  329. package/src/tests/wrap-resize-perf.test.ts +276 -0
  330. package/src/tests/yoga-api-coverage.test.ts +409 -0
  331. package/src/tests/yoga-callback-stress.test.ts +122 -0
  332. package/src/tests/yoga-ffi.test.ts +117 -0
  333. package/src/tests/yoga-setters.test.ts +921 -0
  334. package/src/tests/yoga-upstream/YGAlignBaselineTest.test.ts +124 -0
  335. package/src/tests/yoga-upstream/YGComputedBorderTest.test.ts +27 -0
  336. package/src/tests/yoga-upstream/YGComputedMarginTest.test.ts +27 -0
  337. package/src/tests/yoga-upstream/YGComputedPaddingTest.test.ts +27 -0
  338. package/src/tests/yoga-upstream/YGDirtiedTest.test.ts +157 -0
  339. package/src/tests/yoga-upstream/YGErrataTest.test.ts +40 -0
  340. package/src/tests/yoga-upstream/YGFlexBasisAuto.test.ts +23 -0
  341. package/src/tests/yoga-upstream/YGHasNewLayout.test.ts +81 -0
  342. package/src/tests/yoga-upstream/YGMeasureCacheTest.test.ts +31 -0
  343. package/src/tests/yoga-upstream/YGMeasureTest.test.ts +70 -0
  344. package/src/tests/yoga-upstream/generated/YGAbsolutePositionTest.test.ts +1864 -0
  345. package/src/tests/yoga-upstream/generated/YGAlignContentTest.test.ts +5829 -0
  346. package/src/tests/yoga-upstream/generated/YGAlignItemsTest.test.ts +2444 -0
  347. package/src/tests/yoga-upstream/generated/YGAlignSelfTest.test.ts +293 -0
  348. package/src/tests/yoga-upstream/generated/YGAndroidNewsFeed.test.ts +315 -0
  349. package/src/tests/yoga-upstream/generated/YGAspectRatioTest.test.ts +231 -0
  350. package/src/tests/yoga-upstream/generated/YGAutoTest.test.ts +347 -0
  351. package/src/tests/yoga-upstream/generated/YGBorderTest.test.ts +256 -0
  352. package/src/tests/yoga-upstream/generated/YGBoxSizingTest.test.ts +2747 -0
  353. package/src/tests/yoga-upstream/generated/YGDimensionTest.test.ts +129 -0
  354. package/src/tests/yoga-upstream/generated/YGDisplayContentsTest.test.ts +109 -0
  355. package/src/tests/yoga-upstream/generated/YGDisplayTest.test.ts +1097 -0
  356. package/src/tests/yoga-upstream/generated/YGFlexDirectionTest.test.ts +4562 -0
  357. package/src/tests/yoga-upstream/generated/YGFlexTest.test.ts +677 -0
  358. package/src/tests/yoga-upstream/generated/YGFlexWrapTest.test.ts +2071 -0
  359. package/src/tests/yoga-upstream/generated/YGGapTest.test.ts +3303 -0
  360. package/src/tests/yoga-upstream/generated/YGIntrinsicSizeTest.test.ts +582 -0
  361. package/src/tests/yoga-upstream/generated/YGJustifyContentTest.test.ts +2150 -0
  362. package/src/tests/yoga-upstream/generated/YGMarginTest.test.ts +1963 -0
  363. package/src/tests/yoga-upstream/generated/YGMinMaxDimensionTest.test.ts +1446 -0
  364. package/src/tests/yoga-upstream/generated/YGPaddingTest.test.ts +354 -0
  365. package/src/tests/yoga-upstream/generated/YGPercentageTest.test.ts +1760 -0
  366. package/src/tests/yoga-upstream/generated/YGRoundingTest.test.ts +1161 -0
  367. package/src/tests/yoga-upstream/generated/YGSizeOverflowTest.test.ts +207 -0
  368. package/src/tests/yoga-upstream/generated/YGStaticPositionTest.test.ts +6338 -0
  369. package/src/tests/yoga-upstream/tools/MeasureCounter.ts +54 -0
  370. package/src/tests/yoga-upstream/tools/utils.ts +81 -0
  371. package/src/text-buffer-view.test.ts +722 -0
  372. package/src/text-buffer-view.ts +193 -0
  373. package/src/text-buffer.test.ts +512 -0
  374. package/src/text-buffer.ts +250 -0
  375. package/src/types.ts +199 -0
  376. package/src/utils.ts +88 -0
  377. package/src/yoga.ts +1146 -0
  378. package/src/zig/ansi.zig +502 -0
  379. package/src/zig/audio.zig +1206 -0
  380. package/src/zig/bench/README.md +50 -0
  381. package/src/zig/bench/buffer-color-blending_bench.zig +350 -0
  382. package/src/zig/bench/buffer-draw-box_bench.zig +786 -0
  383. package/src/zig/bench/buffer-draw-text-buffer_bench.zig +894 -0
  384. package/src/zig/bench/edit-buffer_bench.zig +476 -0
  385. package/src/zig/bench/editor-view_bench.zig +169 -0
  386. package/src/zig/bench/native-span-feed_bench.zig +107 -0
  387. package/src/zig/bench/rope-markers_bench.zig +713 -0
  388. package/src/zig/bench/rope_bench.zig +514 -0
  389. package/src/zig/bench/styled-text_bench.zig +476 -0
  390. package/src/zig/bench/text-buffer-coords_bench.zig +362 -0
  391. package/src/zig/bench/text-buffer-view_bench.zig +459 -0
  392. package/src/zig/bench/text-chunk-graphemes_bench.zig +272 -0
  393. package/src/zig/bench/utf8_bench.zig +799 -0
  394. package/src/zig/bench-utils.zig +431 -0
  395. package/src/zig/bench.zig +223 -0
  396. package/src/zig/buffer-methods.zig +215 -0
  397. package/src/zig/buffer.zig +2515 -0
  398. package/src/zig/build.zig +700 -0
  399. package/src/zig/build.zig.zon +19 -0
  400. package/src/zig/edit-buffer.zig +900 -0
  401. package/src/zig/editor-view.zig +812 -0
  402. package/src/zig/event-bus.zig +28 -0
  403. package/src/zig/event-emitter.zig +65 -0
  404. package/src/zig/file-logger.zig +92 -0
  405. package/src/zig/grapheme.zig +838 -0
  406. package/src/zig/handles.zig +267 -0
  407. package/src/zig/lib.zig +2813 -0
  408. package/src/zig/link.zig +349 -0
  409. package/src/zig/logger.zig +43 -0
  410. package/src/zig/mem-registry.zig +126 -0
  411. package/src/zig/miniaudio_shim.c +21 -0
  412. package/src/zig/native-renderable.zig +112 -0
  413. package/src/zig/native-span-feed-bench-lib.zig +7 -0
  414. package/src/zig/native-span-feed.zig +747 -0
  415. package/src/zig/renderer-output.zig +677 -0
  416. package/src/zig/renderer.zig +2106 -0
  417. package/src/zig/rope.zig +1220 -0
  418. package/src/zig/split-scrollback.zig +88 -0
  419. package/src/zig/syntax-style.zig +174 -0
  420. package/src/zig/terminal.zig +1652 -0
  421. package/src/zig/test.zig +82 -0
  422. package/src/zig/tests/README.md +18 -0
  423. package/src/zig/tests/ansi_test.zig +18 -0
  424. package/src/zig/tests/audio_test.zig +439 -0
  425. package/src/zig/tests/buffer-methods_test.zig +1111 -0
  426. package/src/zig/tests/buffer_test.zig +2926 -0
  427. package/src/zig/tests/edit-buffer-history_test.zig +299 -0
  428. package/src/zig/tests/edit-buffer_test.zig +1688 -0
  429. package/src/zig/tests/editor-view_test.zig +3454 -0
  430. package/src/zig/tests/event-emitter_test.zig +249 -0
  431. package/src/zig/tests/grapheme_test.zig +1329 -0
  432. package/src/zig/tests/handles_test.zig +123 -0
  433. package/src/zig/tests/link_test.zig +220 -0
  434. package/src/zig/tests/mem-registry_test.zig +473 -0
  435. package/src/zig/tests/memory_leak_regression_test.zig +234 -0
  436. package/src/zig/tests/native-renderable_test.zig +16 -0
  437. package/src/zig/tests/native-span-feed_test.zig +1266 -0
  438. package/src/zig/tests/renderer_test.zig +2604 -0
  439. package/src/zig/tests/rope-nested_test.zig +712 -0
  440. package/src/zig/tests/rope_fuzz_test.zig +238 -0
  441. package/src/zig/tests/rope_test.zig +2362 -0
  442. package/src/zig/tests/segment-merge.test.zig +148 -0
  443. package/src/zig/tests/split-scrollback_test.zig +70 -0
  444. package/src/zig/tests/syntax-style_test.zig +558 -0
  445. package/src/zig/tests/terminal_test.zig +1489 -0
  446. package/src/zig/tests/test-renderer.zig +126 -0
  447. package/src/zig/tests/text-buffer-drawing_test.zig +3291 -0
  448. package/src/zig/tests/text-buffer-highlights_test.zig +706 -0
  449. package/src/zig/tests/text-buffer-iterators_test.zig +776 -0
  450. package/src/zig/tests/text-buffer-segment_test.zig +320 -0
  451. package/src/zig/tests/text-buffer-selection_test.zig +1056 -0
  452. package/src/zig/tests/text-buffer-selection_viewport_test.zig +358 -0
  453. package/src/zig/tests/text-buffer-view_test.zig +3672 -0
  454. package/src/zig/tests/text-buffer_test.zig +2191 -0
  455. package/src/zig/tests/unicode-width-map.zon +3909 -0
  456. package/src/zig/tests/utf8_no_zwj_test.zig +260 -0
  457. package/src/zig/tests/utf8_test.zig +4060 -0
  458. package/src/zig/tests/utf8_wcwidth_cursor_test.zig +267 -0
  459. package/src/zig/tests/utf8_wcwidth_test.zig +357 -0
  460. package/src/zig/tests/word-wrap-editing_test.zig +498 -0
  461. package/src/zig/tests/wrap-cache-perf_test.zig +113 -0
  462. package/src/zig/tests/yoga_test.zig +65 -0
  463. package/src/zig/text-buffer-iterators.zig +496 -0
  464. package/src/zig/text-buffer-segment.zig +403 -0
  465. package/src/zig/text-buffer-view.zig +1428 -0
  466. package/src/zig/text-buffer.zig +1272 -0
  467. package/src/zig/utf8.zig +1954 -0
  468. package/src/zig/utils.zig +12 -0
  469. package/src/zig/uucode-stub.zig +76 -0
  470. package/src/zig/vendor/miniaudio/miniaudio.h +93468 -0
  471. package/src/zig/yoga.zig +658 -0
  472. package/src/zig-structs.ts +381 -0
  473. package/src/zig.ts +5260 -0
  474. package/tsconfig.build.json +17 -0
  475. package/tsconfig.json +27 -0
  476. package/tsconfig.node-test.json +138 -0
@@ -0,0 +1,342 @@
1
+ import { describe, expect, it } from "bun:test"
2
+ import {
3
+ mergeKeyBindings,
4
+ getKeyBindingKey,
5
+ getKeyBindingAction,
6
+ buildKeyBindingsMap,
7
+ mergeKeyAliases,
8
+ defaultKeyAliases,
9
+ keyBindingToString,
10
+ matchesKeyBinding,
11
+ type KeyAliasMap,
12
+ } from "./keybinding.internal.js"
13
+
14
+ describe("keybinding.internal", () => {
15
+ describe("getKeyBindingKey", () => {
16
+ it("should generate key with meta modifier", () => {
17
+ const metaBinding = { name: "a", meta: true, action: "test" }
18
+ const key = getKeyBindingKey(metaBinding)
19
+ expect(key).toBe("a:0:0:1:0")
20
+ })
21
+
22
+ it("should generate different keys for different modifiers", () => {
23
+ const noMod = getKeyBindingKey({ name: "a" })
24
+ const withMeta = getKeyBindingKey({ name: "a", meta: true })
25
+ const withCtrl = getKeyBindingKey({ name: "a", ctrl: true })
26
+ const withShift = getKeyBindingKey({ name: "a", shift: true })
27
+
28
+ expect(noMod).not.toBe(withMeta)
29
+ expect(noMod).not.toBe(withCtrl)
30
+ expect(noMod).not.toBe(withShift)
31
+ expect(withMeta).not.toBe(withCtrl)
32
+ })
33
+
34
+ it("should handle combined modifiers", () => {
35
+ const key = getKeyBindingKey({ name: "a", ctrl: true, shift: true, meta: true })
36
+ expect(key).toBe("a:1:1:1:0")
37
+ })
38
+
39
+ it("should generate key with super modifier", () => {
40
+ const superBinding = { name: "z", super: true, action: "test" }
41
+ const key = getKeyBindingKey(superBinding)
42
+ expect(key).toBe("z:0:0:0:1")
43
+ })
44
+ })
45
+
46
+ describe("getKeyBindingAction", () => {
47
+ it("should fall back to baseCode when the parsed name differs", () => {
48
+ const map = buildKeyBindingsMap([{ name: "c", ctrl: true, action: "copy" as const }])
49
+
50
+ const action = getKeyBindingAction(map, { name: "ㅊ", baseCode: 99, ctrl: true })
51
+
52
+ expect(action).toBe("copy")
53
+ })
54
+
55
+ it("should prefer a direct name match over the baseCode fallback", () => {
56
+ const map = buildKeyBindingsMap([
57
+ { name: "c", ctrl: true, action: "copy" as const },
58
+ { name: "ㅊ", ctrl: true, action: "insert" as const },
59
+ ])
60
+
61
+ const action = getKeyBindingAction(map, { name: "ㅊ", baseCode: 99, ctrl: true })
62
+
63
+ expect(action).toBe("insert")
64
+ })
65
+ })
66
+
67
+ describe("matchesKeyBinding", () => {
68
+ it("should match a binding by baseCode when available", () => {
69
+ expect(matchesKeyBinding({ name: "ㅊ", baseCode: 99, ctrl: true }, { name: "c", ctrl: true })).toBe(true)
70
+ })
71
+ })
72
+
73
+ describe("mergeKeyBindings", () => {
74
+ it("should merge defaults and custom bindings", () => {
75
+ const defaults = [
76
+ { name: "a", action: "action1" as const },
77
+ { name: "b", action: "action2" as const },
78
+ ]
79
+ const custom = [{ name: "c", action: "action3" as const }]
80
+
81
+ const merged = mergeKeyBindings(defaults, custom)
82
+ expect(merged.length).toBe(3)
83
+ })
84
+
85
+ it("should allow custom to override defaults", () => {
86
+ const defaults = [{ name: "a", action: "action1" as const }]
87
+ const custom = [{ name: "a", action: "action2" as const }]
88
+
89
+ const merged = mergeKeyBindings(defaults, custom)
90
+ expect(merged.length).toBe(1)
91
+ expect(merged[0]!.action).toBe("action2")
92
+ })
93
+
94
+ it("should override when meta matches", () => {
95
+ const defaults = [{ name: "a", meta: true, action: "action1" as const }]
96
+ const custom = [{ name: "a", meta: true, action: "action2" as const }]
97
+
98
+ const merged = mergeKeyBindings(defaults, custom)
99
+ expect(merged.length).toBe(1)
100
+ expect(merged[0]!.action).toBe("action2")
101
+ })
102
+ })
103
+
104
+ describe("buildKeyBindingsMap", () => {
105
+ it("should build map from bindings", () => {
106
+ const bindings = [
107
+ { name: "a", action: "action1" as const },
108
+ { name: "b", meta: true, action: "action2" as const },
109
+ ]
110
+
111
+ const map = buildKeyBindingsMap(bindings)
112
+ expect(map.size).toBe(2)
113
+ expect(map.get("a:0:0:0:0")).toBe("action1")
114
+ expect(map.get("b:0:0:1:0")).toBe("action2")
115
+ })
116
+
117
+ it("should handle meta modifier correctly", () => {
118
+ const bindings = [{ name: "a", meta: true, action: "action1" as const }]
119
+
120
+ const map = buildKeyBindingsMap(bindings)
121
+ expect(map.get("a:0:0:1:0")).toBe("action1")
122
+ })
123
+
124
+ it("should handle aliases and normalize key names", () => {
125
+ const bindings = [{ name: "return", action: "submit" as const }]
126
+ const aliases: KeyAliasMap = { enter: "return" }
127
+
128
+ const map = buildKeyBindingsMap(bindings, aliases)
129
+
130
+ expect(map.get("return:0:0:0:0")).toBe("submit")
131
+ expect(map.get("enter:0:0:0:0")).toBeUndefined()
132
+ })
133
+
134
+ it("should create aliased mappings for aliased key names", () => {
135
+ const bindings = [{ name: "enter", action: "submit" as const }]
136
+ const aliases: KeyAliasMap = { enter: "return" }
137
+
138
+ const map = buildKeyBindingsMap(bindings, aliases)
139
+
140
+ expect(map.get("enter:0:0:0:0")).toBe("submit")
141
+ expect(map.get("return:0:0:0:0")).toBe("submit")
142
+ })
143
+
144
+ it("should handle multiple aliases", () => {
145
+ const bindings = [
146
+ { name: "enter", action: "submit" as const },
147
+ { name: "esc", action: "cancel" as const },
148
+ ]
149
+ const aliases: KeyAliasMap = { enter: "return", esc: "escape" }
150
+
151
+ const map = buildKeyBindingsMap(bindings, aliases)
152
+
153
+ expect(map.get("enter:0:0:0:0")).toBe("submit")
154
+ expect(map.get("return:0:0:0:0")).toBe("submit")
155
+ expect(map.get("esc:0:0:0:0")).toBe("cancel")
156
+ expect(map.get("escape:0:0:0:0")).toBe("cancel")
157
+ })
158
+
159
+ it("should handle aliases with modifiers", () => {
160
+ const bindings = [{ name: "enter", meta: true, action: "special-submit" as const }]
161
+ const aliases: KeyAliasMap = { enter: "return" }
162
+
163
+ const map = buildKeyBindingsMap(bindings, aliases)
164
+
165
+ expect(map.get("enter:0:0:1:0")).toBe("special-submit")
166
+ expect(map.get("return:0:0:1:0")).toBe("special-submit")
167
+ })
168
+ })
169
+
170
+ describe("mergeKeyAliases", () => {
171
+ it("should merge default and custom aliases", () => {
172
+ const defaults: KeyAliasMap = { enter: "return" }
173
+ const custom: KeyAliasMap = { esc: "escape" }
174
+
175
+ const merged = mergeKeyAliases(defaults, custom)
176
+
177
+ expect(merged.enter).toBe("return")
178
+ expect(merged.esc).toBe("escape")
179
+ })
180
+
181
+ it("should allow custom aliases to override defaults", () => {
182
+ const defaults: KeyAliasMap = { enter: "return" }
183
+ const custom: KeyAliasMap = { enter: "custom-return" }
184
+
185
+ const merged = mergeKeyAliases(defaults, custom)
186
+
187
+ expect(merged.enter).toBe("custom-return")
188
+ })
189
+
190
+ it("should preserve defaults when no custom aliases provided", () => {
191
+ const defaults: KeyAliasMap = { enter: "return", esc: "escape" }
192
+ const custom: KeyAliasMap = {}
193
+
194
+ const merged = mergeKeyAliases(defaults, custom)
195
+
196
+ expect(merged.enter).toBe("return")
197
+ expect(merged.esc).toBe("escape")
198
+ })
199
+ })
200
+
201
+ describe("defaultKeyAliases", () => {
202
+ it("should have enter -> return alias", () => {
203
+ expect(defaultKeyAliases.enter).toBe("return")
204
+ })
205
+
206
+ it("should have esc -> escape alias", () => {
207
+ expect(defaultKeyAliases.esc).toBe("escape")
208
+ })
209
+
210
+ it("should have keypad aliases to normalized key names", () => {
211
+ const keypadAliases: KeyAliasMap = {
212
+ kp0: "0",
213
+ kp1: "1",
214
+ kp2: "2",
215
+ kp3: "3",
216
+ kp4: "4",
217
+ kp5: "5",
218
+ kp6: "6",
219
+ kp7: "7",
220
+ kp8: "8",
221
+ kp9: "9",
222
+ kpdecimal: ".",
223
+ kpdivide: "/",
224
+ kpmultiply: "*",
225
+ kpminus: "-",
226
+ kpplus: "+",
227
+ kpenter: "enter",
228
+ kpequal: "=",
229
+ kpseparator: ",",
230
+ kpleft: "left",
231
+ kpright: "right",
232
+ kpup: "up",
233
+ kpdown: "down",
234
+ kppageup: "pageup",
235
+ kppagedown: "pagedown",
236
+ kphome: "home",
237
+ kpend: "end",
238
+ kpinsert: "insert",
239
+ kpdelete: "delete",
240
+ }
241
+
242
+ for (const [key, value] of Object.entries(keypadAliases)) {
243
+ expect(defaultKeyAliases[key]).toBe(value)
244
+ }
245
+ })
246
+ })
247
+
248
+ describe("alias override behavior", () => {
249
+ it("should override 'return' binding when custom provides 'enter' binding with aliases", () => {
250
+ const defaults = [{ name: "return", action: "newline" as const }]
251
+ const custom = [{ name: "enter", action: "submit" as const }]
252
+ const aliases: KeyAliasMap = { enter: "return" }
253
+
254
+ const merged = mergeKeyBindings(defaults, custom)
255
+ const map = buildKeyBindingsMap(merged, aliases)
256
+
257
+ const returnAction = map.get("return:0:0:0:0")
258
+ const enterAction = map.get("enter:0:0:0:0")
259
+
260
+ expect(returnAction).toBe("submit")
261
+ expect(enterAction).toBe("submit")
262
+ })
263
+
264
+ it("should also allow direct override using canonical name", () => {
265
+ const defaults = [{ name: "return", action: "newline" as const }]
266
+ const custom = [{ name: "return", action: "submit" as const }]
267
+ const aliases: KeyAliasMap = { enter: "return" }
268
+
269
+ const merged = mergeKeyBindings(defaults, custom)
270
+ const map = buildKeyBindingsMap(merged, aliases)
271
+
272
+ const returnAction = map.get("return:0:0:0:0")
273
+ const enterAction = map.get("enter:0:0:0:0")
274
+
275
+ expect(returnAction).toBe("submit")
276
+ expect(enterAction).toBeUndefined()
277
+ })
278
+
279
+ it("should handle the Textarea scenario: defaults with 'return', custom with 'enter'", () => {
280
+ const defaults = [
281
+ { name: "return", action: "newline" as const },
282
+ { name: "return", meta: true, action: "submit" as const },
283
+ ]
284
+ const custom = [{ name: "enter", action: "custom-submit" as const }]
285
+ const aliases: KeyAliasMap = { enter: "return" }
286
+
287
+ const merged = mergeKeyBindings(defaults, custom)
288
+ const map = buildKeyBindingsMap(merged, aliases)
289
+
290
+ const returnNoMod = map.get("return:0:0:0:0")
291
+ const returnWithMeta = map.get("return:0:0:1:0")
292
+ const enterNoMod = map.get("enter:0:0:0:0")
293
+
294
+ expect(returnNoMod).toBe("custom-submit")
295
+ expect(enterNoMod).toBe("custom-submit")
296
+ expect(returnWithMeta).toBe("submit")
297
+ })
298
+ })
299
+
300
+ describe("keyBindingToString", () => {
301
+ it("should convert simple key binding without modifiers", () => {
302
+ const binding = { name: "escape", action: "cancel" as const }
303
+ expect(keyBindingToString(binding)).toBe("escape")
304
+ })
305
+
306
+ it("should convert key binding with ctrl modifier", () => {
307
+ const binding = { name: "c", ctrl: true, action: "copy" as const }
308
+ expect(keyBindingToString(binding)).toBe("ctrl+c")
309
+ })
310
+
311
+ it("should convert key binding with shift modifier", () => {
312
+ const binding = { name: "up", shift: true, action: "scroll-fast" as const }
313
+ expect(keyBindingToString(binding)).toBe("shift+up")
314
+ })
315
+
316
+ it("should convert key binding with multiple modifiers", () => {
317
+ const binding = { name: "y", ctrl: true, shift: true, action: "copy" as const }
318
+ expect(keyBindingToString(binding)).toBe("ctrl+shift+y")
319
+ })
320
+
321
+ it("should convert key binding with all modifiers", () => {
322
+ const binding = { name: "a", ctrl: true, shift: true, meta: true, super: true, action: "all" as const }
323
+ expect(keyBindingToString(binding)).toBe("ctrl+shift+meta+super+a")
324
+ })
325
+
326
+ it("should convert key binding with meta modifier", () => {
327
+ const binding = { name: "s", meta: true, action: "save" as const }
328
+ expect(keyBindingToString(binding)).toBe("meta+s")
329
+ })
330
+
331
+ it("should convert key binding with super modifier", () => {
332
+ const binding = { name: "z", super: true, action: "undo" as const }
333
+ expect(keyBindingToString(binding)).toBe("super+z")
334
+ })
335
+
336
+ it("should handle special keys correctly", () => {
337
+ expect(keyBindingToString({ name: "return", action: "submit" as const })).toBe("return")
338
+ expect(keyBindingToString({ name: "space", action: "select" as const })).toBe("space")
339
+ expect(keyBindingToString({ name: "tab", action: "next" as const })).toBe("tab")
340
+ })
341
+ })
342
+ })
@@ -0,0 +1,182 @@
1
+ export interface KeyBindingLike {
2
+ name: string
3
+ ctrl?: boolean
4
+ shift?: boolean
5
+ meta?: boolean
6
+ super?: boolean
7
+ }
8
+
9
+ export interface KeyBinding<Action extends string = string> extends KeyBindingLike {
10
+ action: Action
11
+ }
12
+
13
+ export interface KeyBindingLookup extends KeyBindingLike {
14
+ // Kitty's base-layout codepoint as a Unicode number. Example: 99 means "c".
15
+ baseCode?: number
16
+ }
17
+
18
+ export type KeyAliasMap = Record<string, string>
19
+
20
+ export const defaultKeyAliases: KeyAliasMap = {
21
+ enter: "return",
22
+ esc: "escape",
23
+ kp0: "0",
24
+ kp1: "1",
25
+ kp2: "2",
26
+ kp3: "3",
27
+ kp4: "4",
28
+ kp5: "5",
29
+ kp6: "6",
30
+ kp7: "7",
31
+ kp8: "8",
32
+ kp9: "9",
33
+ kpdecimal: ".",
34
+ kpdivide: "/",
35
+ kpmultiply: "*",
36
+ kpminus: "-",
37
+ kpplus: "+",
38
+ kpenter: "enter",
39
+ kpequal: "=",
40
+ kpseparator: ",",
41
+ kpleft: "left",
42
+ kpright: "right",
43
+ kpup: "up",
44
+ kpdown: "down",
45
+ kppageup: "pageup",
46
+ kppagedown: "pagedown",
47
+ kphome: "home",
48
+ kpend: "end",
49
+ kpinsert: "insert",
50
+ kpdelete: "delete",
51
+ }
52
+
53
+ export function mergeKeyAliases(defaults: KeyAliasMap, custom: KeyAliasMap): KeyAliasMap {
54
+ return { ...defaults, ...custom }
55
+ }
56
+
57
+ export function mergeKeyBindings<Action extends string>(
58
+ defaults: KeyBinding<Action>[],
59
+ custom: KeyBinding<Action>[],
60
+ ): KeyBinding<Action>[] {
61
+ const map = new Map<string, KeyBinding<Action>>()
62
+ for (const binding of defaults) {
63
+ const key = getKeyBindingKey(binding)
64
+ map.set(key, binding)
65
+ }
66
+ for (const binding of custom) {
67
+ const key = getKeyBindingKey(binding)
68
+ map.set(key, binding)
69
+ }
70
+ return Array.from(map.values())
71
+ }
72
+
73
+ export function getKeyBindingKey(binding: KeyBindingLike): string {
74
+ return `${binding.name}:${binding.ctrl ? 1 : 0}:${binding.shift ? 1 : 0}:${binding.meta ? 1 : 0}:${binding.super ? 1 : 0}`
75
+ }
76
+
77
+ // `baseCode` is Kitty's "base layout codepoint": the character for the same
78
+ // physical key on the keyboard's base layout. Example: an event may arrive as
79
+ // `name: "ㅊ", baseCode: 99`, where `99` is Unicode `c`. We normalize that
80
+ // numeric codepoint to the key names we store in key maps so Ctrl+ㅊ can still
81
+ // match a Ctrl+C binding.
82
+ function getBaseCodeKeyName(baseCode: number | undefined): string | undefined {
83
+ if (baseCode === undefined || baseCode < 32 || baseCode === 127) {
84
+ return undefined
85
+ }
86
+
87
+ try {
88
+ const name = String.fromCodePoint(baseCode)
89
+
90
+ if (name.length === 1 && name >= "A" && name <= "Z") {
91
+ return name.toLowerCase()
92
+ }
93
+
94
+ return name
95
+ } catch {
96
+ return undefined
97
+ }
98
+ }
99
+
100
+ // Return every lookup key that can represent this event. We try the parsed
101
+ // name first, then the base-layout key when Kitty provides one. That keeps
102
+ // direct character bindings precise, and still lets physical-layout
103
+ // shortcuts resolve.
104
+ export function getKeyBindingKeys(binding: KeyBindingLookup): string[] {
105
+ const names = new Set([binding.name])
106
+ const baseCodeName = getBaseCodeKeyName(binding.baseCode)
107
+
108
+ if (baseCodeName) {
109
+ names.add(baseCodeName)
110
+ }
111
+
112
+ return [...names].map((name) => getKeyBindingKey({ ...binding, name }))
113
+ }
114
+
115
+ export function getKeyBindingAction<Action extends string>(
116
+ map: Map<string, Action>,
117
+ binding: KeyBindingLookup,
118
+ ): Action | undefined {
119
+ for (const key of getKeyBindingKeys(binding)) {
120
+ const action = map.get(key)
121
+
122
+ if (action !== undefined) {
123
+ return action
124
+ }
125
+ }
126
+
127
+ return undefined
128
+ }
129
+
130
+ export function matchesKeyBinding(binding: KeyBindingLookup, match: KeyBindingLike): boolean {
131
+ const matchKey = getKeyBindingKey(match)
132
+
133
+ return getKeyBindingKeys(binding).includes(matchKey)
134
+ }
135
+
136
+ export function buildKeyBindingsMap<Action extends string>(
137
+ bindings: KeyBinding<Action>[],
138
+ aliasMap?: KeyAliasMap,
139
+ ): Map<string, Action> {
140
+ const map = new Map<string, Action>()
141
+ const aliases = aliasMap || {}
142
+
143
+ for (const binding of bindings) {
144
+ const key = getKeyBindingKey(binding)
145
+ map.set(key, binding.action)
146
+ }
147
+
148
+ // Add aliased versions of all bindings
149
+ for (const binding of bindings) {
150
+ const normalizedName = aliases[binding.name] || binding.name
151
+ if (normalizedName !== binding.name) {
152
+ // Create aliased key with normalized name
153
+ const aliasedKey = getKeyBindingKey({ ...binding, name: normalizedName })
154
+ map.set(aliasedKey, binding.action)
155
+ }
156
+ }
157
+
158
+ return map
159
+ }
160
+
161
+ /**
162
+ * Converts a key binding to a human-readable string representation
163
+ * @param binding The key binding to stringify
164
+ * @returns A string like "ctrl+shift+y" or just "escape"
165
+ * @example
166
+ * keyBindingToString({ name: "y", ctrl: true, shift: true }) // "ctrl+shift+y"
167
+ * keyBindingToString({ name: "escape" }) // "escape"
168
+ * keyBindingToString({ name: "c", ctrl: true }) // "ctrl+c"
169
+ * keyBindingToString({ name: "s", super: true }) // "super+s"
170
+ */
171
+ export function keyBindingToString<Action extends string>(binding: KeyBinding<Action>): string {
172
+ const parts: string[] = []
173
+
174
+ if (binding.ctrl) parts.push("ctrl")
175
+ if (binding.shift) parts.push("shift")
176
+ if (binding.meta) parts.push("meta")
177
+ if (binding.super) parts.push("super")
178
+
179
+ parts.push(binding.name)
180
+
181
+ return parts.join("+")
182
+ }