@pheem49/mint 1.8.1 → 1.9.0

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/Cargo.lock +3 -3
  2. package/Cargo.toml +1 -1
  3. package/README.md +0 -17
  4. package/Release_Note.md +137 -58
  5. package/bin/mint +0 -0
  6. package/crates/mint-cli/src/agent.rs +150 -45
  7. package/crates/mint-cli/src/image.rs +8 -8
  8. package/crates/mint-cli/src/main.rs +929 -412
  9. package/crates/mint-cli/src/onboard.rs +108 -108
  10. package/crates/mint-cli/src/setup.rs +143 -143
  11. package/crates/mint-core/src/agent_loop.rs +1 -0
  12. package/crates/mint-core/src/api_server.rs +108 -52
  13. package/crates/mint-core/src/browser.rs +56 -1
  14. package/crates/mint-core/src/channels.rs +1 -0
  15. package/crates/mint-core/src/chat.rs +126 -8
  16. package/crates/mint-core/src/code_tools.rs +18 -20
  17. package/crates/mint-core/src/config.rs +245 -17
  18. package/crates/mint-core/src/image_gen.rs +5 -5
  19. package/crates/mint-core/src/lib.rs +16 -2
  20. package/crates/mint-core/src/memory.rs +6 -1
  21. package/crates/mint-core/src/orchestration.rs +260 -72
  22. package/crates/mint-core/src/pictures.rs +3 -4
  23. package/crates/mint-core/src/semantic.rs +1 -1
  24. package/crates/mint-core/src/shell.rs +1 -1
  25. package/crates/mint-core/src/skills.rs +46 -19
  26. package/crates/mint-core/src/tts.rs +3 -1
  27. package/flatpak/com.pheem49.mint.desktop +9 -0
  28. package/flatpak/com.pheem49.mint.metainfo.xml +35 -0
  29. package/flatpak/com.pheem49.mint.yaml +52 -0
  30. package/package.json +1 -1
  31. package/shared-modules/.github/workflows/ci.yml +120 -0
  32. package/shared-modules/.github/workflows/update.yaml +28 -0
  33. package/shared-modules/CODEOWNERS +23 -0
  34. package/shared-modules/README.md +79 -0
  35. package/shared-modules/SDL/README.md +3 -0
  36. package/shared-modules/SDL/SDL-1.2.15.json +51 -0
  37. package/shared-modules/SDL/SDL_Pango-0.1.2-API-adds.patch +118 -0
  38. package/shared-modules/SDL/SDL_image-1.2.12.json +25 -0
  39. package/shared-modules/SDL/SDL_mixer-1.2.12.json +27 -0
  40. package/shared-modules/SDL/SDL_net-1.2.8.json +19 -0
  41. package/shared-modules/SDL/SDL_pango-0.1.2.json +26 -0
  42. package/shared-modules/SDL/SDL_ttf-2.0.11.json +28 -0
  43. package/shared-modules/SDL/libsdl-1.2.15-strict-prototypes.patch +316 -0
  44. package/shared-modules/SDL/sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch +24 -0
  45. package/shared-modules/SDL/sdl-libx11-build.patch +59 -0
  46. package/shared-modules/SDL/sdl-sysloadso-buffer-length.patch +27 -0
  47. package/shared-modules/SDL/sdl12-compat.json +22 -0
  48. package/shared-modules/SDL2/SDL2_image.json +27 -0
  49. package/shared-modules/SDL2/SDL2_ttf.json +29 -0
  50. package/shared-modules/cld2/CMakeLists.txt +157 -0
  51. package/shared-modules/cld2/cld2.json +24 -0
  52. package/shared-modules/clutter/clutter.json +72 -0
  53. package/shared-modules/dbus-glib/dbus-glib.json +29 -0
  54. package/shared-modules/glew/glew.json +36 -0
  55. package/shared-modules/glu/glu-9.json +12 -0
  56. package/shared-modules/gtk2/arc-gtk2-theme-Replace-Inkscape-dependency-with-rsvg-convert.patch +180 -0
  57. package/shared-modules/gtk2/gtk2-common-themes.json +360 -0
  58. package/shared-modules/gtk2/gtk2-fix-crash-in-show-uri.patch +30 -0
  59. package/shared-modules/gtk2/gtk2-gcc14.patch +281 -0
  60. package/shared-modules/gtk2/gtk2-gcc15.patch +250 -0
  61. package/shared-modules/gtk2/gtk2-murrine-c99.patch +34 -0
  62. package/shared-modules/gtk2/gtk2-use-adwaita-theme.patch +80 -0
  63. package/shared-modules/gtk2/gtk2.json +120 -0
  64. package/shared-modules/gtk2/ibus-gcc15.patch +13 -0
  65. package/shared-modules/gtk2/murrine-engine-fix-crash.patch +12 -0
  66. package/shared-modules/gtk2/readme.md +10 -0
  67. package/shared-modules/intltool/intltool-0.51.json +20 -0
  68. package/shared-modules/intltool/intltool-perl5.26-regex-fixes.patch +59 -0
  69. package/shared-modules/lame/0001-Build-xmm_quantize_sub.c-with-msse.patch +34 -0
  70. package/shared-modules/lame/0002-Include-GTK-1-autoconf-directives-in-build-system.patch +222 -0
  71. package/shared-modules/lame/0003-Patch-out-remaining-ansi2knr.patch +70 -0
  72. package/shared-modules/lame/0004-initscr-is-not-used-anywhere-in-lame-sourcetree.patch +41 -0
  73. package/shared-modules/lame/0005-Update-autoconf.patch +81 -0
  74. package/shared-modules/lame/lame-3.99.5.json +32 -0
  75. package/shared-modules/libappindicator/libappindicator-gtk2-12.10.json +140 -0
  76. package/shared-modules/libappindicator/libappindicator-gtk3-12.10.json +140 -0
  77. package/shared-modules/libappindicator/libappindicator-gtk3-introspection-12.10.json +140 -0
  78. package/shared-modules/libappindicator/patches/0001-libappindicator-ftbfs.patch +20 -0
  79. package/shared-modules/libappindicator/patches/0002-libappindicator-no-python.patch +888 -0
  80. package/shared-modules/libappindicator/patches/0003-libappindicator-fix-crash-from-incorrect-signal-emission.patch +74 -0
  81. package/shared-modules/libappindicator/patches/0004-libappindicator-fix-crash-iterating-icon-themes.patch +11 -0
  82. package/shared-modules/libayatana-appindicator/0001-Fix-HAVE_VALGRIND-AM_CONDITIONAL.patch +51 -0
  83. package/shared-modules/libayatana-appindicator/0001-Make-introspection-configurable.patch +44 -0
  84. package/shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json +121 -0
  85. package/shared-modules/libcanberra/0001-gtk-Don-t-assume-all-GdkDisplays-are-GdkX11Displays-.patch +123 -0
  86. package/shared-modules/libcanberra/libcanberra.json +44 -0
  87. package/shared-modules/libmad/libmad.json +19 -0
  88. package/shared-modules/libsecret/libsecret.json +23 -0
  89. package/shared-modules/libsoup/libsoup-2.4.json +14 -0
  90. package/shared-modules/libusb/libusb.json +27 -0
  91. package/shared-modules/linux-audio/dssi.json +31 -0
  92. package/shared-modules/linux-audio/fftw3f-static.json +44 -0
  93. package/shared-modules/linux-audio/fftw3f.json +42 -0
  94. package/shared-modules/linux-audio/fluidsynth2-static.json +36 -0
  95. package/shared-modules/linux-audio/fluidsynth2.json +25 -0
  96. package/shared-modules/linux-audio/ladspa.json +26 -0
  97. package/shared-modules/linux-audio/lash.json +35 -0
  98. package/shared-modules/linux-audio/libinstpatch.json +16 -0
  99. package/shared-modules/linux-audio/liblo-static.json +22 -0
  100. package/shared-modules/linux-audio/liblo.json +17 -0
  101. package/shared-modules/linux-audio/lilv-static.json +94 -0
  102. package/shared-modules/linux-audio/lilv.json +89 -0
  103. package/shared-modules/linux-audio/lrdf.json +57 -0
  104. package/shared-modules/linux-audio/lv2.json +20 -0
  105. package/shared-modules/linux-audio/patches/lash/0001-configure-remove-aclocal-m4.patch +29 -0
  106. package/shared-modules/linux-audio/patches/lash/0002-Fix-build-with-recent-gcc.patch +73 -0
  107. package/shared-modules/linux-audio/patches/lash/0003-Fix-linking.patch +136 -0
  108. package/shared-modules/linux-audio/patches/raptor-libxml.patch +31 -0
  109. package/shared-modules/linux-audio/patches/stk-install.sh +54 -0
  110. package/shared-modules/linux-audio/stk.json +28 -0
  111. package/shared-modules/lua5.1/0004-Fix-stack-overflow-in-vararg-functions.patch +21 -0
  112. package/shared-modules/lua5.1/lua-5.1.5-so.patch +43 -0
  113. package/shared-modules/lua5.1/lua-5.1.5.json +42 -0
  114. package/shared-modules/lua5.3/lua-5.3.5.json +41 -0
  115. package/shared-modules/lua5.3/lua.pc.in +8 -0
  116. package/shared-modules/lua5.3/luaconf.patch +47 -0
  117. package/shared-modules/lua5.3/makefile-add-liblua.so-rule.patch +47 -0
  118. package/shared-modules/lua5.4/lua-5.4.json +41 -0
  119. package/shared-modules/lua5.4/lua.pc.in +8 -0
  120. package/shared-modules/lua5.4/luaconf.patch +34 -0
  121. package/shared-modules/lua5.4/makefile-add-liblua.so-rule.patch +42 -0
  122. package/shared-modules/luajit/luajit.json +30 -0
  123. package/shared-modules/lzo/lzo.json +27 -0
  124. package/shared-modules/mac/mac.json +18 -0
  125. package/shared-modules/physfs/physfs.json +26 -0
  126. package/shared-modules/pygame/audiofile-gcc6.patch +21 -0
  127. package/shared-modules/pygame/cython-fixes.patch +34 -0
  128. package/shared-modules/pygame/fluidsynth-no-rawmidi.patch +69 -0
  129. package/shared-modules/pygame/glib.patch +25 -0
  130. package/shared-modules/pygame/pygame1-python2.json +157 -0
  131. package/shared-modules/pygtk/pygtk-c99.patch +15 -0
  132. package/shared-modules/pygtk/pygtk.json +117 -0
  133. package/shared-modules/pygtk/pygtk.patch +39 -0
  134. package/shared-modules/python2.7/python-2.7.json +48 -0
  135. package/shared-modules/smpeg/smpeg-0.4.5.json +50 -0
  136. package/shared-modules/smpeg/smpeg-am-prog-as.patch +12 -0
  137. package/shared-modules/smpeg/smpeg-export-mpegaudio-class.patch +17 -0
  138. package/shared-modules/smpeg/smpeg-gcc6.patch +40 -0
  139. package/shared-modules/smpeg/smpeg-no-gtk.patch +37 -0
  140. package/shared-modules/squashfs-tools/squashfs-tools.json +25 -0
  141. package/shared-modules/update-checker.sh +50 -0
  142. package/shared-modules/vorbisgain/0001-temp_files.patch +75 -0
  143. package/shared-modules/vorbisgain/0002-errno.patch +33 -0
  144. package/shared-modules/vorbisgain/0003-manpage.patch +248 -0
  145. package/shared-modules/vorbisgain/0004-vorbisgain_mtime.patch +183 -0
  146. package/shared-modules/vorbisgain/0005-double_fclose.patch +21 -0
  147. package/shared-modules/vorbisgain/0006-manpage_hyphens.patch +56 -0
  148. package/shared-modules/vorbisgain/0007-recursively_spelling.patch +14 -0
  149. package/shared-modules/vorbisgain/0008-manpage_recursion_mistake.patch +23 -0
  150. package/shared-modules/vorbisgain/0009-hardening.patch +13 -0
  151. package/shared-modules/vorbisgain/0010-fclose.patch +13 -0
  152. package/shared-modules/vorbisgain/README.md +23 -0
  153. package/shared-modules/vorbisgain/vorbisgain_0.37-2.json +29 -0
  154. package/src/renderer/shared/agentProgress.ts +145 -0
  155. package/src/renderer/shared/calculator.ts +52 -0
  156. package/src/renderer/shared/components/AgentActivityDrawer.tsx +72 -0
  157. package/src/renderer/shared/components/AgentActivityTable.tsx +85 -0
  158. package/src/renderer/shared/components/ApprovalCard.tsx +93 -0
  159. package/src/renderer/shared/components/ChatCodeBlock.tsx +75 -0
  160. package/src/renderer/shared/components/ThinkingBlock.tsx +101 -0
  161. package/src/renderer/shared/constants/models.ts +91 -0
  162. package/src/renderer/shared/platform.ts +82 -0
  163. package/src/renderer/shared/types.ts +140 -0
  164. package/src/renderer/shared/utils/agentActivity.ts +187 -0
  165. package/src/renderer/shared/utils/approval.ts +1 -0
  166. package/src/renderer/shared/utils/approval.tsx +64 -0
  167. package/src/renderer/shared/utils/fileIcons.ts +135 -0
  168. package/src/renderer/shared/utils/markdown.tsx +347 -0
  169. package/src/renderer/shared/utils/providers.ts +36 -0
  170. package/src/renderer/shared/utils/speech.ts +216 -0
  171. package/src/renderer/shared/utils/ui.ts +147 -0
  172. package/src/renderer/src/agentProgress.ts +11 -39
  173. package/src/renderer/src/calculator.ts +3 -46
  174. package/src/renderer/src/components/ChatPanel.tsx +219 -1308
  175. package/src/renderer/src/components/MintDashboard.tsx +67 -170
  176. package/src/renderer/src/components/PicturesLibrary.tsx +32 -8
  177. package/src/renderer/src/components/Settings/AgentsTab.tsx +381 -0
  178. package/src/renderer/src/components/Settings/AutomationTab.tsx +0 -22
  179. package/src/renderer/src/components/Settings/GeneralTab.tsx +256 -2
  180. package/src/renderer/src/components/SettingsWindow.tsx +120 -3
  181. package/src/renderer/src/components/WorkspacePanel.tsx +7 -125
  182. package/src/renderer/src/css/styles.css +100 -0
  183. package/src/renderer/src/tauri.ts +128 -143
  184. package/src/renderer/src-web/agentProgress.ts +11 -39
  185. package/src/renderer/src-web/calculator.ts +3 -46
  186. package/src/renderer/src-web/components/ChatPanel.tsx +219 -1306
  187. package/src/renderer/src-web/components/MintDashboard.tsx +42 -170
  188. package/src/renderer/src-web/components/PicturesLibrary.tsx +23 -29
  189. package/src/renderer/src-web/components/Settings/AgentsTab.tsx +381 -0
  190. package/src/renderer/src-web/components/Settings/AutomationTab.tsx +0 -22
  191. package/src/renderer/src-web/components/Settings/GeneralTab.tsx +254 -3
  192. package/src/renderer/src-web/components/SettingsWindow.tsx +127 -3
  193. package/src/renderer/src-web/css/styles.css +100 -0
  194. package/src/renderer/src-web/tauri.ts +180 -102
  195. package/src-tauri/src/desktop.rs +18 -16
  196. package/src-tauri/src/discord_rpc.rs +1 -1
  197. package/src-tauri/src/lib.rs +168 -40
  198. package/src-tauri/src/webhooks.rs +1 -0
  199. package/src-tauri/src/workflows.rs +1 -0
  200. package/src-tauri/tauri.conf.json +1 -1
