@deepseek-harness-tui/dsh-tui 0.7.1 → 0.7.3
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 +7 -5
- package/bin/dsh-tui.js +42 -1
- package/cordis.patch.yml +10 -0
- package/lib/types/commands.js +1 -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/LoadedContextPanel.d.ts.map +1 -1
- package/lib/types/components/LoadedContextPanel.js +1 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +5 -1
- 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/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/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 +9 -3
- package/lib/types/components/design-system/ThemedText.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemedText.js +3 -5
- 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/components/sessions/SessionListRow.d.ts +26 -0
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -0
- package/lib/types/components/sessions/SessionListRow.js +35 -0
- package/lib/types/components/sessions/SessionPreview.d.ts +25 -0
- package/lib/types/components/sessions/SessionPreview.d.ts.map +1 -0
- package/lib/types/components/sessions/SessionPreview.js +60 -0
- package/lib/types/dsh-adapter/channel.d.ts +19 -4
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +76 -82
- package/lib/types/dsh-adapter/compat/index.d.ts +1 -1
- package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/index.js +1 -1
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts +8 -0
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/sessionLog.js +1 -1
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +10 -1
- package/lib/types/dsh-adapter/packaged-presets.d.ts +22 -0
- package/lib/types/dsh-adapter/packaged-presets.d.ts.map +1 -0
- package/lib/types/dsh-adapter/packaged-presets.js +93 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +53 -7
- package/lib/types/dsh-adapter/presets.d.ts +16 -0
- package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/presets.js +23 -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/sessions/digest.d.ts +29 -0
- package/lib/types/dsh-adapter/sessions/digest.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/digest.js +224 -0
- package/lib/types/dsh-adapter/sessions/frames.d.ts +100 -0
- package/lib/types/dsh-adapter/sessions/frames.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/frames.js +246 -0
- package/lib/types/dsh-adapter/sessions/header.d.ts +50 -0
- package/lib/types/dsh-adapter/sessions/header.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/header.js +62 -0
- package/lib/types/dsh-adapter/sessions/index.d.ts +17 -0
- package/lib/types/dsh-adapter/sessions/index.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/index.js +15 -0
- package/lib/types/dsh-adapter/sessions/list.d.ts +41 -0
- package/lib/types/dsh-adapter/sessions/list.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/list.js +213 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts +48 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/store.js +164 -0
- package/lib/types/dsh-adapter/sessions/types.d.ts +142 -0
- package/lib/types/dsh-adapter/sessions/types.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/types.js +14 -0
- package/lib/types/i18n.d.ts +150 -22
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +45 -9
- 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/screens/Chat.d.ts +3 -2
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +104 -160
- package/lib/types/screens/SessionBrowser.d.ts +37 -0
- package/lib/types/screens/SessionBrowser.d.ts.map +1 -0
- package/lib/types/screens/SessionBrowser.js +452 -0
- package/lib/types/sessionHistory.d.ts +0 -8
- package/lib/types/sessionHistory.d.ts.map +1 -1
- package/lib/types/sessions/format.d.ts +117 -0
- package/lib/types/sessions/format.d.ts.map +1 -0
- package/lib/types/sessions/format.js +244 -0
- package/lib/types/sessions/view.d.ts +125 -0
- package/lib/types/sessions/view.d.ts.map +1 -0
- package/lib/types/sessions/view.js +234 -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 +37 -9
- package/presets/liangshen/.dsh-tui-managed.json +5 -0
- package/presets/liangshen/agent.cordis.yml +389 -0
- package/presets/liangshen/compaction-epoch.mjs +81 -0
- package/presets/liangshen/custom-bash.mjs +126 -0
- package/presets/liangshen/instruction-hint.mjs +181 -0
- package/presets/liangshen/preset.yml +3 -0
- package/presets/liangshen/skill-search.mjs +142 -0
- package/presets/liangshen/tool-bootstrap.mjs +300 -0
- package/lib/types/components/ResumePicker.d.ts +0 -26
- package/lib/types/components/ResumePicker.d.ts.map +0 -1
- package/lib/types/components/ResumePicker.js +0 -50
|
@@ -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
|
}
|
|
@@ -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"}
|
|
@@ -26,7 +26,7 @@ const THINKING_GLOW_PERIOD_S = 2;
|
|
|
26
26
|
* animation clock (frame, glimmer, token counter animation, elapsed time,
|
|
27
27
|
* stalled intensity, thinking shimmer).
|
|
28
28
|
*/
|
|
29
|
-
export function SpinnerAnimationRow({ mode, reducedMotion, hasActiveTools, responseLengthRef, message, messageColor, shimmerColor, loadingStartTimeRef, totalPausedMsRef, pauseStartTimeRef, spinnerSuffix, verbose, columns, thinkingStatus, }) {
|
|
29
|
+
export function SpinnerAnimationRow({ mode, reducedMotion, hasActiveTools, responseLengthRef, uploadTokensRef, message, messageColor, shimmerColor, loadingStartTimeRef, totalPausedMsRef, pauseStartTimeRef, spinnerSuffix, verbose, columns, thinkingStatus, }) {
|
|
30
30
|
const [viewportRef, time] = useAnimationFrame(reducedMotion ? null : 50);
|
|
31
31
|
// === Elapsed time (wall-clock, derived from refs each frame) ===
|
|
32
32
|
const now = Date.now();
|
|
@@ -81,8 +81,13 @@ export function SpinnerAnimationRow({ mode, reducedMotion, hasActiveTools, respo
|
|
|
81
81
|
const timerText = formatDuration(elapsedTimeMs);
|
|
82
82
|
const timerWidth = stringWidth(timerText);
|
|
83
83
|
const tokenCount = formatNumber(leaderTokens);
|
|
84
|
-
const
|
|
85
|
-
|
|
84
|
+
const uploadTokens = uploadTokensRef.current;
|
|
85
|
+
// Real upload tokens (last request's input + cache) ride beside the
|
|
86
|
+
// animated download estimate; both labeled once to keep the row short.
|
|
87
|
+
const tokensLabel = uploadTokens > 0
|
|
88
|
+
? `↑ ${formatNumber(uploadTokens)} · ↓ ${tokenCount} tokens`
|
|
89
|
+
: `↓ ${tokenCount} tokens`;
|
|
90
|
+
const tokensWidth = stringWidth(tokensLabel);
|
|
86
91
|
// === Thinking text (may shrink to fit) ===
|
|
87
92
|
let thinkingText = thinkingStatus === 'thinking'
|
|
88
93
|
? 'thinking'
|
|
@@ -108,7 +113,7 @@ export function SpinnerAnimationRow({ mode, reducedMotion, hasActiveTools, respo
|
|
|
108
113
|
const showTimer = wantsTimerAndTokens && availableSpace > usedAfterThinking + timerWidth;
|
|
109
114
|
const usedAfterTimer = usedAfterThinking + (showTimer ? timerWidth + sep : 0);
|
|
110
115
|
const showTokens = wantsTimerAndTokens &&
|
|
111
|
-
leaderTokens > 0 &&
|
|
116
|
+
(leaderTokens > 0 || uploadTokens > 0) &&
|
|
112
117
|
availableSpace > usedAfterTimer + tokensWidth;
|
|
113
118
|
const thinkingOnly = showThinking &&
|
|
114
119
|
thinkingStatus === 'thinking' &&
|
|
@@ -137,7 +142,7 @@ export function SpinnerAnimationRow({ mode, reducedMotion, hasActiveTools, respo
|
|
|
137
142
|
: []),
|
|
138
143
|
...(showTokens
|
|
139
144
|
? [
|
|
140
|
-
_jsxs(Box, { flexDirection: "row", children: [_jsx(SpinnerModeGlyph, { mode: mode }),
|
|
145
|
+
_jsxs(Box, { flexDirection: "row", children: [_jsx(SpinnerModeGlyph, { mode: mode }), _jsx(Text, { dimColor: true, children: tokensLabel })] }, "tokens"),
|
|
141
146
|
]
|
|
142
147
|
: []),
|
|
143
148
|
...(showThinking && thinkingText
|
|
@@ -8,10 +8,12 @@ import type { SpinnerMode } from './Spinner/spinnerMode.js';
|
|
|
8
8
|
*
|
|
9
9
|
* Random verb is picked once per turn (per mount of the spinner).
|
|
10
10
|
*/
|
|
11
|
-
export declare function WorkingSpinner({ mode, hasActiveTools, responseLengthRef, loadingStartTimeRef, totalPausedMsRef, pauseStartTimeRef, thinkingStatus, }: {
|
|
11
|
+
export declare function WorkingSpinner({ mode, hasActiveTools, responseLengthRef, uploadTokensRef, loadingStartTimeRef, totalPausedMsRef, pauseStartTimeRef, thinkingStatus, }: {
|
|
12
12
|
mode: SpinnerMode;
|
|
13
13
|
hasActiveTools: boolean;
|
|
14
14
|
responseLengthRef: React.RefObject<number>;
|
|
15
|
+
/** Most recent request's real upload tokens; 0 until the first usage event. */
|
|
16
|
+
uploadTokensRef: React.RefObject<number>;
|
|
15
17
|
loadingStartTimeRef: React.RefObject<number>;
|
|
16
18
|
totalPausedMsRef: React.RefObject<number>;
|
|
17
19
|
pauseStartTimeRef: React.RefObject<number | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkingSpinner.d.ts","sourceRoot":"","sources":["../../../src/components/WorkingSpinner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAA;AAG1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAK3D;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,GACf,EAAE;IACD,IAAI,EAAE,WAAW,CAAA;IACjB,cAAc,EAAE,OAAO,CAAA;IACvB,iBAAiB,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAC1C,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,cAAc,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAA;CAC3C,GAAG,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"WorkingSpinner.d.ts","sourceRoot":"","sources":["../../../src/components/WorkingSpinner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAA;AAG1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAK3D;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,GACf,EAAE;IACD,IAAI,EAAE,WAAW,CAAA;IACjB,cAAc,EAAE,OAAO,CAAA;IACvB,iBAAiB,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAC1C,+EAA+E;IAC/E,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IACxC,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,cAAc,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAA;CAC3C,GAAG,KAAK,CAAC,SAAS,CA4BlB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,OAAO,GAClB,UAAU,GAAG,MAAM,GAAG,IAAI,CA6C5B"}
|
|
@@ -13,12 +13,12 @@ import { sample } from 'lodash-es';
|
|
|
13
13
|
*
|
|
14
14
|
* Random verb is picked once per turn (per mount of the spinner).
|
|
15
15
|
*/
|
|
16
|
-
export function WorkingSpinner({ mode, hasActiveTools, responseLengthRef, loadingStartTimeRef, totalPausedMsRef, pauseStartTimeRef, thinkingStatus, }) {
|
|
16
|
+
export function WorkingSpinner({ mode, hasActiveTools, responseLengthRef, uploadTokensRef, loadingStartTimeRef, totalPausedMsRef, pauseStartTimeRef, thinkingStatus, }) {
|
|
17
17
|
const { columns } = useTerminalSize();
|
|
18
18
|
// Pick a random verb once per spinner mount (per turn).
|
|
19
19
|
const [randomVerb] = useState(() => sample(SPINNER_VERBS) ?? 'Working');
|
|
20
20
|
const message = `${randomVerb}…`;
|
|
21
|
-
return (_jsx(Box, { flexDirection: "column", width: "100%", alignItems: "flex-start", children: _jsx(SpinnerAnimationRow, { mode: mode, reducedMotion: false, hasActiveTools: hasActiveTools, responseLengthRef: responseLengthRef, message: message, messageColor: "claude", shimmerColor: "claudeShimmer", loadingStartTimeRef: loadingStartTimeRef, totalPausedMsRef: totalPausedMsRef, pauseStartTimeRef: pauseStartTimeRef, spinnerSuffix: null, verbose: true, columns: columns, thinkingStatus: thinkingStatus }) }));
|
|
21
|
+
return (_jsx(Box, { flexDirection: "column", width: "100%", alignItems: "flex-start", children: _jsx(SpinnerAnimationRow, { mode: mode, reducedMotion: false, hasActiveTools: hasActiveTools, responseLengthRef: responseLengthRef, uploadTokensRef: uploadTokensRef, message: message, messageColor: "claude", shimmerColor: "claudeShimmer", loadingStartTimeRef: loadingStartTimeRef, totalPausedMsRef: totalPausedMsRef, pauseStartTimeRef: pauseStartTimeRef, spinnerSuffix: null, verbose: true, columns: columns, thinkingStatus: thinkingStatus }) }));
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Tracks thinking status: 'thinking' while the model is streaming reasoning,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { Color, Styles } from '../../ink/styles.js';
|
|
3
|
+
import type { DOMElement } from '../../ink/dom.js';
|
|
3
4
|
import { type Theme } from '../../theme.js';
|
|
4
5
|
/**
|
|
5
6
|
* Colors uncolored ThemedText in the subtree. Precedence: explicit `color` >
|
|
@@ -13,9 +14,9 @@ export type Props = {
|
|
|
13
14
|
*/
|
|
14
15
|
readonly color?: keyof Theme | Color;
|
|
15
16
|
/**
|
|
16
|
-
* Same as `color`, but for background.
|
|
17
|
+
* Same as `color`, but for background.
|
|
17
18
|
*/
|
|
18
|
-
readonly backgroundColor?: keyof Theme;
|
|
19
|
+
readonly backgroundColor?: keyof Theme | Color;
|
|
19
20
|
/**
|
|
20
21
|
* Dim the color using the theme's inactive color.
|
|
21
22
|
* This is compatible with bold (unlike ANSI dim).
|
|
@@ -45,6 +46,11 @@ export type Props = {
|
|
|
45
46
|
* This property tells Ink to wrap or truncate text if its width is larger than container.
|
|
46
47
|
*/
|
|
47
48
|
readonly wrap?: Styles['textWrap'];
|
|
49
|
+
/**
|
|
50
|
+
* Ref to the underlying ink-text DOMElement (e.g. useDeclaredCursor
|
|
51
|
+
* parking the native cursor on an inline caret cell).
|
|
52
|
+
*/
|
|
53
|
+
readonly ref?: React.Ref<DOMElement>;
|
|
48
54
|
readonly children?: React.ReactNode;
|
|
49
55
|
};
|
|
50
56
|
/**
|
|
@@ -52,5 +58,5 @@ export type Props = {
|
|
|
52
58
|
* (in the Claude Code visual language). This is what lets every ported CC
|
|
53
59
|
* component use `color="subtle"`-style theme keys unchanged.
|
|
54
60
|
*/
|
|
55
|
-
export default function ThemedText({ color, backgroundColor, dimColor, bold, italic, underline, strikethrough, inverse, wrap, children, }: Props): React.ReactNode;
|
|
61
|
+
export default function ThemedText({ color, backgroundColor, dimColor, bold, italic, underline, strikethrough, inverse, wrap, ref, children, }: Props): React.ReactNode;
|
|
56
62
|
//# sourceMappingURL=ThemedText.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemedText.d.ts","sourceRoot":"","sources":["../../../../src/components/design-system/ThemedText.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAY,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAGrD;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,wCAEtB,CAAA;AAqBZ,MAAM,MAAM,KAAK,GAAG;IAClB;;OAEG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,GAAG,KAAK,CAAA;IAEpC;;OAEG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"ThemedText.d.ts","sourceRoot":"","sources":["../../../../src/components/design-system/ThemedText.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAY,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAGrD;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,wCAEtB,CAAA;AAqBZ,MAAM,MAAM,KAAK,GAAG;IAClB;;OAEG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,GAAG,KAAK,CAAA;IAEpC;;OAEG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,KAAK,GAAG,KAAK,CAAA;IAE9C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAE3B;;OAEG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IAEvB;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IAEzB;;OAEG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAE5B;;OAEG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAA;IAEhC;;OAEG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAE1B;;OAEG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAA;IAElC;;;OAGG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IAEpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CACpC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EACjC,KAAK,EACL,eAAe,EACf,QAAgB,EAChB,IAAY,EACZ,MAAc,EACd,SAAiB,EACjB,aAAqB,EACrB,OAAe,EACf,IAAa,EACb,GAAG,EACH,QAAQ,GACT,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CA6BzB"}
|
|
@@ -28,7 +28,7 @@ function resolveColor(color, theme) {
|
|
|
28
28
|
* (in the Claude Code visual language). This is what lets every ported CC
|
|
29
29
|
* component use `color="subtle"`-style theme keys unchanged.
|
|
30
30
|
*/
|
|
31
|
-
export default function ThemedText({ color, backgroundColor, dimColor = false, bold = false, italic = false, underline = false, strikethrough = false, inverse = false, wrap = 'wrap', children, }) {
|
|
31
|
+
export default function ThemedText({ color, backgroundColor, dimColor = false, bold = false, italic = false, underline = false, strikethrough = false, inverse = false, wrap = 'wrap', ref, children, }) {
|
|
32
32
|
const [themeName] = useTheme();
|
|
33
33
|
const theme = getTheme(themeName);
|
|
34
34
|
const hoverColor = React.useContext(TextHoverColorContext);
|
|
@@ -38,8 +38,6 @@ export default function ThemedText({ color, backgroundColor, dimColor = false, b
|
|
|
38
38
|
: dimColor
|
|
39
39
|
? theme.inactive
|
|
40
40
|
: resolveColor(color, theme);
|
|
41
|
-
const resolvedBackgroundColor = backgroundColor
|
|
42
|
-
|
|
43
|
-
: undefined;
|
|
44
|
-
return (_jsx(Text, { color: resolvedColor, backgroundColor: resolvedBackgroundColor, bold: bold, italic: italic, underline: underline, strikethrough: strikethrough, inverse: inverse, wrap: wrap, children: children }));
|
|
41
|
+
const resolvedBackgroundColor = resolveColor(backgroundColor, theme);
|
|
42
|
+
return (_jsx(Text, { ref: ref, color: resolvedColor, backgroundColor: resolvedBackgroundColor, bold: bold, italic: italic, underline: underline, strikethrough: strikethrough, inverse: inverse, wrap: wrap, children: children }));
|
|
45
43
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AskUserQuestionPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/AskUserQuestionPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"AskUserQuestionPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/AskUserQuestionPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AASvE,MAAM,MAAM,yBAAyB,GAAG;IACtC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;QACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;YAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC3F,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;QAC9B;;oEAE4D;QAC5D,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;QAClC;0EACkE;QAClE,QAAQ,CAAC,MAAM,CAAC,EAAE;YAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;YAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;KAC7E,CAAA;IACD,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACzD,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;CAC9B,CAAA;AAED,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,QAAQ,GACT,EAAE,yBAAyB,GAAG,KAAK,CAAC,SAAS,CAwW7C"}
|
|
@@ -14,11 +14,13 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
14
14
|
*/
|
|
15
15
|
import React from 'react';
|
|
16
16
|
import { t } from '../../i18n.js';
|
|
17
|
-
import { Box, Text, useInput } from '../../ui.js';
|
|
17
|
+
import { Box, Text, useInput, useTerminalSize } from '../../ui.js';
|
|
18
|
+
import { useDeclaredCursor } from '../../ink/hooks/use-declared-cursor.js';
|
|
18
19
|
import { Divider } from '../design-system/Divider.js';
|
|
19
20
|
import { POINTER } from '../../cc/figures.js';
|
|
20
21
|
import { PlanReviewPanel } from './PlanReviewPanel.js';
|
|
21
22
|
import { isPlainReturnInput } from '../../utils/modifiers.js';
|
|
23
|
+
import { listWindow } from '../listWindow.js';
|
|
22
24
|
const CHECKED = '◉';
|
|
23
25
|
const UNCHECKED = '○';
|
|
24
26
|
const PENCIL = '✎';
|
|
@@ -32,6 +34,7 @@ export function AskUserQuestionPanel({ question, position, total, answered, onAn
|
|
|
32
34
|
const options = question.options ?? [];
|
|
33
35
|
const multiSelect = question.multiSelect === true;
|
|
34
36
|
const hideCustomInput = question.hideCustomInput === true && options.length > 0;
|
|
37
|
+
const { rows: terminalRows } = useTerminalSize();
|
|
35
38
|
/** Rows: the real options plus the inline input row at the tail. */
|
|
36
39
|
const rowCount = options.length + (hideCustomInput ? 0 : 1);
|
|
37
40
|
const [focusIndex, setFocusIndex] = React.useState(0);
|
|
@@ -43,6 +46,35 @@ export function AskUserQuestionPanel({ question, position, total, answered, onAn
|
|
|
43
46
|
const [attached, setAttached] = React.useState(null);
|
|
44
47
|
const [error, setError] = React.useState(null);
|
|
45
48
|
const inputFocused = !hideCustomInput && focusIndex === options.length;
|
|
49
|
+
// Chat chrome + panel scaffolding consume twelve rows before the option
|
|
50
|
+
// list: status line, outer/divider/question/list/hint spacing and content.
|
|
51
|
+
// Optional header/detail/input/error rows are charged explicitly. Long
|
|
52
|
+
// lists then use fixed one/two-line rows so listWindow's budget is exact;
|
|
53
|
+
// short questionnaires retain their existing wrapped presentation.
|
|
54
|
+
const detailRows = question.detail === undefined ? 0 : question.detail.split('\n').length + 1;
|
|
55
|
+
const reservedRows = 12
|
|
56
|
+
+ (question.header === undefined ? 0 : 1)
|
|
57
|
+
+ detailRows
|
|
58
|
+
+ (hideCustomInput ? 0 : 1)
|
|
59
|
+
+ (error === null ? 0 : 2);
|
|
60
|
+
const optionBudget = Math.max(terminalRows - reservedRows, 2);
|
|
61
|
+
const optionHeights = options.map(option => option.description === undefined ? 1 : 2);
|
|
62
|
+
const windowedOptions = optionHeights.reduce((sum, height) => sum + height, 0) > optionBudget;
|
|
63
|
+
const optionFocus = Math.min(focusIndex, Math.max(options.length - 1, 0));
|
|
64
|
+
const optionWindow = windowedOptions
|
|
65
|
+
? listWindow(optionHeights, optionFocus, optionBudget)
|
|
66
|
+
: { start: 0, end: options.length };
|
|
67
|
+
// Park the native terminal cursor on the custom-answer caret: terminal
|
|
68
|
+
// emulators render IME preedit (pinyin) at the physical cursor, so without
|
|
69
|
+
// this declaration CJK composition appears at the screen's bottom row
|
|
70
|
+
// instead of inline at the input (same mechanism as PromptInput's value
|
|
71
|
+
// box). Active whenever the input row is visible — typing on an option row
|
|
72
|
+
// also lands in this input, so the IME anchor must follow even when the
|
|
73
|
+
// row itself is not focused. The ref rides on the caret Text itself (all
|
|
74
|
+
// three visual variants): its nodeCache rect IS the caret cell, so (0, 0)
|
|
75
|
+
// stays exact under CJK widths and line wrapping without any
|
|
76
|
+
// layout-affecting wrapper Box.
|
|
77
|
+
const caretRef = useDeclaredCursor({ line: 0, column: 0, active: !hideCustomInput });
|
|
46
78
|
const moveFocus = (delta) => {
|
|
47
79
|
if (rowCount <= 1)
|
|
48
80
|
return;
|
|
@@ -209,13 +241,25 @@ export function AskUserQuestionPanel({ question, position, total, answered, onAn
|
|
|
209
241
|
const remaining = total - answered;
|
|
210
242
|
const headerTitle = ` ${t('question-header-progress', { position, total, remaining: remaining > 1 ? t('question-remaining-more', { n: remaining }) : '' })} `;
|
|
211
243
|
const cursorChar = customCursor < customText.length ? customText[customCursor] : ' ';
|
|
212
|
-
const renderInputRow = () => (_jsxs(Box, { flexDirection: "row", marginTop: inputFocused ? 1 : 0, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: inputFocused ? 'claude' : undefined, bold: inputFocused, children: inputFocused ? POINTER : ' ' }) }), _jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: inputFocused ? 'claude' : 'suggestion', children: PENCIL }) }), _jsxs(Box, { flexDirection: "row", marginLeft: 1, children: [_jsx(Text, { bold: inputFocused, color: inputFocused ? 'claude' : 'suggestion', children: t('question-custom-tab') }), attached !== null && (_jsx(Text, { color: "suggestion", children: t('question-attached-label', { label: attached }) })), _jsx(Text, { dimColor: true, children: "\uFF1A" }), customText === '' && !inputFocused ? (_jsx(Text, { dimColor: true, children: t('question-direct-input') })) : (_jsxs(_Fragment, { children: [_jsx(Text, { wrap: "wrap", children: customText.slice(0, customCursor) }), inputFocused
|
|
213
|
-
? _jsx(Text, { inverse: true, children: cursorChar })
|
|
214
|
-
: _jsx(Text, { color: "suggestion", children: "\u258F" }), _jsx(Text, { wrap: "wrap", children: customText.slice(inputFocused ? customCursor + 1 : customCursor) })] }))] })] }));
|
|
215
|
-
const renderOptions = () => (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [options.map((option, index) => {
|
|
216
|
-
const
|
|
217
|
-
const
|
|
218
|
-
|
|
244
|
+
const renderInputRow = () => (_jsxs(Box, { flexDirection: "row", marginTop: inputFocused ? 1 : 0, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: inputFocused ? 'claude' : undefined, bold: inputFocused, children: inputFocused ? POINTER : ' ' }) }), _jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: inputFocused ? 'claude' : 'suggestion', children: PENCIL }) }), _jsxs(Box, { flexDirection: "row", marginLeft: 1, children: [_jsx(Text, { bold: inputFocused, color: inputFocused ? 'claude' : 'suggestion', children: t('question-custom-tab') }), attached !== null && (_jsx(Text, { color: "suggestion", children: t('question-attached-label', { label: attached }) })), _jsx(Text, { dimColor: true, children: "\uFF1A" }), customText === '' && !inputFocused ? (_jsx(Text, { ref: caretRef, dimColor: true, children: t('question-direct-input') })) : (_jsxs(_Fragment, { children: [_jsx(Text, { wrap: "wrap", children: customText.slice(0, customCursor) }), inputFocused
|
|
245
|
+
? _jsx(Text, { ref: caretRef, inverse: true, children: cursorChar })
|
|
246
|
+
: _jsx(Text, { ref: caretRef, color: "suggestion", children: "\u258F" }), _jsx(Text, { wrap: "wrap", children: customText.slice(inputFocused ? customCursor + 1 : customCursor) })] }))] })] }));
|
|
247
|
+
const renderOptions = () => (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [options.slice(optionWindow.start, optionWindow.end).map((option, index) => {
|
|
248
|
+
const absoluteIndex = optionWindow.start + index;
|
|
249
|
+
const focused = absoluteIndex === focusIndex;
|
|
250
|
+
const selected = multiSelect ? checked.has(absoluteIndex) : focused;
|
|
251
|
+
const pointer = focused
|
|
252
|
+
? POINTER
|
|
253
|
+
: absoluteIndex === optionWindow.start && optionWindow.start > 0
|
|
254
|
+
? '↑'
|
|
255
|
+
: absoluteIndex === optionWindow.end - 1 && optionWindow.end < options.length
|
|
256
|
+
? '↓'
|
|
257
|
+
: ' ';
|
|
258
|
+
const label = windowedOptions ? option.label.replace(/\s+/gu, ' ').trim() : option.label;
|
|
259
|
+
const description = windowedOptions
|
|
260
|
+
? option.description?.replace(/\s+/gu, ' ').trim()
|
|
261
|
+
: option.description;
|
|
262
|
+
return (_jsxs(Box, { flexDirection: "row", marginTop: !windowedOptions && focused ? 1 : 0, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: focused ? 'claude' : undefined, bold: focused, children: pointer }) }), _jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: focused ? 'claude' : undefined, bold: selected, children: selected ? (multiSelect ? CHECKED : '●') : UNCHECKED }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [_jsx(Text, { bold: focused || selected, color: focused ? 'claude' : undefined, wrap: windowedOptions ? 'truncate' : 'wrap', children: label }), description !== undefined && (_jsx(Text, { dimColor: true, wrap: windowedOptions ? 'truncate' : 'wrap', children: description }))] })] }, `${absoluteIndex}:${option.label}`));
|
|
219
263
|
}), hideCustomInput ? null : renderInputRow()] }));
|
|
220
264
|
const hintParts = inputFocused
|
|
221
265
|
? [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlanReviewPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/PlanReviewPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"PlanReviewPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/questions/PlanReviewPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAKvE,MAAM,MAAM,oBAAoB,GAAG;IACjC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;QACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;YAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC3F,QAAQ,CAAC,MAAM,CAAC,EAAE;YAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;YAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAA;KAC7E,CAAA;IACD,QAAQ,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACzD,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;CAC9B,CAAA;AAED,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,QAAQ,EACR,QAAQ,GACT,EAAE,oBAAoB,GAAG,KAAK,CAAC,SAAS,CAyOxC"}
|
|
@@ -18,6 +18,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
18
18
|
import React from 'react';
|
|
19
19
|
import { t } from '../../i18n.js';
|
|
20
20
|
import { Box, Text, useInput } from '../../ui.js';
|
|
21
|
+
import { useDeclaredCursor } from '../../ink/hooks/use-declared-cursor.js';
|
|
21
22
|
import { Divider } from '../design-system/Divider.js';
|
|
22
23
|
import { Markdown } from '../Markdown.js';
|
|
23
24
|
import { POINTER } from '../../cc/figures.js';
|
|
@@ -34,6 +35,14 @@ export function PlanReviewPanel({ question, onAnswer, onCancel, }) {
|
|
|
34
35
|
const [cursor, setCursor] = React.useState(0);
|
|
35
36
|
const [error, setError] = React.useState(null);
|
|
36
37
|
const inputFocused = focusIndex === options.length;
|
|
38
|
+
// Park the native terminal cursor on the feedback caret so IME preedit
|
|
39
|
+
// (pinyin) renders inline at the input instead of the screen's bottom row
|
|
40
|
+
// (same mechanism as AskUserQuestionPanel / PromptInput). Always active —
|
|
41
|
+
// typing on an option row also lands in the feedback buffer. The ref rides
|
|
42
|
+
// on the caret Text itself (all visual variants): its nodeCache rect IS
|
|
43
|
+
// the caret cell, so (0, 0) stays exact under wrapping without a
|
|
44
|
+
// layout-affecting wrapper Box.
|
|
45
|
+
const caretRef = useDeclaredCursor({ line: 0, column: 0, active: true });
|
|
37
46
|
const moveFocus = (delta) => {
|
|
38
47
|
setFocusIndex(index => (index + delta + rowCount) % rowCount);
|
|
39
48
|
setError(null);
|
|
@@ -163,7 +172,7 @@ export function PlanReviewPanel({ question, onAnswer, onCancel, }) {
|
|
|
163
172
|
const focused = index === focusIndex;
|
|
164
173
|
const isApprove = option.label === approveLabel;
|
|
165
174
|
return (_jsxs(Box, { flexDirection: "row", marginTop: focused ? 1 : 0, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: focused ? 'claude' : undefined, bold: focused, children: focused ? POINTER : ' ' }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [_jsxs(Text, { bold: focused, color: focused || isApprove ? 'claude' : undefined, wrap: "wrap", children: [index + 1, ". ", option.label] }), option.description !== undefined && (_jsx(Text, { dimColor: true, wrap: "wrap", children: option.description }))] })] }, option.label));
|
|
166
|
-
}), _jsxs(Box, { flexDirection: "row", marginTop: inputFocused ? 1 : 0, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: inputFocused ? 'claude' : undefined, bold: inputFocused, children: inputFocused ? POINTER : ' ' }) }), _jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: inputFocused ? 'claude' : 'suggestion', children: PENCIL }) }), _jsx(Box, { flexDirection: "row", marginLeft: 1, children: feedback === '' && !inputFocused ? (_jsx(Text, { dimColor: true, children: t('plan-review-feedback-placeholder') })) : (_jsxs(_Fragment, { children: [_jsx(Text, { wrap: "wrap", children: feedback.slice(0, cursor) }), inputFocused
|
|
167
|
-
? _jsx(Text, { inverse: true, children: cursorChar })
|
|
168
|
-
: _jsx(Text, { color: "suggestion", children: "\u258F" }), _jsx(Text, { wrap: "wrap", children: feedback.slice(inputFocused ? cursor + 1 : cursor) })] })) })] })] }), error !== null && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "error", children: error }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: t('plan-review-hint') }) })] }));
|
|
175
|
+
}), _jsxs(Box, { flexDirection: "row", marginTop: inputFocused ? 1 : 0, children: [_jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: inputFocused ? 'claude' : undefined, bold: inputFocused, children: inputFocused ? POINTER : ' ' }) }), _jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { color: inputFocused ? 'claude' : 'suggestion', children: PENCIL }) }), _jsx(Box, { flexDirection: "row", marginLeft: 1, children: feedback === '' && !inputFocused ? (_jsx(Text, { ref: caretRef, dimColor: true, children: t('plan-review-feedback-placeholder') })) : (_jsxs(_Fragment, { children: [_jsx(Text, { wrap: "wrap", children: feedback.slice(0, cursor) }), inputFocused
|
|
176
|
+
? _jsx(Text, { ref: caretRef, inverse: true, children: cursorChar })
|
|
177
|
+
: _jsx(Text, { ref: caretRef, color: "suggestion", children: "\u258F" }), _jsx(Text, { wrap: "wrap", children: feedback.slice(inputFocused ? cursor + 1 : cursor) })] })) })] })] }), error !== null && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "error", children: error }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: t('plan-review-hint') }) })] }));
|
|
169
178
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { SessionSummary } from '../../dsh-adapter/sessions/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* One session in the browser's list: a title line and a metadata line.
|
|
5
|
+
*
|
|
6
|
+
* Two lines rather than one because the two carry different jobs. The title
|
|
7
|
+
* answers "is this the conversation I mean"; the metadata answers "which of
|
|
8
|
+
* the three that look alike is it" — when it was, on what branch, how big,
|
|
9
|
+
* under which model. Folding both onto one line makes the title compete with
|
|
10
|
+
* facts nobody reads first, and on a narrow terminal the title is what loses.
|
|
11
|
+
*
|
|
12
|
+
* Widths are resolved here rather than delegated to flexbox: the row must
|
|
13
|
+
* stay exactly two lines at every terminal width, and a row that wraps
|
|
14
|
+
* destroys the alignment that lets the eye scan a list at all.
|
|
15
|
+
*/
|
|
16
|
+
export declare function SessionListRow({ session, width, depth, focused, now, }: {
|
|
17
|
+
session: SessionSummary;
|
|
18
|
+
/** Columns available to the row, indentation included. */
|
|
19
|
+
width: number;
|
|
20
|
+
/** 0 for a conversation, 1 for a sub-agent run under its parent. */
|
|
21
|
+
depth: number;
|
|
22
|
+
focused: boolean;
|
|
23
|
+
/** Epoch ms used for every relative time in this render pass. */
|
|
24
|
+
now: number;
|
|
25
|
+
}): React.ReactNode;
|
|
26
|
+
//# sourceMappingURL=SessionListRow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionListRow.d.ts","sourceRoot":"","sources":["../../../../src/components/sessions/SessionListRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAUzB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AAEzE;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,KAAK,EACL,KAAK,EACL,OAAO,EACP,GAAG,GACJ,EAAE;IACD,OAAO,EAAE,cAAc,CAAA;IACvB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAA;IACb,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAA;CACZ,GAAG,KAAK,CAAC,SAAS,CAoClB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from '../../ui.js';
|
|
3
|
+
import { t } from '../../i18n.js';
|
|
4
|
+
import { formatBytes, formatWhen, kindMark, titleColor, truncateWidth, } from '../../sessions/format.js';
|
|
5
|
+
/**
|
|
6
|
+
* One session in the browser's list: a title line and a metadata line.
|
|
7
|
+
*
|
|
8
|
+
* Two lines rather than one because the two carry different jobs. The title
|
|
9
|
+
* answers "is this the conversation I mean"; the metadata answers "which of
|
|
10
|
+
* the three that look alike is it" — when it was, on what branch, how big,
|
|
11
|
+
* under which model. Folding both onto one line makes the title compete with
|
|
12
|
+
* facts nobody reads first, and on a narrow terminal the title is what loses.
|
|
13
|
+
*
|
|
14
|
+
* Widths are resolved here rather than delegated to flexbox: the row must
|
|
15
|
+
* stay exactly two lines at every terminal width, and a row that wraps
|
|
16
|
+
* destroys the alignment that lets the eye scan a list at all.
|
|
17
|
+
*/
|
|
18
|
+
export function SessionListRow({ session, width, depth, focused, now, }) {
|
|
19
|
+
const indent = depth * 2;
|
|
20
|
+
// Two cells for the focus marker, plus the indent for a nested run.
|
|
21
|
+
const body = Math.max(8, width - 2 - indent);
|
|
22
|
+
const mark = kindMark(session.kind);
|
|
23
|
+
const facts = [formatWhen(session.updatedAt, now)];
|
|
24
|
+
if (session.branch !== undefined)
|
|
25
|
+
facts.push(session.branch);
|
|
26
|
+
const size = formatBytes(session.bytes);
|
|
27
|
+
if (size !== undefined)
|
|
28
|
+
facts.push(size);
|
|
29
|
+
if (session.model !== undefined)
|
|
30
|
+
facts.push(session.model);
|
|
31
|
+
if (session.childCount > 0 && depth === 0) {
|
|
32
|
+
facts.push(t('session-children', { n: session.childCount }));
|
|
33
|
+
}
|
|
34
|
+
return (_jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: focused ? 'suggestion' : 'subtle', children: `${' '.repeat(indent)}${focused ? '❯ ' : ' '}` }), mark !== undefined && _jsx(Text, { color: mark.color, children: `${mark.glyph} ` }), _jsx(Text, { color: titleColor(session.title.source, focused), bold: focused, children: truncateWidth(session.label ?? session.title.text, body - (mark === undefined ? 0 : 2)) })] }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: `${' '.repeat(indent + 2)}${truncateWidth(facts.join(' · '), body)}` }) })] }));
|
|
35
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { PreviewEntry, SessionSummary } from '../../dsh-adapter/sessions/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* The preview pane: what this session actually says.
|
|
5
|
+
*
|
|
6
|
+
* It shows the END of the conversation, not the beginning, for two reasons.
|
|
7
|
+
* The title already carries the beginning — it is usually the first prompt —
|
|
8
|
+
* so repeating it would spend the pane on something the list already said. And
|
|
9
|
+
* the question a person asks at this moment is "is this the one I was in the
|
|
10
|
+
* middle of", which only the last exchange answers.
|
|
11
|
+
*
|
|
12
|
+
* It is also the reason the pane costs nothing: the tail of a log is exactly
|
|
13
|
+
* what a bounded read already has in hand, so opening the preview on a 4 MB
|
|
14
|
+
* session is the same amount of work as on a 40 KB one.
|
|
15
|
+
*/
|
|
16
|
+
export declare function SessionPreview({ session, entries, loading, width, height, home, now, }: {
|
|
17
|
+
session: SessionSummary;
|
|
18
|
+
entries: readonly PreviewEntry[];
|
|
19
|
+
loading: boolean;
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
home: string;
|
|
23
|
+
now: number;
|
|
24
|
+
}): React.ReactNode;
|
|
25
|
+
//# sourceMappingURL=SessionPreview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionPreview.d.ts","sourceRoot":"","sources":["../../../../src/components/sessions/SessionPreview.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AAQvF;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,OAAO,EACP,OAAO,EACP,KAAK,EACL,MAAM,EACN,IAAI,EACJ,GAAG,GACJ,EAAE;IACD,OAAO,EAAE,cAAc,CAAA;IACvB,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ,GAAG,KAAK,CAAC,SAAS,CA8DlB"}
|