@pheem49/mint 1.8.0 → 1.8.2

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 (200) hide show
  1. package/.github/workflows/release.yml +1 -1
  2. package/Cargo.lock +34 -3
  3. package/Cargo.toml +2 -2
  4. package/README.md +20 -6
  5. package/Release_Note.md +129 -0
  6. package/assets/Mint_CLI.png +0 -0
  7. package/bin/mint +0 -0
  8. package/crates/mint-cli/Cargo.toml +1 -0
  9. package/crates/mint-cli/src/agent.rs +242 -54
  10. package/crates/mint-cli/src/main.rs +397 -74
  11. package/crates/mint-core/src/agent_loop.rs +1 -0
  12. package/crates/mint-core/src/api_server.rs +64 -2
  13. package/crates/mint-core/src/browser.rs +456 -0
  14. package/crates/mint-core/src/channels.rs +1 -0
  15. package/crates/mint-core/src/chat.rs +3 -0
  16. package/crates/mint-core/src/config.rs +130 -1
  17. package/crates/mint-core/src/lib.rs +19 -0
  18. package/crates/mint-core/src/orchestration.rs +317 -12
  19. package/crates/mint-core/src/skills.rs +113 -4
  20. package/crates/mint-core/src/tts.rs +3 -1
  21. package/crates/mint-core/src/workflows.rs +1 -22
  22. package/flatpak/com.pheem49.mint.desktop +9 -0
  23. package/flatpak/com.pheem49.mint.metainfo.xml +35 -0
  24. package/flatpak/com.pheem49.mint.yaml +52 -0
  25. package/package.json +1 -1
  26. package/shared-modules/.github/workflows/ci.yml +120 -0
  27. package/shared-modules/.github/workflows/update.yaml +28 -0
  28. package/shared-modules/CODEOWNERS +23 -0
  29. package/shared-modules/README.md +79 -0
  30. package/shared-modules/SDL/README.md +3 -0
  31. package/shared-modules/SDL/SDL-1.2.15.json +51 -0
  32. package/shared-modules/SDL/SDL_Pango-0.1.2-API-adds.patch +118 -0
  33. package/shared-modules/SDL/SDL_image-1.2.12.json +25 -0
  34. package/shared-modules/SDL/SDL_mixer-1.2.12.json +27 -0
  35. package/shared-modules/SDL/SDL_net-1.2.8.json +19 -0
  36. package/shared-modules/SDL/SDL_pango-0.1.2.json +26 -0
  37. package/shared-modules/SDL/SDL_ttf-2.0.11.json +28 -0
  38. package/shared-modules/SDL/libsdl-1.2.15-strict-prototypes.patch +316 -0
  39. package/shared-modules/SDL/sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch +24 -0
  40. package/shared-modules/SDL/sdl-libx11-build.patch +59 -0
  41. package/shared-modules/SDL/sdl-sysloadso-buffer-length.patch +27 -0
  42. package/shared-modules/SDL/sdl12-compat.json +22 -0
  43. package/shared-modules/SDL2/SDL2_image.json +27 -0
  44. package/shared-modules/SDL2/SDL2_ttf.json +29 -0
  45. package/shared-modules/cld2/CMakeLists.txt +157 -0
  46. package/shared-modules/cld2/cld2.json +24 -0
  47. package/shared-modules/clutter/clutter.json +72 -0
  48. package/shared-modules/dbus-glib/dbus-glib.json +29 -0
  49. package/shared-modules/glew/glew.json +36 -0
  50. package/shared-modules/glu/glu-9.json +12 -0
  51. package/shared-modules/gtk2/arc-gtk2-theme-Replace-Inkscape-dependency-with-rsvg-convert.patch +180 -0
  52. package/shared-modules/gtk2/gtk2-common-themes.json +360 -0
  53. package/shared-modules/gtk2/gtk2-fix-crash-in-show-uri.patch +30 -0
  54. package/shared-modules/gtk2/gtk2-gcc14.patch +281 -0
  55. package/shared-modules/gtk2/gtk2-gcc15.patch +250 -0
  56. package/shared-modules/gtk2/gtk2-murrine-c99.patch +34 -0
  57. package/shared-modules/gtk2/gtk2-use-adwaita-theme.patch +80 -0
  58. package/shared-modules/gtk2/gtk2.json +120 -0
  59. package/shared-modules/gtk2/ibus-gcc15.patch +13 -0
  60. package/shared-modules/gtk2/murrine-engine-fix-crash.patch +12 -0
  61. package/shared-modules/gtk2/readme.md +10 -0
  62. package/shared-modules/intltool/intltool-0.51.json +20 -0
  63. package/shared-modules/intltool/intltool-perl5.26-regex-fixes.patch +59 -0
  64. package/shared-modules/lame/0001-Build-xmm_quantize_sub.c-with-msse.patch +34 -0
  65. package/shared-modules/lame/0002-Include-GTK-1-autoconf-directives-in-build-system.patch +222 -0
  66. package/shared-modules/lame/0003-Patch-out-remaining-ansi2knr.patch +70 -0
  67. package/shared-modules/lame/0004-initscr-is-not-used-anywhere-in-lame-sourcetree.patch +41 -0
  68. package/shared-modules/lame/0005-Update-autoconf.patch +81 -0
  69. package/shared-modules/lame/lame-3.99.5.json +32 -0
  70. package/shared-modules/libappindicator/libappindicator-gtk2-12.10.json +140 -0
  71. package/shared-modules/libappindicator/libappindicator-gtk3-12.10.json +140 -0
  72. package/shared-modules/libappindicator/libappindicator-gtk3-introspection-12.10.json +140 -0
  73. package/shared-modules/libappindicator/patches/0001-libappindicator-ftbfs.patch +20 -0
  74. package/shared-modules/libappindicator/patches/0002-libappindicator-no-python.patch +888 -0
  75. package/shared-modules/libappindicator/patches/0003-libappindicator-fix-crash-from-incorrect-signal-emission.patch +74 -0
  76. package/shared-modules/libappindicator/patches/0004-libappindicator-fix-crash-iterating-icon-themes.patch +11 -0
  77. package/shared-modules/libayatana-appindicator/0001-Fix-HAVE_VALGRIND-AM_CONDITIONAL.patch +51 -0
  78. package/shared-modules/libayatana-appindicator/0001-Make-introspection-configurable.patch +44 -0
  79. package/shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json +121 -0
  80. package/shared-modules/libcanberra/0001-gtk-Don-t-assume-all-GdkDisplays-are-GdkX11Displays-.patch +123 -0
  81. package/shared-modules/libcanberra/libcanberra.json +44 -0
  82. package/shared-modules/libmad/libmad.json +19 -0
  83. package/shared-modules/libsecret/libsecret.json +23 -0
  84. package/shared-modules/libsoup/libsoup-2.4.json +14 -0
  85. package/shared-modules/libusb/libusb.json +27 -0
  86. package/shared-modules/linux-audio/dssi.json +31 -0
  87. package/shared-modules/linux-audio/fftw3f-static.json +44 -0
  88. package/shared-modules/linux-audio/fftw3f.json +42 -0
  89. package/shared-modules/linux-audio/fluidsynth2-static.json +30 -0
  90. package/shared-modules/linux-audio/fluidsynth2.json +19 -0
  91. package/shared-modules/linux-audio/ladspa.json +26 -0
  92. package/shared-modules/linux-audio/lash.json +35 -0
  93. package/shared-modules/linux-audio/libinstpatch.json +16 -0
  94. package/shared-modules/linux-audio/liblo-static.json +22 -0
  95. package/shared-modules/linux-audio/liblo.json +17 -0
  96. package/shared-modules/linux-audio/lilv-static.json +94 -0
  97. package/shared-modules/linux-audio/lilv.json +82 -0
  98. package/shared-modules/linux-audio/lrdf.json +57 -0
  99. package/shared-modules/linux-audio/lv2.json +21 -0
  100. package/shared-modules/linux-audio/patches/lash/0001-configure-remove-aclocal-m4.patch +29 -0
  101. package/shared-modules/linux-audio/patches/lash/0002-Fix-build-with-recent-gcc.patch +73 -0
  102. package/shared-modules/linux-audio/patches/lash/0003-Fix-linking.patch +136 -0
  103. package/shared-modules/linux-audio/patches/raptor-libxml.patch +31 -0
  104. package/shared-modules/linux-audio/patches/stk-install.sh +54 -0
  105. package/shared-modules/linux-audio/stk.json +28 -0
  106. package/shared-modules/lua5.1/0004-Fix-stack-overflow-in-vararg-functions.patch +21 -0
  107. package/shared-modules/lua5.1/lua-5.1.5-so.patch +43 -0
  108. package/shared-modules/lua5.1/lua-5.1.5.json +42 -0
  109. package/shared-modules/lua5.3/lua-5.3.5.json +41 -0
  110. package/shared-modules/lua5.3/lua.pc.in +8 -0
  111. package/shared-modules/lua5.3/luaconf.patch +47 -0
  112. package/shared-modules/lua5.3/makefile-add-liblua.so-rule.patch +47 -0
  113. package/shared-modules/lua5.4/lua-5.4.json +41 -0
  114. package/shared-modules/lua5.4/lua.pc.in +8 -0
  115. package/shared-modules/lua5.4/luaconf.patch +34 -0
  116. package/shared-modules/lua5.4/makefile-add-liblua.so-rule.patch +42 -0
  117. package/shared-modules/luajit/luajit.json +30 -0
  118. package/shared-modules/lzo/lzo.json +27 -0
  119. package/shared-modules/mac/mac.json +18 -0
  120. package/shared-modules/physfs/physfs.json +26 -0
  121. package/shared-modules/pygame/audiofile-gcc6.patch +21 -0
  122. package/shared-modules/pygame/cython-fixes.patch +34 -0
  123. package/shared-modules/pygame/fluidsynth-no-rawmidi.patch +69 -0
  124. package/shared-modules/pygame/glib.patch +25 -0
  125. package/shared-modules/pygame/pygame1-python2.json +157 -0
  126. package/shared-modules/pygtk/pygtk-c99.patch +15 -0
  127. package/shared-modules/pygtk/pygtk.json +117 -0
  128. package/shared-modules/pygtk/pygtk.patch +39 -0
  129. package/shared-modules/python2.7/python-2.7.json +48 -0
  130. package/shared-modules/smpeg/smpeg-0.4.5.json +50 -0
  131. package/shared-modules/smpeg/smpeg-am-prog-as.patch +12 -0
  132. package/shared-modules/smpeg/smpeg-export-mpegaudio-class.patch +17 -0
  133. package/shared-modules/smpeg/smpeg-gcc6.patch +40 -0
  134. package/shared-modules/smpeg/smpeg-no-gtk.patch +37 -0
  135. package/shared-modules/squashfs-tools/squashfs-tools.json +25 -0
  136. package/shared-modules/update-checker.sh +50 -0
  137. package/shared-modules/vorbisgain/0001-temp_files.patch +75 -0
  138. package/shared-modules/vorbisgain/0002-errno.patch +33 -0
  139. package/shared-modules/vorbisgain/0003-manpage.patch +248 -0
  140. package/shared-modules/vorbisgain/0004-vorbisgain_mtime.patch +183 -0
  141. package/shared-modules/vorbisgain/0005-double_fclose.patch +21 -0
  142. package/shared-modules/vorbisgain/0006-manpage_hyphens.patch +56 -0
  143. package/shared-modules/vorbisgain/0007-recursively_spelling.patch +14 -0
  144. package/shared-modules/vorbisgain/0008-manpage_recursion_mistake.patch +23 -0
  145. package/shared-modules/vorbisgain/0009-hardening.patch +13 -0
  146. package/shared-modules/vorbisgain/0010-fclose.patch +13 -0
  147. package/shared-modules/vorbisgain/README.md +23 -0
  148. package/shared-modules/vorbisgain/vorbisgain_0.37-2.json +29 -0
  149. package/src/renderer/shared/agentProgress.ts +145 -0
  150. package/src/renderer/shared/calculator.ts +52 -0
  151. package/src/renderer/shared/components/AgentActivityDrawer.tsx +72 -0
  152. package/src/renderer/shared/components/AgentActivityTable.tsx +66 -0
  153. package/src/renderer/shared/components/ApprovalCard.tsx +93 -0
  154. package/src/renderer/shared/components/ChatCodeBlock.tsx +75 -0
  155. package/src/renderer/shared/components/ThinkingBlock.tsx +101 -0
  156. package/src/renderer/shared/constants/models.ts +91 -0
  157. package/src/renderer/shared/platform.ts +82 -0
  158. package/src/renderer/shared/types.ts +140 -0
  159. package/src/renderer/shared/utils/agentActivity.ts +187 -0
  160. package/src/renderer/shared/utils/approval.ts +1 -0
  161. package/src/renderer/shared/utils/approval.tsx +64 -0
  162. package/src/renderer/shared/utils/markdown.tsx +347 -0
  163. package/src/renderer/shared/utils/providers.ts +36 -0
  164. package/src/renderer/shared/utils/speech.ts +216 -0
  165. package/src/renderer/shared/utils/ui.ts +147 -0
  166. package/src/renderer/src/agentProgress.ts +11 -39
  167. package/src/renderer/src/calculator.ts +3 -46
  168. package/src/renderer/src/components/ChatPanel.tsx +230 -1202
  169. package/src/renderer/src/components/ImageStudioPanel.tsx +11 -1
  170. package/src/renderer/src/components/MintDashboard.tsx +63 -171
  171. package/src/renderer/src/components/PicturesLibrary.tsx +32 -8
  172. package/src/renderer/src/components/Settings/AgentsTab.tsx +362 -0
  173. package/src/renderer/src/components/Settings/AutomationTab.tsx +0 -22
  174. package/src/renderer/src/components/Settings/PluginsTab.tsx +221 -41
  175. package/src/renderer/src/components/SettingsWindow.tsx +54 -12
  176. package/src/renderer/src/components/WorkspacePanel.tsx +66 -9
  177. package/src/renderer/src/css/image-studio.css +6 -0
  178. package/src/renderer/src/css/styles.css +148 -11
  179. package/src/renderer/src/index.css +12 -1
  180. package/src/renderer/src/tauri.ts +169 -143
  181. package/src/renderer/src-web/agentProgress.ts +11 -39
  182. package/src/renderer/src-web/calculator.ts +3 -46
  183. package/src/renderer/src-web/components/ChatPanel.tsx +223 -1199
  184. package/src/renderer/src-web/components/ImageStudioPanel.tsx +11 -1
  185. package/src/renderer/src-web/components/MintDashboard.tsx +31 -172
  186. package/src/renderer/src-web/components/PicturesLibrary.tsx +23 -29
  187. package/src/renderer/src-web/components/Settings/AgentsTab.tsx +362 -0
  188. package/src/renderer/src-web/components/Settings/AutomationTab.tsx +0 -22
  189. package/src/renderer/src-web/components/Settings/PluginsTab.tsx +221 -41
  190. package/src/renderer/src-web/components/SettingsWindow.tsx +54 -12
  191. package/src/renderer/src-web/css/image-studio.css +6 -0
  192. package/src/renderer/src-web/css/styles.css +184 -12
  193. package/src/renderer/src-web/index.css +12 -1
  194. package/src/renderer/src-web/tauri.ts +204 -104
  195. package/src-tauri/src/browser.rs +1 -141
  196. package/src-tauri/src/headless.rs +3 -2
  197. package/src-tauri/src/lib.rs +254 -59
  198. package/src-tauri/src/webhooks.rs +1 -0
  199. package/src-tauri/src/workflows.rs +57 -4
  200. package/src-tauri/tauri.conf.json +1 -1