@@ -0,0 +1,216 @@
1
+ import { useState, useRef, useEffect } from 'react'
2
+
3
+ export interface SpeechToTextOptions {
4
+ language?: string
5
+ message: string
6
+ sending: boolean
7
+ isSpeaking: boolean
8
+ onSendVoiceMessage: (text: string) => Promise<any>
9
+ onSetMessage: (text: string) => void
10
+ }
11
+
12
+ export function useSpeechToText({
13
+ language,
14
+ message,
15
+ sending,
16
+ isSpeaking,
17
+ onSendVoiceMessage,
18
+ onSetMessage
19
+ }: SpeechToTextOptions) {
20
+ const [isRecording, setIsRecording] = useState(false)
21
+ const [voiceMode, setVoiceMode] = useState(false)
22
+ const [voiceTranscript, setVoiceTranscript] = useState('')
23
+ const [voiceAwaitingResponse, setVoiceAwaitingResponse] = useState(false)
24
+
25
+ const recognitionRef = useRef<any>(null)
26
+ const silenceTimerRef = useRef<number | null>(null)
27
+ const restartTimerRef = useRef<number | null>(null)
28
+
29
+ const voiceModeRef = useRef(false)
30
+ const sendingRef = useRef(false)
31
+ const voiceAwaitingResponseRef = useRef(false)
32
+ const isSpeakingRef = useRef(false)
33
+ const messageRef = useRef(message)
34
+
35
+ // Sync state to refs for event handlers
36
+ useEffect(() => {
37
+ voiceModeRef.current = voiceMode
38
+ if (!voiceMode) {
39
+ clearRestartTimer()
40
+ setVoiceTranscript('')
41
+ }
42
+ }, [voiceMode])
43
+
44
+ useEffect(() => {
45
+ sendingRef.current = sending
46
+ }, [sending])
47
+
48
+ useEffect(() => {
49
+ voiceAwaitingResponseRef.current = voiceAwaitingResponse
50
+ }, [voiceAwaitingResponse])
51
+
52
+ useEffect(() => {
53
+ isSpeakingRef.current = isSpeaking
54
+ }, [isSpeaking])
55
+
56
+ useEffect(() => {
57
+ messageRef.current = message
58
+ }, [message])
59
+
60
+ const clearRestartTimer = () => {
61
+ if (restartTimerRef.current !== null) {
62
+ window.clearTimeout(restartTimerRef.current)
63
+ restartTimerRef.current = null
64
+ }
65
+ }
66
+
67
+ const clearSilenceTimer = () => {
68
+ if (silenceTimerRef.current !== null) {
69
+ window.clearTimeout(silenceTimerRef.current)
70
+ silenceTimerRef.current = null
71
+ }
72
+ }
73
+
74
+ const stopRecognition = () => {
75
+ clearRestartTimer()
76
+ clearSilenceTimer()
77
+ recognitionRef.current?.stop()
78
+ recognitionRef.current = null
79
+ setIsRecording(false)
80
+ }
81
+
82
+ const scheduleVoiceListen = (delayMs = 350) => {
83
+ clearRestartTimer()
84
+ if (!voiceModeRef.current || sendingRef.current || voiceAwaitingResponseRef.current || isSpeakingRef.current) return
85
+ restartTimerRef.current = window.setTimeout(() => {
86
+ restartTimerRef.current = null
87
+ startRecognition(true)
88
+ }, delayMs)
89
+ }
90
+
91
+ const startRecognition = (autoSend: boolean) => {
92
+ if (recognitionRef.current || sendingRef.current || voiceAwaitingResponseRef.current || isSpeakingRef.current) return
93
+
94
+ const SpeechRecognitionApi = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition
95
+ if (!SpeechRecognitionApi) {
96
+ alert('Speech-to-text (Speech Recognition) is not supported in this browser/WebView. Please use Google Chrome or Microsoft Edge to enable voice conversation.')
97
+ voiceModeRef.current = false
98
+ setVoiceMode(false)
99
+ return
100
+ }
101
+
102
+ let accumulatedTranscript = ''
103
+ clearSilenceTimer()
104
+
105
+ try {
106
+ const recognition = new SpeechRecognitionApi()
107
+ recognition.continuous = true
108
+ recognition.interimResults = true
109
+ recognition.lang = language === 'en' ? 'en-US' : 'th-TH'
110
+
111
+ const resetSilenceTimeout = () => {
112
+ clearSilenceTimer()
113
+ if (autoSend) {
114
+ silenceTimerRef.current = window.setTimeout(() => {
115
+ recognition.stop()
116
+ }, 2000)
117
+ }
118
+ }
119
+
120
+ recognition.onstart = () => {
121
+ setIsRecording(true)
122
+ }
123
+ recognition.onresult = (event: any) => {
124
+ let interimText = ''
125
+ let finalText = ''
126
+ for (let index = event.resultIndex; index < event.results.length; index += 1) {
127
+ const transcript = event.results[index]?.[0]?.transcript ?? ''
128
+ if (event.results[index]?.isFinal) {
129
+ finalText += transcript
130
+ } else {
131
+ interimText += transcript
132
+ }
133
+ }
134
+ const displayText = (finalText || interimText).trim()
135
+ if (displayText) setVoiceTranscript(displayText)
136
+ if (finalText.trim()) {
137
+ accumulatedTranscript = finalText.trim()
138
+ }
139
+ if (displayText) {
140
+ resetSilenceTimeout()
141
+ }
142
+ }
143
+ recognition.onerror = (event: any) => {
144
+ console.error('Speech recognition error', event)
145
+ setIsRecording(false)
146
+ clearSilenceTimer()
147
+ }
148
+ recognition.onend = () => {
149
+ recognitionRef.current = null
150
+ setIsRecording(false)
151
+ clearSilenceTimer()
152
+ const finalText = accumulatedTranscript.trim()
153
+ if (finalText) {
154
+ if (autoSend) {
155
+ voiceAwaitingResponseRef.current = true
156
+ setVoiceAwaitingResponse(true)
157
+ onSendVoiceMessage(finalText)
158
+ .catch((error: any) => console.error('Voice message failed', error))
159
+ .finally(() => {
160
+ voiceAwaitingResponseRef.current = false
161
+ setVoiceAwaitingResponse(false)
162
+ scheduleVoiceListen()
163
+ })
164
+ } else {
165
+ onSetMessage(messageRef.current.trim() ? `${messageRef.current.trimEnd()} ${finalText}` : finalText)
166
+ }
167
+ } else {
168
+ if (autoSend && voiceModeRef.current) scheduleVoiceListen()
169
+ }
170
+ }
171
+ recognitionRef.current = recognition
172
+ recognition.start()
173
+ } catch (error) {
174
+ console.error('Failed to start speech recognition', error)
175
+ recognitionRef.current = null
176
+ setIsRecording(false)
177
+ clearSilenceTimer()
178
+ }
179
+ }
180
+
181
+ // Auto-listen trigger
182
+ useEffect(() => {
183
+ if (!voiceMode || sending || voiceAwaitingResponse || isSpeaking || isRecording) return
184
+ scheduleVoiceListen()
185
+ }, [voiceMode, sending, voiceAwaitingResponse, isSpeaking, isRecording])
186
+
187
+ // Cleanup on unmount
188
+ useEffect(() => {
189
+ return () => {
190
+ clearRestartTimer()
191
+ clearSilenceTimer()
192
+ if (recognitionRef.current) {
193
+ try {
194
+ recognitionRef.current.stop()
195
+ } catch (e) {
196
+ // ignore
197
+ }
198
+ }
199
+ }
200
+ }, [])
201
+
202
+ return {
203
+ isRecording,
204
+ voiceMode,
205
+ setVoiceMode,
206
+ voiceTranscript,
207
+ setVoiceTranscript,
208
+ voiceAwaitingResponse,
209
+ voiceAwaitingResponseRef,
210
+ voiceModeRef,
211
+ startRecognition,
212
+ stopRecognition,
213
+ scheduleVoiceListen,
214
+ clearRestartTimer
215
+ }
216
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * shared/utils/ui.ts
3
+ * Shared UI and theme helper functions.
4
+ * Used by both Desktop and Web components.
5
+ */
6
+
7
+ export function numericSetting(value: unknown, fallback: number): number {
8
+ const numeric = Number(value)
9
+ return Number.isFinite(numeric) ? numeric : fallback
10
+ }
11
+
12
+ export function errorMessage(reason: unknown): string {
13
+ return reason instanceof Error ? reason.message : String(reason)
14
+ }
15
+
16
+ export function readImage(file: File): Promise<string> {
17
+ return new Promise((resolve, reject) => {
18
+ const reader = new FileReader()
19
+ reader.onload = () => resolve(String(reader.result))
20
+ reader.onerror = () => reject(reader.error ?? new Error('Unable to read image'))
21
+ reader.readAsDataURL(file)
22
+ })
23
+ }
24
+
25
+ export function readDocument(file: File): Promise<string> {
26
+ return new Promise((resolve, reject) => {
27
+ const reader = new FileReader()
28
+ reader.onload = () => resolve(String(reader.result))
29
+ reader.onerror = () => reject(reader.error ?? new Error('Unable to read document'))
30
+ reader.readAsDataURL(file)
31
+ })
32
+ }
33
+
34
+ export async function createTrimmedImagePreview(dataUri: string): Promise<string> {
35
+ const image = await new Promise<HTMLImageElement>((resolve, reject) => {
36
+ const nextImage = new Image()
37
+ nextImage.onload = () => resolve(nextImage)
38
+ nextImage.onerror = () => reject(new Error('Unable to prepare image preview'))
39
+ nextImage.src = dataUri
40
+ })
41
+
42
+ const canvas = document.createElement('canvas')
43
+ canvas.width = image.naturalWidth || image.width
44
+ canvas.height = image.naturalHeight || image.height
45
+ const context = canvas.getContext('2d', { willReadFrequently: true })
46
+ if (!context || canvas.width === 0 || canvas.height === 0) return dataUri
47
+
48
+ context.drawImage(image, 0, 0)
49
+ const pixels = context.getImageData(0, 0, canvas.width, canvas.height)
50
+ let minX = canvas.width
51
+ let minY = canvas.height
52
+ let maxX = -1
53
+ let maxY = -1
54
+
55
+ for (let y = 0; y < canvas.height; y += 1) {
56
+ for (let x = 0; x < canvas.width; x += 1) {
57
+ const alpha = pixels.data[(y * canvas.width + x) * 4 + 3]
58
+ if (alpha > 12) {
59
+ minX = Math.min(minX, x)
60
+ minY = Math.min(minY, y)
61
+ maxX = Math.max(maxX, x)
62
+ maxY = Math.max(maxY, y)
63
+ }
64
+ }
65
+ }
66
+
67
+ if (maxX < minX || maxY < minY) return dataUri
68
+
69
+ const padding = 8
70
+ const sx = Math.max(0, minX - padding)
71
+ const sy = Math.max(0, minY - padding)
72
+ const sw = Math.min(canvas.width - sx, maxX - minX + 1 + padding * 2)
73
+ const sh = Math.min(canvas.height - sy, maxY - minY + 1 + padding * 2)
74
+
75
+ if (sw >= canvas.width * 0.92 && sh >= canvas.height * 0.92) return dataUri
76
+
77
+ const previewCanvas = document.createElement('canvas')
78
+ previewCanvas.width = sw
79
+ previewCanvas.height = sh
80
+ const previewContext = previewCanvas.getContext('2d')
81
+ if (!previewContext) return dataUri
82
+ previewContext.drawImage(canvas, sx, sy, sw, sh, 0, 0, sw, sh)
83
+ return previewCanvas.toDataURL('image/png')
84
+ }
85
+
86
+ export const lightenColor = (hex: string, amount: number): string => {
87
+ const clean = hex.replace('#', '')
88
+ if (clean.length !== 6) return hex
89
+ const num = parseInt(clean, 16)
90
+ const r = Math.min(255, (num >> 16) + amount)
91
+ const g = Math.min(255, ((num >> 8) & 0x00FF) + amount)
92
+ const b = Math.min(255, (num & 0x0000FF) + amount)
93
+ return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`
94
+ }
95
+
96
+ export const hexToRgb = (hex: string): { r: number; g: number; b: number } => {
97
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
98
+ return result ? {
99
+ r: parseInt(result[1], 16),
100
+ g: parseInt(result[2], 16),
101
+ b: parseInt(result[3], 16),
102
+ } : { r: 15, g: 23, b: 42 }
103
+ }
104
+
105
+ export const applyThemeStyles = (cfg: any): void => {
106
+ const theme = cfg.theme || 'dark'
107
+ const accentColor = cfg.accentColor || '#4f83e6'
108
+ const systemTextColor = cfg.systemTextColor || '#f8fafc'
109
+
110
+ document.documentElement.setAttribute('data-theme', theme)
111
+ document.documentElement.style.setProperty('--accent', accentColor)
112
+ document.documentElement.style.setProperty('--accent-hover', lightenColor(accentColor, 20))
113
+ document.documentElement.style.setProperty('--text-main', systemTextColor)
114
+ document.documentElement.style.setProperty('--glass-blur', cfg.glassBlur || 'blur(16px)')
115
+ document.body.style.fontFamily = cfg.fontFamily || "'Outfit', sans-serif"
116
+ document.documentElement.style.fontSize = cfg.fontSize || '18px'
117
+
118
+ if (theme === 'custom') {
119
+ if (cfg.customBgStart && cfg.customBgEnd) {
120
+ document.documentElement.style.setProperty('--bg-color', cfg.customBgStart)
121
+ document.documentElement.style.setProperty('--bg-gradient', `linear-gradient(135deg, ${cfg.customBgStart} 0%, ${cfg.customBgEnd} 100%)`)
122
+ }
123
+ if (cfg.customPanelBg) {
124
+ const rgb = hexToRgb(cfg.customPanelBg)
125
+ document.documentElement.style.setProperty('--panel-bg', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.75)`)
126
+ document.documentElement.style.setProperty('--panel-raised', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.82)`)
127
+ document.documentElement.style.setProperty('--panel-soft', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.46)`)
128
+ document.documentElement.style.setProperty('--chrome-bg', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.88)`)
129
+ document.documentElement.style.setProperty('--surface-bg', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.62)`)
130
+ document.documentElement.style.setProperty('--surface-strong', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.86)`)
131
+ document.documentElement.style.setProperty('--input-bg', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.72)`)
132
+ }
133
+ return
134
+ }
135
+
136
+ ;[
137
+ '--bg-color',
138
+ '--bg-gradient',
139
+ '--panel-bg',
140
+ '--panel-raised',
141
+ '--panel-soft',
142
+ '--chrome-bg',
143
+ '--surface-bg',
144
+ '--surface-strong',
145
+ '--input-bg',
146
+ ].forEach((prop) => document.documentElement.style.removeProperty(prop))
147
+ }
@@ -1,39 +1,11 @@
1
- import type { AgentProgress, InteractionMemory } from './tauri'
2
-
3
- export function thoughtsFrom(progress: AgentProgress[]): string[] {
4
- return progress
5
- .filter((event) => event.type === 'Thought')
6
- .map((event) => (event as Extract<AgentProgress, { type: 'Thought' }>).data.thought)
7
- .filter(Boolean)
8
- }
9
-
10
- export function trimAgentProgress(progress: AgentProgress[], maxNonThought = 20): AgentProgress[] {
11
- if (progress.length === 0) return progress
12
- const keptNonThought = new Set(
13
- progress.filter((event) => event.type !== 'Thought').slice(-maxNonThought),
14
- )
15
- return progress.filter((event) => event.type === 'Thought' || keptNonThought.has(event))
16
- }
17
-
18
- export function hasAgentToolActivity(progress: AgentProgress[]): boolean {
19
- return progress.some((event) => event.type === 'ToolStart' || event.type === 'ToolEnd')
20
- }
21
-
22
- export function mergeActivitySnapshots(
23
- current: Record<string, AgentProgress[]>,
24
- interactions: InteractionMemory[],
25
- ): Record<string, AgentProgress[]> {
26
- const merged = { ...current }
27
- for (const interaction of interactions) {
28
- const key = String(interaction.id)
29
- if (interaction.agentActivity?.length && !merged[key]) {
30
- merged[key] = interaction.agentActivity
31
- }
32
- }
33
- return merged
34
- }
35
-
36
- export function parseAgentActivity(value: unknown): AgentProgress[] | undefined {
37
- if (!Array.isArray(value)) return undefined
38
- return value as AgentProgress[]
39
- }
1
+ // Re-export everything from the shared module — do NOT add logic here.
2
+ // Edit src/renderer/shared/agentProgress.ts instead.
3
+ export type { AgentProgress, InteractionMemory } from '../shared/agentProgress'
4
+ export {
5
+ thoughtsFrom,
6
+ trimAgentProgress,
7
+ hasAgentToolActivity,
8
+ mergeActivitySnapshots,
9
+ parseAgentActivity,
10
+ parseFileChangesFromProgress,
11
+ } from '../shared/agentProgress'
@@ -1,47 +1,4 @@
1
- export function evaluateArithmetic(source: string): number {
2
- const tokens = source.replace(/\s+/g, '')
3
- let index = 0
1
+ // Re-export from the shared module — do NOT add logic here.
2
+ // Edit src/renderer/shared/calculator.ts instead.
3
+ export { evaluateArithmetic } from '../shared/calculator'
4
4
 
5
- function parseExpression(): number {
6
- let value = parseTerm()
7
- while (tokens[index] === '+' || tokens[index] === '-') {
8
- const operator = tokens[index++]
9
- const right = parseTerm()
10
- value = operator === '+' ? value + right : value - right
11
- }
12
- return value
13
- }
14
-
15
- function parseTerm(): number {
16
- let value = parseFactor()
17
- while (tokens[index] === '*' || tokens[index] === '/') {
18
- const operator = tokens[index++]
19
- const right = parseFactor()
20
- value = operator === '*' ? value * right : value / right
21
- }
22
- return value
23
- }
24
-
25
- function parseFactor(): number {
26
- if (tokens[index] === '+' || tokens[index] === '-') {
27
- return tokens[index++] === '-' ? -parseFactor() : parseFactor()
28
- }
29
- if (tokens[index] === '(') {
30
- index += 1
31
- const value = parseExpression()
32
- if (tokens[index++] !== ')') throw new Error('missing closing parenthesis')
33
- return value
34
- }
35
- const match = tokens.slice(index).match(/^(?:\d+(?:\.\d*)?|\.\d+)/)
36
- if (!match) throw new Error('expected a number')
37
- index += match[0].length
38
- return Number(match[0])
39
- }
40
-
41
- if (!tokens) throw new Error('expression is empty')
42
- const result = parseExpression()
43
- if (index !== tokens.length || !Number.isFinite(result)) {
44
- throw new Error('invalid arithmetic expression')
45
- }
46
- return result
47
- }