@deepseek-harness-tui/dsh-tui 0.7.2 → 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.
- package/README.md +3 -3
- package/bin/dsh-tui.js +24 -3
- package/cordis.patch.yml +24 -11
- package/cordis.yml +9 -1
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +2 -1
- package/lib/types/components/GoalTodoPanel.d.ts.map +1 -1
- package/lib/types/components/GoalTodoPanel.js +6 -1
- package/lib/types/components/HistorySearchDialog.d.ts.map +1 -1
- package/lib/types/components/HistorySearchDialog.js +5 -1
- package/lib/types/components/MessageList.d.ts +3 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +9 -5
- package/lib/types/components/PluginSceneBoundary.d.ts +31 -0
- package/lib/types/components/PluginSceneBoundary.d.ts.map +1 -0
- package/lib/types/components/PluginSceneBoundary.js +29 -0
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +52 -74
- package/lib/types/components/SearchBox.d.ts +4 -0
- package/lib/types/components/SearchBox.d.ts.map +1 -1
- package/lib/types/components/SearchBox.js +101 -2
- package/lib/types/components/SkillsPicker.d.ts +18 -0
- package/lib/types/components/SkillsPicker.d.ts.map +1 -0
- package/lib/types/components/SkillsPicker.js +50 -0
- package/lib/types/components/Spinner/SpinnerAnimationRow.d.ts +4 -1
- package/lib/types/components/Spinner/SpinnerAnimationRow.d.ts.map +1 -1
- package/lib/types/components/Spinner/SpinnerAnimationRow.js +10 -5
- package/lib/types/components/SplitDiffView.d.ts +28 -0
- package/lib/types/components/SplitDiffView.d.ts.map +1 -0
- package/lib/types/components/SplitDiffView.js +417 -0
- package/lib/types/components/WorkingSpinner.d.ts +3 -1
- package/lib/types/components/WorkingSpinner.d.ts.map +1 -1
- package/lib/types/components/WorkingSpinner.js +2 -2
- package/lib/types/components/design-system/ThemedText.d.ts +7 -1
- package/lib/types/components/design-system/ThemedText.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemedText.js +2 -2
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +3 -1
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +17 -7
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +52 -8
- package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
- package/lib/types/components/questions/PlanReviewPanel.js +12 -3
- package/lib/types/dsh-adapter/channel.d.ts +78 -0
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +165 -14
- package/lib/types/dsh-adapter/contract.d.ts +1 -1
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +1 -0
- package/lib/types/dsh-adapter/index.d.ts +4 -0
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +1 -0
- package/lib/types/dsh-adapter/packaged-presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/packaged-presets.js +5 -2
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +66 -3
- package/lib/types/dsh-adapter/promptDebug.d.ts +10 -0
- package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -0
- package/lib/types/dsh-adapter/promptDebug.js +148 -0
- package/lib/types/dsh-adapter/providerWizard.d.ts.map +1 -1
- package/lib/types/dsh-adapter/providerWizard.js +3 -1
- package/lib/types/dsh-adapter/scenes.d.ts +75 -0
- package/lib/types/dsh-adapter/scenes.d.ts.map +1 -0
- package/lib/types/dsh-adapter/scenes.js +78 -0
- package/lib/types/dsh-adapter/settings-sections.d.ts +116 -0
- package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -0
- package/lib/types/dsh-adapter/settings-sections.js +63 -0
- package/lib/types/dsh-adapter/settingsEditor.d.ts +128 -0
- package/lib/types/dsh-adapter/settingsEditor.d.ts.map +1 -0
- package/lib/types/dsh-adapter/settingsEditor.js +244 -0
- package/lib/types/i18n.d.ts +169 -18
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +45 -6
- package/lib/types/ink/components/Text.d.ts +9 -1
- package/lib/types/ink/components/Text.d.ts.map +1 -1
- package/lib/types/ink/components/Text.js +7 -6
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +4 -2
- package/lib/types/ink/log-update.d.ts.map +1 -1
- package/lib/types/ink/log-update.js +76 -9
- package/lib/types/ink/parse-keypress.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.js +4 -1
- package/lib/types/ink/terminal.d.ts +40 -4
- package/lib/types/ink/terminal.d.ts.map +1 -1
- package/lib/types/ink/terminal.js +57 -5
- package/lib/types/ink/termio/tokenize.d.ts +6 -0
- package/lib/types/ink/termio/tokenize.d.ts.map +1 -1
- package/lib/types/ink/termio/tokenize.js +18 -4
- package/lib/types/jsx-runtime.d.ts +2 -0
- package/lib/types/jsx-runtime.d.ts.map +1 -0
- package/lib/types/jsx-runtime.js +13 -0
- package/lib/types/modelRoute.d.ts +10 -2
- package/lib/types/modelRoute.d.ts.map +1 -1
- package/lib/types/modelRoute.js +33 -4
- package/lib/types/scenes.d.ts +3 -0
- package/lib/types/scenes.d.ts.map +1 -0
- package/lib/types/scenes.js +4 -0
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +182 -26
- package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
- package/lib/types/screens/SessionBrowser.js +30 -15
- package/lib/types/screens/Settings.d.ts +20 -0
- package/lib/types/screens/Settings.d.ts.map +1 -0
- package/lib/types/screens/Settings.js +333 -0
- package/lib/types/screens/StatusLine.js +1 -1
- package/lib/types/sessions/view.d.ts.map +1 -1
- package/lib/types/sessions/view.js +13 -1
- package/lib/types/settings-sections.d.ts +3 -0
- package/lib/types/settings-sections.d.ts.map +1 -0
- package/lib/types/settings-sections.js +4 -0
- package/lib/types/theme.d.ts +12 -0
- package/lib/types/theme.d.ts.map +1 -1
- package/lib/types/theme.js +36 -0
- package/lib/types/update.d.ts +21 -7
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +66 -16
- package/lib/types/utils/paths.d.ts.map +1 -1
- package/lib/types/utils/paths.js +4 -1
- package/package.json +49 -9
|
@@ -103,6 +103,8 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
103
103
|
controllerRef.current = {
|
|
104
104
|
hasText: () => value.length > 0,
|
|
105
105
|
clear: () => {
|
|
106
|
+
valueRef.current = '';
|
|
107
|
+
cursorRef.current = 0;
|
|
106
108
|
setValue('');
|
|
107
109
|
setCursor(0);
|
|
108
110
|
},
|
|
@@ -120,6 +122,8 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
120
122
|
React.useEffect(() => {
|
|
121
123
|
if (fillText && fillText !== lastFill.current) {
|
|
122
124
|
lastFill.current = fillText;
|
|
125
|
+
valueRef.current = fillText;
|
|
126
|
+
cursorRef.current = fillText.length;
|
|
123
127
|
setValue(fillText);
|
|
124
128
|
setCursor(fillText.length);
|
|
125
129
|
onFillConsumed?.();
|
|
@@ -132,16 +136,6 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
132
136
|
const clipboardBusyRef = React.useRef(false);
|
|
133
137
|
/** True while the external editor owns the terminal (Ctrl+X round-trip). */
|
|
134
138
|
const editorBusyRef = React.useRef(false);
|
|
135
|
-
/**
|
|
136
|
-
* Latest committed input state. The Ctrl+V clipboard read resolves
|
|
137
|
-
* asynchronously; inserting against the render that STARTED the read
|
|
138
|
-
* would drop whatever the user typed while waiting. The continuation
|
|
139
|
-
* inserts against this mirror instead.
|
|
140
|
-
*/
|
|
141
|
-
const liveInputRef = React.useRef({ value: '', cursor: 0 });
|
|
142
|
-
React.useEffect(() => {
|
|
143
|
-
liveInputRef.current = { value, cursor };
|
|
144
|
-
});
|
|
145
139
|
/** Enter dedupe window: cmd pipelines can deliver one Enter as `\r`+`\n`. */
|
|
146
140
|
const lastEnterAtRef = React.useRef(0);
|
|
147
141
|
React.useEffect(() => {
|
|
@@ -193,6 +187,12 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
193
187
|
!helpOpen &&
|
|
194
188
|
!selectionActive &&
|
|
195
189
|
fileEscRef.current !== mention?.start;
|
|
190
|
+
const setInput = (next, cursorOffset = next.length) => {
|
|
191
|
+
valueRef.current = next;
|
|
192
|
+
cursorRef.current = Math.max(0, Math.min(cursorOffset, next.length));
|
|
193
|
+
setValue(next);
|
|
194
|
+
setCursor(cursorRef.current);
|
|
195
|
+
};
|
|
196
196
|
/**
|
|
197
197
|
* Accept the selected file suggestion: replace ONLY the mention token at
|
|
198
198
|
* the caret (prefix/suffix text survives), quoting whitespace paths. A
|
|
@@ -216,8 +216,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
216
216
|
if (history.current.length > HISTORY_LIMIT)
|
|
217
217
|
history.current.shift();
|
|
218
218
|
historyIndex.current = -1;
|
|
219
|
-
|
|
220
|
-
setCursor(0);
|
|
219
|
+
setInput('', 0);
|
|
221
220
|
setSelectedCommand(0);
|
|
222
221
|
appendHistory(trimmed);
|
|
223
222
|
channel.submit(trimmed);
|
|
@@ -243,8 +242,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
243
242
|
if (history.current.length > HISTORY_LIMIT)
|
|
244
243
|
history.current.shift();
|
|
245
244
|
historyIndex.current = -1;
|
|
246
|
-
|
|
247
|
-
setCursor(0);
|
|
245
|
+
setInput('', 0);
|
|
248
246
|
setSelectedCommand(0);
|
|
249
247
|
appendHistory(trimmed);
|
|
250
248
|
channel.steer(trimmed);
|
|
@@ -262,8 +260,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
262
260
|
if (history.current.length > HISTORY_LIMIT)
|
|
263
261
|
history.current.shift();
|
|
264
262
|
historyIndex.current = -1;
|
|
265
|
-
|
|
266
|
-
setCursor(0);
|
|
263
|
+
setInput('', 0);
|
|
267
264
|
setSelectedCommand(0);
|
|
268
265
|
appendHistory(trimmed);
|
|
269
266
|
channel.submit(trimmed);
|
|
@@ -282,8 +279,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
282
279
|
channel.notify(t('input-cannot-retract'), { color: 'warning', timeoutMs: 2500 });
|
|
283
280
|
return;
|
|
284
281
|
}
|
|
285
|
-
|
|
286
|
-
setCursor(item.text.length);
|
|
282
|
+
setInput(item.text);
|
|
287
283
|
setSelectedCommand(0);
|
|
288
284
|
setFileSelected(0);
|
|
289
285
|
channel.notify(t('input-retracted'), { timeoutMs: 2000 });
|
|
@@ -308,8 +304,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
308
304
|
if (history.current.length > HISTORY_LIMIT)
|
|
309
305
|
history.current.shift();
|
|
310
306
|
historyIndex.current = -1;
|
|
311
|
-
|
|
312
|
-
setCursor(0);
|
|
307
|
+
setInput('', 0);
|
|
313
308
|
setSelectedCommand(0);
|
|
314
309
|
setFileSelected(0);
|
|
315
310
|
appendHistory(trimmed);
|
|
@@ -336,19 +331,12 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
336
331
|
if (history.current.length > HISTORY_LIMIT)
|
|
337
332
|
history.current.shift();
|
|
338
333
|
historyIndex.current = -1;
|
|
339
|
-
|
|
340
|
-
setCursor(0);
|
|
334
|
+
setInput('', 0);
|
|
341
335
|
setSelectedCommand(0);
|
|
342
336
|
appendHistory(text.trim());
|
|
343
337
|
}
|
|
344
338
|
return handled;
|
|
345
339
|
};
|
|
346
|
-
const setInput = (next, cursorOffset = next.length) => {
|
|
347
|
-
valueRef.current = next;
|
|
348
|
-
cursorRef.current = Math.max(0, Math.min(cursorOffset, next.length));
|
|
349
|
-
setValue(next);
|
|
350
|
-
setCursor(cursorRef.current);
|
|
351
|
-
};
|
|
352
340
|
/** Clipboard reads are asynchronous; insert against the latest render so
|
|
353
341
|
* typing while PowerShell owns the clipboard never gets overwritten. */
|
|
354
342
|
const insertClipboardAtCaret = (text) => {
|
|
@@ -359,13 +347,13 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
359
347
|
setFileSelected(0);
|
|
360
348
|
};
|
|
361
349
|
/** Line index of the cursor; -1 when the cursor is at the very end. */
|
|
362
|
-
const cursorLine = () => {
|
|
363
|
-
const before =
|
|
350
|
+
const cursorLine = (text, cursorOffset) => {
|
|
351
|
+
const before = text.slice(0, cursorOffset);
|
|
364
352
|
return before.split('\n').length - 1;
|
|
365
353
|
};
|
|
366
354
|
/** Column of the cursor within its line. */
|
|
367
|
-
const cursorColumn = () => {
|
|
368
|
-
const before =
|
|
355
|
+
const cursorColumn = (text, cursorOffset) => {
|
|
356
|
+
const before = text.slice(0, cursorOffset);
|
|
369
357
|
const line = before.split('\n').pop() ?? '';
|
|
370
358
|
return line.length;
|
|
371
359
|
};
|
|
@@ -377,13 +365,17 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
377
365
|
// setValue can never overwrite fresh typing (and vice versa).
|
|
378
366
|
if (editorBusyRef.current)
|
|
379
367
|
return;
|
|
368
|
+
// App deliberately dispatches every parsed key from one stdin read in a
|
|
369
|
+
// single React update. Read the synchronous mirrors so each event sees
|
|
370
|
+
// the text/caret produced by the preceding event in that batch.
|
|
371
|
+
const value = valueRef.current;
|
|
372
|
+
const cursor = cursorRef.current;
|
|
380
373
|
/** Insert text at the caret (typing, paste) and dismiss overlays. */
|
|
381
374
|
const insertAtCaret = (text) => {
|
|
382
375
|
if (helpOpen)
|
|
383
376
|
onToggleHelp();
|
|
384
377
|
const next = value.slice(0, cursor) + text + value.slice(cursor);
|
|
385
|
-
|
|
386
|
-
setCursor(cursor + text.length);
|
|
378
|
+
setInput(next, cursor + text.length);
|
|
387
379
|
setSelectedCommand(0);
|
|
388
380
|
setFileSelected(0);
|
|
389
381
|
};
|
|
@@ -441,10 +433,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
441
433
|
// Insert against the LIVE input state: the read above resolved
|
|
442
434
|
// asynchronously and the user may have typed while waiting.
|
|
443
435
|
const text = formatClipboardInsert(content);
|
|
444
|
-
|
|
445
|
-
const caret = Math.min(live.cursor, live.value.length);
|
|
446
|
-
setValue(live.value.slice(0, caret) + text + live.value.slice(caret));
|
|
447
|
-
setCursor(caret + text.length);
|
|
436
|
+
insertClipboardAtCaret(text);
|
|
448
437
|
})
|
|
449
438
|
.catch(() => {
|
|
450
439
|
channel.notify(t('input-clipboard-read-failed'), { color: 'warning' });
|
|
@@ -469,8 +458,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
469
458
|
try {
|
|
470
459
|
const outcome = await editInExternalEditor(value);
|
|
471
460
|
if (outcome.kind === 'edited') {
|
|
472
|
-
|
|
473
|
-
setCursor(outcome.text.length);
|
|
461
|
+
setInput(outcome.text);
|
|
474
462
|
setSelectedCommand(0);
|
|
475
463
|
setFileSelected(0);
|
|
476
464
|
}
|
|
@@ -575,8 +563,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
575
563
|
// ink/terminal.ts); Option+Enter (ESC CR) is the fallback on terminals
|
|
576
564
|
// that can't report shift — e.g. macOS Terminal.app (issue #110).
|
|
577
565
|
const next = value.slice(0, cursor) + '\n' + value.slice(cursor);
|
|
578
|
-
|
|
579
|
-
setCursor(cursor + 1);
|
|
566
|
+
setInput(next, cursor + 1);
|
|
580
567
|
setSelectedCommand(0);
|
|
581
568
|
return;
|
|
582
569
|
}
|
|
@@ -620,13 +607,13 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
620
607
|
setFileSelected(index => index <= 0 ? fileMatches.length - 1 : index - 1);
|
|
621
608
|
return;
|
|
622
609
|
}
|
|
623
|
-
const line = cursorLine();
|
|
610
|
+
const line = cursorLine(value, cursor);
|
|
624
611
|
if (line > 0) {
|
|
625
612
|
// Move to the previous line, clamping to its length.
|
|
626
613
|
const upToLineStart = value.lastIndexOf('\n', cursor - 1);
|
|
627
614
|
const prevLineStart = upToLineStart === -1 ? 0 : value.lastIndexOf('\n', upToLineStart - 1) + 1;
|
|
628
615
|
const prevLine = value.slice(prevLineStart, upToLineStart);
|
|
629
|
-
|
|
616
|
+
setInput(value, prevLineStart + Math.min(cursorColumn(value, cursor), prevLine.length));
|
|
630
617
|
return;
|
|
631
618
|
}
|
|
632
619
|
if (overlayOpen) {
|
|
@@ -639,8 +626,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
639
626
|
? history.current.length - 1
|
|
640
627
|
: Math.max(0, historyIndex.current - 1);
|
|
641
628
|
const entry = history.current[historyIndex.current] ?? '';
|
|
642
|
-
|
|
643
|
-
setCursor(entry.length);
|
|
629
|
+
setInput(entry);
|
|
644
630
|
return;
|
|
645
631
|
}
|
|
646
632
|
if (key.downArrow) {
|
|
@@ -648,13 +634,13 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
648
634
|
setFileSelected(index => index >= fileMatches.length - 1 ? 0 : index + 1);
|
|
649
635
|
return;
|
|
650
636
|
}
|
|
651
|
-
const line = cursorLine();
|
|
637
|
+
const line = cursorLine(value, cursor);
|
|
652
638
|
const lines = value.split('\n');
|
|
653
639
|
if (line < lines.length - 1) {
|
|
654
640
|
const nextLineStart = value.indexOf('\n', cursor) + 1;
|
|
655
641
|
const nextLineEnd = value.indexOf('\n', nextLineStart);
|
|
656
642
|
const nextLine = value.slice(nextLineStart, nextLineEnd === -1 ? value.length : nextLineEnd);
|
|
657
|
-
|
|
643
|
+
setInput(value, nextLineStart + Math.min(cursorColumn(value, cursor), nextLine.length));
|
|
658
644
|
return;
|
|
659
645
|
}
|
|
660
646
|
if (overlayOpen) {
|
|
@@ -667,76 +653,73 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
667
653
|
const entry = historyIndex.current >= history.current.length
|
|
668
654
|
? ''
|
|
669
655
|
: (history.current[historyIndex.current] ?? '');
|
|
670
|
-
|
|
671
|
-
setCursor(entry.length);
|
|
656
|
+
setInput(entry);
|
|
672
657
|
return;
|
|
673
658
|
}
|
|
674
659
|
if (isMod(key) && key.leftArrow) {
|
|
675
660
|
// Jump to the previous word boundary (readline alt+b). Must precede the
|
|
676
661
|
// bare-arrow arms: Ctrl+Left arrives as leftArrow + ctrl.
|
|
677
|
-
|
|
662
|
+
setInput(value, wordBoundaryLeft(value, cursor));
|
|
678
663
|
return;
|
|
679
664
|
}
|
|
680
665
|
if (isMod(key) && key.rightArrow) {
|
|
681
666
|
// Jump to the next word boundary (readline alt+f).
|
|
682
|
-
|
|
667
|
+
setInput(value, wordBoundaryRight(value, cursor));
|
|
683
668
|
return;
|
|
684
669
|
}
|
|
685
670
|
if (key.leftArrow) {
|
|
686
|
-
|
|
671
|
+
setInput(value, Math.max(0, cursor - 1));
|
|
687
672
|
return;
|
|
688
673
|
}
|
|
689
674
|
if (key.rightArrow) {
|
|
690
|
-
|
|
675
|
+
setInput(value, Math.min(value.length, cursor + 1));
|
|
691
676
|
return;
|
|
692
677
|
}
|
|
693
678
|
if (key.backspace) {
|
|
694
679
|
if (cursor === 0)
|
|
695
680
|
return;
|
|
696
|
-
|
|
697
|
-
setCursor(cursor - 1);
|
|
681
|
+
setInput(value.slice(0, cursor - 1) + value.slice(cursor), cursor - 1);
|
|
698
682
|
return;
|
|
699
683
|
}
|
|
700
684
|
if (key.delete) {
|
|
701
685
|
if (cursor >= value.length)
|
|
702
686
|
return;
|
|
703
|
-
|
|
687
|
+
setInput(value.slice(0, cursor) + value.slice(cursor + 1), cursor);
|
|
704
688
|
return;
|
|
705
689
|
}
|
|
706
690
|
if (key.home) {
|
|
707
691
|
// Start of the current line.
|
|
708
692
|
const lineStart = value.lastIndexOf('\n', cursor - 1) + 1;
|
|
709
|
-
|
|
693
|
+
setInput(value, lineStart);
|
|
710
694
|
return;
|
|
711
695
|
}
|
|
712
696
|
if (key.end) {
|
|
713
697
|
// End of the current line.
|
|
714
698
|
const nextLine = value.indexOf('\n', cursor);
|
|
715
|
-
|
|
699
|
+
setInput(value, nextLine === -1 ? value.length : nextLine);
|
|
716
700
|
return;
|
|
717
701
|
}
|
|
718
702
|
if (isMod(key) && input === 'a') {
|
|
719
703
|
const lineStart = value.lastIndexOf('\n', cursor - 1) + 1;
|
|
720
|
-
|
|
704
|
+
setInput(value, lineStart);
|
|
721
705
|
return;
|
|
722
706
|
}
|
|
723
707
|
if (isMod(key) && input === 'e') {
|
|
724
708
|
const nextLine = value.indexOf('\n', cursor);
|
|
725
|
-
|
|
709
|
+
setInput(value, nextLine === -1 ? value.length : nextLine);
|
|
726
710
|
return;
|
|
727
711
|
}
|
|
728
712
|
if (isMod(key) && input === 'u') {
|
|
729
713
|
// Delete to start of line.
|
|
730
714
|
const lineStart = value.lastIndexOf('\n', cursor - 1) + 1;
|
|
731
|
-
|
|
732
|
-
setCursor(lineStart);
|
|
715
|
+
setInput(value.slice(0, lineStart) + value.slice(cursor), lineStart);
|
|
733
716
|
return;
|
|
734
717
|
}
|
|
735
718
|
if (isMod(key) && input === 'k') {
|
|
736
719
|
// Delete to end of line.
|
|
737
720
|
const nextLine = value.indexOf('\n', cursor);
|
|
738
721
|
const end = nextLine === -1 ? value.length : nextLine;
|
|
739
|
-
|
|
722
|
+
setInput(value.slice(0, cursor) + value.slice(end), cursor);
|
|
740
723
|
return;
|
|
741
724
|
}
|
|
742
725
|
if (isMod(key) && input === 'w') {
|
|
@@ -749,8 +732,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
749
732
|
let start = end;
|
|
750
733
|
while (start > 0 && !/\s/.test(before[start - 1]))
|
|
751
734
|
start--;
|
|
752
|
-
|
|
753
|
-
setCursor(start);
|
|
735
|
+
setInput(value.slice(0, start) + value.slice(cursor), start);
|
|
754
736
|
return;
|
|
755
737
|
}
|
|
756
738
|
if (key.escape) {
|
|
@@ -761,8 +743,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
761
743
|
// A single Esc closes the open command menu first (CC/pi behavior);
|
|
762
744
|
// the double-tap-clear semantics only apply to ordinary input.
|
|
763
745
|
if (overlayOpen) {
|
|
764
|
-
|
|
765
|
-
setCursor(0);
|
|
746
|
+
setInput('', 0);
|
|
766
747
|
setSelectedCommand(0);
|
|
767
748
|
setFileSelected(0);
|
|
768
749
|
return;
|
|
@@ -786,8 +767,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
786
767
|
// A single Esc clears the current input (if any); the double-tap
|
|
787
768
|
// path below handles rewind/clear on an already-empty input.
|
|
788
769
|
if (value.length > 0) {
|
|
789
|
-
|
|
790
|
-
setCursor(0);
|
|
770
|
+
setInput('', 0);
|
|
791
771
|
setSelectedCommand(0);
|
|
792
772
|
setFileSelected(0);
|
|
793
773
|
return;
|
|
@@ -803,8 +783,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
803
783
|
onRewindRequest?.();
|
|
804
784
|
}
|
|
805
785
|
else {
|
|
806
|
-
|
|
807
|
-
setCursor(0);
|
|
786
|
+
setInput('', 0);
|
|
808
787
|
}
|
|
809
788
|
return;
|
|
810
789
|
}
|
|
@@ -826,8 +805,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
826
805
|
if (helpOpen)
|
|
827
806
|
onToggleHelp();
|
|
828
807
|
const next = value.slice(0, cursor) + input + value.slice(cursor);
|
|
829
|
-
|
|
830
|
-
setCursor(cursor + input.length);
|
|
808
|
+
setInput(next, cursor + input.length);
|
|
831
809
|
setSelectedCommand(0);
|
|
832
810
|
setFileSelected(0);
|
|
833
811
|
}
|
|
@@ -6,6 +6,10 @@ import React from 'react';
|
|
|
6
6
|
* placeholder is right-aligned (dimmed) — kept off the caret's cell so the
|
|
7
7
|
* terminal-painted IME preedit (pinyin) can never be overlaid on it during
|
|
8
8
|
* CJK composition.
|
|
9
|
+
*
|
|
10
|
+
* The query row is strictly single-line: an overlong query is windowed
|
|
11
|
+
* around the caret (horizontal scroll) instead of wrapping, so the native
|
|
12
|
+
* cursor declaration below stays exact for any query length.
|
|
9
13
|
*/
|
|
10
14
|
export declare function SearchBox({ query, placeholder, isFocused, isTerminalFocused, prefix, width, cursorOffset, borderless, }: {
|
|
11
15
|
query: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchBox.d.ts","sourceRoot":"","sources":["../../../src/components/SearchBox.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"SearchBox.d.ts","sourceRoot":"","sources":["../../../src/components/SearchBox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyD,MAAM,OAAO,CAAA;AAyD7E;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,WAAuB,EACvB,SAAS,EACT,iBAAiB,EACjB,MAAY,EACZ,KAAK,EACL,YAAY,EACZ,UAAkB,GACnB,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,OAAO,CAAA;IAClB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,GAAG,KAAK,CAAC,SAAS,CA0GlB"}
|
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
|
2
3
|
import Box from '../ink/components/Box.js';
|
|
3
4
|
import Text from '../ink/components/Text.js';
|
|
5
|
+
import measureElement from '../ink/measure-element.js';
|
|
6
|
+
import { useDeclaredCursor } from '../ink/hooks/use-declared-cursor.js';
|
|
7
|
+
import { useTerminalSize } from '../ink/hooks/use-terminal-size.js';
|
|
8
|
+
import { stringWidth } from '../ink/stringWidth.js';
|
|
9
|
+
/**
|
|
10
|
+
* Window a single-line query around the caret so the visible slice fits
|
|
11
|
+
* `avail` display cells (CJK display width, code-point safe — `[...s]`
|
|
12
|
+
* iterates code points, not UTF-16 units). The caret character itself
|
|
13
|
+
* always stays inside the window: leading characters are dropped until
|
|
14
|
+
* before-caret text + caret fit, then after-caret text fills what remains.
|
|
15
|
+
* `offset` arrives in UTF-16 units (callers move the caret by key count);
|
|
16
|
+
* a mid-surrogate offset would split an emoji, so it snaps back to the
|
|
17
|
+
* pair's start first.
|
|
18
|
+
*/
|
|
19
|
+
function windowQuery(query, offset, avail) {
|
|
20
|
+
const budget = Math.max(avail, 1);
|
|
21
|
+
let caret = Math.max(0, Math.min(offset, query.length));
|
|
22
|
+
if (caret > 0 &&
|
|
23
|
+
caret < query.length &&
|
|
24
|
+
query.charCodeAt(caret - 1) >= 0xd800 &&
|
|
25
|
+
query.charCodeAt(caret - 1) <= 0xdbff &&
|
|
26
|
+
query.charCodeAt(caret) >= 0xdc00 &&
|
|
27
|
+
query.charCodeAt(caret) <= 0xdfff) {
|
|
28
|
+
caret--; // mid-surrogate: snap to the emoji's start
|
|
29
|
+
}
|
|
30
|
+
const beforeChars = [...query.slice(0, caret)];
|
|
31
|
+
const at = caret < query.length ? [...query.slice(caret)][0] : ' ';
|
|
32
|
+
const atWidth = Math.max(1, stringWidth(at));
|
|
33
|
+
let caretColumn = 0;
|
|
34
|
+
for (const ch of beforeChars)
|
|
35
|
+
caretColumn += stringWidth(ch);
|
|
36
|
+
let start = 0;
|
|
37
|
+
while (start < beforeChars.length && caretColumn + atWidth > budget) {
|
|
38
|
+
caretColumn -= stringWidth(beforeChars[start]);
|
|
39
|
+
start++;
|
|
40
|
+
}
|
|
41
|
+
let rest = budget - caretColumn - atWidth;
|
|
42
|
+
let after = '';
|
|
43
|
+
for (const ch of [...query.slice(caret + at.length)]) {
|
|
44
|
+
const w = stringWidth(ch);
|
|
45
|
+
if (w > rest)
|
|
46
|
+
break;
|
|
47
|
+
after += ch;
|
|
48
|
+
rest -= w;
|
|
49
|
+
}
|
|
50
|
+
return { before: beforeChars.slice(start).join(''), at, after, caretColumn };
|
|
51
|
+
}
|
|
4
52
|
/**
|
|
5
53
|
* A single-line search input in the round-bordered box of Claude Code's
|
|
6
54
|
* SearchBox: `⌕ ` prefix, block cursor at `cursorOffset` (inverse cell).
|
|
@@ -8,6 +56,10 @@ import Text from '../ink/components/Text.js';
|
|
|
8
56
|
* placeholder is right-aligned (dimmed) — kept off the caret's cell so the
|
|
9
57
|
* terminal-painted IME preedit (pinyin) can never be overlaid on it during
|
|
10
58
|
* CJK composition.
|
|
59
|
+
*
|
|
60
|
+
* The query row is strictly single-line: an overlong query is windowed
|
|
61
|
+
* around the caret (horizontal scroll) instead of wrapping, so the native
|
|
62
|
+
* cursor declaration below stays exact for any query length.
|
|
11
63
|
*/
|
|
12
64
|
export function SearchBox({ query, placeholder = 'Search…', isFocused, isTerminalFocused, prefix = '⌕', width, cursorOffset, borderless = false, }) {
|
|
13
65
|
const offset = cursorOffset ?? query.length;
|
|
@@ -17,10 +69,57 @@ export function SearchBox({ query, placeholder = 'Search…', isFocused, isTermi
|
|
|
17
69
|
// Focused + empty + terminal focused: inline caret row (block caret at the
|
|
18
70
|
// start, placeholder right-aligned) instead of the inline placeholder.
|
|
19
71
|
const inlineCaret = isFocused && query === '' && isTerminalFocused;
|
|
72
|
+
// Content width of the box in display cells. Measured from yoga after
|
|
73
|
+
// layout (resize re-layouts without any prop/state change, so measure on
|
|
74
|
+
// every commit — the setState is a no-op when the width is unchanged);
|
|
75
|
+
// the terminal-columns estimate only covers the very first frame.
|
|
76
|
+
const { columns } = useTerminalSize();
|
|
77
|
+
const chrome = borderless ? 0 : 4; // 2 border cells + paddingX 2
|
|
78
|
+
const [measuredWidth, setMeasuredWidth] = useState(null);
|
|
79
|
+
const contentWidth = measuredWidth ?? Math.max(8, columns - chrome - 2);
|
|
80
|
+
const prefixWidth = stringWidth(`${prefix} `);
|
|
81
|
+
const win = windowQuery(query, offset, contentWidth - prefixWidth);
|
|
82
|
+
// Park the native terminal cursor at the caret so IME preedit (pinyin)
|
|
83
|
+
// renders inline at the input instead of the screen's bottom row (same
|
|
84
|
+
// mechanism as PromptInput). The declaration hangs on the outer Box —
|
|
85
|
+
// content stays a single Text (splitting it into flex siblings
|
|
86
|
+
// reorders/drops glyphs on narrow widths), so the position is computed:
|
|
87
|
+
// border (1) + paddingX (1) per edge when bordered, then the `prefix `
|
|
88
|
+
// run and the windowed before-caret text, all in display cells.
|
|
89
|
+
const showCaret = isFocused && isTerminalFocused;
|
|
90
|
+
// Clamp into the box's content area: on absurdly narrow layouts the
|
|
91
|
+
// prefix alone can meet or exceed the content width, and the park must
|
|
92
|
+
// never land outside the box's rect.
|
|
93
|
+
const edge = borderless ? 0 : 2;
|
|
94
|
+
const maxColumn = edge + Math.max(0, contentWidth - 1);
|
|
95
|
+
const caretColumn = Math.min(edge + prefixWidth + win.caretColumn, maxColumn);
|
|
96
|
+
const declarationRef = useDeclaredCursor({
|
|
97
|
+
line: borderless ? 0 : 1,
|
|
98
|
+
column: caretColumn,
|
|
99
|
+
active: showCaret,
|
|
100
|
+
});
|
|
101
|
+
const boxNodeRef = useRef(null);
|
|
102
|
+
const boxRef = useCallback((node) => {
|
|
103
|
+
boxNodeRef.current = node;
|
|
104
|
+
declarationRef(node);
|
|
105
|
+
}, [declarationRef]);
|
|
106
|
+
useLayoutEffect(() => {
|
|
107
|
+
const node = boxNodeRef.current;
|
|
108
|
+
if (!node)
|
|
109
|
+
return;
|
|
110
|
+
// Layout runs before layout effects (reconciler resetAfterCommit), so a
|
|
111
|
+
// zero raw width means a genuinely zero-width box — but a zero CONTENT
|
|
112
|
+
// width (chrome eats the whole box) is a real, must-clamp case.
|
|
113
|
+
const raw = measureElement(node).width;
|
|
114
|
+
if (raw > 0) {
|
|
115
|
+
const w = Math.max(0, raw - chrome);
|
|
116
|
+
setMeasuredWidth(prev => (prev === w ? prev : w));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
20
119
|
let content;
|
|
21
120
|
if (isFocused) {
|
|
22
121
|
if (query) {
|
|
23
|
-
content = isTerminalFocused ? (_jsxs(_Fragment, { children: [_jsx(Text, { children:
|
|
122
|
+
content = isTerminalFocused ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: win.before }), _jsx(Text, { inverse: true, children: win.at }), win.after !== '' && _jsx(Text, { children: win.after })] })) : (_jsx(Text, { children: query }));
|
|
24
123
|
}
|
|
25
124
|
else if (!isTerminalFocused) {
|
|
26
125
|
content = _jsx(Text, { dimColor: true, children: placeholder });
|
|
@@ -29,5 +128,5 @@ export function SearchBox({ query, placeholder = 'Search…', isFocused, isTermi
|
|
|
29
128
|
else {
|
|
30
129
|
content = query ? _jsx(Text, { children: query }) : _jsx(Text, { children: placeholder });
|
|
31
130
|
}
|
|
32
|
-
return (_jsx(Box, { flexShrink: 0, borderStyle: borderStyle, borderColor: borderColor, borderDimColor: borderDimColor, paddingX: borderless ? 0 : 1, width: width, children: inlineCaret ? (_jsxs(Box, { flexDirection: "row", width: "100%", children: [_jsxs(Text, { children: [prefix, " "] }), _jsx(Text, { inverse: true, children: " " }), _jsx(Box, { flexGrow: 1 }), _jsx(Text, { dimColor: true, wrap: "truncate", children: placeholder })] })) : (_jsxs(Text, { dimColor: !isFocused, children: [prefix, " ", content] })) }));
|
|
131
|
+
return (_jsx(Box, { ref: boxRef, flexShrink: 0, borderStyle: borderStyle, borderColor: borderColor, borderDimColor: borderDimColor, paddingX: borderless ? 0 : 1, width: width, children: inlineCaret ? (_jsxs(Box, { flexDirection: "row", width: "100%", children: [_jsxs(Text, { children: [prefix, " "] }), _jsx(Text, { inverse: true, children: " " }), _jsx(Box, { flexGrow: 1 }), _jsx(Text, { dimColor: true, wrap: "truncate", children: placeholder })] })) : (_jsxs(Text, { dimColor: !isFocused, wrap: "truncate-end", children: [prefix, " ", content] })) }));
|
|
33
132
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { SkillInfo } from '../dsh-adapter/channel.js';
|
|
3
|
+
/**
|
|
4
|
+
* `/skills` picker (issue #204) in the ModelPicker style: a
|
|
5
|
+
* permission-colored Pane listing the live agent's skill catalog —
|
|
6
|
+
* user-invocable skills lead with `/name`(它们在 / 菜单里也是这个形态),
|
|
7
|
+
* 描述行是「来源 · 简述」。Enter 由 Chat 填回 `/name `,Esc 关闭。
|
|
8
|
+
*
|
|
9
|
+
* 长列表按焦点窗口化(ModelPicker 同款):picker 经 OverlayAbove 浮层挂载后
|
|
10
|
+
* 有 maxHeight 裁剪,全量渲染会让焦点行被裁掉(看不到焦点按 Enter)。
|
|
11
|
+
*/
|
|
12
|
+
export declare function SkillsPicker({ skills, focusIndex, }: {
|
|
13
|
+
skills: readonly SkillInfo[];
|
|
14
|
+
focusIndex: number;
|
|
15
|
+
}): React.ReactNode;
|
|
16
|
+
/** `/skills` while the registry snapshot is still in flight (ModelPickerLoading 同款). */
|
|
17
|
+
export declare function SkillsPickerLoading(): React.ReactNode;
|
|
18
|
+
//# sourceMappingURL=SkillsPicker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SkillsPicker.d.ts","sourceRoot":"","sources":["../../../src/components/SkillsPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AA2B1D;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,UAAU,GACX,EAAE;IACD,MAAM,EAAE,SAAS,SAAS,EAAE,CAAA;IAC5B,UAAU,EAAE,MAAM,CAAA;CACnB,GAAG,KAAK,CAAC,SAAS,CAyClB;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,IAAI,KAAK,CAAC,SAAS,CAerD"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { t } from '../i18n.js';
|
|
3
|
+
import { Box, Text, useTerminalSize } from '../ui.js';
|
|
4
|
+
import { Pane } from './design-system/Pane.js';
|
|
5
|
+
import { ListItem } from './design-system/ListItem.js';
|
|
6
|
+
import { HintLine } from './design-system/HintLine.js';
|
|
7
|
+
import { LoadingState } from './design-system/LoadingState.js';
|
|
8
|
+
import { listWindow } from './listWindow.js';
|
|
9
|
+
/** 来源桶 → 本地化标签(未知桶原样显示,SkillSource 对自定义桶开放)。 */
|
|
10
|
+
function sourceLabel(source) {
|
|
11
|
+
switch (source) {
|
|
12
|
+
case 'bundled':
|
|
13
|
+
return t('skills-source-bundled');
|
|
14
|
+
case 'user-dsh':
|
|
15
|
+
case 'user-agents':
|
|
16
|
+
return t('skills-source-user');
|
|
17
|
+
case 'project-dsh':
|
|
18
|
+
case 'project-agents':
|
|
19
|
+
return t('skills-source-project');
|
|
20
|
+
case 'runtime':
|
|
21
|
+
return t('skills-source-runtime');
|
|
22
|
+
case 'custom':
|
|
23
|
+
return t('skills-source-custom');
|
|
24
|
+
default:
|
|
25
|
+
return source;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* `/skills` picker (issue #204) in the ModelPicker style: a
|
|
30
|
+
* permission-colored Pane listing the live agent's skill catalog —
|
|
31
|
+
* user-invocable skills lead with `/name`(它们在 / 菜单里也是这个形态),
|
|
32
|
+
* 描述行是「来源 · 简述」。Enter 由 Chat 填回 `/name `,Esc 关闭。
|
|
33
|
+
*
|
|
34
|
+
* 长列表按焦点窗口化(ModelPicker 同款):picker 经 OverlayAbove 浮层挂载后
|
|
35
|
+
* 有 maxHeight 裁剪,全量渲染会让焦点行被裁掉(看不到焦点按 Enter)。
|
|
36
|
+
*/
|
|
37
|
+
export function SkillsPicker({ skills, focusIndex, }) {
|
|
38
|
+
const { rows: terminalRows } = useTerminalSize();
|
|
39
|
+
// 每项恒占 2 行(正文 + 来源/简述描述行,均 truncate 成单行)。
|
|
40
|
+
// 框架行:浮层预留 8 + Pane 2 + 标题 2 + 页脚 1 = 13(ModelPicker 同款)。
|
|
41
|
+
const { start, end } = listWindow(skills.map(() => 2), focusIndex, Math.max(terminalRows - 13, 2));
|
|
42
|
+
return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: t('picker-title-skills') }) }), skills.length === 0 ? (_jsx(Text, { dimColor: true, children: t('skills-empty') })) : (skills.slice(start, end).map((skill, index) => {
|
|
43
|
+
const absoluteIndex = start + index;
|
|
44
|
+
return (_jsx(ListItem, { isFocused: absoluteIndex === focusIndex, description: `${sourceLabel(skill.source)}${skill.description === '' ? '' : ` · ${skill.description}`}`, showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < skills.length, children: skill.userInvocable ? `/${skill.name}` : skill.name }, skill.name));
|
|
45
|
+
}))] }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-fill-exit') }) })] }));
|
|
46
|
+
}
|
|
47
|
+
/** `/skills` while the registry snapshot is still in flight (ModelPickerLoading 同款). */
|
|
48
|
+
export function SkillsPickerLoading() {
|
|
49
|
+
return (_jsx(Pane, { color: "permission", children: _jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { bold: true, color: "permission", children: t('picker-title-skills') }), _jsx(LoadingState, { message: t('skills-loading'), bold: true, subtitle: t('skills-loading-subtitle') })] }) }));
|
|
50
|
+
}
|
|
@@ -7,6 +7,9 @@ export type SpinnerAnimationRowProps = {
|
|
|
7
7
|
hasActiveTools: boolean;
|
|
8
8
|
/** Raw response length (chars) — feeds the animated token counter. */
|
|
9
9
|
responseLengthRef: React.RefObject<number>;
|
|
10
|
+
/** Most recent request's real upload tokens (input + cache read/write);
|
|
11
|
+
* 0 until the first usage event lands. */
|
|
12
|
+
uploadTokensRef: React.RefObject<number>;
|
|
10
13
|
/** Stable within a turn. */
|
|
11
14
|
message: string;
|
|
12
15
|
messageColor: keyof Theme;
|
|
@@ -27,5 +30,5 @@ export type SpinnerAnimationRowProps = {
|
|
|
27
30
|
* animation clock (frame, glimmer, token counter animation, elapsed time,
|
|
28
31
|
* stalled intensity, thinking shimmer).
|
|
29
32
|
*/
|
|
30
|
-
export declare function SpinnerAnimationRow({ mode, reducedMotion, hasActiveTools, responseLengthRef, message, messageColor, shimmerColor, loadingStartTimeRef, totalPausedMsRef, pauseStartTimeRef, spinnerSuffix, verbose, columns, thinkingStatus, }: SpinnerAnimationRowProps): React.ReactNode;
|
|
33
|
+
export declare function SpinnerAnimationRow({ mode, reducedMotion, hasActiveTools, responseLengthRef, uploadTokensRef, message, messageColor, shimmerColor, loadingStartTimeRef, totalPausedMsRef, pauseStartTimeRef, spinnerSuffix, verbose, columns, thinkingStatus, }: SpinnerAnimationRowProps): React.ReactNode;
|
|
31
34
|
//# sourceMappingURL=SpinnerAnimationRow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpinnerAnimationRow.d.ts","sourceRoot":"","sources":["../../../../src/components/Spinner/SpinnerAnimationRow.tsx"],"names":[],"mappings":"AACA,OAAO,KAA0B,MAAM,OAAO,CAAA;AAM9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAI3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAcnD,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,WAAW,CAAA;IACjB,aAAa,EAAE,OAAO,CAAA;IACtB,cAAc,EAAE,OAAO,CAAA;IACvB,sEAAsE;IACtE,iBAAiB,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAC1C,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,KAAK,CAAA;IACzB,YAAY,EAAE,MAAM,KAAK,CAAA;IACzB,mBAAmB,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAC5C,gBAAgB,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IACzC,iBAAiB,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACjD,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,gFAAgF;IAChF,cAAc,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAA;CAC3C,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,OAAO,EACP,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,OAAO,EACP,OAAO,EACP,cAAc,GACf,EAAE,wBAAwB,GAAG,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"SpinnerAnimationRow.d.ts","sourceRoot":"","sources":["../../../../src/components/Spinner/SpinnerAnimationRow.tsx"],"names":[],"mappings":"AACA,OAAO,KAA0B,MAAM,OAAO,CAAA;AAM9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAI3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAcnD,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,WAAW,CAAA;IACjB,aAAa,EAAE,OAAO,CAAA;IACtB,cAAc,EAAE,OAAO,CAAA;IACvB,sEAAsE;IACtE,iBAAiB,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAC1C;+CAC2C;IAC3C,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IACxC,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,KAAK,CAAA;IACzB,YAAY,EAAE,MAAM,KAAK,CAAA;IACzB,mBAAmB,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAC5C,gBAAgB,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IACzC,iBAAiB,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACjD,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,gFAAgF;IAChF,cAAc,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAA;CAC3C,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,OAAO,EACP,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,OAAO,EACP,OAAO,EACP,cAAc,GACf,EAAE,wBAAwB,GAAG,KAAK,CAAC,SAAS,CA6M5C"}
|