@pheem49/mint 1.4.2 → 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/GUIDE_TH.md +113 -0
- package/README.md +267 -78
- package/assets/CLI_Screen.png +0 -0
- package/main.js +76 -890
- package/mint-cli-logic.js +3 -107
- package/mint-cli.js +594 -29
- 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 +37 -4
- package/src/AI_Brain/Gemini_API.js +223 -65
- package/src/AI_Brain/autonomous_brain.js +11 -0
- package/src/AI_Brain/behavior_memory.js +26 -5
- package/src/AI_Brain/headless_agent.js +4 -0
- package/src/AI_Brain/knowledge_base.js +61 -8
- package/src/AI_Brain/memory_store.js +354 -10
- package/src/Automation_Layer/file_operations.js +1 -1
- package/src/CLI/chat_router.js +20 -7
- package/src/CLI/chat_ui.js +596 -825
- package/src/CLI/code_agent.js +347 -56
- package/src/CLI/gmail_auth.js +210 -0
- package/src/CLI/image_input.js +90 -0
- package/src/CLI/list_features.js +2 -0
- package/src/CLI/onboarding.js +364 -55
- package/src/CLI/updater.js +210 -0
- package/src/Channels/brave_search_bridge.js +35 -0
- package/src/Channels/discord_bridge.js +68 -0
- package/src/Channels/google_search_bridge.js +38 -0
- package/src/Channels/line_bridge.js +60 -0
- package/src/Channels/slack_bridge.js +53 -0
- package/src/Channels/telegram_bridge.js +49 -0
- package/src/Channels/whatsapp_bridge.js +55 -0
- package/src/Command_Parser/parser.js +12 -1
- package/src/Plugins/gmail.js +251 -0
- package/src/Plugins/google_calendar.js +245 -19
- package/src/Plugins/notion.js +256 -0
- package/src/System/action_executor.js +178 -0
- package/src/System/bridge_manager.js +76 -0
- package/src/System/chat_history_manager.js +23 -5
- package/src/System/config_manager.js +71 -7
- package/src/System/custom_workflows.js +31 -2
- package/src/System/google_tts_urls.js +51 -0
- package/src/System/granular_automation.js +122 -53
- package/src/System/ipc_handlers.js +238 -0
- package/src/System/proactive_loop.js +153 -0
- package/src/System/safety_manager.js +273 -0
- package/src/System/sandbox_runner.js +182 -0
- package/src/System/screen_capture.js +175 -0
- package/src/System/system_automation.js +127 -81
- package/src/System/system_info.js +70 -0
- package/src/System/task_manager.js +15 -5
- package/src/System/tool_registry.js +280 -0
- package/src/System/window_manager.js +212 -0
- package/src/UI/live2d_manager.js +368 -0
- package/src/UI/renderer.js +208 -24
- package/src/UI/settings.html +24 -0
- package/src/UI/settings.js +14 -4
- package/src/UI/styles.css +466 -32
- 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/index.html +0 -132
- 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/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/memory_store.test.js +0 -185
- package/tests/provider_routing.test.js +0 -67
- package/tests/spotify.test.js +0 -201
- package/tests/system_monitor.test.js +0 -37
- 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
|
|
|
@@ -558,7 +596,15 @@ clearBtn.addEventListener('click', async () => {
|
|
|
558
596
|
appendMessage('Chat history cleared. Starting fresh! 🌿', 'ai', null, new Date().toISOString());
|
|
559
597
|
});
|
|
560
598
|
|
|
561
|
-
function
|
|
599
|
+
function formatProviderInfo(providerInfo) {
|
|
600
|
+
if (!providerInfo || typeof providerInfo !== 'object') return '';
|
|
601
|
+
const provider = String(providerInfo.provider || '').trim();
|
|
602
|
+
const model = String(providerInfo.model || '').trim();
|
|
603
|
+
if (!provider && !model) return '';
|
|
604
|
+
return model ? `${provider || 'AI'} • ${model}` : provider;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function appendMessage(text, sender, base64Image = null, timestamp = null, options = {}) {
|
|
562
608
|
const messageDiv = document.createElement('div');
|
|
563
609
|
messageDiv.classList.add('message', `${sender}-message`);
|
|
564
610
|
|
|
@@ -586,11 +632,23 @@ function appendMessage(text, sender, base64Image = null, timestamp = null) {
|
|
|
586
632
|
|
|
587
633
|
bubbleWrapper.appendChild(bubble);
|
|
588
634
|
|
|
589
|
-
|
|
590
|
-
|
|
635
|
+
const providerLabel = sender === 'ai' ? formatProviderInfo(options.providerInfo) : '';
|
|
636
|
+
|
|
637
|
+
// Add metadata
|
|
638
|
+
if (timestamp || providerLabel) {
|
|
591
639
|
const timeDiv = document.createElement('div');
|
|
592
640
|
timeDiv.classList.add('message-time');
|
|
593
|
-
|
|
641
|
+
if (providerLabel) {
|
|
642
|
+
const providerSpan = document.createElement('span');
|
|
643
|
+
providerSpan.classList.add('provider-badge');
|
|
644
|
+
providerSpan.textContent = providerLabel;
|
|
645
|
+
timeDiv.appendChild(providerSpan);
|
|
646
|
+
}
|
|
647
|
+
if (timestamp) {
|
|
648
|
+
const timeSpan = document.createElement('span');
|
|
649
|
+
timeSpan.textContent = formatTime(timestamp);
|
|
650
|
+
timeDiv.appendChild(timeSpan);
|
|
651
|
+
}
|
|
594
652
|
bubbleWrapper.appendChild(timeDiv);
|
|
595
653
|
}
|
|
596
654
|
|
|
@@ -638,6 +696,7 @@ function estimateMessageDelay(text) {
|
|
|
638
696
|
async function appendAiMessages(text, options = {}) {
|
|
639
697
|
const allowDelay = options.allowDelay !== false;
|
|
640
698
|
const timestamp = options.timestamp || new Date().toISOString();
|
|
699
|
+
const providerInfo = options.providerInfo || null;
|
|
641
700
|
const parts = splitAiMessages(text);
|
|
642
701
|
let lastDiv = null;
|
|
643
702
|
|
|
@@ -649,7 +708,8 @@ async function appendAiMessages(text, options = {}) {
|
|
|
649
708
|
}
|
|
650
709
|
// Only show timestamp for the last bubble in a group if multiple
|
|
651
710
|
const partTimestamp = (index === parts.length - 1) ? timestamp : null;
|
|
652
|
-
|
|
711
|
+
const partProviderInfo = (index === parts.length - 1) ? providerInfo : null;
|
|
712
|
+
lastDiv = appendMessage(parts[index], 'ai', null, partTimestamp, { providerInfo: partProviderInfo });
|
|
653
713
|
}
|
|
654
714
|
|
|
655
715
|
return lastDiv;
|
|
@@ -734,14 +794,52 @@ function scrollToBottom() {
|
|
|
734
794
|
chatContainer.scrollTop = chatContainer.scrollHeight;
|
|
735
795
|
}
|
|
736
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
|
+
|
|
737
830
|
async function loadChatHistory() {
|
|
738
831
|
try {
|
|
739
832
|
const history = await window.api.getChatHistory();
|
|
833
|
+
const initial = chatContainer.querySelector('.message.initial');
|
|
834
|
+
|
|
740
835
|
if (!Array.isArray(history) || history.length === 0) {
|
|
836
|
+
if (initial) {
|
|
837
|
+
initial.style.display = 'flex';
|
|
838
|
+
initial.style.opacity = '1';
|
|
839
|
+
}
|
|
741
840
|
return;
|
|
742
841
|
}
|
|
743
842
|
|
|
744
|
-
const initial = chatContainer.querySelector('.message.initial');
|
|
745
843
|
if (initial) {
|
|
746
844
|
initial.remove();
|
|
747
845
|
}
|
|
@@ -749,11 +847,12 @@ async function loadChatHistory() {
|
|
|
749
847
|
for (const item of history) {
|
|
750
848
|
if (!item || typeof item.text !== 'string' || !item.text.trim()) continue;
|
|
751
849
|
const sender = item.sender === 'user' ? 'user' : 'ai';
|
|
752
|
-
if (sender === '
|
|
753
|
-
|
|
754
|
-
} else {
|
|
755
|
-
appendMessage(item.text, sender, null, item.timestamp);
|
|
850
|
+
if (sender === 'user' && !String(item.text).startsWith('Model interaction:')) {
|
|
851
|
+
rememberConversationLanguage(item.text);
|
|
756
852
|
}
|
|
853
|
+
appendMessage(item.text, sender, null, item.timestamp, {
|
|
854
|
+
providerInfo: sender === 'ai' ? item.providerInfo : null
|
|
855
|
+
});
|
|
757
856
|
}
|
|
758
857
|
} catch (error) {
|
|
759
858
|
console.error('Failed to load chat history:', error);
|
|
@@ -763,23 +862,31 @@ async function loadChatHistory() {
|
|
|
763
862
|
async function sendTextMessage(text, options = {}) {
|
|
764
863
|
const cleanText = (text || '').trim();
|
|
765
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;
|
|
766
868
|
|
|
767
869
|
// We can send either a text message, an image, or both.
|
|
768
|
-
if (!cleanText && !currentBase64Image) return;
|
|
870
|
+
if (!cleanText && (!includePendingImage || !currentBase64Image)) return;
|
|
769
871
|
|
|
770
872
|
// Cache the image for sending and UI, then clear
|
|
771
|
-
let imageToSend = currentBase64Image;
|
|
873
|
+
let imageToSend = includePendingImage ? currentBase64Image : null;
|
|
772
874
|
|
|
773
875
|
// Clear input & UI for explicit images
|
|
774
876
|
chatInput.value = '';
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
877
|
+
if (includePendingImage) {
|
|
878
|
+
currentBase64Image = null;
|
|
879
|
+
imagePreviewContainer.style.display = 'none';
|
|
880
|
+
imagePreview.src = '';
|
|
881
|
+
}
|
|
778
882
|
|
|
779
883
|
const now = new Date().toISOString();
|
|
780
884
|
|
|
781
885
|
// Show user message (with explicit image if available)
|
|
782
|
-
appendMessage(
|
|
886
|
+
appendMessage(displayText, 'user', imageToSend, now);
|
|
887
|
+
if (trackLanguage) {
|
|
888
|
+
rememberConversationLanguage(displayText || cleanText);
|
|
889
|
+
}
|
|
783
890
|
|
|
784
891
|
// Show typing early so user knows we are processing
|
|
785
892
|
showTyping();
|
|
@@ -824,12 +931,18 @@ async function sendTextMessage(text, options = {}) {
|
|
|
824
931
|
} else {
|
|
825
932
|
// General system info (date, time, RAM, CPU)
|
|
826
933
|
const info = await window.api.getSystemInfo();
|
|
827
|
-
|
|
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})`;
|
|
828
937
|
}
|
|
829
938
|
}
|
|
830
939
|
|
|
831
940
|
// Show AI response
|
|
832
|
-
const msgDiv = await appendAiMessages(response.response, {
|
|
941
|
+
const msgDiv = await appendAiMessages(response.response, {
|
|
942
|
+
allowDelay: true,
|
|
943
|
+
timestamp: response.timestamp,
|
|
944
|
+
providerInfo: response.providerInfo
|
|
945
|
+
});
|
|
833
946
|
|
|
834
947
|
// Speak AI response
|
|
835
948
|
await speakText(normalizeAiText(response.response), { onEnd: resumeSpeechIfNeeded });
|
|
@@ -854,6 +967,20 @@ chatForm.addEventListener('submit', throttle(async (e) => {
|
|
|
854
967
|
await sendTextMessage(text);
|
|
855
968
|
}, 500));
|
|
856
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
|
+
|
|
857
984
|
// --- Image Paste and Drag-n-Drop Support ---
|
|
858
985
|
function handleImageFile(file) {
|
|
859
986
|
if (!file || !file.type.startsWith('image/')) return;
|
|
@@ -910,6 +1037,8 @@ window.addEventListener('DOMContentLoaded', async () => {
|
|
|
910
1037
|
chatInput.focus();
|
|
911
1038
|
await loadTheme();
|
|
912
1039
|
await loadChatHistory();
|
|
1040
|
+
const scheduleLive2DLoad = window.requestIdleCallback || ((callback) => setTimeout(callback, 750));
|
|
1041
|
+
scheduleLive2DLoad(() => loadLive2DWhenIdle());
|
|
913
1042
|
});
|
|
914
1043
|
|
|
915
1044
|
// Proactive OS Notifications (Battery, Network, etc.)
|
|
@@ -1002,6 +1131,61 @@ if (smartContextToggle) {
|
|
|
1002
1131
|
});
|
|
1003
1132
|
}
|
|
1004
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
|
+
|
|
1005
1189
|
// Spotlight integration
|
|
1006
1190
|
window.api.onSpotlightToChat((query) => {
|
|
1007
1191
|
chatInput.value = query;
|
package/src/UI/settings.html
CHANGED
|
@@ -417,6 +417,30 @@
|
|
|
417
417
|
</div>
|
|
418
418
|
</div>
|
|
419
419
|
|
|
420
|
+
<!-- Gmail -->
|
|
421
|
+
<div class="plugin-card">
|
|
422
|
+
<div class="plugin-icon">✉️</div>
|
|
423
|
+
<div class="plugin-info">
|
|
424
|
+
<div class="plugin-name">Gmail</div>
|
|
425
|
+
<div class="plugin-desc">Read email and create drafts safely</div>
|
|
426
|
+
</div>
|
|
427
|
+
<div class="plugin-actions">
|
|
428
|
+
<button class="btn-connect" id="btn-plugin-gmail" data-plugin="gmail">Connect</button>
|
|
429
|
+
</div>
|
|
430
|
+
</div>
|
|
431
|
+
|
|
432
|
+
<!-- Notion -->
|
|
433
|
+
<div class="plugin-card">
|
|
434
|
+
<div class="plugin-icon">📝</div>
|
|
435
|
+
<div class="plugin-info">
|
|
436
|
+
<div class="plugin-name">Notion</div>
|
|
437
|
+
<div class="plugin-desc">Create notes, pages, and read databases</div>
|
|
438
|
+
</div>
|
|
439
|
+
<div class="plugin-actions">
|
|
440
|
+
<button class="btn-connect" id="btn-plugin-notion" data-plugin="notion">Connect</button>
|
|
441
|
+
</div>
|
|
442
|
+
</div>
|
|
443
|
+
|
|
420
444
|
<!-- Discord -->
|
|
421
445
|
<div class="plugin-card">
|
|
422
446
|
<div class="plugin-icon">💬</div>
|
package/src/UI/settings.js
CHANGED
|
@@ -23,9 +23,17 @@ const DEFAULT_CONFIG = {
|
|
|
23
23
|
ttsPitch: 1.0,
|
|
24
24
|
pluginSpotifyEnabled: true,
|
|
25
25
|
pluginCalendarEnabled: false,
|
|
26
|
+
pluginGmailEnabled: false,
|
|
27
|
+
pluginNotionEnabled: false,
|
|
26
28
|
pluginDiscordEnabled: false,
|
|
27
29
|
showDesktopWidget: true,
|
|
28
|
-
mcpServers: {}
|
|
30
|
+
mcpServers: {},
|
|
31
|
+
openaiModel: 'gpt-4o',
|
|
32
|
+
anthropicModel: 'claude-3-5-sonnet-latest',
|
|
33
|
+
hfModel: 'meta-llama/Meta-Llama-3-8B-Instruct',
|
|
34
|
+
localApiBaseUrl: '',
|
|
35
|
+
localModelName: 'local-model',
|
|
36
|
+
ollamaHost: ''
|
|
29
37
|
};
|
|
30
38
|
|
|
31
39
|
let currentConfig = { ...DEFAULT_CONFIG };
|
|
@@ -68,7 +76,7 @@ function applyConfig(config) {
|
|
|
68
76
|
if (hfInput) hfInput.value = config.hfApiKey || '';
|
|
69
77
|
|
|
70
78
|
const ollamaHostInput = document.getElementById('ollama-host-input');
|
|
71
|
-
if (ollamaHostInput) ollamaHostInput.value = config.ollamaHost || '
|
|
79
|
+
if (ollamaHostInput) ollamaHostInput.value = config.ollamaHost || '';
|
|
72
80
|
|
|
73
81
|
// Apply Gemini model
|
|
74
82
|
applyModelSelection(config.geminiModel);
|
|
@@ -95,7 +103,7 @@ function applyConfig(config) {
|
|
|
95
103
|
|
|
96
104
|
const localApiBaseUrlInput = document.getElementById('local-api-base-url');
|
|
97
105
|
if (localApiBaseUrlInput) {
|
|
98
|
-
localApiBaseUrlInput.value = config.localApiBaseUrl || '
|
|
106
|
+
localApiBaseUrlInput.value = config.localApiBaseUrl || '';
|
|
99
107
|
}
|
|
100
108
|
|
|
101
109
|
const localModelNameInput = document.getElementById('local-model-name');
|
|
@@ -142,6 +150,8 @@ function applyConfig(config) {
|
|
|
142
150
|
// Plugins logic
|
|
143
151
|
updatePluginButton('spotify', config.pluginSpotifyEnabled);
|
|
144
152
|
updatePluginButton('calendar', config.pluginCalendarEnabled);
|
|
153
|
+
updatePluginButton('gmail', config.pluginGmailEnabled);
|
|
154
|
+
updatePluginButton('notion', config.pluginNotionEnabled);
|
|
145
155
|
updatePluginButton('discord', config.pluginDiscordEnabled);
|
|
146
156
|
|
|
147
157
|
// Apply Automation Browser
|
|
@@ -691,7 +701,7 @@ function updatePluginButton(pluginName, isEnabled) {
|
|
|
691
701
|
}
|
|
692
702
|
|
|
693
703
|
// Bind plugin buttons
|
|
694
|
-
['spotify', 'calendar', 'discord'].forEach(plugin => {
|
|
704
|
+
['spotify', 'calendar', 'gmail', 'notion', 'discord'].forEach(plugin => {
|
|
695
705
|
const btn = document.getElementById(`btn-plugin-${plugin}`);
|
|
696
706
|
if (btn) {
|
|
697
707
|
btn.addEventListener('click', () => {
|