erosolar-cli 1.7.14 → 1.7.15
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/dist/core/responseVerifier.d.ts +79 -0
- package/dist/core/responseVerifier.d.ts.map +1 -0
- package/dist/core/responseVerifier.js +443 -0
- package/dist/core/responseVerifier.js.map +1 -0
- package/dist/shell/interactiveShell.d.ts +5 -0
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +38 -0
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/ui/ShellUIAdapter.d.ts +3 -0
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +4 -10
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/dist/ui/persistentPrompt.d.ts +4 -0
- package/dist/ui/persistentPrompt.d.ts.map +1 -1
- package/dist/ui/persistentPrompt.js +10 -11
- package/dist/ui/persistentPrompt.js.map +1 -1
- package/package.json +1 -1
- package/dist/bin/core/agent.js +0 -362
- package/dist/bin/core/agentProfileManifest.js +0 -187
- package/dist/bin/core/agentProfiles.js +0 -34
- package/dist/bin/core/agentRulebook.js +0 -135
- package/dist/bin/core/agentSchemaLoader.js +0 -233
- package/dist/bin/core/contextManager.js +0 -412
- package/dist/bin/core/contextWindow.js +0 -122
- package/dist/bin/core/customCommands.js +0 -80
- package/dist/bin/core/errors/apiKeyErrors.js +0 -114
- package/dist/bin/core/errors/errorTypes.js +0 -340
- package/dist/bin/core/errors/safetyValidator.js +0 -304
- package/dist/bin/core/errors.js +0 -32
- package/dist/bin/core/modelDiscovery.js +0 -755
- package/dist/bin/core/preferences.js +0 -224
- package/dist/bin/core/schemaValidator.js +0 -92
- package/dist/bin/core/secretStore.js +0 -199
- package/dist/bin/core/sessionStore.js +0 -187
- package/dist/bin/core/toolRuntime.js +0 -290
- package/dist/bin/core/types.js +0 -1
- package/dist/bin/shell/bracketedPasteManager.js +0 -350
- package/dist/bin/shell/fileChangeTracker.js +0 -65
- package/dist/bin/shell/interactiveShell.js +0 -2908
- package/dist/bin/shell/liveStatus.js +0 -78
- package/dist/bin/shell/shellApp.js +0 -290
- package/dist/bin/shell/systemPrompt.js +0 -60
- package/dist/bin/shell/updateManager.js +0 -108
- package/dist/bin/ui/ShellUIAdapter.js +0 -459
- package/dist/bin/ui/UnifiedUIController.js +0 -183
- package/dist/bin/ui/animation/AnimationScheduler.js +0 -430
- package/dist/bin/ui/codeHighlighter.js +0 -854
- package/dist/bin/ui/designSystem.js +0 -121
- package/dist/bin/ui/display.js +0 -1222
- package/dist/bin/ui/interrupts/InterruptManager.js +0 -437
- package/dist/bin/ui/layout.js +0 -139
- package/dist/bin/ui/orchestration/StatusOrchestrator.js +0 -403
- package/dist/bin/ui/outputMode.js +0 -38
- package/dist/bin/ui/persistentPrompt.js +0 -183
- package/dist/bin/ui/richText.js +0 -338
- package/dist/bin/ui/shortcutsHelp.js +0 -87
- package/dist/bin/ui/telemetry/UITelemetry.js +0 -443
- package/dist/bin/ui/textHighlighter.js +0 -210
- package/dist/bin/ui/theme.js +0 -116
- package/dist/bin/ui/toolDisplay.js +0 -423
- package/dist/bin/ui/toolDisplayAdapter.js +0 -357
package/dist/bin/ui/richText.js
DELETED
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
import { icons, theme } from './theme.js';
|
|
2
|
-
import { getContentWidth, measure, normalizePanelWidth, renderPanel, wrapParagraph, wrapPreformatted, } from './layout.js';
|
|
3
|
-
import { highlightAndWrapCode } from './codeHighlighter.js';
|
|
4
|
-
import { isPlainOutputMode } from './outputMode.js';
|
|
5
|
-
export function formatRichContent(content, width) {
|
|
6
|
-
const blocks = parseBlocks(content);
|
|
7
|
-
const lines = [];
|
|
8
|
-
for (const block of blocks) {
|
|
9
|
-
let blockLines = [];
|
|
10
|
-
switch (block.type) {
|
|
11
|
-
case 'paragraph': {
|
|
12
|
-
const formatted = formatInlineText(block.text);
|
|
13
|
-
blockLines = wrapParagraph(formatted, width);
|
|
14
|
-
break;
|
|
15
|
-
}
|
|
16
|
-
case 'list':
|
|
17
|
-
blockLines = formatList(block.items, width);
|
|
18
|
-
break;
|
|
19
|
-
case 'code':
|
|
20
|
-
blockLines = formatCodeBlock(block.content, width, block.language);
|
|
21
|
-
break;
|
|
22
|
-
case 'diff':
|
|
23
|
-
blockLines = formatDiffBlock(block.content, width);
|
|
24
|
-
break;
|
|
25
|
-
case 'heading':
|
|
26
|
-
blockLines = formatHeadingBlock(block, width);
|
|
27
|
-
break;
|
|
28
|
-
case 'quote':
|
|
29
|
-
blockLines = formatQuoteBlock(block.lines, width);
|
|
30
|
-
break;
|
|
31
|
-
case 'divider':
|
|
32
|
-
blockLines = [formatDivider(width)];
|
|
33
|
-
break;
|
|
34
|
-
default:
|
|
35
|
-
blockLines = [];
|
|
36
|
-
}
|
|
37
|
-
if (!blockLines.length) {
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (lines.length) {
|
|
41
|
-
const lastLine = lines[lines.length - 1];
|
|
42
|
-
if (lastLine?.trim()) {
|
|
43
|
-
lines.push('');
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
lines.push(...blockLines);
|
|
47
|
-
}
|
|
48
|
-
while (lines.length) {
|
|
49
|
-
const lastLine = lines[lines.length - 1];
|
|
50
|
-
if (lastLine?.trim()) {
|
|
51
|
-
break;
|
|
52
|
-
}
|
|
53
|
-
lines.pop();
|
|
54
|
-
}
|
|
55
|
-
return lines;
|
|
56
|
-
}
|
|
57
|
-
export function renderMessagePanel(content, options) {
|
|
58
|
-
const width = normalizePanelWidth(options.width ?? getContentWidth());
|
|
59
|
-
const lines = formatRichContent(content, width);
|
|
60
|
-
return renderPanel(lines, { ...options, width });
|
|
61
|
-
}
|
|
62
|
-
export function renderMessageBody(content, width) {
|
|
63
|
-
const normalizedWidth = normalizePanelWidth(width ?? getContentWidth());
|
|
64
|
-
const lines = formatRichContent(content, normalizedWidth);
|
|
65
|
-
return lines.join('\n');
|
|
66
|
-
}
|
|
67
|
-
export function formatDiffBlock(diff, width) {
|
|
68
|
-
const lines = diff.replace(/\t/g, ' ').split('\n');
|
|
69
|
-
const result = [];
|
|
70
|
-
for (const line of lines) {
|
|
71
|
-
if (!line.trim()) {
|
|
72
|
-
result.push('');
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
const color = pickDiffColor(line);
|
|
76
|
-
const chunks = wrapPreformatted(line, width);
|
|
77
|
-
chunks.forEach((chunk) => result.push(color(chunk)));
|
|
78
|
-
}
|
|
79
|
-
return result;
|
|
80
|
-
}
|
|
81
|
-
function parseBlocks(content) {
|
|
82
|
-
const blocks = [];
|
|
83
|
-
const lines = content.split('\n');
|
|
84
|
-
let fence = null;
|
|
85
|
-
let paragraph = [];
|
|
86
|
-
let blockquote = null;
|
|
87
|
-
const flushParagraph = () => {
|
|
88
|
-
if (!paragraph.length) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const merged = paragraph.join('\n');
|
|
92
|
-
const trimmedLines = merged
|
|
93
|
-
.split('\n')
|
|
94
|
-
.map((line) => line.trim())
|
|
95
|
-
.filter(Boolean);
|
|
96
|
-
if (!trimmedLines.length) {
|
|
97
|
-
paragraph = [];
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
const isList = trimmedLines.every((line) => /^(\*|-|•|\d+\.)\s+/.test(line));
|
|
101
|
-
if (isList) {
|
|
102
|
-
blocks.push({
|
|
103
|
-
type: 'list',
|
|
104
|
-
items: trimmedLines.map((line) => line.replace(/^(\*|-|•|\d+\.)\s+/, '')),
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
blocks.push({ type: 'paragraph', text: trimmedLines.join(' ') });
|
|
109
|
-
}
|
|
110
|
-
paragraph = [];
|
|
111
|
-
};
|
|
112
|
-
const flushBlockquote = () => {
|
|
113
|
-
if (!blockquote?.length) {
|
|
114
|
-
blockquote = null;
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
blocks.push({ type: 'quote', lines: blockquote });
|
|
118
|
-
blockquote = null;
|
|
119
|
-
};
|
|
120
|
-
for (const line of lines) {
|
|
121
|
-
if (line.trimStart().startsWith('```')) {
|
|
122
|
-
const raw = line.trim();
|
|
123
|
-
if (fence) {
|
|
124
|
-
blocks.push(fence.language.includes('diff')
|
|
125
|
-
? { type: 'diff', content: fence.buffer.join('\n') }
|
|
126
|
-
: { type: 'code', content: fence.buffer.join('\n'), language: fence.language });
|
|
127
|
-
fence = null;
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
flushParagraph();
|
|
131
|
-
flushBlockquote();
|
|
132
|
-
const language = raw.slice(3).trim().toLowerCase();
|
|
133
|
-
fence = { language, buffer: [] };
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
if (fence) {
|
|
137
|
-
fence.buffer.push(line);
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
const trimmed = line.trim();
|
|
141
|
-
if (!trimmed) {
|
|
142
|
-
flushParagraph();
|
|
143
|
-
flushBlockquote();
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
const quoteMatch = line.match(/^\s*>\s?(.*)$/);
|
|
147
|
-
if (quoteMatch) {
|
|
148
|
-
flushParagraph();
|
|
149
|
-
blockquote = blockquote ?? [];
|
|
150
|
-
blockquote.push(quoteMatch[1] ?? '');
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
flushBlockquote();
|
|
154
|
-
const headingMatch = trimmed.match(/^(#{1,6})\s+(.*)$/);
|
|
155
|
-
if (headingMatch) {
|
|
156
|
-
flushParagraph();
|
|
157
|
-
const hashes = headingMatch[1] ?? '';
|
|
158
|
-
const text = headingMatch[2] ?? '';
|
|
159
|
-
blocks.push({ type: 'heading', level: hashes.length, text: text.trim() });
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
if (/^(-{3,}|_{3,}|\*{3,})$/.test(trimmed)) {
|
|
163
|
-
flushParagraph();
|
|
164
|
-
blocks.push({ type: 'divider' });
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
paragraph.push(line);
|
|
168
|
-
}
|
|
169
|
-
flushParagraph();
|
|
170
|
-
flushBlockquote();
|
|
171
|
-
return blocks;
|
|
172
|
-
}
|
|
173
|
-
function formatList(items, width) {
|
|
174
|
-
const lines = [];
|
|
175
|
-
const bullet = theme.secondary(`${icons.bullet} `);
|
|
176
|
-
const bulletWidth = measure(`${icons.bullet} `);
|
|
177
|
-
const contentWidth = Math.max(10, width - bulletWidth);
|
|
178
|
-
for (const item of items) {
|
|
179
|
-
const formatted = formatInlineText(item);
|
|
180
|
-
const wrapped = wrapParagraph(formatted, contentWidth);
|
|
181
|
-
wrapped.forEach((segment, index) => {
|
|
182
|
-
if (index === 0) {
|
|
183
|
-
lines.push(`${bullet}${segment}`);
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
lines.push(`${' '.repeat(bulletWidth)}${segment}`);
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
return lines;
|
|
191
|
-
}
|
|
192
|
-
function formatCodeBlock(code, width, language) {
|
|
193
|
-
const gutterRaw = isPlainOutputMode() ? '' : '│ ';
|
|
194
|
-
const gutter = theme.ui.muted(gutterRaw);
|
|
195
|
-
const available = Math.max(16, width - measure(gutterRaw));
|
|
196
|
-
const { lines, languageLabel } = highlightAndWrapCode(code, language, available);
|
|
197
|
-
const headerLabel = (languageLabel ?? 'CODE').toUpperCase();
|
|
198
|
-
const result = [];
|
|
199
|
-
if (isPlainOutputMode()) {
|
|
200
|
-
result.push(theme.ui.muted(`[${headerLabel}]`));
|
|
201
|
-
for (const line of lines) {
|
|
202
|
-
result.push(line);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
result.push(`${gutter}${theme.ui.muted(buildCodeDivider(headerLabel, available))}`);
|
|
207
|
-
for (const line of lines) {
|
|
208
|
-
result.push(`${gutter}${line}`);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
return result;
|
|
212
|
-
}
|
|
213
|
-
function formatHeadingBlock(block, width) {
|
|
214
|
-
const wrapped = wrapParagraph(formatInlineText(block.text), width);
|
|
215
|
-
if (!wrapped.length) {
|
|
216
|
-
return [];
|
|
217
|
-
}
|
|
218
|
-
const accent = pickHeadingAccent(block.level);
|
|
219
|
-
const content = wrapped.map((line) => accent(theme.bold(line)));
|
|
220
|
-
if (block.level <= 2 && !isPlainOutputMode()) {
|
|
221
|
-
content.push(accent('─'.repeat(width)));
|
|
222
|
-
}
|
|
223
|
-
return content;
|
|
224
|
-
}
|
|
225
|
-
function formatQuoteBlock(lines, width) {
|
|
226
|
-
if (!lines.length) {
|
|
227
|
-
return [];
|
|
228
|
-
}
|
|
229
|
-
const gutterText = isPlainOutputMode() ? '> ' : '│ ';
|
|
230
|
-
const gutter = theme.ui.muted(gutterText);
|
|
231
|
-
const available = Math.max(12, width - measure(gutterText));
|
|
232
|
-
const result = [];
|
|
233
|
-
for (const line of lines) {
|
|
234
|
-
if (!line.trim()) {
|
|
235
|
-
result.push(gutter);
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
const wrapped = wrapParagraph(formatInlineText(line), available);
|
|
239
|
-
wrapped.forEach((segment) => {
|
|
240
|
-
result.push(`${gutter}${segment}`);
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
return result;
|
|
244
|
-
}
|
|
245
|
-
function formatDivider(width) {
|
|
246
|
-
if (isPlainOutputMode()) {
|
|
247
|
-
return theme.ui.muted('---');
|
|
248
|
-
}
|
|
249
|
-
return theme.ui.muted('─'.repeat(width));
|
|
250
|
-
}
|
|
251
|
-
function buildCodeDivider(label, width) {
|
|
252
|
-
const normalized = label.trim() || 'CODE';
|
|
253
|
-
const targetWidth = Math.max(8, width);
|
|
254
|
-
const title = ` ${normalized} `;
|
|
255
|
-
if (title.length >= targetWidth) {
|
|
256
|
-
return title.slice(0, targetWidth);
|
|
257
|
-
}
|
|
258
|
-
const remaining = targetWidth - title.length;
|
|
259
|
-
const left = '─'.repeat(Math.floor(remaining / 2));
|
|
260
|
-
const right = '─'.repeat(remaining - left.length);
|
|
261
|
-
return `${left}${title}${right}`;
|
|
262
|
-
}
|
|
263
|
-
function pickDiffColor(line) {
|
|
264
|
-
if (line.startsWith('+++') || line.startsWith('---')) {
|
|
265
|
-
return theme.diff.header;
|
|
266
|
-
}
|
|
267
|
-
if (line.startsWith('@@')) {
|
|
268
|
-
return theme.diff.hunk;
|
|
269
|
-
}
|
|
270
|
-
if (line.startsWith('+')) {
|
|
271
|
-
return theme.diff.added;
|
|
272
|
-
}
|
|
273
|
-
if (line.startsWith('-')) {
|
|
274
|
-
return theme.diff.removed;
|
|
275
|
-
}
|
|
276
|
-
if (line.startsWith('diff')) {
|
|
277
|
-
return theme.diff.meta;
|
|
278
|
-
}
|
|
279
|
-
return theme.ui.text;
|
|
280
|
-
}
|
|
281
|
-
function formatInlineText(text) {
|
|
282
|
-
if (!text) {
|
|
283
|
-
return '';
|
|
284
|
-
}
|
|
285
|
-
const codeSpans = [];
|
|
286
|
-
const linkSpans = [];
|
|
287
|
-
const LINK_PLACEHOLDER = '\u0001';
|
|
288
|
-
let result = text.replace(/`([^`]+)`/g, (_, inner) => {
|
|
289
|
-
codeSpans.push(inner);
|
|
290
|
-
return `\u0000${codeSpans.length - 1}\u0000`;
|
|
291
|
-
});
|
|
292
|
-
const formatBold = (_match, value) => theme.bold(value);
|
|
293
|
-
result = result.replace(/\*\*(.+?)\*\*/g, formatBold);
|
|
294
|
-
result = result.replace(/__(.+?)__/g, formatBold);
|
|
295
|
-
const formatItalics = (_match, value) => theme.italic(value);
|
|
296
|
-
result = result.replace(/(?<!\*)\*(?!\*)([^*]+?)(?<!\*)\*(?!\*)/g, formatItalics);
|
|
297
|
-
result = result.replace(/(?<!_)_(?!_)([^_]+?)(?<!_)_(?!_)/g, formatItalics);
|
|
298
|
-
result = result.replace(/~~(.+?)~~/g, (_match, value) => theme.dim(value));
|
|
299
|
-
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, url) => {
|
|
300
|
-
linkSpans.push(formatMarkdownLink(label, url));
|
|
301
|
-
return `${LINK_PLACEHOLDER}${linkSpans.length - 1}${LINK_PLACEHOLDER}`;
|
|
302
|
-
});
|
|
303
|
-
result = result.replace(/(\bhttps?:\/\/[^\s)]+)([)\]}>,.;:!?"]*)/g, (_match, url, trailing = '') => {
|
|
304
|
-
linkSpans.push(`${formatBareLink(url)}${trailing}`);
|
|
305
|
-
return `${LINK_PLACEHOLDER}${linkSpans.length - 1}${LINK_PLACEHOLDER}`;
|
|
306
|
-
});
|
|
307
|
-
result = result.replace(new RegExp(`${LINK_PLACEHOLDER}(\\d+)${LINK_PLACEHOLDER}`, 'g'), (_match, index) => linkSpans[Number.parseInt(index, 10)] ?? '');
|
|
308
|
-
result = result.replace(/\u0000(\d+)\u0000/g, (_match, index) => formatInlineCode(codeSpans[Number.parseInt(index, 10)] ?? ''));
|
|
309
|
-
return result;
|
|
310
|
-
}
|
|
311
|
-
function formatInlineCode(value) {
|
|
312
|
-
const normalized = value.length ? value.trim() : value;
|
|
313
|
-
const display = normalized.length ? normalized : value;
|
|
314
|
-
const snippet = display.replace(/\s+/g, ' ');
|
|
315
|
-
return theme.ui.background(theme.ui.text(` ${snippet} `));
|
|
316
|
-
}
|
|
317
|
-
function formatMarkdownLink(label, url) {
|
|
318
|
-
const cleanUrl = url.trim();
|
|
319
|
-
const cleanLabel = label.trim() || cleanUrl;
|
|
320
|
-
const labelColor = theme.link?.label ?? theme.secondary;
|
|
321
|
-
const urlColor = theme.link?.url ?? theme.info;
|
|
322
|
-
const styledLabel = labelColor(cleanLabel);
|
|
323
|
-
const styledUrl = urlColor(`(${cleanUrl})`);
|
|
324
|
-
return `${styledLabel} ${styledUrl}`;
|
|
325
|
-
}
|
|
326
|
-
function formatBareLink(url) {
|
|
327
|
-
const colorize = theme.link?.url ?? theme.info;
|
|
328
|
-
return colorize(url.trim());
|
|
329
|
-
}
|
|
330
|
-
function pickHeadingAccent(level) {
|
|
331
|
-
if (level <= 1) {
|
|
332
|
-
return theme.primary;
|
|
333
|
-
}
|
|
334
|
-
if (level === 2) {
|
|
335
|
-
return theme.secondary;
|
|
336
|
-
}
|
|
337
|
-
return theme.assistant;
|
|
338
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Keyboard Shortcuts Help Display (Claude Code style)
|
|
3
|
-
*/
|
|
4
|
-
import { theme } from './theme.js';
|
|
5
|
-
import { getTerminalColumns } from './layout.js';
|
|
6
|
-
const EROSOLAR_SHORTCUTS = [
|
|
7
|
-
{
|
|
8
|
-
title: 'Navigation',
|
|
9
|
-
shortcuts: [
|
|
10
|
-
{ keys: 'ctrl+c', description: 'Cancel current operation' },
|
|
11
|
-
{ keys: 'ctrl+d', description: 'Exit (when input is empty)' },
|
|
12
|
-
{ keys: 'up/down', description: 'Navigate command history' },
|
|
13
|
-
],
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
title: 'Editing',
|
|
17
|
-
shortcuts: [
|
|
18
|
-
{ keys: 'ctrl+a', description: 'Move to start of line' },
|
|
19
|
-
{ keys: 'ctrl+e', description: 'Move to end of line' },
|
|
20
|
-
{ keys: 'ctrl+k', description: 'Delete to end of line' },
|
|
21
|
-
{ keys: 'ctrl+u', description: 'Delete to start of line' },
|
|
22
|
-
{ keys: 'ctrl+w', description: 'Delete previous word' },
|
|
23
|
-
],
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
title: 'Composable Messages',
|
|
27
|
-
shortcuts: [
|
|
28
|
-
{ keys: 'ctrl+g', description: 'Edit pasted content blocks' },
|
|
29
|
-
{ keys: 'shift+tab', description: 'Cycle paste preview options' },
|
|
30
|
-
{ keys: 'enter', description: 'Send message with blocks' },
|
|
31
|
-
],
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
title: 'Special',
|
|
35
|
-
shortcuts: [
|
|
36
|
-
{ keys: '/', description: 'Trigger slash commands' },
|
|
37
|
-
{ keys: '?', description: 'Show this help' },
|
|
38
|
-
{ keys: 'ctrl+o', description: 'Expand collapsed output' },
|
|
39
|
-
],
|
|
40
|
-
},
|
|
41
|
-
];
|
|
42
|
-
/**
|
|
43
|
-
* Format keyboard shortcuts help in Claude Code style
|
|
44
|
-
*/
|
|
45
|
-
export function formatShortcutsHelp() {
|
|
46
|
-
const width = Math.min(getTerminalColumns(), 80);
|
|
47
|
-
const lines = [];
|
|
48
|
-
// Title
|
|
49
|
-
lines.push('');
|
|
50
|
-
lines.push(theme.gradient.primary('Keyboard Shortcuts'));
|
|
51
|
-
lines.push(theme.ui.muted('─'.repeat(width - 1)));
|
|
52
|
-
lines.push('');
|
|
53
|
-
// Groups
|
|
54
|
-
for (const group of EROSOLAR_SHORTCUTS) {
|
|
55
|
-
lines.push(theme.bold(` ${group.title}`));
|
|
56
|
-
lines.push('');
|
|
57
|
-
for (const shortcut of group.shortcuts) {
|
|
58
|
-
const keys = theme.info(shortcut.keys.padEnd(20));
|
|
59
|
-
const desc = theme.ui.muted(shortcut.description);
|
|
60
|
-
lines.push(` ${keys} ${desc}`);
|
|
61
|
-
}
|
|
62
|
-
lines.push('');
|
|
63
|
-
}
|
|
64
|
-
// Footer
|
|
65
|
-
lines.push(theme.ui.muted('─'.repeat(width - 1)));
|
|
66
|
-
lines.push(theme.ui.muted(' Press any key to continue...'));
|
|
67
|
-
lines.push('');
|
|
68
|
-
return lines.join('\n');
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Format inline shortcut hint (Claude Code style)
|
|
72
|
-
*/
|
|
73
|
-
export function formatShortcutHint(keys, description) {
|
|
74
|
-
return `${theme.info(keys)} ${theme.ui.muted(description)}`;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Quick shortcuts reference (one-line)
|
|
78
|
-
*/
|
|
79
|
-
export function formatQuickShortcuts() {
|
|
80
|
-
const hints = [
|
|
81
|
-
'? for help',
|
|
82
|
-
'/ for commands',
|
|
83
|
-
'ctrl+c to cancel',
|
|
84
|
-
'ctrl+d to exit',
|
|
85
|
-
];
|
|
86
|
-
return hints.map(h => theme.ui.muted(h)).join(' · ');
|
|
87
|
-
}
|