@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>
@@ -11,9 +11,9 @@ import {
11
11
  listSavedPictures,
12
12
  saveInteractionAgentActivity,
13
13
  streamChatMessage,
14
+ cancelChatMessage,
14
15
  submitToolApproval,
15
16
  listen,
16
- readClipboardImage as readTauriClipboardImage,
17
17
  type AgentProgress,
18
18
  type ChatResponse,
19
19
  type ChatSession,
@@ -25,6 +25,13 @@ import ChatPanel from './ChatPanel'
25
25
  import DashboardSidebar, { type DashboardView } from './DashboardSidebar'
26
26
  import PicturesLibrary from './PicturesLibrary'
27
27
  import ImageStudioPanel from './ImageStudioPanel'
28
+ import {
29
+ errorMessage,
30
+ readImage,
31
+ readDocument,
32
+ createTrimmedImagePreview,
33
+ applyThemeStyles,
34
+ } from '../../shared/utils/ui'
28
35
 
29
36
  const DEFAULT_CONFIG = {
30
37
  theme: 'dark',
@@ -66,142 +73,6 @@ const MOCK_WELCOME_INTERACTION = {
66
73
  createdAt: new Date().toISOString(),
67
74
  }
68
75
 
69
- function errorMessage(reason: unknown) {
70
- return reason instanceof Error ? reason.message : String(reason)
71
- }
72
-
73
- function readImage(file: File): Promise<string> {
74
- return new Promise((resolve, reject) => {
75
- const reader = new FileReader()
76
- reader.onload = () => resolve(String(reader.result))
77
- reader.onerror = () => reject(reader.error ?? new Error('Unable to read image'))
78
- reader.readAsDataURL(file)
79
- })
80
- }
81
-
82
- function readDocument(file: File): Promise<string> {
83
- return new Promise((resolve, reject) => {
84
- const reader = new FileReader()
85
- reader.onload = () => resolve(String(reader.result))
86
- reader.onerror = () => reject(reader.error ?? new Error('Unable to read document'))
87
- reader.readAsDataURL(file)
88
- })
89
- }
90
-
91
- async function createTrimmedImagePreview(dataUri: string): Promise<string> {
92
- const image = await new Promise<HTMLImageElement>((resolve, reject) => {
93
- const nextImage = new Image()
94
- nextImage.onload = () => resolve(nextImage)
95
- nextImage.onerror = () => reject(new Error('Unable to prepare image preview'))
96
- nextImage.src = dataUri
97
- })
98
-
99
- const canvas = document.createElement('canvas')
100
- canvas.width = image.naturalWidth || image.width
101
- canvas.height = image.naturalHeight || image.height
102
- const context = canvas.getContext('2d', { willReadFrequently: true })
103
- if (!context || canvas.width === 0 || canvas.height === 0) return dataUri
104
-
105
- context.drawImage(image, 0, 0)
106
- const pixels = context.getImageData(0, 0, canvas.width, canvas.height)
107
- let minX = canvas.width
108
- let minY = canvas.height
109
- let maxX = -1
110
- let maxY = -1
111
-
112
- for (let y = 0; y < canvas.height; y += 1) {
113
- for (let x = 0; x < canvas.width; x += 1) {
114
- const alpha = pixels.data[(y * canvas.width + x) * 4 + 3]
115
- if (alpha > 12) {
116
- minX = Math.min(minX, x)
117
- minY = Math.min(minY, y)
118
- maxX = Math.max(maxX, x)
119
- maxY = Math.max(maxY, y)
120
- }
121
- }
122
- }
123
-
124
- if (maxX < minX || maxY < minY) return dataUri
125
-
126
- const padding = 8
127
- const sx = Math.max(0, minX - padding)
128
- const sy = Math.max(0, minY - padding)
129
- const sw = Math.min(canvas.width - sx, maxX - minX + 1 + padding * 2)
130
- const sh = Math.min(canvas.height - sy, maxY - minY + 1 + padding * 2)
131
-
132
- if (sw >= canvas.width * 0.92 && sh >= canvas.height * 0.92) return dataUri
133
-
134
- const previewCanvas = document.createElement('canvas')
135
- previewCanvas.width = sw
136
- previewCanvas.height = sh
137
- const previewContext = previewCanvas.getContext('2d')
138
- if (!previewContext) return dataUri
139
- previewContext.drawImage(canvas, sx, sy, sw, sh, 0, 0, sw, sh)
140
- return previewCanvas.toDataURL('image/png')
141
- }
142
-
143
- const lightenColor = (hex: string, amount: number) => {
144
- const clean = hex.replace('#', '')
145
- if (clean.length !== 6) return hex
146
- const num = parseInt(clean, 16)
147
- const r = Math.min(255, (num >> 16) + amount)
148
- const g = Math.min(255, ((num >> 8) & 0x00FF) + amount)
149
- const b = Math.min(255, (num & 0x0000FF) + amount)
150
- return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`
151
- }
152
-
153
- const hexToRgb = (hex: string) => {
154
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
155
- return result ? {
156
- r: parseInt(result[1], 16),
157
- g: parseInt(result[2], 16),
158
- b: parseInt(result[3], 16),
159
- } : { r: 15, g: 23, b: 42 }
160
- }
161
-
162
- const applyThemeStyles = (cfg: any) => {
163
- const theme = cfg.theme || 'dark'
164
- const accentColor = cfg.accentColor || '#4f83e6'
165
- const systemTextColor = cfg.systemTextColor || '#f8fafc'
166
-
167
- document.documentElement.setAttribute('data-theme', theme)
168
- document.documentElement.style.setProperty('--accent', accentColor)
169
- document.documentElement.style.setProperty('--accent-hover', lightenColor(accentColor, 20))
170
- document.documentElement.style.setProperty('--text-main', systemTextColor)
171
- document.documentElement.style.setProperty('--glass-blur', cfg.glassBlur || 'blur(16px)')
172
- document.body.style.fontFamily = cfg.fontFamily || "'Outfit', sans-serif"
173
- document.documentElement.style.fontSize = cfg.fontSize || '18px'
174
-
175
- if (theme === 'custom') {
176
- if (cfg.customBgStart && cfg.customBgEnd) {
177
- document.documentElement.style.setProperty('--bg-color', cfg.customBgStart)
178
- document.documentElement.style.setProperty('--bg-gradient', `linear-gradient(135deg, ${cfg.customBgStart} 0%, ${cfg.customBgEnd} 100%)`)
179
- }
180
- if (cfg.customPanelBg) {
181
- const rgb = hexToRgb(cfg.customPanelBg)
182
- document.documentElement.style.setProperty('--panel-bg', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.75)`)
183
- document.documentElement.style.setProperty('--panel-raised', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.82)`)
184
- document.documentElement.style.setProperty('--panel-soft', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.46)`)
185
- document.documentElement.style.setProperty('--chrome-bg', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.88)`)
186
- document.documentElement.style.setProperty('--surface-bg', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.62)`)
187
- document.documentElement.style.setProperty('--surface-strong', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.86)`)
188
- document.documentElement.style.setProperty('--input-bg', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.72)`)
189
- }
190
- return
191
- }
192
-
193
- ;[
194
- '--bg-color',
195
- '--bg-gradient',
196
- '--panel-bg',
197
- '--panel-raised',
198
- '--panel-soft',
199
- '--chrome-bg',
200
- '--surface-bg',
201
- '--surface-strong',
202
- '--input-bg',
203
- ].forEach((name) => document.documentElement.style.removeProperty(name))
204
- }
205
76
 