@@ -16,6 +16,7 @@ interface ImageStudioPanelProps {
16
16
  view: string
17
17
  onRefreshPictures?: () => void
18
18
  onSendToChat?: (dataUri: string, prompt: string) => void
19
+ onToggleMobileSidebar?: () => void
19
20
  }
20
21
 
21
22
  const ASPECT_OPTIONS: { value: AspectRatio; label: string; icon: string }[] = [
@@ -85,7 +86,7 @@ function SkeletonCard() {
85
86
  )
86
87
  }
87
88
 
88
- export default function ImageStudioPanel({ view, onRefreshPictures, onSendToChat }: ImageStudioPanelProps) {
89
+ export default function ImageStudioPanel({ view, onRefreshPictures, onSendToChat, onToggleMobileSidebar }: ImageStudioPanelProps) {
89
90
  const [prompt, setPrompt] = useState('')
90
91
  const [negativePrompt, setNegativePrompt] = useState('')
91
92
  const [showNegative, setShowNegative] = useState(false)
@@ -206,6 +207,15 @@ export default function ImageStudioPanel({ view, onRefreshPictures, onSendToChat
206
207
  {/* Header */}
207
208
  <header className="img-studio-header">
208
209
  <div className="img-studio-header-title">
210
+ <button
211
+ className="mobile-menu-btn"
212
+ type="button"
213
+ onClick={onToggleMobileSidebar}
214
+ aria-label="Toggle menu"
215
+ style={{ marginRight: '8px' }}
216
+ >
217
+ ☰
218
+ </button>
209
219
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
210
220
  <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
211
221
  </svg>
@@ -12,9 +12,9 @@ import {
12
12
  selectWorkspaceDirectory,
13
13
  saveInteractionAgentActivity,
14
14
  streamChatMessage,
15
+ cancelChatMessage,
15
16
  submitToolApproval,
16
17
  listen,
17
- readClipboardImage as readTauriClipboardImage,
18
18
  type AgentProgress,
19
19
  type ChatResponse,
20
20
  type ChatSession,
@@ -30,6 +30,13 @@ import type { ModelInteraction } from './ModelPanel'
30
30
  import PicturesLibrary from './PicturesLibrary'
31
31
  import WorkspacePanel from './WorkspacePanel'
32
32
  import WorkflowBuilderPanel from './WorkflowBuilderPanel'
33
+ import {
34
+ errorMessage,
35
+ readImage,
36
+ readDocument,
37
+ createTrimmedImagePreview,
38
+ applyThemeStyles,
39
+ } from '../../shared/utils/ui'
33
40
 
34
41
  const EXPRESSIONS = [
35
42
  "Default",
@@ -88,142 +95,6 @@ const MOCK_WELCOME_INTERACTION = {
88
95
  createdAt: new Date().toISOString(),
89
96
  }
90
97
 
91
- function errorMessage(reason: unknown) {
92
- return reason instanceof Error ? reason.message : String(reason)
93
- }
94
-
95
- function readImage(file: File): Promise<string> {
96
- return new Promise((resolve, reject) => {
97
- const reader = new FileReader()
98
- reader.onload = () => resolve(String(reader.result))
99
- reader.onerror = () => reject(reader.error ?? new Error('Unable to read image'))
100
- reader.readAsDataURL(file)
101
- })
102
- }
103
-
104
- function readDocument(file: File): Promise<string> {
105
- return new Promise((resolve, reject) => {
106
- const reader = new FileReader()
107
- reader.onload = () => resolve(String(reader.result))
108
- reader.onerror = () => reject(reader.error ?? new Error('Unable to read document'))
109
- reader.readAsDataURL(file)
110
- })
111
- }
112
-
113
- async function createTrimmedImagePreview(dataUri: string): Promise<string> {
114
- const image = await new Promise<HTMLImageElement>((resolve, reject) => {
115
- const nextImage = new Image()
116
- nextImage.onload = () => resolve(nextImage)
117
- nextImage.onerror = () => reject(new Error('Unable to prepare image preview'))
118
- nextImage.src = dataUri
119
- })
120
-
121
- const canvas = document.createElement('canvas')
122
- canvas.width = image.naturalWidth || image.width
123
- canvas.height = image.naturalHeight || image.height
124
- const context = canvas.getContext('2d', { willReadFrequently: true })
125
- if (!context || canvas.width === 0 || canvas.height === 0) return dataUri
126
-
127
- context.drawImage(image, 0, 0)
128
- const pixels = context.getImageData(0, 0, canvas.width, canvas.height)
129
- let minX = canvas.width
130
- let minY = canvas.height
131
- let maxX = -1
132
- let maxY = -1
133
-
134
- for (let y = 0; y < canvas.height; y += 1) {
135
- for (let x = 0; x < canvas.width; x += 1) {
136
- const alpha = pixels.data[(y * canvas.width + x) * 4 + 3]
137
- if (alpha > 12) {
138
- minX = Math.min(minX, x)
139
- minY = Math.min(minY, y)
140
- maxX = Math.max(maxX, x)
141
- maxY = Math.max(maxY, y)
142
- }
143
- }
144
- }
145
-
146
- if (maxX < minX || maxY < minY) return dataUri
147
-
148
- const padding = 8
149
- const sx = Math.max(0, minX - padding)
150
- const sy = Math.max(0, minY - padding)
151
- const sw = Math.min(canvas.width - sx, maxX - minX + 1 + padding * 2)
152
- const sh = Math.min(canvas.height - sy, maxY - minY + 1 + padding * 2)
153
-
154
- if (sw >= canvas.width * 0.92 && sh >= canvas.height * 0.92) return dataUri
155
-
156
- const previewCanvas = document.createElement('canvas')
157
- previewCanvas.width = sw
158
- previewCanvas.height = sh
159
- const previewContext = previewCanvas.getContext('2d')
160
- if (!previewContext) return dataUri
161
- previewContext.drawImage(canvas, sx, sy, sw, sh, 0, 0, sw, sh)
162
- return previewCanvas.toDataURL('image/png')
163
- }
164
-
165
- const lightenColor = (hex: string, amount: number) => {
166
- const clean = hex.replace('#', '')
167
- if (clean.length !== 6) return hex
168
- const num = parseInt(clean, 16)
169
- const r = Math.min(255, (num >> 16) + amount)
170
- const g = Math.min(255, ((num >> 8) & 0x00FF) + amount)
171
- const b = Math.min(255, (num & 0x0000FF) + amount)
172
- return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`
173
- }
174
-
175
- const hexToRgb = (hex: string) => {
176
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
177
- return result ? {
178
- r: parseInt(result[1], 16),
179
- g: parseInt(result[2], 16),
180
- b: parseInt(result[3], 16),
181
- } : { r: 15, g: 23, b: 42 }
182
- }
183
-
184
- const applyThemeStyles = (cfg: any) => {
185
- const theme = cfg.theme || 'dark'
186
- const accentColor = cfg.accentColor || '#4f83e6'
187
- const systemTextColor = cfg.systemTextColor || '#f8fafc'
188
-
189
- document.documentElement.setAttribute('data-theme', theme)
190
- document.documentElement.style.setProperty('--accent', accentColor)
191
- document.documentElement.style.setProperty('--accent-hover', lightenColor(accentColor, 20))
192
- document.documentElement.style.setProperty('--text-main', systemTextColor)
193
- document.documentElement.style.setProperty('--glass-blur', cfg.glassBlur || 'blur(16px)')
194
- document.body.style.fontFamily = cfg.fontFamily || "'Outfit', sans-serif"
195
- document.documentElement.style.fontSize = cfg.fontSize || '18px'
196
-
197
- if (theme === 'custom') {
198
- if (cfg.customBgStart && cfg.customBgEnd) {
199
- document.documentElement.style.setProperty('--bg-color', cfg.customBgStart)
200
- document.documentElement.style.setProperty('--bg-gradient', `linear-gradient(135deg, ${cfg.customBgStart} 0%, ${cfg.customBgEnd} 100%)`)
201
- }
202
- if (cfg.customPanelBg) {
203
- const rgb = hexToRgb(cfg.customPanelBg)
204
- document.documentElement.style.setProperty('--panel-bg', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.75)`)
205
- document.documentElement.style.setProperty('--panel-raised', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.82)`)
206
- document.documentElement.style.setProperty('--panel-soft', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.46)`)
207
- document.documentElement.style.setProperty('--chrome-bg', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.88)`)
208
- document.documentElement.style.setProperty('--surface-bg', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.62)`)
209
- document.documentElement.style.setProperty('--surface-strong', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.86)`)
210
- document.documentElement.style.setProperty('--input-bg', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.72)`)
211
- }
212
- return
213
- }
214
-
215
- ;[
216
- '--bg-color',
217
- '--bg-gradient',
218
- '--panel-bg',
219
- '--panel-raised',
220
- '--panel-soft',
221
- '--chrome-bg',
222
- '--surface-bg',
223
- '--surface-strong',
224
- '--input-bg',
225
- ].forEach((name) => document.documentElement.style.removeProperty(name))
226
- }
227
98
 
