@dungle-scrubs/tallow 0.9.4 → 0.9.7

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 (212) hide show
  1. package/dist/cli.js +8 -5
  2. package/dist/cli.js.map +1 -1
  3. package/dist/config.d.ts +1 -1
  4. package/dist/config.js +1 -1
  5. package/dist/interactive-mode-patch.d.ts +24 -12
  6. package/dist/interactive-mode-patch.d.ts.map +1 -1
  7. package/dist/interactive-mode-patch.js +229 -146
  8. package/dist/interactive-mode-patch.js.map +1 -1
  9. package/dist/interactive-reset.d.ts +49 -0
  10. package/dist/interactive-reset.d.ts.map +1 -0
  11. package/dist/interactive-reset.js +40 -0
  12. package/dist/interactive-reset.js.map +1 -0
  13. package/dist/pi-tui-editor-patch.d.ts +10 -0
  14. package/dist/pi-tui-editor-patch.d.ts.map +1 -0
  15. package/dist/pi-tui-editor-patch.js +159 -0
  16. package/dist/pi-tui-editor-patch.js.map +1 -0
  17. package/dist/pi-tui-patch.d.ts +2 -0
  18. package/dist/pi-tui-patch.d.ts.map +1 -0
  19. package/dist/pi-tui-patch.js +563 -0
  20. package/dist/pi-tui-patch.js.map +1 -0
  21. package/dist/pi-tui-settings-list-patch.d.ts +11 -0
  22. package/dist/pi-tui-settings-list-patch.d.ts.map +1 -0
  23. package/dist/pi-tui-settings-list-patch.js +38 -0
  24. package/dist/pi-tui-settings-list-patch.js.map +1 -0
  25. package/dist/process-cleanup.js +1 -1
  26. package/dist/process-cleanup.js.map +1 -1
  27. package/dist/reset-diagnostics.d.ts +69 -0
  28. package/dist/reset-diagnostics.d.ts.map +1 -0
  29. package/dist/reset-diagnostics.js +41 -0
  30. package/dist/reset-diagnostics.js.map +1 -0
  31. package/dist/sdk.d.ts +7 -23
  32. package/dist/sdk.d.ts.map +1 -1
  33. package/dist/sdk.js +211 -174
  34. package/dist/sdk.js.map +1 -1
  35. package/dist/workspace-transition-interactive.d.ts +1 -0
  36. package/dist/workspace-transition-interactive.d.ts.map +1 -1
  37. package/dist/workspace-transition-interactive.js +8 -18
  38. package/dist/workspace-transition-interactive.js.map +1 -1
  39. package/extensions/__integration__/audit-findings.test.ts +4 -5
  40. package/extensions/_icons/index.ts +2 -4
  41. package/extensions/_shared/__tests__/image-metadata.test.ts +33 -0
  42. package/extensions/_shared/__tests__/shell-policy.test.ts +19 -0
  43. package/extensions/_shared/__tests__/terminal-links.test.ts +18 -0
  44. package/extensions/_shared/image-metadata.ts +99 -0
  45. package/extensions/_shared/inline-preview.ts +1 -1
  46. package/extensions/_shared/shell-policy.ts +121 -1
  47. package/extensions/_shared/terminal-links.ts +22 -0
  48. package/extensions/ask-user-question-tool/index.ts +0 -3
  49. package/extensions/clear/__tests__/clear.test.ts +269 -2
  50. package/extensions/command-expansion/index.ts +9 -3
  51. package/extensions/context-files/index.ts +5 -1
  52. package/extensions/context-fork/__tests__/context-fork.test.ts +94 -1
  53. package/extensions/context-fork/extension.json +1 -1
  54. package/extensions/context-fork/frontmatter-index.ts +6 -1
  55. package/extensions/context-fork/index.ts +32 -0
  56. package/extensions/edit-tool-enhanced/index.ts +2 -1
  57. package/extensions/git-status/__tests__/git-status.test.ts +65 -2
  58. package/extensions/git-status/index.ts +268 -98
  59. package/extensions/hooks/index.ts +33 -11
  60. package/extensions/loop/index.ts +14 -1
  61. package/extensions/lsp/index.ts +64 -13
  62. package/extensions/lsp/package.json +2 -2
  63. package/extensions/minimal-skill-display/index.ts +7 -1
  64. package/extensions/random-spinner/index.ts +7 -642
  65. package/extensions/read-tool-enhanced/index.ts +13 -10
  66. package/extensions/render-stabilizer/__tests__/render-stabilizer.test.ts +2 -3
  67. package/extensions/render-stabilizer/index.ts +6 -6
  68. package/extensions/rewind/__tests__/session-files.test.ts +115 -0
  69. package/extensions/rewind/__tests__/snapshots.test.ts +23 -0
  70. package/extensions/rewind/index.ts +5 -0
  71. package/extensions/rewind/session-files.ts +138 -0
  72. package/extensions/rewind/snapshots.ts +104 -5
  73. package/extensions/skill-commands/index.ts +6 -1
  74. package/extensions/slash-command-bridge/__tests__/slash-command-bridge.test.ts +26 -0
  75. package/extensions/slash-command-bridge/index.ts +14 -2
  76. package/extensions/subagent-tool/model-resolver.ts +274 -7
  77. package/extensions/subagent-tool/schema.ts +1 -2
  78. package/extensions/tasks/commands/register-tasks-extension.ts +9 -9
  79. package/extensions/teams-tool/tools/register-extension.ts +1 -3
  80. package/extensions/teams-tool/tools/teammate-tools.ts +1 -2
  81. package/extensions/web-search-tool/index.ts +2 -1
  82. package/extensions/wezterm-pane-control/index.ts +1 -2
  83. package/extensions/write-tool-enhanced/index.ts +2 -1
  84. package/node_modules/@mariozechner/pi-tui/README.md +56 -34
  85. package/node_modules/@mariozechner/pi-tui/dist/autocomplete.d.ts +18 -13
  86. package/node_modules/@mariozechner/pi-tui/dist/autocomplete.d.ts.map +1 -1
  87. package/node_modules/@mariozechner/pi-tui/dist/autocomplete.js +182 -113
  88. package/node_modules/@mariozechner/pi-tui/dist/autocomplete.js.map +1 -1
  89. package/node_modules/@mariozechner/pi-tui/dist/components/cancellable-loader.js +3 -3
  90. package/node_modules/@mariozechner/pi-tui/dist/components/cancellable-loader.js.map +1 -1
  91. package/node_modules/@mariozechner/pi-tui/dist/components/editor.d.ts +45 -36
  92. package/node_modules/@mariozechner/pi-tui/dist/components/editor.d.ts.map +1 -1
  93. package/node_modules/@mariozechner/pi-tui/dist/components/editor.js +489 -325
  94. package/node_modules/@mariozechner/pi-tui/dist/components/editor.js.map +1 -1
  95. package/node_modules/@mariozechner/pi-tui/dist/components/image.d.ts +1 -99
  96. package/node_modules/@mariozechner/pi-tui/dist/components/image.d.ts.map +1 -1
  97. package/node_modules/@mariozechner/pi-tui/dist/components/image.js +17 -192
  98. package/node_modules/@mariozechner/pi-tui/dist/components/image.js.map +1 -1
  99. package/node_modules/@mariozechner/pi-tui/dist/components/input.d.ts.map +1 -1
  100. package/node_modules/@mariozechner/pi-tui/dist/components/input.js +57 -60
  101. package/node_modules/@mariozechner/pi-tui/dist/components/input.js.map +1 -1
  102. package/node_modules/@mariozechner/pi-tui/dist/components/loader.d.ts +2 -69
  103. package/node_modules/@mariozechner/pi-tui/dist/components/loader.d.ts.map +1 -1
  104. package/node_modules/@mariozechner/pi-tui/dist/components/loader.js +5 -102
  105. package/node_modules/@mariozechner/pi-tui/dist/components/loader.js.map +1 -1
  106. package/node_modules/@mariozechner/pi-tui/dist/components/markdown.d.ts.map +1 -1
  107. package/node_modules/@mariozechner/pi-tui/dist/components/markdown.js +111 -53
  108. package/node_modules/@mariozechner/pi-tui/dist/components/markdown.js.map +1 -1
  109. package/node_modules/@mariozechner/pi-tui/dist/components/select-list.d.ts +19 -1
  110. package/node_modules/@mariozechner/pi-tui/dist/components/select-list.d.ts.map +1 -1
  111. package/node_modules/@mariozechner/pi-tui/dist/components/select-list.js +78 -67
  112. package/node_modules/@mariozechner/pi-tui/dist/components/select-list.js.map +1 -1
  113. package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.d.ts +1 -25
  114. package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.d.ts.map +1 -1
  115. package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.js +13 -50
  116. package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.js.map +1 -1
  117. package/node_modules/@mariozechner/pi-tui/dist/index.d.ts +8 -10
  118. package/node_modules/@mariozechner/pi-tui/dist/index.d.ts.map +1 -1
  119. package/node_modules/@mariozechner/pi-tui/dist/index.js +6 -9
  120. package/node_modules/@mariozechner/pi-tui/dist/index.js.map +1 -1
  121. package/node_modules/@mariozechner/pi-tui/dist/keybindings.d.ts +108 -238
  122. package/node_modules/@mariozechner/pi-tui/dist/keybindings.d.ts.map +1 -1
  123. package/node_modules/@mariozechner/pi-tui/dist/keybindings.js +108 -365
  124. package/node_modules/@mariozechner/pi-tui/dist/keybindings.js.map +1 -1
  125. package/node_modules/@mariozechner/pi-tui/dist/keys.d.ts +33 -48
  126. package/node_modules/@mariozechner/pi-tui/dist/keys.d.ts.map +1 -1
  127. package/node_modules/@mariozechner/pi-tui/dist/keys.js +239 -155
  128. package/node_modules/@mariozechner/pi-tui/dist/keys.js.map +1 -1
  129. package/node_modules/@mariozechner/pi-tui/dist/terminal-image.d.ts +14 -94
  130. package/node_modules/@mariozechner/pi-tui/dist/terminal-image.d.ts.map +1 -1
  131. package/node_modules/@mariozechner/pi-tui/dist/terminal-image.js +44 -186
  132. package/node_modules/@mariozechner/pi-tui/dist/terminal-image.js.map +1 -1
  133. package/node_modules/@mariozechner/pi-tui/dist/terminal.d.ts +13 -58
  134. package/node_modules/@mariozechner/pi-tui/dist/terminal.d.ts.map +1 -1
  135. package/node_modules/@mariozechner/pi-tui/dist/terminal.js +78 -111
  136. package/node_modules/@mariozechner/pi-tui/dist/terminal.js.map +1 -1
  137. package/node_modules/@mariozechner/pi-tui/dist/tui.d.ts +24 -110
  138. package/node_modules/@mariozechner/pi-tui/dist/tui.d.ts.map +1 -1
  139. package/node_modules/@mariozechner/pi-tui/dist/tui.js +188 -435
  140. package/node_modules/@mariozechner/pi-tui/dist/tui.js.map +1 -1
  141. package/node_modules/@mariozechner/pi-tui/dist/utils.d.ts +0 -18
  142. package/node_modules/@mariozechner/pi-tui/dist/utils.d.ts.map +1 -1
  143. package/node_modules/@mariozechner/pi-tui/dist/utils.js +251 -119
  144. package/node_modules/@mariozechner/pi-tui/dist/utils.js.map +1 -1
  145. package/node_modules/@mariozechner/pi-tui/package.json +6 -6
  146. package/node_modules/@mariozechner/pi-tui/src/__tests__/__snapshots__/render.test.ts.snap +3 -40
  147. package/node_modules/@mariozechner/pi-tui/src/__tests__/image-component.test.ts +71 -81
  148. package/node_modules/@mariozechner/pi-tui/src/__tests__/render.test.ts +0 -33
  149. package/node_modules/@mariozechner/pi-tui/src/__tests__/terminal-image.test.ts +93 -334
  150. package/node_modules/@mariozechner/pi-tui/src/__tests__/tui-render-scheduling.test.ts +1 -1
  151. package/node_modules/@mariozechner/pi-tui/src/__tests__/utils.test.ts +11 -196
  152. package/node_modules/@mariozechner/pi-tui/src/autocomplete.ts +228 -142
  153. package/node_modules/@mariozechner/pi-tui/src/components/cancellable-loader.ts +3 -3
  154. package/node_modules/@mariozechner/pi-tui/src/components/editor.ts +624 -390
  155. package/node_modules/@mariozechner/pi-tui/src/components/image.ts +17 -227
  156. package/node_modules/@mariozechner/pi-tui/src/components/input.ts +71 -63
  157. package/node_modules/@mariozechner/pi-tui/src/components/loader.ts +5 -137
  158. package/node_modules/@mariozechner/pi-tui/src/components/markdown.ts +143 -52
  159. package/node_modules/@mariozechner/pi-tui/src/components/select-list.ts +136 -70
  160. package/node_modules/@mariozechner/pi-tui/src/components/settings-list.ts +12 -51
  161. package/node_modules/@mariozechner/pi-tui/src/index.ts +17 -36
  162. package/node_modules/@mariozechner/pi-tui/src/keybindings.ts +148 -421
  163. package/node_modules/@mariozechner/pi-tui/src/keys.ts +253 -181
  164. package/node_modules/@mariozechner/pi-tui/src/terminal-image.ts +51 -252
  165. package/node_modules/@mariozechner/pi-tui/src/terminal.ts +78 -133
  166. package/node_modules/@mariozechner/pi-tui/src/tui.ts +202 -478
  167. package/node_modules/@mariozechner/pi-tui/src/utils.ts +289 -125
  168. package/node_modules/@mariozechner/pi-tui/tsconfig.build.json +1 -0
  169. package/package.json +13 -13
  170. package/packages/tallow-tui/node_modules/@types/mime-types/README.md +8 -2
  171. package/packages/tallow-tui/node_modules/@types/mime-types/index.d.ts +6 -0
  172. package/packages/tallow-tui/node_modules/@types/mime-types/package.json +9 -3
  173. package/packages/tallow-tui/node_modules/get-east-asian-width/lookup-data.js +18 -0
  174. package/packages/tallow-tui/node_modules/get-east-asian-width/lookup.js +116 -384
  175. package/packages/tallow-tui/node_modules/get-east-asian-width/package.json +5 -4
  176. package/packages/tallow-tui/node_modules/get-east-asian-width/utilities.js +24 -0
  177. package/packages/tallow-tui/node_modules/marked/README.md +5 -4
  178. package/packages/tallow-tui/node_modules/marked/bin/main.js +10 -8
  179. package/packages/tallow-tui/node_modules/marked/bin/marked.js +2 -1
  180. package/packages/tallow-tui/node_modules/marked/lib/marked.d.ts +156 -125
  181. package/packages/tallow-tui/node_modules/marked/lib/marked.esm.js +67 -2179
  182. package/packages/tallow-tui/node_modules/marked/lib/marked.esm.js.map +3 -3
  183. package/packages/tallow-tui/node_modules/marked/lib/marked.umd.js +67 -2201
  184. package/packages/tallow-tui/node_modules/marked/lib/marked.umd.js.map +3 -3
  185. package/packages/tallow-tui/node_modules/marked/man/marked.1 +4 -2
  186. package/packages/tallow-tui/node_modules/marked/man/marked.1.md +2 -1
  187. package/packages/tallow-tui/node_modules/marked/package.json +26 -34
  188. package/skills/tallow-expert/SKILL.md +3 -5
  189. package/node_modules/@mariozechner/pi-tui/dist/border-styles.d.ts +0 -32
  190. package/node_modules/@mariozechner/pi-tui/dist/border-styles.d.ts.map +0 -1
  191. package/node_modules/@mariozechner/pi-tui/dist/border-styles.js +0 -46
  192. package/node_modules/@mariozechner/pi-tui/dist/border-styles.js.map +0 -1
  193. package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.d.ts +0 -52
  194. package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.d.ts.map +0 -1
  195. package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.js +0 -89
  196. package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.js.map +0 -1
  197. package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.d.ts +0 -14
  198. package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.d.ts.map +0 -1
  199. package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.js +0 -55
  200. package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.js.map +0 -1
  201. package/node_modules/@mariozechner/pi-tui/src/__tests__/editor-change-listener.test.ts +0 -121
  202. package/node_modules/@mariozechner/pi-tui/src/__tests__/editor-ghost-text.test.ts +0 -112
  203. package/node_modules/@mariozechner/pi-tui/src/__tests__/mouse-events.test.ts +0 -134
  204. package/node_modules/@mariozechner/pi-tui/src/__tests__/settings-list.test.ts +0 -81
  205. package/node_modules/@mariozechner/pi-tui/src/__tests__/tui-diff-regression.test.ts +0 -555
  206. package/node_modules/@mariozechner/pi-tui/src/border-styles.ts +0 -60
  207. package/node_modules/@mariozechner/pi-tui/src/components/bordered-box.ts +0 -113
  208. package/node_modules/@mariozechner/pi-tui/src/test-utils/capability-env.ts +0 -56
  209. package/packages/tallow-tui/node_modules/marked/lib/marked.cjs +0 -2211
  210. package/packages/tallow-tui/node_modules/marked/lib/marked.cjs.map +0 -7
  211. package/packages/tallow-tui/node_modules/marked/lib/marked.d.cts +0 -728
  212. package/packages/tallow-tui/node_modules/marked/marked.min.js +0 -69
