clarity-ai 6.5.6 → 6.7.0
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/CHANGELOG.md +25 -0
- package/bin/clarity.js +15 -4
- package/package.json +6 -2
- package/src/components/AppRoot.js +3 -9
- package/src/components/CodeBlock.js +10 -8
- package/src/components/CommandPicker.js +33 -21
- package/src/components/Composer.js +10 -12
- package/src/components/Layout.js +17 -20
- package/src/components/LoadingIndicator.js +2 -2
- package/src/components/MessageList.js +42 -36
- package/src/components/ModelPicker.js +42 -23
- package/src/components/StatusBar.js +9 -6
- package/src/components/ThinkingBlock.js +15 -32
- package/src/components/ToolCard.js +15 -11
- package/src/config/layout.js +14 -28
- package/src/config/models.js +2 -2
- package/src/config/theme.js +27 -11
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
## 6.7.0 (2026-06-06)
|
|
6
|
+
|
|
7
|
+
### Premium Ink+React TUI Engine — zero-bleed grid
|
|
8
|
+
- **Hard-locked 3-tier grid**: Header(1) + Viewport(fill) + Dock(4) — absolute layout isolation
|
|
9
|
+
- **Floating picker overlays** via `position: absolute` — CommandPicker/ModelPicker never shift the message stream
|
|
10
|
+
- **Box-drawing borders** (`┌─┐│└─┘`) on all overlay panels
|
|
11
|
+
- **Orange `#FF6B35` selection bars** — full-width active item highlight
|
|
12
|
+
- **Gradient CLARITY header** via gradient-string (orange→blue)
|
|
13
|
+
- **Alternate screen buffer** (`fullscreen: true`) + SIGINT/SIGTERM cleanup
|
|
14
|
+
- **`string-width` render protection** — correct visual width for Unicode/emoji
|
|
15
|
+
- **Permanent ASCII logo** centered in empty viewport
|
|
16
|
+
- **ansi-escapes** for cursor hide/show, screen clear on boot
|
|
17
|
+
|
|
18
|
+
## 6.6.0 (2026-06-06)
|
|
19
|
+
|
|
20
|
+
### Premium UI rebuild + DeepSeek R1 reasoning models
|
|
21
|
+
- **Models**: Flash → DeepSeek-R1-Distill-Qwen-1.5B (reasoning), Heavy → DeepSeek-R1-Distill-Qwen-7B (reasoning)
|
|
22
|
+
- **Permanent center CLARITY ASCII logo** — stays as background until first message
|
|
23
|
+
- **Box-drawing overlays** (`┌─┐│└─┘`) on CommandPicker and ModelPicker
|
|
24
|
+
- **Non-bouncing 3-tier viewport** — topBar(1) + viewport(fill) + dock(4)
|
|
25
|
+
- **Cleaner message rendering** — `│` prefix for assistant text, no wasted rows
|
|
26
|
+
- **Tool log compression** — completed tools collapse to single-line chips
|
|
27
|
+
- **Orange selection bar** (`#FF6B35`) on all pickers
|
|
28
|
+
- **DeepSeek R1 reasoning** — model outputs `<think>` blocks naturally
|
|
29
|
+
|
|
5
30
|
## 6.5.6 (2026-06-06)
|
|
6
31
|
|
|
7
32
|
### Fix first-request timeout — model download needs >8s
|
package/bin/clarity.js
CHANGED
|
@@ -4,6 +4,7 @@ import { render } from 'ink';
|
|
|
4
4
|
import { App } from '../src/components/AppRoot.js';
|
|
5
5
|
import { hasKey } from '../src/config/keys.js';
|
|
6
6
|
import { createInterface } from 'readline';
|
|
7
|
+
import ansiEscapes from 'ansi-escapes';
|
|
7
8
|
|
|
8
9
|
process.stdin.resume();
|
|
9
10
|
process.stdin.setEncoding('utf8');
|
|
@@ -24,29 +25,39 @@ async function main() {
|
|
|
24
25
|
process.stdin.resume();
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
process.stdout.write(ansiEscapes.clearScreen);
|
|
29
|
+
process.stdout.write(ansiEscapes.cursorHide);
|
|
30
|
+
|
|
27
31
|
const config = { provider, model: process.env.CLARITY_MODEL || 'groq/llama-3.3-70b-versatile' };
|
|
28
32
|
|
|
29
|
-
const { clear } = render(React.createElement(App, { config }), {
|
|
33
|
+
const { clear, waitUntilExit } = render(React.createElement(App, { config }), {
|
|
30
34
|
fullscreen: true,
|
|
31
35
|
patchConsole: false,
|
|
36
|
+
exitOnCtrlC: false,
|
|
32
37
|
});
|
|
33
38
|
|
|
34
|
-
setInterval(() => {}, 2 ** 31 - 1);
|
|
39
|
+
const keepAlive = setInterval(() => {}, 2 ** 31 - 1);
|
|
35
40
|
|
|
36
41
|
function cleanup() {
|
|
42
|
+
clearInterval(keepAlive);
|
|
43
|
+
process.stdout.write(ansiEscapes.cursorShow);
|
|
44
|
+
process.stdout.write('\x1b[0m');
|
|
37
45
|
try { clear(); } catch {}
|
|
38
|
-
process.stdout.write('\x1b[?25h\x1b[0m');
|
|
39
46
|
process.exit(0);
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
process.on('SIGINT', () => cleanup());
|
|
43
50
|
process.on('SIGTERM', () => cleanup());
|
|
51
|
+
process.on('exit', () => {
|
|
52
|
+
process.stdout.write(ansiEscapes.cursorShow);
|
|
53
|
+
});
|
|
44
54
|
|
|
45
55
|
await new Promise(() => {});
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
main().catch(err => {
|
|
49
|
-
process.stdout.write(
|
|
59
|
+
process.stdout.write(ansiEscapes.cursorShow);
|
|
60
|
+
process.stdout.write('\x1b[0m');
|
|
50
61
|
console.error('\n\x1b[31mFatal error:\x1b[0m', err.message);
|
|
51
62
|
process.exit(1);
|
|
52
63
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clarity-ai",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.7.0",
|
|
4
4
|
"description": "CLARITY — terminal AI agent with local GGUF inference on HF Spaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
"react": "^18",
|
|
24
24
|
"ink-spinner": "^5",
|
|
25
25
|
"chalk": "^5",
|
|
26
|
-
"wrap-ansi": "^9"
|
|
26
|
+
"wrap-ansi": "^9",
|
|
27
|
+
"gradient-string": "^3",
|
|
28
|
+
"figures": "^6",
|
|
29
|
+
"ansi-escapes": "^7",
|
|
30
|
+
"string-width": "^7"
|
|
27
31
|
}
|
|
28
32
|
}
|
|
@@ -34,11 +34,9 @@ export function App({ config }) {
|
|
|
34
34
|
const stateRef = useRef(state);
|
|
35
35
|
const modelRef = useRef(model);
|
|
36
36
|
const providerRef = useRef(provider);
|
|
37
|
-
const streamRef = useRef(streamContent);
|
|
38
37
|
stateRef.current = state;
|
|
39
38
|
modelRef.current = model;
|
|
40
39
|
providerRef.current = provider;
|
|
41
|
-
streamRef.current = streamContent;
|
|
42
40
|
|
|
43
41
|
const onSubmit = useCallback(async (input) => {
|
|
44
42
|
if (input === '/exit') { process.exit(0); return; }
|
|
@@ -74,14 +72,10 @@ export function App({ config }) {
|
|
|
74
72
|
}));
|
|
75
73
|
}
|
|
76
74
|
|
|
77
|
-
return h(Box, { flexDirection: 'column', backgroundColor: hex.bg },
|
|
75
|
+
return h(Box, { flexDirection: 'column', backgroundColor: hex.bg, height: '100%' },
|
|
78
76
|
h(Layout, {
|
|
79
|
-
state,
|
|
80
|
-
|
|
81
|
-
model,
|
|
82
|
-
provider,
|
|
83
|
-
showCommands,
|
|
84
|
-
showModels,
|
|
77
|
+
state, streamContent, model, provider,
|
|
78
|
+
showCommands, showModels,
|
|
85
79
|
onCommandSelect: handleCommandSelect,
|
|
86
80
|
onModelSelect: handleModelSelect,
|
|
87
81
|
onCloseCommands: () => setShowCommands(false),
|
|
@@ -21,26 +21,28 @@ export function CodeBlock({ code, language }) {
|
|
|
21
21
|
const { cols } = getLayout();
|
|
22
22
|
const maxLines = 20;
|
|
23
23
|
const visible = lines.slice(0, maxLines);
|
|
24
|
+
const codeWidth = cols - 10;
|
|
24
25
|
|
|
25
26
|
return h(Box, { flexDirection: 'column', backgroundColor: hex.codeBg },
|
|
26
|
-
h(Box, {
|
|
27
|
-
h(Text, { color: langColor, bold: true, backgroundColor: hex.codeBg }, ' ' + lang
|
|
28
|
-
h(Text, { color: hex.textMuted, backgroundColor: hex.codeBg }, String(lines.length) + ' lines '),
|
|
27
|
+
h(Box, { height: 1, backgroundColor: hex.codeBg },
|
|
28
|
+
h(Text, { color: langColor, bold: true, backgroundColor: hex.codeBg }, ' ' + lang),
|
|
29
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.codeBg }, ' ' + String(lines.length) + ' lines '),
|
|
29
30
|
),
|
|
30
31
|
h(Box, { flexDirection: 'column', backgroundColor: hex.codeBg },
|
|
31
32
|
visible.map((line, i) =>
|
|
32
|
-
h(Box, { key: i,
|
|
33
|
+
h(Box, { key: i, height: 1, backgroundColor: hex.codeBg },
|
|
33
34
|
h(Text, { color: hex.textMuted, backgroundColor: hex.codeBg },
|
|
34
|
-
'
|
|
35
|
+
' ' + String(i + 1).padStart(lnW) + ' '
|
|
35
36
|
),
|
|
36
37
|
h(Text, { color: '#C9D1D9', backgroundColor: hex.codeBg, wrap: 'truncate-end' },
|
|
37
|
-
(line || ' ').slice(0,
|
|
38
|
+
(line || ' ').slice(0, codeWidth)
|
|
38
39
|
)
|
|
39
40
|
)
|
|
40
41
|
),
|
|
41
42
|
lines.length > maxLines
|
|
42
|
-
? h(
|
|
43
|
-
'
|
|
43
|
+
? h(Box, { height: 1, backgroundColor: hex.codeBg },
|
|
44
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.codeBg }, ' ' + sym.ellipsis + ' ' + (lines.length - maxLines) + ' more lines')
|
|
45
|
+
)
|
|
44
46
|
: null
|
|
45
47
|
)
|
|
46
48
|
);
|
|
@@ -5,7 +5,7 @@ import { getLayout } from '../config/layout.js';
|
|
|
5
5
|
const { createElement: h } = React;
|
|
6
6
|
|
|
7
7
|
const COMMANDS = [
|
|
8
|
-
{ name: '/keys', desc: 'Set API key
|
|
8
|
+
{ name: '/keys', desc: 'Set API key' },
|
|
9
9
|
{ name: '/model', desc: 'Switch model' },
|
|
10
10
|
{ name: '/provider', desc: 'Switch provider' },
|
|
11
11
|
{ name: '/agent', desc: 'Toggle agent mode' },
|
|
@@ -29,35 +29,47 @@ export function CommandPicker({ query, onSelect, onClose }) {
|
|
|
29
29
|
if (key.upArrow) setIdx(i => Math.max(0, i - 1));
|
|
30
30
|
if (key.downArrow) setIdx(i => Math.min(filtered.length - 1, i + 1));
|
|
31
31
|
if (key.return && filtered[idx]) onSelect(filtered[idx].name);
|
|
32
|
-
if (key.escape) onClose();
|
|
32
|
+
if (key.escape || key.ctrl && key.c) onClose();
|
|
33
33
|
if (key.backspace) setSearch(s => s.slice(0, -1));
|
|
34
34
|
else if (input && !key.ctrl && !key.meta) setSearch(s => s + input);
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
const w = Math.min(cols - 4, 48);
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
const items = filtered.map((cmd, i) =>
|
|
40
|
+
h(Box, {
|
|
41
|
+
key: cmd.name, height: 1,
|
|
42
|
+
backgroundColor: i === idx ? hex.selectionBg : hex.bg,
|
|
43
|
+
},
|
|
44
|
+
h(Text, {
|
|
45
|
+
color: i === idx ? hex.selectionText : hex.text,
|
|
46
|
+
bold: i === idx,
|
|
47
|
+
backgroundColor: i === idx ? hex.selectionBg : hex.bg,
|
|
48
|
+
}, ' ' + (i === idx ? '\u276F ' : ' ') + cmd.name + ' ' + cmd.desc)
|
|
49
|
+
)
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return h(Box, { flexDirection: 'column', backgroundColor: hex.bg, width: w },
|
|
40
53
|
h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
41
|
-
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
54
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
55
|
+
sym.box.tl + sym.box.h.repeat(w - 2) + sym.box.tr)
|
|
42
56
|
),
|
|
43
|
-
|
|
44
|
-
h(
|
|
45
|
-
|
|
46
|
-
backgroundColor: i === idx ? hex.selectionBg : 'transparent',
|
|
47
|
-
},
|
|
48
|
-
h(Text, {
|
|
49
|
-
color: i === idx ? hex.selectionText : hex.text,
|
|
50
|
-
bold: i === idx,
|
|
51
|
-
backgroundColor: i === idx ? hex.selectionBg : 'transparent',
|
|
52
|
-
}, ' ' + cmd.name + ' '),
|
|
53
|
-
h(Text, {
|
|
54
|
-
color: i === idx ? hex.selectionText : hex.textDim,
|
|
55
|
-
backgroundColor: i === idx ? hex.selectionBg : 'transparent',
|
|
56
|
-
}, cmd.desc)
|
|
57
|
-
)
|
|
57
|
+
h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
58
|
+
h(Text, { color: hex.gold, backgroundColor: hex.surfaceAlt }, sym.box.v + ' Commands'),
|
|
59
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt }, ' '.repeat(Math.max(0, w - 14)) + sym.box.v)
|
|
58
60
|
),
|
|
59
61
|
h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
60
|
-
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
61
|
-
|
|
62
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
63
|
+
sym.box.v + ' ' + (search || '\u2026') + ' '.repeat(Math.max(0, w - 6)) + sym.box.v)
|
|
64
|
+
),
|
|
65
|
+
h(Box, { flexDirection: 'column', backgroundColor: hex.bg }, ...items),
|
|
66
|
+
h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
67
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
68
|
+
sym.box.bl + sym.box.h.repeat(w - 2) + sym.box.br)
|
|
69
|
+
),
|
|
70
|
+
h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
71
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
72
|
+
' \u2191\u2193 nav \u2192 select Esc close')
|
|
73
|
+
),
|
|
62
74
|
);
|
|
63
75
|
}
|
|
@@ -45,11 +45,11 @@ export function Composer({ provider, model, agentMode, thinking, onSlash, onSubm
|
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
const rows = [
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
const rows = [];
|
|
49
|
+
rows.push(h(Box, { key: 'sep', height: 1, backgroundColor: hex.surfaceAlt },
|
|
50
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
51
|
+
' \u250C' + sym.box.h.repeat(Math.max(0, cols - 6)) + '\u2510')
|
|
52
|
+
));
|
|
53
53
|
|
|
54
54
|
for (let i = 0; i < MAX_ROWS; i++) {
|
|
55
55
|
const start = i * w;
|
|
@@ -60,17 +60,15 @@ export function Composer({ provider, model, agentMode, thinking, onSlash, onSubm
|
|
|
60
60
|
color: isPlaceholder ? hex.textMuted : hex.text,
|
|
61
61
|
backgroundColor: hex.bg,
|
|
62
62
|
wrap: 'truncate-end',
|
|
63
|
-
}, '
|
|
63
|
+
}, ' \u2502 ' + (seg || (i === 0 && isPlaceholder ? 'type a message...' : ' ')))
|
|
64
64
|
)
|
|
65
65
|
);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
rows.push(
|
|
69
|
-
h(
|
|
70
|
-
h(
|
|
71
|
-
|
|
72
|
-
)
|
|
73
|
-
);
|
|
68
|
+
rows.push(h(Box, { key: 'st', height: 1, backgroundColor: hex.surfaceAlt },
|
|
69
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
70
|
+
' \u2514' + sym.box.h + ' ' + provider + ' \u00B7 ' + mShort + (agentMode ? ' \u00B7 AGENT' : '') + ' \u00B7 Ctrl+P')
|
|
71
|
+
));
|
|
74
72
|
|
|
75
73
|
return h(Box, { flexDirection: 'column', backgroundColor: hex.surfaceAlt }, ...rows);
|
|
76
74
|
}
|
package/src/components/Layout.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Box } from 'ink';
|
|
3
3
|
import { hex } from '../config/theme.js';
|
|
4
|
+
import { getLayout } from '../config/layout.js';
|
|
4
5
|
import { StatusBar } from './StatusBar.js';
|
|
5
6
|
import { MessageList } from './MessageList.js';
|
|
6
7
|
import { Composer } from './Composer.js';
|
|
@@ -9,29 +10,25 @@ import { ModelPicker } from './ModelPicker.js';
|
|
|
9
10
|
const { createElement: h } = React;
|
|
10
11
|
|
|
11
12
|
export function Layout({ state, streamContent, model, provider, showCommands, showModels, onCommandSelect, onModelSelect, onCloseCommands, onCloseModels, onSlash, onSubmit }) {
|
|
12
|
-
|
|
13
|
+
const { headerHeight, dockHeight, cols } = getLayout();
|
|
14
|
+
|
|
15
|
+
const picker = showCommands || showModels
|
|
16
|
+
? h(Box, { position: 'absolute', top: headerHeight + 1, left: 2, width: Math.min(cols - 4, 52), backgroundColor: hex.bg, flexDirection: 'column' },
|
|
17
|
+
showCommands ? h(CommandPicker, { query: '', onSelect: onCommandSelect, onClose: onCloseCommands }) : null,
|
|
18
|
+
showModels ? h(ModelPicker, { onSelect: onModelSelect, onClose: onCloseModels }) : null
|
|
19
|
+
)
|
|
20
|
+
: null;
|
|
21
|
+
|
|
22
|
+
return h(Box, { flexDirection: 'column', backgroundColor: hex.bg, height: '100%' },
|
|
13
23
|
h(StatusBar, { model, provider, agentMode: state.agentMode, thinking: state.thinking }),
|
|
14
|
-
h(Box, { flexGrow: 1, flexDirection: 'column' },
|
|
24
|
+
h(Box, { flexGrow: 1, flexDirection: 'column', position: 'relative' },
|
|
15
25
|
h(MessageList, {
|
|
16
|
-
messages: state.messages,
|
|
17
|
-
|
|
18
|
-
streamContent,
|
|
19
|
-
agentStatus: state.agentStatus,
|
|
26
|
+
messages: state.messages, thinking: state.thinking,
|
|
27
|
+
streamContent, agentStatus: state.agentStatus,
|
|
20
28
|
toolExecutions: state.toolExecutions,
|
|
21
|
-
})
|
|
29
|
+
}),
|
|
30
|
+
picker
|
|
22
31
|
),
|
|
23
|
-
|
|
24
|
-
? h(Box, { flexDirection: 'column', backgroundColor: hex.surfaceAlt },
|
|
25
|
-
showCommands ? h(CommandPicker, { query: '', onSelect: onCommandSelect, onClose: onCloseCommands }) : null,
|
|
26
|
-
showModels ? h(ModelPicker, { onSelect: onModelSelect, onClose: onCloseModels }) : null
|
|
27
|
-
)
|
|
28
|
-
: null,
|
|
29
|
-
h(Composer, {
|
|
30
|
-
provider, model,
|
|
31
|
-
agentMode: state.agentMode,
|
|
32
|
-
thinking: state.thinking,
|
|
33
|
-
onSlash,
|
|
34
|
-
onSubmit,
|
|
35
|
-
})
|
|
32
|
+
h(Composer, { provider, model, agentMode: state.agentMode, thinking: state.thinking, onSlash, onSubmit })
|
|
36
33
|
);
|
|
37
34
|
}
|
|
@@ -4,8 +4,8 @@ import { hex, sym } from '../config/theme.js';
|
|
|
4
4
|
const { createElement: h } = React;
|
|
5
5
|
|
|
6
6
|
export function LoadingIndicator({ label }) {
|
|
7
|
-
return h(Box, {
|
|
8
|
-
h(Text, { color: hex.blue, backgroundColor: hex.surface }, '
|
|
7
|
+
return h(Box, { height: 1, backgroundColor: hex.surface },
|
|
8
|
+
h(Text, { color: hex.blue, backgroundColor: hex.surface }, ' ' + sym.dot),
|
|
9
9
|
h(Text, { color: hex.textDim, backgroundColor: hex.surface }, ' ' + (label || 'processing'))
|
|
10
10
|
);
|
|
11
11
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
|
-
import { hex, sym } from '../config/theme.js';
|
|
3
|
+
import { hex, sym, LOGO } from '../config/theme.js';
|
|
4
4
|
import { getLayout, sliceToViewport, buildLineArray } from '../config/layout.js';
|
|
5
5
|
const { createElement: h } = React;
|
|
6
6
|
|
|
@@ -8,47 +8,47 @@ function Line({ type, text, data }) {
|
|
|
8
8
|
switch (type) {
|
|
9
9
|
case 'user_head':
|
|
10
10
|
return h(Box, { height: 1, backgroundColor: hex.userBg },
|
|
11
|
-
h(Text, { color: hex.accent, bold: true, backgroundColor: hex.userBg }, '
|
|
11
|
+
h(Text, { color: hex.accent, bold: true, backgroundColor: hex.userBg }, ' \u276F \u25C9 YOU')
|
|
12
12
|
);
|
|
13
13
|
case 'user_line':
|
|
14
14
|
return h(Box, { height: 1, backgroundColor: hex.userBg },
|
|
15
|
-
h(Text, { color: hex.text, backgroundColor: hex.userBg, wrap: 'wrap' }, '
|
|
15
|
+
h(Text, { color: hex.text, backgroundColor: hex.userBg, wrap: 'wrap' }, ' ' + (text || ' '))
|
|
16
16
|
);
|
|
17
17
|
case 'asst_head':
|
|
18
18
|
return h(Box, { height: 1, backgroundColor: hex.surface },
|
|
19
|
-
h(Text, { color: hex.purple, bold: true, backgroundColor: hex.surface }, '
|
|
19
|
+
h(Text, { color: hex.purple, bold: true, backgroundColor: hex.surface }, ' \u25C6 CLARITY')
|
|
20
20
|
);
|
|
21
21
|
case 'asst_line':
|
|
22
22
|
return h(Box, { height: 1, backgroundColor: hex.surface },
|
|
23
|
-
h(Text, { color: hex.text, backgroundColor: hex.surface, wrap: 'wrap' }, ' ' + (text || ' '))
|
|
23
|
+
h(Text, { color: hex.text, backgroundColor: hex.surface, wrap: 'wrap' }, ' \u2502 ' + (text || ' '))
|
|
24
24
|
);
|
|
25
25
|
case 'asst_foot':
|
|
26
26
|
return h(Box, { height: 1, backgroundColor: hex.surface },
|
|
27
|
-
h(Text, { color: hex.textDim, backgroundColor: hex.surface }, '
|
|
27
|
+
h(Text, { color: hex.textDim, backgroundColor: hex.surface }, ' \u25B8 ' + (parseInt(text) < 1000 ? text + 'ms' : (parseInt(text) / 1000).toFixed(1) + 's'))
|
|
28
28
|
);
|
|
29
29
|
case 'tool_line':
|
|
30
30
|
return h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
31
|
-
h(Text, { color: hex.purple, backgroundColor: hex.surfaceAlt }, '
|
|
31
|
+
h(Text, { color: hex.purple, backgroundColor: hex.surfaceAlt }, ' ' + sym.bullet + ' ' + (text || ''))
|
|
32
32
|
);
|
|
33
33
|
case 'sys_line':
|
|
34
34
|
return h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
35
|
-
h(Text, { color: hex.green, backgroundColor: hex.surfaceAlt }, '
|
|
35
|
+
h(Text, { color: hex.green, backgroundColor: hex.surfaceAlt }, ' ' + sym.bullet + ' ' + (text || ''))
|
|
36
36
|
);
|
|
37
37
|
case 'err_line':
|
|
38
38
|
return h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
39
|
-
h(Text, { color: hex.red, backgroundColor: hex.surfaceAlt }, '
|
|
39
|
+
h(Text, { color: hex.red, backgroundColor: hex.surfaceAlt }, ' ' + sym.cross + ' ' + (text || ''))
|
|
40
40
|
);
|
|
41
41
|
case 'stream_head':
|
|
42
42
|
return h(Box, { height: 1, backgroundColor: hex.surface },
|
|
43
|
-
h(Text, { color: hex.purple, bold: true, backgroundColor: hex.surface }, '
|
|
43
|
+
h(Text, { color: hex.purple, bold: true, backgroundColor: hex.surface }, ' \u25C6 CLARITY')
|
|
44
44
|
);
|
|
45
45
|
case 'stream_status':
|
|
46
46
|
return h(Box, { height: 1, backgroundColor: hex.surface },
|
|
47
|
-
h(Text, { color: hex.blue, backgroundColor: hex.surface }, '
|
|
47
|
+
h(Text, { color: hex.blue, backgroundColor: hex.surface }, ' \u25CF ' + (text || ''))
|
|
48
48
|
);
|
|
49
49
|
case 'stream_line':
|
|
50
50
|
return h(Box, { height: 1, backgroundColor: hex.surface },
|
|
51
|
-
h(Text, { color: hex.text, backgroundColor: hex.surface, wrap: 'wrap' }, ' ' + (text || ' '))
|
|
51
|
+
h(Text, { color: hex.text, backgroundColor: hex.surface, wrap: 'wrap' }, ' \u2502 ' + (text || ' '))
|
|
52
52
|
);
|
|
53
53
|
default:
|
|
54
54
|
return null;
|
|
@@ -59,43 +59,49 @@ export function MessageList({ messages, thinking, streamContent, agentStatus, to
|
|
|
59
59
|
const { viewport, contentWidth } = getLayout();
|
|
60
60
|
|
|
61
61
|
const entries = useMemo(() => {
|
|
62
|
-
|
|
62
|
+
return messages.map(m => ({
|
|
63
63
|
id: m.id, role: m.role, content: m.content,
|
|
64
64
|
duration: m.duration, toolName: m.toolName, error: m.error, completed: true,
|
|
65
65
|
}));
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
status: agentStatus || (thinking ? 'processing...' : ''),
|
|
71
|
-
completed: false,
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
return e;
|
|
75
|
-
}, [messages, thinking, streamContent, agentStatus]);
|
|
66
|
+
}, [messages]);
|
|
67
|
+
|
|
68
|
+
const showLogo = entries.length <= 1 && !thinking && !streamContent;
|
|
69
|
+
const effectiveViewport = showLogo ? Math.max(viewport - 14, 4) : viewport;
|
|
76
70
|
|
|
77
71
|
const { slice, clipIndex, clipLines } = useMemo(
|
|
78
|
-
() => sliceToViewport(entries,
|
|
79
|
-
[entries,
|
|
72
|
+
() => sliceToViewport(entries, effectiveViewport, contentWidth),
|
|
73
|
+
[entries, effectiveViewport, contentWidth]
|
|
80
74
|
);
|
|
81
75
|
|
|
82
|
-
const
|
|
76
|
+
const rawLines = useMemo(
|
|
83
77
|
() => buildLineArray(slice, clipIndex, clipLines, contentWidth),
|
|
84
78
|
[slice, clipIndex, clipLines, contentWidth]
|
|
85
79
|
);
|
|
86
80
|
|
|
87
|
-
const padded = [
|
|
88
|
-
|
|
89
|
-
|
|
81
|
+
const padded = [];
|
|
82
|
+
const logoRows = showLogo ? 14 : 0;
|
|
83
|
+
const fillRows = Math.max(0, effectiveViewport - rawLines.length - logoRows);
|
|
84
|
+
for (let i = 0; i < fillRows; i++) padded.push({ type: 'empty' });
|
|
85
|
+
if (showLogo) {
|
|
86
|
+
for (let i = 0; i < 14; i++) padded.push({ type: 'logo', line: i });
|
|
90
87
|
}
|
|
88
|
+
for (const ln of rawLines) padded.push(ln);
|
|
91
89
|
|
|
92
90
|
return h(Box, { height: viewport, flexDirection: 'column', overflow: 'hidden' },
|
|
93
|
-
padded.map((ln, i) =>
|
|
94
|
-
ln.type === 'empty'
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
91
|
+
padded.map((ln, i) => {
|
|
92
|
+
if (ln.type === 'empty') return h(Box, { key: 'e' + i, height: 1, backgroundColor: hex.bg });
|
|
93
|
+
if (ln.type === 'logo') {
|
|
94
|
+
const logoLine = LOGO[ln.line] || '';
|
|
95
|
+
return h(Box, { key: 'l' + i, height: 1, backgroundColor: hex.bg },
|
|
96
|
+
h(Text, {
|
|
97
|
+
color: (ln.line >= 1 && ln.line <= 5) || ln.line === 9 ? hex.accent : hex.textMuted,
|
|
98
|
+
backgroundColor: hex.bg,
|
|
99
|
+
}, ' ' + logoLine)
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
return h(Box, { key: (ln.data?.id || 'r') + '-' + i, height: 1 },
|
|
103
|
+
h(Line, { type: ln.type, text: ln.text, data: ln.data })
|
|
104
|
+
);
|
|
105
|
+
})
|
|
100
106
|
);
|
|
101
107
|
}
|
|
@@ -32,37 +32,56 @@ export function ModelPicker({ onSelect, onClose }) {
|
|
|
32
32
|
if (key.upArrow) setIdx(i => Math.max(0, i - 1));
|
|
33
33
|
if (key.downArrow) setIdx(i => Math.min(flat.length - 1, i + 1));
|
|
34
34
|
if (key.return) { const m = flat[idx]; if (m && !m._header) onSelect(m.id); return; }
|
|
35
|
-
if (key.escape) onClose();
|
|
35
|
+
if (key.escape || key.ctrl && key.c) onClose();
|
|
36
36
|
if (key.backspace) setSearch(s => s.slice(0, -1));
|
|
37
37
|
else if (input && !key.ctrl && !key.meta) setSearch(s => s + input);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
const w = Math.min(cols - 4, 52);
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
const items = flat.map((m, i) => {
|
|
43
|
+
if (m._header) {
|
|
44
|
+
return h(Box, { key: 'h-' + m._provider, height: 1, backgroundColor: hex.surfaceAlt },
|
|
45
|
+
h(Text, { color: hex.blue, bold: true, backgroundColor: hex.surfaceAlt }, sym.box.v + ' ' + m._provider.toUpperCase()),
|
|
46
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt }, ' '.repeat(Math.max(0, w - m._provider.length - 6)) + sym.box.v)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
const isSel = i === idx;
|
|
50
|
+
return h(Box, {
|
|
51
|
+
key: m.id, height: 1,
|
|
52
|
+
backgroundColor: isSel ? hex.selectionBg : hex.bg,
|
|
53
|
+
},
|
|
54
|
+
h(Text, {
|
|
55
|
+
color: isSel ? hex.selectionText : hex.text,
|
|
56
|
+
bold: isSel,
|
|
57
|
+
backgroundColor: isSel ? hex.selectionBg : hex.bg,
|
|
58
|
+
}, (isSel ? '\u276F ' : ' ') + m.label + (m.badge ? ' [' + m.badge + ']' : ''))
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const count = flat.filter(m => !m._header).length;
|
|
63
|
+
|
|
64
|
+
return h(Box, { flexDirection: 'column', backgroundColor: hex.bg, width: w },
|
|
43
65
|
h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
44
|
-
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
66
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
67
|
+
sym.box.tl + sym.box.h.repeat(w - 2) + sym.box.tr)
|
|
45
68
|
),
|
|
46
|
-
flat.map((m, i) => {
|
|
47
|
-
if (m._header) {
|
|
48
|
-
return h(Box, { key: 'h-' + m._provider, height: 1, backgroundColor: hex.surfaceAlt },
|
|
49
|
-
h(Text, { color: hex.blue, bold: true, backgroundColor: hex.surfaceAlt }, ' ' + sym.triD + ' ' + m._provider.toUpperCase())
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
const isSel = i === idx;
|
|
53
|
-
return h(Box, {
|
|
54
|
-
key: m.id, height: 1,
|
|
55
|
-
backgroundColor: isSel ? hex.selectionBg : 'transparent',
|
|
56
|
-
},
|
|
57
|
-
h(Text, {
|
|
58
|
-
color: isSel ? hex.selectionText : hex.text,
|
|
59
|
-
bold: isSel,
|
|
60
|
-
backgroundColor: isSel ? hex.selectionBg : 'transparent',
|
|
61
|
-
}, ' ' + m.label + (m.badge ? ' [' + m.badge + ']' : ''))
|
|
62
|
-
);
|
|
63
|
-
}),
|
|
64
69
|
h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
65
|
-
h(Text, { color: hex.
|
|
66
|
-
|
|
70
|
+
h(Text, { color: hex.gold, backgroundColor: hex.surfaceAlt }, sym.box.v + ' Models ' + count + ' available'),
|
|
71
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt }, ' '.repeat(Math.max(0, w - 19)) + sym.box.v)
|
|
72
|
+
),
|
|
73
|
+
h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
74
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
75
|
+
sym.box.v + ' ' + (search || '\u2026') + ' '.repeat(Math.max(0, w - 6)) + sym.box.v)
|
|
76
|
+
),
|
|
77
|
+
h(Box, { flexDirection: 'column', backgroundColor: hex.bg }, ...items),
|
|
78
|
+
h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
79
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
80
|
+
sym.box.bl + sym.box.h.repeat(w - 2) + sym.box.br)
|
|
81
|
+
),
|
|
82
|
+
h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
83
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
84
|
+
' \u2191\u2193 nav \u2192 select Esc close')
|
|
85
|
+
),
|
|
67
86
|
);
|
|
68
87
|
}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
|
-
import { hex
|
|
3
|
+
import { hex } from '../config/theme.js';
|
|
4
4
|
import { getLayout } from '../config/layout.js';
|
|
5
5
|
const { createElement: h } = React;
|
|
6
6
|
|
|
7
7
|
export function StatusBar({ model, provider, agentMode, thinking }) {
|
|
8
8
|
const { cols } = getLayout();
|
|
9
|
-
const m = model.replace(/^[^/]+\//, '').slice(0,
|
|
10
|
-
const left =
|
|
11
|
-
const right = (agentMode ? 'AGENT' : 'USER') + (thinking ? '
|
|
12
|
-
const gap = Math.max(1, cols - left.length - right.length -
|
|
9
|
+
const m = model.replace(/^[^/]+\//, '').slice(0, 20);
|
|
10
|
+
const left = '\u25C9 CLARITY \u00B7 ' + m + ' \u00B7 ' + provider;
|
|
11
|
+
const right = (agentMode ? '\u25C8 AGENT' : '\u25CB USER') + (thinking ? ' \u25CF' : '');
|
|
12
|
+
const gap = Math.max(1, cols - left.length - right.length - 2);
|
|
13
|
+
|
|
13
14
|
return h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
14
|
-
h(Text, { color: hex.
|
|
15
|
+
h(Text, { color: hex.accent, bold: true, backgroundColor: hex.surfaceAlt }, ' ' + left),
|
|
16
|
+
h(Text, { backgroundColor: hex.surfaceAlt }, ' '.repeat(gap)),
|
|
17
|
+
h(Text, { color: hex.textDim, backgroundColor: hex.surfaceAlt }, right + ' ')
|
|
15
18
|
);
|
|
16
19
|
}
|
|
@@ -12,40 +12,23 @@ export function ThinkingBlock({ toolResults, duration }) {
|
|
|
12
12
|
? (duration < 1000 ? duration + 'ms' : (duration / 1000).toFixed(1) + 's')
|
|
13
13
|
: '';
|
|
14
14
|
|
|
15
|
-
const headerText = sym.triR + ' ' + (collapsed ? sym.triR : sym.triD) + ' Thought' + (durStr ? ' (' + durStr + ')' : '');
|
|
16
|
-
|
|
17
|
-
const rows = 1;
|
|
18
|
-
const totalRows = collapsed ? 1 : 1 + items.length;
|
|
19
|
-
|
|
20
15
|
return h(Box, { flexDirection: 'column', backgroundColor: hex.surfaceAlt },
|
|
21
16
|
h(Box, { height: 1 },
|
|
22
|
-
h(Text, { color: hex.purple, backgroundColor: hex.surfaceAlt },
|
|
17
|
+
h(Text, { color: hex.purple, backgroundColor: hex.surfaceAlt },
|
|
18
|
+
' ' + (collapsed ? sym.triR : sym.triD) + ' Thought' + (durStr ? ' (' + durStr + ')' : ''))
|
|
23
19
|
),
|
|
24
|
-
collapsed
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
: null;
|
|
38
|
-
return h(Box, { key: tr.execId || i, flexDirection: 'column' },
|
|
39
|
-
h(Box, { height: 1 },
|
|
40
|
-
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt }, line)
|
|
41
|
-
),
|
|
42
|
-
contentLine
|
|
43
|
-
? h(Box, { height: 1 },
|
|
44
|
-
h(Text, { color: hex.textDim, backgroundColor: hex.surfaceAlt }, contentLine)
|
|
45
|
-
)
|
|
46
|
-
: null
|
|
47
|
-
);
|
|
48
|
-
})
|
|
49
|
-
)
|
|
20
|
+
collapsed ? null : h(Box, { flexDirection: 'column', backgroundColor: hex.surfaceAlt },
|
|
21
|
+
items.map((tr, i) => {
|
|
22
|
+
const isLast = i === items.length - 1;
|
|
23
|
+
const prefix = isLast ? sym.treeTip + '\u2500' : sym.treeFork + '\u2500';
|
|
24
|
+
const conn = isLast ? ' ' : sym.treeCon;
|
|
25
|
+
const ico = tr.status === 'failed' ? sym.cross : sym.circle;
|
|
26
|
+
const td = tr.duration ? ' ' + tr.duration + 'ms' : '';
|
|
27
|
+
return h(Box, { key: tr.execId || i, height: 1 },
|
|
28
|
+
h(Text, { color: hex.textMuted, backgroundColor: hex.surfaceAlt },
|
|
29
|
+
' ' + prefix + ' ' + ico + ' ' + tr.name + td)
|
|
30
|
+
);
|
|
31
|
+
})
|
|
32
|
+
)
|
|
50
33
|
);
|
|
51
34
|
}
|
|
@@ -16,21 +16,25 @@ export function ToolCard({ exec, isActive }) {
|
|
|
16
16
|
const c = status === 'failed' ? hex.red : hex.green;
|
|
17
17
|
const icon = status === 'failed' ? sym.cross : sym.bullet;
|
|
18
18
|
return h(Box, { height: 1, backgroundColor: hex.surfaceAlt },
|
|
19
|
-
h(Text, { color: c, backgroundColor: hex.surfaceAlt }, '
|
|
19
|
+
h(Text, { color: c, backgroundColor: hex.surfaceAlt }, ' ' + icon + ' ' + name + (dur ? ' ' + dur : ''))
|
|
20
20
|
);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const w = Math.min(cols -
|
|
24
|
-
const lines = [sym.bullet + ' ' + name + (dur ? ' ' + dur : '')];
|
|
25
|
-
if (args.length < 80) lines.push(' ' + sym.triR + ' ' + args.slice(0, w));
|
|
26
|
-
if (status === 'running') lines.push(' ' + sym.dot + ' running');
|
|
27
|
-
if (status === 'failed' && exec.error) lines.push(' ' + sym.cross + ' ' + String(exec.error).slice(0, w - 4));
|
|
23
|
+
const w = Math.min(cols - 8, 56);
|
|
28
24
|
|
|
29
25
|
return h(Box, { flexDirection: 'column', backgroundColor: hex.surfaceAlt },
|
|
30
|
-
|
|
31
|
-
h(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
h(Box, { height: 1 },
|
|
27
|
+
h(Text, { color: hex.purple, backgroundColor: hex.surfaceAlt }, ' ' + sym.bullet + ' ' + name + (dur ? ' ' + dur : ''))
|
|
28
|
+
),
|
|
29
|
+
args.length < 80
|
|
30
|
+
? h(Box, { height: 1 },
|
|
31
|
+
h(Text, { color: hex.textDim, backgroundColor: hex.surfaceAlt }, ' ' + sym.triR + ' ' + args.slice(0, w))
|
|
32
|
+
)
|
|
33
|
+
: null,
|
|
34
|
+
status === 'running'
|
|
35
|
+
? h(Box, { height: 1 },
|
|
36
|
+
h(Text, { color: hex.blue, backgroundColor: hex.surfaceAlt }, ' ' + sym.dot + ' running')
|
|
37
|
+
)
|
|
38
|
+
: null,
|
|
35
39
|
);
|
|
36
40
|
}
|
package/src/config/layout.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import wrapAnsi from 'wrap-ansi';
|
|
2
|
+
import stringWidth from 'string-width';
|
|
2
3
|
|
|
3
4
|
export function getLayout() {
|
|
4
5
|
const rows = process.stdout.rows || 30;
|
|
5
6
|
const cols = process.stdout.columns || 80;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
const headerHeight = 1;
|
|
8
|
+
const dockHeight = 4;
|
|
9
|
+
const viewport = Math.max(8, rows - headerHeight - dockHeight);
|
|
10
|
+
const contentWidth = Math.max(20, cols - 4);
|
|
11
|
+
return { rows, cols, headerHeight, dockHeight, viewport, contentWidth, pad: ' ' };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function sw(text) {
|
|
15
|
+
return stringWidth(text || '');
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export function wrapText(text, width) {
|
|
@@ -33,9 +34,9 @@ export function truncateText(text, maxLines, width) {
|
|
|
33
34
|
|
|
34
35
|
export function measureEntry(entry, w) {
|
|
35
36
|
const nr = entry.role;
|
|
36
|
-
if (nr === 'user') return 1 + countLines(entry.content, w)
|
|
37
|
+
if (nr === 'user') return 1 + countLines(entry.content, w);
|
|
37
38
|
if (nr === 'assistant') return 2 + countLines(entry.content, w) + (entry.duration ? 1 : 0);
|
|
38
|
-
if (nr === 'tool') return
|
|
39
|
+
if (nr === 'tool') return 1;
|
|
39
40
|
if (nr === 'system' || nr === 'error') return 1;
|
|
40
41
|
if (nr === 'streaming') return 2 + Math.min(40, countLines(entry.content, w));
|
|
41
42
|
return 1;
|
|
@@ -78,7 +79,6 @@ export function buildLineArray(slice, clipIndex, clipLines, w) {
|
|
|
78
79
|
}
|
|
79
80
|
} else if (nr === 'assistant') {
|
|
80
81
|
lines.push({ type: 'asst_head', data: e });
|
|
81
|
-
lines.push({ type: 'asst_bar', data: e });
|
|
82
82
|
if (skip <= 0) {
|
|
83
83
|
const wrapped = wrapText(e.content, w);
|
|
84
84
|
const contentLines = wrapped.split('\n');
|
|
@@ -94,32 +94,18 @@ export function buildLineArray(slice, clipIndex, clipLines, w) {
|
|
|
94
94
|
}
|
|
95
95
|
if (e.duration) lines.push({ type: 'asst_foot', text: String(e.duration), data: e });
|
|
96
96
|
} else if (nr === 'tool') {
|
|
97
|
-
|
|
98
|
-
if (e.completed) {
|
|
99
|
-
if (skip <= 0) lines.push({ type: 'tool_line', text: label + ' ' + (e.duration ? e.duration + 'ms' : ''), data: e });
|
|
100
|
-
} else {
|
|
101
|
-
lines.push({ type: 'tool_head', text: label, data: e });
|
|
102
|
-
if (e.content) {
|
|
103
|
-
const wrapped = wrapText(String(e.content).slice(0, w * 3), w);
|
|
104
|
-
const cls = wrapped.split('\n');
|
|
105
|
-
for (let ci = Math.max(0, skip - 1); ci < Math.min(cls.length, 4); ci++) {
|
|
106
|
-
lines.push({ type: 'tool_line', text: cls[ci], data: e });
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
97
|
+
lines.push({ type: 'tool_line', text: (e.error ? '\u2716 ' : '\u25C9 ') + (e.toolName || 'tool') + (e.duration ? ' ' + e.duration + 'ms' : ''), data: e });
|
|
110
98
|
} else if (nr === 'system') {
|
|
111
99
|
if (skip <= 0) lines.push({ type: 'sys_line', text: e.content, data: e });
|
|
112
100
|
} else if (nr === 'error') {
|
|
113
101
|
if (skip <= 0) lines.push({ type: 'err_line', text: e.content, data: e });
|
|
114
102
|
} else if (nr === 'streaming') {
|
|
115
103
|
lines.push({ type: 'stream_head', data: e });
|
|
116
|
-
lines.push({ type: 'stream_bar', data: e });
|
|
117
104
|
if (e.status) lines.push({ type: 'stream_status', text: e.status, data: e });
|
|
118
105
|
const wrapped = wrapText(e.content || '', w);
|
|
119
106
|
const contentLines = wrapped.split('\n');
|
|
120
|
-
const maxLines = 40;
|
|
121
107
|
const startLine = Math.min(skip, contentLines.length);
|
|
122
|
-
const endLine = Math.min(contentLines.length, startLine +
|
|
108
|
+
const endLine = Math.min(contentLines.length, startLine + 40);
|
|
123
109
|
for (let ci = startLine; ci < endLine; ci++) {
|
|
124
110
|
lines.push({ type: 'stream_line', text: contentLines[ci], data: e });
|
|
125
111
|
}
|
package/src/config/models.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const ALL_MODELS = [
|
|
2
|
-
{ id: 'huggingface/Universal-618/Clarity-flash-weights', provider: 'huggingface', label: '
|
|
3
|
-
{ id: 'huggingface/Universal-618/Clarity-heavy-weights', provider: 'huggingface', label: '
|
|
2
|
+
{ id: 'huggingface/Universal-618/Clarity-flash-weights', provider: 'huggingface', label: 'DeepSeek R1 1.5B', badge: 'Reasoning' },
|
|
3
|
+
{ id: 'huggingface/Universal-618/Clarity-heavy-weights', provider: 'huggingface', label: 'DeepSeek R1 7B', badge: 'Reasoning' },
|
|
4
4
|
{ id: 'groq/llama-3.3-70b-versatile', provider: 'groq', label: 'Llama 3.3 70B Versatile', badge: null },
|
|
5
5
|
{ id: 'groq/llama-3.1-8b-instant', provider: 'groq', label: 'Llama 3.1 8B Instant', badge: 'Fast' },
|
|
6
6
|
{ id: 'groq/llama-4-scout-17b-16e-instruct', provider: 'groq', label: 'Llama 4 Scout 17B', badge: null },
|
package/src/config/theme.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import gradient from 'gradient-string';
|
|
3
|
+
import figures from 'figures';
|
|
2
4
|
|
|
3
5
|
export const hex = {
|
|
4
6
|
bg: '#0A0A14',
|
|
@@ -21,7 +23,6 @@ export const hex = {
|
|
|
21
23
|
textMuted: '#555577',
|
|
22
24
|
white: '#FFFFFF',
|
|
23
25
|
black: '#000000',
|
|
24
|
-
modalOverlay: 'rgba(0,0,0,0.85)',
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
export const color = {
|
|
@@ -46,32 +47,47 @@ export const color = {
|
|
|
46
47
|
};
|
|
47
48
|
|
|
48
49
|
export const sym = {
|
|
49
|
-
diamond: '\u25C6',
|
|
50
50
|
circle: '\u25C9',
|
|
51
51
|
dot: '\u25CF',
|
|
52
|
-
smallDot: '\u25CB',
|
|
53
52
|
triR: '\u25B8',
|
|
54
53
|
triD: '\u25BE',
|
|
55
54
|
bullet: '\u25C9',
|
|
56
55
|
cross: '\u2716',
|
|
57
56
|
ellipsis: '\u2026',
|
|
58
57
|
mdash: '\u2014',
|
|
59
|
-
ndash: '\u2013',
|
|
60
58
|
midDot: '\u00B7',
|
|
61
59
|
arrowR: '\u2192',
|
|
62
|
-
arrowL: '\u2190',
|
|
63
60
|
arrowU: '\u2191',
|
|
64
61
|
arrowD: '\u2193',
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
box: {
|
|
63
|
+
tl: '\u250C', tr: '\u2510', bl: '\u2514', br: '\u2518',
|
|
64
|
+
h: '\u2500', v: '\u2502',
|
|
65
|
+
},
|
|
67
66
|
treeJ: '\u2514',
|
|
68
67
|
treeT: '\u251C',
|
|
69
68
|
treeCon: '\u2502',
|
|
70
|
-
triR2: '\u25B8',
|
|
71
|
-
triD2: '\u25BE',
|
|
72
|
-
u: { h: '\u2500' },
|
|
73
69
|
treeTip: '\u2570',
|
|
74
70
|
treeFork: '\u256D',
|
|
75
71
|
star: '\u2726',
|
|
76
|
-
|
|
72
|
+
gear: '\u2699',
|
|
73
|
+
pointer: '\u276F',
|
|
77
74
|
};
|
|
75
|
+
|
|
76
|
+
export const gradientText = gradient(['#FF6B35', '#3B82F6']);
|
|
77
|
+
export const gradientLine = gradient(['#FF6B35', '#3B82F6']);
|
|
78
|
+
|
|
79
|
+
export const LOGO = [
|
|
80
|
+
' ',
|
|
81
|
+
' ██████ ██ █████ ██████ ██ ████████ ██ ██ ',
|
|
82
|
+
' ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ',
|
|
83
|
+
' ██████ ██ ███████ ██████ ██ ██ ██ ██ ',
|
|
84
|
+
' ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ',
|
|
85
|
+
' ██ ███████ ██ ██ ██ ██ ██ ██ ██ ██████▄ ',
|
|
86
|
+
' ',
|
|
87
|
+
' █████ ██ ',
|
|
88
|
+
' ██ ██ ██ ',
|
|
89
|
+
' ██████ ██ ',
|
|
90
|
+
' ██ ██ ██ ',
|
|
91
|
+
' ██ ██ ███████ ',
|
|
92
|
+
' ',
|
|
93
|
+
];
|