@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/UI/renderer.js
CHANGED
|
@@ -11,6 +11,9 @@ const visionBtn = document.getElementById('vision-btn');
|
|
|
11
11
|
const imagePreviewContainer = document.getElementById('image-preview-container');
|
|
12
12
|
const imagePreview = document.getElementById('image-preview');
|
|
13
13
|
const removeImageBtn = document.getElementById('remove-image-btn');
|
|
14
|
+
const modelMount = document.getElementById('model-mount');
|
|
15
|
+
const modelShell = document.getElementById('model-shell');
|
|
16
|
+
const modelStatus = document.getElementById('model-status');
|
|
14
17
|
|
|
15
18
|
// Proactive Assistant elements
|
|
16
19
|
const proactiveBar = document.getElementById('proactive-bar');
|
|
@@ -24,6 +27,31 @@ let ttsProvider = 'google';
|
|
|
24
27
|
let ttsVolume = 1.0;
|
|
25
28
|
let ttsSpeed = 1.0;
|
|
26
29
|
let ttsPitch = 1.0;
|
|
30
|
+
let lastConversationLanguage = 'auto';
|
|
31
|
+
|
|
32
|
+
function detectConversationLanguage(text) {
|
|
33
|
+
const value = String(text || '');
|
|
34
|
+
if (/[\u0E00-\u0E7F]/.test(value)) return 'thai';
|
|
35
|
+
if (/[A-Za-z]/.test(value)) return 'english';
|
|
36
|
+
return 'auto';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function rememberConversationLanguage(text) {
|
|
40
|
+
const detected = detectConversationLanguage(text);
|
|
41
|
+
if (detected !== 'auto') {
|
|
42
|
+
lastConversationLanguage = detected;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function buildInteractionLanguageInstruction() {
|
|
47
|
+
if (lastConversationLanguage === 'thai') {
|
|
48
|
+
return 'Current conversation language: Thai. Reply in Thai. Do not reply in English just because this interaction instruction is written in English.';
|
|
49
|
+
}
|
|
50
|
+
if (lastConversationLanguage === 'english') {
|
|
51
|
+
return 'Current conversation language: English. Reply in English. Do not switch to Thai.';
|
|
52
|
+
}
|
|
53
|
+
return 'Infer the reply language from the recent conversation before this interaction instruction, not from the language of this instruction.';
|
|
54
|
+
}
|
|
27
55
|
|
|
28
56
|
// --- Theme Loading ---
|
|
29
57
|
function applyTheme(theme, accentColor, systemTextColor, config = {}) {
|
|
@@ -392,11 +420,17 @@ let currentAudioPlayer = null;
|
|
|
392
420
|
|
|
393
421
|
function speakText(text, options = {}) {
|
|
394
422
|
if (window.api && window.api.setAiState) window.api.setAiState('speaking');
|
|
395
|
-
const onEnd = typeof options.onEnd === 'function' ? options.onEnd :
|
|
423
|
+
const onEnd = typeof options.onEnd === 'function' ? options.onEnd : () => {};
|
|
424
|
+
|
|
425
|
+
const wrappedOnEnd = () => {
|
|
426
|
+
if (window.Live2DManager) Live2DManager.stopLipSync();
|
|
427
|
+
onEnd();
|
|
428
|
+
};
|
|
429
|
+
|
|
396
430
|
return new Promise(async (resolve) => {
|
|
397
431
|
if (!enableVoiceReply) {
|
|
398
432
|
if (window.api && window.api.setAiState) window.api.setAiState('idle');
|
|
399
|
-
|
|
433
|
+
wrappedOnEnd();
|
|
400
434
|
return resolve();
|
|
401
435
|
}
|
|
402
436
|
|
|
@@ -406,16 +440,20 @@ function speakText(text, options = {}) {
|
|
|
406
440
|
currentAudioPlayer.currentTime = 0;
|
|
407
441
|
currentAudioPlayer = null;
|
|
408
442
|
}
|
|
443
|
+
if (window.Live2DManager) Live2DManager.stopLipSync();
|
|
444
|
+
|
|
409
445
|
if ('speechSynthesis' in window) {
|
|
410
446
|
window.speechSynthesis.cancel();
|
|
411
447
|
}
|
|
412
448
|
|
|
413
449
|
if (!text || !text.trim()) {
|
|
414
450
|
if (window.api && window.api.setAiState) window.api.setAiState('idle');
|
|
415
|
-
|
|
451
|
+
wrappedOnEnd();
|
|
416
452
|
return resolve();
|
|
417
453
|
}
|
|
418
454
|
|
|
455
|
+
if (window.Live2DManager) Live2DManager.startLipSync();
|
|
456
|
+
|
|
419
457
|
try {
|
|
420
458
|
if (ttsProvider !== 'native') {
|
|
421
459
|
const urls = await window.api.getTtsUrls(text);
|
|
@@ -424,7 +462,7 @@ function speakText(text, options = {}) {
|
|
|
424
462
|
const playNext = () => {
|
|
425
463
|
if (i >= urls.length) {
|
|
426
464
|
if (window.api && window.api.setAiState) window.api.setAiState('idle');
|
|
427
|
-
|
|
465
|
+
wrappedOnEnd();
|
|
428
466
|
return resolve();
|
|
429
467
|
}
|
|
430
468
|
const audio = new Audio(urls[i].url);
|
|
@@ -443,7 +481,7 @@ function speakText(text, options = {}) {
|
|
|
443
481
|
};
|
|
444
482
|
audio.play().catch(e => {
|
|
445
483
|
console.error("Audio playback prevented:", e);
|
|
446
|
-
fallbackSpeak(text,
|
|
484
|
+
fallbackSpeak(text, wrappedOnEnd, resolve);
|
|
447
485
|
});
|
|
448
486
|
};
|
|
449
487
|
playNext();
|
|
@@ -455,7 +493,7 @@ function speakText(text, options = {}) {
|
|
|
455
493
|
}
|
|
456
494
|
|
|
457
495
|
// Fallback
|
|
458
|
-
fallbackSpeak(text,
|
|
496
|
+
fallbackSpeak(text, wrappedOnEnd, resolve);
|
|
459
497
|
});
|
|
460
498
|
}
|
|
461
499
|
|
|
@@ -756,14 +794,52 @@ function scrollToBottom() {
|
|
|
756
794
|
chatContainer.scrollTop = chatContainer.scrollHeight;
|
|
757
795
|
}
|
|
758
796
|
|
|
797
|
+
function loadScript(src) {
|
|
798
|
+
return new Promise((resolve, reject) => {
|
|
799
|
+
if (document.querySelector(`script[src="${src}"]`)) {
|
|
800
|
+
resolve();
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
const script = document.createElement('script');
|
|
804
|
+
script.src = src;
|
|
805
|
+
script.onload = resolve;
|
|
806
|
+
script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
|
|
807
|
+
document.body.appendChild(script);
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
async function loadLive2DWhenIdle() {
|
|
812
|
+
if (!modelMount || window.Live2DManager) return;
|
|
813
|
+
try {
|
|
814
|
+
await loadScript('../../node_modules/@hazart-pkg/live2d-core/live2dcubismcore.min.js');
|
|
815
|
+
await loadScript('../../node_modules/pixi.js/dist/browser/pixi.min.js');
|
|
816
|
+
await loadScript('../../node_modules/pixi-live2d-display/dist/cubism4.min.js');
|
|
817
|
+
await loadScript('live2d_manager.js');
|
|
818
|
+
if (window.Live2DManager) {
|
|
819
|
+
await Live2DManager.loadModel(modelMount, modelStatus, modelShell);
|
|
820
|
+
}
|
|
821
|
+
} catch (err) {
|
|
822
|
+
console.error('[Live2D] Deferred load failed:', err);
|
|
823
|
+
if (modelStatus) {
|
|
824
|
+
modelStatus.classList.add('is-error');
|
|
825
|
+
modelStatus.textContent = 'Live2D model unavailable.';
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
759
830
|
async function loadChatHistory() {
|
|
760
831
|
try {
|
|
761
832
|
const history = await window.api.getChatHistory();
|
|
833
|
+
const initial = chatContainer.querySelector('.message.initial');
|
|
834
|
+
|
|
762
835
|
if (!Array.isArray(history) || history.length === 0) {
|
|
836
|
+
if (initial) {
|
|
837
|
+
initial.style.display = 'flex';
|
|
838
|
+
initial.style.opacity = '1';
|
|
839
|
+
}
|
|
763
840
|
return;
|
|
764
841
|
}
|
|
765
842
|
|
|
766
|
-
const initial = chatContainer.querySelector('.message.initial');
|
|
767
843
|
if (initial) {
|
|
768
844
|
initial.remove();
|
|
769
845
|
}
|
|
@@ -771,11 +847,12 @@ async function loadChatHistory() {
|
|
|
771
847
|
for (const item of history) {
|
|
772
848
|
if (!item || typeof item.text !== 'string' || !item.text.trim()) continue;
|
|
773
849
|
const sender = item.sender === 'user' ? 'user' : 'ai';
|
|
774
|
-
if (sender === '
|
|
775
|
-
|
|
776
|
-
} else {
|
|
777
|
-
appendMessage(item.text, sender, null, item.timestamp);
|
|
850
|
+
if (sender === 'user' && !String(item.text).startsWith('Model interaction:')) {
|
|
851
|
+
rememberConversationLanguage(item.text);
|
|
778
852
|
}
|
|
853
|
+
appendMessage(item.text, sender, null, item.timestamp, {
|
|
854
|
+
providerInfo: sender === 'ai' ? item.providerInfo : null
|
|
855
|
+
});
|
|
779
856
|
}
|
|
780
857
|
} catch (error) {
|
|
781
858
|
console.error('Failed to load chat history:', error);
|
|
@@ -785,23 +862,31 @@ async function loadChatHistory() {
|
|
|
785
862
|
async function sendTextMessage(text, options = {}) {
|
|
786
863
|
const cleanText = (text || '').trim();
|
|
787
864
|
const allowSmartContext = options.allowSmartContext !== false;
|
|
865
|
+
const includePendingImage = options.includePendingImage !== false;
|
|
866
|
+
const displayText = options.displayText !== undefined ? options.displayText : cleanText;
|
|
867
|
+
const trackLanguage = options.trackLanguage !== false;
|
|
788
868
|
|
|
789
869
|
// We can send either a text message, an image, or both.
|
|
790
|
-
if (!cleanText && !currentBase64Image) return;
|
|
870
|
+
if (!cleanText && (!includePendingImage || !currentBase64Image)) return;
|
|
791
871
|
|
|
792
872
|
// Cache the image for sending and UI, then clear
|
|
793
|
-
let imageToSend = currentBase64Image;
|
|
873
|
+
let imageToSend = includePendingImage ? currentBase64Image : null;
|
|
794
874
|
|
|
795
875
|
// Clear input & UI for explicit images
|
|
796
876
|
chatInput.value = '';
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
877
|
+
if (includePendingImage) {
|
|
878
|
+
currentBase64Image = null;
|
|
879
|
+
imagePreviewContainer.style.display = 'none';
|
|
880
|
+
imagePreview.src = '';
|
|
881
|
+
}
|
|
800
882
|
|
|
801
883
|
const now = new Date().toISOString();
|
|
802
884
|
|
|
803
885
|
// Show user message (with explicit image if available)
|
|
804
|
-
appendMessage(
|
|
886
|
+
appendMessage(displayText, 'user', imageToSend, now);
|
|
887
|
+
if (trackLanguage) {
|
|
888
|
+
rememberConversationLanguage(displayText || cleanText);
|
|
889
|
+
}
|
|
805
890
|
|
|
806
891
|
// Show typing early so user knows we are processing
|
|
807
892
|
showTyping();
|
|
@@ -846,7 +931,9 @@ async function sendTextMessage(text, options = {}) {
|
|
|
846
931
|
} else {
|
|
847
932
|
// General system info (date, time, RAM, CPU)
|
|
848
933
|
const info = await window.api.getSystemInfo();
|
|
849
|
-
|
|
934
|
+
const machine = info.machine && info.machine.display ? `\n🖥️ รุ่นเครื่อง: ${info.machine.display}` : '';
|
|
935
|
+
const distro = info.distro ? `\nระบบ: ${info.distro}` : '';
|
|
936
|
+
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})`;
|
|
850
937
|
}
|
|
851
938
|
}
|
|
852
939
|
|
|
@@ -880,6 +967,20 @@ chatForm.addEventListener('submit', throttle(async (e) => {
|
|
|
880
967
|
await sendTextMessage(text);
|
|
881
968
|
}, 500));
|
|
882
969
|
|
|
970
|
+
window.addEventListener('live2d-model-interaction', async (event) => {
|
|
971
|
+
const prompt = event?.detail?.prompt;
|
|
972
|
+
if (!prompt) return;
|
|
973
|
+
if (window.api && window.api.setAiState) window.api.setAiState('thinking');
|
|
974
|
+
const interactionPrompt = `${prompt}\n\n${buildInteractionLanguageInstruction()}`;
|
|
975
|
+
const displayPrefix = lastConversationLanguage === 'thai' ? 'แตะโมเดล' : 'Model interaction';
|
|
976
|
+
await sendTextMessage(interactionPrompt, {
|
|
977
|
+
allowSmartContext: false,
|
|
978
|
+
includePendingImage: false,
|
|
979
|
+
trackLanguage: false,
|
|
980
|
+
displayText: `${displayPrefix}: ${event.detail.label || event.detail.region || 'Interaction'}`
|
|
981
|
+
});
|
|
982
|
+
});
|
|
983
|
+
|
|
883
984
|
// --- Image Paste and Drag-n-Drop Support ---
|
|
884
985
|
function handleImageFile(file) {
|
|
885
986
|
if (!file || !file.type.startsWith('image/')) return;
|
|
@@ -936,6 +1037,8 @@ window.addEventListener('DOMContentLoaded', async () => {
|
|
|
936
1037
|
chatInput.focus();
|
|
937
1038
|
await loadTheme();
|
|
938
1039
|
await loadChatHistory();
|
|
1040
|
+
const scheduleLive2DLoad = window.requestIdleCallback || ((callback) => setTimeout(callback, 750));
|
|
1041
|
+
scheduleLive2DLoad(() => loadLive2DWhenIdle());
|
|
939
1042
|
});
|
|
940
1043
|
|
|
941
1044
|
// Proactive OS Notifications (Battery, Network, etc.)
|
|
@@ -1028,6 +1131,61 @@ if (smartContextToggle) {
|
|
|
1028
1131
|
});
|
|
1029
1132
|
}
|
|
1030
1133
|
|
|
1134
|
+
// Toggle Live2D Model visibility
|
|
1135
|
+
const toggleModelBtn = document.getElementById('toggle-model-btn');
|
|
1136
|
+
const assistantWorkspace = document.querySelector('.assistant-workspace');
|
|
1137
|
+
|
|
1138
|
+
if (toggleModelBtn && assistantWorkspace) {
|
|
1139
|
+
toggleModelBtn.addEventListener('click', () => {
|
|
1140
|
+
const isHidden = assistantWorkspace.classList.toggle('model-hidden');
|
|
1141
|
+
toggleModelBtn.classList.toggle('active', isHidden);
|
|
1142
|
+
|
|
1143
|
+
// Save preference to local storage
|
|
1144
|
+
localStorage.setItem('mint-model-hidden', String(isHidden));
|
|
1145
|
+
|
|
1146
|
+
// Refit model if shown
|
|
1147
|
+
if (!isHidden && window.Live2DManager && Live2DManager.model) {
|
|
1148
|
+
// Wait for transition
|
|
1149
|
+
setTimeout(() => {
|
|
1150
|
+
// Trigger a resize event to refit
|
|
1151
|
+
window.dispatchEvent(new Event('resize'));
|
|
1152
|
+
}, 450);
|
|
1153
|
+
}
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
// Restore preference on load
|
|
1157
|
+
const savedModelHidden = localStorage.getItem('mint-model-hidden');
|
|
1158
|
+
const savedHidden = savedModelHidden === null || savedModelHidden === 'true';
|
|
1159
|
+
if (savedHidden) {
|
|
1160
|
+
assistantWorkspace.classList.add('model-hidden');
|
|
1161
|
+
toggleModelBtn.classList.add('active');
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// Cycle Shiroko's Expression
|
|
1166
|
+
const changeExpressionBtn = document.getElementById('change-expression-btn');
|
|
1167
|
+
if (changeExpressionBtn) {
|
|
1168
|
+
changeExpressionBtn.addEventListener('click', () => {
|
|
1169
|
+
if (window.Live2DManager) {
|
|
1170
|
+
Live2DManager.cycleExpression();
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// Toggle Live2D interaction area guide
|
|
1176
|
+
const interactionGuideBtn = document.getElementById('interaction-guide-btn');
|
|
1177
|
+
if (interactionGuideBtn && modelShell) {
|
|
1178
|
+
const savedGuideVisible = localStorage.getItem('mint-interaction-guide-visible') === 'true';
|
|
1179
|
+
modelShell.classList.toggle('show-interaction-guide', savedGuideVisible);
|
|
1180
|
+
interactionGuideBtn.classList.toggle('active', savedGuideVisible);
|
|
1181
|
+
|
|
1182
|
+
interactionGuideBtn.addEventListener('click', () => {
|
|
1183
|
+
const isVisible = modelShell.classList.toggle('show-interaction-guide');
|
|
1184
|
+
interactionGuideBtn.classList.toggle('active', isVisible);
|
|
1185
|
+
localStorage.setItem('mint-interaction-guide-visible', String(isVisible));
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1031
1189
|
// Spotlight integration
|
|
1032
1190
|
window.api.onSpotlightToChat((query) => {
|
|
1033
1191
|
chatInput.value = query;
|