@pheem49/mint 1.2.3 → 1.3.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/README.md +28 -0
- package/docs/assets/Agent_Mint.png +0 -0
- package/docs/assets/Settings.png +0 -0
- package/docs/assets/icon.png +0 -0
- package/docs/index.html +53 -5
- package/docs/style.css +295 -6
- package/index.html +16 -0
- package/main.js +36 -83
- package/mint-cli-logic.js +19 -0
- package/mint-cli.js +121 -17
- package/package.json +8 -2
- package/src/AI_Brain/Gemini_API.js +175 -9
- package/src/AI_Brain/knowledge_base.js +199 -125
- package/src/Automation_Layer/file_operations.js +74 -10
- package/src/CLI/chat_router.js +166 -0
- package/src/CLI/chat_ui.js +239 -110
- package/src/CLI/code_agent.js +443 -0
- package/src/CLI/code_session_memory.js +62 -0
- package/src/CLI/list_features.js +1 -0
- package/src/Plugins/mcp_manager.js +95 -0
- package/src/Plugins/plugin_manager.js +2 -2
- package/src/System/config_manager.js +27 -7
- package/src/System/granular_automation.js +88 -0
- package/src/UI/settings.html +24 -0
- package/src/UI/settings.js +98 -1
- package/docs/assets/hero-bg.png +0 -0
- package/docs/assets/logo.png +0 -0
package/src/CLI/chat_ui.js
CHANGED
|
@@ -6,9 +6,9 @@ const blessed = require('blessed');
|
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const { execSync } = require('child_process');
|
|
8
8
|
const { readConfig } = require('../System/config_manager');
|
|
9
|
-
const fs = require('fs');
|
|
10
9
|
|
|
11
10
|
const SLASH_COMMANDS = [
|
|
11
|
+
{ name: '/code', desc: 'Force workspace code mode for a task' },
|
|
12
12
|
{ name: '/models', desc: 'List or switch Gemini models' },
|
|
13
13
|
{ name: '/config', desc: 'Show current configuration' },
|
|
14
14
|
{ name: '/copy', desc: 'Copy last response to clipboard' },
|
|
@@ -29,6 +29,9 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
29
29
|
const config = readConfig();
|
|
30
30
|
const modelName = config.geminiModel || 'gemini';
|
|
31
31
|
const workspaceName = path.basename(process.cwd());
|
|
32
|
+
const HINT_DEFAULT = `{gray-fg} Enter send · Ctrl+Y copy · /help commands{/}`;
|
|
33
|
+
const INPUT_FG = '#f8fafc';
|
|
34
|
+
const INPUT_BG = '#10141c';
|
|
32
35
|
|
|
33
36
|
// ─── Screen ───────────────────────────────────────────────────────────────
|
|
34
37
|
const screen = blessed.screen({
|
|
@@ -40,82 +43,80 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
40
43
|
|
|
41
44
|
// ─── Banner ───────────────────────────────────────────────────────────────
|
|
42
45
|
const banner = blessed.box({
|
|
43
|
-
top: 0, left:
|
|
46
|
+
top: 0, left: 1, width: '100%-2', height: 4,
|
|
44
47
|
tags: true,
|
|
45
|
-
|
|
48
|
+
padding: { left: 1, right: 1 },
|
|
49
|
+
style: { bg: 'default', fg: '#d7dde8' }
|
|
46
50
|
});
|
|
47
51
|
banner.setContent([
|
|
48
|
-
`{
|
|
49
|
-
`{
|
|
50
|
-
`{
|
|
51
|
-
`{
|
|
52
|
-
`{bold}{#88e0b0-fg} | | | | | | | | |_ | |____| |____ _| |_ {/}`,
|
|
53
|
-
`{bold}{#88e0b0-fg} |_| |_|_|_| |_|\\__| \\_____|______|_____|{/}`,
|
|
54
|
-
``,
|
|
55
|
-
`{bold} Welcome to Mint Interactive AI!{/} {gray-fg}Type '/help' for commands · 'exit' or Esc to quit{/}`
|
|
52
|
+
`{#88e0b0-fg} __ __ _ _ ___ _ ___ {/}`,
|
|
53
|
+
`{#88e0b0-fg}| \\/ (_)_ __ | |_ / __| | |_ _|{/}`,
|
|
54
|
+
`{#88e0b0-fg}| |\\/| | | '_ \\| _| (__| |__ | | {/}`,
|
|
55
|
+
`{#88e0b0-fg}|_| |_|_|_| |_|\\__|\\___|____|___|{/}`
|
|
56
56
|
].join('\n'));
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
style: { fg: '#
|
|
58
|
+
const subBanner = blessed.box({
|
|
59
|
+
top: 4, left: 2, width: '100%-4', height: 2,
|
|
60
|
+
tags: true,
|
|
61
|
+
content: `{gray-fg}Type naturally to chat. Coding requests can auto-enter {/}{#ffd166-fg}Code Mode{/}{gray-fg}. Use {/}{#88e0b0-fg}/help{/}{gray-fg}, {/}{#88e0b0-fg}/code{/}{gray-fg}, or {/}{#88e0b0-fg}Esc{/}{gray-fg}.{/}`,
|
|
62
|
+
style: { bg: 'default', fg: '#9aa6bf' }
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
// ─── Chat log (scrollable) ────────────────────────────────────────────────
|
|
66
66
|
const chatBox = blessed.log({
|
|
67
|
-
top:
|
|
68
|
-
bottom: 8,
|
|
67
|
+
top: 6, left: 1, width: '100%-2',
|
|
68
|
+
bottom: 8,
|
|
69
69
|
tags: true,
|
|
70
70
|
scrollable: true,
|
|
71
71
|
alwaysScroll: true,
|
|
72
|
-
scrollbar: { ch: '
|
|
73
|
-
style: { bg: '
|
|
72
|
+
scrollbar: { ch: '┃', style: { fg: '#335d52' } },
|
|
73
|
+
style: { bg: '#171b24', fg: '#ffffff', border: { fg: '#2f3747' } },
|
|
74
74
|
mouse: true,
|
|
75
|
-
scrollable: true
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const divider2 = blessed.line({
|
|
80
|
-
bottom: 7, left: 0, width: '100%',
|
|
81
|
-
orientation: 'horizontal',
|
|
82
|
-
style: { fg: '#333333' }
|
|
75
|
+
scrollable: true,
|
|
76
|
+
border: { type: 'line' },
|
|
77
|
+
padding: { left: 1, right: 1, top: 0, bottom: 0 },
|
|
78
|
+
label: ' Conversation '
|
|
83
79
|
});
|
|
84
80
|
|
|
85
81
|
// ─── Hint bar ─────────────────────────────────────────────────────────────
|
|
86
82
|
const hintBar = blessed.box({
|
|
87
|
-
bottom: 6, left:
|
|
83
|
+
bottom: 6, left: 1, width: '100%-2', height: 1,
|
|
88
84
|
tags: true,
|
|
89
|
-
content:
|
|
85
|
+
content: HINT_DEFAULT,
|
|
90
86
|
style: { bg: 'default' }
|
|
91
87
|
});
|
|
92
88
|
|
|
93
89
|
// ─── Input area ───────────────────────────────────────────────────────────
|
|
94
|
-
const inputBox = blessed.
|
|
95
|
-
bottom: 3, left:
|
|
90
|
+
const inputBox = blessed.textbox({
|
|
91
|
+
bottom: 3, left: 1, width: '100%-2', height: 3,
|
|
96
92
|
tags: false,
|
|
97
93
|
inputOnFocus: true,
|
|
98
94
|
keys: true,
|
|
99
95
|
style: {
|
|
100
|
-
bg:
|
|
101
|
-
fg:
|
|
102
|
-
border: { fg: '#
|
|
103
|
-
focus: {
|
|
96
|
+
bg: INPUT_BG,
|
|
97
|
+
fg: INPUT_FG,
|
|
98
|
+
border: { fg: '#335d52' },
|
|
99
|
+
focus: {
|
|
100
|
+
fg: INPUT_FG,
|
|
101
|
+
bg: INPUT_BG,
|
|
102
|
+
border: { fg: '#88e0b0' }
|
|
103
|
+
}
|
|
104
104
|
},
|
|
105
105
|
border: { type: 'line' },
|
|
106
|
-
padding: { left: 1 }
|
|
106
|
+
padding: { left: 1 },
|
|
107
|
+
label: ' Message '
|
|
107
108
|
});
|
|
108
109
|
|
|
109
110
|
// ─── Placeholder (SIBLING widget floating over input content area) ─────────
|
|
110
111
|
// inputBox: bottom=3, height=3, border=1 → content row at bottom=4, left=2
|
|
111
112
|
const placeholderWidget = blessed.text({
|
|
112
113
|
bottom: 4, // inside input content area (border offset)
|
|
113
|
-
left:
|
|
114
|
-
width: '100%-
|
|
114
|
+
left: 3,
|
|
115
|
+
width: '100%-6',
|
|
115
116
|
height: 1,
|
|
116
|
-
content: '>
|
|
117
|
+
content: '> Ask anything, or describe a coding task for this workspace',
|
|
117
118
|
tags: false,
|
|
118
|
-
style: { fg: '#
|
|
119
|
+
style: { fg: '#5d6678', bg: '#10141c' }
|
|
119
120
|
});
|
|
120
121
|
|
|
121
122
|
let placeholderVisible = true;
|
|
@@ -136,12 +137,40 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
139
|
|
|
140
|
+
function refreshInputStyles() {
|
|
141
|
+
inputBox.style.fg = INPUT_FG;
|
|
142
|
+
inputBox.style.bg = INPUT_BG;
|
|
143
|
+
if (inputBox.style.focus) {
|
|
144
|
+
inputBox.style.focus.fg = INPUT_FG;
|
|
145
|
+
inputBox.style.focus.bg = INPUT_BG;
|
|
146
|
+
}
|
|
147
|
+
if (Array.isArray(inputBox.children)) {
|
|
148
|
+
inputBox.children.forEach((child) => {
|
|
149
|
+
if (child.style) {
|
|
150
|
+
child.style.fg = INPUT_FG;
|
|
151
|
+
child.style.bg = INPUT_BG;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
applyTerminalInputAttrs();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function applyTerminalInputAttrs() {
|
|
159
|
+
try {
|
|
160
|
+
if (!screen || !screen.program || typeof inputBox.sattr !== 'function' || typeof screen.codeAttr !== 'function') {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const attr = inputBox.sattr(inputBox.style);
|
|
164
|
+
screen.program.write(screen.codeAttr(attr));
|
|
165
|
+
} catch (_) {}
|
|
166
|
+
}
|
|
167
|
+
|
|
139
168
|
// ─── Status bar (3 columns: left / center / right) ──────────────────────
|
|
140
169
|
const statusBar = blessed.box({
|
|
141
|
-
bottom: 0, left:
|
|
170
|
+
bottom: 0, left: 1, width: '100%-2', height: 3,
|
|
142
171
|
tags: true,
|
|
143
|
-
style: { bg: '#
|
|
144
|
-
border: { type: 'line', fg: '#
|
|
172
|
+
style: { bg: '#10141c', fg: '#888888' },
|
|
173
|
+
border: { type: 'line', fg: '#222c38' }
|
|
145
174
|
});
|
|
146
175
|
|
|
147
176
|
// Left: workspace info
|
|
@@ -152,20 +181,20 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
152
181
|
height: 1,
|
|
153
182
|
tags: true,
|
|
154
183
|
content: ` workspace {bold}(${workspaceName}){/bold}`,
|
|
155
|
-
style: { bg: '#
|
|
184
|
+
style: { bg: '#10141c', fg: '#93a0b7' }
|
|
156
185
|
});
|
|
157
186
|
|
|
158
|
-
// Center:
|
|
187
|
+
// Center: mode + status
|
|
159
188
|
const statusCenter = blessed.text({
|
|
160
189
|
parent: statusBar,
|
|
161
190
|
top: 0,
|
|
162
191
|
left: 'center',
|
|
163
|
-
width: '
|
|
192
|
+
width: '44%',
|
|
164
193
|
height: 1,
|
|
165
194
|
align: 'center',
|
|
166
195
|
tags: true,
|
|
167
|
-
content: `{#cc4444-fg}no sandbox{/}`,
|
|
168
|
-
style: { bg: '#
|
|
196
|
+
content: `{#88aaff-fg}[Chat]{/} {#cc4444-fg}no sandbox{/}`,
|
|
197
|
+
style: { bg: '#10141c', fg: '#888888' }
|
|
169
198
|
});
|
|
170
199
|
|
|
171
200
|
// Right: current model
|
|
@@ -177,18 +206,30 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
177
206
|
align: 'right',
|
|
178
207
|
tags: true,
|
|
179
208
|
content: `{#88e0b0-fg}${modelName}{/}`,
|
|
180
|
-
style: { bg: '#
|
|
209
|
+
style: { bg: '#10141c', fg: '#88e0b0' }
|
|
181
210
|
});
|
|
182
211
|
|
|
212
|
+
let activeMode = 'Chat';
|
|
213
|
+
|
|
214
|
+
function formatModeTag(mode) {
|
|
215
|
+
if (mode === 'Code') return `{#ffd166-fg}[Code]{/}`;
|
|
216
|
+
return `{#88aaff-fg}[Chat]{/}`;
|
|
217
|
+
}
|
|
218
|
+
|
|
183
219
|
function updateStatusBar(thinkingText = null) {
|
|
184
220
|
if (thinkingText) {
|
|
185
|
-
statusCenter.setContent(
|
|
221
|
+
statusCenter.setContent(`${formatModeTag(activeMode)} {#88e0b0-fg}${thinkingText}{/}`);
|
|
186
222
|
} else {
|
|
187
|
-
statusCenter.setContent(
|
|
223
|
+
statusCenter.setContent(`${formatModeTag(activeMode)} {#cc4444-fg}no sandbox{/}`);
|
|
188
224
|
}
|
|
189
225
|
screen.render();
|
|
190
226
|
}
|
|
191
227
|
|
|
228
|
+
function setMode(mode) {
|
|
229
|
+
activeMode = mode === 'Code' ? 'Code' : 'Chat';
|
|
230
|
+
updateStatusBar(null);
|
|
231
|
+
}
|
|
232
|
+
|
|
192
233
|
/** Update model name in status bar (called after /models switch) */
|
|
193
234
|
function updateStatusModel(newModel) {
|
|
194
235
|
statusRight.setContent(`{#88e0b0-fg}${newModel}{/}`);
|
|
@@ -198,9 +239,8 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
198
239
|
|
|
199
240
|
// ─── Append widgets to screen ─────────────────────────────────────────────
|
|
200
241
|
screen.append(banner);
|
|
201
|
-
screen.append(
|
|
242
|
+
screen.append(subBanner);
|
|
202
243
|
screen.append(chatBox);
|
|
203
|
-
screen.append(divider2);
|
|
204
244
|
screen.append(hintBar);
|
|
205
245
|
screen.append(inputBox);
|
|
206
246
|
screen.append(statusBar);
|
|
@@ -209,9 +249,9 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
209
249
|
// ─── Suggestion List ──────────────────────────────────────────────────────
|
|
210
250
|
const commandList = blessed.list({
|
|
211
251
|
parent: screen,
|
|
212
|
-
bottom: 6,
|
|
213
|
-
left:
|
|
214
|
-
width: '
|
|
252
|
+
bottom: 6,
|
|
253
|
+
left: 3,
|
|
254
|
+
width: '64%',
|
|
215
255
|
height: 8,
|
|
216
256
|
tags: true,
|
|
217
257
|
keys: false, // We will handle keys manually to keep focus on input
|
|
@@ -219,10 +259,10 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
219
259
|
hidden: true,
|
|
220
260
|
border: { type: 'line', fg: '#88e0b0' },
|
|
221
261
|
style: {
|
|
222
|
-
bg: '#
|
|
262
|
+
bg: '#10141c',
|
|
223
263
|
fg: '#ffffff',
|
|
224
264
|
selected: {
|
|
225
|
-
bg: '#
|
|
265
|
+
bg: '#22352f',
|
|
226
266
|
fg: '#88e0b0',
|
|
227
267
|
bold: true
|
|
228
268
|
}
|
|
@@ -230,6 +270,22 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
230
270
|
});
|
|
231
271
|
|
|
232
272
|
let activeSuggestions = [];
|
|
273
|
+
const approvalDialog = blessed.question({
|
|
274
|
+
parent: screen,
|
|
275
|
+
tags: true,
|
|
276
|
+
border: { type: 'line', fg: '#88e0b0' },
|
|
277
|
+
style: {
|
|
278
|
+
bg: '#10141c',
|
|
279
|
+
fg: '#ffffff',
|
|
280
|
+
border: { fg: '#88e0b0' }
|
|
281
|
+
},
|
|
282
|
+
width: '80%',
|
|
283
|
+
height: 'shrink',
|
|
284
|
+
top: 'center',
|
|
285
|
+
left: 'center',
|
|
286
|
+
label: ' Approval ',
|
|
287
|
+
hidden: true
|
|
288
|
+
});
|
|
233
289
|
|
|
234
290
|
function updateSuggestions(filter = '') {
|
|
235
291
|
activeSuggestions = SLASH_COMMANDS.filter(cmd =>
|
|
@@ -260,6 +316,7 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
260
316
|
|
|
261
317
|
// Consolidated key handling
|
|
262
318
|
inputBox.on('element keypress', (el, ch, key) => {
|
|
319
|
+
refreshInputStyles();
|
|
263
320
|
// 1. Handle placeholder visibility
|
|
264
321
|
if (!key.ctrl && !key.meta && key.name !== 'enter' && key.name !== 'tab') {
|
|
265
322
|
if (ch) hidePlaceholder();
|
|
@@ -287,6 +344,7 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
287
344
|
|
|
288
345
|
// 3. Logic for suggestions and placeholder after key is processed
|
|
289
346
|
setImmediate(() => {
|
|
347
|
+
refreshInputStyles();
|
|
290
348
|
const val = (inputBox.getValue ? inputBox.getValue() : inputBox.value) || '';
|
|
291
349
|
const isCommand = val.startsWith('/') && !val.includes(' ');
|
|
292
350
|
|
|
@@ -308,6 +366,15 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
308
366
|
});
|
|
309
367
|
});
|
|
310
368
|
|
|
369
|
+
inputBox.on('focus', () => {
|
|
370
|
+
refreshInputStyles();
|
|
371
|
+
screen.render();
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
inputBox.on('keypress', () => {
|
|
375
|
+
applyTerminalInputAttrs();
|
|
376
|
+
});
|
|
377
|
+
|
|
311
378
|
|
|
312
379
|
// Submit or Select Suggestion on Enter
|
|
313
380
|
inputBox.key(['enter'], () => {
|
|
@@ -318,6 +385,7 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
318
385
|
commandList.hide();
|
|
319
386
|
hidePlaceholder();
|
|
320
387
|
inputBox.focus();
|
|
388
|
+
refreshInputStyles();
|
|
321
389
|
screen.render();
|
|
322
390
|
return; // Don't submit yet, let user add args or press enter again
|
|
323
391
|
}
|
|
@@ -325,12 +393,20 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
325
393
|
|
|
326
394
|
const raw = (inputBox.getValue ? inputBox.getValue() : inputBox.value) || '';
|
|
327
395
|
const text = raw.trim();
|
|
328
|
-
if (!text)
|
|
396
|
+
if (!text) {
|
|
397
|
+
inputBox.clearValue();
|
|
398
|
+
showPlaceholder();
|
|
399
|
+
inputBox.focus();
|
|
400
|
+
refreshInputStyles();
|
|
401
|
+
screen.render();
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
329
404
|
|
|
330
405
|
// Clear input and restore placeholder
|
|
331
406
|
inputBox.clearValue();
|
|
332
407
|
showPlaceholder();
|
|
333
408
|
inputBox.focus();
|
|
409
|
+
refreshInputStyles();
|
|
334
410
|
screen.render();
|
|
335
411
|
|
|
336
412
|
if (text.toLowerCase() === 'exit' || text.toLowerCase() === 'quit') {
|
|
@@ -342,18 +418,9 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
342
418
|
});
|
|
343
419
|
|
|
344
420
|
// Shift+Enter = newline in input
|
|
345
|
-
inputBox.key(['S-enter'], () => {
|
|
346
|
-
hidePlaceholder();
|
|
347
|
-
const val = (inputBox.getValue ? inputBox.getValue() : inputBox.value) || '';
|
|
348
|
-
inputBox.setValue(val + '\n');
|
|
349
|
-
screen.render();
|
|
350
|
-
});
|
|
351
|
-
|
|
352
421
|
// Ctrl+C — double-press to exit
|
|
353
422
|
let ctrlCPressed = false;
|
|
354
423
|
let ctrlCTimer = null;
|
|
355
|
-
const HINT_DEFAULT = `{gray-fg} Shift+Drag to select text · Ctrl+Y to copy · /help for commands{/}`;
|
|
356
|
-
|
|
357
424
|
screen.key(['C-c'], () => {
|
|
358
425
|
if (ctrlCPressed) {
|
|
359
426
|
clearTimeout(ctrlCTimer);
|
|
@@ -415,6 +482,7 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
415
482
|
|
|
416
483
|
// ─── Initial render ───────────────────────────────────────────────────────
|
|
417
484
|
inputBox.focus();
|
|
485
|
+
refreshInputStyles();
|
|
418
486
|
screen.render();
|
|
419
487
|
|
|
420
488
|
// ─── Public API ───────────────────────────────────────────────────────────
|
|
@@ -427,59 +495,95 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
427
495
|
* @param {string} text
|
|
428
496
|
* @param {string} timestamp - ISO string or Date object
|
|
429
497
|
*/
|
|
430
|
-
function
|
|
431
|
-
|
|
432
|
-
|
|
498
|
+
function wrapLineSmart(line, width) {
|
|
499
|
+
if (line.length <= width) return [line];
|
|
500
|
+
if (!line.includes(' ')) {
|
|
501
|
+
const pieces = [];
|
|
502
|
+
for (let index = 0; index < line.length; index += width) {
|
|
503
|
+
pieces.push(line.slice(index, index + width));
|
|
504
|
+
}
|
|
505
|
+
return pieces;
|
|
506
|
+
}
|
|
433
507
|
|
|
434
|
-
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
continue;
|
|
508
|
+
const words = line.split(/\s+/);
|
|
509
|
+
const lines = [];
|
|
510
|
+
let current = '';
|
|
511
|
+
for (const word of words) {
|
|
512
|
+
if (word.length > width) {
|
|
513
|
+
if (current) {
|
|
514
|
+
lines.push(current);
|
|
515
|
+
current = '';
|
|
443
516
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
if (current.length >= width) {
|
|
451
|
-
lines.push(current);
|
|
452
|
-
current = '';
|
|
517
|
+
for (let index = 0; index < word.length; index += width) {
|
|
518
|
+
const slice = word.slice(index, index + width);
|
|
519
|
+
if (slice.length === width) {
|
|
520
|
+
lines.push(slice);
|
|
521
|
+
} else {
|
|
522
|
+
current = slice;
|
|
453
523
|
}
|
|
454
524
|
}
|
|
455
|
-
|
|
525
|
+
continue;
|
|
456
526
|
}
|
|
457
|
-
return lines;
|
|
458
|
-
};
|
|
459
527
|
|
|
460
|
-
|
|
528
|
+
if (!current) {
|
|
529
|
+
current = word;
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (`${current} ${word}`.length <= width) {
|
|
534
|
+
current += ` ${word}`;
|
|
535
|
+
} else {
|
|
536
|
+
lines.push(current);
|
|
537
|
+
current = word;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (current) lines.push(current);
|
|
541
|
+
return lines;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function wrapText(str, width) {
|
|
545
|
+
const lines = [];
|
|
546
|
+
const originalLines = String(str).split('\n');
|
|
547
|
+
for (const line of originalLines) {
|
|
548
|
+
if (line.length === 0) {
|
|
549
|
+
lines.push('');
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
lines.push(...wrapLineSmart(line, width));
|
|
553
|
+
}
|
|
554
|
+
return lines;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function appendMessage(role, text, timestamp = null) {
|
|
558
|
+
const now = timestamp ? new Date(timestamp) : new Date();
|
|
559
|
+
const timeStr = now.toLocaleTimeString('th-TH', { hour: '2-digit', minute: '2-digit', hour12: false });
|
|
560
|
+
const maxLineWidth = Math.max(screen.width - 20, 36);
|
|
561
|
+
const lines = wrapText(text, maxLineWidth);
|
|
461
562
|
|
|
462
563
|
if (role === 'user') {
|
|
463
|
-
chatBox.log(
|
|
464
|
-
|
|
465
|
-
lines.forEach(l => chatBox.log(`
|
|
466
|
-
chatBox.log(` {gray-fg}${timeStr}{/}`);
|
|
564
|
+
chatBox.log(``);
|
|
565
|
+
chatBox.log(` {bold}{#88e0b0-fg}You{/} {gray-fg}${timeStr}{/}`);
|
|
566
|
+
lines.forEach(l => chatBox.log(` {#88e0b0-fg}▏{/} {#ffffff-fg}${l}{/}`));
|
|
467
567
|
} else if (role === 'assistant') {
|
|
468
568
|
lastAssistantResponse = text;
|
|
469
|
-
chatBox.log(
|
|
470
|
-
|
|
471
|
-
lines.forEach(l => chatBox.log(`
|
|
472
|
-
chatBox.log(` {#444444-fg}┕${'─'.repeat(4)}{/} {gray-fg}${timeStr}{/}`);
|
|
569
|
+
chatBox.log(``);
|
|
570
|
+
chatBox.log(` {bold}{#d4a8ff-fg}Mint{/} {gray-fg}${timeStr}{/}`);
|
|
571
|
+
lines.forEach(l => chatBox.log(` {#5a456d-fg}▏{/} {#ffffff-fg}${l}{/}`));
|
|
473
572
|
} else if (role === 'system') {
|
|
474
|
-
const displayTag = text.startsWith('Action:')
|
|
573
|
+
const displayTag = text.startsWith('Action:')
|
|
574
|
+
? '{#88e0b0-fg}Action{/}'
|
|
575
|
+
: text.startsWith('[Code]')
|
|
576
|
+
? '{#ffd166-fg}Code{/}'
|
|
577
|
+
: '{#8ba0ff-fg}System{/}';
|
|
475
578
|
const cleanText = text.replace(/^(Action:|System:)\s*/, '');
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
579
|
+
const systemLines = wrapText(cleanText, maxLineWidth - 4);
|
|
580
|
+
chatBox.log(``);
|
|
581
|
+
chatBox.log(` {bold}${displayTag}{/}`);
|
|
582
|
+
systemLines.forEach(l => chatBox.log(` {#95a2b8-fg}${l}{/}`));
|
|
479
583
|
} else if (role === 'error') {
|
|
480
|
-
chatBox.log(
|
|
481
|
-
|
|
482
|
-
lines.forEach(l => chatBox.log(`
|
|
584
|
+
chatBox.log(``);
|
|
585
|
+
chatBox.log(` {bold}{#ff6b6b-fg}Error{/} {gray-fg}${timeStr}{/}`);
|
|
586
|
+
lines.forEach(l => chatBox.log(` {#7a2e2e-fg}▏{/} {#ff7d7d-fg}${l}{/}`));
|
|
483
587
|
}
|
|
484
588
|
screen.render();
|
|
485
589
|
}
|
|
@@ -499,7 +603,32 @@ function createChatUI({ onSubmit, onExit }) {
|
|
|
499
603
|
return copyToClipboard(lastAssistantResponse);
|
|
500
604
|
}
|
|
501
605
|
|
|
502
|
-
|
|
606
|
+
function requestApproval(request) {
|
|
607
|
+
return new Promise((resolve) => {
|
|
608
|
+
const typeLabel = request.type === 'shell'
|
|
609
|
+
? 'Shell Command'
|
|
610
|
+
: request.type === 'patch'
|
|
611
|
+
? 'Patch Edit'
|
|
612
|
+
: 'File Write';
|
|
613
|
+
const preview = request.preview || request.label || '';
|
|
614
|
+
const message = [
|
|
615
|
+
`{bold}${typeLabel}{/bold}`,
|
|
616
|
+
'',
|
|
617
|
+
preview,
|
|
618
|
+
'',
|
|
619
|
+
'Approve this action?'
|
|
620
|
+
].join('\n');
|
|
621
|
+
|
|
622
|
+
approvalDialog.ask(message, (approved) => {
|
|
623
|
+
inputBox.focus();
|
|
624
|
+
refreshInputStyles();
|
|
625
|
+
screen.render();
|
|
626
|
+
resolve(Boolean(approved));
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
return { screen, appendMessage, setThinking, updateStatusModel, copyLastResponse, requestApproval, setMode };
|
|
503
632
|
}
|
|
504
633
|
|
|
505
634
|
module.exports = { createChatUI };
|