206
77
  export default function MintDashboard() {
207
78
  const [view, setView] = useState<DashboardView>('chat')
@@ -384,6 +255,10 @@ export default function MintDashboard() {
384
255
  }
385
256
 
386
257
  const toggleSidebar = () => {
258
+ if (window.innerWidth <= 760) {
259
+ setMobileSidebarOpen(false)
260
+ return
261
+ }
387
262
  const next = !sidebarCollapsed
388
263
  window.localStorage.setItem('mint:sidebar-collapsed', String(next))
389
264
  setSidebarCollapsed(next)
@@ -400,10 +275,10 @@ export default function MintDashboard() {
400
275
  setAgentMode(enabled)
401
276
  }
402
277
 
403
- async function handleApproval(approved: boolean, _autoApproveSession = false) {
278
+ async function handleApproval(approved: boolean, _autoApproveSession = false, answer?: string) {
404
279
  if (!pendingApproval) return
405
280
  try {
406
- await submitToolApproval(pendingApproval.token, approved)
281
+ await submitToolApproval(pendingApproval.token, approved, answer)
407
282
  } catch (reason) {
408
283
  setError(errorMessage(reason))
409
284
  } finally {
@@ -411,6 +286,20 @@ export default function MintDashboard() {
411
286
  }
412
287
  }
413
288
 
289
+ async function handleCancelMessage() {
290
+ if (!sending || !streamingConversationId) return
291
+ try {
292
+ await cancelChatMessage(streamingConversationId)
293
+ } catch (e) {
294
+ console.error("Failed to cancel message stream:", e)
295
+ } finally {
296
+ setSending(false)
297
+ setStreamingConversationId(null)
298
+ setSendingMessage('')
299
+ setSendingImageCount(0)
300
+ }
301
+ }
302
+
414
303
  async function sendPrompt(
415
304
  promptText: string,
416
305
  options: {
@@ -583,37 +472,6 @@ export default function MintDashboard() {
583
472
  return true
584
473
  }
585
474
 
586
- async function readClipboardImage() {
587
- try {
588
- const dataUri = await readTauriClipboardImage()
589
- if (dataUri) {
590
- const previewDataUri = await createTrimmedImagePreview(dataUri).catch(() => dataUri)
591
- setImageAttachments((current) => [...current, { dataUri, previewDataUri, name: 'Pasted image' }])
592
- return true
593
- }
594
- } catch (err) {
595
- console.warn('Tauri clipboard fallback error:', err)
596
- }
597
-
598
- try {
599
- if (!navigator.clipboard?.read) return false
600
- const items = await navigator.clipboard.read()
601
- for (const item of items) {
602
- const imageType = item.types.find((type) => type.startsWith('image/'))
603
- if (!imageType) continue
604
- const blob = await item.getType(imageType)
605
- const file = new File([blob], 'Pasted image', { type: imageType })
606
- const dataUri = await readImage(file)
607
- const previewDataUri = await createTrimmedImagePreview(dataUri).catch(() => dataUri)
608
- setImageAttachments((current) => [...current, { dataUri, previewDataUri, name: 'Pasted image' }])
609
- return true
610
- }
611
- return false
612
- } catch {
613
- // Some environments expose pasted images only through ClipboardEvent.
614
- return false
615
- }
616
- }
617
475
 
618
476
  async function selectDocument(event: ChangeEvent<HTMLInputElement>) {
619
477
  const file = event.target.files?.[0]
@@ -825,7 +683,7 @@ export default function MintDashboard() {
825
683
 
826
684
  return (
827
685
  <div className={`app-container ${startupReady ? '' : 'is-loading'}`}>
828
- <div className={`app-body ${sidebarCollapsed ? 'sidebar-collapsed' : ''} ${view === 'pictures' ? 'pictures-open' : ''} ${mobileSidebarOpen ? 'mobile-sidebar-open' : ''}`}>
686
+ <div className={`app-body ${(sidebarCollapsed && window.innerWidth > 760) ? 'sidebar-collapsed' : ''} ${view === 'pictures' ? 'pictures-open' : ''} ${mobileSidebarOpen ? 'mobile-sidebar-open' : ''}`}>
829
687
  {mobileSidebarOpen && (
830
688
  <div
831
689
  className="sidebar-backdrop"
@@ -882,7 +740,6 @@ export default function MintDashboard() {
882
740
  onSelectImage={selectImage}
883
741
  onSelectDocument={selectDocument}
884
742
  onPasteImage={pasteImage}
885
- onReadClipboardImage={readClipboardImage}
886
743
  onSetMessage={setMessage}
887
744
  onSendVoiceMessage={sendVoiceMessage}
888
745
  onRemoveImage={(idx: number) => {
@@ -897,6 +754,7 @@ export default function MintDashboard() {
897
754
  settingsConfig={settingsConfig}
898
755
  onSetModel={changeModel}
899
756
  onApproval={handleApproval}
757
+ onCancelMessage={handleCancelMessage}
900
758
  onToggleMobileSidebar={() => setMobileSidebarOpen(!mobileSidebarOpen)}
901
759
  />
902
760
  </main>
@@ -908,6 +766,7 @@ export default function MintDashboard() {
908
766
  setView('chat')
909
767
  setMessage(imgPrompt)
910
768
  }}
769
+ onToggleMobileSidebar={() => setMobileSidebarOpen(!mobileSidebarOpen)}
911
770
  />
912
771
  </div>
913
772
  <div className={`startup-loading ${startupReady ? 'is-hidden' : ''}`} aria-live="polite" aria-busy={!startupReady}>
@@ -2,10 +2,6 @@ import { useEffect, useMemo, useState } from 'react'
2
2
  import { type PictureEntry, convertFileSrc } from '../tauri'
3
3
  import type { DashboardView } from './DashboardSidebar'
4
4
 
5
- const INITIAL_VISIBLE_PICTURES = 18
6
- const PICTURE_RENDER_BATCH_SIZE = 18
7
- const PICTURE_RENDER_BATCH_DELAY_MS = 80
8
-
9
5
  interface PicturesLibraryProps {
10
6
  view: DashboardView
11
7
  pictures: PictureEntry[]
@@ -14,31 +10,16 @@ interface PicturesLibraryProps {
14
10
  }
15
11
 
16
12
  export default function PicturesLibrary({ view, pictures, onSetView, onRefreshPictures }: PicturesLibraryProps) {
17
- const [visibleCount, setVisibleCount] = useState(INITIAL_VISIBLE_PICTURES)
13
+ const [visibleCount, setVisibleCount] = useState(24)
18
14
  const visiblePictures = useMemo(
19
15
  () => pictures.slice(0, visibleCount),
20
16
  [pictures, visibleCount],
21
17
  )
22
18
 
23
19
  useEffect(() => {
24
- if (view !== 'pictures') {
25
- setVisibleCount(INITIAL_VISIBLE_PICTURES)
26
- return
27
- }
28
-
29
- setVisibleCount(INITIAL_VISIBLE_PICTURES)
20
+ setVisibleCount(24)
30
21
  }, [view, pictures])
31
22
 
32
- useEffect(() => {
33
- if (view !== 'pictures' || visibleCount >= pictures.length) return
34
-
35
- const timer = window.setTimeout(() => {
36
- setVisibleCount((current) => Math.min(current + PICTURE_RENDER_BATCH_SIZE, pictures.length))
37
- }, PICTURE_RENDER_BATCH_DELAY_MS)
38
-
39
- return () => window.clearTimeout(timer)
40
- }, [view, pictures.length, visibleCount])
41
-
42
23
  return (
43
24
  <section className={`pictures-library ${view === 'pictures' ? 'is-visible' : ''}`} aria-hidden={view !== 'pictures'}>
44
25
  <header className="pictures-header">
@@ -61,14 +42,27 @@ export default function PicturesLibrary({ view, pictures, onSetView, onRefreshPi
61
42
  <span>Images appear here after a message with an attachment is sent successfully.</span>
62
43
  </div>
63
44
  ) : (
64
- <div className="pictures-grid">
65
- {visiblePictures.map((picture, index) => (
66
- <article className="picture-card" key={picture.id}>
67
- <img src={convertFileSrc(picture.thumbnailPath || picture.thumbnailUrl || picture.path)} alt={picture.message || picture.filename} loading={index < 6 ? 'eager' : 'lazy'} decoding="async" />
68
- <div className="picture-card-meta"><span>{picture.message || picture.filename}</span></div>
69
- </article>
70
- ))}
71
- </div>
45
+ <>
46
+ <div className="pictures-grid">
47
+ {visiblePictures.map((picture, index) => (
48
+ <article className="picture-card" key={picture.id}>
49
+ <img src={convertFileSrc(picture.thumbnailPath || picture.thumbnailUrl || picture.path)} alt={picture.message || picture.filename} loading={index < 6 ? 'eager' : 'lazy'} decoding="async" />
50
+ <div className="picture-card-meta"><span>{picture.message || picture.filename}</span></div>
51
+ </article>
52
+ ))}
53
+ </div>
54
+ {visibleCount < pictures.length && (
55
+ <div className="pictures-load-more-container">
56
+ <button
57
+ type="button"
58
+ className="pictures-load-more-btn"
59
+ onClick={() => setVisibleCount((prev) => prev + 24)}
60
+ >
61
+ Load More ({pictures.length - visibleCount} remaining)
62
+ </button>
63
+ </div>
64
+ )}
65
+ </>
72
66
  )}
73
67
  </section>
74
68
  )