228
99
  export default function MintDashboard() {
229
100
  const [view, setView] = useState<DashboardView>('chat')
@@ -268,6 +139,7 @@ export default function MintDashboard() {
268
139
  const [chatSessions, setChatSessions] = useState<ChatSession[]>([])
269
140
  const chatEnd = useRef<HTMLDivElement | null>(null)
270
141
  const startupReady = (dashboardDataReady && modelReady) || startupTimedOut
142
+ const [proactiveSuggestion, setProactiveSuggestion] = useState<any>(null)
271
143
 
272
144
  const [isSearchOpen, setIsSearchOpen] = useState(false)
273
145
  const [searchQuery, setSearchQuery] = useState('')
@@ -419,10 +291,14 @@ export default function MintDashboard() {
419
291
  setPendingApproval(event.payload)
420
292
  }
421
293
  })
294
+ const unlistenProactive = window.api.onProactiveSuggestion?.((suggestion: any) => {
295
+ setProactiveSuggestion(suggestion)
296
+ })
422
297
  return () => {
423
298
  unlistenPromise?.then?.((unlisten) => unlisten?.())
424
299
  unlistenSpotlight?.then?.((unlisten) => unlisten?.())
425
300
  unlistenVision?.then?.((unlisten) => unlisten?.())
301
+ unlistenProactive?.then?.((unlisten) => unlisten?.())
426
302
  }
