@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.
Files changed (78) hide show
  1. package/README.md +27 -1
  2. package/main.js +28 -14
  3. package/mint-cli-logic.js +3 -119
  4. package/mint-cli.js +497 -23
  5. package/models/Shiroko_Model/Shiroko/Shiroko_Core/72d86db84cfa9730b894c241fd24c0db.png +0 -0
  6. package/models/Shiroko_Model/Shiroko/Shiroko_Core/items_pinned_to_model.json +14 -0
  7. package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253.exp3.json +10 -0
  8. 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
  9. package/models/Shiroko_Model/Shiroko/Shiroko_Core//345/233/264/350/243/231.exp3.json +10 -0
  10. package/models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/215/347/205/247.exp3.json +50 -0
  11. package/models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/277/347/254/224.exp3.json +10 -0
  12. package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/202/271/344/270/200/344/270/213.exp3.json +10 -0
  13. package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/214/253/345/222/252/346/273/244/351/225/234.exp3.json +10 -0
  14. package/models/Shiroko_Model/Shiroko/Shiroko_Core//347/234/274/351/225/234.exp3.json +10 -0
  15. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_00.png +0 -0
  16. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_01.png +0 -0
  17. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_02.png +0 -0
  18. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_03.png +0 -0
  19. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.cdi3.json +1498 -0
  20. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.moc3 +0 -0
  21. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.model3.json +47 -0
  22. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.physics3.json +6658 -0
  23. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.vtube.json +1299 -0
  24. 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
  25. package/package.json +26 -1
  26. package/src/AI_Brain/Gemini_API.js +147 -46
  27. package/src/AI_Brain/autonomous_brain.js +2 -1
  28. package/src/AI_Brain/memory_store.js +299 -3
  29. package/src/CLI/chat_router.js +18 -6
  30. package/src/CLI/chat_ui.js +396 -50
  31. package/src/CLI/code_agent.js +203 -14
  32. package/src/CLI/image_input.js +90 -0
  33. package/src/CLI/onboarding.js +72 -15
  34. package/src/CLI/updater.js +6 -4
  35. package/src/System/action_executor.js +59 -10
  36. package/src/System/config_manager.js +31 -1
  37. package/src/System/granular_automation.js +122 -53
  38. package/src/System/proactive_loop.js +19 -3
  39. package/src/System/safety_manager.js +108 -0
  40. package/src/System/sandbox_runner.js +182 -0
  41. package/src/System/system_automation.js +127 -81
  42. package/src/System/system_info.js +70 -0
  43. package/src/System/tool_registry.js +280 -0
  44. package/src/System/window_manager.js +4 -2
  45. package/src/UI/live2d_manager.js +368 -0
  46. package/src/UI/renderer.js +176 -18
  47. package/src/UI/styles.css +452 -31
  48. package/.codex +0 -0
  49. package/docs/assets/Agent_Mint.png +0 -0
  50. package/docs/assets/CLI_Screen.png +0 -0
  51. package/docs/assets/Settings.png +0 -0
  52. package/docs/assets/icon.png +0 -0
  53. package/docs/guide.html +0 -632
  54. package/docs/index.html +0 -133
  55. package/docs/style.css +0 -579
  56. package/index.html +0 -16
  57. package/src/UI/index.html +0 -126
  58. package/tech_news.txt +0 -3
  59. package/test_knowledge.txt +0 -3
  60. package/tests/action_executor_safety.test.js +0 -67
  61. package/tests/agent_orchestrator.test.js +0 -41
  62. package/tests/chat_router.test.js +0 -42
  63. package/tests/code_agent.test.js +0 -69
  64. package/tests/config_manager.test.js +0 -141
  65. package/tests/docker.test.js +0 -46
  66. package/tests/file_operations.test.js +0 -57
  67. package/tests/gmail.test.js +0 -135
  68. package/tests/gmail_auth.test.js +0 -129
  69. package/tests/google_calendar.test.js +0 -113
  70. package/tests/google_tts_urls.test.js +0 -24
  71. package/tests/memory_store.test.js +0 -185
  72. package/tests/notion.test.js +0 -121
  73. package/tests/provider_routing.test.js +0 -83
  74. package/tests/safety_manager.test.js +0 -40
  75. package/tests/spotify.test.js +0 -201
  76. package/tests/system_monitor.test.js +0 -37
  77. package/tests/updater.test.js +0 -32
  78. package/tests/workspace_manager.test.js +0 -56
@@ -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 : null;
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
- if (onEnd) onEnd();
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
- if (onEnd) onEnd();
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
- if (onEnd) onEnd();
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, onEnd, resolve);
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, onEnd, resolve);
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 === 'ai') {
775
- await appendAiMessages(item.text, { allowDelay: false, timestamp: item.timestamp, providerInfo: item.providerInfo });
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
- currentBase64Image = null;
798
- imagePreviewContainer.style.display = 'none';
799
- imagePreview.src = '';
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(cleanText, 'user', imageToSend, now);
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
- response.response += `\n\n📅 วันนี้: ${info.date}\n⏰ เวลา: ${info.time}\n💻 RAM: ${info.ram.used} / ${info.ram.total} (${info.ram.percent})`;
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;