agen-vektor 0.3.5 → 0.3.6
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.js +6 -3
- package/dist/tui/chat.js +121 -36
- package/dist/tui/components.js +4 -4
- package/dist/tui/input.js +2 -1
- package/dist/tui/statusbar.js +19 -11
- package/dist/tui/theme.js +20 -16
- package/dist/utils/terminal.js +19 -1
- package/package.json +1 -1
package/dist/tui/app.js
CHANGED
|
@@ -78,6 +78,7 @@ class App {
|
|
|
78
78
|
planToExecute = null;
|
|
79
79
|
abortController = null;
|
|
80
80
|
spinner = new theme_1.Spinner();
|
|
81
|
+
runStart = null;
|
|
81
82
|
constructor(agent, opts) {
|
|
82
83
|
this.agent = agent;
|
|
83
84
|
this.opts = opts;
|
|
@@ -90,7 +91,6 @@ class App {
|
|
|
90
91
|
start() {
|
|
91
92
|
this.connected = true;
|
|
92
93
|
this.status = 'Ready';
|
|
93
|
-
this.addSystem('VectorHead ready. Type a request or press ? for help.');
|
|
94
94
|
if (this.opts.continueSession && this.sessionName) {
|
|
95
95
|
const sess = (0, session_1.loadSession)(this.sessionName);
|
|
96
96
|
if (sess) {
|
|
@@ -241,6 +241,7 @@ class App {
|
|
|
241
241
|
this.statusColor = (0, theme_1.statusColor)('Thinking');
|
|
242
242
|
this.spinner.reset();
|
|
243
243
|
this.abortController = new AbortController();
|
|
244
|
+
this.runStart = Date.now();
|
|
244
245
|
// Show plan first if requested
|
|
245
246
|
if (this.opts.showPlan) {
|
|
246
247
|
this.status = 'Planning…';
|
|
@@ -356,6 +357,7 @@ class App {
|
|
|
356
357
|
m.content = '(no response)';
|
|
357
358
|
}
|
|
358
359
|
this.running = false;
|
|
360
|
+
this.runStart = null;
|
|
359
361
|
if (this.statusColor !== terminal_1.ANSI.red) {
|
|
360
362
|
this.status = 'Ready';
|
|
361
363
|
this.statusColor = terminal_1.ANSI.green;
|
|
@@ -725,6 +727,7 @@ class App {
|
|
|
725
727
|
mode: this.agent.config.permissionMode,
|
|
726
728
|
spinner: this.spinner.frame(),
|
|
727
729
|
running: this.running,
|
|
730
|
+
elapsed: this.running && this.runStart !== null ? Math.floor((Date.now() - this.runStart) / 1000) : undefined,
|
|
728
731
|
};
|
|
729
732
|
const parts = [];
|
|
730
733
|
// Row 1-2: header + project line
|
|
@@ -732,9 +735,9 @@ class App {
|
|
|
732
735
|
parts.push((0, terminal_1.cursorTo)(2, 1) + terminal_1.ANSI.clearLineEnd + (0, statusbar_1.renderProjectBar)(cols, info));
|
|
733
736
|
// Row 3: separator
|
|
734
737
|
parts.push((0, terminal_1.cursorTo)(3, 1) + terminal_1.ANSI.clearLineEnd + theme_1.THEME.border + terminal_1.BOX.h.repeat(Math.min(cols, 120)) + theme_1.THEME.reset);
|
|
735
|
-
// Chat area (rows 4..) —
|
|
738
|
+
// Chat area (rows 4..) — Freebuff-style message blocks (welcome screen when empty)
|
|
736
739
|
const chatH = this.chatHeight();
|
|
737
|
-
const all = (0, chat_1.renderMessages)(this.messages, cols);
|
|
740
|
+
const all = this.messages.length === 0 ? (0, chat_1.renderWelcome)(cols, chatH, this.agent.cwd) : (0, chat_1.renderMessages)(this.messages, cols);
|
|
738
741
|
const maxScroll = Math.max(0, all.length - chatH);
|
|
739
742
|
this.scroll = Math.min(this.scroll, maxScroll);
|
|
740
743
|
const visible = all.slice(this.scroll, this.scroll + chatH);
|
package/dist/tui/chat.js
CHANGED
|
@@ -3,54 +3,98 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.renderMessages = renderMessages;
|
|
4
4
|
exports.defaultScroll = defaultScroll;
|
|
5
5
|
exports.clampScroll = clampScroll;
|
|
6
|
+
exports.renderWelcome = renderWelcome;
|
|
6
7
|
/**
|
|
7
|
-
* Chat view — renders conversation messages the Freebuff way
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Chat view — renders conversation messages the Freebuff way (adopted from
|
|
9
|
+
* CodebuffAI/freebuff TUI):
|
|
10
|
+
* - user messages: green left bar + italic text (no label)
|
|
11
|
+
* - assistant messages: plain foreground text
|
|
12
|
+
* - tool calls: compact dim blocks (⚙ tool · preview)
|
|
10
13
|
* No boxes or frames — stays aligned on narrow/mobile terminals.
|
|
11
14
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
15
|
+
* ██╗...VECTORHEAD logo (welcome only)
|
|
16
|
+
* VectorHead will run commands on your behalf to help you build.
|
|
17
|
+
* Directory /home/x
|
|
14
18
|
*
|
|
15
|
-
*
|
|
16
|
-
* perbaiki error auth
|
|
19
|
+
* │ Inspecting project... (user, italic, green bar)
|
|
17
20
|
*
|
|
18
|
-
*
|
|
21
|
+
* Saya cek dulu file auth.ts. (assistant)
|
|
22
|
+
*
|
|
23
|
+
* ⚙ shell · $ npm test (tool, dim)
|
|
19
24
|
*/
|
|
20
25
|
const theme_1 = require("./theme");
|
|
21
26
|
const terminal_1 = require("../utils/terminal");
|
|
22
|
-
/**
|
|
23
|
-
function
|
|
24
|
-
|
|
27
|
+
/** Center a string (with ANSI) horizontally within `width`. */
|
|
28
|
+
function center(str, width) {
|
|
29
|
+
const pad = Math.max(0, Math.floor((width - (0, terminal_1.visibleWidth)(str)) / 2));
|
|
30
|
+
return ' '.repeat(pad) + str;
|
|
25
31
|
}
|
|
26
|
-
|
|
27
|
-
user: { prefix: 'YOU', color: theme_1.THEME.gold },
|
|
28
|
-
assistant: { prefix: 'VectorHead', color: theme_1.THEME.accent },
|
|
29
|
-
tool: { prefix: 'TOOL', color: theme_1.THEME.warn },
|
|
30
|
-
system: { prefix: '···', color: theme_1.THEME.muted },
|
|
31
|
-
error: { prefix: '✗', color: theme_1.THEME.error },
|
|
32
|
-
success: { prefix: '✓', color: theme_1.THEME.success },
|
|
33
|
-
warning: { prefix: '⚠', color: theme_1.THEME.warn },
|
|
34
|
-
};
|
|
35
|
-
/** Render one message as a label row + wrapped content (user right-aligned). */
|
|
32
|
+
/** Render one message as Freebuff-style lines. */
|
|
36
33
|
function renderMessage(m, width) {
|
|
37
|
-
const style = KIND_STYLE[m.kind];
|
|
38
|
-
const prefix = m.prefix || style.prefix;
|
|
39
|
-
const dot = m.streaming ? `${theme_1.THEME.warn}◐${theme_1.THEME.reset}` : `${style.color}●${theme_1.THEME.reset}`;
|
|
40
|
-
const meta = m.meta ? ` ${theme_1.THEME.dim}${m.meta}${theme_1.THEME.reset}` : '';
|
|
41
|
-
const toolName = m.tool ? ` ${theme_1.THEME.warn}${m.tool}${theme_1.THEME.reset}` : '';
|
|
42
|
-
const rightAlign = m.kind === 'user';
|
|
43
|
-
// Label row: ● Label · tool · model (right-aligned for user, truncated to fit narrow widths)
|
|
44
|
-
const label = `${dot} ${theme_1.THEME.bold}${style.color}${prefix}${theme_1.THEME.reset}${toolName}${meta}`;
|
|
45
|
-
const out = [rightAlign ? padLeft((0, terminal_1.truncate)(label, width), width) : (0, terminal_1.truncate)(label, width)];
|
|
46
|
-
// Content rows — wrapped to fit; user wraps the full width for a flush right edge
|
|
47
34
|
const body = m.content || '(no content)';
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
35
|
+
const out = [];
|
|
36
|
+
switch (m.kind) {
|
|
37
|
+
case 'user': {
|
|
38
|
+
// Green vertical bar + italic text — Freebuff's signature user style.
|
|
39
|
+
const bar = `${theme_1.THEME.userLine}│${theme_1.THEME.reset}`;
|
|
40
|
+
const inner = Math.max(4, width - 2);
|
|
41
|
+
for (const line of (0, terminal_1.wrapText)(body, inner)) {
|
|
42
|
+
out.push(`${bar} ${terminal_1.ANSI.italic}${line}${theme_1.THEME.reset}`);
|
|
43
|
+
}
|
|
44
|
+
out.push('');
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
case 'tool': {
|
|
48
|
+
// Compact dim block: ⚙ tool · preview, then dim result lines.
|
|
49
|
+
const label = m.tool ? `⚙ ${m.tool}` : '⚙';
|
|
50
|
+
const parts = body.split('\n');
|
|
51
|
+
const first = `${theme_1.THEME.warn}${label}${theme_1.THEME.reset} ${theme_1.THEME.faint}${(parts[0] || '').trim()}${theme_1.THEME.reset}`;
|
|
52
|
+
if (first.trim()) {
|
|
53
|
+
for (const line of (0, terminal_1.wrapText)(first, width))
|
|
54
|
+
out.push(line);
|
|
55
|
+
}
|
|
56
|
+
const rest = parts.slice(1).filter((l) => l.trim());
|
|
57
|
+
if (rest.length > 0) {
|
|
58
|
+
for (const line of (0, terminal_1.wrapText)(rest.join('\n'), Math.max(4, width - 2))) {
|
|
59
|
+
out.push(`${theme_1.THEME.faint}${line}${theme_1.THEME.reset}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
out.push('');
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
case 'system':
|
|
66
|
+
for (const line of (0, terminal_1.wrapText)(body, width))
|
|
67
|
+
out.push(`${theme_1.THEME.faint}${line}${theme_1.THEME.reset}`);
|
|
68
|
+
out.push('');
|
|
69
|
+
return out;
|
|
70
|
+
case 'error':
|
|
71
|
+
for (const line of (0, terminal_1.wrapText)(body, width))
|
|
72
|
+
out.push(`${theme_1.THEME.error}${line}${theme_1.THEME.reset}`);
|
|
73
|
+
out.push('');
|
|
74
|
+
return out;
|
|
75
|
+
case 'success':
|
|
76
|
+
for (const line of (0, terminal_1.wrapText)(body, width))
|
|
77
|
+
out.push(`${theme_1.THEME.success}${line}${theme_1.THEME.reset}`);
|
|
78
|
+
out.push('');
|
|
79
|
+
return out;
|
|
80
|
+
case 'warning':
|
|
81
|
+
for (const line of (0, terminal_1.wrapText)(body, width))
|
|
82
|
+
out.push(`${theme_1.THEME.warn}${line}${theme_1.THEME.reset}`);
|
|
83
|
+
out.push('');
|
|
84
|
+
return out;
|
|
85
|
+
default: {
|
|
86
|
+
// Assistant: plain foreground text (Freebuff style — no label).
|
|
87
|
+
const marker = m.streaming ? `${theme_1.THEME.info}◐ ${theme_1.THEME.reset}` : '';
|
|
88
|
+
const lines = (0, terminal_1.wrapText)(body, width);
|
|
89
|
+
if (lines.length === 0)
|
|
90
|
+
lines.push('(no content)');
|
|
91
|
+
for (let i = 0; i < lines.length; i++) {
|
|
92
|
+
out.push(i === 0 ? `${marker}${lines[i]}` : lines[i]);
|
|
93
|
+
}
|
|
94
|
+
out.push('');
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
51
97
|
}
|
|
52
|
-
out.push(''); // blank separator between messages
|
|
53
|
-
return out;
|
|
54
98
|
}
|
|
55
99
|
/** Precompute the full list of rendered lines for all messages. */
|
|
56
100
|
function renderMessages(messages, width) {
|
|
@@ -69,3 +113,44 @@ function clampScroll(offset, messages, width, height) {
|
|
|
69
113
|
const max = Math.max(0, all.length - height);
|
|
70
114
|
return Math.min(Math.max(0, offset), max);
|
|
71
115
|
}
|
|
116
|
+
/** Full-width VECTORHEAD ASCII logo (white, Freebuff block style). */
|
|
117
|
+
const VECTORHEAD_LOGO = [
|
|
118
|
+
'██╗ ██╗███████╗ ██████╗████████╗ ██████╗ ██████╗ ██╗ ██╗███████╗ █████╗ ██████╗',
|
|
119
|
+
'██║ ██║██╔════╝██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗██║ ██║██╔════╝██╔══██╗██╔══██╗',
|
|
120
|
+
'██║ ██║█████╗ ██║ ██║ ██║ ██║██████╔╝███████║█████╗ ███████║██║ ██║',
|
|
121
|
+
'╚██╗ ██╔╝██╔══╝ ██║ ██║ ██║ ██║██╔══██╗██╔══██║██╔══╝ ██╔══██║██║ ██║',
|
|
122
|
+
' ╚████╔╝ ███████╗╚██████╗ ██║ ╚██████╔╝██║ ██║██║ ██║███████╗██║ ██║██████╔╝',
|
|
123
|
+
' ╚═══╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═════╝ ',
|
|
124
|
+
];
|
|
125
|
+
/**
|
|
126
|
+
* Freebuff-style welcome screen (shown while the chat is empty): logo,
|
|
127
|
+
* tagline and Directory line, centered in the chat area. On narrow
|
|
128
|
+
* terminals the full logo is replaced by a compact wordmark.
|
|
129
|
+
*/
|
|
130
|
+
function renderWelcome(width, height, cwd) {
|
|
131
|
+
const lines = [];
|
|
132
|
+
if (width >= 85) { // full logo is ~82 cols; narrower terminals get the wordmark
|
|
133
|
+
for (const l of VECTORHEAD_LOGO)
|
|
134
|
+
lines.push(`${theme_1.THEME.textBright}${l}${theme_1.THEME.reset}`);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
lines.push(`${theme_1.THEME.accent}▮${theme_1.THEME.reset} ${theme_1.THEME.bold}${theme_1.THEME.textBright}VectorHead${theme_1.THEME.reset}`);
|
|
138
|
+
}
|
|
139
|
+
lines.push('');
|
|
140
|
+
for (const t of ['VectorHead will run commands on your behalf', 'to help you build.']) {
|
|
141
|
+
for (const line of (0, terminal_1.wrapText)(t, Math.max(10, width - 4))) {
|
|
142
|
+
lines.push(`${theme_1.THEME.muted}${line}${theme_1.THEME.reset}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
lines.push('');
|
|
146
|
+
lines.push(`${theme_1.THEME.muted}Directory ${theme_1.THEME.directory}${(0, terminal_1.truncate)(cwd, Math.max(10, width - 12))}${theme_1.THEME.reset}`);
|
|
147
|
+
const centered = lines.map((l) => center(l, width));
|
|
148
|
+
const padTop = Math.max(0, Math.floor((height - centered.length) / 2));
|
|
149
|
+
const out = [];
|
|
150
|
+
for (let i = 0; i < padTop; i++)
|
|
151
|
+
out.push('');
|
|
152
|
+
out.push(...centered);
|
|
153
|
+
while (out.length < height)
|
|
154
|
+
out.push('');
|
|
155
|
+
return out.slice(0, Math.max(0, height));
|
|
156
|
+
}
|
package/dist/tui/components.js
CHANGED
|
@@ -51,13 +51,13 @@ function renderSelector(opts) {
|
|
|
51
51
|
const top = Math.max(1, Math.floor((opts.rows - height) / 2));
|
|
52
52
|
const left = Math.max(1, Math.floor((opts.cols - innerWidth - 2) / 2));
|
|
53
53
|
const lines = [];
|
|
54
|
-
const borderColor =
|
|
54
|
+
const borderColor = theme_1.THEME.accent;
|
|
55
55
|
const contentW = innerWidth - 2;
|
|
56
|
-
// Title row: ╭── Title ────────────╮ (exactly innerWidth+2 wide)
|
|
56
|
+
// Title row: ╭── Title ────────────╮ (exactly innerWidth+2 wide, green accent)
|
|
57
57
|
const t = (0, terminal_1.truncate)(opts.title, contentW);
|
|
58
58
|
lines.push((0, terminal_1.cursorTo)(top, left) +
|
|
59
59
|
borderColor + terminal_1.BOX.tl + terminal_1.BOX.h.repeat(2) +
|
|
60
|
-
terminal_1.ANSI.bold +
|
|
60
|
+
terminal_1.ANSI.bold + theme_1.THEME.textBright + t + terminal_1.ANSI.reset + borderColor +
|
|
61
61
|
terminal_1.BOX.h.repeat(Math.max(0, innerWidth - 2 - (0, terminal_1.visibleWidth)(t))) + terminal_1.BOX.tr);
|
|
62
62
|
for (let i = 0; i < visible; i++) {
|
|
63
63
|
const idx = start + i;
|
|
@@ -69,7 +69,7 @@ function renderSelector(opts) {
|
|
|
69
69
|
const label = marker + option.label;
|
|
70
70
|
const hint = option.hint ? terminal_1.ANSI.dim + ' — ' + option.hint + terminal_1.ANSI.reset : '';
|
|
71
71
|
if (idx === opts.selected) {
|
|
72
|
-
content = terminal_1.ANSI.bold +
|
|
72
|
+
content = terminal_1.ANSI.bold + theme_1.THEME.accent + (0, terminal_1.truncate)(label, contentW - (0, terminal_1.visibleWidth)(hint)) + terminal_1.ANSI.reset + hint;
|
|
73
73
|
}
|
|
74
74
|
else {
|
|
75
75
|
content = (0, terminal_1.truncate)(label, contentW - (0, terminal_1.visibleWidth)(hint)) + hint;
|
package/dist/tui/input.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.InputBox = void 0;
|
|
|
5
5
|
* Input box — single-line text input with cursor, history, and hints.
|
|
6
6
|
*/
|
|
7
7
|
const terminal_1 = require("../utils/terminal");
|
|
8
|
+
const theme_1 = require("./theme");
|
|
8
9
|
class InputBox {
|
|
9
10
|
text = '';
|
|
10
11
|
cursor = 0;
|
|
@@ -97,7 +98,7 @@ class InputBox {
|
|
|
97
98
|
}
|
|
98
99
|
/** Render the input line, returns the cursor column (0-based within line). */
|
|
99
100
|
render(width) {
|
|
100
|
-
const prefix = `${
|
|
101
|
+
const prefix = `${theme_1.THEME.accent}❯${terminal_1.ANSI.reset} `;
|
|
101
102
|
const prefixWidth = 2; // ❯ + space (ANSI codes are invisible)
|
|
102
103
|
const available = Math.max(1, width - prefixWidth - 1);
|
|
103
104
|
let display = this.text;
|
package/dist/tui/statusbar.js
CHANGED
|
@@ -4,9 +4,10 @@ exports.renderHeader = renderHeader;
|
|
|
4
4
|
exports.renderProjectBar = renderProjectBar;
|
|
5
5
|
exports.renderStatusBar = renderStatusBar;
|
|
6
6
|
/**
|
|
7
|
-
* Status bar — top header + project line + bottom
|
|
8
|
-
*
|
|
9
|
-
* terminals.
|
|
7
|
+
* Status bar — top header + project line + bottom status bar.
|
|
8
|
+
* Freebuff-style clean lines (no shortcut hints), truncated to fit
|
|
9
|
+
* narrow/mobile terminals. Bottom bar shows live status, spinner and
|
|
10
|
+
* an elapsed timer while the agent is working.
|
|
10
11
|
*/
|
|
11
12
|
const terminal_1 = require("../utils/terminal");
|
|
12
13
|
const theme_1 = require("./theme");
|
|
@@ -28,7 +29,7 @@ function renderHeader(width, info) {
|
|
|
28
29
|
const status = `${dot} ${(0, theme_1.statusColor)(info.status)}${info.status}${theme_1.THEME.reset}`;
|
|
29
30
|
const left = `${brand} ${status}`;
|
|
30
31
|
const provider = `${theme_1.THEME.dim}${info.provider}${theme_1.THEME.reset}`;
|
|
31
|
-
const model = `${theme_1.THEME.
|
|
32
|
+
const model = `${theme_1.THEME.faint}${info.model}${theme_1.THEME.reset}`;
|
|
32
33
|
const right = `${provider} · ${model}`;
|
|
33
34
|
const fit = fitTwo(left, right, width);
|
|
34
35
|
const mid = Math.max(1, width - (0, terminal_1.stripAnsi)(fit.left).length - (0, terminal_1.stripAnsi)(fit.right).length - 1);
|
|
@@ -36,22 +37,29 @@ function renderHeader(width, info) {
|
|
|
36
37
|
}
|
|
37
38
|
/** Render the project line: Project: <path> ... Mode: ask */
|
|
38
39
|
function renderProjectBar(width, info) {
|
|
39
|
-
const label = `${theme_1.THEME.dim}Project:${theme_1.THEME.reset} ${theme_1.THEME.
|
|
40
|
-
const modeColor = info.mode === 'yolo' ? theme_1.THEME.warn : theme_1.THEME.
|
|
40
|
+
const label = `${theme_1.THEME.dim}Project:${theme_1.THEME.reset} ${theme_1.THEME.muted}${info.project}${theme_1.THEME.reset}`;
|
|
41
|
+
const modeColor = info.mode === 'yolo' ? theme_1.THEME.warn : theme_1.THEME.faint;
|
|
41
42
|
const mode = `${theme_1.THEME.dim}Mode:${theme_1.THEME.reset} ${modeColor}${info.mode}${theme_1.THEME.reset}`;
|
|
42
43
|
const fit = fitTwo(label, mode, width);
|
|
43
44
|
const mid = Math.max(1, width - (0, terminal_1.stripAnsi)(fit.left).length - (0, terminal_1.stripAnsi)(fit.right).length - 1);
|
|
44
45
|
return theme_1.THEME.bgBar + (0, terminal_1.pad)(`${fit.left}${' '.repeat(mid)}${fit.right}`, width) + theme_1.THEME.reset;
|
|
45
46
|
}
|
|
46
|
-
/** Render the bottom
|
|
47
|
+
/** Render the bottom status bar: live status + spinner + elapsed timer. */
|
|
47
48
|
function renderStatusBar(width, info) {
|
|
48
|
-
const
|
|
49
|
-
let
|
|
49
|
+
const statusColorCode = (0, theme_1.statusColor)(info.status);
|
|
50
|
+
let left;
|
|
50
51
|
if (info.running && info.spinner) {
|
|
51
|
-
|
|
52
|
+
left = `${statusColorCode}${info.spinner}${theme_1.THEME.reset} ${statusColorCode}${info.status}${theme_1.THEME.reset}`;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
left = `${statusColorCode}${info.status}${theme_1.THEME.reset}`;
|
|
56
|
+
}
|
|
57
|
+
let right = '';
|
|
58
|
+
if (info.running && info.elapsed !== undefined) {
|
|
59
|
+
right += `${theme_1.THEME.dim}${info.elapsed}s${theme_1.THEME.reset} `;
|
|
52
60
|
}
|
|
53
61
|
const conn = info.connected ? `${theme_1.THEME.success}●${theme_1.THEME.reset}` : `${theme_1.THEME.error}○${theme_1.THEME.reset}`;
|
|
54
|
-
const mode = info.mode === 'yolo' ? `${theme_1.THEME.warn}YOLO${theme_1.THEME.reset}` : `${theme_1.THEME.
|
|
62
|
+
const mode = info.mode === 'yolo' ? `${theme_1.THEME.warn}YOLO${theme_1.THEME.reset}` : `${theme_1.THEME.faint}ask${theme_1.THEME.reset}`;
|
|
55
63
|
right += `${conn} ${mode}`;
|
|
56
64
|
const fit = fitTwo(left, right, width);
|
|
57
65
|
const mid = Math.max(1, width - (0, terminal_1.stripAnsi)(fit.left).length - (0, terminal_1.stripAnsi)(fit.right).length - 1);
|
package/dist/tui/theme.js
CHANGED
|
@@ -3,25 +3,29 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Spinner = exports.SPINNERS = exports.THEME = void 0;
|
|
4
4
|
exports.statusColor = statusColor;
|
|
5
5
|
/**
|
|
6
|
-
* VectorHead theme —
|
|
7
|
-
*
|
|
6
|
+
* VectorHead theme — Freebuff-style palette (adopted from CodebuffAI/freebuff dark theme).
|
|
7
|
+
* Primary brand green #9EFC62, slate neutrals, gray AI line, green user line.
|
|
8
8
|
*/
|
|
9
9
|
exports.THEME = {
|
|
10
|
-
accent: '\x1b[38;5;
|
|
11
|
-
accentDim: '\x1b[38;5;
|
|
12
|
-
gold: '\x1b[38;5;220m',
|
|
13
|
-
text: '\x1b[38;5;
|
|
10
|
+
accent: '\x1b[38;5;156m', // Freebuff primary green (#9EFC62 ≈ 156)
|
|
11
|
+
accentDim: '\x1b[38;5;120m',
|
|
12
|
+
gold: '\x1b[38;5;220m', // markdown heading yellow (#facc15 ≈ 220)
|
|
13
|
+
text: '\x1b[38;5;255m', // foreground #f1f5f9 ≈ 255
|
|
14
14
|
textBright: '\x1b[38;5;255m',
|
|
15
|
-
muted: '\x1b[38;5;
|
|
16
|
-
faint: '\x1b[38;5;
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
muted: '\x1b[38;5;250m', // #acb3bf ≈ 250
|
|
16
|
+
faint: '\x1b[38;5;244m',
|
|
17
|
+
directory: '\x1b[38;5;249m', // #9CA3AF ≈ 249
|
|
18
|
+
border: '\x1b[38;5;60m', // slate #536175 ≈ 60
|
|
19
|
+
borderBright: '\x1b[38;5;156m',
|
|
19
20
|
success: '\x1b[38;5;114m',
|
|
20
|
-
warn: '\x1b[38;5;215m',
|
|
21
|
+
warn: '\x1b[38;5;215m', // #FFA500 ≈ 215
|
|
21
22
|
error: '\x1b[38;5;203m',
|
|
22
|
-
info: '\x1b[38;5;
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
info: '\x1b[38;5;156m',
|
|
24
|
+
// Message indicator lines (Freebuff): gray for AI, green for user
|
|
25
|
+
aiLine: '\x1b[38;5;244m',
|
|
26
|
+
userLine: '\x1b[38;5;156m',
|
|
27
|
+
bgBar: '\x1b[48;5;236m',
|
|
28
|
+
bgPanel: '\x1b[48;5;237m',
|
|
25
29
|
bold: '\x1b[1m',
|
|
26
30
|
dim: '\x1b[2m',
|
|
27
31
|
reset: '\x1b[0m',
|
|
@@ -39,8 +43,8 @@ function statusColor(status) {
|
|
|
39
43
|
return exports.THEME.success;
|
|
40
44
|
if (s.includes('stopped') || s.includes('cancel'))
|
|
41
45
|
return exports.THEME.muted;
|
|
42
|
-
if (s.includes('thinking') || s.includes('plan'))
|
|
43
|
-
return exports.THEME.
|
|
46
|
+
if (s.includes('thinking') || s.includes('plan') || s.includes('work'))
|
|
47
|
+
return exports.THEME.accent;
|
|
44
48
|
if (s.includes('run') || s.includes('exec'))
|
|
45
49
|
return exports.THEME.accent;
|
|
46
50
|
return exports.THEME.info;
|
package/dist/utils/terminal.js
CHANGED
|
@@ -135,7 +135,25 @@ function wrapText(text, width) {
|
|
|
135
135
|
const unit = String.fromCodePoint(cp);
|
|
136
136
|
const w = cp > 0x2fff ? 2 : 1;
|
|
137
137
|
if (visibleLen + w > width && visibleLen > 0) {
|
|
138
|
-
|
|
138
|
+
// Word-aware wrap (Freebuff uses wrapMode: 'word'): break at the last
|
|
139
|
+
// space so words are not split mid-word. ANSI codes never contain a
|
|
140
|
+
// space, so lastIndexOf(' ') on the raw buffer is safe.
|
|
141
|
+
const lastSpace = current.lastIndexOf(' ');
|
|
142
|
+
if (lastSpace > 0) {
|
|
143
|
+
const carried = current.slice(lastSpace + 1);
|
|
144
|
+
const carriedW = visibleWidth(carried);
|
|
145
|
+
if (carriedW > 0 && carriedW + w <= width) {
|
|
146
|
+
lines.push(current.slice(0, lastSpace));
|
|
147
|
+
current = carried;
|
|
148
|
+
visibleLen = carriedW;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
flushLine();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
flushLine();
|
|
156
|
+
}
|
|
139
157
|
}
|
|
140
158
|
current += unit;
|
|
141
159
|
visibleLen += w;
|
package/package.json
CHANGED