@sagmans/dsh-tui 0.6.0 → 0.8.0

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 (271) hide show
  1. package/.agents/skills/dsh-tui-dogfood/SKILL.md +83 -0
  2. package/.agents/skills/dsh-tui-dogfood/references/home-state.md +78 -0
  3. package/.agents/skills/dsh-tui-dogfood/scripts/clone-links.mjs +133 -0
  4. package/.agents/skills/dsh-tui-dogfood/scripts/run-plugin-from-worktree.sh +417 -0
  5. package/.agents/skills/dsh-tui-update-models/SKILL.md +199 -0
  6. package/.agents/skills/dsh-tui-update-models/references/model-wiring.md +292 -0
  7. package/.agents/skills/dsh-tui-update-models/scripts/dump-model-catalog.mjs +314 -0
  8. package/README.md +154 -27
  9. package/lib/agent/present.d.ts.map +1 -1
  10. package/lib/agent/present.js +2 -1
  11. package/lib/agent/present.js.map +1 -1
  12. package/lib/cards/composition.d.ts +68 -0
  13. package/lib/cards/composition.d.ts.map +1 -0
  14. package/lib/cards/composition.js +117 -0
  15. package/lib/cards/composition.js.map +1 -0
  16. package/lib/cards/presenter.d.ts +23 -0
  17. package/lib/cards/presenter.d.ts.map +1 -0
  18. package/lib/cards/presenter.js +394 -0
  19. package/lib/cards/presenter.js.map +1 -0
  20. package/lib/cards/preview.d.ts +42 -0
  21. package/lib/cards/preview.d.ts.map +1 -0
  22. package/lib/cards/preview.js +51 -0
  23. package/lib/cards/preview.js.map +1 -0
  24. package/lib/cards.d.ts +12 -120
  25. package/lib/cards.d.ts.map +1 -1
  26. package/lib/cards.js +4 -556
  27. package/lib/cards.js.map +1 -1
  28. package/lib/compat/section.d.ts +29 -0
  29. package/lib/compat/section.d.ts.map +1 -0
  30. package/lib/compat/section.js +82 -0
  31. package/lib/compat/section.js.map +1 -0
  32. package/lib/config.d.ts +27 -2
  33. package/lib/config.d.ts.map +1 -1
  34. package/lib/config.js +61 -1
  35. package/lib/config.js.map +1 -1
  36. package/lib/contracts.d.ts +28 -0
  37. package/lib/contracts.d.ts.map +1 -1
  38. package/lib/fold-cursor.d.ts +8 -0
  39. package/lib/fold-cursor.d.ts.map +1 -1
  40. package/lib/fold-cursor.js +18 -0
  41. package/lib/fold-cursor.js.map +1 -1
  42. package/lib/gates/question-card.d.ts +77 -0
  43. package/lib/gates/question-card.d.ts.map +1 -0
  44. package/lib/gates/question-card.js +113 -0
  45. package/lib/gates/question-card.js.map +1 -0
  46. package/lib/gates/questions.d.ts +127 -0
  47. package/lib/gates/questions.d.ts.map +1 -0
  48. package/lib/gates/questions.js +418 -0
  49. package/lib/gates/questions.js.map +1 -0
  50. package/lib/gates.d.ts +1 -113
  51. package/lib/gates.d.ts.map +1 -1
  52. package/lib/gates.js +1 -484
  53. package/lib/gates.js.map +1 -1
  54. package/lib/herdr/client.d.ts +8 -0
  55. package/lib/herdr/client.d.ts.map +1 -1
  56. package/lib/herdr/client.js +13 -1
  57. package/lib/herdr/client.js.map +1 -1
  58. package/lib/herdr/constants.d.ts +18 -0
  59. package/lib/herdr/constants.d.ts.map +1 -1
  60. package/lib/herdr/constants.js +18 -0
  61. package/lib/herdr/constants.js.map +1 -1
  62. package/lib/herdr/reporter.d.ts +20 -4
  63. package/lib/herdr/reporter.d.ts.map +1 -1
  64. package/lib/herdr/reporter.js +70 -20
  65. package/lib/herdr/reporter.js.map +1 -1
  66. package/lib/herdr/state.d.ts +10 -0
  67. package/lib/herdr/state.d.ts.map +1 -1
  68. package/lib/herdr/state.js +21 -4
  69. package/lib/herdr/state.js.map +1 -1
  70. package/lib/index.d.ts +1 -0
  71. package/lib/index.d.ts.map +1 -1
  72. package/lib/index.js +348 -2602
  73. package/lib/index.js.map +1 -1
  74. package/lib/input/action-catalog.d.ts +74 -0
  75. package/lib/input/action-catalog.d.ts.map +1 -0
  76. package/lib/input/action-catalog.js +137 -0
  77. package/lib/input/action-catalog.js.map +1 -0
  78. package/lib/input/actions.d.ts +5 -81
  79. package/lib/input/actions.d.ts.map +1 -1
  80. package/lib/input/actions.js +4 -625
  81. package/lib/input/actions.js.map +1 -1
  82. package/lib/input/completion.d.ts +1 -1
  83. package/lib/input/completion.d.ts.map +1 -1
  84. package/lib/input/completion.js +2 -1
  85. package/lib/input/completion.js.map +1 -1
  86. package/lib/input/file-index.d.ts +40 -0
  87. package/lib/input/file-index.d.ts.map +1 -0
  88. package/lib/input/file-index.js +141 -0
  89. package/lib/input/file-index.js.map +1 -0
  90. package/lib/input/file-search.d.ts +1 -38
  91. package/lib/input/file-search.d.ts.map +1 -1
  92. package/lib/input/file-search.js +1 -487
  93. package/lib/input/file-search.js.map +1 -1
  94. package/lib/input/key-press.d.ts +57 -0
  95. package/lib/input/key-press.d.ts.map +1 -0
  96. package/lib/input/key-press.js +218 -0
  97. package/lib/input/key-press.js.map +1 -0
  98. package/lib/input/keymap-conflicts.d.ts +59 -0
  99. package/lib/input/keymap-conflicts.d.ts.map +1 -0
  100. package/lib/input/keymap-conflicts.js +292 -0
  101. package/lib/input/keymap-conflicts.js.map +1 -0
  102. package/lib/input/keymap-settings.js +1 -1
  103. package/lib/input/keymap-settings.js.map +1 -1
  104. package/lib/input/keymap.d.ts.map +1 -1
  105. package/lib/input/keymap.js +3 -1
  106. package/lib/input/keymap.js.map +1 -1
  107. package/lib/input/workspace-files.d.ts +10 -0
  108. package/lib/input/workspace-files.d.ts.map +1 -0
  109. package/lib/input/workspace-files.js +359 -0
  110. package/lib/input/workspace-files.js.map +1 -0
  111. package/lib/install-skills.d.ts +39 -0
  112. package/lib/install-skills.d.ts.map +1 -0
  113. package/lib/install-skills.js +273 -0
  114. package/lib/install-skills.js.map +1 -0
  115. package/lib/keys-command.d.ts +2 -1
  116. package/lib/keys-command.d.ts.map +1 -1
  117. package/lib/keys-command.js +2 -1
  118. package/lib/keys-command.js.map +1 -1
  119. package/lib/model-list.d.ts +37 -0
  120. package/lib/model-list.d.ts.map +1 -0
  121. package/lib/model-list.js +60 -0
  122. package/lib/model-list.js.map +1 -0
  123. package/lib/startup.d.ts +8 -0
  124. package/lib/startup.d.ts.map +1 -1
  125. package/lib/startup.js +120 -24
  126. package/lib/startup.js.map +1 -1
  127. package/lib/subagents.d.ts +7 -3
  128. package/lib/subagents.d.ts.map +1 -1
  129. package/lib/subagents.js +28 -4
  130. package/lib/subagents.js.map +1 -1
  131. package/lib/surface/appearance-preferences.d.ts +26 -0
  132. package/lib/surface/appearance-preferences.d.ts.map +1 -0
  133. package/lib/surface/appearance-preferences.js +139 -0
  134. package/lib/surface/appearance-preferences.js.map +1 -0
  135. package/lib/surface/appearance.d.ts +87 -0
  136. package/lib/surface/appearance.d.ts.map +1 -0
  137. package/lib/surface/appearance.js +353 -0
  138. package/lib/surface/appearance.js.map +1 -0
  139. package/lib/surface/background-work.d.ts +51 -0
  140. package/lib/surface/background-work.d.ts.map +1 -0
  141. package/lib/surface/background-work.js +172 -0
  142. package/lib/surface/background-work.js.map +1 -0
  143. package/lib/surface/commands.d.ts +82 -0
  144. package/lib/surface/commands.d.ts.map +1 -0
  145. package/lib/surface/commands.js +370 -0
  146. package/lib/surface/commands.js.map +1 -0
  147. package/lib/surface/modal-input.d.ts +59 -0
  148. package/lib/surface/modal-input.d.ts.map +1 -0
  149. package/lib/surface/modal-input.js +243 -0
  150. package/lib/surface/modal-input.js.map +1 -0
  151. package/lib/surface/model-choice.d.ts +54 -0
  152. package/lib/surface/model-choice.d.ts.map +1 -0
  153. package/lib/surface/model-choice.js +325 -0
  154. package/lib/surface/model-choice.js.map +1 -0
  155. package/lib/surface/preset-choice.d.ts +78 -0
  156. package/lib/surface/preset-choice.d.ts.map +1 -0
  157. package/lib/surface/preset-choice.js +163 -0
  158. package/lib/surface/preset-choice.js.map +1 -0
  159. package/lib/surface/prompt-input.d.ts +97 -0
  160. package/lib/surface/prompt-input.d.ts.map +1 -0
  161. package/lib/surface/prompt-input.js +215 -0
  162. package/lib/surface/prompt-input.js.map +1 -0
  163. package/lib/surface/prompt-memory.d.ts +76 -0
  164. package/lib/surface/prompt-memory.d.ts.map +1 -0
  165. package/lib/surface/prompt-memory.js +167 -0
  166. package/lib/surface/prompt-memory.js.map +1 -0
  167. package/lib/surface/session-lifecycle.d.ts +120 -0
  168. package/lib/surface/session-lifecycle.d.ts.map +1 -0
  169. package/lib/surface/session-lifecycle.js +324 -0
  170. package/lib/surface/session-lifecycle.js.map +1 -0
  171. package/lib/surface/session-picker.d.ts +39 -0
  172. package/lib/surface/session-picker.d.ts.map +1 -0
  173. package/lib/surface/session-picker.js +107 -0
  174. package/lib/surface/session-picker.js.map +1 -0
  175. package/lib/surface/session-view.d.ts +86 -0
  176. package/lib/surface/session-view.d.ts.map +1 -0
  177. package/lib/surface/session-view.js +215 -0
  178. package/lib/surface/session-view.js.map +1 -0
  179. package/lib/surface/staged-turns.d.ts +81 -0
  180. package/lib/surface/staged-turns.d.ts.map +1 -0
  181. package/lib/surface/staged-turns.js +206 -0
  182. package/lib/surface/staged-turns.js.map +1 -0
  183. package/lib/surface/terminal-lifecycle.d.ts +76 -0
  184. package/lib/surface/terminal-lifecycle.d.ts.map +1 -0
  185. package/lib/surface/terminal-lifecycle.js +222 -0
  186. package/lib/surface/terminal-lifecycle.js.map +1 -0
  187. package/lib/terminal-text/scan.d.ts +44 -0
  188. package/lib/terminal-text/scan.d.ts.map +1 -0
  189. package/lib/terminal-text/scan.js +195 -0
  190. package/lib/terminal-text/scan.js.map +1 -0
  191. package/lib/terminal-text/sgr.d.ts +32 -0
  192. package/lib/terminal-text/sgr.d.ts.map +1 -0
  193. package/lib/terminal-text/sgr.js +203 -0
  194. package/lib/terminal-text/sgr.js.map +1 -0
  195. package/lib/terminal-text.d.ts +1 -1
  196. package/lib/terminal-text.d.ts.map +1 -1
  197. package/lib/terminal-text.js +2 -379
  198. package/lib/terminal-text.js.map +1 -1
  199. package/lib/theme-command.js +2 -1
  200. package/lib/theme-command.js.map +1 -1
  201. package/lib/theme-defaults.d.ts +64 -0
  202. package/lib/theme-defaults.d.ts.map +1 -0
  203. package/lib/theme-defaults.js +248 -0
  204. package/lib/theme-defaults.js.map +1 -0
  205. package/lib/theme-files.d.ts +1 -1
  206. package/lib/theme-files.d.ts.map +1 -1
  207. package/lib/theme-files.js +1 -1
  208. package/lib/theme-files.js.map +1 -1
  209. package/lib/theme-resolver.d.ts +36 -0
  210. package/lib/theme-resolver.d.ts.map +1 -0
  211. package/lib/theme-resolver.js +112 -0
  212. package/lib/theme-resolver.js.map +1 -0
  213. package/lib/theme-schema.d.ts.map +1 -1
  214. package/lib/theme-schema.js +3 -2
  215. package/lib/theme-schema.js.map +1 -1
  216. package/lib/theme-settings.d.ts +5 -7
  217. package/lib/theme-settings.d.ts.map +1 -1
  218. package/lib/theme-settings.js +37 -21
  219. package/lib/theme-settings.js.map +1 -1
  220. package/lib/theme-tokens.d.ts +0 -89
  221. package/lib/theme-tokens.d.ts.map +1 -1
  222. package/lib/theme-tokens.js +0 -356
  223. package/lib/theme-tokens.js.map +1 -1
  224. package/lib/theme.d.ts +1 -1
  225. package/lib/theme.d.ts.map +1 -1
  226. package/lib/theme.js +2 -1
  227. package/lib/theme.js.map +1 -1
  228. package/lib/tool-display.d.ts.map +1 -1
  229. package/lib/tool-display.js +2 -1
  230. package/lib/tool-display.js.map +1 -1
  231. package/lib/transcript/message-content.d.ts +21 -0
  232. package/lib/transcript/message-content.d.ts.map +1 -0
  233. package/lib/transcript/message-content.js +61 -0
  234. package/lib/transcript/message-content.js.map +1 -0
  235. package/lib/transcript/tool-calls.d.ts +106 -0
  236. package/lib/transcript/tool-calls.d.ts.map +1 -0
  237. package/lib/transcript/tool-calls.js +254 -0
  238. package/lib/transcript/tool-calls.js.map +1 -0
  239. package/lib/transcript.d.ts +14 -55
  240. package/lib/transcript.d.ts.map +1 -1
  241. package/lib/transcript.js +31 -253
  242. package/lib/transcript.js.map +1 -1
  243. package/lib/ui/dock.d.ts +14 -2
  244. package/lib/ui/dock.d.ts.map +1 -1
  245. package/lib/ui/dock.js +52 -10
  246. package/lib/ui/dock.js.map +1 -1
  247. package/lib/ui/editor.d.ts.map +1 -1
  248. package/lib/ui/editor.js +2 -1
  249. package/lib/ui/editor.js.map +1 -1
  250. package/lib/ui/keymap-picker.d.ts +2 -1
  251. package/lib/ui/keymap-picker.d.ts.map +1 -1
  252. package/lib/ui/keymap-picker.js +1 -0
  253. package/lib/ui/keymap-picker.js.map +1 -1
  254. package/lib/ui/view/gate-card.d.ts +21 -0
  255. package/lib/ui/view/gate-card.d.ts.map +1 -0
  256. package/lib/ui/view/gate-card.js +104 -0
  257. package/lib/ui/view/gate-card.js.map +1 -0
  258. package/lib/ui/view/tool-card.d.ts +149 -0
  259. package/lib/ui/view/tool-card.d.ts.map +1 -0
  260. package/lib/ui/view/tool-card.js +429 -0
  261. package/lib/ui/view/tool-card.js.map +1 -0
  262. package/lib/ui/view/transcript-message.d.ts +62 -0
  263. package/lib/ui/view/transcript-message.d.ts.map +1 -0
  264. package/lib/ui/view/transcript-message.js +158 -0
  265. package/lib/ui/view/transcript-message.js.map +1 -0
  266. package/lib/ui/view.d.ts +105 -223
  267. package/lib/ui/view.d.ts.map +1 -1
  268. package/lib/ui/view.js +132 -679
  269. package/lib/ui/view.js.map +1 -1
  270. package/package.json +3 -1
  271. package/themes/violet-orbit.yaml +7 -5
