klyro 0.1.34 → 0.1.36
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 +3 -6
- package/dist/tui/app.js +24 -16
- package/dist/tui/tokens.d.ts +7 -7
- package/dist/tui/tokens.js +8 -8
- package/package.json +1 -1
package/dist/cli/repl.js
CHANGED
|
@@ -105,12 +105,9 @@ export async function startRepl(opts = {}) {
|
|
|
105
105
|
else
|
|
106
106
|
pendingQueue.push({ kind: 'plan', plan: p });
|
|
107
107
|
}
|
|
108
|
-
// ── Full-screen takeover like OpenCode
|
|
109
|
-
//
|
|
110
|
-
|
|
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;
|
|
108
|
+
// ── Full-screen takeover like OpenCode — always when klyro in TTY (user explicitly wants it)
|
|
109
|
+
// Scroll now works correctly via internal viewport, not native terminal scroll
|
|
110
|
+
const isAltScreen = useTui && !!process.stdout.isTTY && process.env.KLYRO_NO_ALT !== '1';
|
|
114
111
|
const enterAlt = () => {
|
|
115
112
|
if (!isAltScreen)
|
|
116
113
|
return;
|
package/dist/tui/app.js
CHANGED
|
@@ -19,7 +19,7 @@ function Header({ cwd, model, version, width }) {
|
|
|
19
19
|
return '';
|
|
20
20
|
} })();
|
|
21
21
|
const showLinks = width >= 120;
|
|
22
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { bold: true, color: tokens.
|
|
22
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { bold: true, color: tokens.colors.accent, children: ["KLYRO v", version] }), showLinks ? _jsx(Text, { color: tokens.colors.dim, children: "\u2502 /help /config /clear /exit" }) : null] }), _jsxs(Text, { color: tokens.colors.dim, children: [model, "[200k] \u00B7 API Usage Billing"] }), _jsxs(Text, { color: tokens.colors.dim, children: [cwd, branch ? ` · ${branch}` : ''] })] }));
|
|
23
23
|
}
|
|
24
24
|
function verbForTool(name) {
|
|
25
25
|
if (name === 'read_file')
|
|
@@ -81,14 +81,14 @@ function MarkdownText({ text, dim, width }) {
|
|
|
81
81
|
let idx = 0;
|
|
82
82
|
while ((m = re.exec(text))) {
|
|
83
83
|
if (m.index > last)
|
|
84
|
-
parts.push(_jsx(Text, { color: dim ? tokens.
|
|
85
|
-
parts.push(_jsx(Text, { bold: true, color: dim ? undefined : tokens.
|
|
84
|
+
parts.push(_jsx(Text, { color: dim ? tokens.colors.dim : undefined, wrap: "wrap", children: text.slice(last, m.index) }, `t-${idx++}`));
|
|
85
|
+
parts.push(_jsx(Text, { bold: true, color: dim ? undefined : tokens.colors.soft, wrap: "wrap", children: m[1] }, `b-${idx++}`));
|
|
86
86
|
last = m.index + m[0].length;
|
|
87
87
|
}
|
|
88
88
|
if (last < text.length)
|
|
89
|
-
parts.push(_jsx(Text, { color: dim ? tokens.
|
|
89
|
+
parts.push(_jsx(Text, { color: dim ? tokens.colors.dim : undefined, wrap: "wrap", children: text.slice(last) }, `t-${idx++}`));
|
|
90
90
|
if (parts.length === 0)
|
|
91
|
-
return _jsx(Text, { color: dim ? tokens.
|
|
91
|
+
return _jsx(Text, { color: dim ? tokens.colors.dim : undefined, wrap: "wrap", children: text });
|
|
92
92
|
// Render as single line with bold segments — Ink will wrap the parent Box
|
|
93
93
|
return _jsx(Text, { wrap: "wrap", children: parts });
|
|
94
94
|
}
|
|
@@ -110,7 +110,15 @@ export function App(props) {
|
|
|
110
110
|
const isFullscreen = props.isFullscreen ?? false;
|
|
111
111
|
const grouped = groupTools(transcript);
|
|
112
112
|
const viewportH = Math.max(5, height - 10);
|
|
113
|
-
const
|
|
113
|
+
const estLines = (s) => Math.max(1, Math.ceil(s.length / Math.max(20, width - 6)));
|
|
114
|
+
const totalRows = grouped.reduce((sum, it) => {
|
|
115
|
+
if (it.verb)
|
|
116
|
+
return sum + 1 + ((it.items.length > 1 && expandedGroups.has(it.id)) ? it.items.length : 0);
|
|
117
|
+
const t = it;
|
|
118
|
+
if (t.kind === 'text')
|
|
119
|
+
return sum + estLines(t.text) + 1;
|
|
120
|
+
return sum + 2;
|
|
121
|
+
}, 0) + (plan.length > 0 ? plan.length + 1 : 0) + 2;
|
|
114
122
|
const maxOffset = Math.max(0, totalRows - viewportH);
|
|
115
123
|
const isAtBottom = scrollOffset >= maxOffset;
|
|
116
124
|
const trackH = viewportH;
|
|
@@ -243,7 +251,7 @@ export function App(props) {
|
|
|
243
251
|
const ctxPct = totalTokens > 0 ? Math.round((totalTokens / 120_000) * 100) : 0;
|
|
244
252
|
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';
|
|
245
253
|
const hints = maxOffset > 0 && isFullscreen ? `${baseHints} · PgUp/Dn scroll` : baseHints;
|
|
246
|
-
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.
|
|
254
|
+
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.colors.dim, children: "Message Klyro\u2026" })) : visibleGrouped.map((item) => {
|
|
247
255
|
if (item.verb) {
|
|
248
256
|
const gr = item;
|
|
249
257
|
const isExpanded = expandedGroups.has(gr.id);
|
|
@@ -278,8 +286,8 @@ export function App(props) {
|
|
|
278
286
|
})();
|
|
279
287
|
const right = gr.status === 'running' ? `${(elapsed / 1000).toFixed(1)}s` : gr.status === 'error' ? '✗' : `${gr.totalMs}ms`;
|
|
280
288
|
const marker = isExpanded ? '▼' : '✓';
|
|
281
|
-
const markerColor = gr.status === 'error' ? tokens.
|
|
282
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: tokens.
|
|
289
|
+
const markerColor = gr.status === 'error' ? tokens.colors.err : gr.status === 'running' ? tokens.colors.warn : tokens.colors.ok;
|
|
290
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: tokens.colors.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { color: markerColor, children: [marker, " ", verbLine] }), _jsxs(Text, { color: tokens.colors.dim, children: [" ", right] })] }), isExpanded ? gr.items.map((it) => {
|
|
283
291
|
let friendly = '';
|
|
284
292
|
try {
|
|
285
293
|
const a = JSON.parse(it.args);
|
|
@@ -297,25 +305,25 @@ export function App(props) {
|
|
|
297
305
|
catch {
|
|
298
306
|
friendly = it.args.slice(0, 40);
|
|
299
307
|
}
|
|
300
|
-
return (_jsxs(Box, { paddingLeft: 4, children: [_jsxs(Text, { color: tokens.
|
|
308
|
+
return (_jsxs(Box, { paddingLeft: 4, children: [_jsxs(Text, { color: tokens.colors.guide, children: [g('end'), " "] }), _jsx(Text, { color: tokens.colors.dim, children: friendly })] }, it.id));
|
|
301
309
|
}) : null] }, gr.id));
|
|
302
310
|
}
|
|
303
311
|
const it = item;
|
|
304
312
|
if (it.kind === 'text' && it.role === 'user') {
|
|
305
|
-
return _jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: tokens.
|
|
313
|
+
return _jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: tokens.colors.accent, bold: true, children: [g('prompt'), " "] }), _jsx(Text, { wrap: "wrap", children: it.text })] }, it.id);
|
|
306
314
|
}
|
|
307
315
|
if (it.kind === 'text') {
|
|
308
316
|
// prose — render markdown, not raw **, with proper wrap and guide
|
|
309
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: tokens.
|
|
317
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: tokens.colors.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { color: tokens.colors.accent, children: [g('agentBullet'), " Klyro"] })] }), _jsx(Box, { paddingLeft: 2, flexDirection: "column", children: _jsxs(Box, { children: [_jsxs(Text, { color: tokens.colors.dim, children: [" ", g('guide'), " "] }), _jsx(Box, { flexGrow: 1, children: _jsx(MarkdownText, { text: it.text }) })] }) })] }, it.id));
|
|
310
318
|
}
|
|
311
319
|
if (it.kind === 'error')
|
|
312
|
-
return _jsx(Box, { paddingLeft: 2, marginBottom: 1, children: _jsxs(Text, { color: tokens.
|
|
320
|
+
return _jsx(Box, { paddingLeft: 2, marginBottom: 1, children: _jsxs(Text, { color: tokens.colors.err, children: [" ", g('guide'), " \u2717 ", it.message] }) }, it.id);
|
|
313
321
|
if (it.kind === 'policy')
|
|
314
322
|
return null;
|
|
315
323
|
if (it.kind === 'file_changed')
|
|
316
|
-
return _jsx(Box, { paddingLeft: 2, marginBottom: 1, children: _jsxs(Text, { color: tokens.
|
|
324
|
+
return _jsx(Box, { paddingLeft: 2, marginBottom: 1, children: _jsxs(Text, { color: tokens.colors.dim, children: [" ", g('guide'), " ", g('editsBadge'), " ", it.path, " ", it.op] }) }, it.id);
|
|
317
325
|
if (it.kind === 'diff')
|
|
318
|
-
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [_jsx(Text, { bold: true, color: tokens.
|
|
326
|
+
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [_jsx(Text, { bold: true, color: tokens.colors.soft, children: it.summary ?? 'Diff' }), it.hunks.map((h, i) => (_jsxs(Box, { flexDirection: "column", marginTop: 0, children: [_jsx(Text, { color: tokens.colors.soft, children: h.path }), h.lines.map((l, j) => (_jsxs(Text, { wrap: "wrap", color: l.kind === 'add' ? tokens.colors.ok : l.kind === 'remove' ? tokens.colors.err : tokens.colors.dim, children: [l.kind === 'add' ? '+ ' : l.kind === 'remove' ? '- ' : ' ', l.text] }, j)))] }, i)))] }, it.id));
|
|
319
327
|
return null;
|
|
320
|
-
}), status.status === 'running' && !streamingIdRef.current ? (_jsxs(Box, { paddingLeft: 2, marginBottom: 1, children: [_jsxs(Text, { color: tokens.
|
|
328
|
+
}), status.status === 'running' && !streamingIdRef.current ? (_jsxs(Box, { paddingLeft: 2, marginBottom: 1, children: [_jsxs(Text, { color: tokens.colors.guide, children: [" ", g('guide'), " "] }), _jsx(Text, { color: tokens.colors.dim, children: "Thinking..." }), _jsxs(Text, { color: tokens.colors.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.colors.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.colors.guide, children: [" ", g('guide'), " "] }), _jsxs(Text, { color: p.status === 'done' ? tokens.colors.ok : p.status === 'in_progress' ? tokens.colors.accent : tokens.colors.dim, children: [p.status === 'done' ? g('todoDone') : p.status === 'in_progress' ? g('todoActive') : g('todoPending'), " ", p.title] })] }, p.id)))] })) : null, queuedInputs.length > 0 ? (_jsx(Box, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: queuedInputs.map((q, i) => (_jsxs(Text, { color: tokens.colors.dim, children: ["queued: ", q.slice(0, 60), i === 0 ? ' esc to drop' : ''] }, i))) })) : null] }), isFullscreen ? (_jsx(Box, { flexDirection: "column", width: 1, marginLeft: 1, children: Array.from({ length: trackH }).map((_, i) => (_jsx(Text, { color: i === thumbPos ? tokens.colors.accent : tokens.colors.guide, children: i === thumbPos ? '●' : '│' }, i))) })) : null] }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: tokens.colors.guide, children: g('rule').repeat(Math.max(10, width - 2)) }), _jsxs(Box, { children: [_jsxs(Text, { color: tokens.colors.accent, bold: true, children: [g('prompt'), " "] }), _jsxs(Text, { wrap: "wrap", children: [input || _jsx(Text, { color: tokens.colors.dim, children: "Message Klyro\u2026" }), "\u258F"] })] }), _jsx(Text, { color: tokens.colors.guide, children: g('rule').repeat(Math.max(10, width - 2)) })] }), _jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { color: tokens.colors.dim, children: [baseHints, maxOffset > 0 && isFullscreen ? ' · PgUp/Dn scroll' : ''] }), _jsxs(Text, { color: tokens.colors.dim, children: [cost > 0 ? `$${cost.toFixed(2)} · ` : '', ctxPct > 0 ? `${ctxPct}% ctx · ` : '', status.status === 'running' ? 'auto mode on ●' : ''] })] })] }));
|
|
321
329
|
}
|
package/dist/tui/tokens.d.ts
CHANGED
|
@@ -18,21 +18,21 @@ export declare const tokens: {
|
|
|
18
18
|
readonly diffDelBg: "#2A1212";
|
|
19
19
|
};
|
|
20
20
|
readonly ansi: {
|
|
21
|
-
readonly accent: "
|
|
21
|
+
readonly accent: "yellowBright";
|
|
22
22
|
readonly accentBold: "yellowBright";
|
|
23
|
-
readonly fg: "
|
|
24
|
-
readonly soft: "
|
|
23
|
+
readonly fg: "whiteBright";
|
|
24
|
+
readonly soft: "white";
|
|
25
25
|
readonly dim: "gray";
|
|
26
26
|
readonly guide: "gray";
|
|
27
|
-
readonly ok: "
|
|
27
|
+
readonly ok: "yellowBright";
|
|
28
28
|
readonly err: "red";
|
|
29
|
-
readonly warn: "
|
|
29
|
+
readonly warn: "yellowBright";
|
|
30
30
|
readonly info: "white";
|
|
31
31
|
readonly border: "gray";
|
|
32
32
|
readonly muted: "gray";
|
|
33
|
-
readonly success: "
|
|
33
|
+
readonly success: "yellowBright";
|
|
34
34
|
readonly error: "red";
|
|
35
|
-
readonly warning: "
|
|
35
|
+
readonly warning: "yellowBright";
|
|
36
36
|
};
|
|
37
37
|
};
|
|
38
38
|
export declare const glyphs: {
|
package/dist/tui/tokens.js
CHANGED
|
@@ -18,21 +18,21 @@ export const tokens = {
|
|
|
18
18
|
diffDelBg: '#2A1212',
|
|
19
19
|
},
|
|
20
20
|
ansi: {
|
|
21
|
-
accent: '
|
|
21
|
+
accent: 'yellowBright', // #E8843C → vivid orange
|
|
22
22
|
accentBold: 'yellowBright',
|
|
23
|
-
fg: '
|
|
24
|
-
soft: '
|
|
25
|
-
dim: 'gray',
|
|
23
|
+
fg: 'whiteBright', // #FFFFFF — pure white
|
|
24
|
+
soft: 'white',
|
|
25
|
+
dim: 'gray',
|
|
26
26
|
guide: 'gray',
|
|
27
|
-
ok: '
|
|
27
|
+
ok: 'yellowBright', // ✓ orange vivid
|
|
28
28
|
err: 'red',
|
|
29
|
-
warn: '
|
|
29
|
+
warn: 'yellowBright', // spinner orange vivid
|
|
30
30
|
info: 'white',
|
|
31
31
|
border: 'gray',
|
|
32
32
|
muted: 'gray',
|
|
33
|
-
success: '
|
|
33
|
+
success: 'yellowBright',
|
|
34
34
|
error: 'red',
|
|
35
|
-
warning: '
|
|
35
|
+
warning: 'yellowBright',
|
|
36
36
|
},
|
|
37
37
|
};
|
|
38
38
|
export const glyphs = {
|