klyro 0.1.26 → 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/cli/repl.js +6 -3
- package/dist/tui/app.d.ts +3 -3
- package/dist/tui/app.js +75 -106
- package/package.json +1 -1
package/dist/cli/repl.js
CHANGED
|
@@ -106,9 +106,11 @@ export async function startRepl(opts = {}) {
|
|
|
106
106
|
pendingQueue.push({ kind: 'plan', plan: p });
|
|
107
107
|
}
|
|
108
108
|
// ── Full-screen takeover like OpenCode/Claude Code (§3) ──
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
const
|
|
109
|
+
// Detect alt-screen support (§19.5): Windows Terminal (WT_SESSION), VS Code, xterm, or non-Windows.
|
|
110
|
+
// Fallback to inline (native scroll) on conhost/mintty so text isn't clipped — degrade don't break (§1.8).
|
|
111
|
+
const supportsAlt = !!process.env.WT_SESSION || process.env.TERM_PROGRAM === 'vscode' || (process.env.TERM ?? '').includes('xterm') || process.platform !== 'win32' || process.env.KLYRO_ALT_SCREEN === '1';
|
|
112
|
+
const noAlt = process.env.KLYRO_NO_ALT === '1' || process.env.NO_ALT === '1';
|
|
113
|
+
const isAltScreen = useTui && !!process.stdout.isTTY && supportsAlt && !noAlt;
|
|
112
114
|
const enterAlt = () => {
|
|
113
115
|
if (!isAltScreen)
|
|
114
116
|
return;
|
|
@@ -140,6 +142,7 @@ export async function startRepl(opts = {}) {
|
|
|
140
142
|
cwd,
|
|
141
143
|
initialStatus: { status: 'idle' },
|
|
142
144
|
approvalBridge: tuiBridge,
|
|
145
|
+
isFullscreen: isAltScreen,
|
|
143
146
|
onPrompt: async (text) => {
|
|
144
147
|
inflight = runWithBridge(text);
|
|
145
148
|
await inflight;
|
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';
|
|
@@ -24,5 +23,6 @@ export interface AppProps {
|
|
|
24
23
|
updatePlan: (p: PlanStep[]) => void;
|
|
25
24
|
}) => void;
|
|
26
25
|
version?: string;
|
|
26
|
+
isFullscreen?: boolean;
|
|
27
27
|
}
|
|
28
28
|
export declare function App(props: AppProps): React.JSX.Element;
|
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,31 +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 grouped = groupTools(transcript);
|
|
165
|
-
const viewportH = Math.max(5, height - 10);
|
|
166
|
-
const totalRows = grouped.length + (plan.length > 0 ? 1 : 0) + 2;
|
|
167
|
-
const maxOffset = Math.max(0, totalRows - viewportH);
|
|
168
|
-
const isAtBottom = scrollOffset >= maxOffset;
|
|
169
|
-
useEffect(() => { if (isAtBottom)
|
|
170
|
-
setScrollOffset(maxOffset); }, [transcript.length, plan.length, maxOffset, isAtBottom]);
|
|
171
141
|
const scrollUp = (n = 3) => setScrollOffset((p) => Math.max(0, p - n));
|
|
172
142
|
const scrollDown = (n = 3) => setScrollOffset((p) => Math.min(maxOffset, p + n));
|
|
173
143
|
useInput((inputStr, key) => {
|
|
174
|
-
// slider scroll: PageUp/PageDown, Ctrl+U/D, Shift+↑/↓, j/k in vim-like
|
|
175
144
|
if (key.pageUp || (key.ctrl && inputStr === 'u')) {
|
|
176
145
|
scrollUp(5);
|
|
177
146
|
return;
|
|
@@ -191,7 +160,7 @@ export function App(props) {
|
|
|
191
160
|
if (awaitingApproval)
|
|
192
161
|
return;
|
|
193
162
|
if (key.ctrl && inputStr === 'o') {
|
|
194
|
-
const groups =
|
|
163
|
+
const groups = grouped.filter((x) => typeof x.verb === 'string');
|
|
195
164
|
const last = groups[groups.length - 1];
|
|
196
165
|
if (last)
|
|
197
166
|
toggleGroup(last.id);
|
|
@@ -240,37 +209,37 @@ export function App(props) {
|
|
|
240
209
|
if (!key.ctrl && !key.meta)
|
|
241
210
|
setInput((v) => v + inputStr);
|
|
242
211
|
});
|
|
243
|
-
const
|
|
244
|
-
const ver = props.version ?? '0.1.24';
|
|
212
|
+
const ver = props.version ?? '0.1.27';
|
|
245
213
|
const rule = g('rule').repeat(Math.max(10, width - 2));
|
|
246
|
-
// Derived status right
|
|
247
214
|
const cost = (status.usageInput / 1000 * 0.003 + status.usageOutput / 1000 * 0.015);
|
|
248
215
|
const totalTokens = status.usageInput + status.usageOutput;
|
|
249
216
|
const ctxPct = totalTokens > 0 ? Math.round((totalTokens / 120_000) * 100) : 0;
|
|
250
|
-
const baseHints = status.status === 'running' ? 'ctrl+c to stop · enter to queue · ctrl+o expand' :
|
|
251
|
-
const hints = maxOffset > 0 ? `${baseHints} · PgUp/Dn scroll` : baseHints;
|
|
252
|
-
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
return (_jsxs(Box, { flexDirection: "column", width: width, height: height - 1, children: [_jsx(Header, { cwd: props.cwd, model: status.model, version: ver, width: width }), _jsxs(Box, { flexDirection: "row", flexGrow: 1, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden", paddingX: 0, children: [grouped.length === 0 ? (_jsx(Text, { color: tokens.ansi.dim, children: placeholder })) : visibleGrouped.map((item) => {
|
|
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';
|
|
218
|
+
const hints = maxOffset > 0 && isFullscreen ? `${baseHints} · PgUp/Dn scroll` : baseHints;
|
|
219
|
+
// wrap width: leave 6 cells for guide+indent+scrollbar so words never clump
|
|
220
|
+
const wrapW = Math.max(20, width - 6);
|
|
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) => {
|
|
256
222
|
if (item.verb) {
|
|
257
223
|
const gr = item;
|
|
258
224
|
const isExpanded = expandedGroups.has(gr.id);
|
|
259
|
-
const marker = isExpanded ? g('expanded') : g('collapsed');
|
|
260
225
|
const verbLine = (() => {
|
|
261
226
|
if (gr.items.length === 1) {
|
|
262
227
|
const it = gr.items[0];
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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}`;
|
|
274
243
|
}
|
|
275
244
|
if (gr.verb === 'Read')
|
|
276
245
|
return `Read ${gr.items.length} files`;
|
|
@@ -278,21 +247,21 @@ export function App(props) {
|
|
|
278
247
|
return `Searched ${gr.items.length} patterns`;
|
|
279
248
|
if (gr.verb === 'Ran')
|
|
280
249
|
return `Ran ${gr.items.length} commands`;
|
|
281
|
-
if (gr.verb === 'Edited'
|
|
282
|
-
return
|
|
250
|
+
if (gr.verb === 'Edited')
|
|
251
|
+
return `Edited ${gr.items.length} files`;
|
|
283
252
|
return `${gr.verb} ${gr.items.length} items`;
|
|
284
253
|
})();
|
|
285
254
|
const right = gr.status === 'running' ? `${(elapsed / 1000).toFixed(1)}s` : gr.status === 'error' ? '✗' : `${gr.totalMs}ms`;
|
|
286
|
-
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));
|
|
287
256
|
}
|
|
288
257
|
const it = item;
|
|
289
258
|
if (it.kind === 'text' && it.role === 'user') {
|
|
290
|
-
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);
|
|
291
260
|
}
|
|
292
261
|
if (it.kind === 'text') {
|
|
293
262
|
if (it.text.startsWith('queued:'))
|
|
294
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);
|
|
295
|
-
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));
|
|
296
265
|
}
|
|
297
266
|
if (it.kind === 'error')
|
|
298
267
|
return _jsx(Box, { paddingLeft: 2, marginBottom: 1, children: _jsxs(Text, { color: tokens.ansi.err, children: [" ", g('guide'), " \u2717 ", it.message] }) }, it.id);
|
|
@@ -303,5 +272,5 @@ export function App(props) {
|
|
|
303
272
|
if (it.kind === 'diff')
|
|
304
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));
|
|
305
274
|
return null;
|
|
306
|
-
}), 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 ●' : ''] })] })] }));
|
|
307
276
|
}
|