klyro 0.1.27 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tui/app.d.ts +2 -3
- package/dist/tui/app.js +73 -105
- package/package.json +1 -1
package/dist/tui/app.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Klyro TUI — TUI_DESIGN.md §
|
|
3
|
-
*
|
|
4
|
-
* Regions: scrollback (header+turns) / live window (streaming tail+groups) / pinned (input+status)
|
|
2
|
+
* Klyro TUI — opencode-perfect — TUI_DESIGN.md §2-7 + user request: no clumsy words, smooth scroll like opencode
|
|
3
|
+
* Full-screen alt takeover when supported, inline degrade otherwise. Clean wrap at word boundaries, slider │●.
|
|
5
4
|
*/
|
|
6
5
|
import React from 'react';
|
|
7
6
|
import type { StatusSnapshot } from './status.js';
|
package/dist/tui/app.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
|
-
* Klyro TUI — TUI_DESIGN.md §
|
|
4
|
-
*
|
|
5
|
-
* Regions: scrollback (header+turns) / live window (streaming tail+groups) / pinned (input+status)
|
|
3
|
+
* Klyro TUI — opencode-perfect — TUI_DESIGN.md §2-7 + user request: no clumsy words, smooth scroll like opencode
|
|
4
|
+
* Full-screen alt takeover when supported, inline degrade otherwise. Clean wrap at word boundaries, slider │●.
|
|
6
5
|
*/
|
|
7
6
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
8
7
|
import { Box, Text, useInput, useStdout } from 'ink';
|
|
@@ -11,7 +10,6 @@ import { parse as parseSlash } from '../cli/slash/parser.js';
|
|
|
11
10
|
import { tokens, g } from './tokens.js';
|
|
12
11
|
let _id = 0;
|
|
13
12
|
function nextId(p) { _id++; return `${p}-${_id}`; }
|
|
14
|
-
// ── Header §4 ───────────────────────────────────────────────────────────────
|
|
15
13
|
function Header({ cwd, model, version, width }) {
|
|
16
14
|
const branch = (() => { try {
|
|
17
15
|
const { execSync } = require('node:child_process');
|
|
@@ -21,29 +19,26 @@ function Header({ cwd, model, version, width }) {
|
|
|
21
19
|
return '';
|
|
22
20
|
} })();
|
|
23
21
|
const showLinks = width >= 120;
|
|
24
|
-
|
|
25
|
-
const ctxShort = '200k'; // TODO wire real context window
|
|
26
|
-
const row1Left = `KLYRO v${version}`;
|
|
27
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { bold: true, color: tokens.ansi.accent, children: row1Left }), showLinks ? _jsx(Text, { color: tokens.ansi.dim, children: links }) : null] }), _jsxs(Text, { color: tokens.ansi.dim, children: [model, "[", ctxShort, "] \u00B7 API Usage Billing"] }), _jsxs(Text, { color: tokens.ansi.dim, children: [cwd, branch ? ` · ${branch}` : ''] })] }));
|
|
22
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { bold: true, color: tokens.ansi.accent, children: ["KLYRO v", version] }), showLinks ? _jsx(Text, { color: tokens.ansi.dim, children: "\u2502 /help /config /clear /exit" }) : null] }), _jsxs(Text, { color: tokens.ansi.dim, children: [model, "[200k] \u00B7 API Usage Billing"] }), _jsxs(Text, { color: tokens.ansi.dim, children: [cwd, branch ? ` · ${branch}` : ''] })] }));
|
|
28
23
|
}
|
|
29
24
|
function verbForTool(name) {
|
|
30
25
|
if (name === 'read_file')
|
|
31
|
-
return
|
|
26
|
+
return 'Read';
|
|
32
27
|
if (name === 'list_directory')
|
|
33
|
-
return
|
|
28
|
+
return 'Listed';
|
|
34
29
|
if (name === 'grep' || name === 'glob' || name === 'find_files' || name === 'search_files' || name === 'recent_files')
|
|
35
|
-
return
|
|
30
|
+
return 'Searched';
|
|
36
31
|
if (name === 'shell_exec')
|
|
37
|
-
return
|
|
32
|
+
return 'Ran';
|
|
38
33
|
if (name.startsWith('git_'))
|
|
39
|
-
return
|
|
34
|
+
return 'Checked git';
|
|
40
35
|
if (name === 'web_fetch')
|
|
41
|
-
return
|
|
36
|
+
return 'Fetched';
|
|
42
37
|
if (name === 'web_search')
|
|
43
|
-
return
|
|
38
|
+
return 'Searched web';
|
|
44
39
|
if (name === 'edit_file' || name === 'multi_edit' || name === 'apply_patch' || name === 'write_file')
|
|
45
|
-
return
|
|
46
|
-
return
|
|
40
|
+
return 'Edited';
|
|
41
|
+
return 'Called';
|
|
47
42
|
}
|
|
48
43
|
function groupTools(items) {
|
|
49
44
|
const out = [];
|
|
@@ -51,10 +46,9 @@ function groupTools(items) {
|
|
|
51
46
|
const flush = () => {
|
|
52
47
|
if (cur.length === 0)
|
|
53
48
|
return;
|
|
54
|
-
// bucket by verb
|
|
55
49
|
const byVerb = new Map();
|
|
56
50
|
for (const it of cur) {
|
|
57
|
-
const v = verbForTool(it.name)
|
|
51
|
+
const v = verbForTool(it.name);
|
|
58
52
|
if (!byVerb.has(v))
|
|
59
53
|
byVerb.set(v, []);
|
|
60
54
|
byVerb.get(v).push(it);
|
|
@@ -77,7 +71,6 @@ function groupTools(items) {
|
|
|
77
71
|
flush();
|
|
78
72
|
return out;
|
|
79
73
|
}
|
|
80
|
-
// ── Main App §3 ────────────────────────────────────────────────────────────
|
|
81
74
|
export function App(props) {
|
|
82
75
|
const { stdout } = useStdout();
|
|
83
76
|
const [transcript, setTranscript] = useState(props.initialTranscript ?? []);
|
|
@@ -91,50 +84,43 @@ export function App(props) {
|
|
|
91
84
|
const [expandedGroups, setExpandedGroups] = useState(new Set());
|
|
92
85
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
93
86
|
const streamingIdRef = useRef(null);
|
|
94
|
-
const
|
|
95
|
-
const
|
|
87
|
+
const placeholder = 'Message Klyro…';
|
|
88
|
+
const width = stdout?.columns ?? 100;
|
|
89
|
+
const height = stdout?.rows ?? 30;
|
|
90
|
+
const isFullscreen = props.isFullscreen ?? false;
|
|
91
|
+
const viewportH = Math.max(5, height - 10);
|
|
92
|
+
const grouped = groupTools(transcript);
|
|
93
|
+
const totalRows = grouped.length + (plan.length > 0 ? 1 : 0) + 2;
|
|
94
|
+
const maxOffset = Math.max(0, totalRows - viewportH);
|
|
95
|
+
const isAtBottom = scrollOffset >= maxOffset;
|
|
96
|
+
const trackH = viewportH;
|
|
97
|
+
const thumbPos = maxOffset === 0 ? 0 : Math.round((scrollOffset / maxOffset) * (trackH - 1));
|
|
98
|
+
const visibleGrouped = isFullscreen ? grouped.slice(scrollOffset, scrollOffset + viewportH) : grouped;
|
|
96
99
|
useEffect(() => bridge.subscribe((p) => setAwaitingApproval(p !== null)), [bridge]);
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
useEffect(() => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const t = setInterval(() => setElapsed(Date.now() - start), 1000);
|
|
115
|
-
return () => clearInterval(t);
|
|
116
|
-
}, [status.status, elapsed]);
|
|
117
|
-
const append = useCallback((item) => {
|
|
118
|
-
if (item.kind !== 'text' || item.role !== 'assistant')
|
|
119
|
-
streamingIdRef.current = null;
|
|
120
|
-
setTranscript((prev) => [...prev, item]);
|
|
121
|
-
}, []);
|
|
100
|
+
useEffect(() => { if (queued && status.status !== 'running' && !awaitingApproval) {
|
|
101
|
+
const toSend = queued;
|
|
102
|
+
setQueued(null);
|
|
103
|
+
setTranscript((prev) => [...prev, { id: nextId('user'), kind: 'text', text: toSend, role: 'user' }]);
|
|
104
|
+
streamingIdRef.current = null;
|
|
105
|
+
const cmd = parseSlash(toSend.trim());
|
|
106
|
+
if (cmd.kind === 'prompt')
|
|
107
|
+
void props.onPrompt(cmd.text);
|
|
108
|
+
else
|
|
109
|
+
void props.onSlash(cmd);
|
|
110
|
+
} }, [queued, status.status, awaitingApproval]);
|
|
111
|
+
useEffect(() => { if (status.status !== 'running')
|
|
112
|
+
return; const start = Date.now() - elapsed; const t = setInterval(() => setElapsed(Date.now() - start), 1000); return () => clearInterval(t); }, [status.status, elapsed]);
|
|
113
|
+
useEffect(() => { if (isAtBottom)
|
|
114
|
+
setScrollOffset(maxOffset); }, [transcript.length, plan.length, maxOffset, isAtBottom]);
|
|
115
|
+
const append = useCallback((item) => { if (item.kind !== 'text' || item.role !== 'assistant')
|
|
116
|
+
streamingIdRef.current = null; setTranscript((prev) => [...prev, item]); }, []);
|
|
122
117
|
const appendDelta = useCallback((text) => {
|
|
123
118
|
if (!text)
|
|
124
119
|
return;
|
|
125
120
|
const sid = streamingIdRef.current;
|
|
126
|
-
if (sid)
|
|
127
|
-
setTranscript((prev) => {
|
|
128
|
-
const idx = prev
|
|
129
|
-
if (idx === -1)
|
|
130
|
-
return [...prev, { id: sid, kind: 'text', text, role: 'assistant' }];
|
|
131
|
-
const cur = prev[idx];
|
|
132
|
-
const next = { ...cur, text: cur.text + text };
|
|
133
|
-
const copy = [...prev];
|
|
134
|
-
copy[idx] = next;
|
|
135
|
-
return copy;
|
|
136
|
-
});
|
|
137
|
-
}
|
|
121
|
+
if (sid)
|
|
122
|
+
setTranscript((prev) => { const idx = prev.findIndex((x) => x.id === sid); if (idx === -1)
|
|
123
|
+
return [...prev, { id: sid, kind: 'text', text, role: 'assistant' }]; const cur = prev[idx]; const copy = [...prev]; copy[idx] = { ...cur, text: cur.text + text }; return copy; });
|
|
138
124
|
else {
|
|
139
125
|
const id = nextId('stream');
|
|
140
126
|
streamingIdRef.current = id;
|
|
@@ -147,32 +133,14 @@ export function App(props) {
|
|
|
147
133
|
const updatePlan = useCallback((p) => setPlan(p), []);
|
|
148
134
|
const onMountedRef = useRef(props.onMounted);
|
|
149
135
|
useEffect(() => { onMountedRef.current = props.onMounted; }, [props.onMounted]);
|
|
150
|
-
useEffect(() => {
|
|
151
|
-
onMountedRef.current?.({ append, appendDelta, updateStatus, updatePlan });
|
|
152
|
-
globalThis.__klyroAppAppend = append;
|
|
153
|
-
globalThis.__klyroAppendDelta = appendDelta;
|
|
154
|
-
globalThis.__klyroAppStatus = updateStatus;
|
|
155
|
-
globalThis.__klyroAppPlan = updatePlan;
|
|
156
|
-
return () => { delete globalThis.__klyroAppAppend; delete globalThis.__klyroAppendDelta; delete globalThis.__klyroAppStatus; delete globalThis.__klyroAppPlan; };
|
|
157
|
-
}, [append, appendDelta, updateStatus, updatePlan]);
|
|
136
|
+
useEffect(() => { onMountedRef.current?.({ append, appendDelta, updateStatus, updatePlan }); globalThis.__klyroAppAppend = append; globalThis.__klyroAppendDelta = appendDelta; globalThis.__klyroAppStatus = updateStatus; globalThis.__klyroAppPlan = updatePlan; return () => { delete globalThis.__klyroAppAppend; delete globalThis.__klyroAppendDelta; delete globalThis.__klyroAppStatus; delete globalThis.__klyroAppPlan; }; }, [append, appendDelta, updateStatus, updatePlan]);
|
|
158
137
|
const toggleGroup = (id) => setExpandedGroups((prev) => { const n = new Set(prev); if (n.has(id))
|
|
159
138
|
n.delete(id);
|
|
160
139
|
else
|
|
161
140
|
n.add(id); return n; });
|
|
162
|
-
const width = stdout?.columns ?? 100;
|
|
163
|
-
const height = stdout?.rows ?? 30;
|
|
164
|
-
const isFullscreen = props.isFullscreen ?? false;
|
|
165
|
-
const grouped = groupTools(transcript);
|
|
166
|
-
const viewportH = Math.max(5, height - 10);
|
|
167
|
-
const totalRows = grouped.length + (plan.length > 0 ? 1 : 0) + 2;
|
|
168
|
-
const maxOffset = Math.max(0, totalRows - viewportH);
|
|
169
|
-
const isAtBottom = scrollOffset >= maxOffset;
|
|
170
|
-
useEffect(() => { if (isAtBottom)
|
|
171
|
-
setScrollOffset(maxOffset); }, [transcript.length, plan.length, maxOffset, isAtBottom]);
|
|
172
141
|
const scrollUp = (n = 3) => setScrollOffset((p) => Math.max(0, p - n));
|
|
173
142
|
const scrollDown = (n = 3) => setScrollOffset((p) => Math.min(maxOffset, p + n));
|
|
174
143
|
useInput((inputStr, key) => {
|
|
175
|
-
// slider scroll: PageUp/PageDown, Ctrl+U/D, Shift+↑/↓, j/k in vim-like
|
|
176
144
|
if (key.pageUp || (key.ctrl && inputStr === 'u')) {
|
|
177
145
|
scrollUp(5);
|
|
178
146
|
return;
|
|
@@ -192,7 +160,7 @@ export function App(props) {
|
|
|
192
160
|
if (awaitingApproval)
|
|
193
161
|
return;
|
|
194
162
|
if (key.ctrl && inputStr === 'o') {
|
|
195
|
-
const groups =
|
|
163
|
+
const groups = grouped.filter((x) => typeof x.verb === 'string');
|
|
196
164
|
const last = groups[groups.length - 1];
|
|
197
165
|
if (last)
|
|
198
166
|
toggleGroup(last.id);
|
|
@@ -241,37 +209,37 @@ export function App(props) {
|
|
|
241
209
|
if (!key.ctrl && !key.meta)
|
|
242
210
|
setInput((v) => v + inputStr);
|
|
243
211
|
});
|
|
244
|
-
const
|
|
245
|
-
const ver = props.version ?? '0.1.24';
|
|
212
|
+
const ver = props.version ?? '0.1.27';
|
|
246
213
|
const rule = g('rule').repeat(Math.max(10, width - 2));
|
|
247
|
-
// Derived status right
|
|
248
214
|
const cost = (status.usageInput / 1000 * 0.003 + status.usageOutput / 1000 * 0.015);
|
|
249
215
|
const totalTokens = status.usageInput + status.usageOutput;
|
|
250
216
|
const ctxPct = totalTokens > 0 ? Math.round((totalTokens / 120_000) * 100) : 0;
|
|
251
|
-
const baseHints = status.status === 'running' ? 'ctrl+c to stop · enter to queue · ctrl+o expand' :
|
|
217
|
+
const baseHints = status.status === 'running' ? 'ctrl+c to stop · enter to queue · ctrl+o expand' : transcript.length === 0 ? 'shift+tab to cycle · ↑↓ for history · / for commands' : 'enter to send · shift+enter newline · @ to attach';
|
|
252
218
|
const hints = maxOffset > 0 && isFullscreen ? `${baseHints} · PgUp/Dn scroll` : baseHints;
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
const thumbPos = maxOffset === 0 ? 0 : Math.round((scrollOffset / maxOffset) * (trackH - 1));
|
|
219
|
+
// wrap width: leave 6 cells for guide+indent+scrollbar so words never clump
|
|
220
|
+
const wrapW = Math.max(20, width - 6);
|
|
256
221
|
return (_jsxs(Box, { flexDirection: "column", width: width, height: isFullscreen ? height - 1 : undefined, children: [_jsx(Header, { cwd: props.cwd, model: status.model, version: ver, width: width }), _jsxs(Box, { flexDirection: "row", flexGrow: isFullscreen ? 1 : 0, overflow: isFullscreen ? 'hidden' : undefined, children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, overflow: isFullscreen ? 'hidden' : undefined, paddingX: 0, children: [grouped.length === 0 ? (_jsx(Text, { color: tokens.ansi.dim, children: placeholder })) : visibleGrouped.map((item) => {
|
|
257
222
|
if (item.verb) {
|
|
258
223
|
const gr = item;
|
|
259
224
|
const isExpanded = expandedGroups.has(gr.id);
|
|
260
|
-
const marker = isExpanded ? g('expanded') : g('collapsed');
|
|
261
225
|
const verbLine = (() => {
|
|
262
226
|
if (gr.items.length === 1) {
|
|
263
227
|
const it = gr.items[0];
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
228
|
+
let p = '';
|
|
229
|
+
try {
|
|
230
|
+
const a = JSON.parse(it.args);
|
|
231
|
+
p = a.path ?? a.pattern ?? a.command?.slice(0, 48) ?? '';
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
p = '';
|
|
235
|
+
}
|
|
236
|
+
if (gr.verb === 'Read' && p)
|
|
237
|
+
return `Read ${p.split('/').pop()}`;
|
|
238
|
+
if (gr.verb === 'Searched' && p)
|
|
239
|
+
return `Searched "${p}"`;
|
|
240
|
+
if (gr.verb === 'Ran' && p)
|
|
241
|
+
return `Ran ${p.split(' ')[0]}`;
|
|
242
|
+
return `${gr.verb} ${p}`;
|
|
275
243
|
}
|
|
276
244
|
if (gr.verb === 'Read')
|
|
277
245
|
return `Read ${gr.items.length} files`;
|
|
@@ -279,21 +247,21 @@ export function App(props) {
|
|
|
279
247
|
return `Searched ${gr.items.length} patterns`;
|
|
280
248
|
if (gr.verb === 'Ran')
|
|
281
249
|
return `Ran ${gr.items.length} commands`;
|
|
282
|
-
if (gr.verb === 'Edited'
|
|
283
|
-
return
|
|
250
|
+
if (gr.verb === 'Edited')
|
|
251
|
+
return `Edited ${gr.items.length} files`;
|
|
284
252
|
return `${gr.verb} ${gr.items.length} items`;
|
|
285
253
|
})();
|
|
286
254
|
const right = gr.status === 'running' ? `${(elapsed / 1000).toFixed(1)}s` : gr.status === 'error' ? '✗' : `${gr.totalMs}ms`;
|
|
287
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: tokens.ansi.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, {
|
|
255
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: tokens.ansi.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { children: [isExpanded ? g('expanded') : g('collapsed'), " ", verbLine] }), _jsxs(Text, { color: tokens.ansi.dim, children: [" ", right] })] }), isExpanded ? gr.items.map((it) => (_jsxs(Box, { paddingLeft: 4, children: [_jsxs(Text, { color: tokens.ansi.guide, children: [g('end'), " "] }), _jsx(Text, { children: it.name }), _jsxs(Text, { color: tokens.ansi.dim, children: [" ", it.args.slice(0, 50)] })] }, it.id))) : null] }, gr.id));
|
|
288
256
|
}
|
|
289
257
|
const it = item;
|
|
290
258
|
if (it.kind === 'text' && it.role === 'user') {
|
|
291
|
-
return _jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: tokens.ansi.accent, bold: true, children: [g('prompt'), " "] }), _jsx(Text, { children: it.text })] }, it.id);
|
|
259
|
+
return _jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: tokens.ansi.accent, bold: true, children: [g('prompt'), " "] }), _jsx(Text, { wrap: "wrap", children: it.text })] }, it.id);
|
|
292
260
|
}
|
|
293
261
|
if (it.kind === 'text') {
|
|
294
262
|
if (it.text.startsWith('queued:'))
|
|
295
263
|
return _jsx(Box, { paddingLeft: 2, marginBottom: 1, children: _jsxs(Text, { color: tokens.ansi.dim, children: [" ", g('guide'), " ", it.text, " esc to drop"] }) }, it.id);
|
|
296
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: tokens.ansi.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { color: tokens.ansi.accent, children: [g('agentBullet'), " Klyro"] })] }), _jsxs(Box, { paddingLeft: 2,
|
|
264
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: tokens.ansi.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { color: tokens.ansi.accent, children: [g('agentBullet'), " Klyro"] })] }), _jsxs(Box, { paddingLeft: 2, children: [_jsxs(Text, { color: tokens.ansi.dim, children: [" ", g('guide'), " "] }), _jsx(Text, { wrap: "wrap", children: it.text })] })] }, it.id));
|
|
297
265
|
}
|
|
298
266
|
if (it.kind === 'error')
|
|
299
267
|
return _jsx(Box, { paddingLeft: 2, marginBottom: 1, children: _jsxs(Text, { color: tokens.ansi.err, children: [" ", g('guide'), " \u2717 ", it.message] }) }, it.id);
|
|
@@ -304,5 +272,5 @@ export function App(props) {
|
|
|
304
272
|
if (it.kind === 'diff')
|
|
305
273
|
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [_jsx(Text, { bold: true, color: tokens.ansi.soft, children: it.summary ?? 'Diff' }), it.hunks.map((h, i) => (_jsxs(Box, { flexDirection: "column", marginTop: 0, children: [_jsx(Text, { color: tokens.ansi.soft, children: h.path }), h.lines.map((l, j) => (_jsxs(Text, { wrap: "wrap", color: l.kind === 'add' ? tokens.ansi.ok : l.kind === 'remove' ? tokens.ansi.err : tokens.ansi.dim, children: [l.kind === 'add' ? '+ ' : l.kind === 'remove' ? '- ' : ' ', l.text] }, j)))] }, i)))] }, it.id));
|
|
306
274
|
return null;
|
|
307
|
-
}), status.status === 'running' && !streamingIdRef.current ? (_jsxs(Box, { paddingLeft: 2, marginBottom: 1, children: [_jsxs(Text, { color: tokens.ansi.guide, children: [" ", g('guide'), " "] }), _jsx(Text, { color: tokens.ansi.dim, children: "Thinking..." }), _jsxs(Text, { color: tokens.ansi.dim, children: [" ", (elapsed / 1000).toFixed(1), "s"] })] })) : null, plan.length > 0 ? (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, marginTop: 0, marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: tokens.ansi.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { bold: true, children: [g('todoPlan'), " Plan ", plan.filter((p) => p.status === 'done').length, "/", plan.length] })] }), plan.slice(0, 8).map((p) => (_jsxs(Box, { children: [_jsxs(Text, { color: tokens.ansi.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { color: p.status === 'done' ? tokens.ansi.ok : p.status === 'in_progress' ? tokens.ansi.accent : tokens.ansi.dim, children: [p.status === 'done' ? g('todoDone') : p.status === 'in_progress' ? g('todoActive') : g('todoPending'), " ", p.title] })] }, p.id)))
|
|
275
|
+
}), status.status === 'running' && !streamingIdRef.current ? (_jsxs(Box, { paddingLeft: 2, marginBottom: 1, children: [_jsxs(Text, { color: tokens.ansi.guide, children: [" ", g('guide'), " "] }), _jsx(Text, { color: tokens.ansi.dim, children: "Thinking..." }), _jsxs(Text, { color: tokens.ansi.dim, children: [" ", (elapsed / 1000).toFixed(1), "s"] })] })) : null, plan.length > 0 ? (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, marginTop: 0, marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: tokens.ansi.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { bold: true, children: [g('todoPlan'), " Plan ", plan.filter((p) => p.status === 'done').length, "/", plan.length] })] }), plan.slice(0, 8).map((p) => (_jsxs(Box, { children: [_jsxs(Text, { color: tokens.ansi.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { color: p.status === 'done' ? tokens.ansi.ok : p.status === 'in_progress' ? tokens.ansi.accent : tokens.ansi.dim, children: [p.status === 'done' ? g('todoDone') : p.status === 'in_progress' ? g('todoActive') : g('todoPending'), " ", p.title] })] }, p.id)))] })) : null] }), isFullscreen ? (_jsx(Box, { flexDirection: "column", width: 1, marginLeft: 1, children: Array.from({ length: trackH }).map((_, i) => (_jsx(Text, { color: i === thumbPos ? tokens.ansi.accent : tokens.ansi.guide, children: i === thumbPos ? '●' : '│' }, i))) })) : null] }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: tokens.ansi.guide, children: rule }), _jsxs(Box, { children: [_jsxs(Text, { color: tokens.ansi.accent, bold: true, children: [g('prompt'), " "] }), _jsxs(Text, { wrap: "wrap", children: [input || _jsx(Text, { color: tokens.ansi.dim, children: placeholder }), "\u258F"] })] }), _jsx(Text, { color: tokens.ansi.guide, children: rule })] }), _jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { color: tokens.ansi.dim, children: hints }), _jsxs(Text, { color: tokens.ansi.dim, children: [cost > 0 ? `$${cost.toFixed(2)} · ` : '', ctxPct > 0 ? `${ctxPct}% ctx · ` : '', status.status === 'running' ? 'auto mode on ●' : ''] })] })] }));
|
|
308
276
|
}
|