@pheem49/mint 1.5.0 → 1.5.1
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 +27 -1
- package/main.js +28 -14
- package/mint-cli-logic.js +3 -119
- package/mint-cli.js +497 -23
- package/models/Shiroko_Model/Shiroko/Shiroko_Core/72d86db84cfa9730b894c241fd24c0db.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core/items_pinned_to_model.json +14 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253/347/234/274/347/217/240/346/221/207/346/231/203.exp3.json +15 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/233/264/350/243/231.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/215/347/205/247.exp3.json +50 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/277/347/254/224.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/202/271/344/270/200/344/270/213.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/214/253/345/222/252/346/273/244/351/225/234.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/234/274/351/225/234.exp3.json +10 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_00.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_01.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_02.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_03.png +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.cdi3.json +1498 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.moc3 +0 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.model3.json +47 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.physics3.json +6658 -0
- package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.vtube.json +1299 -0
- package/models/Shiroko_Model/Shiroko//342/232/241/351/253/230/344/272/256/342/232/241/344/275/277/347/224/250/346/225/231/347/250/213/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.txt +23 -0
- package/package.json +26 -1
- package/src/AI_Brain/Gemini_API.js +147 -46
- package/src/AI_Brain/autonomous_brain.js +2 -1
- package/src/AI_Brain/memory_store.js +299 -3
- package/src/CLI/chat_router.js +18 -6
- package/src/CLI/chat_ui.js +396 -50
- package/src/CLI/code_agent.js +203 -14
- package/src/CLI/image_input.js +90 -0
- package/src/CLI/onboarding.js +72 -15
- package/src/CLI/updater.js +6 -4
- package/src/System/action_executor.js +59 -10
- package/src/System/config_manager.js +31 -1
- package/src/System/granular_automation.js +122 -53
- package/src/System/proactive_loop.js +19 -3
- package/src/System/safety_manager.js +108 -0
- package/src/System/sandbox_runner.js +182 -0
- package/src/System/system_automation.js +127 -81
- package/src/System/system_info.js +70 -0
- package/src/System/tool_registry.js +280 -0
- package/src/System/window_manager.js +4 -2
- package/src/UI/live2d_manager.js +368 -0
- package/src/UI/renderer.js +176 -18
- package/src/UI/styles.css +452 -31
- package/.codex +0 -0
- package/docs/assets/Agent_Mint.png +0 -0
- package/docs/assets/CLI_Screen.png +0 -0
- package/docs/assets/Settings.png +0 -0
- package/docs/assets/icon.png +0 -0
- package/docs/guide.html +0 -632
- package/docs/index.html +0 -133
- package/docs/style.css +0 -579
- package/index.html +0 -16
- package/src/UI/index.html +0 -126
- package/tech_news.txt +0 -3
- package/test_knowledge.txt +0 -3
- package/tests/action_executor_safety.test.js +0 -67
- package/tests/agent_orchestrator.test.js +0 -41
- package/tests/chat_router.test.js +0 -42
- package/tests/code_agent.test.js +0 -69
- package/tests/config_manager.test.js +0 -141
- package/tests/docker.test.js +0 -46
- package/tests/file_operations.test.js +0 -57
- package/tests/gmail.test.js +0 -135
- package/tests/gmail_auth.test.js +0 -129
- package/tests/google_calendar.test.js +0 -113
- package/tests/google_tts_urls.test.js +0 -24
- package/tests/memory_store.test.js +0 -185
- package/tests/notion.test.js +0 -121
- package/tests/provider_routing.test.js +0 -83
- package/tests/safety_manager.test.js +0 -40
- package/tests/spotify.test.js +0 -201
- package/tests/system_monitor.test.js +0 -37
- package/tests/updater.test.js +0 -32
- package/tests/workspace_manager.test.js +0 -56
package/src/CLI/chat_ui.js
CHANGED
|
@@ -12,9 +12,15 @@ const h = React.createElement;
|
|
|
12
12
|
|
|
13
13
|
const SLASH_COMMANDS = [
|
|
14
14
|
{ cmd: '/help', desc: 'Show available commands' },
|
|
15
|
+
{ cmd: '/image', desc: 'Attach an image from a file path' },
|
|
16
|
+
{ cmd: '/paste', desc: 'Attach an image from the clipboard' },
|
|
17
|
+
{ cmd: '/fast', desc: 'Toggle fast mode (hide thinking)' },
|
|
18
|
+
{ cmd: '/learn', desc: 'Remember a markdown skill file' },
|
|
15
19
|
{ cmd: '/code', desc: 'Force workspace Code Mode' },
|
|
16
20
|
{ cmd: '/cd', desc: 'Change current working directory' },
|
|
17
21
|
{ cmd: '/models', desc: 'List or switch Gemini models' },
|
|
22
|
+
{ cmd: '/memory', desc: 'List, search, clear, or export long-term memory' },
|
|
23
|
+
{ cmd: '/memory skills', desc: 'Show learned skill files' },
|
|
18
24
|
{ cmd: '/config', desc: 'Show current configuration' },
|
|
19
25
|
{ cmd: '/copy', desc: 'Copy last response to clipboard' },
|
|
20
26
|
{ cmd: '/clear', desc: 'Clear conversation history' },
|
|
@@ -35,29 +41,96 @@ async function createChatUI(options) {
|
|
|
35
41
|
const TextInput = (await import('ink-text-input')).default;
|
|
36
42
|
const { useState, useImperativeHandle, forwardRef, createRef, useEffect, useMemo } = React;
|
|
37
43
|
|
|
38
|
-
const App = forwardRef(({ onSubmit, onExit, initialHistory = [] }, ref) => {
|
|
44
|
+
const App = forwardRef(({ onSubmit, onExit, onPasteImage, initialHistory = [] }, ref) => {
|
|
39
45
|
const config = readConfig();
|
|
40
46
|
const { exit } = useApp();
|
|
41
47
|
const [input, setInput] = useState('');
|
|
42
48
|
const [history, setHistory] = useState(initialHistory);
|
|
49
|
+
const [liveAssistant, setLiveAssistant] = useState(null);
|
|
43
50
|
const [thinking, setThinking] = useState(false);
|
|
44
|
-
const [
|
|
51
|
+
const [fastMode, setFastMode] = useState(false);
|
|
52
|
+
const [mode, setMode] = useState('Agent');
|
|
45
53
|
const [model, setModel] = useState('');
|
|
46
54
|
const [workspace, setWorkspace] = useState(process.cwd());
|
|
55
|
+
const [pendingImages, setPendingImages] = useState([]);
|
|
56
|
+
const [pendingImagePrefix, setPendingImagePrefix] = useState('');
|
|
57
|
+
const [pendingPaste, setPendingPaste] = useState(null);
|
|
58
|
+
const [pendingPastePrefix, setPendingPastePrefix] = useState('');
|
|
59
|
+
const [pendingApproval, setPendingApproval] = useState(null);
|
|
60
|
+
const [approvalChoice, setApprovalChoice] = useState('approve');
|
|
47
61
|
|
|
48
62
|
// Suggestions State
|
|
49
63
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
50
64
|
const inputRef = React.useRef(input);
|
|
65
|
+
const pendingImagesRef = React.useRef(pendingImages);
|
|
66
|
+
const pendingImagePrefixRef = React.useRef(pendingImagePrefix);
|
|
67
|
+
const pendingPasteRef = React.useRef(pendingPaste);
|
|
68
|
+
const pendingPastePrefixRef = React.useRef(pendingPastePrefix);
|
|
69
|
+
const liveAssistantRef = React.useRef(liveAssistant);
|
|
70
|
+
const fastModeRef = React.useRef(fastMode);
|
|
71
|
+
const suppressPasteCharRef = React.useRef(false);
|
|
51
72
|
const selectedIndexRef = React.useRef(selectedIndex);
|
|
73
|
+
const pendingApprovalRef = React.useRef(null);
|
|
74
|
+
const approvalChoiceRef = React.useRef('approve');
|
|
75
|
+
|
|
76
|
+
const removePasteArtifact = (value) => {
|
|
77
|
+
const text = String(value || '');
|
|
78
|
+
return text.replace(/[vV]$/, '');
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const normalizeInputText = (value) => {
|
|
82
|
+
return String(value || '').replace(/\s*[\r\n]+\s*/g, ' ');
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const shouldStoreAsPastedContent = (value) => {
|
|
86
|
+
const text = String(value || '');
|
|
87
|
+
return text.length > 500 || /[\r\n]/.test(text);
|
|
88
|
+
};
|
|
52
89
|
|
|
53
90
|
useEffect(() => {
|
|
54
91
|
inputRef.current = input;
|
|
55
92
|
}, [input]);
|
|
56
93
|
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
pendingImagesRef.current = pendingImages;
|
|
96
|
+
}, [pendingImages]);
|
|
97
|
+
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
pendingImagePrefixRef.current = pendingImagePrefix;
|
|
100
|
+
}, [pendingImagePrefix]);
|
|
101
|
+
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
pendingPasteRef.current = pendingPaste;
|
|
104
|
+
}, [pendingPaste]);
|
|
105
|
+
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
pendingPastePrefixRef.current = pendingPastePrefix;
|
|
108
|
+
}, [pendingPastePrefix]);
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
liveAssistantRef.current = liveAssistant;
|
|
112
|
+
}, [liveAssistant]);
|
|
113
|
+
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
fastModeRef.current = fastMode;
|
|
116
|
+
}, [fastMode]);
|
|
117
|
+
|
|
57
118
|
useEffect(() => {
|
|
58
119
|
selectedIndexRef.current = selectedIndex;
|
|
59
120
|
}, [selectedIndex]);
|
|
60
121
|
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
pendingApprovalRef.current = pendingApproval;
|
|
124
|
+
if (pendingApproval) {
|
|
125
|
+
approvalChoiceRef.current = 'approve';
|
|
126
|
+
setApprovalChoice('approve');
|
|
127
|
+
}
|
|
128
|
+
}, [pendingApproval]);
|
|
129
|
+
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
approvalChoiceRef.current = approvalChoice;
|
|
132
|
+
}, [approvalChoice]);
|
|
133
|
+
|
|
61
134
|
const showSuggestions = input.startsWith('/') && !input.includes(' ');
|
|
62
135
|
const suggestions = useMemo(() => {
|
|
63
136
|
if (!showSuggestions) return [];
|
|
@@ -81,11 +154,69 @@ async function createChatUI(options) {
|
|
|
81
154
|
setModel(model ? `${provider} • ${model}` : provider);
|
|
82
155
|
}
|
|
83
156
|
},
|
|
157
|
+
beginAssistantStream: (metadata = {}) => {
|
|
158
|
+
const msg = { role: 'assistant', text: '', time: new Date(), ...metadata };
|
|
159
|
+
liveAssistantRef.current = msg;
|
|
160
|
+
setLiveAssistant(msg);
|
|
161
|
+
if (metadata.providerInfo) {
|
|
162
|
+
const { provider, model } = metadata.providerInfo;
|
|
163
|
+
setModel(model ? `${provider} • ${model}` : provider);
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
appendAssistantStreamChunk: (chunk) => {
|
|
167
|
+
const current = liveAssistantRef.current || { role: 'assistant', text: '', time: new Date() };
|
|
168
|
+
const next = { ...current, text: `${current.text || ''}${chunk}` };
|
|
169
|
+
liveAssistantRef.current = next;
|
|
170
|
+
setLiveAssistant(next);
|
|
171
|
+
},
|
|
172
|
+
finalizeAssistantStream: () => {
|
|
173
|
+
const current = liveAssistantRef.current;
|
|
174
|
+
liveAssistantRef.current = null;
|
|
175
|
+
setLiveAssistant(null);
|
|
176
|
+
if (current && String(current.text || '').trim()) {
|
|
177
|
+
setHistory(prev => [...prev, current]);
|
|
178
|
+
}
|
|
179
|
+
},
|
|
84
180
|
setThinking: (val) => setThinking(val),
|
|
85
181
|
setMode: (val) => setMode(val),
|
|
182
|
+
setFastMode: (val) => {
|
|
183
|
+
const next = Boolean(val);
|
|
184
|
+
fastModeRef.current = next;
|
|
185
|
+
setFastMode(next);
|
|
186
|
+
return next;
|
|
187
|
+
},
|
|
188
|
+
toggleFastMode: () => {
|
|
189
|
+
const next = !fastModeRef.current;
|
|
190
|
+
fastModeRef.current = next;
|
|
191
|
+
setFastMode(next);
|
|
192
|
+
return next;
|
|
193
|
+
},
|
|
194
|
+
getFastMode: () => fastModeRef.current,
|
|
195
|
+
setInputText: (val) => setInput(val || ''),
|
|
196
|
+
setPendingPasteText: (text) => {
|
|
197
|
+
const normalized = normalizeInputText(text);
|
|
198
|
+
setPendingPaste({ text: normalized, label: `[Pasted Content ${normalized.length} chars]` });
|
|
199
|
+
setPendingPastePrefix('');
|
|
200
|
+
setInput('');
|
|
201
|
+
},
|
|
86
202
|
updateStatusModel: (val) => setModel(val),
|
|
87
203
|
updateWorkspace: (val) => setWorkspace(val),
|
|
204
|
+
attachImage: (image) => {
|
|
205
|
+
setPendingImages(prev => {
|
|
206
|
+
if (prev.length === 0) {
|
|
207
|
+
const prefix = normalizeInputText(inputRef.current).trim();
|
|
208
|
+
setPendingImagePrefix(prefix);
|
|
209
|
+
pendingImagePrefixRef.current = prefix;
|
|
210
|
+
setInput('');
|
|
211
|
+
}
|
|
212
|
+
return [...prev, image];
|
|
213
|
+
});
|
|
214
|
+
},
|
|
88
215
|
appendCodeStep: (info) => {
|
|
216
|
+
if (fastModeRef.current) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
89
220
|
let text = '';
|
|
90
221
|
let label = 'System';
|
|
91
222
|
let labelColor = 'blueBright';
|
|
@@ -95,6 +226,9 @@ async function createChatUI(options) {
|
|
|
95
226
|
text = info;
|
|
96
227
|
} else {
|
|
97
228
|
const { action, phase, target, message, thought } = info;
|
|
229
|
+
if (action === 'memory_context' && process.env.MINT_SHOW_MEMORY_TRACE !== '1') {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
98
232
|
if (thought) {
|
|
99
233
|
text = thought;
|
|
100
234
|
label = 'Thinking';
|
|
@@ -127,17 +261,131 @@ async function createChatUI(options) {
|
|
|
127
261
|
isThought,
|
|
128
262
|
time: new Date()
|
|
129
263
|
}]);
|
|
264
|
+
},
|
|
265
|
+
requestApproval: (request = {}) => {
|
|
266
|
+
return new Promise((resolve) => {
|
|
267
|
+
const approval = {
|
|
268
|
+
type: request.type || 'action',
|
|
269
|
+
label: request.label || 'Requested action',
|
|
270
|
+
preview: request.preview || '',
|
|
271
|
+
resolve
|
|
272
|
+
};
|
|
273
|
+
pendingApprovalRef.current = approval;
|
|
274
|
+
setPendingApproval(approval);
|
|
275
|
+
});
|
|
130
276
|
}
|
|
131
277
|
}));
|
|
132
278
|
|
|
133
279
|
// Handle exiting and keyboard navigation
|
|
134
280
|
useInput((inputStr, key) => {
|
|
281
|
+
const approval = pendingApprovalRef.current;
|
|
282
|
+
if (approval) {
|
|
283
|
+
const resolveApproval = (approved) => {
|
|
284
|
+
pendingApprovalRef.current = null;
|
|
285
|
+
setPendingApproval(null);
|
|
286
|
+
setHistory(prev => [...prev, {
|
|
287
|
+
role: 'system',
|
|
288
|
+
label: 'Approval',
|
|
289
|
+
labelColor: approved ? 'greenBright' : 'redBright',
|
|
290
|
+
text: `${approved ? 'Approved' : 'Denied'}: ${approval.label}`,
|
|
291
|
+
time: new Date()
|
|
292
|
+
}]);
|
|
293
|
+
approval.resolve(approved);
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
if (key.leftArrow || key.rightArrow || key.tab) {
|
|
297
|
+
const next = approvalChoiceRef.current === 'approve' ? 'deny' : 'approve';
|
|
298
|
+
approvalChoiceRef.current = next;
|
|
299
|
+
setApprovalChoice(next);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const answer = String(inputStr || '').toLowerCase();
|
|
304
|
+
if (key.return) {
|
|
305
|
+
resolveApproval(approvalChoiceRef.current === 'approve');
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (answer === 'y') {
|
|
309
|
+
resolveApproval(true);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (answer === 'n' || key.escape || (key.ctrl && inputStr === 'c')) {
|
|
313
|
+
resolveApproval(false);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (key.escape && pendingImagesRef.current.length > 0) {
|
|
320
|
+
setPendingImages([]);
|
|
321
|
+
pendingImagesRef.current = [];
|
|
322
|
+
setPendingImagePrefix('');
|
|
323
|
+
pendingImagePrefixRef.current = '';
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (key.escape && pendingPasteRef.current) {
|
|
328
|
+
setPendingPaste(null);
|
|
329
|
+
pendingPasteRef.current = null;
|
|
330
|
+
setPendingPastePrefix('');
|
|
331
|
+
pendingPastePrefixRef.current = '';
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (key.ctrl && key.backspace && pendingImagesRef.current.length > 0) {
|
|
336
|
+
setPendingImages(prev => prev.slice(0, -1));
|
|
337
|
+
pendingImagesRef.current = pendingImagesRef.current.slice(0, -1);
|
|
338
|
+
if (pendingImagesRef.current.length === 0) {
|
|
339
|
+
setPendingImagePrefix('');
|
|
340
|
+
pendingImagePrefixRef.current = '';
|
|
341
|
+
}
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
135
345
|
if (key.escape || (key.ctrl && inputStr === 'c')) {
|
|
136
|
-
onExit();
|
|
137
346
|
exit();
|
|
347
|
+
onExit();
|
|
348
|
+
return;
|
|
138
349
|
}
|
|
139
350
|
|
|
140
351
|
const currentInput = inputRef.current;
|
|
352
|
+
if (key.ctrl && inputStr === 'v') {
|
|
353
|
+
suppressPasteCharRef.current = true;
|
|
354
|
+
const inputBeforePaste = currentInput;
|
|
355
|
+
setInput(prev => removePasteArtifact(prev));
|
|
356
|
+
if (typeof onPasteImage === 'function') {
|
|
357
|
+
Promise.resolve(onPasteImage())
|
|
358
|
+
.then((image) => {
|
|
359
|
+
if (image) {
|
|
360
|
+
setPendingImages(prev => {
|
|
361
|
+
if (prev.length === 0) {
|
|
362
|
+
const prefix = normalizeInputText(inputBeforePaste).trim();
|
|
363
|
+
setPendingImagePrefix(prefix);
|
|
364
|
+
pendingImagePrefixRef.current = prefix;
|
|
365
|
+
}
|
|
366
|
+
return [...prev, image];
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
})
|
|
370
|
+
.catch((err) => {
|
|
371
|
+
setHistory(prev => [...prev, {
|
|
372
|
+
role: 'error',
|
|
373
|
+
text: err && err.message ? err.message : String(err || 'Unknown error'),
|
|
374
|
+
time: new Date()
|
|
375
|
+
}]);
|
|
376
|
+
})
|
|
377
|
+
.finally(() => {
|
|
378
|
+
setInput(prev => {
|
|
379
|
+
if (prev === `${inputBeforePaste}v` || prev === `${inputBeforePaste}V`) {
|
|
380
|
+
return inputBeforePaste;
|
|
381
|
+
}
|
|
382
|
+
return removePasteArtifact(prev);
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
141
389
|
const currentShowSuggestions = currentInput.startsWith('/') && !currentInput.includes(' ');
|
|
142
390
|
|
|
143
391
|
if (currentShowSuggestions) {
|
|
@@ -160,10 +408,20 @@ async function createChatUI(options) {
|
|
|
160
408
|
});
|
|
161
409
|
|
|
162
410
|
const handleSubmit = (value) => {
|
|
163
|
-
const text = value.trim();
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
411
|
+
const text = normalizeInputText(value).trim();
|
|
412
|
+
const images = pendingImagesRef.current;
|
|
413
|
+
const imagePrefix = normalizeInputText(pendingImagePrefixRef.current).trim();
|
|
414
|
+
const imageLabels = images.map((_, index) => `[Image #${index + 1}]`).join(' ');
|
|
415
|
+
const pasted = pendingPasteRef.current;
|
|
416
|
+
const pastePrefix = normalizeInputText(pendingPastePrefixRef.current).trim();
|
|
417
|
+
const submittedText = pasted
|
|
418
|
+
? [pastePrefix, pasted.text, text].filter(Boolean).join('\n\n')
|
|
419
|
+
: images.length > 0
|
|
420
|
+
? [imagePrefix, imageLabels, text].filter(Boolean).join('\n\n')
|
|
421
|
+
: text;
|
|
422
|
+
if (!submittedText && images.length === 0) return;
|
|
423
|
+
|
|
424
|
+
if (!pasted && images.length === 0 && showSuggestions && suggestions.length > 0) {
|
|
167
425
|
const picked = suggestions[selectedIndex];
|
|
168
426
|
if (picked && text !== picked.cmd) {
|
|
169
427
|
setInput(picked.cmd + ' ');
|
|
@@ -172,42 +430,79 @@ async function createChatUI(options) {
|
|
|
172
430
|
}
|
|
173
431
|
|
|
174
432
|
setInput('');
|
|
175
|
-
|
|
433
|
+
setPendingImages([]);
|
|
434
|
+
setPendingImagePrefix('');
|
|
435
|
+
setPendingPaste(null);
|
|
436
|
+
setPendingPastePrefix('');
|
|
437
|
+
pendingImagesRef.current = [];
|
|
438
|
+
pendingImagePrefixRef.current = '';
|
|
439
|
+
pendingPasteRef.current = null;
|
|
440
|
+
pendingPastePrefixRef.current = '';
|
|
441
|
+
onSubmit(submittedText, { images, pasted });
|
|
176
442
|
};
|
|
177
443
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
444
|
+
const handleInputChange = (value) => {
|
|
445
|
+
if (shouldStoreAsPastedContent(value)) {
|
|
446
|
+
const normalized = normalizeInputText(value);
|
|
447
|
+
const previous = normalizeInputText(inputRef.current).trim();
|
|
448
|
+
setPendingPaste({ text: normalized, label: `[Pasted Content ${normalized.length} chars]` });
|
|
449
|
+
setPendingPastePrefix(previous);
|
|
450
|
+
setInput('');
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
186
453
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
nameColor = msg.labelColor || 'blueBright';
|
|
454
|
+
const normalizedValue = normalizeInputText(value);
|
|
455
|
+
if (suppressPasteCharRef.current) {
|
|
456
|
+
suppressPasteCharRef.current = false;
|
|
457
|
+
const previous = inputRef.current;
|
|
458
|
+
if (normalizedValue === `${previous}v` || normalizedValue === `${previous}V`) {
|
|
459
|
+
setInput(previous);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
if (normalizedValue.length > previous.length && /^[vV]$/.test(normalizedValue.slice(previous.length))) {
|
|
463
|
+
setInput(previous);
|
|
464
|
+
return;
|
|
199
465
|
}
|
|
466
|
+
}
|
|
467
|
+
setInput(normalizedValue);
|
|
468
|
+
};
|
|
200
469
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
),
|
|
206
|
-
h(Box, { paddingLeft: 2, marginBottom: 1 },
|
|
207
|
-
h(Text, null, msg.text)
|
|
208
|
-
)
|
|
470
|
+
const renderMessage = (msg, index, keyPrefix = 'msg') => {
|
|
471
|
+
if (msg.isThought) {
|
|
472
|
+
return h(Box, { key: `${keyPrefix}-${index}`, flexDirection: 'row', marginBottom: 0, paddingLeft: 2 },
|
|
473
|
+
h(Text, { color: 'gray', dimColor: true }, `Thinking: ${msg.text}`)
|
|
209
474
|
);
|
|
210
|
-
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
let name = 'Mint';
|
|
478
|
+
let nameColor = 'greenBright';
|
|
479
|
+
|
|
480
|
+
if (msg.role === 'user') {
|
|
481
|
+
name = 'You';
|
|
482
|
+
nameColor = 'cyanBright';
|
|
483
|
+
} else if (msg.role === 'error') {
|
|
484
|
+
name = 'Error';
|
|
485
|
+
nameColor = 'redBright';
|
|
486
|
+
} else if (msg.role === 'system') {
|
|
487
|
+
name = msg.label || 'System';
|
|
488
|
+
nameColor = msg.labelColor || 'blueBright';
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return h(Box, { key: `${keyPrefix}-${index}`, flexDirection: 'column', marginBottom: 0 },
|
|
492
|
+
h(Box, null,
|
|
493
|
+
h(Text, { bold: true, color: nameColor }, name),
|
|
494
|
+
h(Text, { color: 'gray' }, ` ${msg.time instanceof Date ? msg.time.toLocaleTimeString() : ''}`)
|
|
495
|
+
),
|
|
496
|
+
h(Box, { paddingLeft: 2, marginBottom: 1 },
|
|
497
|
+
h(Text, null, msg.text)
|
|
498
|
+
)
|
|
499
|
+
);
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
return h(Box, { flexDirection: 'column', paddingX: 1, width: '100%' },
|
|
503
|
+
// Static History: Messages
|
|
504
|
+
h(Static, { items: history }, (msg, index) => renderMessage(msg, index, 'history')),
|
|
505
|
+
liveAssistant && renderMessage(liveAssistant, 'live', 'live'),
|
|
211
506
|
|
|
212
507
|
// Floating (Persistent) UI part
|
|
213
508
|
h(Box, { flexDirection: 'column' },
|
|
@@ -232,21 +527,64 @@ async function createChatUI(options) {
|
|
|
232
527
|
))
|
|
233
528
|
),
|
|
234
529
|
|
|
530
|
+
pendingApproval && h(Box, {
|
|
531
|
+
flexDirection: 'column',
|
|
532
|
+
borderStyle: 'single',
|
|
533
|
+
borderColor: 'yellow',
|
|
534
|
+
paddingX: 1,
|
|
535
|
+
marginBottom: 1
|
|
536
|
+
},
|
|
537
|
+
h(Box, null,
|
|
538
|
+
h(Text, { bold: true, color: 'yellow' }, 'Approval '),
|
|
539
|
+
h(Text, { color: 'gray' }, `[${pendingApproval.type}] `),
|
|
540
|
+
h(Text, { color: 'white' }, pendingApproval.label)
|
|
541
|
+
),
|
|
542
|
+
pendingApproval.preview && pendingApproval.preview !== pendingApproval.label && h(Box, null,
|
|
543
|
+
h(Text, { color: 'gray' }, pendingApproval.preview)
|
|
544
|
+
),
|
|
545
|
+
h(Box, null,
|
|
546
|
+
h(Text, {
|
|
547
|
+
color: approvalChoice === 'approve' ? 'black' : 'greenBright',
|
|
548
|
+
backgroundColor: approvalChoice === 'approve' ? 'greenBright' : undefined,
|
|
549
|
+
bold: true
|
|
550
|
+
}, ' Approve '),
|
|
551
|
+
h(Text, { color: 'gray' }, ' '),
|
|
552
|
+
h(Text, {
|
|
553
|
+
color: approvalChoice === 'deny' ? 'white' : 'redBright',
|
|
554
|
+
backgroundColor: approvalChoice === 'deny' ? 'redBright' : undefined,
|
|
555
|
+
bold: true
|
|
556
|
+
}, ' Deny '),
|
|
557
|
+
h(Text, { color: 'gray' }, ' Tab/←/→ Enter')
|
|
558
|
+
)
|
|
559
|
+
),
|
|
560
|
+
|
|
235
561
|
// Compact Input Area
|
|
236
|
-
h(Box, { borderStyle: 'round', borderColor: 'greenBright', paddingX: 1, flexDirection: '
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
562
|
+
h(Box, { borderStyle: 'round', borderColor: pendingApproval ? 'gray' : 'greenBright', paddingX: 1, flexDirection: 'column' },
|
|
563
|
+
pendingImages.length > 0 && h(Box, null,
|
|
564
|
+
pendingImagePrefix && h(Text, { color: 'cyanBright' }, '[Text before] '),
|
|
565
|
+
h(Text, { color: 'greenBright' }, pendingImages.map((_, index) => `[Image #${index + 1}]`).join(' ') + ' '),
|
|
566
|
+
h(Text, { color: 'gray' }, 'Enter to send, Ctrl+Backspace remove, Esc clear')
|
|
567
|
+
),
|
|
568
|
+
pendingPaste && h(Box, null,
|
|
569
|
+
pendingPastePrefix && h(Text, { color: 'cyanBright' }, '[Text before] '),
|
|
570
|
+
h(Text, { color: 'yellowBright' }, pendingPaste.label),
|
|
571
|
+
h(Text, { color: 'gray' }, ' Enter to send, Esc clear')
|
|
572
|
+
),
|
|
573
|
+
h(Box, { flexDirection: 'row' },
|
|
574
|
+
h(Text, { bold: true, color: 'greenBright' }, '› '),
|
|
575
|
+
h(TextInput, {
|
|
576
|
+
value: input,
|
|
577
|
+
onChange: pendingApproval ? () => {} : handleInputChange,
|
|
578
|
+
onSubmit: pendingApproval ? () => {} : handleSubmit,
|
|
579
|
+
placeholder: pendingApproval ? 'Approval pending...' : 'Ask anything...'
|
|
580
|
+
})
|
|
581
|
+
)
|
|
244
582
|
),
|
|
245
583
|
|
|
246
584
|
// Status Bar
|
|
247
585
|
h(Box, { justifyContent: 'space-between' },
|
|
248
586
|
h(Box, null,
|
|
249
|
-
h(Text, { color: 'cyan' }, `[${mode}] `),
|
|
587
|
+
h(Text, { color: 'cyan' }, `[${fastMode ? 'Fast' : mode}] `),
|
|
250
588
|
h(Text, { color: 'magentaBright' }, (model || config.geminiModel || 'gemini').slice(0, 46))
|
|
251
589
|
),
|
|
252
590
|
h(Box, null,
|
|
@@ -265,28 +603,36 @@ async function createChatUI(options) {
|
|
|
265
603
|
console.log(`\x1b[90mType naturally to chat. Esc to exit.\x1b[0m\n`);
|
|
266
604
|
|
|
267
605
|
const ref = createRef();
|
|
268
|
-
render(h(App, { ref, ...options }));
|
|
606
|
+
render(h(App, { ref, ...options }), { exitOnCtrlC: false });
|
|
269
607
|
|
|
270
608
|
return {
|
|
271
609
|
appendMessage: (role, text, metadata) => ref.current?.appendMessage(role, text, metadata),
|
|
272
610
|
setThinking: (val) => ref.current?.setThinking(val),
|
|
273
611
|
setMode: (val) => ref.current?.setMode(val),
|
|
612
|
+
setFastMode: (val) => ref.current?.setFastMode(val),
|
|
613
|
+
toggleFastMode: () => ref.current?.toggleFastMode(),
|
|
614
|
+
getFastMode: () => ref.current?.getFastMode(),
|
|
615
|
+
setInputText: (val) => ref.current?.setInputText(val),
|
|
616
|
+
setPendingPasteText: (text) => ref.current?.setPendingPasteText(text),
|
|
274
617
|
updateStatusModel: (val) => ref.current?.updateStatusModel(val),
|
|
275
618
|
updateWorkspace: (val) => ref.current?.updateWorkspace(val),
|
|
619
|
+
attachImage: (image) => ref.current?.attachImage(image),
|
|
276
620
|
appendCodeStep: (info) => ref.current?.appendCodeStep(info),
|
|
277
|
-
streamMessage: () => {
|
|
621
|
+
streamMessage: (metadata = {}) => {
|
|
278
622
|
let fullText = '';
|
|
623
|
+
ref.current?.beginAssistantStream(metadata);
|
|
279
624
|
return {
|
|
280
625
|
appendChunk: (chunk) => {
|
|
281
626
|
fullText += chunk;
|
|
627
|
+
ref.current?.appendAssistantStreamChunk(chunk);
|
|
282
628
|
},
|
|
283
629
|
finalize: () => {
|
|
284
|
-
ref.current?.
|
|
630
|
+
ref.current?.finalizeAssistantStream();
|
|
285
631
|
}
|
|
286
632
|
};
|
|
287
633
|
},
|
|
288
634
|
copyLastResponse: () => false,
|
|
289
|
-
requestApproval: () => Promise.resolve(
|
|
635
|
+
requestApproval: (request) => ref.current?.requestApproval(request) || Promise.resolve(false),
|
|
290
636
|
askUser: () => Promise.resolve('')
|
|
291
637
|
};
|
|
292
638
|
}
|