klyro 0.1.26 → 0.1.27
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 +1 -0
- package/dist/tui/app.js +5 -4
- 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
package/dist/tui/app.js
CHANGED
|
@@ -161,6 +161,7 @@ export function App(props) {
|
|
|
161
161
|
n.add(id); return n; });
|
|
162
162
|
const width = stdout?.columns ?? 100;
|
|
163
163
|
const height = stdout?.rows ?? 30;
|
|
164
|
+
const isFullscreen = props.isFullscreen ?? false;
|
|
164
165
|
const grouped = groupTools(transcript);
|
|
165
166
|
const viewportH = Math.max(5, height - 10);
|
|
166
167
|
const totalRows = grouped.length + (plan.length > 0 ? 1 : 0) + 2;
|
|
@@ -248,11 +249,11 @@ export function App(props) {
|
|
|
248
249
|
const totalTokens = status.usageInput + status.usageOutput;
|
|
249
250
|
const ctxPct = totalTokens > 0 ? Math.round((totalTokens / 120_000) * 100) : 0;
|
|
250
251
|
const baseHints = status.status === 'running' ? 'ctrl+c to stop · enter to queue · ctrl+o expand' : status.status === 'idle' && transcript.length === 0 ? 'shift+tab to cycle · ↑↓ for history · / for commands' : 'enter to send · shift+enter newline · @ to attach';
|
|
251
|
-
const hints = maxOffset > 0 ? `${baseHints} · PgUp/Dn scroll` : baseHints;
|
|
252
|
-
const visibleGrouped = grouped.slice(scrollOffset, scrollOffset + viewportH);
|
|
252
|
+
const hints = maxOffset > 0 && isFullscreen ? `${baseHints} · PgUp/Dn scroll` : baseHints;
|
|
253
|
+
const visibleGrouped = isFullscreen ? grouped.slice(scrollOffset, scrollOffset + viewportH) : grouped;
|
|
253
254
|
const trackH = viewportH;
|
|
254
255
|
const thumbPos = maxOffset === 0 ? 0 : Math.round((scrollOffset / maxOffset) * (trackH - 1));
|
|
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:
|
|
256
|
+
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
257
|
if (item.verb) {
|
|
257
258
|
const gr = item;
|
|
258
259
|
const isExpanded = expandedGroups.has(gr.id);
|
|
@@ -303,5 +304,5 @@ export function App(props) {
|
|
|
303
304
|
if (it.kind === 'diff')
|
|
304
305
|
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
306
|
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))), plan.length > 8 ? _jsxs(Text, { color: tokens.ansi.dim, children: [" ", g('guide'), " \u2026 +", plan.length - 8, " more (/todos)"] }) : null] })) : null, status.status === 'done' && transcript.some((x) => x.kind === 'file_changed') ? (_jsx(Box, { paddingLeft: 2, marginBottom: 1, children: _jsxs(Text, { color: tokens.ansi.dim, children: [" ", g('guide'), " ", g('editsBadge'), " ", transcript.filter((x) => x.kind === 'file_changed').length, " files \u00B7 /diff"] }) })) : null] }), _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))) })] }), _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, { 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
|
+
}), 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))), plan.length > 8 ? _jsxs(Text, { color: tokens.ansi.dim, children: [" ", g('guide'), " \u2026 +", plan.length - 8, " more (/todos)"] }) : null] })) : null, status.status === 'done' && transcript.some((x) => x.kind === 'file_changed') ? (_jsx(Box, { paddingLeft: 2, marginBottom: 1, children: _jsxs(Text, { color: tokens.ansi.dim, children: [" ", g('guide'), " ", g('editsBadge'), " ", transcript.filter((x) => x.kind === 'file_changed').length, " files \u00B7 /diff"] }) })) : 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, { 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
308
|
}
|