427
303
  }, [])
428
304
 
@@ -502,14 +378,14 @@ export default function MintDashboard() {
502
378
  }
503
379
  }, [sending])
504
380
 
505
- async function handleApproval(approved: boolean, autoApproveSession = false) {
381
+ async function handleApproval(approved: boolean, autoApproveSession = false, answer?: string) {
506
382
  if (!pendingApproval) return
507
383
  try {
508
384
  if (autoApproveSession) {
509
385
  sessionAutoApprovedRef.current = true
510
386
  setSessionAutoApproved(true)
511
387
  }
512
- await submitToolApproval(pendingApproval.token, approved)
388
+ await submitToolApproval(pendingApproval.token, approved, answer)
513
389
  } catch (reason) {
514
390
  setError(errorMessage(reason))
515
391
  } finally {
@@ -517,6 +393,20 @@ export default function MintDashboard() {
517
393
  }
518
394
  }
519
395
 
396
+ async function handleCancelMessage() {
397
+ if (!sending || !streamingConversationId) return
398
+ try {
399
+ await cancelChatMessage(streamingConversationId)
400
+ } catch (e) {
401
+ console.error("Failed to cancel message stream:", e)
402
+ } finally {
403
+ setSending(false)
404
+ setStreamingConversationId(null)
405
+ setSendingMessage('')
406
+ setSendingImageCount(0)
407
+ }
408
+ }
409
+
520
410
  async function sendPrompt(
521
411
  promptText: string,
522
412
  options: {
@@ -608,6 +498,17 @@ export default function MintDashboard() {
608
498
  }
609
499
  }
610
500
 
501
+ const handleProactiveAction = async (action: any) => {
502
+ setProactiveSuggestion(null)
503
+ if (action && action.type !== 'none') {
504
+ try {
505
+ await window.api.executeProactiveAction(action)
506
+ } catch (err) {
507
+ setError(String(err))
508
+ }
509
+ }
510
+ }
511
+
611
512
  async function handleSubmit(event: FormEvent<HTMLFormElement>) {
612
513
  event.preventDefault()
613
514
  const trimmed = message.trim()
@@ -689,37 +590,6 @@ export default function MintDashboard() {
689
590
  return true
690
591
  }
691
592
 
692
- async function readClipboardImage() {
693
- try {
694
- const dataUri = await readTauriClipboardImage()
695
- if (dataUri) {
696
- const previewDataUri = await createTrimmedImagePreview(dataUri).catch(() => dataUri)
697
- setImageAttachments((current) => [...current, { dataUri, previewDataUri, name: 'Pasted image' }])
698
- return true
699
- }
700
- } catch (err) {
701
- console.warn('Tauri clipboard fallback error:', err)
702
- }
703
-
704
- try {
705
- if (!navigator.clipboard?.read) return false
706
- const items = await navigator.clipboard.read()
707
- for (const item of items) {
708
- const imageType = item.types.find((type) => type.startsWith('image/'))
709
- if (!imageType) continue
710
- const blob = await item.getType(imageType)
711
- const file = new File([blob], 'Pasted image', { type: imageType })
712
- const dataUri = await readImage(file)
713
- const previewDataUri = await createTrimmedImagePreview(dataUri).catch(() => dataUri)
714
- setImageAttachments((current) => [...current, { dataUri, previewDataUri, name: 'Pasted image' }])
715
- return true
716
- }
717
- return false
718
- } catch {
719
- // Some environments expose pasted images only through ClipboardEvent.
720
- return false
721
- }
722
- }
723
593
 
724
594
  async function selectDocument(event: ChangeEvent<HTMLInputElement>) {
725
595
  const file = event.target.files?.[0]
@@ -1018,6 +888,28 @@ export default function MintDashboard() {
1018
888
  onSetSearchOpen={setIsSearchOpen}
1019
889
  />
1020
890
  <main className={`assistant-workspace ${layoutPreset === 'chat-wide' ? 'layout-chat-wide' : 'layout-model-wide'} ${modelVisible || view === 'workspace' ? '' : 'model-hidden'} ${view === 'workspace' ? 'workspace-open' : ''}`}>
891
+ {proactiveSuggestion && (
892
+ <div className="proactive-bar" style={{ position: 'absolute', top: 0, left: 0, right: 0, zIndex: 100 }}>
893
+ <div className="proactive-header">
894
+ <span className="proactive-icon">✨</span>
895
+ <div className="proactive-message">{proactiveSuggestion.message}</div>
896
+ <button className="proactive-dismiss-btn" onClick={() => setProactiveSuggestion(null)}>
897
+ Dismiss
898
+ </button>
899
+ </div>
900
+ <div className="proactive-chips">
901
+ {proactiveSuggestion.suggestions?.map((sug: any, i: number) => (
902
+ <button
903
+ key={i}
904
+ className="suggestion-chip"
905
+ onClick={() => handleProactiveAction(sug.action)}
906
+ >
907
+ {sug.label}
908
+ </button>
909
+ ))}
910
+ </div>
911
+ </div>
912
+ )}
1021
913
  {view === 'workspace' && (
1022
914
  <WorkspacePanel
1023
915
  agentMode={agentMode}
@@ -1072,7 +964,6 @@ export default function MintDashboard() {
1072
964
  onSelectImage={selectImage}
1073
965
  onSelectDocument={selectDocument}
1074
966
  onPasteImage={pasteImage}
1075
- onReadClipboardImage={readClipboardImage}
1076
967
  onSetMessage={setMessage}
1077
968
  onSendVoiceMessage={sendVoiceMessage}
1078
969
  onRemoveImage={(idx: number) => {
@@ -1088,6 +979,7 @@ export default function MintDashboard() {
1088
979
  settingsConfig={settingsConfig}
1089
980
  onSetModel={changeModel}
1090
981
  onApproval={handleApproval}
982
+ onCancelMessage={handleCancelMessage}
1091
983
  />
1092
984
  </main>
1093
985
  <PicturesLibrary view={view} pictures={pictures} onSetView={setView} onRefreshPictures={refreshPictures} />
@@ -1,3 +1,4 @@
1
+ import { useEffect, useMemo, useState } from 'react'
1
2
  import { type PictureEntry, convertFileSrc } from '../tauri'
2
3
  import type { DashboardView } from './DashboardSidebar'
3
4
 
@@ -9,6 +10,16 @@ interface PicturesLibraryProps {
9
10
  }
10
11
 
11
12
  export default function PicturesLibrary({ view, pictures, onSetView, onRefreshPictures }: PicturesLibraryProps) {
13
+ const [visibleCount, setVisibleCount] = useState(24)
14
+ const visiblePictures = useMemo(
15
+ () => pictures.slice(0, visibleCount),
16
+ [pictures, visibleCount],
17
+ )
18
+
19
+ useEffect(() => {
20
+ setVisibleCount(24)
21
+ }, [view, pictures])
22
+
12
23
  return (
13
24
  <section className={`pictures-library ${view === 'pictures' ? 'is-visible' : ''}`} aria-hidden={view !== 'pictures'}>
14
25
  <header className="pictures-header">
@@ -34,14 +45,27 @@ export default function PicturesLibrary({ view, pictures, onSetView, onRefreshPi
34
45
  <span>Images appear here after a message with an attachment is sent successfully.</span>
35
46
  </div>
36
47
  ) : (
37
- <div className="pictures-grid">
38
- {pictures.map((picture, index) => (
39
- <article className="picture-card" key={picture.id}>
40
- <img src={convertFileSrc(picture.thumbnailPath || picture.thumbnailUrl || picture.path)} alt={picture.message || picture.filename} loading={index < 6 ? 'eager' : 'lazy'} decoding="async" />
41
- <div className="picture-card-meta"><span>{picture.message || picture.filename}</span></div>
42
- </article>
43
- ))}
44
- </div>
48
+ <>
49
+ <div className="pictures-grid">
50
+ {visiblePictures.map((picture, index) => (
51
+ <article className="picture-card" key={picture.id}>
52
+ <img src={convertFileSrc(picture.thumbnailPath || picture.thumbnailUrl || picture.path)} alt={picture.message || picture.filename} loading={index < 6 ? 'eager' : 'lazy'} decoding="async" />
53
+ <div className="picture-card-meta"><span>{picture.message || picture.filename}</span></div>
54
+ </article>
55
+ ))}
56
+ </div>
57
+ {visibleCount < pictures.length && (
58
+ <div className="pictures-load-more-container">
59
+ <button
60
+ type="button"
61
+ className="pictures-load-more-btn"
62
+ onClick={() => setVisibleCount((prev) => prev + 24)}
63
+ >
64
+ Load More ({pictures.length - visibleCount} remaining)
65
+ </button>
66
+ </div>
67
+ )}
68
+ </>
45
69
  )}
46
70
  </section>
47
71
  )