terminal-pilot 0.0.50 → 0.0.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/jetbrains-mono-400-italic.ttf +0 -0
- package/assets/jetbrains-mono-400-normal.ttf +0 -0
- package/assets/jetbrains-mono-700-italic.ttf +0 -0
- package/assets/jetbrains-mono-700-normal.ttf +0 -0
- package/dist/cli.js +64 -32
- package/dist/cli.js.map +4 -4
- package/dist/commands/close-session.js +26 -0
- package/dist/commands/close-session.js.map +2 -2
- package/dist/commands/create-session.js +26 -0
- package/dist/commands/create-session.js.map +2 -2
- package/dist/commands/daemon-runtime.js.map +1 -1
- package/dist/commands/fill.js +26 -0
- package/dist/commands/fill.js.map +2 -2
- package/dist/commands/get-session.js +26 -0
- package/dist/commands/get-session.js.map +2 -2
- package/dist/commands/index.js +39 -6
- package/dist/commands/index.js.map +4 -4
- package/dist/commands/install.js +42 -3
- package/dist/commands/install.js.map +4 -4
- package/dist/commands/installer.js +16 -3
- package/dist/commands/installer.js.map +4 -4
- package/dist/commands/list-sessions.js +26 -0
- package/dist/commands/list-sessions.js.map +2 -2
- package/dist/commands/press-key.js +26 -0
- package/dist/commands/press-key.js.map +2 -2
- package/dist/commands/read-history.js +26 -0
- package/dist/commands/read-history.js.map +2 -2
- package/dist/commands/read-screen.js +26 -0
- package/dist/commands/read-screen.js.map +2 -2
- package/dist/commands/resize.js +26 -0
- package/dist/commands/resize.js.map +2 -2
- package/dist/commands/runtime.js.map +1 -1
- package/dist/commands/screenshot.js +26 -0
- package/dist/commands/screenshot.js.map +2 -2
- package/dist/commands/send-signal.js +26 -0
- package/dist/commands/send-signal.js.map +2 -2
- package/dist/commands/type.js +26 -0
- package/dist/commands/type.js.map +2 -2
- package/dist/commands/uninstall.js +42 -3
- package/dist/commands/uninstall.js.map +4 -4
- package/dist/commands/wait-for-exit.js +26 -0
- package/dist/commands/wait-for-exit.js.map +2 -2
- package/dist/commands/wait-for.js +26 -0
- package/dist/commands/wait-for.js.map +2 -2
- package/dist/composition.json +1 -1
- package/dist/testing/cli-repl.js +64 -32
- package/dist/testing/cli-repl.js.map +4 -4
- package/dist/testing/qa-cli.js +64 -32
- package/dist/testing/qa-cli.js.map +4 -4
- package/node_modules/toolcraft-design/README.md +30 -0
- package/node_modules/toolcraft-design/dist/dashboard/terminal-width.d.ts +1 -0
- package/node_modules/toolcraft-design/dist/dashboard/terminal-width.js +18 -1
- package/node_modules/toolcraft-design/dist/explorer/actions.d.ts +3 -2
- package/node_modules/toolcraft-design/dist/explorer/actions.js +50 -4
- package/node_modules/toolcraft-design/dist/explorer/events.d.ts +7 -2
- package/node_modules/toolcraft-design/dist/explorer/filter.js +1 -1
- package/node_modules/toolcraft-design/dist/explorer/index.d.ts +2 -4
- package/node_modules/toolcraft-design/dist/explorer/index.js +1 -2
- package/node_modules/toolcraft-design/dist/explorer/jobs.d.ts +2 -2
- package/node_modules/toolcraft-design/dist/explorer/jobs.js +3 -5
- package/node_modules/toolcraft-design/dist/explorer/keymap.d.ts +13 -3
- package/node_modules/toolcraft-design/dist/explorer/keymap.js +89 -348
- package/node_modules/toolcraft-design/dist/explorer/layout.d.ts +1 -0
- package/node_modules/toolcraft-design/dist/explorer/layout.js +14 -4
- package/node_modules/toolcraft-design/dist/explorer/reducer.js +116 -16
- package/node_modules/toolcraft-design/dist/explorer/render/detail.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/explorer/render/detail.js +45 -7
- package/node_modules/toolcraft-design/dist/explorer/render/footer.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/explorer/render/footer.js +18 -3
- package/node_modules/toolcraft-design/dist/explorer/render/header.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/explorer/render/header.js +6 -2
- package/node_modules/toolcraft-design/dist/explorer/render/index.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/explorer/render/index.js +2 -1
- package/node_modules/toolcraft-design/dist/explorer/render/list.d.ts +16 -2
- package/node_modules/toolcraft-design/dist/explorer/render/list.js +13 -34
- package/node_modules/toolcraft-design/dist/explorer/render/modal.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/explorer/render/modal.js +11 -11
- package/node_modules/toolcraft-design/dist/explorer/render/pane.d.ts +5 -2
- package/node_modules/toolcraft-design/dist/explorer/render/pane.js +9 -6
- package/node_modules/toolcraft-design/dist/explorer/render/test-fixtures.js +2 -3
- package/node_modules/toolcraft-design/dist/explorer/runtime.js +115 -73
- package/node_modules/toolcraft-design/dist/explorer/runtime.test-helpers.d.ts +23 -37
- package/node_modules/toolcraft-design/dist/explorer/runtime.test-helpers.js +32 -83
- package/node_modules/toolcraft-design/dist/explorer/state.d.ts +79 -8
- package/node_modules/toolcraft-design/dist/explorer/state.js +70 -18
- package/node_modules/toolcraft-design/dist/index.d.ts +8 -2
- package/node_modules/toolcraft-design/dist/index.js +5 -1
- package/node_modules/toolcraft-design/dist/screen/ansi-text.d.ts +2 -0
- package/node_modules/toolcraft-design/dist/screen/ansi-text.js +35 -0
- package/node_modules/toolcraft-design/dist/screen/screen.d.ts +40 -0
- package/node_modules/toolcraft-design/dist/screen/screen.js +122 -0
- package/node_modules/toolcraft-design/dist/screen/style.d.ts +16 -0
- package/node_modules/toolcraft-design/dist/screen/style.js +55 -0
- package/node_modules/toolcraft-design/dist/terminal/driver.d.ts +34 -0
- package/node_modules/toolcraft-design/dist/terminal/driver.js +65 -0
- package/node_modules/toolcraft-design/dist/terminal/input.d.ts +25 -0
- package/node_modules/toolcraft-design/dist/terminal/input.js +181 -0
- package/node_modules/toolcraft-design/dist/terminal/output.d.ts +10 -0
- package/node_modules/toolcraft-design/dist/terminal/output.js +44 -0
- package/package.json +3 -2
- package/node_modules/toolcraft-design/dist/explorer/two-pane.d.ts +0 -102
- package/node_modules/toolcraft-design/dist/explorer/two-pane.js +0 -510
- package/node_modules/toolcraft-design/dist/run-two-pane-explorer.d.ts +0 -2
- package/node_modules/toolcraft-design/dist/run-two-pane-explorer.js +0 -1
|
@@ -1,376 +1,117 @@
|
|
|
1
|
-
const
|
|
2
|
-
quit: ["
|
|
3
|
-
filter: [
|
|
4
|
-
help: [
|
|
5
|
-
palette: ["Ctrl+p"
|
|
6
|
-
cursorUp: ["up"
|
|
7
|
-
cursorDown: ["down"
|
|
8
|
-
top: ["home"
|
|
9
|
-
bottom: ["end"
|
|
10
|
-
pageUp: ["pageup"
|
|
11
|
-
pageDown: ["pagedown"
|
|
1
|
+
const BUILTINS = {
|
|
2
|
+
quit: ["Ctrl+c"],
|
|
3
|
+
filter: [],
|
|
4
|
+
help: [],
|
|
5
|
+
palette: ["Ctrl+p"],
|
|
6
|
+
cursorUp: ["up"],
|
|
7
|
+
cursorDown: ["down"],
|
|
8
|
+
top: ["home"],
|
|
9
|
+
bottom: ["end"],
|
|
10
|
+
pageUp: ["pageup"],
|
|
11
|
+
pageDown: ["pagedown"],
|
|
12
|
+
halfPageUp: ["Ctrl+u"],
|
|
13
|
+
halfPageDown: ["Ctrl+d"],
|
|
12
14
|
focusNext: ["tab"],
|
|
13
15
|
escape: ["escape"],
|
|
14
16
|
confirm: ["return", "enter"],
|
|
15
17
|
toggleSelect: ["space"],
|
|
16
18
|
selectAll: ["Ctrl+a"],
|
|
17
19
|
clearSelection: ["Ctrl+/"],
|
|
18
|
-
detailScrollDown: [
|
|
19
|
-
detailScrollUp: [
|
|
20
|
+
detailScrollDown: [],
|
|
21
|
+
detailScrollUp: [],
|
|
20
22
|
extendSelectionUp: ["Shift+up"],
|
|
21
23
|
extendSelectionDown: ["Shift+down"],
|
|
22
|
-
reorderUp: ["Shift+up"
|
|
23
|
-
reorderDown: ["Shift+down"
|
|
24
|
+
reorderUp: ["Shift+up"],
|
|
25
|
+
reorderDown: ["Shift+down"]
|
|
24
26
|
};
|
|
25
|
-
const
|
|
26
|
-
"quit",
|
|
27
|
-
"filter",
|
|
28
|
-
"help",
|
|
29
|
-
"palette",
|
|
30
|
-
"cursorUp",
|
|
31
|
-
"cursorDown",
|
|
32
|
-
"top",
|
|
33
|
-
"bottom",
|
|
34
|
-
"pageUp",
|
|
35
|
-
"pageDown",
|
|
36
|
-
"focusNext",
|
|
37
|
-
"escape",
|
|
38
|
-
"confirm",
|
|
39
|
-
"toggleSelect",
|
|
40
|
-
"selectAll",
|
|
41
|
-
"clearSelection",
|
|
42
|
-
"detailScrollDown",
|
|
43
|
-
"detailScrollUp",
|
|
44
|
-
"extendSelectionUp",
|
|
45
|
-
"extendSelectionDown"
|
|
46
|
-
];
|
|
47
|
-
const reorderCommands = ["reorderUp", "reorderDown"];
|
|
48
|
-
const selectionCommands = new Set([
|
|
49
|
-
"toggleSelect",
|
|
50
|
-
"selectAll",
|
|
51
|
-
"clearSelection",
|
|
52
|
-
"extendSelectionUp",
|
|
53
|
-
"extendSelectionDown"
|
|
54
|
-
]);
|
|
55
|
-
const reservedActionIds = new Set(["quit"]);
|
|
27
|
+
const CORE_ACCELERATORS = new Set(["c", "u", "d", "p"]);
|
|
56
28
|
export function resolveBindings(config, defaults = {}) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
]
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const claimed = new Map();
|
|
71
|
-
const conflicts = new Map();
|
|
29
|
+
assertNoBareLetterBindings(config);
|
|
30
|
+
assertAcceleratorsFree(config);
|
|
31
|
+
const bindings = new Map();
|
|
32
|
+
const keysByTarget = new Map();
|
|
33
|
+
const commands = Object.keys(BUILTINS).filter(command => {
|
|
34
|
+
if (config.multiSelect === false && ["toggleSelect", "selectAll", "clearSelection", "extendSelectionUp", "extendSelectionDown"].includes(command))
|
|
35
|
+
return false;
|
|
36
|
+
if (config.reorder === undefined && (command === "reorderUp" || command === "reorderDown"))
|
|
37
|
+
return false;
|
|
38
|
+
if (config.reorder !== undefined && (command === "extendSelectionUp" || command === "extendSelectionDown"))
|
|
39
|
+
return false;
|
|
40
|
+
return true;
|
|
41
|
+
});
|
|
72
42
|
for (const command of commands) {
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
? builtinBindings.quit
|
|
76
|
-
: defaults[command] ?? builtinBindings[command];
|
|
77
|
-
addBindings({
|
|
78
|
-
keys,
|
|
79
|
-
owner: command,
|
|
80
|
-
commandId: `builtin:${command}`,
|
|
81
|
-
target,
|
|
82
|
-
commandBindings,
|
|
83
|
-
flatBindings,
|
|
84
|
-
targetKeys,
|
|
85
|
-
targetsByCommand,
|
|
86
|
-
claimed,
|
|
87
|
-
conflicts,
|
|
88
|
-
warn: false
|
|
89
|
-
});
|
|
43
|
+
const keys = command === "quit" ? BUILTINS.quit : (defaults[command] ?? BUILTINS[command]);
|
|
44
|
+
add(keys, { type: "builtin", id: command }, bindings, keysByTarget);
|
|
90
45
|
}
|
|
91
|
-
for (const action of
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
const keys = toBindingArray(config.keybindOverrides?.[action.id] ?? action.key);
|
|
96
|
-
addBindings({
|
|
97
|
-
keys,
|
|
98
|
-
owner: action.id,
|
|
99
|
-
commandId: `action:${action.id}`,
|
|
100
|
-
target: { type: "action", id: action.id },
|
|
101
|
-
commandBindings,
|
|
102
|
-
flatBindings,
|
|
103
|
-
targetKeys,
|
|
104
|
-
targetsByCommand,
|
|
105
|
-
claimed,
|
|
106
|
-
conflicts,
|
|
107
|
-
warn: true
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
if (process.env.NODE_ENV !== "production") {
|
|
111
|
-
for (const warning of conflicts.values()) {
|
|
112
|
-
process.stderr.write(`${warning}\n`);
|
|
113
|
-
}
|
|
46
|
+
for (const action of allActions(config)) {
|
|
47
|
+
if (action.accelerator !== undefined)
|
|
48
|
+
add([`Ctrl+${action.accelerator}`], { type: "action", id: action.id }, bindings, keysByTarget);
|
|
114
49
|
}
|
|
115
|
-
const commandIds = Array.from(commandBindings.keys());
|
|
116
|
-
const defaultKeymapBindings = Object.fromEntries(Array.from(commandBindings.entries()));
|
|
117
|
-
const resolveCommand = createKeymap(commandIds, defaultKeymapBindings);
|
|
118
50
|
return {
|
|
119
|
-
bindings
|
|
120
|
-
keysByTarget
|
|
121
|
-
resolve:
|
|
122
|
-
const command = resolveCommand(event);
|
|
123
|
-
return command === undefined ? undefined : targetsByCommand.get(command);
|
|
124
|
-
}
|
|
51
|
+
bindings,
|
|
52
|
+
keysByTarget,
|
|
53
|
+
resolve: event => bindings.get(eventKey(event))
|
|
125
54
|
};
|
|
126
55
|
}
|
|
127
|
-
function
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
const existing = opts.claimed.get(canonical);
|
|
135
|
-
if (existing !== undefined) {
|
|
136
|
-
if (existing !== opts.owner && opts.warn) {
|
|
137
|
-
opts.conflicts.set(`${opts.owner}:${canonical}`, `Explorer key binding conflict: ${key} for ${opts.owner} is already bound to ${existing}`);
|
|
138
|
-
}
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
opts.claimed.set(canonical, opts.owner);
|
|
142
|
-
opts.flatBindings.set(canonical, opts.target);
|
|
143
|
-
accepted.push(key);
|
|
56
|
+
export function assertNoBareLetterBindings(config) {
|
|
57
|
+
for (const action of allActions(config)) {
|
|
58
|
+
const legacy = action.key === undefined ? [] : (Array.isArray(action.key) ? action.key : [action.key]);
|
|
59
|
+
if (legacy.length > 0)
|
|
60
|
+
throw new Error(`Explorer action ${action.id} uses a bare key; use accelerator instead`);
|
|
144
61
|
}
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
opts.targetKeys.set(targetKey(opts.target), accepted);
|
|
148
|
-
opts.targetsByCommand.set(opts.commandId, opts.target);
|
|
62
|
+
if (config.keybindOverrides !== undefined && Object.keys(config.keybindOverrides).length > 0) {
|
|
63
|
+
throw new Error("Explorer keybind overrides are not supported because printable keys belong to filtering");
|
|
149
64
|
}
|
|
150
|
-
return accepted;
|
|
151
65
|
}
|
|
152
|
-
function
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (value === undefined) {
|
|
157
|
-
return [];
|
|
158
|
-
}
|
|
159
|
-
return Array.isArray(value) ? value : [value];
|
|
160
|
-
}
|
|
161
|
-
function createKeymap(commands, defaultBindings) {
|
|
162
|
-
const bindings = new Map();
|
|
163
|
-
const sequences = new Set();
|
|
164
|
-
let pendingSequence = "";
|
|
165
|
-
for (const command of commands) {
|
|
166
|
-
const commandBindings = defaultBindings[command]
|
|
167
|
-
.map(parseBinding)
|
|
168
|
-
.filter((binding) => binding !== undefined);
|
|
169
|
-
for (const binding of commandBindings) {
|
|
170
|
-
if (binding.sequence !== undefined) {
|
|
171
|
-
sequences.add(binding.sequence);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
bindings.set(command, commandBindings);
|
|
175
|
-
}
|
|
176
|
-
return (event) => {
|
|
177
|
-
for (const command of commands) {
|
|
178
|
-
const commandBindings = bindings.get(command);
|
|
179
|
-
if (commandBindings?.some((binding) => matchesSingleKey(binding, event))) {
|
|
180
|
-
pendingSequence = "";
|
|
181
|
-
return command;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
const token = eventToSequenceToken(event);
|
|
185
|
-
if (token === undefined) {
|
|
186
|
-
pendingSequence = "";
|
|
187
|
-
return undefined;
|
|
188
|
-
}
|
|
189
|
-
pendingSequence = `${pendingSequence}${token}`;
|
|
190
|
-
for (const command of commands) {
|
|
191
|
-
const commandBindings = bindings.get(command);
|
|
192
|
-
if (commandBindings?.some((binding) => binding.sequence === pendingSequence)) {
|
|
193
|
-
pendingSequence = "";
|
|
194
|
-
return command;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
if (hasSequencePrefix(sequences, pendingSequence)) {
|
|
198
|
-
return undefined;
|
|
199
|
-
}
|
|
200
|
-
pendingSequence = token;
|
|
201
|
-
if (hasSequencePrefix(sequences, pendingSequence)) {
|
|
202
|
-
return undefined;
|
|
203
|
-
}
|
|
204
|
-
pendingSequence = "";
|
|
205
|
-
return undefined;
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
function canonicalizeBinding(binding) {
|
|
209
|
-
const parsed = parseBinding(binding);
|
|
210
|
-
if (parsed === undefined) {
|
|
211
|
-
return undefined;
|
|
212
|
-
}
|
|
213
|
-
const modifiers = [
|
|
214
|
-
parsed.ctrl ? "ctrl" : undefined,
|
|
215
|
-
parsed.meta ? "meta" : undefined,
|
|
216
|
-
parsed.shift ? "shift" : undefined
|
|
217
|
-
].filter((modifier) => modifier !== undefined);
|
|
218
|
-
const key = parsed.name ?? parsed.ch;
|
|
219
|
-
if (parsed.sequence !== undefined) {
|
|
220
|
-
return parsed.sequence.toLowerCase();
|
|
221
|
-
}
|
|
222
|
-
return key === undefined ? undefined : [...modifiers, key.toLowerCase()].join("+");
|
|
223
|
-
}
|
|
224
|
-
function parseBinding(binding) {
|
|
225
|
-
const value = binding.trim();
|
|
226
|
-
if (value.length === 0) {
|
|
227
|
-
return undefined;
|
|
228
|
-
}
|
|
229
|
-
const parts = value.split("+").map((part) => part.trim()).filter(Boolean);
|
|
230
|
-
if (parts.length === 0) {
|
|
231
|
-
return undefined;
|
|
232
|
-
}
|
|
233
|
-
let ctrl = false;
|
|
234
|
-
let meta = false;
|
|
235
|
-
let shift = false;
|
|
236
|
-
const key = parts.at(-1);
|
|
237
|
-
if (key === undefined) {
|
|
238
|
-
return undefined;
|
|
239
|
-
}
|
|
240
|
-
for (const modifier of parts.slice(0, -1)) {
|
|
241
|
-
const normalized = modifier.toLowerCase();
|
|
242
|
-
if (normalized === "ctrl" || normalized === "control") {
|
|
243
|
-
ctrl = true;
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
if (normalized === "meta" || normalized === "alt") {
|
|
247
|
-
meta = true;
|
|
66
|
+
export function assertAcceleratorsFree(config) {
|
|
67
|
+
const claimed = new Map();
|
|
68
|
+
for (const action of allActions(config)) {
|
|
69
|
+
if (action.accelerator === undefined)
|
|
248
70
|
continue;
|
|
249
|
-
|
|
250
|
-
if (
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
if (normalizedKey.length === 1) {
|
|
259
|
-
return {
|
|
260
|
-
ch: normalizeBindingCharacter(normalizedKey, shift),
|
|
261
|
-
ctrl,
|
|
262
|
-
meta,
|
|
263
|
-
shift
|
|
264
|
-
};
|
|
71
|
+
const accelerator = action.accelerator.toLowerCase();
|
|
72
|
+
if (accelerator.length !== 1 || accelerator < "a" || accelerator > "z")
|
|
73
|
+
throw new Error(`Explorer action ${action.id} accelerator must be one letter`);
|
|
74
|
+
if (CORE_ACCELERATORS.has(accelerator))
|
|
75
|
+
throw new Error(`Explorer action ${action.id} accelerator Ctrl+${accelerator.toUpperCase()} collides with a core key`);
|
|
76
|
+
const owner = claimed.get(accelerator);
|
|
77
|
+
if (owner !== undefined)
|
|
78
|
+
throw new Error(`Explorer actions ${owner} and ${action.id} share Ctrl+${accelerator.toUpperCase()}`);
|
|
79
|
+
claimed.set(accelerator, action.id);
|
|
265
80
|
}
|
|
266
|
-
if (!ctrl &&
|
|
267
|
-
!meta &&
|
|
268
|
-
!shift &&
|
|
269
|
-
!isNamedKey(normalizedKey) &&
|
|
270
|
-
isPrintableSequence(normalizedKey)) {
|
|
271
|
-
return {
|
|
272
|
-
sequence: normalizedKey,
|
|
273
|
-
ctrl,
|
|
274
|
-
meta,
|
|
275
|
-
shift
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
return {
|
|
279
|
-
name: normalizedKey.toLowerCase(),
|
|
280
|
-
ctrl,
|
|
281
|
-
meta,
|
|
282
|
-
shift
|
|
283
|
-
};
|
|
284
81
|
}
|
|
285
|
-
function
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
return event.ch === binding.ch || event.name === binding.ch.toLowerCase();
|
|
299
|
-
}
|
|
300
|
-
if (binding.name !== undefined) {
|
|
301
|
-
return event.name === binding.name;
|
|
302
|
-
}
|
|
303
|
-
return false;
|
|
82
|
+
export function keymapToHelp(config) {
|
|
83
|
+
return [
|
|
84
|
+
{ title: "Navigation", entries: [
|
|
85
|
+
{ key: "↑/↓", label: "move" }, { key: "PgUp/PgDn", label: "page" },
|
|
86
|
+
{ key: "Home/End", label: "first/last" }, { key: "Tab", label: "focus" }
|
|
87
|
+
] },
|
|
88
|
+
{ title: "Actions", entries: [
|
|
89
|
+
{ key: "Enter", label: "actions" }, { key: "Ctrl+P", label: "palette" },
|
|
90
|
+
...allActions(config).filter(action => action.accelerator !== undefined).map(action => ({ key: `Ctrl+${action.accelerator.toUpperCase()}`, label: typeof action.label === "string" ? action.label : action.id }))
|
|
91
|
+
] },
|
|
92
|
+
{ title: "General", entries: [{ key: "Esc", label: "clear/quit" }, { key: "Ctrl+C", label: "quit" }] }
|
|
93
|
+
];
|
|
304
94
|
}
|
|
305
|
-
function
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
}
|
|
309
|
-
return event.ch;
|
|
95
|
+
function allActions(config) {
|
|
96
|
+
const actions = [...config.actions, ...(config.detail?.actions ?? [])];
|
|
97
|
+
return [...new Map(actions.map(action => [action.id, action])).values()];
|
|
310
98
|
}
|
|
311
|
-
function
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
99
|
+
function add(keys, target, bindings, keysByTarget) {
|
|
100
|
+
const accepted = [];
|
|
101
|
+
for (const key of keys) {
|
|
102
|
+
const normalized = canonical(key);
|
|
103
|
+
if (!bindings.has(normalized)) {
|
|
104
|
+
bindings.set(normalized, target);
|
|
105
|
+
accepted.push(key);
|
|
315
106
|
}
|
|
316
107
|
}
|
|
317
|
-
|
|
318
|
-
}
|
|
319
|
-
function isShiftedCharacter(value) {
|
|
320
|
-
return value.length === 1 && value.toLowerCase() !== value && value.toUpperCase() === value;
|
|
321
|
-
}
|
|
322
|
-
function normalizeBindingCharacter(value, shift) {
|
|
323
|
-
if (!shift || value.toLowerCase() === value.toUpperCase()) {
|
|
324
|
-
return value;
|
|
325
|
-
}
|
|
326
|
-
return value.toUpperCase();
|
|
327
|
-
}
|
|
328
|
-
function normalizeKeyName(value) {
|
|
329
|
-
if (value.toLowerCase() === "space") {
|
|
330
|
-
return " ";
|
|
331
|
-
}
|
|
332
|
-
if (value === "↑") {
|
|
333
|
-
return "up";
|
|
334
|
-
}
|
|
335
|
-
if (value === "↓") {
|
|
336
|
-
return "down";
|
|
337
|
-
}
|
|
338
|
-
if (value === "←") {
|
|
339
|
-
return "left";
|
|
340
|
-
}
|
|
341
|
-
if (value === "→") {
|
|
342
|
-
return "right";
|
|
343
|
-
}
|
|
344
|
-
return value;
|
|
345
|
-
}
|
|
346
|
-
function isNamedKey(value) {
|
|
347
|
-
return namedKeys.has(value.toLowerCase());
|
|
108
|
+
if (accepted.length > 0)
|
|
109
|
+
keysByTarget.set(`${target.type}:${target.id}`, accepted);
|
|
348
110
|
}
|
|
349
|
-
function
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
if (codePoint === undefined || codePoint < 0x20 || codePoint === 0x7f) {
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
return true;
|
|
111
|
+
function canonical(value) { return value.trim().toLowerCase().replace("control+", "ctrl+"); }
|
|
112
|
+
function eventKey(event) {
|
|
113
|
+
const name = event.name ?? event.ch ?? "";
|
|
114
|
+
const modifiers = [event.ctrl ? "ctrl" : "", event.meta ? "meta" : "", event.shift ? "shift" : ""].filter(Boolean);
|
|
115
|
+
const normalized = name === "return" ? "return" : name.toLowerCase();
|
|
116
|
+
return [...modifiers, normalized].join("+");
|
|
360
117
|
}
|
|
361
|
-
const namedKeys = new Set([
|
|
362
|
-
"backspace",
|
|
363
|
-
"delete",
|
|
364
|
-
"down",
|
|
365
|
-
"end",
|
|
366
|
-
"enter",
|
|
367
|
-
"escape",
|
|
368
|
-
"home",
|
|
369
|
-
"left",
|
|
370
|
-
"pagedown",
|
|
371
|
-
"pageup",
|
|
372
|
-
"return",
|
|
373
|
-
"right",
|
|
374
|
-
"tab",
|
|
375
|
-
"up"
|
|
376
|
-
]);
|
|
@@ -3,7 +3,7 @@ const FOOTER_HEIGHT = 1;
|
|
|
3
3
|
export function computeExplorerLayout(opts) {
|
|
4
4
|
const cols = normalizeSize(opts.cols);
|
|
5
5
|
const rows = normalizeSize(opts.rows);
|
|
6
|
-
const mode = resolveMode(cols);
|
|
6
|
+
const mode = resolveMode(cols, rows);
|
|
7
7
|
const footerHeight = rows > 0 ? Math.min(FOOTER_HEIGHT, rows) : 0;
|
|
8
8
|
const headerHeight = Math.min(HEADER_HEIGHT, Math.max(0, rows - footerHeight));
|
|
9
9
|
const contentY = headerHeight;
|
|
@@ -11,7 +11,7 @@ export function computeExplorerLayout(opts) {
|
|
|
11
11
|
const footerY = headerHeight + contentHeight;
|
|
12
12
|
const header = { x: 0, y: 0, width: cols, height: headerHeight };
|
|
13
13
|
const footer = { x: 0, y: footerY, width: cols, height: footerHeight };
|
|
14
|
-
if (mode === "too-narrow"
|
|
14
|
+
if (mode === "too-narrow") {
|
|
15
15
|
return {
|
|
16
16
|
mode,
|
|
17
17
|
header,
|
|
@@ -20,6 +20,16 @@ export function computeExplorerLayout(opts) {
|
|
|
20
20
|
footer
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
+
if (mode === "narrow-list-only") {
|
|
24
|
+
const detailFocused = opts.focused === "detail";
|
|
25
|
+
return {
|
|
26
|
+
mode,
|
|
27
|
+
header,
|
|
28
|
+
list: detailFocused ? { x: 0, y: contentY, width: 0, height: contentHeight } : { x: 0, y: contentY, width: cols, height: contentHeight },
|
|
29
|
+
detail: detailFocused ? { x: 0, y: contentY, width: cols, height: contentHeight } : { x: cols, y: contentY, width: 0, height: contentHeight },
|
|
30
|
+
footer
|
|
31
|
+
};
|
|
32
|
+
}
|
|
23
33
|
if (opts.detailHidden === true) {
|
|
24
34
|
return {
|
|
25
35
|
mode,
|
|
@@ -52,8 +62,8 @@ export function computeExplorerLayout(opts) {
|
|
|
52
62
|
footer
|
|
53
63
|
};
|
|
54
64
|
}
|
|
55
|
-
function resolveMode(cols) {
|
|
56
|
-
if (cols <
|
|
65
|
+
function resolveMode(cols, rows) {
|
|
66
|
+
if (cols < 60 || rows < 8) {
|
|
57
67
|
return "too-narrow";
|
|
58
68
|
}
|
|
59
69
|
if (cols < 80) {
|