@pheem49/mint 1.5.3 → 1.5.5
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/GUIDE_TH.md +16 -4
- package/README.md +17 -1
- package/install.ps1 +64 -0
- package/install.sh +54 -0
- package/package.json +8 -3
- package/preload.js +2 -0
- package/src/AI_Brain/Gemini_API.js +71 -24
- package/src/AI_Brain/agent_orchestrator.js +1 -1
- package/src/AI_Brain/provider_adapter.js +8 -1
- package/src/Automation_Layer/file_operations.js +17 -5
- package/src/CLI/chat_ui.js +17 -4
- package/src/CLI/cli_colors.js +1 -1
- package/src/CLI/code_agent.js +35 -1
- package/src/CLI/interactive_chat.js +22 -5
- package/src/System/config_manager.js +1 -0
- package/src/System/ipc_handlers.js +85 -1
- package/src/System/smart_context.js +227 -0
- package/src/UI/preload-spotlight.js +1 -0
- package/src/UI/renderer.js +380 -21
- package/src/UI/settings.js +1 -0
- package/src/UI/spotlight.js +13 -9
- package/src/UI/styles.css +303 -4
package/src/UI/renderer.js
CHANGED
|
@@ -22,6 +22,7 @@ const chatProviderSelect = document.getElementById('chat-provider-select');
|
|
|
22
22
|
const imagePreviewContainer = document.getElementById('image-preview-container');
|
|
23
23
|
const imagePreview = document.getElementById('image-preview');
|
|
24
24
|
const removeImageBtn = document.getElementById('remove-image-btn');
|
|
25
|
+
const agentModeToggle = document.getElementById('agent-mode-toggle');
|
|
25
26
|
const modelMount = document.getElementById('model-mount');
|
|
26
27
|
const modelShell = document.getElementById('model-shell');
|
|
27
28
|
const modelStatus = document.getElementById('model-status');
|
|
@@ -73,6 +74,12 @@ function buildProviderPicker(settings = currentSettings) {
|
|
|
73
74
|
chatProviderSelect.value = settings.aiProvider || 'gemini';
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
function syncAgentModeToggle(settings = currentSettings) {
|
|
78
|
+
if (!agentModeToggle) return;
|
|
79
|
+
agentModeToggle.checked = settings.assistantMode === 'agent';
|
|
80
|
+
agentModeToggle.closest('.smart-context-control')?.classList.toggle('is-active', agentModeToggle.checked);
|
|
81
|
+
}
|
|
82
|
+
|
|
76
83
|
async function changeChatProvider(provider) {
|
|
77
84
|
if (!PROVIDER_PICKER_OPTIONS.some(([value]) => value === provider)) return;
|
|
78
85
|
const nextSettings = { ...currentSettings, aiProvider: provider };
|
|
@@ -222,9 +229,11 @@ async function loadTheme() {
|
|
|
222
229
|
ttsSpeed = config.ttsSpeed !== undefined ? config.ttsSpeed : 1.0;
|
|
223
230
|
ttsPitch = config.ttsPitch !== undefined ? config.ttsPitch : 1.0;
|
|
224
231
|
buildProviderPicker(currentSettings);
|
|
232
|
+
syncAgentModeToggle(currentSettings);
|
|
225
233
|
} catch (e) {
|
|
226
234
|
applyTheme('dark', '#8b5cf6', '#f8fafc');
|
|
227
235
|
buildProviderPicker(currentSettings);
|
|
236
|
+
syncAgentModeToggle(currentSettings);
|
|
228
237
|
}
|
|
229
238
|
}
|
|
230
239
|
|
|
@@ -248,12 +257,35 @@ window.api.onSettingsChanged((config) => {
|
|
|
248
257
|
ttsSpeed = config.ttsSpeed !== undefined ? config.ttsSpeed : 1.0;
|
|
249
258
|
ttsPitch = config.ttsPitch !== undefined ? config.ttsPitch : 1.0;
|
|
250
259
|
buildProviderPicker(currentSettings);
|
|
260
|
+
syncAgentModeToggle(currentSettings);
|
|
251
261
|
});
|
|
252
262
|
|
|
253
263
|
chatProviderSelect?.addEventListener('change', (event) => {
|
|
254
264
|
changeChatProvider(event.target.value);
|
|
255
265
|
});
|
|
256
266
|
|
|
267
|
+
agentModeToggle?.addEventListener('change', async () => {
|
|
268
|
+
const nextSettings = {
|
|
269
|
+
...currentSettings,
|
|
270
|
+
assistantMode: agentModeToggle.checked ? 'agent' : 'chat'
|
|
271
|
+
};
|
|
272
|
+
agentModeToggle.disabled = true;
|
|
273
|
+
try {
|
|
274
|
+
const result = await window.api.saveSettings(nextSettings);
|
|
275
|
+
if (!result || result.success !== false) {
|
|
276
|
+
currentSettings = nextSettings;
|
|
277
|
+
} else {
|
|
278
|
+
throw new Error(result.message || 'Unable to save assistant mode');
|
|
279
|
+
}
|
|
280
|
+
} catch (error) {
|
|
281
|
+
console.error('Failed to change assistant mode:', error);
|
|
282
|
+
setMintActivity('error');
|
|
283
|
+
} finally {
|
|
284
|
+
syncAgentModeToggle(currentSettings);
|
|
285
|
+
agentModeToggle.disabled = false;
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
257
289
|
// --- Voice Input Setup ---
|
|
258
290
|
let mediaRecorder = null;
|
|
259
291
|
let audioChunks = [];
|
|
@@ -468,7 +500,9 @@ async function sendVoiceMessage(base64Audio) {
|
|
|
468
500
|
await speakText(normalizeAiText(response.response), { onEnd: resumeSpeechIfNeeded });
|
|
469
501
|
notifyAiIfNeeded();
|
|
470
502
|
|
|
471
|
-
if (response.
|
|
503
|
+
if (response.approval?.required) {
|
|
504
|
+
appendApprovalCard(msgDiv, response.approval);
|
|
505
|
+
} else if (response.action && response.action.type !== 'none') {
|
|
472
506
|
appendActionCard(msgDiv, response.action);
|
|
473
507
|
}
|
|
474
508
|
} catch (error) {
|
|
@@ -799,6 +833,125 @@ function formatTime(isoString) {
|
|
|
799
833
|
}
|
|
800
834
|
}
|
|
801
835
|
|
|
836
|
+
function compactSmartContext(context) {
|
|
837
|
+
if (!context || typeof context !== 'object') return null;
|
|
838
|
+
const activeWindow = context.activeWindow || {};
|
|
839
|
+
const currentApp = context.currentApp || {};
|
|
840
|
+
const browser = context.browser || null;
|
|
841
|
+
return {
|
|
842
|
+
capturedAt: context.capturedAt,
|
|
843
|
+
platform: context.platform,
|
|
844
|
+
currentApp: currentApp.name || activeWindow.appName || activeWindow.processName || '',
|
|
845
|
+
processName: currentApp.processName || activeWindow.processName || '',
|
|
846
|
+
pid: currentApp.pid || activeWindow.pid || null,
|
|
847
|
+
activeWindowTitle: activeWindow.title || '',
|
|
848
|
+
browser: browser ? {
|
|
849
|
+
title: browser.title || '',
|
|
850
|
+
url: browser.url || '',
|
|
851
|
+
urlUnavailableReason: browser.urlUnavailableReason || ''
|
|
852
|
+
} : null,
|
|
853
|
+
selectedText: context.selectedText || '',
|
|
854
|
+
clipboardText: context.clipboardText || ''
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
function appendSmartContextToMessage(message, context) {
|
|
859
|
+
const compact = compactSmartContext(context);
|
|
860
|
+
if (!compact) return message;
|
|
861
|
+
return [
|
|
862
|
+
message,
|
|
863
|
+
'',
|
|
864
|
+
'[SMART_CONTEXT]',
|
|
865
|
+
'Use this structured desktop context together with the attached screenshot. Do not mention it unless it helps answer the user.',
|
|
866
|
+
JSON.stringify(compact, null, 2),
|
|
867
|
+
'[/SMART_CONTEXT]'
|
|
868
|
+
].join('\n');
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
function shouldShowAgentActivity(options = {}) {
|
|
872
|
+
return options.showAgentActivity !== false && currentSettings.assistantMode === 'agent';
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
function createAgentActivityCard() {
|
|
876
|
+
const messageDiv = document.createElement('div');
|
|
877
|
+
messageDiv.classList.add('message', 'ai-message', 'agent-activity-message');
|
|
878
|
+
|
|
879
|
+
const bubble = document.createElement('div');
|
|
880
|
+
bubble.classList.add('message-bubble', 'agent-activity-card');
|
|
881
|
+
|
|
882
|
+
const header = document.createElement('div');
|
|
883
|
+
header.className = 'agent-activity-header';
|
|
884
|
+
const title = document.createElement('span');
|
|
885
|
+
title.textContent = 'Agent Activity';
|
|
886
|
+
const status = document.createElement('span');
|
|
887
|
+
status.className = 'agent-activity-status';
|
|
888
|
+
status.textContent = 'Running';
|
|
889
|
+
header.appendChild(title);
|
|
890
|
+
header.appendChild(status);
|
|
891
|
+
|
|
892
|
+
const list = document.createElement('div');
|
|
893
|
+
list.className = 'agent-activity-list';
|
|
894
|
+
|
|
895
|
+
bubble.appendChild(header);
|
|
896
|
+
bubble.appendChild(list);
|
|
897
|
+
messageDiv.appendChild(bubble);
|
|
898
|
+
chatContainer.appendChild(messageDiv);
|
|
899
|
+
scrollToBottom();
|
|
900
|
+
|
|
901
|
+
return {
|
|
902
|
+
element: messageDiv,
|
|
903
|
+
list,
|
|
904
|
+
status,
|
|
905
|
+
add(label, state = 'running', detail = '') {
|
|
906
|
+
const item = document.createElement('div');
|
|
907
|
+
item.className = 'agent-activity-item';
|
|
908
|
+
item.dataset.state = state;
|
|
909
|
+
|
|
910
|
+
const dot = document.createElement('span');
|
|
911
|
+
dot.className = 'agent-activity-dot';
|
|
912
|
+
|
|
913
|
+
const content = document.createElement('span');
|
|
914
|
+
content.className = 'agent-activity-text';
|
|
915
|
+
content.textContent = detail ? `${label}: ${detail}` : label;
|
|
916
|
+
|
|
917
|
+
item.appendChild(dot);
|
|
918
|
+
item.appendChild(content);
|
|
919
|
+
list.appendChild(item);
|
|
920
|
+
scrollToBottom();
|
|
921
|
+
return item;
|
|
922
|
+
},
|
|
923
|
+
update(item, state, label, detail = '') {
|
|
924
|
+
if (!item) return;
|
|
925
|
+
item.dataset.state = state;
|
|
926
|
+
const content = item.querySelector('.agent-activity-text');
|
|
927
|
+
if (content && label) {
|
|
928
|
+
content.textContent = detail ? `${label}: ${detail}` : label;
|
|
929
|
+
}
|
|
930
|
+
},
|
|
931
|
+
finish(state = 'done', label = 'Done') {
|
|
932
|
+
status.textContent = label;
|
|
933
|
+
status.dataset.state = state;
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
function describeSmartContextActivity(context, hasScreenshot) {
|
|
939
|
+
const compact = compactSmartContext(context) || {};
|
|
940
|
+
const parts = [];
|
|
941
|
+
if (hasScreenshot) parts.push('screen');
|
|
942
|
+
if (compact.currentApp) parts.push(compact.currentApp);
|
|
943
|
+
if (compact.activeWindowTitle) parts.push(compact.activeWindowTitle);
|
|
944
|
+
if (compact.selectedText) parts.push('selected text');
|
|
945
|
+
if (compact.clipboardText) parts.push('clipboard');
|
|
946
|
+
return parts.slice(0, 3).join(' · ') || 'desktop context';
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
function describeActionActivity(action) {
|
|
950
|
+
if (!action || action.type === 'none') return 'No desktop action';
|
|
951
|
+
const meta = getActionCardMeta(action);
|
|
952
|
+
return meta.detail ? `${meta.title} · ${meta.detail}` : meta.title;
|
|
953
|
+
}
|
|
954
|
+
|
|
802
955
|
// Clear chat history
|
|
803
956
|
async function clearChatHistory(confirmMessage = 'Clear current chat history?') {
|
|
804
957
|
const shouldClear = window.confirm(confirmMessage);
|
|
@@ -1143,29 +1296,193 @@ function autoChunkAiText(text) {
|
|
|
1143
1296
|
}
|
|
1144
1297
|
|
|
1145
1298
|
function appendActionCard(messageDiv, action) {
|
|
1299
|
+
if (!messageDiv || !action || action.type === 'none') return;
|
|
1300
|
+
|
|
1301
|
+
const meta = getActionCardMeta(action);
|
|
1146
1302
|
const card = document.createElement('div');
|
|
1147
1303
|
card.classList.add('action-card');
|
|
1304
|
+
card.dataset.actionType = action.type || 'unknown';
|
|
1148
1305
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1306
|
+
const icon = document.createElement('span');
|
|
1307
|
+
icon.className = 'action-card-icon';
|
|
1308
|
+
icon.textContent = meta.icon;
|
|
1309
|
+
|
|
1310
|
+
const content = document.createElement('div');
|
|
1311
|
+
content.className = 'action-card-content';
|
|
1312
|
+
|
|
1313
|
+
const title = document.createElement('div');
|
|
1314
|
+
title.className = 'action-card-title';
|
|
1315
|
+
title.textContent = meta.title;
|
|
1316
|
+
content.appendChild(title);
|
|
1317
|
+
|
|
1318
|
+
if (meta.detail) {
|
|
1319
|
+
const detail = document.createElement('div');
|
|
1320
|
+
detail.className = 'action-card-detail';
|
|
1321
|
+
detail.textContent = meta.detail;
|
|
1322
|
+
content.appendChild(detail);
|
|
1163
1323
|
}
|
|
1164
1324
|
|
|
1165
|
-
card.
|
|
1325
|
+
card.appendChild(icon);
|
|
1326
|
+
card.appendChild(content);
|
|
1327
|
+
messageDiv.querySelector('.message-bubble')?.appendChild(card);
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
function getActionCardMeta(action) {
|
|
1331
|
+
const target = formatActionTarget(action);
|
|
1332
|
+
const type = action?.type || 'unknown';
|
|
1333
|
+
const targetOrFallback = target || 'No target';
|
|
1334
|
+
|
|
1335
|
+
const map = {
|
|
1336
|
+
open_url: ['🌐', 'Opened URL', target],
|
|
1337
|
+
search: ['🔍', 'Searched the web', target],
|
|
1338
|
+
open_app: ['🚀', 'Launched app', target],
|
|
1339
|
+
web_automation: ['🧭', 'Ran browser automation', target],
|
|
1340
|
+
create_folder: ['📁', 'Created folder', target],
|
|
1341
|
+
open_file: ['📄', 'Opened file', target],
|
|
1342
|
+
open_folder: ['📂', 'Opened folder', target],
|
|
1343
|
+
delete_file: ['🗑️', 'Deleted file', target],
|
|
1344
|
+
find_path: ['🔎', action.openAfter ? 'Found and opened path' : 'Found path', buildFindPathDetail(action)],
|
|
1345
|
+
clipboard_write: ['📋', 'Updated clipboard', target],
|
|
1346
|
+
learn_file: ['📚', 'Indexed file', target],
|
|
1347
|
+
learn_folder: ['📚', 'Indexed folder', target],
|
|
1348
|
+
system_info: ['💻', target ? 'Checked weather' : 'Checked system info', target],
|
|
1349
|
+
plugin: ['🔌', 'Ran plugin', target],
|
|
1350
|
+
mcp_tool: ['🧩', 'Called MCP tool', target],
|
|
1351
|
+
mouse_move: ['↗', 'Moved pointer', target],
|
|
1352
|
+
mouse_click: ['☝', 'Clicked screen', buildMouseDetail(action)],
|
|
1353
|
+
type_text: ['⌨', 'Typed text', target],
|
|
1354
|
+
key_tap: ['⌨', 'Pressed key', target],
|
|
1355
|
+
system_automation: ['⚙', 'Changed system setting', target]
|
|
1356
|
+
};
|
|
1357
|
+
|
|
1358
|
+
const [icon, title, detail] = map[type] || ['⚡', `Ran action: ${type}`, targetOrFallback];
|
|
1359
|
+
return { icon, title, detail };
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
function buildFindPathDetail(action) {
|
|
1363
|
+
const target = formatActionTarget(action);
|
|
1364
|
+
const typeLabel = action.pathType && action.pathType !== 'any' ? ` (${action.pathType})` : '';
|
|
1365
|
+
return target ? `${target}${typeLabel}` : typeLabel.trim();
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
function buildMouseDetail(action) {
|
|
1369
|
+
const point = formatActionTarget(action);
|
|
1370
|
+
const button = action.button ? `button ${action.button}` : 'left button';
|
|
1371
|
+
return point ? `${point} · ${button}` : button;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
function formatActionTarget(action) {
|
|
1375
|
+
if (!action || typeof action !== 'object') return '';
|
|
1376
|
+
if (action.server && action.target) return `${action.server}:${action.target}`;
|
|
1377
|
+
if (action.pluginName) return `${action.pluginName} ${action.target || ''}`.trim();
|
|
1378
|
+
if (action.target) return String(action.target);
|
|
1379
|
+
if (Number.isFinite(action.x) && Number.isFinite(action.y)) return `${action.x}, ${action.y}`;
|
|
1380
|
+
return '';
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
function getApprovalCopy(approval) {
|
|
1384
|
+
const action = approval?.action || {};
|
|
1385
|
+
const actionType = action.type || 'unknown';
|
|
1386
|
+
const target = formatActionTarget(action);
|
|
1387
|
+
const isDangerous = approval?.tier === 'dangerous';
|
|
1388
|
+
return {
|
|
1389
|
+
title: isDangerous ? 'Dangerous action requires approval' : 'Action requires approval',
|
|
1390
|
+
body: target ? `${actionType}: ${target}` : actionType,
|
|
1391
|
+
reason: approval?.reason || 'This action needs your permission before Mint can run it.',
|
|
1392
|
+
approveLabel: isDangerous ? 'Allow Dangerous Action' : 'Allow Action'
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
function appendApprovalCard(messageDiv, approval, activity = null) {
|
|
1397
|
+
if (!messageDiv || !approval?.action || !window.api?.executeApprovedAction) return;
|
|
1398
|
+
|
|
1399
|
+
const copy = getApprovalCopy(approval);
|
|
1400
|
+
const card = document.createElement('div');
|
|
1401
|
+
card.classList.add('action-card', 'approval-card');
|
|
1402
|
+
card.dataset.tier = approval.tier || 'approval';
|
|
1403
|
+
|
|
1404
|
+
const content = document.createElement('div');
|
|
1405
|
+
content.className = 'approval-card-content';
|
|
1166
1406
|
|
|
1167
|
-
|
|
1168
|
-
|
|
1407
|
+
const title = document.createElement('div');
|
|
1408
|
+
title.className = 'approval-card-title';
|
|
1409
|
+
title.textContent = copy.title;
|
|
1410
|
+
|
|
1411
|
+
const body = document.createElement('div');
|
|
1412
|
+
body.className = 'approval-card-body';
|
|
1413
|
+
body.textContent = copy.body;
|
|
1414
|
+
|
|
1415
|
+
const reason = document.createElement('div');
|
|
1416
|
+
reason.className = 'approval-card-reason';
|
|
1417
|
+
reason.textContent = copy.reason;
|
|
1418
|
+
|
|
1419
|
+
content.appendChild(title);
|
|
1420
|
+
content.appendChild(body);
|
|
1421
|
+
content.appendChild(reason);
|
|
1422
|
+
|
|
1423
|
+
const actions = document.createElement('div');
|
|
1424
|
+
actions.className = 'approval-card-actions';
|
|
1425
|
+
|
|
1426
|
+
const approveBtn = document.createElement('button');
|
|
1427
|
+
approveBtn.type = 'button';
|
|
1428
|
+
approveBtn.className = 'approval-btn approval-btn-approve';
|
|
1429
|
+
approveBtn.textContent = copy.approveLabel;
|
|
1430
|
+
|
|
1431
|
+
const cancelBtn = document.createElement('button');
|
|
1432
|
+
cancelBtn.type = 'button';
|
|
1433
|
+
cancelBtn.className = 'approval-btn approval-btn-cancel';
|
|
1434
|
+
cancelBtn.textContent = 'Cancel';
|
|
1435
|
+
|
|
1436
|
+
const setDone = (message, state) => {
|
|
1437
|
+
approveBtn.disabled = true;
|
|
1438
|
+
cancelBtn.disabled = true;
|
|
1439
|
+
card.dataset.state = state;
|
|
1440
|
+
reason.textContent = message;
|
|
1441
|
+
};
|
|
1442
|
+
|
|
1443
|
+
approveBtn.addEventListener('click', async () => {
|
|
1444
|
+
approveBtn.disabled = true;
|
|
1445
|
+
cancelBtn.disabled = true;
|
|
1446
|
+
reason.textContent = 'Running approved action...';
|
|
1447
|
+
const runStep = activity?.add('Running approved action', 'running', describeActionActivity(approval.action));
|
|
1448
|
+
setMintActivity('thinking');
|
|
1449
|
+
|
|
1450
|
+
try {
|
|
1451
|
+
const result = await window.api.executeApprovedAction(approval.action);
|
|
1452
|
+
if (!result || result.success === false) {
|
|
1453
|
+
setDone(result?.message || 'Action failed.', 'error');
|
|
1454
|
+
activity?.update(runStep, 'error', 'Action failed', result?.message || '');
|
|
1455
|
+
activity?.finish('error', 'Failed');
|
|
1456
|
+
setMintActivity('error');
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
setDone(result.message || 'Action completed.', 'approved');
|
|
1461
|
+
activity?.update(runStep, 'done', 'Action completed', result.message || describeActionActivity(approval.action));
|
|
1462
|
+
activity?.finish('done', 'Completed');
|
|
1463
|
+
setMintActivity('idle');
|
|
1464
|
+
} catch (error) {
|
|
1465
|
+
console.error('[Approval] Failed to execute action:', error);
|
|
1466
|
+
setDone(error.message || 'Action failed.', 'error');
|
|
1467
|
+
activity?.update(runStep, 'error', 'Action failed', error.message || '');
|
|
1468
|
+
activity?.finish('error', 'Failed');
|
|
1469
|
+
setMintActivity('error');
|
|
1470
|
+
}
|
|
1471
|
+
});
|
|
1472
|
+
|
|
1473
|
+
cancelBtn.addEventListener('click', () => {
|
|
1474
|
+
setDone('Cancelled by user.', 'cancelled');
|
|
1475
|
+
activity?.add('Approval cancelled', 'cancelled');
|
|
1476
|
+
activity?.finish('cancelled', 'Cancelled');
|
|
1477
|
+
setMintActivity('idle');
|
|
1478
|
+
});
|
|
1479
|
+
|
|
1480
|
+
actions.appendChild(approveBtn);
|
|
1481
|
+
actions.appendChild(cancelBtn);
|
|
1482
|
+
card.appendChild(content);
|
|
1483
|
+
card.appendChild(actions);
|
|
1484
|
+
|
|
1485
|
+
messageDiv.querySelector('.message-bubble')?.appendChild(card);
|
|
1169
1486
|
}
|
|
1170
1487
|
|
|
1171
1488
|
function showTyping() {
|
|
@@ -1300,31 +1617,55 @@ async function sendTextMessage(text, options = {}) {
|
|
|
1300
1617
|
rememberConversationLanguage(displayText || cleanText);
|
|
1301
1618
|
}
|
|
1302
1619
|
|
|
1620
|
+
const activity = shouldShowAgentActivity(options) ? createAgentActivityCard() : null;
|
|
1621
|
+
const contextStep = activity?.add('Preparing desktop context', 'running');
|
|
1622
|
+
|
|
1303
1623
|
// Show typing early so user knows we are processing
|
|
1304
1624
|
showTyping();
|
|
1305
1625
|
setMintActivity('thinking');
|
|
1306
1626
|
|
|
1627
|
+
let messageToSend = cleanText;
|
|
1628
|
+
|
|
1307
1629
|
// Check Smart Context Toggle
|
|
1308
1630
|
const smartToggle = document.getElementById('smart-context-toggle');
|
|
1309
1631
|
if (allowSmartContext && smartToggle && smartToggle.checked && !imageToSend) {
|
|
1310
1632
|
try {
|
|
1311
|
-
const silentCapture = await
|
|
1633
|
+
const [silentCapture, smartContext] = await Promise.all([
|
|
1634
|
+
window.api.captureSilentScreen(),
|
|
1635
|
+
window.api.getSmartContext ? window.api.getSmartContext() : Promise.resolve(null)
|
|
1636
|
+
]);
|
|
1312
1637
|
if (silentCapture) {
|
|
1313
1638
|
// Set imageToSend so it gets sent to the API, but we already appended the chat bubble
|
|
1314
1639
|
imageToSend = silentCapture;
|
|
1315
1640
|
}
|
|
1641
|
+
if (smartContext) {
|
|
1642
|
+
messageToSend = appendSmartContextToMessage(cleanText, smartContext);
|
|
1643
|
+
}
|
|
1644
|
+
if (activity && contextStep) {
|
|
1645
|
+
activity.update(
|
|
1646
|
+
contextStep,
|
|
1647
|
+
'done',
|
|
1648
|
+
'Read Smart Context',
|
|
1649
|
+
describeSmartContextActivity(smartContext, Boolean(silentCapture))
|
|
1650
|
+
);
|
|
1651
|
+
}
|
|
1316
1652
|
} catch (err) {
|
|
1317
1653
|
console.error("Smart Context capture failed:", err);
|
|
1654
|
+
activity?.update(contextStep, 'error', 'Smart Context unavailable', err.message || '');
|
|
1318
1655
|
}
|
|
1656
|
+
} else if (activity && contextStep) {
|
|
1657
|
+
activity.update(contextStep, 'skipped', 'Smart Context skipped', imageToSend ? 'image already attached' : 'toggle is off');
|
|
1319
1658
|
}
|
|
1320
1659
|
|
|
1321
1660
|
// Hide proactive bar if user is actively typing a message
|
|
1322
1661
|
hideProactiveBar();
|
|
1662
|
+
const modelStep = activity?.add('Waiting for model response', 'running');
|
|
1323
1663
|
|
|
1324
1664
|
try {
|
|
1325
1665
|
// Send to main process (text, image, audio=null)
|
|
1326
|
-
const response = await window.api.sendMessage(
|
|
1666
|
+
const response = await window.api.sendMessage(messageToSend, imageToSend, null);
|
|
1327
1667
|
removeTyping();
|
|
1668
|
+
activity?.update(modelStep, 'done', 'Model response received');
|
|
1328
1669
|
|
|
1329
1670
|
if (typeof response.response !== 'string') {
|
|
1330
1671
|
response.response = normalizeAiText(response.response);
|
|
@@ -1332,6 +1673,7 @@ async function sendTextMessage(text, options = {}) {
|
|
|
1332
1673
|
|
|
1333
1674
|
// Handle system_info action: fetch data and append to AI message
|
|
1334
1675
|
if (response.action && response.action.type === 'system_info') {
|
|
1676
|
+
const infoStep = activity?.add('Running local info action', 'running', describeActionActivity(response.action));
|
|
1335
1677
|
const city = (response.action.target || '').trim();
|
|
1336
1678
|
// Only treat as weather if city looks like a real location name (not blank, not 'date', not 'time')
|
|
1337
1679
|
const weatherKeywords = ['date', 'time', 'วัน', 'เวลา', 'today', 'now'];
|
|
@@ -1341,12 +1683,14 @@ async function sendTextMessage(text, options = {}) {
|
|
|
1341
1683
|
// Weather query
|
|
1342
1684
|
const weather = await window.api.getWeather(city);
|
|
1343
1685
|
response.response += `\n\n🌡️ ${weather.data}`;
|
|
1686
|
+
activity?.update(infoStep, 'done', 'Weather info added', city);
|
|
1344
1687
|
} else {
|
|
1345
1688
|
// General system info (date, time, RAM, CPU)
|
|
1346
1689
|
const info = await window.api.getSystemInfo();
|
|
1347
1690
|
const machine = info.machine && info.machine.display ? `\n🖥️ รุ่นเครื่อง: ${info.machine.display}` : '';
|
|
1348
1691
|
const distro = info.distro ? `\nระบบ: ${info.distro}` : '';
|
|
1349
1692
|
response.response += `\n\n📅 วันนี้: ${info.date}\n⏰ เวลา: ${info.time}${machine}${distro}\n💻 CPU: ${info.cpu.model} (${info.cpu.cores} คอร์)\n💻 RAM: ${info.ram.used} / ${info.ram.total} (${info.ram.percent})`;
|
|
1693
|
+
activity?.update(infoStep, 'done', 'System info added');
|
|
1350
1694
|
}
|
|
1351
1695
|
}
|
|
1352
1696
|
|
|
@@ -1362,12 +1706,27 @@ async function sendTextMessage(text, options = {}) {
|
|
|
1362
1706
|
notifyAiIfNeeded();
|
|
1363
1707
|
|
|
1364
1708
|
// Append action card if applicable
|
|
1365
|
-
if (response.
|
|
1709
|
+
if (response.approval?.required) {
|
|
1710
|
+
activity?.add('Selected action', 'approval', describeActionActivity(response.approval.action));
|
|
1711
|
+
activity?.add('Waiting for approval', 'running', response.approval.reason || '');
|
|
1712
|
+
activity?.finish('waiting', 'Waiting');
|
|
1713
|
+
appendApprovalCard(msgDiv, response.approval, activity);
|
|
1714
|
+
} else if (response.action && response.action.type !== 'none' && response.action.type !== 'system_info') {
|
|
1715
|
+
activity?.add('Selected action', 'done', describeActionActivity(response.action));
|
|
1366
1716
|
appendActionCard(msgDiv, response.action);
|
|
1717
|
+
activity?.finish('done', 'Completed');
|
|
1718
|
+
} else if (response.action && response.action.type === 'system_info') {
|
|
1719
|
+
activity?.add('Selected action', 'done', describeActionActivity(response.action));
|
|
1720
|
+
activity?.finish('done', 'Completed');
|
|
1721
|
+
} else {
|
|
1722
|
+
activity?.add('No desktop action selected', 'done');
|
|
1723
|
+
activity?.finish('done', 'Completed');
|
|
1367
1724
|
}
|
|
1368
1725
|
} catch (error) {
|
|
1369
1726
|
removeTyping();
|
|
1370
1727
|
setMintActivity('error');
|
|
1728
|
+
activity?.update(modelStep, 'error', 'Model request failed', error.message || '');
|
|
1729
|
+
activity?.finish('error', 'Failed');
|
|
1371
1730
|
appendMessage("Sorry, I encountered an error communicating with the main process.", 'ai');
|
|
1372
1731
|
console.error(error);
|
|
1373
1732
|
resumeSpeechIfNeeded();
|
package/src/UI/settings.js
CHANGED
package/src/UI/spotlight.js
CHANGED
|
@@ -57,7 +57,7 @@ spotlightInput.addEventListener('input', () => {
|
|
|
57
57
|
label: `Result: ${result}`,
|
|
58
58
|
desc: 'Calculation result (Press Enter to copy)',
|
|
59
59
|
icon: '🧮',
|
|
60
|
-
action: { type: '
|
|
60
|
+
action: { type: 'clipboard_write', target: result.toString() }
|
|
61
61
|
}]);
|
|
62
62
|
return;
|
|
63
63
|
} catch {}
|
|
@@ -119,17 +119,21 @@ spotlightInput.addEventListener('keydown', (e) => {
|
|
|
119
119
|
}
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
-
function handleAction(action) {
|
|
122
|
+
async function handleAction(action) {
|
|
123
123
|
if (action.type === 'chat') {
|
|
124
124
|
window.spotlightAPI.submit(action.query);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (window.spotlightAPI.executeAction) {
|
|
129
|
+
const result = await window.spotlightAPI.executeAction(action);
|
|
130
|
+
if (!result || result.success === false) {
|
|
131
|
+
window.spotlightAPI.submit(`Spotlight action failed: ${result?.message || 'Unknown error'}`);
|
|
132
|
+
}
|
|
133
|
+
return;
|
|
132
134
|
}
|
|
135
|
+
|
|
136
|
+
window.spotlightAPI.submit(action.target || action.value || '');
|
|
133
137
|
}
|
|
134
138
|
|
|
135
139
|
// Auto-focus on show
|