@@ -40,8 +40,8 @@ export function isKittyProtocolActive() {
40
40
  * Usage:
41
41
  * - Key.escape, Key.enter, Key.tab, etc. for special keys
42
42
  * - Key.backtick, Key.comma, Key.period, etc. for symbol keys
43
- * - Key.ctrl("c"), Key.alt("x") for single modifier
44
- * - Key.ctrlShift("p"), Key.ctrlAlt("x") for combined modifiers
43
+ * - Key.ctrl("c"), Key.alt("x"), Key.super("k") for single modifiers
44
+ * - Key.ctrlShift("p"), Key.ctrlAlt("x"), Key.ctrlSuper("k") for combined modifiers
45
45
  */
46
46
  export const Key = {
47
47
  // Special keys
@@ -111,6 +111,7 @@ export const Key = {
111
111
  ctrl: (key) => `ctrl+${key}`,
112
112
  shift: (key) => `shift+${key}`,
113
113
  alt: (key) => `alt+${key}`,
114
+ super: (key) => `super+${key}`,
114
115
  // Combined modifiers
115
116
  ctrlShift: (key) => `ctrl+shift+${key}`,
116
117
  shiftCtrl: (key) => `shift+ctrl+${key}`,
@@ -118,8 +119,15 @@ export const Key = {
118
119
  altCtrl: (key) => `alt+ctrl+${key}`,
119
120
  shiftAlt: (key) => `shift+alt+${key}`,
120
121
  altShift: (key) => `alt+shift+${key}`,
122
+ ctrlSuper: (key) => `ctrl+super+${key}`,
123
+ superCtrl: (key) => `super+ctrl+${key}`,
124
+ shiftSuper: (key) => `shift+super+${key}`,
125
+ superShift: (key) => `super+shift+${key}`,
126
+ altSuper: (key) => `alt+super+${key}`,
127
+ superAlt: (key) => `super+alt+${key}`,
121
128
  // Triple modifiers
122
129
  ctrlShiftAlt: (key) => `ctrl+shift+alt+${key}`,
130
+ ctrlShiftSuper: (key) => `ctrl+shift+super+${key}`,
123
131
  };
124
132
  // =============================================================================
125
133
  // Constants
@@ -161,6 +169,7 @@ const MODIFIERS = {
161
169
  shift: 1,
162
170
  alt: 2,
163
171
  ctrl: 4,
172
+ super: 8,
164
173
  };
165
174
  const LOCK_MASK = 64 + 128; // Caps Lock + Num Lock
166
175
  const CODEPOINTS = {
@@ -185,6 +194,38 @@ const FUNCTIONAL_CODEPOINTS = {
185
194
  home: -14,
186
195
  end: -15,
187
196
  };
197
+ const KITTY_FUNCTIONAL_KEY_EQUIVALENTS = new Map([
198
+ [57399, 48], // KP_0 -> 0
199
+ [57400, 49], // KP_1 -> 1
200
+ [57401, 50], // KP_2 -> 2
201
+ [57402, 51], // KP_3 -> 3
202
+ [57403, 52], // KP_4 -> 4
203
+ [57404, 53], // KP_5 -> 5
204
+ [57405, 54], // KP_6 -> 6
205
+ [57406, 55], // KP_7 -> 7
206
+ [57407, 56], // KP_8 -> 8
207
+ [57408, 57], // KP_9 -> 9
208
+ [57409, 46], // KP_DECIMAL -> .
209
+ [57410, 47], // KP_DIVIDE -> /
210
+ [57411, 42], // KP_MULTIPLY -> *
211
+ [57412, 45], // KP_SUBTRACT -> -
212
+ [57413, 43], // KP_ADD -> +
213
+ [57415, 61], // KP_EQUAL -> =
214
+ [57416, 44], // KP_SEPARATOR -> ,
215
+ [57417, ARROW_CODEPOINTS.left],
216
+ [57418, ARROW_CODEPOINTS.right],
217
+ [57419, ARROW_CODEPOINTS.up],
218
+ [57420, ARROW_CODEPOINTS.down],
219
+ [57421, FUNCTIONAL_CODEPOINTS.pageUp],
220
+ [57422, FUNCTIONAL_CODEPOINTS.pageDown],
221
+ [57423, FUNCTIONAL_CODEPOINTS.home],
222
+ [57424, FUNCTIONAL_CODEPOINTS.end],
223
+ [57425, FUNCTIONAL_CODEPOINTS.insert],
224
+ [57426, FUNCTIONAL_CODEPOINTS.delete],
225
+ ]);
226
+ function normalizeKittyFunctionalCodepoint(codepoint) {
227
+ return KITTY_FUNCTIONAL_KEY_EQUIVALENTS.get(codepoint) ?? codepoint;
228
+ }
188
229
  const LEGACY_KEY_SEQUENCES = {
189
230
  up: ["\x1b[A", "\x1bOA"],
190
231
  down: ["\x1b[B", "\x1bOB"],
@@ -306,15 +347,11 @@ const matchesLegacyModifierSequence = (data, key, modifier) => {
306
347
  }
307
348
  return false;
308
349
  };
309
- // Tracks the event type from the most recent parseKey() call.
350
+ // Store the last parsed event type for isKeyRelease() to query
310
351
  let _lastEventType = "press";
311
352
  /**
312
- * Check if the given input data represents a key release event.
313
- * Performs fast pattern matching on raw terminal escape sequences.
353
+ * Check if the last parsed key event was a key release.
314
354
  * Only meaningful when Kitty keyboard protocol with flag 2 is active.
315
- *
316
- * @param data - Raw terminal input string
317
- * @returns true if the data contains a key release escape sequence
318
355
  */
319
356
  export function isKeyRelease(data) {
320
357
  // Don't treat bracketed paste content as key release, even if it contains
@@ -339,12 +376,8 @@ export function isKeyRelease(data) {
339
376
  return false;
340
377
  }
341
378
  /**
342
- * Check if the given input data represents a key repeat event.
343
- * Performs fast pattern matching on raw terminal escape sequences.
379
+ * Check if the last parsed key event was a key repeat.
344
380
  * Only meaningful when Kitty keyboard protocol with flag 2 is active.
345
- *
346
- * @param data - Raw terminal input string
347
- * @returns true if the data contains a key repeat escape sequence
348
381
  */
349
382
  export function isKeyRepeat(data) {
350
383
  // Don't treat bracketed paste content as key repeat, even if it contains
@@ -444,8 +477,10 @@ function matchesKittySequence(data, expectedCodepoint, expectedModifier) {
444
477
  // Check if modifiers match
445
478
  if (actualMod !== expectedMod)
446
479
  return false;
447
- // Primary match: codepoint matches directly
448
- if (parsed.codepoint === expectedCodepoint)
480
+ const normalizedCodepoint = normalizeKittyFunctionalCodepoint(parsed.codepoint);
481
+ const normalizedExpectedCodepoint = normalizeKittyFunctionalCodepoint(expectedCodepoint);
482
+ // Primary match: codepoint matches directly after normalizing functional keys
483
+ if (normalizedCodepoint === normalizedExpectedCodepoint)
449
484
  return true;
450
485
  // Alternate match: use base layout key for non-Latin keyboard layouts.
451
486
  // This allows Ctrl+С (Cyrillic) to match Ctrl+c (Latin) when terminal reports
@@ -460,7 +495,7 @@ function matchesKittySequence(data, expectedCodepoint, expectedModifier) {
460
495
  // (letter remapping) and Ctrl+/ could falsely match Ctrl+[ (symbol remapping)
461
496
  // if the base layout key were always considered.
462
497
  if (parsed.baseLayoutKey !== undefined && parsed.baseLayoutKey === expectedCodepoint) {
463
- const cp = parsed.codepoint;
498
+ const cp = normalizedCodepoint;
464
499
  const isLatinLetter = cp >= 97 && cp <= 122; // a-z
465
500
  const isKnownSymbol = SYMBOL_KEYS.has(String.fromCharCode(cp));
466
501
  if (!isLatinLetter && !isKnownSymbol)
@@ -468,27 +503,25 @@ function matchesKittySequence(data, expectedCodepoint, expectedModifier) {
468
503
  }
469
504
  return false;
470
505
  }
506
+ function parseModifyOtherKeysSequence(data) {
507
+ const match = data.match(/^\x1b\[27;(\d+);(\d+)~$/);
508
+ if (!match)
509
+ return null;
510
+ const modValue = parseInt(match[1], 10);
511
+ const codepoint = parseInt(match[2], 10);
512
+ return { codepoint, modifier: modValue - 1 };
513
+ }
471
514
  /**
472
515
  * Match xterm modifyOtherKeys format: CSI 27 ; modifiers ; keycode ~
473
516
  * This is used by terminals when Kitty protocol is not enabled.
474
517
  * Modifier values are 1-indexed: 2=shift, 3=alt, 5=ctrl, etc.
475
518
  */
476
519
  function matchesModifyOtherKeys(data, expectedKeycode, expectedModifier) {
477
- const match = data.match(/^\x1b\[27;(\d+);(\d+)~$/);
478
- if (!match)
520
+ const parsed = parseModifyOtherKeysSequence(data);
521
+ if (!parsed)
479
522
  return false;
480
- const modValue = parseInt(match[1], 10);
481
- const keycode = parseInt(match[2], 10);
482
- // Convert from 1-indexed xterm format to our 0-indexed format
483
- const actualMod = modValue - 1;
484
- return keycode === expectedKeycode && actualMod === expectedModifier;
523
+ return parsed.codepoint === expectedKeycode && parsed.modifier === expectedModifier;
485
524
  }
486
- /**
487
- * Detect Windows Terminal by session environment variable.
488
- * Excludes SSH sessions where WT_SESSION may leak from the host.
489
- *
490
- * @returns true when running directly inside Windows Terminal
491
- */
492
525
  function isWindowsTerminalSession() {
493
526
  return (Boolean(process.env.WT_SESSION) &&
494
527
  !process.env.SSH_CONNECTION &&
@@ -503,10 +536,6 @@ function isWindowsTerminalSession() {
503
536
  *
504
537
  * Prefer explicit Kitty / CSI-u / modifyOtherKeys sequences whenever they are
505
538
  * available. Fall back to a Windows Terminal heuristic only for raw BS bytes.
506
- *
507
- * @param data - Raw terminal input byte
508
- * @param expectedModifier - Modifier bitmask to match against
509
- * @returns true if the data matches backspace with the given modifier
510
539
  */
511
540
  function matchesRawBackspace(data, expectedModifier) {
512
541
  if (data === "\x7f")
@@ -543,6 +572,30 @@ function rawCtrlChar(key) {
543
572
  }
544
573
  return null;
545
574
  }
575
+ function isDigitKey(key) {
576
+ return key >= "0" && key <= "9";
577
+ }
578
+ function matchesPrintableModifyOtherKeys(data, expectedKeycode, expectedModifier) {
579
+ if (expectedModifier === 0)
580
+ return false;
581
+ return matchesModifyOtherKeys(data, expectedKeycode, expectedModifier);
582
+ }
583
+ function formatKeyNameWithModifiers(keyName, modifier) {
584
+ const mods = [];
585
+ const effectiveMod = modifier & ~LOCK_MASK;
586
+ const supportedModifierMask = MODIFIERS.shift | MODIFIERS.ctrl | MODIFIERS.alt | MODIFIERS.super;
587
+ if ((effectiveMod & ~supportedModifierMask) !== 0)
588
+ return undefined;
589
+ if (effectiveMod & MODIFIERS.shift)
590
+ mods.push("shift");
591
+ if (effectiveMod & MODIFIERS.ctrl)
592
+ mods.push("ctrl");
593
+ if (effectiveMod & MODIFIERS.alt)
594
+ mods.push("alt");
595
+ if (effectiveMod & MODIFIERS.super)
596
+ mods.push("super");
597
+ return mods.length > 0 ? `${mods.join("+")}+${keyName}` : keyName;
598
+ }
546
599
  function parseKeyId(keyId) {
547
600
  const parts = keyId.toLowerCase().split("+");
548
601
  const key = parts[parts.length - 1];
@@ -553,6 +606,7 @@ function parseKeyId(keyId) {
553
606
  ctrl: parts.includes("ctrl"),
554
607
  shift: parts.includes("shift"),
555
608
  alt: parts.includes("alt"),
609
+ super: parts.includes("super"),
556
610
  };
557
611
  }
558
612
  /**
@@ -564,9 +618,10 @@ function parseKeyId(keyId) {
564
618
  * - Ctrl combinations: "ctrl+c", "ctrl+z", etc.
565
619
  * - Shift combinations: "shift+tab", "shift+enter"
566
620
  * - Alt combinations: "alt+enter", "alt+backspace"
567
- * - Combined modifiers: "shift+ctrl+p", "ctrl+alt+x"
621
+ * - Super combinations: "super+k", "super+enter"
622
+ * - Combined modifiers: "shift+ctrl+p", "ctrl+alt+x", "ctrl+super+k"
568
623
  *
569
- * Use the Key helper for autocomplete: Key.ctrl("c"), Key.escape, Key.ctrlShift("p")
624
+ * Use the Key helper for autocomplete: Key.ctrl("c"), Key.escape, Key.ctrlShift("p"), Key.super("k")
570
625
  *
571
626
  * @param data - Raw input data from terminal
572
627
  * @param keyId - Key identifier (e.g., "ctrl+c", "escape", Key.ctrl("c"))
@@ -575,7 +630,7 @@ export function matchesKey(data, keyId) {
575
630
  const parsed = parseKeyId(keyId);
576
631
  if (!parsed)
577
632
  return false;
578
- const { key, ctrl, shift, alt } = parsed;
633
+ const { key, ctrl, shift, alt, super: superModifier } = parsed;
579
634
  let modifier = 0;
580
635
  if (shift)
581
636
  modifier |= MODIFIERS.shift;
@@ -583,6 +638,8 @@ export function matchesKey(data, keyId) {
583
638
  modifier |= MODIFIERS.alt;
584
639
  if (ctrl)
585
640
  modifier |= MODIFIERS.ctrl;
641
+ if (superModifier)
642
+ modifier |= MODIFIERS.super;
586
643
  switch (key) {
587
644
  case "escape":
588
645
  case "esc":
@@ -593,10 +650,10 @@ export function matchesKey(data, keyId) {
593
650
  matchesModifyOtherKeys(data, CODEPOINTS.escape, 0));
594
651
  case "space":
595
652
  if (!_kittyProtocolActive) {
596
- if (ctrl && !alt && !shift && data === "\x00") {
653
+ if (modifier === MODIFIERS.ctrl && data === "\x00") {
597
654
  return true;
598
655
  }
599
- if (alt && !ctrl && !shift && data === "\x1b ") {
656
+ if (modifier === MODIFIERS.alt && data === "\x1b ") {
600
657
  return true;
601
658
  }
602
659
  }
@@ -608,8 +665,10 @@ export function matchesKey(data, keyId) {
608
665
  return (matchesKittySequence(data, CODEPOINTS.space, modifier) ||
609
666
  matchesModifyOtherKeys(data, CODEPOINTS.space, modifier));
610
667
  case "tab":
611
- if (shift && !ctrl && !alt) {
612
- return data === "\x1b[Z" || matchesKittySequence(data, CODEPOINTS.tab, MODIFIERS.shift);
668
+ if (modifier === MODIFIERS.shift) {
669
+ return (data === "\x1b[Z" ||
670
+ matchesKittySequence(data, CODEPOINTS.tab, MODIFIERS.shift) ||
671
+ matchesModifyOtherKeys(data, CODEPOINTS.tab, MODIFIERS.shift));
613
672
  }
614
673
  if (modifier === 0) {
615
674
  return data === "\t" || matchesKittySequence(data, CODEPOINTS.tab, 0);
@@ -618,7 +677,7 @@ export function matchesKey(data, keyId) {
618
677
  matchesModifyOtherKeys(data, CODEPOINTS.tab, modifier));
619
678
  case "enter":
620
679
  case "return":
621
- if (shift && !ctrl && !alt) {
680
+ if (modifier === MODIFIERS.shift) {
622
681
  // CSI u sequences (standard Kitty protocol)
623
682
  if (matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.shift) ||
624
683
  matchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.shift)) {
@@ -636,7 +695,7 @@ export function matchesKey(data, keyId) {
636
695
  }
637
696
  return false;
638
697
  }
639
- if (alt && !ctrl && !shift) {
698
+ if (modifier === MODIFIERS.alt) {
640
699
  // CSI u sequences (standard Kitty protocol)
641
700
  if (matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.alt) ||
642
701
  matchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.alt)) {
@@ -664,14 +723,14 @@ export function matchesKey(data, keyId) {
664
723
  matchesKittySequence(data, CODEPOINTS.kpEnter, modifier) ||
665
724
  matchesModifyOtherKeys(data, CODEPOINTS.enter, modifier));
666
725
  case "backspace":
667
- if (alt && !ctrl && !shift) {
726
+ if (modifier === MODIFIERS.alt) {
668
727
  if (data === "\x1b\x7f" || data === "\x1b\b") {
669
728
  return true;
670
729
  }
671
730
  return (matchesKittySequence(data, CODEPOINTS.backspace, MODIFIERS.alt) ||
672
731
  matchesModifyOtherKeys(data, CODEPOINTS.backspace, MODIFIERS.alt));
673
732
  }
674
- if (ctrl && !alt && !shift) {
733
+ if (modifier === MODIFIERS.ctrl) {
675
734
  // Legacy raw 0x08 is ambiguous: it can be Ctrl+Backspace on Windows
676
735
  // Terminal or plain Backspace on other terminals, while also
677
736
  // overlapping with Ctrl+H.
@@ -747,7 +806,7 @@ export function matchesKey(data, keyId) {
747
806
  }
748
807
  return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageDown, modifier);
749
808
  case "up":
750
- if (alt && !ctrl && !shift) {
809
+ if (modifier === MODIFIERS.alt) {
751
810
  return data === "\x1bp" || matchesKittySequence(data, ARROW_CODEPOINTS.up, MODIFIERS.alt);
752
811
  }
753
812
  if (modifier === 0) {
@@ -759,7 +818,7 @@ export function matchesKey(data, keyId) {
759
818
  }
760
819
  return matchesKittySequence(data, ARROW_CODEPOINTS.up, modifier);
761
820
  case "down":
762
- if (alt && !ctrl && !shift) {
821
+ if (modifier === MODIFIERS.alt) {
763
822
  return data === "\x1bn" || matchesKittySequence(data, ARROW_CODEPOINTS.down, MODIFIERS.alt);
764
823
  }
765
824
  if (modifier === 0) {
@@ -771,13 +830,13 @@ export function matchesKey(data, keyId) {
771
830
  }
772
831
  return matchesKittySequence(data, ARROW_CODEPOINTS.down, modifier);
773
832
  case "left":
774
- if (alt && !ctrl && !shift) {
833
+ if (modifier === MODIFIERS.alt) {
775
834
  return (data === "\x1b[1;3D" ||
776
835
  (!_kittyProtocolActive && data === "\x1bB") ||
777
836
  data === "\x1bb" ||
778
837
  matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.alt));
779
838
  }
780
- if (ctrl && !alt && !shift) {
839
+ if (modifier === MODIFIERS.ctrl) {
781
840
  return (data === "\x1b[1;5D" ||
782
841
  matchesLegacyModifierSequence(data, "left", MODIFIERS.ctrl) ||
783
842
  matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.ctrl));
@@ -791,13 +850,13 @@ export function matchesKey(data, keyId) {
791
850
  }
792
851
  return matchesKittySequence(data, ARROW_CODEPOINTS.left, modifier);
793
852
  case "right":
794
- if (alt && !ctrl && !shift) {
853
+ if (modifier === MODIFIERS.alt) {
795
854
  return (data === "\x1b[1;3C" ||
796
855
  (!_kittyProtocolActive && data === "\x1bF") ||
797
856
  data === "\x1bf" ||
798
857
  matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.alt));
799
858
  }
800
- if (ctrl && !alt && !shift) {
859
+ if (modifier === MODIFIERS.ctrl) {
801
860
  return (data === "\x1b[1;5C" ||
802
861
  matchesLegacyModifierSequence(data, "right", MODIFIERS.ctrl) ||
803
862
  matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.ctrl));
@@ -829,40 +888,45 @@ export function matchesKey(data, keyId) {
829
888
  return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES[functionKey]);
830
889
  }
831
890
  }
832
- // Handle single letter keys (a-z) and some symbols
833
- if (key.length === 1 && ((key >= "a" && key <= "z") || SYMBOL_KEYS.has(key))) {
891
+ // Handle single letter/digit keys and symbols
892
+ if (key.length === 1 && ((key >= "a" && key <= "z") || isDigitKey(key) || SYMBOL_KEYS.has(key))) {
834
893
  const codepoint = key.charCodeAt(0);
835
894
  const rawCtrl = rawCtrlChar(key);
836
- if (ctrl && alt && !shift && !_kittyProtocolActive && rawCtrl) {
837
- // Legacy: ctrl+alt+key is ESC followed by the control character
838
- return data === `\x1b${rawCtrl}`;
895
+ const isLetter = key >= "a" && key <= "z";
896
+ const isDigit = isDigitKey(key);
897
+ if (modifier === MODIFIERS.ctrl + MODIFIERS.alt && !_kittyProtocolActive && rawCtrl) {
898
+ // Legacy: ctrl+alt+key is ESC followed by the control character.
899
+ // If that legacy form does not match, continue so CSI-u and
900
+ // modifyOtherKeys sequences from tmux can still be recognized.
901
+ if (data === `\x1b${rawCtrl}`)
902
+ return true;
839
903
  }
840
- if (alt && !ctrl && !shift && !_kittyProtocolActive && key >= "a" && key <= "z") {
841
- // Legacy: alt+letter is ESC followed by the letter
904
+ if (modifier === MODIFIERS.alt && !_kittyProtocolActive && (isLetter || isDigit)) {
905
+ // Legacy: alt+letter/digit is ESC followed by the key
842
906
  if (data === `\x1b${key}`)
843
907
  return true;
844
908
  }
845
- if (ctrl && !shift && !alt) {
909
+ if (modifier === MODIFIERS.ctrl) {
846
910
  // Legacy: ctrl+key sends the control character
847
911
  if (rawCtrl && data === rawCtrl)
848
912
  return true;
849
913
  return (matchesKittySequence(data, codepoint, MODIFIERS.ctrl) ||
850
- matchesModifyOtherKeys(data, codepoint, MODIFIERS.ctrl));
914
+ matchesPrintableModifyOtherKeys(data, codepoint, MODIFIERS.ctrl));
851
915
  }
852
- if (ctrl && shift && !alt) {
916
+ if (modifier === MODIFIERS.shift + MODIFIERS.ctrl) {
853
917
  return (matchesKittySequence(data, codepoint, MODIFIERS.shift + MODIFIERS.ctrl) ||
854
- matchesModifyOtherKeys(data, codepoint, MODIFIERS.shift + MODIFIERS.ctrl));
918
+ matchesPrintableModifyOtherKeys(data, codepoint, MODIFIERS.shift + MODIFIERS.ctrl));
855
919
  }
856
- if (shift && !ctrl && !alt) {
920
+ if (modifier === MODIFIERS.shift) {
857
921
  // Legacy: shift+letter produces uppercase
858
- if (data === key.toUpperCase())
922
+ if (isLetter && data === key.toUpperCase())
859
923
  return true;
860
924
  return (matchesKittySequence(data, codepoint, MODIFIERS.shift) ||
861
- matchesModifyOtherKeys(data, codepoint, MODIFIERS.shift));
925
+ matchesPrintableModifyOtherKeys(data, codepoint, MODIFIERS.shift));
862
926
  }
863
927
  if (modifier !== 0) {
864
928
  return (matchesKittySequence(data, codepoint, modifier) ||
865
- matchesModifyOtherKeys(data, codepoint, modifier));
929
+ matchesPrintableModifyOtherKeys(data, codepoint, modifier));
866
930
  }
867
931
  // Check both raw char and Kitty sequence (needed for release events)
868
932
  return data === key || matchesKittySequence(data, codepoint, 0);
@@ -875,64 +939,68 @@ export function matchesKey(data, keyId) {
875
939
  * @param data - Raw input data from terminal
876
940
  * @returns Key identifier string (e.g., "ctrl+c") or undefined
877
941
  */
942
+ function formatParsedKey(codepoint, modifier, baseLayoutKey) {
943
+ const normalizedCodepoint = normalizeKittyFunctionalCodepoint(codepoint);
944
+ // Use base layout key only when codepoint is not a recognized Latin
945
+ // letter (a-z), digit (0-9), or symbol (/, -, [, ;, etc.). For those,
946
+ // the codepoint is authoritative regardless of physical key position.
947
+ // This prevents remapped layouts (Dvorak, Colemak, xremap, etc.) from
948
+ // reporting the wrong key name based on the QWERTY physical position.
949
+ const isLatinLetter = normalizedCodepoint >= 97 && normalizedCodepoint <= 122; // a-z
950
+ const isDigit = normalizedCodepoint >= 48 && normalizedCodepoint <= 57; // 0-9
951
+ const isKnownSymbol = SYMBOL_KEYS.has(String.fromCharCode(normalizedCodepoint));
952
+ const effectiveCodepoint = isLatinLetter || isDigit || isKnownSymbol
953
+ ? normalizedCodepoint
954
+ : (baseLayoutKey ?? normalizedCodepoint);
955
+ let keyName;
956
+ if (effectiveCodepoint === CODEPOINTS.escape)
957
+ keyName = "escape";
958
+ else if (effectiveCodepoint === CODEPOINTS.tab)
959
+ keyName = "tab";
960
+ else if (effectiveCodepoint === CODEPOINTS.enter || effectiveCodepoint === CODEPOINTS.kpEnter)
961
+ keyName = "enter";
962
+ else if (effectiveCodepoint === CODEPOINTS.space)
963
+ keyName = "space";
964
+ else if (effectiveCodepoint === CODEPOINTS.backspace)
965
+ keyName = "backspace";
966
+ else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.delete)
967
+ keyName = "delete";
968
+ else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.insert)
969
+ keyName = "insert";
970
+ else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.home)
971
+ keyName = "home";
972
+ else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.end)
973
+ keyName = "end";
974
+ else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.pageUp)
975
+ keyName = "pageUp";
976
+ else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.pageDown)
977
+ keyName = "pageDown";
978
+ else if (effectiveCodepoint === ARROW_CODEPOINTS.up)
979
+ keyName = "up";
980
+ else if (effectiveCodepoint === ARROW_CODEPOINTS.down)
981
+ keyName = "down";
982
+ else if (effectiveCodepoint === ARROW_CODEPOINTS.left)
983
+ keyName = "left";
984
+ else if (effectiveCodepoint === ARROW_CODEPOINTS.right)
985
+ keyName = "right";
986
+ else if (effectiveCodepoint >= 48 && effectiveCodepoint <= 57)
987
+ keyName = String.fromCharCode(effectiveCodepoint);
988
+ else if (effectiveCodepoint >= 97 && effectiveCodepoint <= 122)
989
+ keyName = String.fromCharCode(effectiveCodepoint);
990
+ else if (SYMBOL_KEYS.has(String.fromCharCode(effectiveCodepoint)))
991
+ keyName = String.fromCharCode(effectiveCodepoint);
992
+ if (!keyName)
993
+ return undefined;
994
+ return formatKeyNameWithModifiers(keyName, modifier);
995
+ }
878
996
  export function parseKey(data) {
879
997
  const kitty = parseKittySequence(data);
880
998
  if (kitty) {
881
- const { codepoint, baseLayoutKey, modifier } = kitty;
882
- const mods = [];
883
- const effectiveMod = modifier & ~LOCK_MASK;
884
- if (effectiveMod & MODIFIERS.shift)
885
- mods.push("shift");
886
- if (effectiveMod & MODIFIERS.ctrl)
887
- mods.push("ctrl");
888
- if (effectiveMod & MODIFIERS.alt)
889
- mods.push("alt");
890
- // Use base layout key only when codepoint is not a recognized Latin
891
- // letter (a-z) or symbol (/, -, [, ;, etc.). For those, the codepoint
892
- // is authoritative regardless of physical key position. This prevents
893
- // remapped layouts (Dvorak, Colemak, xremap, etc.) from reporting the
894
- // wrong key name based on the QWERTY physical position.
895
- const isLatinLetter = codepoint >= 97 && codepoint <= 122; // a-z
896
- const isKnownSymbol = SYMBOL_KEYS.has(String.fromCharCode(codepoint));
897
- const effectiveCodepoint = isLatinLetter || isKnownSymbol ? codepoint : (baseLayoutKey ?? codepoint);
898
- let keyName;
899
- if (effectiveCodepoint === CODEPOINTS.escape)
900
- keyName = "escape";
901
- else if (effectiveCodepoint === CODEPOINTS.tab)
902
- keyName = "tab";
903
- else if (effectiveCodepoint === CODEPOINTS.enter || effectiveCodepoint === CODEPOINTS.kpEnter)
904
- keyName = "enter";
905
- else if (effectiveCodepoint === CODEPOINTS.space)
906
- keyName = "space";
907
- else if (effectiveCodepoint === CODEPOINTS.backspace)
908
- keyName = "backspace";
909
- else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.delete)
910
- keyName = "delete";
911
- else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.insert)
912
- keyName = "insert";
913
- else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.home)
914
- keyName = "home";
915
- else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.end)
916
- keyName = "end";
917
- else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.pageUp)
918
- keyName = "pageUp";
919
- else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.pageDown)
920
- keyName = "pageDown";
921
- else if (effectiveCodepoint === ARROW_CODEPOINTS.up)
922
- keyName = "up";
923
- else if (effectiveCodepoint === ARROW_CODEPOINTS.down)
924
- keyName = "down";
925
- else if (effectiveCodepoint === ARROW_CODEPOINTS.left)
926
- keyName = "left";
927
- else if (effectiveCodepoint === ARROW_CODEPOINTS.right)
928
- keyName = "right";
929
- else if (effectiveCodepoint >= 97 && effectiveCodepoint <= 122)
930
- keyName = String.fromCharCode(effectiveCodepoint);
931
- else if (SYMBOL_KEYS.has(String.fromCharCode(effectiveCodepoint)))
932
- keyName = String.fromCharCode(effectiveCodepoint);
933
- if (keyName) {
934
- return mods.length > 0 ? `${mods.join("+")}+${keyName}` : keyName;
935
- }
999
+ return formatParsedKey(kitty.codepoint, kitty.modifier, kitty.baseLayoutKey);
1000
+ }
1001
+ const modifyOtherKeys = parseModifyOtherKeysSequence(data);
1002
+ if (modifyOtherKeys) {
1003
+ return formatParsedKey(modifyOtherKeys.codepoint, modifyOtherKeys.modifier);
936
1004
  }
937
1005
  // Mode-aware legacy sequences
938
1006
  // When Kitty protocol is active, ambiguous sequences are interpreted as custom terminal mappings:
@@ -991,8 +1059,8 @@ export function parseKey(data) {
991
1059
  if (code >= 1 && code <= 26) {
992
1060
  return `ctrl+alt+${String.fromCharCode(code + 96)}`;
993
1061
  }
994
- // Legacy alt+letter (ESC followed by letter a-z)
995
- if (code >= 97 && code <= 122) {
1062
+ // Legacy alt+letter/digit (ESC followed by the key)
1063
+ if ((code >= 97 && code <= 122) || (code >= 48 && code <= 57)) {
996
1064
  return `alt+${String.fromCharCode(code)}`;
997
1065
  }
998
1066
  }
@@ -1026,42 +1094,58 @@ export function parseKey(data) {
1026
1094
  }
1027
1095
  return undefined;
1028
1096
  }
1029
- /** SGR extended mouse format: \x1b[<button;x;y[Mm] */
1030
- const SGR_MOUSE_RE = /^\x1b\[<(\d+);(\d+);(\d+)([Mm])$/;
1097
+ // =============================================================================
1098
+ // Kitty CSI-u Printable Decoding
1099
+ // =============================================================================
1100
+ const KITTY_CSI_U_REGEX = /^\x1b\[(\d+)(?::(\d*))?(?::(\d+))?(?:;(\d+))?(?::(\d+))?u$/;
1101
+ const KITTY_PRINTABLE_ALLOWED_MODIFIERS = MODIFIERS.shift | LOCK_MASK;
1031
1102
  /**
1032
- * Parse an SGR mouse event from raw terminal input.
1103
+ * Decode a Kitty CSI-u sequence into a printable character, if applicable.
1033
1104
  *
1034
- * @param data - Raw terminal input
1035
- * @returns Parsed mouse event, or null if not a mouse sequence
1036
- */
1037
- export function parseMouseEvent(data) {
1038
- const match = data.match(SGR_MOUSE_RE);
1039
- if (!match)
1040
- return null;
1041
- const code = parseInt(match[1], 10);
1042
- const x = parseInt(match[2], 10);
1043
- const y = parseInt(match[3], 10);
1044
- const isRelease = match[4] === "m";
1045
- // Scroll wheel: codes 64 (up) and 65 (down)
1046
- if (code === 64)
1047
- return { type: "scroll-up", button: 0, x, y };
1048
- if (code === 65)
1049
- return { type: "scroll-down", button: 0, x, y };
1050
- // Button number is in the low 2 bits
1051
- const button = code & 0x03;
1052
- // Bit 5 (32) = motion/drag
1053
- if (code & 32)
1054
- return { type: "drag", button, x, y };
1055
- return { type: isRelease ? "release" : "press", button, x, y };
1056
- }
1057
- /**
1058
- * Fast check: is this data an SGR mouse event?
1059
- * Avoids regex for non-mouse input.
1105
+ * When Kitty keyboard protocol flag 1 (disambiguate) is active, terminals send
1106
+ * CSI-u sequences for all keys, including plain printable characters. This
1107
+ * function extracts the printable character from such sequences.
1060
1108
  *
1061
- * @param data - Raw terminal input
1062
- * @returns true if the input is an SGR mouse sequence
1109
+ * Only accepts plain or Shift-modified keys. Rejects Ctrl, Alt, and unsupported
1110
+ * modifier combinations (those are handled by keybinding matching instead).
1111
+ * Prefers the shifted keycode when Shift is held and a shifted key is reported.
1112
+ *
1113
+ * @param data - Raw input data from terminal
1114
+ * @returns The printable character, or undefined if not a printable CSI-u sequence
1063
1115
  */
1064
- export function isMouseEvent(data) {
1065
- return data.length >= 9 && data.startsWith("\x1b[<");
1116
+ export function decodeKittyPrintable(data) {
1117
+ const match = data.match(KITTY_CSI_U_REGEX);
1118
+ if (!match)
1119
+ return undefined;
1120
+ // CSI-u groups: <codepoint>[:<shifted>[:<base>]];<mod>[:<event>]u
1121
+ const codepoint = Number.parseInt(match[1] ?? "", 10);
1122
+ if (!Number.isFinite(codepoint))
1123
+ return undefined;
1124
+ const shiftedKey = match[2] && match[2].length > 0 ? Number.parseInt(match[2], 10) : undefined;
1125
+ const modValue = match[4] ? Number.parseInt(match[4], 10) : 1;
1126
+ // Modifiers are 1-indexed in CSI-u; normalize to our bitmask.
1127
+ const modifier = Number.isFinite(modValue) ? modValue - 1 : 0;
1128
+ // Only accept printable CSI-u input for plain or Shift-modified text keys.
1129
+ // Reject unsupported modifier bits (e.g. Super/Meta) to avoid inserting
1130
+ // characters from modifier-only terminal events.
1131
+ if ((modifier & ~KITTY_PRINTABLE_ALLOWED_MODIFIERS) !== 0)
1132
+ return undefined;
1133
+ if (modifier & (MODIFIERS.alt | MODIFIERS.ctrl))
1134
+ return undefined;
1135
+ // Prefer the shifted keycode when Shift is held.
1136
+ let effectiveCodepoint = codepoint;
1137
+ if (modifier & MODIFIERS.shift && typeof shiftedKey === "number") {
1138
+ effectiveCodepoint = shiftedKey;
1139
+ }
1140
+ effectiveCodepoint = normalizeKittyFunctionalCodepoint(effectiveCodepoint);
1141
+ // Drop control characters or invalid codepoints.
1142
+ if (!Number.isFinite(effectiveCodepoint) || effectiveCodepoint < 32)
1143
+ return undefined;
1144
+ try {
1145
+ return String.fromCodePoint(effectiveCodepoint);
1146
+ }
1147
+ catch {
1148
+ return undefined;
1149
+ }
1066
1150
  }
1067
1151
  //# sourceMappingURL=keys.js.map