@@ -0,0 +1,218 @@
1
+ import { Key, isKittyProtocolActive, matchesKey, setKittyProtocolActive } from '@earendil-works/pi-tui';
2
+ /**
3
+ * How a key press is spelled, matched, and compared across terminals.
4
+ *
5
+ * A binding is durable, but the bytes that reach the surface are not: this
6
+ * layer owns the equivalences that make one press equal to another, separately
7
+ * from the table of actions those presses resolve to.
8
+ */
9
+ /** A key the tty answers before the application sees it. */
10
+ export const TERMINAL_OWNED_KEYS = ['ctrl+q'];
11
+ /** The key a plain press of Return arrives as. */
12
+ export const ENTER_KEY = 'enter';
13
+ /**
14
+ * The named key a control character is the same press as.
15
+ *
16
+ * A terminal that reports no modifiers sends the control byte itself, so the
17
+ * library matches Ctrl+M where it matches Return and Ctrl+I where it matches
18
+ * Tab. Two rows that read one press have to be refused under the same name
19
+ * however they spell it, or the reader keeps a binding their terminal answers
20
+ * with a different action.
21
+ */
22
+ const CONTROL_ALIASES = {
23
+ 'ctrl+m': ENTER_KEY,
24
+ 'ctrl+i': 'tab',
25
+ 'ctrl+h': 'backspace',
26
+ 'ctrl+[': 'escape',
27
+ };
28
+ /** The press one key names, folded across the spellings a bare terminal cannot tell apart. */
29
+ export function pressOf(key) {
30
+ return CONTROL_ALIASES[key] ?? key;
31
+ }
32
+ /** The modifiers a key id may carry, in the order a canonical id writes them. */
33
+ const MODIFIER_ORDER = ['ctrl', 'shift', 'alt', 'super'];
34
+ const MODIFIERS = new Set(MODIFIER_ORDER);
35
+ /** Spellings the library matches as the same key, folded so they cannot claim two rows. */
36
+ const SYNONYMS = { esc: 'escape', return: ENTER_KEY };
37
+ /** A bare letter or digit needs no name to be a key. */
38
+ const ALPHANUMERIC = /^[a-z0-9]$/u;
39
+ /**
40
+ * The base keys the library can name, lowercased, mapped to the library's own
41
+ * spelling.
42
+ *
43
+ * Read from the library's own helper object: a key the library adds or renames
44
+ * is validated and printed the way the library spells it, with no second table
45
+ * to drift.
46
+ */
47
+ const BASE_KEYS = (() => {
48
+ const keys = new Map();
49
+ for (const value of Object.values(Key)) {
50
+ if (typeof value === 'string')
51
+ keys.set(value.toLowerCase(), value);
52
+ }
53
+ for (const [alias, canonical] of Object.entries(SYNONYMS))
54
+ keys.set(alias, canonical);
55
+ return keys;
56
+ })();
57
+ /**
58
+ * One written key as the library's own canonical id, or undefined when the
59
+ * terminal could never report it as a single press.
60
+ *
61
+ * Modifiers are folded into one order and the key is folded to the library's
62
+ * spelling, because two spellings of one key would otherwise let two actions
63
+ * claim the same press.
64
+ */
65
+ export function normalizeKey(raw) {
66
+ const parts = raw.trim().toLowerCase().split('+');
67
+ const key = parts.pop();
68
+ if (key === undefined || key === '')
69
+ return undefined;
70
+ const base = BASE_KEYS.get(key);
71
+ if (base === undefined && !ALPHANUMERIC.test(key))
72
+ return undefined;
73
+ const written = [];
74
+ const seen = new Set();
75
+ for (const part of parts) {
76
+ // A repeat is not a chord the terminal reports, so it is a typo rather than a binding.
77
+ if (!MODIFIERS.has(part) || seen.has(part))
78
+ return undefined;
79
+ seen.add(part);
80
+ written.push(part);
81
+ }
82
+ written.sort((left, right) => MODIFIER_ORDER.indexOf(left) - MODIFIER_ORDER.indexOf(right));
83
+ return [...written, base ?? key].join('+');
84
+ }
85
+ /**
86
+ * Whether the library can match a key at all.
87
+ *
88
+ * The library answers no modifier on Escape or on a function key, because no
89
+ * terminal reports those, and no modifier beyond plain, shift, and control on
90
+ * Clear. A binding there would be a key the reader could never press.
91
+ */
92
+ export function isPressable(key) {
93
+ const parts = key.split('+');
94
+ const base = parts.pop() ?? '';
95
+ const count = parts.length;
96
+ if (base === 'escape' && count > 0)
97
+ return false;
98
+ if (/^f([1-9]|1[0-2])$/u.test(base) && count > 0)
99
+ return false;
100
+ if (base === 'clear' && (count > 1 || parts.includes('alt') || parts.includes('super')))
101
+ return false;
102
+ return true;
103
+ }
104
+ /** Whether a key is one modifier chord on one letter or digit, the only shape a chord starter may take. */
105
+ export function isSimpleChord(key) {
106
+ const base = key.split('+').pop() ?? '';
107
+ return key.includes('+') && /^[a-z0-9]$/u.test(base);
108
+ }
109
+ /**
110
+ * Whether a key is a character the reader types, which only an armed layer may take.
111
+ *
112
+ * Shift is not a way out of this: a terminal reports a capital letter as the
113
+ * shifted letter, so shift+c types a C wherever c does.
114
+ */
115
+ export function isBareCharacter(key) {
116
+ const bare = key.startsWith('shift+') ? key.slice('shift+'.length) : key;
117
+ if (bare.includes('+'))
118
+ return false;
119
+ return bare === 'space' || (bare.length === 1 && bare >= ' ');
120
+ }
121
+ /** How many single-byte presses a probe walks: every byte a terminal can send on its own. */
122
+ const PROBE_BYTES = 0x80;
123
+ /**
124
+ * The sequences one press can arrive as, so an overlap is read from the matcher.
125
+ *
126
+ * The library is the only authority on which sequences are one press, and its
127
+ * reading is looser than a spelling table could say: a bare terminal reports
128
+ * Return for both Enter and Ctrl+M, one control byte carries Ctrl+- and Ctrl+_
129
+ * alike, and escape-prefixed bytes reach more than one alt key — an escape and a
130
+ * letter answers Alt+Up as well as Alt+P. Both protocol modes are read over the
131
+ * same bytes, because a terminal without the protocol folds spellings together
132
+ * that one with it keeps apart. The answer is cached: which sequences reach a key
133
+ * is a property of the key rather than of the map.
134
+ */
135
+ const PRESS_PROBES = Array.from({ length: PROBE_BYTES }, (_, code) => String.fromCharCode(code)).flatMap(byte => [byte, `\u001b${byte}`]);
136
+ /** One sequence named so two rows can agree on it without carrying the bytes. */
137
+ function spellingOf(sequence) {
138
+ return `seq:${[...sequence].map(character => character.charCodeAt(0).toString(16)).join('-')}`;
139
+ }
140
+ const pressSpellings = new Map();
141
+ /** The sequences that reach a key in either protocol mode, or its own id when none does. */
142
+ function matchPresses(key) {
143
+ const cached = pressSpellings.get(key);
144
+ if (cached !== undefined)
145
+ return cached;
146
+ const spelled = new Set();
147
+ const wasKitty = isKittyProtocolActive();
148
+ try {
149
+ for (const kitty of [false, true]) {
150
+ setKittyProtocolActive(kitty);
151
+ for (const probe of PRESS_PROBES) {
152
+ if (matchesKey(probe, key))
153
+ spelled.add(spellingOf(probe));
154
+ }
155
+ }
156
+ }
157
+ finally {
158
+ setKittyProtocolActive(wasKitty);
159
+ }
160
+ const answer = spelled.size === 0 ? [`key:${key}`] : [...spelled];
161
+ pressSpellings.set(key, answer);
162
+ return answer;
163
+ }
164
+ /**
165
+ * Group the rows one terminal sequence reaches, so a group is a press that cannot
166
+ * be split between them.
167
+ */
168
+ export function pressOverlaps(rows) {
169
+ const rowAt = (index) => rows[index];
170
+ const reached = new Map();
171
+ rows.forEach((row, index) => {
172
+ for (const spelling of matchPresses(row.key)) {
173
+ const found = reached.get(spelling) ?? [];
174
+ if (!found.includes(index))
175
+ found.push(index);
176
+ reached.set(spelling, found);
177
+ }
178
+ });
179
+ const parent = rows.map((_, index) => index);
180
+ const find = (index) => {
181
+ const kept = parent[index] ?? index;
182
+ if (kept === index)
183
+ return index;
184
+ const root = find(kept);
185
+ parent[index] = root;
186
+ return root;
187
+ };
188
+ for (const indexes of reached.values()) {
189
+ for (const index of indexes.slice(1)) {
190
+ const left = find(indexes[0]);
191
+ const right = find(index);
192
+ if (left !== right)
193
+ parent[right] = left;
194
+ }
195
+ }
196
+ const groups = new Map();
197
+ rows.forEach((_, index) => {
198
+ const root = find(index);
199
+ const found = groups.get(root) ?? [];
200
+ found.push(index);
201
+ groups.set(root, found);
202
+ });
203
+ const overlaps = [];
204
+ for (const found of groups.values()) {
205
+ const ids = [...new Set(found.map(index => rowAt(index).id))].sort();
206
+ if (ids.length < 2)
207
+ continue;
208
+ // A spelling counts as shared only when two different rows read it: one row
209
+ // may hold two spellings of the same press (Return beside Ctrl+M), which is
210
+ // the reader saying one thing rather than a press two rows fight over.
211
+ const spellings = [...new Set(found.flatMap(index => matchPresses(rowAt(index).key)))]
212
+ .filter(spelling => new Set((reached.get(spelling) ?? []).filter(index => found.includes(index)).map(index => rowAt(index).id)).size > 1)
213
+ .sort();
214
+ overlaps.push({ ids, key: rowAt(found[0]).key, spellings });
215
+ }
216
+ return overlaps;
217
+ }
218
+ //# sourceMappingURL=key-press.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-press.js","sourceRoot":"","sources":["../../src/input/key-press.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import { type KeyId } from '@earendil-works/pi-tui';
2
+ /**
3
+ * Which bindings a keymap must refuse, and why.
4
+ *
5
+ * A reader may layer their own keys over the shipped ones, so the surface has
6
+ * to decide which collisions are load-bearing rather than merely untidy; that
7
+ * policy is kept out of both the action table and the press spellings it judges.
8
+ */
9
+ /**
10
+ * Refuse two actions of one layer a terminal cannot tell apart.
11
+ *
12
+ * Layers are checked apart because sharing across them is the design: the chord
13
+ * layer takes a key before the surface answers it, and the surface takes one
14
+ * before the library's editor sees it. Within one layer a shared press is an
15
+ * action the reader could never reach — and the surface answers these layers
16
+ * itself, first row first, so two rows sharing one byte is one row that loses.
17
+ */
18
+ export declare function refuseMatcherClashes(effective: Readonly<Record<string, readonly KeyId[]>>): void;
19
+ /**
20
+ * Refuse two prompt rows claiming one press.
21
+ *
22
+ * The bar is the one layer the library's matcher does not decide: Return is
23
+ * answered in the bar itself, which reads a line feed as send when the reader
24
+ * bound it and as a line otherwise. Its rows are therefore compared by the
25
+ * press they arrive as rather than by every byte the matcher folds, so moving
26
+ * send onto Ctrl+J stays possible.
27
+ */
28
+ export declare function refusePromptClashes(effective: Readonly<Record<string, readonly KeyId[]>>): void;
29
+ /**
30
+ * Refuse a chord starter that would take a key the surface already answers.
31
+ *
32
+ * The prefix is consumed before anything else looks at the press, so a starter
33
+ * that is also a surface key would not shadow that action while a chord is
34
+ * armed: it would remove it for the whole session.
35
+ */
36
+ export declare function refusePrefixTakingKeys(effective: Readonly<Record<string, readonly KeyId[]>>): void;
37
+ /**
38
+ * Refuse two rows the library reads on one press.
39
+ *
40
+ * The library's own manager reports a clash between rows the reader wrote and
41
+ * nothing else, which would let a new binding quietly take a key from a row the
42
+ * reader never touched. Rows are compared twice: by the press they were written
43
+ * as, which is what the bar's own rows are matched by, and by every sequence the
44
+ * matcher folds, which is what catches one control byte carrying two spellings.
45
+ * The overlaps the library itself ships are left alone: those are rows it
46
+ * already knows how to tell apart.
47
+ */
48
+ export declare function refuseLibraryClashes(effective: Readonly<Record<string, readonly KeyId[]>>): void;
49
+ /**
50
+ * Refuse a pair the viewport answers before the row it was written on.
51
+ *
52
+ * A key the alternate screen's listener reads never reaches the surface: the
53
+ * press scrolls or searches instead, and the binding only looks alive while an
54
+ * overlay defers the viewport. Either side of the pair can be the one the
55
+ * reader wrote, and both are a binding that does nothing, so both are refused
56
+ * apart from the overlap the library already ships.
57
+ */
58
+ export declare function refuseViewportTakingKeys(effective: Readonly<Record<string, readonly KeyId[]>>, written: ReadonlySet<string>): void;
59
+ //# sourceMappingURL=keymap-conflicts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keymap-conflicts.d.ts","sourceRoot":"","sources":["../../src/input/keymap-conflicts.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAEnD;;;;;;GAMG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC,CAAC,GAAG,IAAI,CAYhG;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC,CAAC,GAAG,IAAI,CAa/F;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC,CAAC,GAAG,IAAI,CAWlG;AAsGD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC,CAAC,GAAG,IAAI,CAWhG;AAoED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAkBlI"}
@@ -0,0 +1,292 @@
1
+ import { ENTER_KEY, pressOf, pressOverlaps } from "./key-press.js";
2
+ import { ACTION_CATALOG, actionOf, shippedKeys } from "./action-catalog.js";
3
+ import {} from '@earendil-works/pi-tui';
4
+ /**
5
+ * Which bindings a keymap must refuse, and why.
6
+ *
7
+ * A reader may layer their own keys over the shipped ones, so the surface has
8
+ * to decide which collisions are load-bearing rather than merely untidy; that
9
+ * policy is kept out of both the action table and the press spellings it judges.
10
+ */
11
+ /**
12
+ * Refuse two actions of one layer a terminal cannot tell apart.
13
+ *
14
+ * Layers are checked apart because sharing across them is the design: the chord
15
+ * layer takes a key before the surface answers it, and the surface takes one
16
+ * before the library's editor sees it. Within one layer a shared press is an
17
+ * action the reader could never reach — and the surface answers these layers
18
+ * itself, first row first, so two rows sharing one byte is one row that loses.
19
+ */
20
+ export function refuseMatcherClashes(effective) {
21
+ for (const layer of ['surface', 'chord', 'gate', 'question', 'picker']) {
22
+ const rows = [];
23
+ for (const action of ACTION_CATALOG) {
24
+ if (action.layer !== layer)
25
+ continue;
26
+ for (const key of effective[action.id] ?? [])
27
+ rows.push({ id: action.id, key });
28
+ }
29
+ const overlap = pressOverlaps(rows)[0];
30
+ if (overlap !== undefined) {
31
+ throw new Error(`key "${overlap.key}" is bound to both ${overlap.ids.join(' and ')}`);
32
+ }
33
+ }
34
+ }
35
+ /**
36
+ * Refuse two prompt rows claiming one press.
37
+ *
38
+ * The bar is the one layer the library's matcher does not decide: Return is
39
+ * answered in the bar itself, which reads a line feed as send when the reader
40
+ * bound it and as a line otherwise. Its rows are therefore compared by the
41
+ * press they arrive as rather than by every byte the matcher folds, so moving
42
+ * send onto Ctrl+J stays possible.
43
+ */
44
+ export function refusePromptClashes(effective) {
45
+ const owner = new Map();
46
+ for (const action of ACTION_CATALOG) {
47
+ if (action.layer !== 'prompt')
48
+ continue;
49
+ for (const key of effective[action.id] ?? []) {
50
+ const press = pressOf(key);
51
+ const taken = owner.get(press);
52
+ if (taken !== undefined && taken !== action.id) {
53
+ throw new Error(`key "${key}" is bound to both ${taken} and ${action.id}`);
54
+ }
55
+ owner.set(press, action.id);
56
+ }
57
+ }
58
+ }
59
+ /**
60
+ * Refuse a chord starter that would take a key the surface already answers.
61
+ *
62
+ * The prefix is consumed before anything else looks at the press, so a starter
63
+ * that is also a surface key would not shadow that action while a chord is
64
+ * armed: it would remove it for the whole session.
65
+ */
66
+ export function refusePrefixTakingKeys(effective) {
67
+ const owners = ACTION_CATALOG.filter(action => action.layer === 'surface' || action.layer === 'prompt');
68
+ for (const key of effective['chord.prefix'] ?? []) {
69
+ for (const owner of owners) {
70
+ // A prefix is consumed before anything else looks at the press, so it takes
71
+ // the key from a row that reads the same press under another spelling too.
72
+ if ((effective[owner.id] ?? []).some(owned => pressOf(owned) === pressOf(key))) {
73
+ throw new Error(`key "${key}" as chord.prefix would take it from ${owner.id}`);
74
+ }
75
+ }
76
+ }
77
+ }
78
+ /**
79
+ * The rows the library reads, as one map.
80
+ *
81
+ * Every library action appears, wherever the key came from, because a moved row
82
+ * and an untouched default are read by the same matcher. The bar's two actions
83
+ * are here as well: they are installed even when the reader never wrote them, so
84
+ * they share the keyboard with the library's own rows.
85
+ */
86
+ function libraryRows(effective) {
87
+ const rows = {};
88
+ for (const action of ACTION_CATALOG) {
89
+ if (action.layer === 'library')
90
+ rows[action.id] = [...(effective[action.id] ?? [])];
91
+ }
92
+ rows['tui.input.submit'] = [...(effective['prompt.submit'] ?? [])];
93
+ // Enter is left out of the installation for one reason and kept here for the
94
+ // opposite one: the library reads that press as a line break before it looks
95
+ // for this row, so a library row that took Return would be the row that never
96
+ // runs while the bar still answers it — as a line, or as send.
97
+ const line = effective['prompt.newLine'] ?? [];
98
+ rows['tui.input.newLine'] = line.includes(ENTER_KEY) ? [...line] : line.filter(key => key !== ENTER_KEY);
99
+ return rows;
100
+ }
101
+ /** The same rows as they read before the reader wrote anything. */
102
+ function shippedLibraryRows() {
103
+ const defaults = new Map(ACTION_CATALOG.map(action => [action.id, action.defaultKeys]));
104
+ const rows = {};
105
+ for (const action of ACTION_CATALOG) {
106
+ if (action.layer === 'library')
107
+ rows[action.id] = [...shippedKeys(action)];
108
+ }
109
+ rows['tui.input.submit'] = [...(defaults.get('prompt.submit') ?? [])];
110
+ rows['tui.input.newLine'] = [...(defaults.get('prompt.newLine') ?? [])];
111
+ return rows;
112
+ }
113
+ /**
114
+ * The component that reads a library row.
115
+ *
116
+ * Two rows one component reads can fight over a press. Rows of different
117
+ * components meet only where the library already ships the overlap, such as the
118
+ * viewport's page keys shadowing the editor's, so those are its business rather
119
+ * than a clash to refuse.
120
+ */
121
+ function libraryComponent(id) {
122
+ if (id.startsWith('tui.editor.') || id.startsWith('tui.input.'))
123
+ return 'editor';
124
+ if (id.startsWith('tui.select.'))
125
+ return 'select';
126
+ if (id.startsWith('tui.altScreen.'))
127
+ return 'viewport';
128
+ return 'other';
129
+ }
130
+ /** Every press one component reads on more than one row, with the rows that read it. */
131
+ function sharedPresses(rows) {
132
+ const owners = new Map();
133
+ for (const [id, keys] of Object.entries(rows)) {
134
+ const component = libraryComponent(id);
135
+ for (const key of keys) {
136
+ const press = pressOf(key);
137
+ const byComponent = owners.get(press) ?? new Map();
138
+ const found = byComponent.get(component) ?? [];
139
+ if (!found.includes(id))
140
+ found.push(id);
141
+ byComponent.set(component, found);
142
+ owners.set(press, byComponent);
143
+ }
144
+ }
145
+ const shared = new Map();
146
+ for (const [press, byComponent] of owners) {
147
+ for (const ids of byComponent.values()) {
148
+ if (ids.length > 1)
149
+ shared.set(press, [...ids].sort());
150
+ }
151
+ }
152
+ return shared;
153
+ }
154
+ /**
155
+ * The overlaps the library's own rows carry, one component at a time.
156
+ *
157
+ * The bar's own two rows are left out: they are read by the bar before the
158
+ * library's matcher runs, so a line feed is send when the reader bound it and a
159
+ * line otherwise, and comparing them by every sequence the matcher folds would
160
+ * forbid a choice the bar can honour.
161
+ */
162
+ function libraryOverlaps(rows) {
163
+ const overlaps = [];
164
+ for (const component of ['editor', 'select', 'viewport', 'other']) {
165
+ const own = [];
166
+ for (const [id, keys] of Object.entries(rows)) {
167
+ if (id === 'tui.input.submit' || id === 'tui.input.newLine')
168
+ continue;
169
+ if (libraryComponent(id) !== component)
170
+ continue;
171
+ for (const key of keys)
172
+ own.push({ id, key });
173
+ }
174
+ overlaps.push(...pressOverlaps(own));
175
+ }
176
+ return overlaps;
177
+ }
178
+ /** What makes two overlaps the same fight: the rows, and the sequences they share. */
179
+ function overlapIdentity(overlap) {
180
+ return `${overlap.ids.join(',')}\u0000${overlap.spellings.join('|')}`;
181
+ }
182
+ /**
183
+ * Refuse two rows the library reads on one press.
184
+ *
185
+ * The library's own manager reports a clash between rows the reader wrote and
186
+ * nothing else, which would let a new binding quietly take a key from a row the
187
+ * reader never touched. Rows are compared twice: by the press they were written
188
+ * as, which is what the bar's own rows are matched by, and by every sequence the
189
+ * matcher folds, which is what catches one control byte carrying two spellings.
190
+ * The overlaps the library itself ships are left alone: those are rows it
191
+ * already knows how to tell apart.
192
+ */
193
+ export function refuseLibraryClashes(effective) {
194
+ const shipped = new Set([...sharedPresses(shippedLibraryRows())].map(([press, ids]) => `${press}\u0000${ids.join(',')}`));
195
+ for (const [press, ids] of sharedPresses(libraryRows(effective))) {
196
+ if (shipped.has(`${press}\u0000${ids.join(',')}`))
197
+ continue;
198
+ throw new Error(`key "${press}" is bound to both ${ids.join(' and ')}; move one of them or pick another key`);
199
+ }
200
+ const shippedOverlaps = new Set(libraryOverlaps(shippedLibraryRows()).map(overlapIdentity));
201
+ for (const overlap of libraryOverlaps(libraryRows(effective))) {
202
+ if (shippedOverlaps.has(overlapIdentity(overlap)))
203
+ continue;
204
+ throw new Error(`key "${overlap.key}" is bound to both ${overlap.ids.join(' and ')}; move one of them or pick another key`);
205
+ }
206
+ }
207
+ /**
208
+ * The library rows that read a press before the surface's own listener runs.
209
+ *
210
+ * The alternate screen registers its viewport listener while the terminal is
211
+ * built, so its keys arrive before a listener the surface adds later. These rows
212
+ * are read without asking whether an overlay holds the keyboard; the search
213
+ * overlay's own next, previous, and close keys are left out because they wait
214
+ * for it and would let an overlay-scoped surface key through.
215
+ */
216
+ const VIEWPORT_FIRST_ROWS = [
217
+ 'tui.altScreen.search',
218
+ 'tui.altScreen.pageUp',
219
+ 'tui.altScreen.pageDown',
220
+ 'tui.altScreen.halfPageUp',
221
+ 'tui.altScreen.halfPageDown',
222
+ 'tui.altScreen.lineUp',
223
+ 'tui.altScreen.lineDown',
224
+ 'tui.altScreen.previousPrompt',
225
+ 'tui.altScreen.nextPrompt',
226
+ 'tui.altScreen.top',
227
+ 'tui.altScreen.bottom',
228
+ ];
229
+ /** Every row a press reaches, with the keys in force: the catalog and the bar's own library rows. */
230
+ function dispatchRows(effective) {
231
+ const rows = {};
232
+ for (const action of ACTION_CATALOG)
233
+ rows[action.id] = [...(effective[action.id] ?? [])];
234
+ rows['tui.input.submit'] = [...(effective['prompt.submit'] ?? [])];
235
+ rows['tui.input.newLine'] = (effective['prompt.newLine'] ?? []).filter(key => key !== ENTER_KEY);
236
+ return rows;
237
+ }
238
+ /** The same rows as they read before the reader wrote anything. */
239
+ function shippedRows() {
240
+ const rows = {};
241
+ for (const action of ACTION_CATALOG)
242
+ rows[action.id] = [...shippedKeys(action)];
243
+ rows['tui.input.submit'] = [...(actionOf('prompt.submit')?.defaultKeys ?? [])];
244
+ rows['tui.input.newLine'] = [...(actionOf('prompt.newLine')?.defaultKeys ?? [])].filter(key => key !== ENTER_KEY);
245
+ return rows;
246
+ }
247
+ function viewportPairs(rows) {
248
+ const table = [];
249
+ for (const [id, keys] of Object.entries(rows)) {
250
+ for (const key of keys)
251
+ table.push({ id, key });
252
+ }
253
+ const pairs = [];
254
+ for (const overlap of pressOverlaps(table)) {
255
+ const viewport = overlap.ids.filter(id => VIEWPORT_FIRST_ROWS.includes(id));
256
+ for (const row of viewport) {
257
+ for (const other of overlap.ids.filter(id => id !== row && !VIEWPORT_FIRST_ROWS.includes(id))) {
258
+ pairs.push({ key: overlap.key, spellings: overlap.spellings, viewport: row, other });
259
+ }
260
+ }
261
+ }
262
+ return pairs;
263
+ }
264
+ /**
265
+ * Refuse a pair the viewport answers before the row it was written on.
266
+ *
267
+ * A key the alternate screen's listener reads never reaches the surface: the
268
+ * press scrolls or searches instead, and the binding only looks alive while an
269
+ * overlay defers the viewport. Either side of the pair can be the one the
270
+ * reader wrote, and both are a binding that does nothing, so both are refused
271
+ * apart from the overlap the library already ships.
272
+ */
273
+ export function refuseViewportTakingKeys(effective, written) {
274
+ const pairKey = (pair) => `${pair.viewport}\u0000${pair.other}\u0000${pair.spellings.join('|')}`;
275
+ const shipped = new Set(viewportPairs(shippedRows()).map(pairKey));
276
+ // A prompt row is read through a library row of the bar's own, so the reader's
277
+ // name for the row is the action they wrote rather than the mirror.
278
+ const isWritten = (id) => written.has(id) ||
279
+ (id === 'tui.input.submit' && written.has('prompt.submit')) ||
280
+ (id === 'tui.input.newLine' && written.has('prompt.newLine'));
281
+ for (const pair of viewportPairs(dispatchRows(effective))) {
282
+ if (shipped.has(pairKey(pair)))
283
+ continue;
284
+ if (isWritten(pair.other)) {
285
+ throw new Error(`key "${pair.key}" on ${pair.other} is read by ${pair.viewport} first; move that row or pick another key`);
286
+ }
287
+ if (isWritten(pair.viewport)) {
288
+ throw new Error(`key "${pair.key}" on ${pair.viewport} would take it from ${pair.other}, which never sees the press; pick another key`);
289
+ }
290
+ }
291
+ }
292
+ //# sourceMappingURL=keymap-conflicts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keymap-conflicts.js","sourceRoot":"","sources":["../../src/input/keymap-conflicts.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import z from '@deepseek-ai/schemastery';
2
- import { ACTION_CATALOG } from "./actions.js";
2
+ import { ACTION_CATALOG } from "./action-catalog.js";
3
3
  /**
4
4
  * One action's keys as the document writes them: one key, or a list of them.
5
5
  *
@@ -1 +1 @@
1
- {"version":3,"file":"keymap-settings.js","sourceRoot":"","sources":["../../src/input/keymap-settings.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,0BAA0B,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAE7C;;;;;GAKG;AACH,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAA;AAEhE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAC5D,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC,CACzD,CAAC,CAAA;AAEF,MAAM,UAAU,GAAwB,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;AAExF;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AAC7B,CAAC"}
1
+ {"version":3,"file":"keymap-settings.js","sourceRoot":"","sources":["../../src/input/keymap-settings.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,0BAA0B,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD;;;;;GAKG;AACH,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAA;AAEhE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAC5D,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC,CACzD,CAAC,CAAA;AAEF,MAAM,UAAU,GAAwB,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;AAExF;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AAC7B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"keymap.d.ts","sourceRoot":"","sources":["../../src/input/keymap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmE,KAAK,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACpH,OAAO,EAA4E,KAAK,MAAM,EAAE,MAAM,cAAc,CAAA;AACpH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,6EAA6E;AAC7E,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,EAAE,SAAS,KAAK,EAA6C,CAAA;AAE7F,uFAAuF;AACvF,eAAO,MAAM,kBAAkB,EAAE,KAA+B,CAAA;AA0BhE,kFAAkF;AAClF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAA;IACnB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,6CAA6C;AAC7C,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,YAAY,EAAE,CASlE;AAED,6DAA6D;AAC7D,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,+DAA+D;AAC/D,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAarE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED,mEAAmE;AACnE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAA;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,KAAK,EAAE,CAAA;IAClC,qFAAqF;IACrF,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAClC;AAED,2FAA2F;AAC3F,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAQlD;AAED,sEAAsE;AACtE,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAAA;AAE/D,iFAAiF;AACjF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAA;IACnD,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAA;CAC9B;AASD;;;;;;GAMG;AACH,qBAAa,WAAW;IAKpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IARzB,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,MAAM,CAAS;gBAGJ,QAAQ,EAAE,MAAM,SAAS,KAAK,EAAE,EAChC,QAAQ,EAAE,MAAM,SAAS,YAAY,EAAE,EACvC,QAAQ,EAAE,MAAM,MAAM,EACtB,QAAQ,EAAE,MAAM,IAAI,EACpB,MAAM,GAAE,WAA2B;IAGtD,kFAAkF;IAClF,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;;;;OAMG;IACH,IAAI,IAAI,MAAM,GAAG,SAAS;IAI1B,gFAAgF;IAChF,MAAM,IAAI,IAAI;IAOd,sFAAsF;IACtF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;CAyB9C"}
1
+ {"version":3,"file":"keymap.d.ts","sourceRoot":"","sources":["../../src/input/keymap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmE,KAAK,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACpH,OAAO,EAA0B,KAAK,MAAM,EAAE,MAAM,cAAc,CAAA;AAGlE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,6EAA6E;AAC7E,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,EAAE,SAAS,KAAK,EAA6C,CAAA;AAE7F,uFAAuF;AACvF,eAAO,MAAM,kBAAkB,EAAE,KAA+B,CAAA;AA0BhE,kFAAkF;AAClF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAA;IACnB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,6CAA6C;AAC7C,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,YAAY,EAAE,CASlE;AAED,6DAA6D;AAC7D,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,+DAA+D;AAC/D,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAarE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED,mEAAmE;AACnE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAA;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,KAAK,EAAE,CAAA;IAClC,qFAAqF;IACrF,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAClC;AAED,2FAA2F;AAC3F,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAQlD;AAED,sEAAsE;AACtE,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,CAAA;AAE/D,iFAAiF;AACjF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAA;IACnD,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAA;CAC9B;AASD;;;;;;GAMG;AACH,qBAAa,WAAW;IAKpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IARzB,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,MAAM,CAAS;gBAGJ,QAAQ,EAAE,MAAM,SAAS,KAAK,EAAE,EAChC,QAAQ,EAAE,MAAM,SAAS,YAAY,EAAE,EACvC,QAAQ,EAAE,MAAM,MAAM,EACtB,QAAQ,EAAE,MAAM,IAAI,EACpB,MAAM,GAAE,WAA2B;IAGtD,kFAAkF;IAClF,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;;;;OAMG;IACH,IAAI,IAAI,MAAM,GAAG,SAAS;IAI1B,gFAAgF;IAChF,MAAM,IAAI,IAAI;IAOd,sFAAsF;IACtF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;CAyB9C"}
@@ -1,5 +1,7 @@
1
1
  import { KeybindingsManager, TUI_KEYBINDINGS, matchesKey, setKeybindings } from '@earendil-works/pi-tui';
2
- import { ACTION_CATALOG, ENTER_KEY, LIBRARY_KEY_ADDITIONS, defaultKeymap, keysFor } from "./actions.js";
2
+ import { defaultKeymap, keysFor } from "./actions.js";
3
+ import { ACTION_CATALOG, LIBRARY_KEY_ADDITIONS } from "./action-catalog.js";
4
+ import { ENTER_KEY } from "./key-press.js";
3
5
  /** How long an armed chord waits for the key that follows it, in seconds. */
4
6
  export const DEFAULT_PREFIX_WINDOW_S = 2;
5
7
  /** The keys that start a chord when the reader configures nothing. */