@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
@@ -0,0 +1,23 @@
1
+ Description: The manpage shows methods that don't work. They have been replaced.
2
+ From: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=447402
3
+ Forwarded: no
4
+ --- a/vorbisgain.1
5
+ +++ b/vorbisgain.1
6
+ @@ -110,7 +110,7 @@
7
+ shell to not do any filename globbing:
8
+ .PP
9
+ .RS
10
+ -vorbisgain \-a \-f \-r "music/*.ogg"
11
+ +vorbisgain \-a \-f \-r music/
12
+ .RE
13
+ .PP
14
+ Calculate the album gain. The files specified before the directory "album"
15
+ @@ -124,7 +124,7 @@
16
+ Remove all replaygain tags from a collection of oggs:
17
+ .PP
18
+ .RS
19
+ -vorbisgain \-c \-r "music/*.ogg"
20
+ +vorbisgain \-c \-r music/
21
+ .RE
22
+ .SH TAG FORMAT
23
+ .B vorbisgain
@@ -0,0 +1,13 @@
1
+ Description: Allow build with dh compat level 9 (debian build flags)
2
+ Forwarded: no
3
+ --- a/misc.c
4
+ +++ b/misc.c
5
+ @@ -56,7 +56,7 @@
6
+ vfprintf(stderr, message, args);
7
+ va_end(args);
8
+
9
+ - fprintf(stderr, strerror(err_num));
10
+ + fputs(strerror(err_num), stderr);
11
+ fprintf(stderr, "\n");
12
+ }
13
+
@@ -0,0 +1,13 @@
1
+ Solve "too many open files" by closing those which are not compatible with vorbisgain. Thanks to Marcel Rehberg for the patch.
2
+ --- a/vorbis.c
3
+ +++ b/vorbis.c
4
+ @@ -357,7 +357,8 @@
5
+ {
6
+ vorbis_error(result, _("Couldn't process '%s': "), filename);
7
+ }
8
+ -
9
+ + // make sure file is closed, since ov_open failed we are still responsible
10
+ + fclose(file);
11
+ return -1;
12
+ }
13
+
@@ -0,0 +1,23 @@
1
+ Vorbisgain comes in two version;
2
+ - The main source: https://sjeng.org/ftp/vorbis/
3
+ - The Debian version: http://deb.debian.org/debian/pool/main/v/vorbisgain/
4
+
5
+ The catch is... Debian has 10 patches:
6
+ - 0001-temp_files.patch
7
+ - 0002-errno.patch
8
+ - 0003-manpage.patch
9
+ - 0004-vorbisgain_mtime.patch
10
+ - 0005-double_fclose.patch
11
+ - 0006-manpage_hyphens.patch
12
+ - 0007-recursively_spelling.patch
13
+ - 0008-manpage_recursion_mistake.patch
14
+ - 0009-hardening.patch
15
+ - 0010-fclose.patch
16
+
17
+ Of particular concern are 0005, 0009 and 0010 which might have implications for memory-leaks and string inputs.
18
+
19
+ ## Other points of concern
20
+
21
+ https://pkgs.org/download/vorbisgain
22
+
23
+ Lots of distributions don't include these patches as well, but I prefer to stick with the tried and tested package from Debian.
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "vorbisgain",
3
+ "buildsystem": "autotools",
4
+ "cleanup": [
5
+ "/man"
6
+ ],
7
+ "sources": [
8
+ {
9
+ "type": "archive",
10
+ "url": "http://deb.debian.org/debian/pool/main/v/vorbisgain/vorbisgain_0.37.orig.tar.gz",
11
+ "sha256": "dd6db051cad972bcac25d47b4a9e40e217bb548a1f16328eddbb4e66613530ec"
12
+ },
13
+ {
14
+ "type": "patch",
15
+ "paths": [
16
+ "0001-temp_files.patch",
17
+ "0002-errno.patch",
18
+ "0003-manpage.patch",
19
+ "0004-vorbisgain_mtime.patch",
20
+ "0005-double_fclose.patch",
21
+ "0006-manpage_hyphens.patch",
22
+ "0007-recursively_spelling.patch",
23
+ "0008-manpage_recursion_mistake.patch",
24
+ "0009-hardening.patch",
25
+ "0010-fclose.patch"
26
+ ]
27
+ }
28
+ ]
29
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * shared/agentProgress.ts
3
+ * Shared utilities for AgentProgress events — used by both Desktop and Web renderers.
4
+ * Source of truth: import from this file, not from the per-renderer copies.
5
+ */
6
+ import type { AgentProgress, InteractionMemory, DiffHunk, FileChange } from './types'
7
+ export type { AgentProgress, InteractionMemory }
8
+
9
+ export function thoughtsFrom(progress: AgentProgress[]): string[] {
10
+ return progress
11
+ .filter((event) => event.type === 'Thought')
12
+ .map((event) => (event as Extract<AgentProgress, { type: 'Thought' }>).data.thought)
13
+ .filter(Boolean)
14
+ }
15
+
16
+ export function trimAgentProgress(progress: AgentProgress[], maxNonThought = 20): AgentProgress[] {
17
+ if (progress.length === 0) return progress
18
+ const keptNonThought = new Set(
19
+ progress.filter((event) => event.type !== 'Thought').slice(-maxNonThought),
20
+ )
21
+ return progress.filter((event) => event.type === 'Thought' || keptNonThought.has(event))
22
+ }
23
+
24
+ export function hasAgentToolActivity(progress: AgentProgress[]): boolean {
25
+ return progress.some((event) => event.type === 'ToolStart' || event.type === 'ToolEnd')
26
+ }
27
+
28
+ export function mergeActivitySnapshots(
29
+ current: Record<string, AgentProgress[]>,
30
+ interactions: InteractionMemory[],
31
+ ): Record<string, AgentProgress[]> {
32
+ const merged = { ...current }
33
+ for (const interaction of interactions) {
34
+ const key = String(interaction.id)
35
+ if (interaction.agentActivity?.length && !merged[key]) {
36
+ merged[key] = interaction.agentActivity
37
+ }
38
+ }
39
+ return merged
40
+ }
41
+
42
+ export function parseAgentActivity(value: unknown): AgentProgress[] | undefined {
43
+ if (!Array.isArray(value)) return undefined
44
+ return value as AgentProgress[]
45
+ }
46
+
47
+ export function parseFileChangesFromProgress(progress: AgentProgress[]): FileChange[] {
48
+ const changes = new Map<string, FileChange>()
49
+ let activeEdit: { action: string; path: string; created: boolean; additions: number; deletions: number; hunks: DiffHunk[] } | null = null
50
+
51
+ for (const event of progress || []) {
52
+ if (event.type === 'ToolStart') {
53
+ if (event.data.action === 'apply_patch') {
54
+ const patch = (event.data.input as any)?.patch
55
+ if (patch && typeof patch.path === 'string') {
56
+ let additions = 0
57
+ let deletions = 0
58
+ const hunksList: DiffHunk[] = []
59
+ const hunks = patch.hunks
60
+ if (Array.isArray(hunks)) {
61
+ for (const hunk of hunks) {
62
+ const oldText = hunk?.oldText || ''
63
+ const newText = hunk?.newText || ''
64
+ const oldLines = oldText ? oldText.split('\n').length : 0
65
+ const newLines = newText ? newText.split('\n').length : 0
66
+ deletions += oldLines
67
+ additions += newLines
68
+ hunksList.push({ oldText, newText })
69
+ }
70
+ }
71
+ activeEdit = {
72
+ action: 'apply_patch',
73
+ path: patch.path,
74
+ created: false,
75
+ additions,
76
+ deletions,
77
+ hunks: hunksList
78
+ }
79
+ }
80
+ } else if (event.data.action === 'write_file') {
81
+ const path = (event.data.input as any)?.path
82
+ const fileContent = (event.data.input as any)?.file_content || ''
83
+ if (typeof path === 'string') {
84
+ const additions = fileContent ? fileContent.split('\n').length : 0
85
+ activeEdit = {
86
+ action: 'write_file',
87
+ path,
88
+ created: true,
89
+ additions,
90
+ deletions: 0,
91
+ hunks: [{ oldText: '', newText: fileContent }]
92
+ }
93
+ }
94
+ } else {
95
+ activeEdit = null
96
+ }
97
+ } else if (event.type === 'ToolEnd') {
98
+ if (activeEdit && (event.data.action === 'apply_patch' || event.data.action === 'write_file')) {
99
+ const isError = typeof event.data.result === 'string' && event.data.result.startsWith('Error:')
100
+ if (!isError) {
101
+ try {
102
+ const applied = JSON.parse(event.data.result)
103
+ const appliedPaths = Array.isArray(applied) ? applied.map(item => item?.path).filter(Boolean) : [activeEdit.path]
104
+
105
+ for (const path of appliedPaths) {
106
+ const existing = changes.get(path)
107
+ if (existing) {
108
+ existing.additions += activeEdit.additions
109
+ existing.deletions += activeEdit.deletions
110
+ existing.hunks.push(...activeEdit.hunks)
111
+ } else {
112
+ changes.set(path, {
113
+ path,
114
+ created: activeEdit.created,
115
+ additions: activeEdit.additions,
116
+ deletions: activeEdit.deletions,
117
+ hunks: [...activeEdit.hunks]
118
+ })
119
+ }
120
+ }
121
+ } catch (e) {
122
+ const path = activeEdit.path
123
+ const existing = changes.get(path)
124
+ if (existing) {
125
+ existing.additions += activeEdit.additions
126
+ existing.deletions += activeEdit.deletions
127
+ existing.hunks.push(...activeEdit.hunks)
128
+ } else {
129
+ changes.set(path, {
130
+ path,
131
+ created: activeEdit.created,
132
+ additions: activeEdit.additions,
133
+ deletions: activeEdit.deletions,
134
+ hunks: [...activeEdit.hunks]
135
+ })
136
+ }
137
+ }
138
+ }
139
+ }
140
+ activeEdit = null
141
+ }
142
+ }
143
+
144
+ return Array.from(changes.values())
145
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * shared/calculator.ts
3
+ * Arithmetic expression evaluator — shared by both Desktop and Web renderers.
4
+ * Source of truth: import from this file, not from the per-renderer copies.
5
+ */
6
+ export function evaluateArithmetic(source: string): number {
7
+ const tokens = source.replace(/\s+/g, '')
8
+ let index = 0
9
+
10
+ function parseExpression(): number {
11
+ let value = parseTerm()
12
+ while (tokens[index] === '+' || tokens[index] === '-') {
13
+ const operator = tokens[index++]
14
+ const right = parseTerm()
15
+ value = operator === '+' ? value + right : value - right
16
+ }
17
+ return value
18
+ }
19
+
20
+ function parseTerm(): number {
21
+ let value = parseFactor()
22
+ while (tokens[index] === '*' || tokens[index] === '/') {
23
+ const operator = tokens[index++]
24
+ const right = parseFactor()
25
+ value = operator === '*' ? value * right : value / right
26
+ }
27
+ return value
28
+ }
29
+
30
+ function parseFactor(): number {
31
+ if (tokens[index] === '+' || tokens[index] === '-') {
32
+ return tokens[index++] === '-' ? -parseFactor() : parseFactor()
33
+ }
34
+ if (tokens[index] === '(') {
35
+ index += 1
36
+ const value = parseExpression()
37
+ if (tokens[index++] !== ')') throw new Error('missing closing parenthesis')
38
+ return value
39
+ }
40
+ const match = tokens.slice(index).match(/^(?:\d+(?:\.\d*)?|\.\d+)/)
41
+ if (!match) throw new Error('expected a number')
42
+ index += match[0].length
43
+ return Number(match[0])
44
+ }
45
+
46
+ if (!tokens) throw new Error('expression is empty')
47
+ const result = parseExpression()
48
+ if (index !== tokens.length || !Number.isFinite(result)) {
49
+ throw new Error('invalid arithmetic expression')
50
+ }
51
+ return result
52
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * shared/components/AgentActivityDrawer.tsx
3
+ * Togglable drawer containing the agent activities list/table.
4
+ * Shared by both Desktop and Web ChatPanel — do NOT duplicate this.
5
+ */
6
+ import React from 'react'
7
+ import type { AgentActivityView } from '../utils/agentActivity'
8
+ import { AgentActivityTable } from './AgentActivityTable'
9
+
10
+
11
+ interface Props {
12
+ activityView: AgentActivityView
13
+ isOpen: boolean
14
+ onToggle: () => void
15
+ isHistorical?: boolean
16
+ pendingApproval?: boolean
17
+ key?: any
18
+ }
19
+
20
+
21
+ export function AgentActivityDrawer({
22
+ activityView,
23
+ isOpen,
24
+ onToggle,
25
+ isHistorical = false,
26
+ pendingApproval = false,
27
+ }: Props) {
28
+ if (activityView.items.length === 0) return null
29
+
30
+ if (isHistorical) {
31
+ return (
32
+ <div className="agent-activity-history">
33
+ <button
34
+ type="button"
35
+ className="agent-activity-toggle"
36
+ aria-expanded={isOpen}
37
+ onClick={onToggle}
38
+ >
39
+ <span>{activityView.summary}</span>
40
+ <span aria-hidden="true">{isOpen ? '^' : '>'}</span>
41
+ </button>
42
+ {isOpen && (
43
+ <div className="agent-activity-card agent-activity-card-history">
44
+ <AgentActivityTable activityView={activityView} />
45
+ </div>
46
+ )}
47
+ </div>
48
+ )
49
+ }
50
+
51
+ // Active / Live view
52
+ return (
53
+ <div className="message ai-message agent-activity-message">
54
+ <div className="agent-activity-card">
55
+ <div className="agent-activity-header" style={{ cursor: 'pointer' }} onClick={onToggle}>
56
+ <span>{activityView.summary}</span>
57
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
58
+ <span className="agent-activity-status" data-state={pendingApproval ? 'approval' : 'active'}>
59
+ {pendingApproval ? 'Waiting for approval' : 'Working'}
60
+ </span>
61
+ <span aria-hidden="true">{isOpen ? '^' : '>'}</span>
62
+ </div>
63
+ </div>
64
+ {isOpen && (
65
+ <div style={{ marginTop: '8px' }}>
66
+ <AgentActivityTable activityView={activityView} />
67
+ </div>
68
+ )}
69
+ </div>
70
+ </div>
71
+ )
72
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * shared/components/AgentActivityTable.tsx
3
+ * Renders the "Working through task" activity drawer rows.
4
+ * Shared by both Desktop and Web ChatPanel — do NOT duplicate this.
5
+ */
6
+ import { useState } from 'react'
7
+ import type { AgentActivityView } from '../utils/agentActivity'
8
+
9
+ interface Props {
10
+ activityView: AgentActivityView
11
+ }
12
+
13
+ export function AgentActivityTable({ activityView }: Props) {
14
+ const [expandedIndices, setExpandedIndices] = useState<Record<number, boolean>>({})
15
+
16
+ const toggleExpand = (index: number) => {
17
+ setExpandedIndices(prev => ({
18
+ ...prev,
19
+ [index]: !prev[index],
20
+ }))
21
+ }
22
+
23
+ return (
24
+ <div className="agent-activity-list">
25
+ <div className="agent-activity-table-head" aria-hidden="true">
26
+ <span>Tool</span>
27
+ <span />
28
+ <span>Target</span>
29
+ <span />
30
+ </div>
31
+ {activityView.items.map((activity, index) => {
32
+ const isExpanded = !!expandedIndices[index]
33
+ return (
34
+ <div
35
+ className="agent-activity-item"
36
+ data-kind={activity.kind}
37
+ data-state={activity.state}
38
+ key={`${index}-${activity.label}-${activity.target}`}
39
+ style={{ cursor: 'pointer' }}
40
+ onClick={() => toggleExpand(index)}
41
+ >
42
+ <span className="agent-activity-label">{activity.label}</span>
43
+ <span className="agent-activity-icon" aria-hidden="true" />
44
+ <span
45
+ className="agent-activity-text"
46
+ style={isExpanded ? { whiteSpace: 'normal', wordBreak: 'break-all', overflow: 'visible', textOverflow: 'clip' } : undefined}
47
+ >
48
+ {activity.target}
49
+ </span>
50
+ <span
51
+ className="agent-activity-chevron"
52
+ style={{
53
+ transform: isExpanded ? 'rotate(90deg)' : 'none',
54
+ transition: 'transform 0.2s ease',
55
+ display: 'inline-block',
56
+ }}
57
+ aria-hidden="true"
58
+ >
59
+ &gt;
60
+ </span>
61
+ </div>
62
+ )
63
+ })}
64
+ </div>
65
+ )
66
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * shared/components/ApprovalCard.tsx
3
+ * Renders the pending tool/action approval card with diffs and input fields.
4
+ * Shared by both Desktop and Web ChatPanel — do NOT duplicate this.
5
+ */
6
+ import React, { useState } from 'react'
7
+ import { renderApprovalDetails, renderDiff } from '../utils/approval'
8
+
9
+ interface Props {
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ pendingApproval: any
12
+ onApproval: (approved: boolean, autoApproveSession?: boolean, answer?: string) => void
13
+ key?: any
14
+ }
15
+
16
+
17
+ export function ApprovalCard({ pendingApproval, onApproval }: Props) {
18
+ const [askAnswer, setAskAnswer] = useState('')
19
+
20
+ const details = renderApprovalDetails(pendingApproval.approval)
21
+ const writeFile = pendingApproval.approval?.WriteFile
22
+ const applyPatch = pendingApproval.approval?.ApplyPatch
23
+ const diffText = writeFile?.diff || applyPatch?.diff
24
+ const isAskUser = !!pendingApproval.approval?.AskUser
25
+
26
+ return (
27
+ <div className="message ai-message" style={{ width: '100%' }}>
28
+ <div className="bubble-wrapper" style={{ width: '100%' }}>
29
+ <div className="action-card approval-card" data-tier={details.isDangerous ? 'dangerous' : undefined} style={{ width: '100%' }}>
30
+ <div className="approval-card-content" style={{ width: '100%' }}>
31
+ <div className="approval-card-title">{details.title}</div>
32
+ <div className="approval-card-body" style={{ whiteSpace: 'pre-wrap', fontFamily: 'monospace' }}>{details.body}</div>
33
+ {diffText ? (
34
+ <div className="approval-card-diff-container" style={{ marginTop: '8px', width: '100%' }}>
35
+ <div style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-soft, #94a3b8)', marginBottom: '4px' }}>Diff:</div>
36
+ {renderDiff(diffText)}
37
+ </div>
38
+ ) : (
39
+ details.reason && <div className="approval-card-reason" style={{ whiteSpace: 'pre-wrap', fontFamily: 'monospace' }}>{details.reason}</div>
40
+ )}
41
+ {isAskUser && (
42
+ <div className="approval-card-input-container" style={{ marginTop: '12px', width: '100%' }}>
43
+ <textarea
44
+ style={{
45
+ width: '100%',
46
+ minHeight: '70px',
47
+ padding: '10px',
48
+ background: 'rgba(0, 0, 0, 0.2)',
49
+ border: '1px solid rgba(255, 255, 255, 0.1)',
50
+ borderRadius: '6px',
51
+ color: 'var(--text-main, #f8fafc)',
52
+ fontSize: '0.9rem',
53
+ fontFamily: 'inherit',
54
+ resize: 'vertical',
55
+ outline: 'none',
56
+ }}
57
+ placeholder="Type your answer here..."
58
+ value={askAnswer}
59
+ onChange={(e) => setAskAnswer(e.target.value)}
60
+ />
61
+ </div>
62
+ )}
63
+ </div>
64
+ {isAskUser ? (
65
+ <div className="approval-card-actions">
66
+ <button
67
+ type="button"
68
+ className="approval-btn approval-btn-approve"
69
+ disabled={!askAnswer.trim()}
70
+ onClick={() => onApproval(true, false, askAnswer)}
71
+ >
72
+ Submit Answer
73
+ </button>
74
+ <button
75
+ type="button"
76
+ className="approval-btn approval-btn-cancel"
77
+ onClick={() => onApproval(false)}
78
+ >
79
+ Decline
80
+ </button>
81
+ </div>
82
+ ) : (
83
+ <div className="approval-card-actions">
84
+ <button type="button" className="approval-btn approval-btn-approve" onClick={() => onApproval(true)}>Approve</button>
85
+ <button type="button" className="approval-btn" style={{ backgroundColor: 'rgba(16, 185, 129, 0.22)', borderColor: 'rgba(16, 185, 129, 0.4)', color: '#a7f3d0' }} onClick={() => onApproval(true, true)}>Approve this session</button>
86
+ <button type="button" className="approval-btn approval-btn-cancel" onClick={() => onApproval(false)}>Cancel</button>
87
+ </div>
88
+ )}
89
+ </div>
90
+ </div>
91
+ </div>
92
+ )
93
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * shared/components/ChatCodeBlock.tsx
3
+ * Syntax-highlighted code block with copy + download actions.
4
+ * Shared by both Desktop and Web ChatPanel — do NOT duplicate this.
5
+ */
6
+ import { useState } from 'react'
7
+
8
+ interface Props {
9
+ code: string
10
+ language: string
11
+ key?: any
12
+ }
13
+
14
+
15
+ export function ChatCodeBlock({ code, language }: Props) {
16
+ const [copied, setCopied] = useState(false)
17
+
18
+ const handleCopy = async () => {
19
+ try {
20
+ await navigator.clipboard.writeText(code)
21
+ setCopied(true)
22
+ setTimeout(() => setCopied(false), 2000)
23
+ } catch (err) {
24
+ console.error('Failed to copy code: ', err)
25
+ }
26
+ }
27
+
28
+ const handleDownload = () => {
29
+ try {
30
+ const blob = new Blob([code], { type: 'text/plain;charset=utf-8' })
31
+ const url = URL.createObjectURL(blob)
32
+ const link = document.createElement('a')
33
+ link.href = url
34
+ link.download = `code.${language === 'plaintext' ? 'txt' : language}`
35
+ link.click()
36
+ URL.revokeObjectURL(url)
37
+ } catch (err) {
38
+ console.error('Failed to download code: ', err)
39
+ }
40
+ }
41
+
42
+ const displayLang = language.charAt(0).toUpperCase() + language.slice(1).toLowerCase()
43
+
44
+ return (
45
+ <div className="chat-code-block-container" style={{ whiteSpace: 'normal' }}>
46
+ <div className="chat-code-block-header">
47
+ <span className="chat-code-block-lang">{displayLang}</span>
48
+ <div className="chat-code-block-actions">
49
+ <button type="button" onClick={handleDownload} title="Download code" className="chat-code-action-btn">
50
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
51
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
52
+ <polyline points="7 10 12 15 17 10" />
53
+ <line x1="12" y1="15" x2="12" y2="3" />
54
+ </svg>
55
+ </button>
56
+ <button type="button" onClick={handleCopy} title="Copy code" className="chat-code-action-btn">
57
+ {copied ? (
58
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
59
+ <polyline points="20 6 9 17 4 12" />
60
+ </svg>
61
+ ) : (
62
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
63
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
64
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
65
+ </svg>
66
+ )}
67
+ </button>
68
+ </div>
69
+ </div>
70
+ <pre className="chat-code-block-body">
71
+ <code>{code}</code>
72
+ </pre>
73
+ </div>
74
+ )
75
+ }