klyro 0.1.38 → 0.1.39
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 -2
- package/dist/tui/app.js +142 -123
- package/dist/tui/tokens.d.ts +52 -71
- package/dist/tui/tokens.js +58 -72
- package/package.json +1 -1
package/dist/tui/app.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Klyro TUI
|
|
3
|
-
*
|
|
2
|
+
* Klyro TUI G�� opencode-clean G�� no clumsy words, correct wrap, markdown, scroll
|
|
3
|
+
* Header 3 rows, guide G�� at col2, G�� Klyro accent, prose wrapped at word boundaries
|
|
4
4
|
*/
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import type { StatusSnapshot } from './status.js';
|
package/dist/tui/app.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
|
-
* Klyro TUI
|
|
4
|
-
*
|
|
3
|
+
* Klyro TUI G�� opencode-clean G�� no clumsy words, correct wrap, markdown, scroll
|
|
4
|
+
* Header 3 rows, guide G�� at col2, G�� Klyro accent, prose wrapped at word boundaries
|
|
5
5
|
*/
|
|
6
6
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
7
7
|
import { Box, Text, useInput, useStdout } from 'ink';
|
|
@@ -10,62 +10,33 @@ import { parse as parseSlash } from '../cli/slash/parser.js';
|
|
|
10
10
|
import { tokens, g } from './tokens.js';
|
|
11
11
|
let _id = 0;
|
|
12
12
|
function nextId(p) { _id++; return `${p}-${_id}`; }
|
|
13
|
-
|
|
14
|
-
function TopBar({ width, cwd, model }) {
|
|
13
|
+
function Header({ cwd, model, version, width }) {
|
|
15
14
|
const branch = (() => { try {
|
|
16
15
|
const { execSync } = require('node:child_process');
|
|
17
16
|
return execSync('git rev-parse --abbrev-ref HEAD', { cwd, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
|
|
18
17
|
}
|
|
19
18
|
catch {
|
|
20
|
-
return '
|
|
19
|
+
return '';
|
|
21
20
|
} })();
|
|
22
|
-
const
|
|
23
|
-
return (_jsxs(Box, {
|
|
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.colors.accent, children: ["KLYRO v", version] }), showLinks ? _jsx(Text, { color: tokens.colors.dim, children: "G\uFFFD\uFFFD /help /config /clear /exit" }) : null] }), _jsxs(Text, { color: tokens.colors.dim, children: [model, "[200k] -+ API Usage Billing"] }), _jsxs(Text, { color: tokens.colors.dim, children: [cwd, branch ? ` -+ ${branch}` : ''] })] }));
|
|
24
23
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (width < 60)
|
|
28
|
-
return null;
|
|
29
|
-
const w = Math.min(28, Math.floor(width * 0.22));
|
|
30
|
-
return (_jsxs(Box, { flexDirection: "column", width: w, borderStyle: "single", borderColor: tokens.colors.border, paddingX: 1, marginRight: 1, children: [_jsx(Text, { bold: true, color: tokens.colors.fg, children: "\u25BE Sessions" }), _jsxs(Text, { color: tokens.colors.orange, children: ["\u25C9 Build auth flow ", _jsx(Text, { color: tokens.colors.fgDim, children: "2m" })] }), _jsx(Text, { color: tokens.colors.fgDim, children: "\u25CB Refactor parser 12m" }), _jsx(Text, { color: tokens.colors.fgDim, children: "\u25CB Fix bug #42 1h" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { bold: true, color: tokens.colors.fg, children: "\u25B8 Files" }) }), _jsx(Text, { color: tokens.colors.fgDim, children: "\u25B8 Agents" }), _jsx(Text, { color: tokens.colors.fgDim, children: "\u25B8 MCP" }), _jsx(Text, { color: tokens.colors.fgDim, children: "\u25B8 Hooks" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: tokens.colors.orange, children: "[+ new]" }) })] }));
|
|
31
|
-
}
|
|
32
|
-
// Inspector §6
|
|
33
|
-
function Inspector({ width }) {
|
|
34
|
-
if (width < 80)
|
|
35
|
-
return null;
|
|
36
|
-
const w = 36;
|
|
37
|
-
return (_jsxs(Box, { flexDirection: "column", width: w, borderStyle: "single", borderColor: tokens.colors.border, paddingX: 1, marginLeft: 1, children: [_jsx(Text, { bold: true, color: tokens.colors.fg, children: "Inspector \u00B7 Tools" }), _jsx(Text, { color: tokens.colors.success, children: "\u2713 edit_file src/routes.rs" }), _jsx(Text, { color: tokens.colors.fgDim, children: " +12 / -3 0.3s" }), _jsx(Text, { children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsx(Text, { color: tokens.colors.success, children: "\u2713 read_file Cargo.toml" }), _jsx(Text, { color: tokens.colors.fgDim, children: " 890 bytes 0.1s" }), _jsx(Text, { children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsx(Text, { color: tokens.colors.warning, children: "\u23F3 bash cargo test" }), _jsx(Text, { color: tokens.colors.fgDim, children: " running... 2.1s" })] }));
|
|
38
|
-
}
|
|
39
|
-
// Chat bubbles §5.1-5.4
|
|
40
|
-
function MarkdownText({ text }) {
|
|
41
|
-
// minimal: **bold** → bold, keep rest
|
|
42
|
-
const parts = [];
|
|
43
|
-
let last = 0;
|
|
44
|
-
const re = /\*\*(.+?)\*\*/g;
|
|
45
|
-
let m;
|
|
46
|
-
let i = 0;
|
|
47
|
-
while ((m = re.exec(text))) {
|
|
48
|
-
if (m.index > last)
|
|
49
|
-
parts.push(_jsx(Text, { wrap: "wrap", children: text.slice(last, m.index) }, `t-${i++}`));
|
|
50
|
-
parts.push(_jsx(Text, { bold: true, wrap: "wrap", children: m[1] }, `b-${i++}`));
|
|
51
|
-
last = m.index + m[0].length;
|
|
52
|
-
}
|
|
53
|
-
if (last < text.length)
|
|
54
|
-
parts.push(_jsx(Text, { wrap: "wrap", children: text.slice(last) }, `t-${i++}`));
|
|
55
|
-
return _jsx(Text, { wrap: "wrap", children: parts.length ? parts : text });
|
|
56
|
-
}
|
|
57
|
-
function verbForTool(n) {
|
|
58
|
-
if (n === 'read_file')
|
|
24
|
+
function verbForTool(name) {
|
|
25
|
+
if (name === 'read_file')
|
|
59
26
|
return 'Read';
|
|
60
|
-
if (
|
|
27
|
+
if (name === 'list_directory')
|
|
61
28
|
return 'Listed';
|
|
62
|
-
if (
|
|
29
|
+
if (name === 'grep' || name === 'glob' || name === 'find_files' || name === 'search_files' || name === 'recent_files')
|
|
63
30
|
return 'Searched';
|
|
64
|
-
if (
|
|
31
|
+
if (name === 'shell_exec')
|
|
65
32
|
return 'Ran';
|
|
66
|
-
if (
|
|
33
|
+
if (name.startsWith('git_'))
|
|
67
34
|
return 'Checked git';
|
|
68
|
-
if (
|
|
35
|
+
if (name === 'web_fetch')
|
|
36
|
+
return 'Fetched';
|
|
37
|
+
if (name === 'web_search')
|
|
38
|
+
return 'Searched web';
|
|
39
|
+
if (name === 'edit_file' || name === 'multi_edit' || name === 'apply_patch' || name === 'write_file')
|
|
69
40
|
return 'Edited';
|
|
70
41
|
return 'Called';
|
|
71
42
|
}
|
|
@@ -73,7 +44,7 @@ function groupTools(items) {
|
|
|
73
44
|
const out = [];
|
|
74
45
|
let cur = [];
|
|
75
46
|
const flush = () => {
|
|
76
|
-
if (
|
|
47
|
+
if (cur.length === 0)
|
|
77
48
|
return;
|
|
78
49
|
const byVerb = new Map();
|
|
79
50
|
for (const it of cur) {
|
|
@@ -84,7 +55,7 @@ function groupTools(items) {
|
|
|
84
55
|
}
|
|
85
56
|
for (const [verb, list] of byVerb) {
|
|
86
57
|
const totalMs = list.reduce((s, x) => s + (x.latencyMs ?? 0), 0);
|
|
87
|
-
const status = list.some(x => x.isError || x.status === 'error') ? 'error' : list.some(x => x.status === 'running') ? 'running' : 'done';
|
|
58
|
+
const status = list.some((x) => x.isError || x.status === 'error') ? 'error' : list.some((x) => x.status === 'running') ? 'running' : 'done';
|
|
88
59
|
out.push({ id: nextId('g'), verb, items: list, totalMs, status });
|
|
89
60
|
}
|
|
90
61
|
cur = [];
|
|
@@ -100,6 +71,27 @@ function groupTools(items) {
|
|
|
100
71
|
flush();
|
|
101
72
|
return out;
|
|
102
73
|
}
|
|
74
|
+
// Simple markdown: **bold** G�� bold, keep lists/tables, wrap at word boundaries
|
|
75
|
+
function MarkdownText({ text, dim, width }) {
|
|
76
|
+
// Split by **bold** segments
|
|
77
|
+
const parts = [];
|
|
78
|
+
let last = 0;
|
|
79
|
+
const re = /\*\*(.+?)\*\*/g;
|
|
80
|
+
let m;
|
|
81
|
+
let idx = 0;
|
|
82
|
+
while ((m = re.exec(text))) {
|
|
83
|
+
if (m.index > last)
|
|
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
|
+
last = m.index + m[0].length;
|
|
87
|
+
}
|
|
88
|
+
if (last < text.length)
|
|
89
|
+
parts.push(_jsx(Text, { color: dim ? tokens.colors.dim : undefined, wrap: "wrap", children: text.slice(last) }, `t-${idx++}`));
|
|
90
|
+
if (parts.length === 0)
|
|
91
|
+
return _jsx(Text, { color: dim ? tokens.colors.dim : undefined, wrap: "wrap", children: text });
|
|
92
|
+
// Render as single line with bold segments G�� Ink will wrap the parent Box
|
|
93
|
+
return _jsx(Text, { wrap: "wrap", children: parts });
|
|
94
|
+
}
|
|
103
95
|
export function App(props) {
|
|
104
96
|
const { stdout } = useStdout();
|
|
105
97
|
const [transcript, setTranscript] = useState(props.initialTranscript ?? []);
|
|
@@ -113,60 +105,66 @@ export function App(props) {
|
|
|
113
105
|
const [expandedGroups, setExpandedGroups] = useState(new Set());
|
|
114
106
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
115
107
|
const streamingIdRef = useRef(null);
|
|
116
|
-
const width = stdout?.columns ??
|
|
108
|
+
const width = stdout?.columns ?? 100;
|
|
117
109
|
const height = stdout?.rows ?? 30;
|
|
118
110
|
const isFullscreen = props.isFullscreen ?? false;
|
|
119
111
|
const grouped = groupTools(transcript);
|
|
120
112
|
const viewportH = Math.max(5, height - 10);
|
|
121
|
-
const totalRows = grouped.
|
|
113
|
+
const totalRows = grouped.length + (plan.length > 0 ? 1 : 0) + 2;
|
|
122
114
|
const maxOffset = Math.max(0, totalRows - viewportH);
|
|
115
|
+
const isAtBottom = scrollOffset >= maxOffset;
|
|
123
116
|
const trackH = viewportH;
|
|
124
|
-
const thumbPos = maxOffset === 0 ? 0 : Math.round(scrollOffset / maxOffset * (trackH - 1));
|
|
117
|
+
const thumbPos = maxOffset === 0 ? 0 : Math.round((scrollOffset / maxOffset) * (trackH - 1));
|
|
125
118
|
const visibleGrouped = isFullscreen ? grouped.slice(scrollOffset, scrollOffset + viewportH) : grouped;
|
|
126
|
-
|
|
127
|
-
useEffect(() =>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
119
|
+
useEffect(() => bridge.subscribe((p) => setAwaitingApproval(p !== null)), [bridge]);
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (queuedInputs.length > 0 && status.status !== 'running' && !awaitingApproval) {
|
|
122
|
+
const toSend = queuedInputs[0];
|
|
123
|
+
setQueuedInputs((prev) => prev.slice(1));
|
|
124
|
+
setTranscript((prev) => [...prev, { id: nextId('user'), kind: 'text', text: toSend, role: 'user' }]);
|
|
125
|
+
streamingIdRef.current = null;
|
|
126
|
+
const cmd = parseSlash(toSend.trim());
|
|
127
|
+
if (cmd.kind === 'prompt')
|
|
128
|
+
void props.onPrompt(cmd.text);
|
|
129
|
+
else
|
|
130
|
+
void props.onSlash(cmd);
|
|
131
|
+
}
|
|
132
|
+
}, [queuedInputs, status.status, awaitingApproval]);
|
|
139
133
|
useEffect(() => { if (status.status !== 'running')
|
|
140
134
|
return; const start = Date.now() - elapsed; const t = setInterval(() => setElapsed(Date.now() - start), 1000); return () => clearInterval(t); }, [status.status, elapsed]);
|
|
141
|
-
useEffect(() => { if (
|
|
142
|
-
setScrollOffset(maxOffset); }, [transcript.length, plan.length, maxOffset]);
|
|
135
|
+
useEffect(() => { if (isAtBottom)
|
|
136
|
+
setScrollOffset(maxOffset); }, [transcript.length, plan.length, maxOffset, isAtBottom]);
|
|
143
137
|
const append = useCallback((item) => { if (item.kind !== 'text' || item.role !== 'assistant')
|
|
144
|
-
streamingIdRef.current = null; setTranscript(
|
|
145
|
-
const appendDelta = useCallback((text) => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
138
|
+
streamingIdRef.current = null; setTranscript((prev) => [...prev, item]); }, []);
|
|
139
|
+
const appendDelta = useCallback((text) => {
|
|
140
|
+
if (!text)
|
|
141
|
+
return;
|
|
142
|
+
const sid = streamingIdRef.current;
|
|
143
|
+
if (sid)
|
|
144
|
+
setTranscript((prev) => { const idx = prev.findIndex((x) => x.id === sid); if (idx === -1)
|
|
145
|
+
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; });
|
|
146
|
+
else {
|
|
147
|
+
const id = nextId('stream');
|
|
148
|
+
streamingIdRef.current = id;
|
|
149
|
+
setTranscript((prev) => [...prev, { id, kind: 'text', text, role: 'assistant' }]);
|
|
150
|
+
}
|
|
151
|
+
}, []);
|
|
154
152
|
useEffect(() => { if (status.status !== 'running')
|
|
155
153
|
streamingIdRef.current = null; }, [status.status]);
|
|
156
|
-
const updateStatus = useCallback((s) => setStatus(p => ({ ...p, ...s })), []);
|
|
154
|
+
const updateStatus = useCallback((s) => setStatus((p) => ({ ...p, ...s })), []);
|
|
157
155
|
const updatePlan = useCallback((p) => setPlan(p), []);
|
|
158
156
|
const onMountedRef = useRef(props.onMounted);
|
|
159
157
|
useEffect(() => { onMountedRef.current = props.onMounted; }, [props.onMounted]);
|
|
160
158
|
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]);
|
|
161
|
-
const toggleGroup = (id) => setExpandedGroups(
|
|
159
|
+
const toggleGroup = (id) => setExpandedGroups((prev) => { const n = new Set(prev); if (n.has(id))
|
|
162
160
|
n.delete(id);
|
|
163
161
|
else
|
|
164
162
|
n.add(id); return n; });
|
|
165
|
-
const scrollUp = (n = 3) => setScrollOffset(p => Math.max(0, p - n));
|
|
166
|
-
const scrollDown = (n = 3) => setScrollOffset(p => Math.min(maxOffset, p + n));
|
|
163
|
+
const scrollUp = (n = 3) => setScrollOffset((p) => Math.max(0, p - n));
|
|
164
|
+
const scrollDown = (n = 3) => setScrollOffset((p) => Math.min(maxOffset, p + n));
|
|
167
165
|
useInput((inputStr, key) => {
|
|
168
166
|
if (key.escape && queuedInputs.length > 0) {
|
|
169
|
-
setQueuedInputs(
|
|
167
|
+
setQueuedInputs((prev) => prev.slice(1));
|
|
170
168
|
return;
|
|
171
169
|
}
|
|
172
170
|
if (key.pageUp || (key.ctrl && inputStr === 'u')) {
|
|
@@ -205,16 +203,16 @@ export function App(props) {
|
|
|
205
203
|
return;
|
|
206
204
|
if (queuedInputs.length >= 3)
|
|
207
205
|
return;
|
|
208
|
-
setQueuedInputs(
|
|
206
|
+
setQueuedInputs((prev) => [...prev, v]);
|
|
209
207
|
setInput('');
|
|
210
208
|
return;
|
|
211
209
|
}
|
|
212
210
|
if (key.backspace || key.delete) {
|
|
213
|
-
setInput(v => v.slice(0, -1));
|
|
211
|
+
setInput((v) => v.slice(0, -1));
|
|
214
212
|
return;
|
|
215
213
|
}
|
|
216
214
|
if (!key.ctrl && !key.meta)
|
|
217
|
-
setInput(v => v + inputStr.replace(/\r\n/g, '\n').replace(/\r/g, '\n'));
|
|
215
|
+
setInput((v) => v + inputStr.replace(/\r\n/g, '\n').replace(/\r/g, '\n'));
|
|
218
216
|
return;
|
|
219
217
|
}
|
|
220
218
|
if (key.return) {
|
|
@@ -222,7 +220,7 @@ export function App(props) {
|
|
|
222
220
|
if (!v)
|
|
223
221
|
return;
|
|
224
222
|
setInput('');
|
|
225
|
-
setTranscript(
|
|
223
|
+
setTranscript((prev) => [...prev, { id: nextId('user'), kind: 'text', text: v, role: 'user' }]);
|
|
226
224
|
streamingIdRef.current = null;
|
|
227
225
|
const cmd = parseSlash(v);
|
|
228
226
|
if (cmd.kind === 'prompt')
|
|
@@ -232,71 +230,92 @@ export function App(props) {
|
|
|
232
230
|
return;
|
|
233
231
|
}
|
|
234
232
|
if (key.backspace || key.delete) {
|
|
235
|
-
setInput(v => v.slice(0, -1));
|
|
233
|
+
setInput((v) => v.slice(0, -1));
|
|
236
234
|
return;
|
|
237
235
|
}
|
|
238
236
|
if (!key.ctrl && !key.meta)
|
|
239
|
-
setInput(v => v + inputStr);
|
|
237
|
+
setInput((v) => v + inputStr);
|
|
240
238
|
});
|
|
241
|
-
const ver = props.version ?? '0.1.
|
|
242
|
-
const rule = '
|
|
239
|
+
const ver = props.version ?? '0.1.27';
|
|
240
|
+
const rule = g('rule').repeat(Math.max(10, width - 2));
|
|
243
241
|
const cost = (status.usageInput / 1000 * 0.003 + status.usageOutput / 1000 * 0.015);
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
|
|
242
|
+
const totalTokens = status.usageInput + status.usageOutput;
|
|
243
|
+
const ctxPct = totalTokens > 0 ? Math.round((totalTokens / 120_000) * 100) : 0;
|
|
244
|
+
const baseHints = status.status === 'running' ? 'ctrl+c to stop -+ enter to queue -+ ctrl+o expand' : transcript.length === 0 ? 'shift+tab to cycle -+ G��G�� for history -+ / for commands' : 'enter to send -+ shift+enter newline -+ @ to attach';
|
|
245
|
+
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.colors.dim, children: "Message KlyroG\u01EA" })) : visibleGrouped.map((item) => {
|
|
247
247
|
if (item.verb) {
|
|
248
248
|
const gr = item;
|
|
249
249
|
const isExpanded = expandedGroups.has(gr.id);
|
|
250
|
-
const verbLine = (() => {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
250
|
+
const verbLine = (() => {
|
|
251
|
+
if (gr.items.length === 1) {
|
|
252
|
+
const it = gr.items[0];
|
|
253
|
+
let p = '';
|
|
254
|
+
try {
|
|
255
|
+
const a = JSON.parse(it.args);
|
|
256
|
+
p = a.path ?? a.pattern ?? a.command?.slice(0, 48) ?? '';
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
p = '';
|
|
260
|
+
}
|
|
261
|
+
if (gr.verb === 'Read' && p)
|
|
262
|
+
return `Read ${p.split('/').pop()}`;
|
|
263
|
+
if (gr.verb === 'Searched' && p)
|
|
264
|
+
return `Searched "${p}"`;
|
|
265
|
+
if (gr.verb === 'Ran' && p)
|
|
266
|
+
return `Ran ${p.split(' ')[0]}`;
|
|
267
|
+
return `${gr.verb} ${p}`;
|
|
256
268
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
const markerColor = gr.status === 'error' ? tokens.colors.danger : gr.status === 'running' ? tokens.colors.orange : tokens.colors.success;
|
|
272
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: tokens.colors.border, paddingX: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { color: markerColor, children: [marker, " ", verbLine] }), _jsx(Text, { color: tokens.colors.fgDim, children: right })] }), isExpanded ? gr.items.map(it => {
|
|
269
|
+
if (gr.verb === 'Read')
|
|
270
|
+
return `Read ${gr.items.length} files`;
|
|
271
|
+
if (gr.verb === 'Searched')
|
|
272
|
+
return `Searched ${gr.items.length} patterns`;
|
|
273
|
+
if (gr.verb === 'Ran')
|
|
274
|
+
return `Ran ${gr.items.length} commands`;
|
|
275
|
+
if (gr.verb === 'Edited')
|
|
276
|
+
return `Edited ${gr.items.length} files`;
|
|
277
|
+
return `${gr.verb} ${gr.items.length} items`;
|
|
278
|
+
})();
|
|
279
|
+
const right = gr.status === 'running' ? `${(elapsed / 1000).toFixed(1)}s` : gr.status === 'error' ? 'G��' : `${gr.totalMs}ms`;
|
|
280
|
+
const marker = isExpanded ? 'G�+' : 'G��';
|
|
281
|
+
const markerColor = gr.status === 'error' ? tokens.colors.err : gr.status === 'running' ? tokens.colors.warn : tokens.colors.ok;
|
|
282
|
+
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) => {
|
|
273
283
|
let friendly = '';
|
|
274
284
|
try {
|
|
275
285
|
const a = JSON.parse(it.args);
|
|
276
|
-
const p = a.path ?? '';
|
|
277
|
-
|
|
286
|
+
const p = a.path ?? a.pattern ?? a.command ?? '';
|
|
287
|
+
const short = p ? String(p).split('/').pop()?.slice(0, 40) ?? p : '';
|
|
288
|
+
if (it.name === 'read_file' && short)
|
|
289
|
+
friendly = `${short}`;
|
|
290
|
+
else if (it.name === 'shell_exec' && p)
|
|
291
|
+
friendly = `$ ${String(p).slice(0, 40)}`;
|
|
292
|
+
else if (short)
|
|
293
|
+
friendly = short;
|
|
294
|
+
else
|
|
295
|
+
friendly = it.args.slice(0, 40);
|
|
278
296
|
}
|
|
279
297
|
catch {
|
|
280
298
|
friendly = it.args.slice(0, 40);
|
|
281
299
|
}
|
|
282
|
-
return
|
|
300
|
+
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));
|
|
283
301
|
}) : null] }, gr.id));
|
|
284
302
|
}
|
|
285
303
|
const it = item;
|
|
286
304
|
if (it.kind === 'text' && it.role === 'user') {
|
|
287
|
-
return _jsxs(Box, {
|
|
305
|
+
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);
|
|
288
306
|
}
|
|
289
307
|
if (it.kind === 'text') {
|
|
290
|
-
|
|
308
|
+
// prose G�� 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.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));
|
|
291
310
|
}
|
|
292
311
|
if (it.kind === 'error')
|
|
293
|
-
return _jsx(Box, {
|
|
312
|
+
return _jsx(Box, { paddingLeft: 2, marginBottom: 1, children: _jsxs(Text, { color: tokens.colors.err, children: [" ", g('guide'), " G\uFFFD\uFFFD ", it.message] }) }, it.id);
|
|
294
313
|
if (it.kind === 'policy')
|
|
295
314
|
return null;
|
|
296
315
|
if (it.kind === 'file_changed')
|
|
297
|
-
return _jsx(Box, {
|
|
316
|
+
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);
|
|
298
317
|
if (it.kind === 'diff')
|
|
299
|
-
return _jsxs(Box, {
|
|
318
|
+
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));
|
|
300
319
|
return null;
|
|
301
|
-
}), status.status === 'running' && !streamingIdRef.current ?
|
|
320
|
+
}), 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 ? 'G��' : 'G��' }, 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 KlyroG\u01EA" }), "G\uFFFD\uFFFD"] })] }), _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 G��' : ''] })] })] }));
|
|
302
321
|
}
|
package/dist/tui/tokens.d.ts
CHANGED
|
@@ -1,83 +1,70 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* -�2.1 Color tokens + -�2.2 Glyph set G�� TUI_DESIGN.md
|
|
3
|
+
* Accent is Orange #E8843C (256:209, 16: yellow bold), one accent G��5%
|
|
4
|
+
* No backgrounds except diff viewer. fg.dim G��4.5:1 on near-black.
|
|
5
5
|
*/
|
|
6
6
|
export declare const tokens: {
|
|
7
7
|
readonly colors: {
|
|
8
|
-
readonly
|
|
9
|
-
readonly
|
|
10
|
-
readonly
|
|
11
|
-
readonly
|
|
12
|
-
readonly
|
|
13
|
-
readonly
|
|
14
|
-
readonly
|
|
15
|
-
readonly
|
|
16
|
-
readonly
|
|
17
|
-
readonly
|
|
18
|
-
readonly
|
|
19
|
-
readonly borderStrong: "#1A1A1A";
|
|
20
|
-
readonly success: "#2E7D32";
|
|
21
|
-
readonly warning: "#C77700";
|
|
22
|
-
readonly danger: "#C62828";
|
|
23
|
-
readonly codeBg: "#FAF7F2";
|
|
24
|
-
readonly codeBorder: "#E8E0D2";
|
|
25
|
-
readonly guide: "#E8E0D2";
|
|
26
|
-
readonly darkBg: "#1A1A1A";
|
|
27
|
-
readonly darkOrange: "#FF8A3D";
|
|
28
|
-
readonly darkBorder: "#333333";
|
|
8
|
+
readonly accent: "#E8843C";
|
|
9
|
+
readonly fg: "#FFFFFF";
|
|
10
|
+
readonly soft: "#F5F5F5";
|
|
11
|
+
readonly dim: "#9A9A9A";
|
|
12
|
+
readonly guide: "#3A3A3A";
|
|
13
|
+
readonly ok: "#E8843C";
|
|
14
|
+
readonly err: "#E06C6C";
|
|
15
|
+
readonly warn: "#E8843C";
|
|
16
|
+
readonly info: "#FFFFFF";
|
|
17
|
+
readonly diffAddBg: "#12250F";
|
|
18
|
+
readonly diffDelBg: "#2A1212";
|
|
29
19
|
};
|
|
30
20
|
readonly ansi: {
|
|
31
|
-
readonly
|
|
32
|
-
readonly fg: "black";
|
|
33
|
-
readonly fgMuted: "gray";
|
|
34
|
-
readonly dim: "gray";
|
|
35
|
-
readonly orange: "yellow";
|
|
36
|
-
readonly orangeBright: "red";
|
|
37
|
-
readonly border: "gray";
|
|
38
|
-
readonly guide: "gray";
|
|
39
|
-
readonly success: "green";
|
|
40
|
-
readonly warning: "yellow";
|
|
41
|
-
readonly danger: "red";
|
|
42
|
-
readonly accent: "yellow";
|
|
21
|
+
readonly accent: "yellowBright";
|
|
43
22
|
readonly accentBold: "yellowBright";
|
|
23
|
+
readonly fg: "whiteBright";
|
|
44
24
|
readonly soft: "white";
|
|
25
|
+
readonly dim: "gray";
|
|
26
|
+
readonly guide: "gray";
|
|
27
|
+
readonly ok: "yellowBright";
|
|
28
|
+
readonly err: "red";
|
|
29
|
+
readonly warn: "yellowBright";
|
|
30
|
+
readonly info: "white";
|
|
31
|
+
readonly border: "gray";
|
|
45
32
|
readonly muted: "gray";
|
|
33
|
+
readonly success: "yellowBright";
|
|
46
34
|
readonly error: "red";
|
|
47
|
-
readonly
|
|
48
|
-
readonly warn: "yellow";
|
|
49
|
-
readonly info: "blue";
|
|
50
|
-
readonly successAlt: "green";
|
|
35
|
+
readonly warning: "yellowBright";
|
|
51
36
|
};
|
|
52
37
|
};
|
|
53
38
|
export declare const glyphs: {
|
|
54
39
|
readonly prompt: ">";
|
|
55
|
-
readonly agentBullet: "
|
|
56
|
-
readonly collapsed: "
|
|
57
|
-
readonly expanded: "
|
|
58
|
-
readonly guide: "
|
|
59
|
-
readonly branch: "
|
|
60
|
-
readonly end: "
|
|
61
|
-
readonly rule: "
|
|
62
|
-
readonly treeBranch: "
|
|
63
|
-
readonly treeEnd: "
|
|
64
|
-
readonly success: "
|
|
65
|
-
readonly failure: "
|
|
40
|
+
readonly agentBullet: "G��";
|
|
41
|
+
readonly collapsed: "G�+";
|
|
42
|
+
readonly expanded: "G�+";
|
|
43
|
+
readonly guide: "G��";
|
|
44
|
+
readonly branch: "G��";
|
|
45
|
+
readonly end: "G��";
|
|
46
|
+
readonly rule: "G��";
|
|
47
|
+
readonly treeBranch: "G��G��G��";
|
|
48
|
+
readonly treeEnd: "G��G��G��";
|
|
49
|
+
readonly success: "G��";
|
|
50
|
+
readonly failure: "G��";
|
|
66
51
|
readonly warning: "!";
|
|
67
|
-
readonly repair: "
|
|
68
|
-
readonly todoPending: "
|
|
69
|
-
readonly todoActive: "
|
|
70
|
-
readonly todoDone: "
|
|
71
|
-
readonly todoPlan: "
|
|
72
|
-
readonly
|
|
73
|
-
readonly
|
|
74
|
-
readonly
|
|
75
|
-
readonly
|
|
76
|
-
readonly
|
|
77
|
-
readonly
|
|
78
|
-
readonly
|
|
79
|
-
readonly
|
|
80
|
-
readonly
|
|
52
|
+
readonly repair: "G�+";
|
|
53
|
+
readonly todoPending: "G��";
|
|
54
|
+
readonly todoActive: "G��";
|
|
55
|
+
readonly todoDone: "G��";
|
|
56
|
+
readonly todoPlan: "G��";
|
|
57
|
+
readonly modeAccept: "G��";
|
|
58
|
+
readonly modePlan: "G��";
|
|
59
|
+
readonly modeAuto: "G��";
|
|
60
|
+
readonly editsBadge: "G��";
|
|
61
|
+
readonly dot: "-+";
|
|
62
|
+
readonly ellipsis: "GǪ";
|
|
63
|
+
readonly meterFilled: "G��";
|
|
64
|
+
readonly meterEmpty: "G��";
|
|
65
|
+
readonly continuation: "G�";
|
|
66
|
+
readonly brand: "G��";
|
|
67
|
+
readonly compaction: "G��";
|
|
81
68
|
};
|
|
82
69
|
export declare const glyphAscii: {
|
|
83
70
|
readonly prompt: ">";
|
|
@@ -98,10 +85,6 @@ export declare const glyphAscii: {
|
|
|
98
85
|
readonly todoActive: "[>]";
|
|
99
86
|
readonly todoDone: "[x]";
|
|
100
87
|
readonly todoPlan: "#";
|
|
101
|
-
readonly logoBar: "|";
|
|
102
|
-
readonly dotFilled: "*";
|
|
103
|
-
readonly dotEmpty: "o";
|
|
104
|
-
readonly dotHalf: "o";
|
|
105
88
|
};
|
|
106
89
|
export declare function isAsciiMode(): boolean;
|
|
107
90
|
export declare function g(name: keyof typeof glyphs): string;
|
|
@@ -109,6 +92,4 @@ export declare const spacing: {
|
|
|
109
92
|
readonly maxWidth: 120;
|
|
110
93
|
readonly indent: 2;
|
|
111
94
|
readonly gap: 1;
|
|
112
|
-
readonly sidebar: 28;
|
|
113
|
-
readonly inspector: 36;
|
|
114
95
|
};
|
package/dist/tui/tokens.js
CHANGED
|
@@ -1,84 +1,71 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* -�2.1 Color tokens + -�2.2 Glyph set G�� TUI_DESIGN.md
|
|
3
|
+
* Accent is Orange #E8843C (256:209, 16: yellow bold), one accent G��5%
|
|
4
|
+
* No backgrounds except diff viewer. fg.dim G��4.5:1 on near-black.
|
|
5
5
|
*/
|
|
6
6
|
export const tokens = {
|
|
7
7
|
colors: {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
borderStrong: '#1A1A1A',
|
|
20
|
-
success: '#2E7D32',
|
|
21
|
-
warning: '#C77700',
|
|
22
|
-
danger: '#C62828',
|
|
23
|
-
codeBg: '#FAF7F2',
|
|
24
|
-
codeBorder: '#E8E0D2',
|
|
25
|
-
guide: '#E8E0D2',
|
|
26
|
-
// dark mirror
|
|
27
|
-
darkBg: '#1A1A1A',
|
|
28
|
-
darkOrange: '#FF8A3D',
|
|
29
|
-
darkBorder: '#333333',
|
|
8
|
+
accent: '#E8843C', // Orange G�� wordmark, prompt >, G��, thumb, selected
|
|
9
|
+
fg: '#FFFFFF', // White G�� user input, headings, file names
|
|
10
|
+
soft: '#F5F5F5', // Soft white G�� assistant prose
|
|
11
|
+
dim: '#9A9A9A', // Dim white G�� hints, durations
|
|
12
|
+
guide: '#3A3A3A', // Guide G��
|
|
13
|
+
ok: '#E8843C', // Orange for success check (white+orange theme)
|
|
14
|
+
err: '#E06C6C',
|
|
15
|
+
warn: '#E8843C', // Orange for running/spinner
|
|
16
|
+
info: '#FFFFFF',
|
|
17
|
+
diffAddBg: '#12250F',
|
|
18
|
+
diffDelBg: '#2A1212',
|
|
30
19
|
},
|
|
31
20
|
ansi: {
|
|
32
|
-
|
|
33
|
-
fg: 'black',
|
|
34
|
-
fgMuted: 'gray',
|
|
35
|
-
dim: 'gray',
|
|
36
|
-
orange: 'yellow', // #FF6B1A → 202
|
|
37
|
-
orangeBright: 'red', // fallback vivid
|
|
38
|
-
border: 'gray',
|
|
39
|
-
guide: 'gray',
|
|
40
|
-
success: 'green',
|
|
41
|
-
warning: 'yellow',
|
|
42
|
-
danger: 'red',
|
|
43
|
-
accent: 'yellow',
|
|
21
|
+
accent: 'yellowBright', // #E8843C G�� vivid orange
|
|
44
22
|
accentBold: 'yellowBright',
|
|
23
|
+
fg: 'whiteBright', // #FFFFFF G�� pure white
|
|
45
24
|
soft: 'white',
|
|
25
|
+
dim: 'gray',
|
|
26
|
+
guide: 'gray',
|
|
27
|
+
ok: 'yellowBright', // G�� orange vivid
|
|
28
|
+
err: 'red',
|
|
29
|
+
warn: 'yellowBright', // spinner orange vivid
|
|
30
|
+
info: 'white',
|
|
31
|
+
border: 'gray',
|
|
46
32
|
muted: 'gray',
|
|
33
|
+
success: 'yellowBright',
|
|
47
34
|
error: 'red',
|
|
48
|
-
|
|
49
|
-
warn: 'yellow',
|
|
50
|
-
info: 'blue',
|
|
51
|
-
successAlt: 'green',
|
|
35
|
+
warning: 'yellowBright',
|
|
52
36
|
},
|
|
53
37
|
};
|
|
54
38
|
export const glyphs = {
|
|
55
39
|
prompt: '>',
|
|
56
|
-
agentBullet: '
|
|
57
|
-
collapsed: '
|
|
58
|
-
expanded: '
|
|
59
|
-
guide: '
|
|
60
|
-
branch: '
|
|
61
|
-
end: '
|
|
62
|
-
rule: '
|
|
63
|
-
treeBranch: '
|
|
64
|
-
treeEnd: '
|
|
65
|
-
success: '
|
|
66
|
-
failure: '
|
|
40
|
+
agentBullet: 'G��',
|
|
41
|
+
collapsed: 'G�+',
|
|
42
|
+
expanded: 'G�+',
|
|
43
|
+
guide: 'G��',
|
|
44
|
+
branch: 'G��',
|
|
45
|
+
end: 'G��',
|
|
46
|
+
rule: 'G��',
|
|
47
|
+
treeBranch: 'G��G��G��',
|
|
48
|
+
treeEnd: 'G��G��G��',
|
|
49
|
+
success: 'G��',
|
|
50
|
+
failure: 'G��',
|
|
67
51
|
warning: '!',
|
|
68
|
-
repair: '
|
|
69
|
-
todoPending: '
|
|
70
|
-
todoActive: '
|
|
71
|
-
todoDone: '
|
|
72
|
-
todoPlan: '
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
52
|
+
repair: 'G�+',
|
|
53
|
+
todoPending: 'G��',
|
|
54
|
+
todoActive: 'G��',
|
|
55
|
+
todoDone: 'G��',
|
|
56
|
+
todoPlan: 'G��',
|
|
57
|
+
modeAccept: 'G��',
|
|
58
|
+
modePlan: 'G��',
|
|
59
|
+
modeAuto: 'G��',
|
|
60
|
+
editsBadge: 'G��',
|
|
61
|
+
dot: '-+',
|
|
62
|
+
ellipsis: 'GǪ',
|
|
63
|
+
meterFilled: 'G��',
|
|
64
|
+
meterEmpty: 'G��',
|
|
65
|
+
continuation: 'G�',
|
|
66
|
+
// compat
|
|
67
|
+
brand: 'G��',
|
|
68
|
+
compaction: 'G��',
|
|
82
69
|
};
|
|
83
70
|
export const glyphAscii = {
|
|
84
71
|
prompt: '>',
|
|
@@ -99,17 +86,16 @@ export const glyphAscii = {
|
|
|
99
86
|
todoActive: '[>]',
|
|
100
87
|
todoDone: '[x]',
|
|
101
88
|
todoPlan: '#',
|
|
102
|
-
logoBar: '|',
|
|
103
|
-
dotFilled: '*',
|
|
104
|
-
dotEmpty: 'o',
|
|
105
|
-
dotHalf: 'o',
|
|
106
89
|
};
|
|
107
90
|
export function isAsciiMode() {
|
|
108
|
-
return process.env.TERM === 'dumb' ||
|
|
91
|
+
return (process.env.TERM === 'dumb' ||
|
|
92
|
+
process.env.KLYRO_ASCII === '1' ||
|
|
93
|
+
(process.env.LANG !== undefined && !process.env.LANG.toLowerCase().includes('utf-8')) ||
|
|
94
|
+
false);
|
|
109
95
|
}
|
|
110
96
|
export function g(name) {
|
|
111
97
|
if (isAsciiMode())
|
|
112
98
|
return glyphAscii[name] ?? glyphs[name];
|
|
113
99
|
return glyphs[name];
|
|
114
100
|
}
|
|
115
|
-
export const spacing = { maxWidth: 120, indent: 2, gap: 1
|
|
101
|
+
export const spacing = { maxWidth: 120, indent: 2, gap: 1 };
|