@pheem49/mint 1.5.5 → 1.6.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 (222) hide show
  1. package/.codex +0 -0
  2. package/.github/FUNDING.yml +2 -0
  3. package/.github/workflows/ci.yml +45 -0
  4. package/.github/workflows/release.yml +79 -0
  5. package/Cargo.lock +5792 -0
  6. package/Cargo.toml +32 -0
  7. package/README.md +387 -353
  8. package/assets/icon.png +0 -0
  9. package/bin/mint +0 -0
  10. package/crates/mint-cli/Cargo.toml +23 -0
  11. package/crates/mint-cli/src/agent.rs +851 -0
  12. package/crates/mint-cli/src/gmail.rs +216 -0
  13. package/crates/mint-cli/src/image.rs +142 -0
  14. package/crates/mint-cli/src/main.rs +2837 -0
  15. package/crates/mint-cli/src/mcp.rs +63 -0
  16. package/crates/mint-cli/src/onboard.rs +1149 -0
  17. package/crates/mint-cli/src/setup.rs +390 -0
  18. package/crates/mint-cli/src/skills.rs +8 -0
  19. package/crates/mint-cli/src/updater.rs +279 -0
  20. package/crates/mint-core/Cargo.toml +22 -0
  21. package/crates/mint-core/src/agent_loop.rs +94 -0
  22. package/crates/mint-core/src/api_server.rs +991 -0
  23. package/crates/mint-core/src/channels.rs +248 -0
  24. package/crates/mint-core/src/chat.rs +895 -0
  25. package/crates/mint-core/src/code_tools.rs +729 -0
  26. package/crates/mint-core/src/config.rs +368 -0
  27. package/crates/mint-core/src/files.rs +159 -0
  28. package/crates/mint-core/src/knowledge.rs +541 -0
  29. package/crates/mint-core/src/lib.rs +84 -0
  30. package/crates/mint-core/src/mcp.rs +273 -0
  31. package/crates/mint-core/src/memory.rs +673 -0
  32. package/crates/mint-core/src/orchestration.rs +2157 -0
  33. package/crates/mint-core/src/pictures.rs +314 -0
  34. package/crates/mint-core/src/plugins.rs +727 -0
  35. package/crates/mint-core/src/safety.rs +416 -0
  36. package/crates/mint-core/src/semantic.rs +254 -0
  37. package/crates/mint-core/src/shell.rs +317 -0
  38. package/crates/mint-core/src/skills.rs +71 -0
  39. package/crates/mint-core/src/symbols.rs +157 -0
  40. package/crates/mint-core/src/tasks.rs +308 -0
  41. package/crates/mint-core/src/tts.rs +92 -0
  42. package/crates/mint-core/src/weather.rs +93 -0
  43. package/crates/mint-core/src/web_search.rs +200 -0
  44. package/crates/mint-core/src/workflows.rs +81 -0
  45. package/crates/mint-core/tests/mcp_stdio.rs +45 -0
  46. package/crates/mint-core/tests/memory_persistence.rs +172 -0
  47. package/crates/mint-core/tests/pictures_storage.rs +14 -0
  48. package/crates/mint-core/tests/task_lifecycle.rs +87 -0
  49. package/package.json +35 -99
  50. package/src/bin/index.js +16 -0
  51. package/src/renderer/index-web.html +17 -0
  52. package/src/renderer/index.html +17 -0
  53. package/src/renderer/public/Live2DCubismCore.js +9 -0
  54. package/src/renderer/public/assets/icon.png +0 -0
  55. package/src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.model3.json +36 -0
  56. package/src/renderer/src/App.tsx +33 -0
  57. package/src/renderer/src/calculator.ts +47 -0
  58. package/src/renderer/src/components/ChatPanel.tsx +1598 -0
  59. package/src/renderer/src/components/DashboardSidebar.tsx +358 -0
  60. package/src/renderer/src/components/Live2DStage.tsx +374 -0
  61. package/src/renderer/src/components/MintDashboard.tsx +950 -0
  62. package/src/renderer/src/components/ModelPanel.tsx +154 -0
  63. package/src/renderer/src/components/PicturesLibrary.tsx +46 -0
  64. package/src/renderer/src/components/ProactiveGlow.tsx +19 -0
  65. package/src/renderer/src/components/ScreenPicker.tsx +579 -0
  66. package/src/renderer/src/components/SettingsWindow.tsx +1467 -0
  67. package/src/renderer/src/components/SpotlightWindow.tsx +280 -0
  68. package/src/renderer/src/components/WidgetWindow.tsx +36 -0
  69. package/src/renderer/src/components/WorkspacePanel.tsx +268 -0
  70. package/src/{UI → renderer/src/css}/settings.css +69 -16
  71. package/src/renderer/src/css/spotlight.css +113 -0
  72. package/src/renderer/src/css/styles.css +3722 -0
  73. package/src/renderer/src/css/widget.css +185 -0
  74. package/src/renderer/src/env.d.ts +116 -0
  75. package/src/renderer/src/index.css +379 -0
  76. package/src/renderer/src/main.tsx +13 -0
  77. package/src/renderer/src/tauri.ts +996 -0
  78. package/src/renderer/src-web/App.tsx +25 -0
  79. package/src/renderer/src-web/calculator.ts +47 -0
  80. package/src/renderer/src-web/components/ChatPanel.tsx +1662 -0
  81. package/src/renderer/src-web/components/DashboardSidebar.tsx +242 -0
  82. package/src/renderer/src-web/components/MintDashboard.tsx +763 -0
  83. package/src/renderer/src-web/components/PicturesLibrary.tsx +73 -0
  84. package/src/renderer/src-web/components/SettingsWindow.tsx +1500 -0
  85. package/src/renderer/src-web/css/settings.css +1100 -0
  86. package/src/{UI → renderer/src-web/css}/spotlight.css +4 -4
  87. package/src/{UI → renderer/src-web/css}/styles.css +1055 -159
  88. package/src/{UI → renderer/src-web/css}/widget.css +2 -2
  89. package/src/renderer/src-web/env.d.ts +107 -0
  90. package/src/renderer/src-web/index.css +379 -0
  91. package/src/renderer/src-web/main.tsx +13 -0
  92. package/src/renderer/src-web/tauri.ts +983 -0
  93. package/tsconfig.json +30 -0
  94. package/vite.config.ts +33 -0
  95. package/vite.config.web.ts +51 -0
  96. package/GUIDE_TH.md +0 -125
  97. package/assets/Agent_Mint.png +0 -0
  98. package/assets/CLI_Screen.png +0 -0
  99. package/assets/Settings.png +0 -0
  100. package/benchmark_ai.js +0 -71
  101. package/install.ps1 +0 -64
  102. package/install.sh +0 -54
  103. package/main.js +0 -139
  104. package/mint-cli-logic.js +0 -3
  105. package/mint-cli.js +0 -410
  106. package/models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.model3.json +0 -47
  107. 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 +0 -23
  108. package/preload-picker.js +0 -11
  109. package/preload-settings.js +0 -11
  110. package/preload.js +0 -41
  111. package/scripts/install_linux_desktop_entry.js +0 -48
  112. package/src/AI_Brain/Gemini_API.js +0 -813
  113. package/src/AI_Brain/agent_orchestrator.js +0 -73
  114. package/src/AI_Brain/autonomous_brain.js +0 -179
  115. package/src/AI_Brain/behavior_memory.js +0 -135
  116. package/src/AI_Brain/headless_agent.js +0 -143
  117. package/src/AI_Brain/knowledge_base.js +0 -349
  118. package/src/AI_Brain/memory_store.js +0 -662
  119. package/src/AI_Brain/proactive_engine.js +0 -172
  120. package/src/AI_Brain/provider_adapter.js +0 -365
  121. package/src/Automation_Layer/browser_automation.js +0 -149
  122. package/src/Automation_Layer/file_operations.js +0 -286
  123. package/src/Automation_Layer/open_app.js +0 -85
  124. package/src/Automation_Layer/open_website.js +0 -38
  125. package/src/CLI/approval_handler.js +0 -47
  126. package/src/CLI/chat_router.js +0 -247
  127. package/src/CLI/chat_ui.js +0 -1159
  128. package/src/CLI/cli_colors.js +0 -115
  129. package/src/CLI/cli_formatters.js +0 -94
  130. package/src/CLI/code_agent.js +0 -1667
  131. package/src/CLI/code_session_memory.js +0 -62
  132. package/src/CLI/gmail_auth.js +0 -210
  133. package/src/CLI/image_input.js +0 -90
  134. package/src/CLI/intent_detectors.js +0 -181
  135. package/src/CLI/interactive_chat.js +0 -658
  136. package/src/CLI/list_features.js +0 -64
  137. package/src/CLI/onboarding.js +0 -416
  138. package/src/CLI/repo_summarizer.js +0 -282
  139. package/src/CLI/semantic_code_search.js +0 -312
  140. package/src/CLI/skill_manager.js +0 -41
  141. package/src/CLI/slash_command_handler.js +0 -418
  142. package/src/CLI/symbol_indexer.js +0 -231
  143. package/src/CLI/updater.js +0 -230
  144. package/src/CLI/workspace_manager.js +0 -90
  145. package/src/Channels/brave_search_bridge.js +0 -35
  146. package/src/Channels/discord_bridge.js +0 -66
  147. package/src/Channels/google_search_bridge.js +0 -38
  148. package/src/Channels/line_bridge.js +0 -60
  149. package/src/Channels/slack_bridge.js +0 -48
  150. package/src/Channels/telegram_bridge.js +0 -41
  151. package/src/Channels/whatsapp_bridge.js +0 -57
  152. package/src/Command_Parser/parser.js +0 -45
  153. package/src/Plugins/dev_tools.js +0 -41
  154. package/src/Plugins/discord.js +0 -20
  155. package/src/Plugins/docker.js +0 -47
  156. package/src/Plugins/gmail.js +0 -251
  157. package/src/Plugins/google_calendar.js +0 -252
  158. package/src/Plugins/mcp_manager.js +0 -95
  159. package/src/Plugins/notion.js +0 -256
  160. package/src/Plugins/obsidian.js +0 -54
  161. package/src/Plugins/plugin_manager.js +0 -81
  162. package/src/Plugins/spotify.js +0 -173
  163. package/src/Plugins/system_metrics.js +0 -31
  164. package/src/Plugins/system_monitor.js +0 -72
  165. package/src/System/action_executor.js +0 -178
  166. package/src/System/bridge_manager.js +0 -76
  167. package/src/System/chat_history_manager.js +0 -83
  168. package/src/System/config_manager.js +0 -194
  169. package/src/System/custom_workflows.js +0 -163
  170. package/src/System/daemon_manager.js +0 -67
  171. package/src/System/google_tts_urls.js +0 -51
  172. package/src/System/granular_automation.js +0 -157
  173. package/src/System/ipc_handlers.js +0 -332
  174. package/src/System/notifications.js +0 -23
  175. package/src/System/optional_require.js +0 -23
  176. package/src/System/picture_store.js +0 -109
  177. package/src/System/proactive_loop.js +0 -153
  178. package/src/System/safety_manager.js +0 -273
  179. package/src/System/sandbox_runner.js +0 -182
  180. package/src/System/screen_capture.js +0 -175
  181. package/src/System/smart_context.js +0 -227
  182. package/src/System/system_automation.js +0 -162
  183. package/src/System/system_events.js +0 -79
  184. package/src/System/system_info.js +0 -125
  185. package/src/System/task_manager.js +0 -222
  186. package/src/System/tool_registry.js +0 -293
  187. package/src/System/window_manager.js +0 -220
  188. package/src/UI/floating.css +0 -80
  189. package/src/UI/floating.html +0 -17
  190. package/src/UI/floating.js +0 -67
  191. package/src/UI/live2d_manager.js +0 -600
  192. package/src/UI/preload-floating.js +0 -7
  193. package/src/UI/preload-spotlight.js +0 -11
  194. package/src/UI/preload-widget.js +0 -5
  195. package/src/UI/proactive-glow.html +0 -42
  196. package/src/UI/renderer.js +0 -2127
  197. package/src/UI/screenPicker.html +0 -214
  198. package/src/UI/screenPicker.js +0 -262
  199. package/src/UI/settings.html +0 -577
  200. package/src/UI/settings.js +0 -770
  201. package/src/UI/spotlight.html +0 -23
  202. package/src/UI/spotlight.js +0 -185
  203. package/src/UI/widget.html +0 -29
  204. package/src/UI/widget.js +0 -10
  205. /package/{models → src/renderer/public/models}/Shiroko_Model/Shiroko/Shiroko_Core/72d86db84cfa9730b894c241fd24c0db.png +0 -0
  206. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//345/233/264/350/243/231.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/apron.exp3.json} +0 -0
  207. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/214/253/345/222/252/346/273/244/351/225/234.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/catfilter.exp3.json} +0 -0
  208. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/202/271/344/270/200/344/270/213.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/click.exp3.json} +0 -0
  209. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//345/221/206/347/214/253.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/dazed.exp3.json} +0 -0
  210. /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" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/dazedeyes.exp3.json} +0 -0
  211. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//347/234/274/351/225/234.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/glasses.exp3.json} +0 -0
  212. /package/{models → src/renderer/public/models}/Shiroko_Model/Shiroko/Shiroko_Core/items_pinned_to_model.json +0 -0
  213. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/277/347/254/224.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/pen.exp3.json} +0 -0
  214. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//346/213/215/347/205/247.exp3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/photo.exp3.json} +0 -0
  215. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_00.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_00.png} +0 -0
  216. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_01.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_01.png} +0 -0
  217. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_02.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_02.png} +0 -0
  218. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.4096/texture_03.png" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.4096/texture_03.png} +0 -0
  219. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.cdi3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.cdi3.json} +0 -0
  220. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.moc3" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.moc3} +0 -0
  221. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.physics3.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.physics3.json} +0 -0
  222. /package/{models/Shiroko_Model/Shiroko/Shiroko_Core//351/235/242/351/245/2740.vtube.json" → src/renderer/public/models/Shiroko_Model/Shiroko/Shiroko_Core/shiroko.vtube.json} +0 -0
@@ -0,0 +1,280 @@
1
+ import React, { useState, useEffect, useRef } from 'react'
2
+ import { evaluateArithmetic } from '../calculator'
3
+
4
+ const COMMANDS = [
5
+ { label: 'Open YouTube', desc: 'เปิดเว็บไซต์ YouTube', icon: '📺', action: { type: 'open_url', target: 'https://youtube.com' } },
6
+ { label: 'Open Facebook', desc: 'เปิดเว็บไซต์ Facebook', icon: '📘', action: { type: 'open_url', target: 'https://facebook.com' } },
7
+ { label: 'Open Instagram', desc: 'เปิดเว็บไซต์ Instagram', icon: '📸', action: { type: 'open_url', target: 'https://instagram.com' } },
8
+ { label: 'Open GitHub', desc: 'เปิดเว็บไซต์ GitHub', icon: '🐙', action: { type: 'open_url', target: 'https://github.com' } },
9
+ { label: 'System Info', desc: 'ดูข้อมูลระบบ', icon: '💻', action: { type: 'chat', query: 'ขอข้อมูลระบบหน่อย' } },
10
+ { label: 'Weather', desc: 'เช็คสภาพอากาศ', icon: '🌤️', action: { type: 'chat', query: 'อากาศที่กรุงเทพเป็นยังไง' } },
11
+ { label: 'Open Spotify', desc: 'เปิดโปรแกรม Spotify', icon: '🎵', action: { type: 'open_app', target: 'spotify' } },
12
+ { label: 'Open VS Code', desc: 'เปิดโปรแกรม VS Code', icon: '💻', action: { type: 'open_app', target: 'code' } },
13
+ ]
14
+
15
+ function getCommandIcon(iconName: string) {
16
+ const props = { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, strokeLinecap: "round" as const, strokeLinejoin: "round" as const, style: { display: 'inline-block', verticalAlign: 'middle' } }
17
+ switch (iconName) {
18
+ case '📺':
19
+ return (
20
+ <svg {...props}>
21
+ <path d="M22.54 6.42a2.78 2.78 0 0 0-1.94-2C18.88 4 12 4 12 4s-6.88 0-8.6.46a2.78 2.78 0 0 0-1.94 2A29 29 0 0 0 1 11.75a29 29 0 0 0 .46 5.33A2.78 2.78 0 0 0 3.4 19c1.72.46 8.6.46 8.6.46s6.88 0 8.6-.46a2.78 2.78 0 0 0 1.94-2 29 29 0 0 0 .46-5.25 29 29 0 0 0-.46-5.33z"></path>
22
+ <polygon points="9.75 15.02 15.5 11.75 9.75 8.48 9.75 15.02"></polygon>
23
+ </svg>
24
+ )
25
+ case '📘':
26
+ return (
27
+ <svg {...props}>
28
+ <path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"></path>
29
+ </svg>
30
+ )
31
+ case '📸':
32
+ return (
33
+ <svg {...props}>
34
+ <rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect>
35
+ <circle cx="12" cy="12" r="4"></circle>
36
+ <line x1="17.5" y1="6.5" x2="17.51" y2="6.5"></line>
37
+ </svg>
38
+ )
39
+ case '🐙':
40
+ return (
41
+ <svg {...props}>
42
+ <path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
43
+ </svg>
44
+ )
45
+ case '💻':
46
+ return (
47
+ <svg {...props}>
48
+ <rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
49
+ <line x1="8" y1="21" x2="16" y2="21"></line>
50
+ <line x1="12" y1="17" x2="12" y2="21"></line>
51
+ </svg>
52
+ )
53
+ case '🌤️':
54
+ return (
55
+ <svg {...props}>
56
+ <path d="M12 2v2"></path>
57
+ <path d="M12 20v2"></path>
58
+ <path d="M4.93 4.93l1.41 1.41"></path>
59
+ <path d="M17.66 17.66l1.41 1.41"></path>
60
+ <path d="M2 12h2"></path>
61
+ <path d="M20 12h2"></path>
62
+ <path d="M6.34 17.66l-1.41 1.41"></path>
63
+ <path d="M19.07 4.93l-1.41 1.41"></path>
64
+ <circle cx="12" cy="12" r="4"></circle>
65
+ </svg>
66
+ )
67
+ case '🎵':
68
+ return (
69
+ <svg {...props}>
70
+ <path d="M9 18V5l12-2v13"></path>
71
+ <circle cx="6" cy="18" r="3"></circle>
72
+ <circle cx="18" cy="16" r="3"></circle>
73
+ </svg>
74
+ )
75
+ case '🧮':
76
+ return (
77
+ <svg {...props}>
78
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
79
+ <line x1="9" y1="9" x2="15" y2="15"></line>
80
+ <line x1="15" y1="9" x2="9" y2="15"></line>
81
+ </svg>
82
+ )
83
+ case '✨':
84
+ return (
85
+ <svg {...props}>
86
+ <path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z"></path>
87
+ </svg>
88
+ )
89
+ default:
90
+ return <span>{iconName}</span>
91
+ }
92
+ }
93
+
94
+ export default function SpotlightWindow() {
95
+ const [query, setQuery] = useState('')
96
+ const [results, setResults] = useState<any[]>([])
97
+ const [selectedIndex, setSelectedIndex] = useState(-1)
98
+ const inputRef = useRef<HTMLInputElement>(null)
99
+
100
+ // Sync theme
101
+ useEffect(() => {
102
+ const applyThemeVariables = (config: any) => {
103
+ if (config.systemTextColor) {
104
+ document.documentElement.style.setProperty('--text-main', config.systemTextColor)
105
+ }
106
+ document.documentElement.setAttribute('data-theme', config.theme || 'dark')
107
+ if (config.theme === 'custom') {
108
+ if (config.customBgStart && config.customBgEnd) {
109
+ const gradient = `linear-gradient(135deg, ${config.customBgStart} 0%, ${config.customBgEnd} 100%)`
110
+ document.documentElement.style.setProperty('--bg-gradient', gradient)
111
+ }
112
+ if (config.customPanelBg) {
113
+ const rgb = hexToRgb(config.customPanelBg)
114
+ document.documentElement.style.setProperty('--panel-bg', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.75)`)
115
+ }
116
+ } else {
117
+ document.documentElement.style.removeProperty('--bg-gradient')
118
+ document.documentElement.style.removeProperty('--panel-bg')
119
+ }
120
+ }
121
+
122
+ if (window.spotlightAPI) {
123
+ window.spotlightAPI.getSettings().then(applyThemeVariables)
124
+ window.spotlightAPI.onSettingsChanged(applyThemeVariables)
125
+ }
126
+
127
+ const handleFocus = () => {
128
+ inputRef.current?.focus()
129
+ inputRef.current?.select()
130
+ }
131
+ window.addEventListener('focus', handleFocus)
132
+ handleFocus()
133
+
134
+ return () => window.removeEventListener('focus', handleFocus)
135
+ }, [])
136
+
137
+ const hexToRgb = (hex: string) => {
138
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
139
+ return result ? {
140
+ r: parseInt(result[1], 16),
141
+ g: parseInt(result[2], 16),
142
+ b: parseInt(result[3], 16)
143
+ } : { r: 15, g: 23, b: 42 }
144
+ }
145
+
146
+ // Calculate matches when query text changes
147
+ useEffect(() => {
148
+ const trimmed = query.trim()
149
+ if (!trimmed) {
150
+ setResults([])
151
+ setSelectedIndex(-1)
152
+ window.spotlightAPI?.resize(600, 80)
153
+ return
154
+ }
155
+
156
+ // Try math evaluation
157
+ if (/^[0-9+\-*/().\s]+$/.test(trimmed) && /[0-9]/.test(trimmed)) {
158
+ try {
159
+ const mathVal = evaluateArithmetic(trimmed)
160
+ const mathResult = [{
161
+ label: `Result: ${mathVal}`,
162
+ desc: 'Calculation result (Press Enter to copy)',
163
+ icon: '🧮',
164
+ action: { type: 'clipboard_write', target: mathVal.toString() }
165
+ }]
166
+ setResults(mathResult)
167
+ setSelectedIndex(0)
168
+ window.spotlightAPI?.resize(600, 80 + 64 + 16)
169
+ return
170
+ } catch {}
171
+ }
172
+
173
+ // Normal commands
174
+ const matches = COMMANDS.filter(c =>
175
+ c.label.toLowerCase().includes(trimmed.toLowerCase()) ||
176
+ c.desc.toLowerCase().includes(trimmed.toLowerCase())
177
+ )
178
+
179
+ // Add default chat fallback
180
+ matches.push({
181
+ label: `Ask Mint: "${trimmed}"`,
182
+ desc: 'Send query to AI Chat',
183
+ icon: '✨',
184
+ action: { type: 'chat', query: trimmed }
185
+ })
186
+
187
+ setResults(matches)
188
+ setSelectedIndex(0)
189
+ const newHeight = Math.min(80 + (matches.length * 64) + 16, 500)
190
+ window.spotlightAPI?.resize(600, newHeight)
191
+ }, [query])
192
+
193
+ const handleAction = async (action: any) => {
194
+ if (action.type === 'chat') {
195
+ window.spotlightAPI?.submit(action.query)
196
+ return
197
+ }
198
+
199
+ if (window.spotlightAPI?.executeAction) {
200
+ const result = await window.spotlightAPI.executeAction(action)
201
+ if (!result || result.success === false) {
202
+ window.spotlightAPI.submit(`Spotlight action failed: ${result?.message || 'Unknown error'}`)
203
+ }
204
+ return
205
+ }
206
+
207
+ window.spotlightAPI?.submit(action.target || action.value || '')
208
+ }
209
+
210
+ const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
211
+ if (e.key === 'Escape') {
212
+ window.spotlightAPI?.hide()
213
+ }
214
+
215
+ if (e.key === 'ArrowDown') {
216
+ e.preventDefault()
217
+ setSelectedIndex(prev => Math.min(prev + 1, results.length - 1))
218
+ }
219
+
220
+ if (e.key === 'ArrowUp') {
221
+ e.preventDefault()
222
+ setSelectedIndex(prev => Math.max(prev - 1, -1))
223
+ }
224
+
225
+ if (e.key === 'Enter') {
226
+ if (selectedIndex >= 0 && results[selectedIndex]) {
227
+ handleAction(results[selectedIndex].action)
228
+ } else {
229
+ const trimmed = query.trim()
230
+ if (trimmed) {
231
+ window.spotlightAPI?.submit(trimmed)
232
+ }
233
+ }
234
+ }
235
+ }
236
+
237
+ return (
238
+ <div className="spotlight-container">
239
+ <div className="input-wrapper">
240
+ <div className="spotlight-icon" style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
241
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
242
+ <path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z"></path>
243
+ </svg>
244
+ </div>
245
+ <input
246
+ type="text"
247
+ id="spotlight-input"
248
+ placeholder="What can I help with?"
249
+ autoComplete="off"
250
+ ref={inputRef}
251
+ value={query}
252
+ onChange={(e) => setQuery(e.target.value)}
253
+ onKeyDown={handleKeyDown}
254
+ autoFocus
255
+ />
256
+ <div className="shortcut-tip">ESC to close</div>
257
+ </div>
258
+ {results.length > 0 && (
259
+ <div id="spotlight-results" className="results-container">
260
+ {results.map((cmd, i) => (
261
+ <div
262
+ key={i}
263
+ className={`result-item ${i === selectedIndex ? 'selected' : ''}`}
264
+ onClick={() => handleAction(cmd.action)}
265
+ onMouseEnter={() => setSelectedIndex(i)}
266
+ >
267
+ <div className="result-icon" style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
268
+ {getCommandIcon(cmd.icon)}
269
+ </div>
270
+ <div className="result-content">
271
+ <div className="result-title">{cmd.label}</div>
272
+ <div className="result-desc">{cmd.desc}</div>
273
+ </div>
274
+ </div>
275
+ ))}
276
+ </div>
277
+ )}
278
+ </div>
279
+ )
280
+ }
@@ -0,0 +1,36 @@
1
+ import React, { useState, useEffect } from 'react'
2
+
3
+ export default function WidgetWindow() {
4
+ const [state, setState] = useState('idle')
5
+
6
+ useEffect(() => {
7
+ if (window.widgetAPI?.onStateChange) {
8
+ window.widgetAPI.onStateChange((newState: string) => {
9
+ setState(newState || 'idle')
10
+ })
11
+ }
12
+ }, [])
13
+
14
+ const stateLabel = state.charAt(0).toUpperCase() + state.slice(1)
15
+
16
+ return (
17
+ <div id="widget-container" className={`state-${state}`}>
18
+ <div className="aura-container">
19
+ <div className="aura"></div>
20
+ </div>
21
+ <div className="character-body">
22
+ {/* Eyes / Face */}
23
+ <div className="eyes">
24
+ <div className="eye left"></div>
25
+ <div className="eye right"></div>
26
+ </div>
27
+ {/* Mouth/Indicator */}
28
+ <div className="mouth"></div>
29
+ </div>
30
+ {/* Status Badge */}
31
+ <div className="status-badge" id="status-badge">
32
+ {stateLabel}
33
+ </div>
34
+ </div>
35
+ )
36
+ }
@@ -0,0 +1,268 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { getWorkspaceTree, type WorkspaceTreeEntry } from '../tauri'
3
+ import folderIcon from 'material-icon-theme/icons/folder.svg?url'
4
+ import folderOpenIcon from 'material-icon-theme/icons/folder-open.svg?url'
5
+ import folderComponentsIcon from 'material-icon-theme/icons/folder-components.svg?url'
6
+ import folderComponentsOpenIcon from 'material-icon-theme/icons/folder-components-open.svg?url'
7
+ import folderConfigIcon from 'material-icon-theme/icons/folder-config.svg?url'
8
+ import folderConfigOpenIcon from 'material-icon-theme/icons/folder-config-open.svg?url'
9
+ import folderDistIcon from 'material-icon-theme/icons/folder-dist.svg?url'
10
+ import folderDistOpenIcon from 'material-icon-theme/icons/folder-dist-open.svg?url'
11
+ import folderDocsIcon from 'material-icon-theme/icons/folder-docs.svg?url'
12
+ import folderDocsOpenIcon from 'material-icon-theme/icons/folder-docs-open.svg?url'
13
+ import folderGitIcon from 'material-icon-theme/icons/folder-git.svg?url'
14
+ import folderGitOpenIcon from 'material-icon-theme/icons/folder-git-open.svg?url'
15
+ import folderGithubIcon from 'material-icon-theme/icons/folder-github.svg?url'
16
+ import folderGithubOpenIcon from 'material-icon-theme/icons/folder-github-open.svg?url'
17
+ import folderImagesIcon from 'material-icon-theme/icons/folder-images.svg?url'
18
+ import folderImagesOpenIcon from 'material-icon-theme/icons/folder-images-open.svg?url'
19
+ import folderLogIcon from 'material-icon-theme/icons/folder-log.svg?url'
20
+ import folderLogOpenIcon from 'material-icon-theme/icons/folder-log-open.svg?url'
21
+ import folderNodeIcon from 'material-icon-theme/icons/folder-node.svg?url'
22
+ import folderNodeOpenIcon from 'material-icon-theme/icons/folder-node-open.svg?url'
23
+ import folderPublicIcon from 'material-icon-theme/icons/folder-public.svg?url'
24
+ import folderPublicOpenIcon from 'material-icon-theme/icons/folder-public-open.svg?url'
25
+ import folderRustIcon from 'material-icon-theme/icons/folder-rust.svg?url'
26
+ import folderRustOpenIcon from 'material-icon-theme/icons/folder-rust-open.svg?url'
27
+ import folderScriptsIcon from 'material-icon-theme/icons/folder-scripts.svg?url'
28
+ import folderScriptsOpenIcon from 'material-icon-theme/icons/folder-scripts-open.svg?url'
29
+ import folderSrcIcon from 'material-icon-theme/icons/folder-src.svg?url'
30
+ import folderSrcOpenIcon from 'material-icon-theme/icons/folder-src-open.svg?url'
31
+ import folderSrcTauriIcon from 'material-icon-theme/icons/folder-src-tauri.svg?url'
32
+ import folderSrcTauriOpenIcon from 'material-icon-theme/icons/folder-src-tauri-open.svg?url'
33
+ import folderTestIcon from 'material-icon-theme/icons/folder-test.svg?url'
34
+ import folderTestOpenIcon from 'material-icon-theme/icons/folder-test-open.svg?url'
35
+ import folderUiIcon from 'material-icon-theme/icons/folder-ui.svg?url'
36
+ import folderUiOpenIcon from 'material-icon-theme/icons/folder-ui-open.svg?url'
37
+ import folderUtilsIcon from 'material-icon-theme/icons/folder-utils.svg?url'
38
+ import folderUtilsOpenIcon from 'material-icon-theme/icons/folder-utils-open.svg?url'
39
+ import cssIcon from 'material-icon-theme/icons/css.svg?url'
40
+ import documentIcon from 'material-icon-theme/icons/document.svg?url'
41
+ import htmlIcon from 'material-icon-theme/icons/html.svg?url'
42
+ import imageIcon from 'material-icon-theme/icons/image.svg?url'
43
+ import javascriptIcon from 'material-icon-theme/icons/javascript.svg?url'
44
+ import jsonIcon from 'material-icon-theme/icons/json.svg?url'
45
+ import lockIcon from 'material-icon-theme/icons/lock.svg?url'
46
+ import logIcon from 'material-icon-theme/icons/log.svg?url'
47
+ import markdownIcon from 'material-icon-theme/icons/markdown.svg?url'
48
+ import npmIcon from 'material-icon-theme/icons/npm.svg?url'
49
+ import reactIcon from 'material-icon-theme/icons/react.svg?url'
50
+ import reactTsIcon from 'material-icon-theme/icons/react_ts.svg?url'
51
+ import rustIcon from 'material-icon-theme/icons/rust.svg?url'
52
+ import settingsIcon from 'material-icon-theme/icons/settings.svg?url'
53
+ import tauriIcon from 'material-icon-theme/icons/tauri.svg?url'
54
+ import tomlIcon from 'material-icon-theme/icons/toml.svg?url'
55
+ import typescriptIcon from 'material-icon-theme/icons/typescript.svg?url'
56
+ import viteIcon from 'material-icon-theme/icons/vite.svg?url'
57
+ import yamlIcon from 'material-icon-theme/icons/yaml.svg?url'
58
+
59
+ interface WorkspacePanelProps {
60
+ agentMode: boolean
61
+ sending: boolean
62
+ workspacePath: string
63
+ onEnableAgentMode: () => void
64
+ onSetMessage: (message: string) => void
65
+ onWorkspaceReady: (path: string) => void
66
+ }
67
+
68
+ const FILE_LABEL: Record<string, string> = {
69
+ css: '#',
70
+ html: '<>',
71
+ js: 'JS',
72
+ json: '{}',
73
+ md: 'MD',
74
+ rs: 'RS',
75
+ ts: 'TS',
76
+ tsx: 'TS',
77
+ }
78
+
79
+ const FOLDER_ICONS: Record<string, { closed: string; open: string }> = {
80
+ '.cargo_home': { closed: folderRustIcon, open: folderRustOpenIcon },
81
+ '.github': { closed: folderGithubIcon, open: folderGithubOpenIcon },
82
+ '.git': { closed: folderGitIcon, open: folderGitOpenIcon },
83
+ '.rustup': { closed: folderRustIcon, open: folderRustOpenIcon },
84
+ '.rustup_copy': { closed: folderRustIcon, open: folderRustOpenIcon },
85
+ '.rustup_home': { closed: folderRustIcon, open: folderRustOpenIcon },
86
+ assets: { closed: folderImagesIcon, open: folderImagesOpenIcon },
87
+ components: { closed: folderComponentsIcon, open: folderComponentsOpenIcon },
88
+ crates: { closed: folderRustIcon, open: folderRustOpenIcon },
89
+ css: { closed: folderUiIcon, open: folderUiOpenIcon },
90
+ dist: { closed: folderDistIcon, open: folderDistOpenIcon },
91
+ docs: { closed: folderDocsIcon, open: folderDocsOpenIcon },
92
+ logs: { closed: folderLogIcon, open: folderLogOpenIcon },
93
+ node_modules: { closed: folderNodeIcon, open: folderNodeOpenIcon },
94
+ out: { closed: folderDistIcon, open: folderDistOpenIcon },
95
+ public: { closed: folderPublicIcon, open: folderPublicOpenIcon },
96
+ renderer: { closed: folderUiIcon, open: folderUiOpenIcon },
97
+ scripts: { closed: folderScriptsIcon, open: folderScriptsOpenIcon },
98
+ src: { closed: folderSrcIcon, open: folderSrcOpenIcon },
99
+ 'src-tauri': { closed: folderSrcTauriIcon, open: folderSrcTauriOpenIcon },
100
+ tests: { closed: folderTestIcon, open: folderTestOpenIcon },
101
+ utils: { closed: folderUtilsIcon, open: folderUtilsOpenIcon },
102
+ }
103
+
104
+ const FILE_ICONS_BY_EXTENSION: Record<string, string> = {
105
+ css: cssIcon,
106
+ html: htmlIcon,
107
+ jpeg: imageIcon,
108
+ jpg: imageIcon,
109
+ js: javascriptIcon,
110
+ json: jsonIcon,
111
+ lock: lockIcon,
112
+ log: logIcon,
113
+ md: markdownIcon,
114
+ png: imageIcon,
115
+ rs: rustIcon,
116
+ svg: imageIcon,
117
+ toml: tomlIcon,
118
+ ts: typescriptIcon,
119
+ tsx: reactTsIcon,
120
+ yaml: yamlIcon,
121
+ yml: yamlIcon,
122
+ }
123
+
124
+ const FILE_ICONS_BY_NAME: Record<string, string> = {
125
+ 'package-lock.json': npmIcon,
126
+ 'package.json': npmIcon,
127
+ 'tauri.conf.json': tauriIcon,
128
+ 'vite.config.ts': viteIcon,
129
+ 'vite.config.web.ts': viteIcon,
130
+ }
131
+
132
+ function extension(name: string) {
133
+ const index = name.lastIndexOf('.')
134
+ return index === -1 ? '' : name.slice(index + 1).toLowerCase()
135
+ }
136
+
137
+ function materialFolderIcon(name: string, open: boolean) {
138
+ const icon = FOLDER_ICONS[name] || FOLDER_ICONS[name.toLowerCase()]
139
+ if (!icon) return open ? folderOpenIcon : folderIcon
140
+ return open ? icon.open : icon.closed
141
+ }
142
+
143
+ function materialFileIcon(name: string, fileExtension: string) {
144
+ if (name.endsWith('.config.ts') || name.endsWith('.config.js')) return settingsIcon
145
+ if (name.endsWith('.tsx')) return reactTsIcon
146
+ if (name.endsWith('.jsx')) return reactIcon
147
+ return FILE_ICONS_BY_NAME[name.toLowerCase()] || FILE_ICONS_BY_EXTENSION[fileExtension] || documentIcon
148
+ }
149
+
150
+ function TreeNode({ entry, level }: { entry: WorkspaceTreeEntry; level: number; key?: string }) {
151
+ const [open, setOpen] = useState(level < 1)
152
+ const isDirectory = entry.kind === 'directory'
153
+ const hasChildren = entry.children.length > 0
154
+ const fileExtension = extension(entry.name)
155
+ const fileLabel = FILE_LABEL[fileExtension] || ''
156
+ const materialIcon = isDirectory ? materialFolderIcon(entry.name, open) : materialFileIcon(entry.name, fileExtension)
157
+ const dragText = `@${entry.path}`
158
+
159
+ return (
160
+ <div className="workspace-tree-node">
161
+ <button
162
+ type="button"
163
+ className={`workspace-tree-row ${isDirectory ? 'is-directory' : 'is-file'}`}
164
+ style={{ paddingLeft: `${level * 16 + 8}px` }}
165
+ onClick={() => isDirectory && hasChildren && setOpen((current) => !current)}
166
+ title={entry.path}
167
+ draggable
168
+ onDragStart={(event) => {
169
+ event.dataTransfer.setData('application/x-mint-workspace-path', dragText)
170
+ event.dataTransfer.setData('text/plain', dragText)
171
+ event.dataTransfer.effectAllowed = 'copy'
172
+ }}
173
+ >
174
+ <span
175
+ className={`workspace-tree-chevron ${isDirectory && hasChildren ? '' : 'is-spacer'}`}
176
+ data-open={open}
177
+ aria-hidden="true"
178
+ >
179
+ {isDirectory && hasChildren ? '' : null}
180
+ </span>
181
+ <span className={`workspace-tree-icon material-icon ${isDirectory ? 'folder' : fileExtension || 'file'}`} aria-hidden="true">
182
+ {materialIcon ? <img src={materialIcon} alt="" draggable={false} /> : isDirectory ? '' : fileLabel}
183
+ </span>
184
+ <span className="workspace-tree-name">{entry.name}</span>
185
+ </button>
186
+ {isDirectory && open && hasChildren && (
187
+ <div className="workspace-tree-children">
188
+ {entry.children.map((child) => (
189
+ <TreeNode key={child.path} entry={child} level={level + 1} />
190
+ ))}
191
+ </div>
192
+ )}
193
+ </div>
194
+ )
195
+ }
196
+
197
+ export default function WorkspacePanel({ agentMode, sending, workspacePath, onEnableAgentMode, onSetMessage, onWorkspaceReady }: WorkspacePanelProps) {
198
+ const [tree, setTree] = useState<WorkspaceTreeEntry | null>(null)
199
+ const [error, setError] = useState('')
200
+
201
+ const refresh = async () => {
202
+ if (!workspacePath.trim()) {
203
+ setError('')
204
+ setTree(null)
205
+ return
206
+ }
207
+
208
+ try {
209
+ setError('')
210
+ const nextTree = await getWorkspaceTree(workspacePath)
211
+ setTree(nextTree)
212
+ if (nextTree.path !== workspacePath) onWorkspaceReady(nextTree.path)
213
+ } catch (reason) {
214
+ setError(reason instanceof Error ? reason.message : String(reason))
215
+ }
216
+ }
217
+
218
+ useEffect(() => {
219
+ refresh()
220
+ }, [workspacePath])
221
+
222
+ const startWorkspacePrompt = () => {
223
+ onEnableAgentMode()
224
+ onSetMessage('ดู workspace นี้แล้วช่วยวางแผนงานต่อไปให้หน่อย')
225
+ }
226
+
227
+ return (
228
+ <section className="workspace-panel">
229
+ <header className="workspace-panel-header">
230
+ <div>
231
+ <span className="workspace-kicker">Agent Workspace</span>
232
+ <h2>Workspace</h2>
233
+ </div>
234
+ <span className="workspace-agent-pill" data-state={sending ? 'thinking' : agentMode ? 'agent' : 'idle'}>
235
+ {sending ? 'Running' : agentMode ? 'Agent mode' : 'Manual'}
236
+ </span>
237
+ </header>
238
+
239
+ <div className="workspace-panel-actions">
240
+ <button type="button" onClick={startWorkspacePrompt}>Use Agent</button>
241
+ <button type="button" onClick={refresh} disabled={!workspacePath.trim()}>Refresh</button>
242
+ </div>
243
+
244
+ <div className="workspace-tree-shell">
245
+ {error ? (
246
+ <div className="workspace-tree-empty">{error}</div>
247
+ ) : tree ? (
248
+ <>
249
+ <div className="workspace-root-row">
250
+ <span className="workspace-tree-chevron" data-open="true" aria-hidden="true" />
251
+ <span className="workspace-tree-icon material-icon folder" aria-hidden="true">
252
+ <img src={folderOpenIcon} alt="" draggable={false} />
253
+ </span>
254
+ <span>{tree.name}</span>
255
+ </div>
256
+ <div className="workspace-tree">
257
+ {tree.children.map((entry) => (
258
+ <TreeNode key={entry.path} entry={entry} level={0} />
259
+ ))}
260
+ </div>
261
+ </>
262
+ ) : (
263
+ <div className="workspace-tree-empty">Select a project to show workspace files.</div>
264
+ )}
265
+ </div>
266
+ </section>
267
+ )
268
+ }