@pheem49/mint 1.5.0 → 1.5.2
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 +35 -1
- package/main.js +28 -14
- package/mint-cli-logic.js +3 -119
- package/mint-cli.js +201 -500
- 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 +40 -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 +15 -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 +40 -17
- 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/AI_Brain/proactive_engine.js +12 -2
- package/src/Automation_Layer/browser_automation.js +26 -24
- package/src/CLI/approval_handler.js +42 -0
- package/src/CLI/chat_router.js +18 -6
- package/src/CLI/chat_ui.js +583 -52
- package/src/CLI/cli_colors.js +32 -0
- package/src/CLI/cli_formatters.js +89 -0
- package/src/CLI/code_agent.js +369 -71
- package/src/CLI/image_input.js +90 -0
- package/src/CLI/intent_detectors.js +181 -0
- package/src/CLI/interactive_chat.js +479 -0
- package/src/CLI/list_features.js +3 -0
- package/src/CLI/onboarding.js +72 -15
- package/src/CLI/repo_summarizer.js +282 -0
- package/src/CLI/semantic_code_search.js +312 -0
- package/src/CLI/skill_manager.js +41 -0
- package/src/CLI/slash_command_handler.js +418 -0
- package/src/CLI/symbol_indexer.js +231 -0
- package/src/CLI/updater.js +6 -4
- package/src/Channels/discord_bridge.js +11 -13
- package/src/Channels/line_bridge.js +10 -10
- package/src/Channels/slack_bridge.js +7 -12
- package/src/Channels/telegram_bridge.js +6 -14
- package/src/Channels/whatsapp_bridge.js +11 -9
- package/src/System/action_executor.js +59 -10
- package/src/System/chat_history_manager.js +20 -12
- package/src/System/config_manager.js +31 -1
- package/src/System/granular_automation.js +122 -53
- package/src/System/optional_require.js +23 -0
- 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 +566 -0
- package/src/UI/renderer.js +339 -21
- package/src/UI/settings.css +655 -420
- package/src/UI/settings.html +478 -432
- package/src/UI/settings.js +10 -8
- package/src/UI/styles.css +516 -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' },
|
|
@@ -26,6 +32,127 @@ const SLASH_COMMANDS = [
|
|
|
26
32
|
{ cmd: '/exit', desc: 'Exit Mint' }
|
|
27
33
|
];
|
|
28
34
|
|
|
35
|
+
const MAX_BLANK_LINES = 1;
|
|
36
|
+
|
|
37
|
+
function compactPathLabel(value) {
|
|
38
|
+
const text = String(value || '').trim();
|
|
39
|
+
if (!text) return '';
|
|
40
|
+
return path.basename(text) || text;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function formatActivityStep(info = {}) {
|
|
44
|
+
if (!info || typeof info !== 'object') return null;
|
|
45
|
+
|
|
46
|
+
const { action, phase, target, message } = info;
|
|
47
|
+
const rawText = String(target || message || '').trim();
|
|
48
|
+
const kind = action || phase || 'activity';
|
|
49
|
+
if (!rawText) return null;
|
|
50
|
+
|
|
51
|
+
switch (kind) {
|
|
52
|
+
case 'list_files':
|
|
53
|
+
return { title: 'Explored', detail: `List ${rawText}` };
|
|
54
|
+
case 'find_path':
|
|
55
|
+
return { title: 'Explored', detail: `Find ${rawText}` };
|
|
56
|
+
case 'read_file':
|
|
57
|
+
return { title: 'Explored', detail: `Read ${compactPathLabel(rawText)}` };
|
|
58
|
+
case 'search_code':
|
|
59
|
+
return { title: 'Explored', detail: `Search ${rawText}` };
|
|
60
|
+
case 'web_search':
|
|
61
|
+
return { title: 'Searched', detail: rawText };
|
|
62
|
+
case 'warn':
|
|
63
|
+
return { title: '⚠ Notice', detail: rawText };
|
|
64
|
+
case 'run_shell':
|
|
65
|
+
return { title: 'Ran', detail: rawText };
|
|
66
|
+
case 'apply_patch':
|
|
67
|
+
case 'write_file':
|
|
68
|
+
return { title: 'Edited', detail: rawText };
|
|
69
|
+
case 'evaluator':
|
|
70
|
+
return { title: 'Checked', detail: rawText };
|
|
71
|
+
case 'reviewer_start':
|
|
72
|
+
return { title: 'Reviewing', detail: rawText };
|
|
73
|
+
case 'ask_user':
|
|
74
|
+
return { title: 'Ask User', detail: rawText };
|
|
75
|
+
default:
|
|
76
|
+
return { title: kind, detail: rawText };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function stripInlineMarkdown(value) {
|
|
81
|
+
return String(value || '')
|
|
82
|
+
.replace(/\*\*([^*]+)\*\*/g, '$1')
|
|
83
|
+
.replace(/\*([^*\n]+)\*/g, '$1')
|
|
84
|
+
.replace(/__([^_]+)__/g, '$1')
|
|
85
|
+
.replace(/`([^`\n]+)`/g, '$1');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function cleanDisplayText(text, role = 'assistant') {
|
|
89
|
+
const raw = String(text || '').replace(/\r\n/g, '\n').trim();
|
|
90
|
+
if (!raw) return '';
|
|
91
|
+
|
|
92
|
+
const shouldPolishMarkdown = role === 'assistant' || role === 'system';
|
|
93
|
+
const lines = raw.split('\n');
|
|
94
|
+
const cleaned = [];
|
|
95
|
+
let inCodeBlock = false;
|
|
96
|
+
let blankCount = 0;
|
|
97
|
+
|
|
98
|
+
for (const sourceLine of lines) {
|
|
99
|
+
let line = sourceLine.replace(/\s+$/g, '');
|
|
100
|
+
const fence = line.match(/^\s*```(.*)$/);
|
|
101
|
+
|
|
102
|
+
if (fence) {
|
|
103
|
+
inCodeBlock = !inCodeBlock;
|
|
104
|
+
const label = fence[1] ? `code: ${fence[1].trim()}` : 'code';
|
|
105
|
+
line = inCodeBlock ? label : '';
|
|
106
|
+
} else if (inCodeBlock) {
|
|
107
|
+
line = line ? ` ${line}` : '';
|
|
108
|
+
} else if (shouldPolishMarkdown) {
|
|
109
|
+
const heading = line.match(/^\s{0,3}#{1,6}\s+(.+)$/);
|
|
110
|
+
const bullet = line.match(/^(\s*)[-*]\s+(.+)$/);
|
|
111
|
+
const numbered = line.match(/^(\s*)\d+[.)]\s+(.+)$/);
|
|
112
|
+
|
|
113
|
+
if (heading) {
|
|
114
|
+
if (cleaned.length > 0 && cleaned[cleaned.length - 1] !== '') cleaned.push('');
|
|
115
|
+
line = stripInlineMarkdown(heading[1]).trim();
|
|
116
|
+
} else if (bullet) {
|
|
117
|
+
line = `${bullet[1]}• ${stripInlineMarkdown(bullet[2]).trim()}`;
|
|
118
|
+
} else if (numbered) {
|
|
119
|
+
line = `${numbered[1]}${stripInlineMarkdown(line).trim()}`;
|
|
120
|
+
} else {
|
|
121
|
+
line = stripInlineMarkdown(line);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!line.trim()) {
|
|
126
|
+
blankCount++;
|
|
127
|
+
if (blankCount <= MAX_BLANK_LINES && cleaned.length > 0) cleaned.push('');
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
blankCount = 0;
|
|
132
|
+
cleaned.push(line);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
while (cleaned[0] === '') cleaned.shift();
|
|
136
|
+
while (cleaned[cleaned.length - 1] === '') cleaned.pop();
|
|
137
|
+
return cleaned.join('\n');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function formatDuration(totalSeconds) {
|
|
141
|
+
const seconds = Math.max(0, Math.floor(Number(totalSeconds) || 0));
|
|
142
|
+
const minutes = Math.floor(seconds / 60);
|
|
143
|
+
const remainingSeconds = seconds % 60;
|
|
144
|
+
|
|
145
|
+
if (minutes <= 0) return `${remainingSeconds}s`;
|
|
146
|
+
return `${minutes}m ${remainingSeconds}s`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function shouldAppendMessage(role, text) {
|
|
150
|
+
if (role === 'assistant' || role === 'system') {
|
|
151
|
+
return String(text || '').trim().length > 0;
|
|
152
|
+
}
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
|
|
29
156
|
/**
|
|
30
157
|
* We wrap everything in an async function to load ESM modules
|
|
31
158
|
*/
|
|
@@ -35,29 +162,109 @@ async function createChatUI(options) {
|
|
|
35
162
|
const TextInput = (await import('ink-text-input')).default;
|
|
36
163
|
const { useState, useImperativeHandle, forwardRef, createRef, useEffect, useMemo } = React;
|
|
37
164
|
|
|
38
|
-
const App = forwardRef(({ onSubmit, onExit, initialHistory = [] }, ref) => {
|
|
165
|
+
const App = forwardRef(({ onSubmit, onExit, onPasteImage, initialHistory = [] }, ref) => {
|
|
39
166
|
const config = readConfig();
|
|
40
167
|
const { exit } = useApp();
|
|
41
168
|
const [input, setInput] = useState('');
|
|
42
169
|
const [history, setHistory] = useState(initialHistory);
|
|
170
|
+
const [liveAssistant, setLiveAssistant] = useState(null);
|
|
43
171
|
const [thinking, setThinking] = useState(false);
|
|
44
|
-
const [
|
|
172
|
+
const [workingSeconds, setWorkingSeconds] = useState(0);
|
|
173
|
+
const [fastMode, setFastMode] = useState(false);
|
|
174
|
+
const [mode, setMode] = useState('Agent');
|
|
45
175
|
const [model, setModel] = useState('');
|
|
46
176
|
const [workspace, setWorkspace] = useState(process.cwd());
|
|
177
|
+
const [pendingImages, setPendingImages] = useState([]);
|
|
178
|
+
const [pendingImagePrefix, setPendingImagePrefix] = useState('');
|
|
179
|
+
const [pendingPaste, setPendingPaste] = useState(null);
|
|
180
|
+
const [pendingPastePrefix, setPendingPastePrefix] = useState('');
|
|
181
|
+
const [pendingApproval, setPendingApproval] = useState(null);
|
|
182
|
+
const [approvalChoice, setApprovalChoice] = useState('approve');
|
|
47
183
|
|
|
48
184
|
// Suggestions State
|
|
49
185
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
50
186
|
const inputRef = React.useRef(input);
|
|
187
|
+
const pendingImagesRef = React.useRef(pendingImages);
|
|
188
|
+
const pendingImagePrefixRef = React.useRef(pendingImagePrefix);
|
|
189
|
+
const pendingPasteRef = React.useRef(pendingPaste);
|
|
190
|
+
const pendingPastePrefixRef = React.useRef(pendingPastePrefix);
|
|
191
|
+
const liveAssistantRef = React.useRef(liveAssistant);
|
|
192
|
+
const thinkingStartedAtRef = React.useRef(null);
|
|
193
|
+
const fastModeRef = React.useRef(fastMode);
|
|
194
|
+
const suppressPasteCharRef = React.useRef(false);
|
|
51
195
|
const selectedIndexRef = React.useRef(selectedIndex);
|
|
196
|
+
const pendingApprovalRef = React.useRef(null);
|
|
197
|
+
const approvalChoiceRef = React.useRef('approve');
|
|
198
|
+
|
|
199
|
+
const removePasteArtifact = (value) => {
|
|
200
|
+
const text = String(value || '');
|
|
201
|
+
return text.replace(/[vV]$/, '');
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const normalizeInputText = (value) => {
|
|
205
|
+
return String(value || '').replace(/\s*[\r\n]+\s*/g, ' ');
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const shouldStoreAsPastedContent = (value) => {
|
|
209
|
+
const text = String(value || '');
|
|
210
|
+
return text.length > 500 || /[\r\n]/.test(text);
|
|
211
|
+
};
|
|
52
212
|
|
|
53
213
|
useEffect(() => {
|
|
54
214
|
inputRef.current = input;
|
|
55
215
|
}, [input]);
|
|
56
216
|
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
pendingImagesRef.current = pendingImages;
|
|
219
|
+
}, [pendingImages]);
|
|
220
|
+
|
|
221
|
+
useEffect(() => {
|
|
222
|
+
pendingImagePrefixRef.current = pendingImagePrefix;
|
|
223
|
+
}, [pendingImagePrefix]);
|
|
224
|
+
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
pendingPasteRef.current = pendingPaste;
|
|
227
|
+
}, [pendingPaste]);
|
|
228
|
+
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
pendingPastePrefixRef.current = pendingPastePrefix;
|
|
231
|
+
}, [pendingPastePrefix]);
|
|
232
|
+
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
liveAssistantRef.current = liveAssistant;
|
|
235
|
+
}, [liveAssistant]);
|
|
236
|
+
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
if (!thinking) return undefined;
|
|
239
|
+
|
|
240
|
+
const timer = setInterval(() => {
|
|
241
|
+
if (!thinkingStartedAtRef.current) return;
|
|
242
|
+
setWorkingSeconds(Math.floor((Date.now() - thinkingStartedAtRef.current) / 1000));
|
|
243
|
+
}, 1000);
|
|
244
|
+
|
|
245
|
+
return () => clearInterval(timer);
|
|
246
|
+
}, [thinking]);
|
|
247
|
+
|
|
248
|
+
useEffect(() => {
|
|
249
|
+
fastModeRef.current = fastMode;
|
|
250
|
+
}, [fastMode]);
|
|
251
|
+
|
|
57
252
|
useEffect(() => {
|
|
58
253
|
selectedIndexRef.current = selectedIndex;
|
|
59
254
|
}, [selectedIndex]);
|
|
60
255
|
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
pendingApprovalRef.current = pendingApproval;
|
|
258
|
+
if (pendingApproval) {
|
|
259
|
+
approvalChoiceRef.current = 'approve';
|
|
260
|
+
setApprovalChoice('approve');
|
|
261
|
+
}
|
|
262
|
+
}, [pendingApproval]);
|
|
263
|
+
|
|
264
|
+
useEffect(() => {
|
|
265
|
+
approvalChoiceRef.current = approvalChoice;
|
|
266
|
+
}, [approvalChoice]);
|
|
267
|
+
|
|
61
268
|
const showSuggestions = input.startsWith('/') && !input.includes(' ');
|
|
62
269
|
const suggestions = useMemo(() => {
|
|
63
270
|
if (!showSuggestions) return [];
|
|
@@ -75,17 +282,91 @@ async function createChatUI(options) {
|
|
|
75
282
|
// Export methods to the outside world via ref
|
|
76
283
|
useImperativeHandle(ref, () => ({
|
|
77
284
|
appendMessage: (role, text, metadata = {}) => {
|
|
285
|
+
if (!shouldAppendMessage(role, text)) return;
|
|
78
286
|
setHistory(prev => [...prev, { role, text, time: new Date(), ...metadata }]);
|
|
79
287
|
if (metadata.providerInfo) {
|
|
80
288
|
const { provider, model } = metadata.providerInfo;
|
|
81
289
|
setModel(model ? `${provider} • ${model}` : provider);
|
|
82
290
|
}
|
|
83
291
|
},
|
|
84
|
-
|
|
292
|
+
beginAssistantStream: (metadata = {}) => {
|
|
293
|
+
const msg = { role: 'assistant', text: '', time: new Date(), ...metadata };
|
|
294
|
+
liveAssistantRef.current = msg;
|
|
295
|
+
setLiveAssistant(msg);
|
|
296
|
+
if (metadata.providerInfo) {
|
|
297
|
+
const { provider, model } = metadata.providerInfo;
|
|
298
|
+
setModel(model ? `${provider} • ${model}` : provider);
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
appendAssistantStreamChunk: (chunk) => {
|
|
302
|
+
if (!String(chunk || '').trim()) return;
|
|
303
|
+
const current = liveAssistantRef.current || { role: 'assistant', text: '', time: new Date() };
|
|
304
|
+
const next = { ...current, text: `${current.text || ''}${chunk}` };
|
|
305
|
+
liveAssistantRef.current = next;
|
|
306
|
+
setLiveAssistant(next);
|
|
307
|
+
},
|
|
308
|
+
finalizeAssistantStream: () => {
|
|
309
|
+
const current = liveAssistantRef.current;
|
|
310
|
+
liveAssistantRef.current = null;
|
|
311
|
+
setLiveAssistant(null);
|
|
312
|
+
if (current && String(current.text || '').trim()) {
|
|
313
|
+
setHistory(prev => [...prev, current]);
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
setThinking: (val, seconds = 0) => {
|
|
317
|
+
if (val) {
|
|
318
|
+
const elapsed = Number.isFinite(seconds) ? Math.max(0, seconds) : 0;
|
|
319
|
+
if (!thinkingStartedAtRef.current) {
|
|
320
|
+
thinkingStartedAtRef.current = Date.now() - (elapsed * 1000);
|
|
321
|
+
}
|
|
322
|
+
setWorkingSeconds(Math.floor((Date.now() - thinkingStartedAtRef.current) / 1000));
|
|
323
|
+
setThinking(true);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
thinkingStartedAtRef.current = null;
|
|
328
|
+
setWorkingSeconds(0);
|
|
329
|
+
setThinking(false);
|
|
330
|
+
},
|
|
85
331
|
setMode: (val) => setMode(val),
|
|
332
|
+
setFastMode: (val) => {
|
|
333
|
+
const next = Boolean(val);
|
|
334
|
+
fastModeRef.current = next;
|
|
335
|
+
setFastMode(next);
|
|
336
|
+
return next;
|
|
337
|
+
},
|
|
338
|
+
toggleFastMode: () => {
|
|
339
|
+
const next = !fastModeRef.current;
|
|
340
|
+
fastModeRef.current = next;
|
|
341
|
+
setFastMode(next);
|
|
342
|
+
return next;
|
|
343
|
+
},
|
|
344
|
+
getFastMode: () => fastModeRef.current,
|
|
345
|
+
setInputText: (val) => setInput(val || ''),
|
|
346
|
+
setPendingPasteText: (text) => {
|
|
347
|
+
const normalized = normalizeInputText(text);
|
|
348
|
+
setPendingPaste({ text: normalized, label: `[Pasted Content ${normalized.length} chars]` });
|
|
349
|
+
setPendingPastePrefix('');
|
|
350
|
+
setInput('');
|
|
351
|
+
},
|
|
86
352
|
updateStatusModel: (val) => setModel(val),
|
|
87
353
|
updateWorkspace: (val) => setWorkspace(val),
|
|
354
|
+
attachImage: (image) => {
|
|
355
|
+
setPendingImages(prev => {
|
|
356
|
+
if (prev.length === 0) {
|
|
357
|
+
const prefix = normalizeInputText(inputRef.current).trim();
|
|
358
|
+
setPendingImagePrefix(prefix);
|
|
359
|
+
pendingImagePrefixRef.current = prefix;
|
|
360
|
+
setInput('');
|
|
361
|
+
}
|
|
362
|
+
return [...prev, image];
|
|
363
|
+
});
|
|
364
|
+
},
|
|
88
365
|
appendCodeStep: (info) => {
|
|
366
|
+
if (fastModeRef.current) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
89
370
|
let text = '';
|
|
90
371
|
let label = 'System';
|
|
91
372
|
let labelColor = 'blueBright';
|
|
@@ -95,7 +376,13 @@ async function createChatUI(options) {
|
|
|
95
376
|
text = info;
|
|
96
377
|
} else {
|
|
97
378
|
const { action, phase, target, message, thought } = info;
|
|
379
|
+
if (action === 'memory_context' && process.env.MINT_SHOW_MEMORY_TRACE !== '1') {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
98
382
|
if (thought) {
|
|
383
|
+
if (process.env.MINT_SHOW_THINKING_TRACE !== '1') {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
99
386
|
text = thought;
|
|
100
387
|
label = 'Thinking';
|
|
101
388
|
labelColor = 'gray';
|
|
@@ -103,6 +390,25 @@ async function createChatUI(options) {
|
|
|
103
390
|
} else if (action === 'thinking' || phase === 'thinking') {
|
|
104
391
|
return;
|
|
105
392
|
} else {
|
|
393
|
+
const activity = formatActivityStep(info);
|
|
394
|
+
if (activity) {
|
|
395
|
+
const fullText = `[${activity.title}] ${activity.detail}`;
|
|
396
|
+
if (fullText === lastSystemMessage.current) return;
|
|
397
|
+
lastSystemMessage.current = fullText;
|
|
398
|
+
|
|
399
|
+
setHistory(prev => [...prev, {
|
|
400
|
+
role: 'system',
|
|
401
|
+
label: activity.title,
|
|
402
|
+
labelColor: 'blueBright',
|
|
403
|
+
text: activity.detail,
|
|
404
|
+
isActivity: true,
|
|
405
|
+
activityTitle: activity.title,
|
|
406
|
+
activityDetail: activity.detail,
|
|
407
|
+
time: new Date()
|
|
408
|
+
}]);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
|
|
106
412
|
label = action || phase || 'Action';
|
|
107
413
|
text = target || message || '';
|
|
108
414
|
if (!text) return;
|
|
@@ -127,17 +433,131 @@ async function createChatUI(options) {
|
|
|
127
433
|
isThought,
|
|
128
434
|
time: new Date()
|
|
129
435
|
}]);
|
|
436
|
+
},
|
|
437
|
+
requestApproval: (request = {}) => {
|
|
438
|
+
return new Promise((resolve) => {
|
|
439
|
+
const approval = {
|
|
440
|
+
type: request.type || 'action',
|
|
441
|
+
label: request.label || 'Requested action',
|
|
442
|
+
preview: request.preview || '',
|
|
443
|
+
resolve
|
|
444
|
+
};
|
|
445
|
+
pendingApprovalRef.current = approval;
|
|
446
|
+
setPendingApproval(approval);
|
|
447
|
+
});
|
|
130
448
|
}
|
|
131
449
|
}));
|
|
132
450
|
|
|
133
451
|
// Handle exiting and keyboard navigation
|
|
134
452
|
useInput((inputStr, key) => {
|
|
453
|
+
const approval = pendingApprovalRef.current;
|
|
454
|
+
if (approval) {
|
|
455
|
+
const resolveApproval = (approved) => {
|
|
456
|
+
pendingApprovalRef.current = null;
|
|
457
|
+
setPendingApproval(null);
|
|
458
|
+
setHistory(prev => [...prev, {
|
|
459
|
+
role: 'system',
|
|
460
|
+
label: 'Approval',
|
|
461
|
+
labelColor: approved ? 'greenBright' : 'redBright',
|
|
462
|
+
text: `${approved ? 'Approved' : 'Denied'}: ${approval.label}`,
|
|
463
|
+
time: new Date()
|
|
464
|
+
}]);
|
|
465
|
+
approval.resolve(approved);
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
if (key.leftArrow || key.rightArrow || key.tab) {
|
|
469
|
+
const next = approvalChoiceRef.current === 'approve' ? 'deny' : 'approve';
|
|
470
|
+
approvalChoiceRef.current = next;
|
|
471
|
+
setApprovalChoice(next);
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const answer = String(inputStr || '').toLowerCase();
|
|
476
|
+
if (key.return) {
|
|
477
|
+
resolveApproval(approvalChoiceRef.current === 'approve');
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (answer === 'y') {
|
|
481
|
+
resolveApproval(true);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
if (answer === 'n' || key.escape || (key.ctrl && inputStr === 'c')) {
|
|
485
|
+
resolveApproval(false);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (key.escape && pendingImagesRef.current.length > 0) {
|
|
492
|
+
setPendingImages([]);
|
|
493
|
+
pendingImagesRef.current = [];
|
|
494
|
+
setPendingImagePrefix('');
|
|
495
|
+
pendingImagePrefixRef.current = '';
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (key.escape && pendingPasteRef.current) {
|
|
500
|
+
setPendingPaste(null);
|
|
501
|
+
pendingPasteRef.current = null;
|
|
502
|
+
setPendingPastePrefix('');
|
|
503
|
+
pendingPastePrefixRef.current = '';
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (key.ctrl && key.backspace && pendingImagesRef.current.length > 0) {
|
|
508
|
+
setPendingImages(prev => prev.slice(0, -1));
|
|
509
|
+
pendingImagesRef.current = pendingImagesRef.current.slice(0, -1);
|
|
510
|
+
if (pendingImagesRef.current.length === 0) {
|
|
511
|
+
setPendingImagePrefix('');
|
|
512
|
+
pendingImagePrefixRef.current = '';
|
|
513
|
+
}
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
135
517
|
if (key.escape || (key.ctrl && inputStr === 'c')) {
|
|
136
|
-
onExit();
|
|
137
518
|
exit();
|
|
519
|
+
onExit();
|
|
520
|
+
return;
|
|
138
521
|
}
|
|
139
522
|
|
|
140
523
|
const currentInput = inputRef.current;
|
|
524
|
+
if (key.ctrl && inputStr === 'v') {
|
|
525
|
+
suppressPasteCharRef.current = true;
|
|
526
|
+
const inputBeforePaste = currentInput;
|
|
527
|
+
setInput(prev => removePasteArtifact(prev));
|
|
528
|
+
if (typeof onPasteImage === 'function') {
|
|
529
|
+
Promise.resolve(onPasteImage())
|
|
530
|
+
.then((image) => {
|
|
531
|
+
if (image) {
|
|
532
|
+
setPendingImages(prev => {
|
|
533
|
+
if (prev.length === 0) {
|
|
534
|
+
const prefix = normalizeInputText(inputBeforePaste).trim();
|
|
535
|
+
setPendingImagePrefix(prefix);
|
|
536
|
+
pendingImagePrefixRef.current = prefix;
|
|
537
|
+
}
|
|
538
|
+
return [...prev, image];
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
})
|
|
542
|
+
.catch((err) => {
|
|
543
|
+
setHistory(prev => [...prev, {
|
|
544
|
+
role: 'error',
|
|
545
|
+
text: err && err.message ? err.message : String(err || 'Unknown error'),
|
|
546
|
+
time: new Date()
|
|
547
|
+
}]);
|
|
548
|
+
})
|
|
549
|
+
.finally(() => {
|
|
550
|
+
setInput(prev => {
|
|
551
|
+
if (prev === `${inputBeforePaste}v` || prev === `${inputBeforePaste}V`) {
|
|
552
|
+
return inputBeforePaste;
|
|
553
|
+
}
|
|
554
|
+
return removePasteArtifact(prev);
|
|
555
|
+
});
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
|
|
141
561
|
const currentShowSuggestions = currentInput.startsWith('/') && !currentInput.includes(' ');
|
|
142
562
|
|
|
143
563
|
if (currentShowSuggestions) {
|
|
@@ -160,10 +580,20 @@ async function createChatUI(options) {
|
|
|
160
580
|
});
|
|
161
581
|
|
|
162
582
|
const handleSubmit = (value) => {
|
|
163
|
-
const text = value.trim();
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
583
|
+
const text = normalizeInputText(value).trim();
|
|
584
|
+
const images = pendingImagesRef.current;
|
|
585
|
+
const imagePrefix = normalizeInputText(pendingImagePrefixRef.current).trim();
|
|
586
|
+
const imageLabels = images.map((_, index) => `[Image #${index + 1}]`).join(' ');
|
|
587
|
+
const pasted = pendingPasteRef.current;
|
|
588
|
+
const pastePrefix = normalizeInputText(pendingPastePrefixRef.current).trim();
|
|
589
|
+
const submittedText = pasted
|
|
590
|
+
? [pastePrefix, pasted.text, text].filter(Boolean).join('\n\n')
|
|
591
|
+
: images.length > 0
|
|
592
|
+
? [imagePrefix, imageLabels, text].filter(Boolean).join('\n\n')
|
|
593
|
+
: text;
|
|
594
|
+
if (!submittedText && images.length === 0) return;
|
|
595
|
+
|
|
596
|
+
if (!pasted && images.length === 0 && showSuggestions && suggestions.length > 0) {
|
|
167
597
|
const picked = suggestions[selectedIndex];
|
|
168
598
|
if (picked && text !== picked.cmd) {
|
|
169
599
|
setInput(picked.cmd + ' ');
|
|
@@ -172,46 +602,97 @@ async function createChatUI(options) {
|
|
|
172
602
|
}
|
|
173
603
|
|
|
174
604
|
setInput('');
|
|
175
|
-
|
|
605
|
+
setPendingImages([]);
|
|
606
|
+
setPendingImagePrefix('');
|
|
607
|
+
setPendingPaste(null);
|
|
608
|
+
setPendingPastePrefix('');
|
|
609
|
+
pendingImagesRef.current = [];
|
|
610
|
+
pendingImagePrefixRef.current = '';
|
|
611
|
+
pendingPasteRef.current = null;
|
|
612
|
+
pendingPastePrefixRef.current = '';
|
|
613
|
+
onSubmit(submittedText, { images, pasted });
|
|
176
614
|
};
|
|
177
615
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
616
|
+
const handleInputChange = (value) => {
|
|
617
|
+
if (shouldStoreAsPastedContent(value)) {
|
|
618
|
+
const normalized = normalizeInputText(value);
|
|
619
|
+
const previous = normalizeInputText(inputRef.current).trim();
|
|
620
|
+
setPendingPaste({ text: normalized, label: `[Pasted Content ${normalized.length} chars]` });
|
|
621
|
+
setPendingPastePrefix(previous);
|
|
622
|
+
setInput('');
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
186
625
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
nameColor = msg.labelColor || 'blueBright';
|
|
626
|
+
const normalizedValue = normalizeInputText(value);
|
|
627
|
+
if (suppressPasteCharRef.current) {
|
|
628
|
+
suppressPasteCharRef.current = false;
|
|
629
|
+
const previous = inputRef.current;
|
|
630
|
+
if (normalizedValue === `${previous}v` || normalizedValue === `${previous}V`) {
|
|
631
|
+
setInput(previous);
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
if (normalizedValue.length > previous.length && /^[vV]$/.test(normalizedValue.slice(previous.length))) {
|
|
635
|
+
setInput(previous);
|
|
636
|
+
return;
|
|
199
637
|
}
|
|
638
|
+
}
|
|
639
|
+
setInput(normalizedValue);
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
const renderMessage = (msg, index, keyPrefix = 'msg') => {
|
|
643
|
+
if (msg.isThought) {
|
|
644
|
+
return h(Box, { key: `${keyPrefix}-${index}`, flexDirection: 'row', marginBottom: 0, paddingLeft: 2 },
|
|
645
|
+
h(Text, { color: 'gray', dimColor: true }, `Thinking: ${msg.text}`)
|
|
646
|
+
);
|
|
647
|
+
}
|
|
200
648
|
|
|
201
|
-
|
|
649
|
+
if (msg.isActivity) {
|
|
650
|
+
return h(Box, { key: `${keyPrefix}-${index}`, flexDirection: 'column', marginBottom: 0 },
|
|
202
651
|
h(Box, null,
|
|
203
|
-
h(Text, {
|
|
204
|
-
h(Text, { color: '
|
|
652
|
+
h(Text, { color: 'greenBright' }, '• '),
|
|
653
|
+
h(Text, { bold: true, color: msg.labelColor || 'blueBright' }, msg.activityTitle || msg.label || 'Activity')
|
|
205
654
|
),
|
|
206
655
|
h(Box, { paddingLeft: 2, marginBottom: 1 },
|
|
207
|
-
h(Text,
|
|
656
|
+
h(Text, { color: 'gray' }, '└ '),
|
|
657
|
+
h(Text, { color: 'cyanBright', wrap: 'wrap' }, msg.activityDetail || msg.text)
|
|
208
658
|
)
|
|
209
659
|
);
|
|
210
|
-
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
let name = 'Mint';
|
|
663
|
+
let nameColor = 'greenBright';
|
|
664
|
+
|
|
665
|
+
if (msg.role === 'user') {
|
|
666
|
+
name = 'You';
|
|
667
|
+
nameColor = 'cyanBright';
|
|
668
|
+
} else if (msg.role === 'error') {
|
|
669
|
+
name = 'Error';
|
|
670
|
+
nameColor = 'redBright';
|
|
671
|
+
} else if (msg.role === 'system') {
|
|
672
|
+
name = msg.label || 'System';
|
|
673
|
+
nameColor = msg.labelColor || 'blueBright';
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
return h(Box, { key: `${keyPrefix}-${index}`, flexDirection: 'column', marginBottom: 0 },
|
|
677
|
+
h(Box, null,
|
|
678
|
+
h(Text, { bold: true, color: nameColor }, name),
|
|
679
|
+
h(Text, { color: 'gray' }, ` ${msg.time instanceof Date ? msg.time.toLocaleTimeString() : ''}`)
|
|
680
|
+
),
|
|
681
|
+
h(Box, { paddingLeft: 2, marginBottom: 1 },
|
|
682
|
+
h(Text, { wrap: 'wrap' }, cleanDisplayText(msg.text, msg.role))
|
|
683
|
+
)
|
|
684
|
+
);
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
return h(Box, { flexDirection: 'column', paddingX: 1, width: '100%' },
|
|
688
|
+
// Static History: Messages
|
|
689
|
+
h(Static, { items: history }, (msg, index) => renderMessage(msg, index, 'history')),
|
|
690
|
+
liveAssistant && renderMessage(liveAssistant, 'live', 'live'),
|
|
211
691
|
|
|
212
692
|
// Floating (Persistent) UI part
|
|
213
693
|
h(Box, { flexDirection: 'column' },
|
|
214
|
-
thinking && h(Box, { marginBottom: 1 },
|
|
694
|
+
thinking && h(Box, { flexDirection: 'column', marginBottom: 1 },
|
|
695
|
+
h(Text, { color: 'gray', dimColor: true }, `─ Working for ${formatDuration(workingSeconds)} ─────────────────────────────────────────────────────────`),
|
|
215
696
|
h(Text, { color: 'yellow' }, '● Mint is thinking...')
|
|
216
697
|
),
|
|
217
698
|
|
|
@@ -232,21 +713,64 @@ async function createChatUI(options) {
|
|
|
232
713
|
))
|
|
233
714
|
),
|
|
234
715
|
|
|
716
|
+
pendingApproval && h(Box, {
|
|
717
|
+
flexDirection: 'column',
|
|
718
|
+
borderStyle: 'single',
|
|
719
|
+
borderColor: 'yellow',
|
|
720
|
+
paddingX: 1,
|
|
721
|
+
marginBottom: 1
|
|
722
|
+
},
|
|
723
|
+
h(Box, null,
|
|
724
|
+
h(Text, { bold: true, color: 'yellow' }, 'Approval '),
|
|
725
|
+
h(Text, { color: 'gray' }, `[${pendingApproval.type}] `),
|
|
726
|
+
h(Text, { color: 'white' }, pendingApproval.label)
|
|
727
|
+
),
|
|
728
|
+
pendingApproval.preview && pendingApproval.preview !== pendingApproval.label && h(Box, null,
|
|
729
|
+
h(Text, { color: 'gray' }, pendingApproval.preview)
|
|
730
|
+
),
|
|
731
|
+
h(Box, null,
|
|
732
|
+
h(Text, {
|
|
733
|
+
color: approvalChoice === 'approve' ? 'black' : 'greenBright',
|
|
734
|
+
backgroundColor: approvalChoice === 'approve' ? 'greenBright' : undefined,
|
|
735
|
+
bold: true
|
|
736
|
+
}, ' Approve '),
|
|
737
|
+
h(Text, { color: 'gray' }, ' '),
|
|
738
|
+
h(Text, {
|
|
739
|
+
color: approvalChoice === 'deny' ? 'white' : 'redBright',
|
|
740
|
+
backgroundColor: approvalChoice === 'deny' ? 'redBright' : undefined,
|
|
741
|
+
bold: true
|
|
742
|
+
}, ' Deny '),
|
|
743
|
+
h(Text, { color: 'gray' }, ' Tab/←/→ Enter')
|
|
744
|
+
)
|
|
745
|
+
),
|
|
746
|
+
|
|
235
747
|
// Compact Input Area
|
|
236
|
-
h(Box, { borderStyle: 'round', borderColor: 'greenBright', paddingX: 1, flexDirection: '
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
748
|
+
h(Box, { borderStyle: 'round', borderColor: pendingApproval ? 'gray' : 'greenBright', paddingX: 1, flexDirection: 'column' },
|
|
749
|
+
pendingImages.length > 0 && h(Box, null,
|
|
750
|
+
pendingImagePrefix && h(Text, { color: 'cyanBright' }, '[Text before] '),
|
|
751
|
+
h(Text, { color: 'greenBright' }, pendingImages.map((_, index) => `[Image #${index + 1}]`).join(' ') + ' '),
|
|
752
|
+
h(Text, { color: 'gray' }, 'Enter to send, Ctrl+Backspace remove, Esc clear')
|
|
753
|
+
),
|
|
754
|
+
pendingPaste && h(Box, null,
|
|
755
|
+
pendingPastePrefix && h(Text, { color: 'cyanBright' }, '[Text before] '),
|
|
756
|
+
h(Text, { color: 'yellowBright' }, pendingPaste.label),
|
|
757
|
+
h(Text, { color: 'gray' }, ' Enter to send, Esc clear')
|
|
758
|
+
),
|
|
759
|
+
h(Box, { flexDirection: 'row' },
|
|
760
|
+
h(Text, { bold: true, color: 'greenBright' }, '› '),
|
|
761
|
+
h(TextInput, {
|
|
762
|
+
value: input,
|
|
763
|
+
onChange: pendingApproval ? () => {} : handleInputChange,
|
|
764
|
+
onSubmit: pendingApproval ? () => {} : handleSubmit,
|
|
765
|
+
placeholder: pendingApproval ? 'Approval pending...' : 'Ask anything...'
|
|
766
|
+
})
|
|
767
|
+
)
|
|
244
768
|
),
|
|
245
769
|
|
|
246
770
|
// Status Bar
|
|
247
771
|
h(Box, { justifyContent: 'space-between' },
|
|
248
772
|
h(Box, null,
|
|
249
|
-
h(Text, { color: 'cyan' }, `[${mode}] `),
|
|
773
|
+
h(Text, { color: 'cyan' }, `[${fastMode ? 'Fast' : mode}] `),
|
|
250
774
|
h(Text, { color: 'magentaBright' }, (model || config.geminiModel || 'gemini').slice(0, 46))
|
|
251
775
|
),
|
|
252
776
|
h(Box, null,
|
|
@@ -265,30 +789,37 @@ async function createChatUI(options) {
|
|
|
265
789
|
console.log(`\x1b[90mType naturally to chat. Esc to exit.\x1b[0m\n`);
|
|
266
790
|
|
|
267
791
|
const ref = createRef();
|
|
268
|
-
render(h(App, { ref, ...options }));
|
|
792
|
+
render(h(App, { ref, ...options }), { exitOnCtrlC: false });
|
|
269
793
|
|
|
270
794
|
return {
|
|
271
795
|
appendMessage: (role, text, metadata) => ref.current?.appendMessage(role, text, metadata),
|
|
272
|
-
setThinking: (val) => ref.current?.setThinking(val),
|
|
796
|
+
setThinking: (val, seconds) => ref.current?.setThinking(val, seconds),
|
|
273
797
|
setMode: (val) => ref.current?.setMode(val),
|
|
798
|
+
setFastMode: (val) => ref.current?.setFastMode(val),
|
|
799
|
+
toggleFastMode: () => ref.current?.toggleFastMode(),
|
|
800
|
+
getFastMode: () => ref.current?.getFastMode(),
|
|
801
|
+
setInputText: (val) => ref.current?.setInputText(val),
|
|
802
|
+
setPendingPasteText: (text) => ref.current?.setPendingPasteText(text),
|
|
274
803
|
updateStatusModel: (val) => ref.current?.updateStatusModel(val),
|
|
275
804
|
updateWorkspace: (val) => ref.current?.updateWorkspace(val),
|
|
805
|
+
attachImage: (image) => ref.current?.attachImage(image),
|
|
276
806
|
appendCodeStep: (info) => ref.current?.appendCodeStep(info),
|
|
277
|
-
streamMessage: () => {
|
|
278
|
-
|
|
807
|
+
streamMessage: (metadata = {}) => {
|
|
808
|
+
ref.current?.beginAssistantStream(metadata);
|
|
279
809
|
return {
|
|
280
810
|
appendChunk: (chunk) => {
|
|
281
|
-
|
|
811
|
+
if (!String(chunk || '').trim()) return;
|
|
812
|
+
ref.current?.appendAssistantStreamChunk(chunk);
|
|
282
813
|
},
|
|
283
814
|
finalize: () => {
|
|
284
|
-
ref.current?.
|
|
815
|
+
ref.current?.finalizeAssistantStream();
|
|
285
816
|
}
|
|
286
817
|
};
|
|
287
818
|
},
|
|
288
819
|
copyLastResponse: () => false,
|
|
289
|
-
requestApproval: () => Promise.resolve(
|
|
820
|
+
requestApproval: (request) => ref.current?.requestApproval(request) || Promise.resolve(false),
|
|
290
821
|
askUser: () => Promise.resolve('')
|
|
291
822
|
};
|
|
292
823
|
}
|
|
293
824
|
|
|
294
|
-
module.exports = { createChatUI };
|
|
825
|
+
module.exports = { createChatUI, _helpers: { cleanDisplayText, stripInlineMarkdown, compactPathLabel, formatActivityStep, formatDuration, shouldAppendMessage } };
|