@pheem49/mint 1.8.2 → 1.10.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 (106) hide show
  1. package/Cargo.lock +55 -3
  2. package/Cargo.toml +2 -1
  3. package/Release_Note.md +297 -107
  4. package/bin/mint +0 -0
  5. package/crates/mint-cli/Cargo.toml +1 -0
  6. package/crates/mint-cli/src/actions.rs +233 -0
  7. package/crates/mint-cli/src/agent.rs +382 -127
  8. package/crates/mint-cli/src/gmail.rs +22 -2
  9. package/crates/mint-cli/src/image.rs +8 -8
  10. package/crates/mint-cli/src/interactive.rs +2888 -0
  11. package/crates/mint-cli/src/main.rs +391 -2506
  12. package/crates/mint-cli/src/markdown.rs +294 -0
  13. package/crates/mint-cli/src/onboard.rs +152 -109
  14. package/crates/mint-cli/src/setup.rs +143 -143
  15. package/crates/mint-cli/src/updater.rs +48 -6
  16. package/crates/mint-core/Cargo.toml +1 -0
  17. package/crates/mint-core/src/agent_loop.rs +1 -0
  18. package/crates/mint-core/src/api_server.rs +431 -65
  19. package/crates/mint-core/src/browser.rs +630 -102
  20. package/crates/mint-core/src/channels.rs +105 -6
  21. package/crates/mint-core/src/chat.rs +174 -11
  22. package/crates/mint-core/src/code_tools.rs +47 -31
  23. package/crates/mint-core/src/config.rs +302 -17
  24. package/crates/mint-core/src/image_gen.rs +299 -38
  25. package/crates/mint-core/src/lib.rs +10 -7
  26. package/crates/mint-core/src/mcp.rs +119 -6
  27. package/crates/mint-core/src/memory.rs +6 -1
  28. package/crates/mint-core/src/orchestration.rs +237 -96
  29. package/crates/mint-core/src/pictures.rs +67 -5
  30. package/crates/mint-core/src/semantic.rs +1 -1
  31. package/crates/mint-core/src/shell.rs +1 -1
  32. package/crates/mint-core/src/skills.rs +128 -25
  33. package/crates/mint-core/src/video_gen.rs +235 -0
  34. package/package.json +8 -2
  35. package/shared-modules/linux-audio/fluidsynth2-static.json +9 -3
  36. package/shared-modules/linux-audio/fluidsynth2.json +8 -2
  37. package/shared-modules/linux-audio/lilv.json +26 -19
  38. package/shared-modules/linux-audio/lv2.json +0 -1
  39. package/shared-modules/luajit/luajit.json +2 -2
  40. package/skills-lock.json +41 -0
  41. package/src/renderer/index-web.html +1 -1
  42. package/src/renderer/index.html +1 -1
  43. package/src/renderer/shared/components/AgentActivityTable.tsx +25 -6
  44. package/src/renderer/shared/constants/slashCommands.ts +27 -0
  45. package/src/renderer/shared/platform.ts +3 -0
  46. package/src/renderer/shared/types.ts +4 -0
  47. package/src/renderer/shared/utils/approval.tsx +86 -7
  48. package/src/renderer/shared/utils/fileIcons.ts +135 -0
  49. package/src/renderer/shared/utils/markdown.tsx +217 -88
  50. package/src/renderer/shared/utils/slashCommandProcessor.ts +239 -0
  51. package/src/renderer/src/components/ChatPanel.tsx +486 -69
  52. package/src/renderer/src/components/DashboardSidebar.tsx +11 -2
  53. package/src/renderer/src/components/ImageStudioPanel.tsx +87 -4
  54. package/src/renderer/src/components/MintDashboard.tsx +195 -7
  55. package/src/renderer/src/components/PicturesLibrary.tsx +156 -15
  56. package/src/renderer/src/components/Settings/AgentsTab.tsx +21 -2
  57. package/src/renderer/src/components/Settings/AutomationTab.tsx +316 -18
  58. package/src/renderer/src/components/Settings/GeneralTab.tsx +795 -413
  59. package/src/renderer/src/components/Settings/PluginsTab.tsx +304 -70
  60. package/src/renderer/src/components/Settings/ThemeTab.tsx +6 -1
  61. package/src/renderer/src/components/SettingsWindow.tsx +106 -3
  62. package/src/renderer/src/components/VeoStudioPanel.tsx +549 -0
  63. package/src/renderer/src/components/WorkspacePanel.tsx +7 -125
  64. package/src/renderer/src/css/image-studio.css +106 -0
  65. package/src/renderer/src/css/settings.css +158 -0
  66. package/src/renderer/src/css/styles.css +368 -1
  67. package/src/renderer/src/css/veo-studio.css +626 -0
  68. package/src/renderer/src/index.css +1 -0
  69. package/src/renderer/src/tauri.ts +144 -17
  70. package/src/renderer/src-web/components/ChatPanel.tsx +385 -93
  71. package/src/renderer/src-web/components/DashboardSidebar.tsx +16 -1
  72. package/src/renderer/src-web/components/ImageStudioPanel.tsx +87 -4
  73. package/src/renderer/src-web/components/MintDashboard.tsx +156 -5
  74. package/src/renderer/src-web/components/PicturesLibrary.tsx +155 -15
  75. package/src/renderer/src-web/components/Settings/AgentsTab.tsx +21 -2
  76. package/src/renderer/src-web/components/Settings/AutomationTab.tsx +316 -18
  77. package/src/renderer/src-web/components/Settings/GeneralTab.tsx +795 -411
  78. package/src/renderer/src-web/components/Settings/PluginsTab.tsx +304 -70
  79. package/src/renderer/src-web/components/Settings/ThemeTab.tsx +6 -1
  80. package/src/renderer/src-web/components/SettingsWindow.tsx +110 -3
  81. package/src/renderer/src-web/components/VeoStudioPanel.tsx +549 -0
  82. package/src/renderer/src-web/css/image-studio.css +106 -0
  83. package/src/renderer/src-web/css/settings.css +158 -0
  84. package/src/renderer/src-web/css/styles.css +369 -1
  85. package/src/renderer/src-web/css/veo-studio.css +650 -0
  86. package/src/renderer/src-web/index.css +1 -0
  87. package/src/renderer/src-web/tauri.ts +135 -16
  88. package/src-tauri/src/desktop.rs +18 -16
  89. package/src-tauri/src/discord_rpc.rs +1 -1
  90. package/src-tauri/src/lib.rs +155 -25
  91. package/src-tauri/src/webhooks.rs +45 -31
  92. package/src-tauri/src/workflows.rs +1 -0
  93. package/src-tauri/tauri.conf.json +1 -1
  94. package/temp_gridgeist/AGENTS.md +8 -0
  95. package/temp_gridgeist/CHANGELOG.md +130 -0
  96. package/temp_gridgeist/LICENSE +21 -0
  97. package/temp_gridgeist/README.md +323 -0
  98. package/temp_gridgeist/README.th.md +325 -0
  99. package/temp_gridgeist/skills/gridgeist/SKILL.md +88 -0
  100. package/temp_gridgeist/skills/gridgeist/agents/openai.yaml +9 -0
  101. package/temp_gridgeist/skills/gridgeist/assets/gridgeist-small.svg +10 -0
  102. package/temp_gridgeist/skills/gridgeist/assets/gridgeist.png +0 -0
  103. package/temp_gridgeist/skills/gridgeist/assets/gridgeist.svg +18 -0
  104. package/temp_gridgeist/skills/gridgeist/references/design-language.md +88 -0
  105. package/temp_gridgeist/skills/gridgeist/references/review-checklist.md +128 -0
  106. package/temp_gridgeist/skills/gridgeist/references/system-contract.md +109 -0
@@ -8,17 +8,32 @@
8
8
  "sources": [
9
9
  {
10
10
  "type": "archive",
11
- "url": "https://download.drobilla.net/serd-0.30.16.tar.xz",
12
- "sha256": "f50f486da519cdd8d03b20c9e42414e459133f5a244411d8e63caef8d9ac9146"
11
+ "url": "https://download.drobilla.net/serd-0.32.10.tar.xz",
12
+ "sha256": "b0e93b49e52f01a049475b7886ef140407115a32d3b1e5dc5f95141c88275d1c"
13
13
  }
14
14
  ],
15
- "post-install": [
16
- "install -Dm644 -t $FLATPAK_DEST/share/licenses/serd ../COPYING"
15
+ "cleanup": [
16
+ "/bin",
17
+ "/include",
18
+ "/lib/pkgconfig",
19
+ "/share/man"
20
+ ]
21
+ },
22
+ {
23
+ "name": "zix",
24
+ "buildsystem": "meson",
25
+ "sources": [
26
+ {
27
+ "type": "archive",
28
+ "url": "https://download.drobilla.net/zix-0.8.2.tar.xz",
29
+ "sha256": "4c73aab0f8cbbfe56b00c8c6d648316021e699c5ae6cbf254391ef309047e67b"
30
+ }
17
31
  ],
18
32
  "cleanup": [
19
33
  "/bin",
20
34
  "/include",
21
35
  "/lib/pkgconfig",
36
+ "/share/doc",
22
37
  "/share/man"
23
38
  ]
24
39
  },
@@ -28,13 +43,10 @@
28
43
  "sources": [
29
44
  {
30
45
  "type": "archive",
31
- "url": "https://download.drobilla.net/sord-0.16.14.tar.xz",
32
- "sha256": "220fd97d5fcb216e7b85db66f685bfdaad7dc58a50d1f96dfb2558dbc6c4731b"
46
+ "url": "https://download.drobilla.net/sord-0.16.22.tar.xz",
47
+ "sha256": "bb23b34b216579136795d518cffa73d91cf205594ce9accebfd408afb839173f"
33
48
  }
34
49
  ],
35
- "post-install": [
36
- "install -Dm644 -t $FLATPAK_DEST/share/licenses/sord ../COPYING"
37
- ],
38
50
  "cleanup": [
39
51
  "/bin",
40
52
  "/include",
@@ -43,6 +55,7 @@
43
55
  "/share/man"
44
56
  ]
45
57
  },
58
+ "lv2.json",
46
59
  {
47
60
  "name": "sratom",
48
61
  "buildsystem": "meson",
@@ -53,25 +66,19 @@
53
66
  "sources": [
54
67
  {
55
68
  "type": "archive",
56
- "url": "https://download.drobilla.net/sratom-0.6.14.tar.xz",
57
- "sha256": "9982faf40db83aedd9b3850e499fecd6852b8b4ba6dede514013655cffaca1e6"
69
+ "url": "https://download.drobilla.net/sratom-0.6.22.tar.xz",
70
+ "sha256": "0209b7d0f22c96abb416722ed735b0933be47931ecff4aa4b26ded7760b4f252"
58
71
  }
59
- ],
60
- "post-install": [
61
- "install -Dm644 -t $FLATPAK_DEST/share/licenses/sratom ../COPYING"
62
72
  ]
63
73
  }
64
74
  ],
65
75
  "sources": [
66
76
  {
67
77
  "type": "archive",
68
- "url": "https://download.drobilla.net/lilv-0.24.20.tar.xz",
69
- "sha256": "4fb082b9b8b286ea92bbb71bde6b75624cecab6df0cc639ee75a2a096212eebc"
78
+ "url": "https://download.drobilla.net/lilv-0.28.0.tar.xz",
79
+ "sha256": "8dcb70adb5cf072335115a6b091f4113710bdc73abaadaa3f9e9c1e55957b149"
70
80
  }
71
81
  ],
72
- "post-install": [
73
- "install -Dm644 -t $FLATPAK_DEST/share/licenses/lilv ../COPYING"
74
- ],
75
82
  "cleanup": [
76
83
  "/bin",
77
84
  "/etc",
@@ -15,7 +15,6 @@
15
15
  }
16
16
  ],
17
17
  "post-install": [
18
- "install -Dm644 -t $FLATPAK_DEST/share/licenses/lv2 ../COPYING",
19
18
  "ln -sf lv2.pc $FLATPAK_DEST/lib/pkgconfig/lv2core.pc"
20
19
  ]
21
20
  }
@@ -12,8 +12,8 @@
12
12
  {
13
13
  "type": "git",
14
14
  "url": "https://github.com/openresty/luajit2.git",
15
- "tag": "v2.1-20260620",
16
- "commit": "b411bec3ce550ef9968fc83bca094455cf812c1f",
15
+ "tag": "v2.1-20260717",
16
+ "commit": "1e5c0db1a86c9d0645bdd05e88e3e7a295831074",
17
17
  "x-checker-data": {
18
18
  "type": "git",
19
19
  "tag-pattern": "^v([\\d.-]+)$",
@@ -0,0 +1,41 @@
1
+ {
2
+ "version": 1,
3
+ "skills": {
4
+ "debug-mantra": {
5
+ "source": "thananon/9arm-skills",
6
+ "sourceType": "github",
7
+ "skillPath": "skills/engineering/debug-mantra/SKILL.md",
8
+ "computedHash": "7c519ffda61c6a44db8ef12f6a9ed06de920b9b4faa2e7b47b0032b9c78056e3"
9
+ },
10
+ "management-talk": {
11
+ "source": "thananon/9arm-skills",
12
+ "sourceType": "github",
13
+ "skillPath": "skills/productivity/management-talk/SKILL.md",
14
+ "computedHash": "65bb8510492db4e31f465e0ba4cc73570772d1dde75839c49993cd85a8437a54"
15
+ },
16
+ "post-mortem": {
17
+ "source": "thananon/9arm-skills",
18
+ "sourceType": "github",
19
+ "skillPath": "skills/engineering/post-mortem/SKILL.md",
20
+ "computedHash": "124d195540d43cc922ee93d398996ac8c4294528e92e4d3d6ad83deaf5cfe59a"
21
+ },
22
+ "qwen-agent": {
23
+ "source": "thananon/9arm-skills",
24
+ "sourceType": "github",
25
+ "skillPath": "skills/engineering/qwen-agent/SKILL.md",
26
+ "computedHash": "237e1a708655b68ab8066455d16b1e14ccba55ab682764e21843f2e47848f0a3"
27
+ },
28
+ "qwenchance": {
29
+ "source": "thananon/9arm-skills",
30
+ "sourceType": "github",
31
+ "skillPath": "skills/productivity/qwenchance/SKILL.md",
32
+ "computedHash": "116b0e8cc306b2ee891dd9a70160842528420153273e0109933224b49c22adf8"
33
+ },
34
+ "scrutinize": {
35
+ "source": "thananon/9arm-skills",
36
+ "sourceType": "github",
37
+ "skillPath": "skills/engineering/scrutinize/SKILL.md",
38
+ "computedHash": "d82a0b76617a00069f972cd0efd57c591e18b56f7aa0652aec63f6cb37cfd735"
39
+ }
40
+ }
41
+ }
@@ -6,7 +6,7 @@
6
6
  <title>Mint Agent (Web)</title>
7
7
  <link rel="icon" type="image/png" href="./assets/icon.png">
8
8
  <!-- Load font families used in settings -->
9
- <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600&family=Mali:wght@400;500;600&family=Prompt:wght@400;500&family=Sarabun:wght@400;500&display=swap" rel="stylesheet">
9
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600&family=Mali:wght@400;500;600&family=Prompt:wght@400;500;600&family=Sarabun:wght@400;500;600&family=Kanit:wght@400;500;600&family=Mitr:wght@400;500&family=Noto+Sans+Thai:wght@400;500;600&family=Inter:wght@400;500;600&family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
10
10
  <!-- Cubism must be available before the Live2D module is imported. -->
11
11
  <script src="./Live2DCubismCore.js"></script>
12
12
  </head>
@@ -6,7 +6,7 @@
6
6
  <title>Mint Agent</title>
7
7
  <link rel="icon" type="image/png" href="./assets/icon.png">
8
8
  <!-- Load font families used in settings -->
9
- <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600&family=Mali:wght@400;500;600&family=Prompt:wght@400;500&family=Sarabun:wght@400;500&display=swap" rel="stylesheet">
9
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600&family=Mali:wght@400;500;600&family=Prompt:wght@400;500;600&family=Sarabun:wght@400;500;600&family=Kanit:wght@400;500;600&family=Mitr:wght@400;500&family=Noto+Sans+Thai:wght@400;500;600&family=Inter:wght@400;500;600&family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
10
10
  <!-- Cubism must be available before the Live2D module is imported. -->
11
11
  <script src="./Live2DCubismCore.js"></script>
12
12
  </head>
@@ -1,15 +1,30 @@
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
1
  import { useState } from 'react'
7
2
  import type { AgentActivityView } from '../utils/agentActivity'
3
+ import { materialFileIcon, materialFolderIcon, getExtension } from '../utils/fileIcons'
8
4
 
9
5
  interface Props {
10
6
  activityView: AgentActivityView
11
7
  }
12
8
 
9
+ function getFilename(target: string): string {
10
+ const clean = target.split(' #')[0].trim()
11
+ const segments = clean.split('/')
12
+ return segments[segments.length - 1]
13
+ }
14
+
15
+ function resolveActivityIcon(kind: string, target: string): string | null {
16
+ if (kind === 'file') {
17
+ const filename = getFilename(target)
18
+ const ext = getExtension(filename)
19
+ return materialFileIcon(filename, ext)
20
+ }
21
+ if (kind === 'folder') {
22
+ const foldername = getFilename(target)
23
+ return materialFolderIcon(foldername, false)
24
+ }
25
+ return null
26
+ }
27
+
13
28
  export function AgentActivityTable({ activityView }: Props) {
14
29
  const [expandedIndices, setExpandedIndices] = useState<Record<number, boolean>>({})
15
30
 
@@ -30,6 +45,7 @@ export function AgentActivityTable({ activityView }: Props) {
30
45
  </div>
31
46
  {activityView.items.map((activity, index) => {
32
47
  const isExpanded = !!expandedIndices[index]
48
+ const icon = resolveActivityIcon(activity.kind, activity.target)
33
49
  return (
34
50
  <div
35
51
  className="agent-activity-item"
@@ -40,7 +56,9 @@ export function AgentActivityTable({ activityView }: Props) {
40
56
  onClick={() => toggleExpand(index)}
41
57
  >
42
58
  <span className="agent-activity-label">{activity.label}</span>
43
- <span className="agent-activity-icon" aria-hidden="true" />
59
+ <span className="agent-activity-icon" aria-hidden="true" data-has-img={!!icon}>
60
+ {icon && <img src={icon} alt="" draggable={false} />}
61
+ </span>
44
62
  <span
45
63
  className="agent-activity-text"
46
64
  style={isExpanded ? { whiteSpace: 'normal', wordBreak: 'break-all', overflow: 'visible', textOverflow: 'clip' } : undefined}
@@ -64,3 +82,4 @@ export function AgentActivityTable({ activityView }: Props) {
64
82
  </div>
65
83
  )
66
84
  }
85
+
@@ -0,0 +1,27 @@
1
+ export interface SlashCommand {
2
+ command: string
3
+ description: string
4
+ usage?: string
5
+ category?: 'system' | 'workspace' | 'models' | 'tools'
6
+ }
7
+
8
+ export const SLASH_COMMANDS: SlashCommand[] = [
9
+ { command: '/cd', description: 'Change active workspace directory', usage: '/cd <path>', category: 'workspace' },
10
+ { command: '/code', description: 'Run in code-agent mode', usage: '/code <task>', category: 'tools' },
11
+ { command: '/fast', description: 'Toggle fast mode (hide thinking traces)', usage: '/fast [on|off]', category: 'system' },
12
+ { command: '/help', description: 'Show interactive help menu', usage: '/help', category: 'system' },
13
+ { command: '/image', description: 'Attach image from disk', usage: '/image [path] [prompt]', category: 'tools' },
14
+ { command: '/image-provider', description: 'List or switch default image gen provider', usage: '/image-provider [name]', category: 'models' },
15
+ { command: '/learn', description: 'Import persistent skill/instruction file', usage: '/learn [path]', category: 'tools' },
16
+ { command: '/mcp', description: 'List configured MCP servers & status', usage: '/mcp [list|allow]', category: 'tools' },
17
+ { command: '/memory', description: 'Manage long-term memory store', usage: '/memory [list|clear|get|set]', category: 'tools' },
18
+ { command: '/models', description: 'List AI providers or switch active provider/model', usage: '/models [provider/model]', category: 'models' },
19
+ { command: '/multi-agent', description: 'Toggle Multi-Agent Collaboration system', usage: '/multi-agent [on|off]', category: 'system' },
20
+ { command: '/paste', description: 'Attach image from clipboard', usage: '/paste [prompt]', category: 'tools' },
21
+ { command: '/plugins', description: 'List or generate plugins/skills', usage: '/plugins [name]', category: 'tools' },
22
+ { command: '/skill add', description: 'Add or install global skill file or folder', usage: '/skill add <path>', category: 'tools' },
23
+ { command: '/stats', description: 'Show session statistics', usage: '/stats', category: 'system' },
24
+ { command: '/veo', description: 'Generate video using Google Veo', usage: '/veo <prompt>', category: 'tools' },
25
+ { command: '/video-provider', description: 'List or switch default video gen provider', usage: '/video-provider [name]', category: 'models' },
26
+ ]
27
+
@@ -28,6 +28,7 @@ export interface MintPlatformApi {
28
28
  message: string,
29
29
  imageDataUri?: string | null,
30
30
  audioDataUri?: string | null,
31
+ videoDataUri?: string | null,
31
32
  documentAttachment?: any | null,
32
33
  workspacePath?: string | null,
33
34
  chatId?: string | null,
@@ -38,6 +39,7 @@ export interface MintPlatformApi {
38
39
  onChunk: (chunk: string) => void,
39
40
  imageDataUri?: string | null,
40
41
  audioDataUri?: string | null,
42
+ videoDataUri?: string | null,
41
43
  systemInstruction?: string,
42
44
  onProgress?: (progress: AgentProgress) => void,
43
45
  documentAttachment?: any | null,
@@ -51,6 +53,7 @@ export interface MintPlatformApi {
51
53
  saveSystemInteraction(
52
54
  chatId: string,
53
55
  userText: string,
56
+ aiText: string,
54
57
  provider: string,
55
58
  model: string,
56
59
  ): Promise<any>
@@ -8,6 +8,7 @@ export interface RuntimeStatus {
8
8
  backend: string
9
9
  configPath: string
10
10
  activeProvider: string
11
+ activeModel?: string
11
12
  availableProviders: string[]
12
13
  integrations: Record<string, unknown>
13
14
  localIp?: string
@@ -76,6 +77,9 @@ export interface ImageGenRequest {
76
77
  numImages?: number
77
78
  model?: string
78
79
  provider?: string
80
+ imageDataUri?: string
81
+ maskDataUri?: string
82
+ mode?: 'generate' | 'edit' | 'inpaint'
79
83
  }
80
84
 
81
85
  export interface ImageGenProviders {
@@ -13,11 +13,30 @@ export interface ApprovalDetails {
13
13
  isDangerous: boolean
14
14
  }
15
15
 
16
+ function parseDiffStats(diffText?: string): string {
17
+ if (!diffText) return ''
18
+ let additions = 0
19
+ let deletions = 0
20
+ const lines = diffText.split('\n')
21
+ for (const line of lines) {
22
+ if (line.startsWith('+++ ') || line.startsWith('--- ')) continue
23
+ if (line.startsWith('+')) additions++
24
+ else if (line.startsWith('-')) deletions++
25
+ }
26
+ return `(+${additions} -${deletions})`
27
+ }
28
+
16
29
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
30
  export function renderApprovalDetails(approval: any): ApprovalDetails {
18
31
  if (!approval) return { title: 'Action Pending Approval', body: 'No action details available.', isDangerous: false }
19
- if (approval.WriteFile) return { title: 'Write File', body: `Path: ${approval.WriteFile.path}`, reason: approval.WriteFile.diff ? `Diff:\n${approval.WriteFile.diff}` : 'Writing new file content.', isDangerous: false }
20
- if (approval.ApplyPatch) return { title: 'Apply Patch', body: `Path: ${approval.ApplyPatch.path}`, reason: approval.ApplyPatch.diff ? `Diff:\n${approval.ApplyPatch.diff}` : 'Applying code patch.', isDangerous: false }
32
+ if (approval.WriteFile) {
33
+ const stats = parseDiffStats(approval.WriteFile.diff)
34
+ return { title: 'Write File', body: `Path: ${approval.WriteFile.path}${stats ? ` ${stats}` : ''}`, reason: approval.WriteFile.diff ? `Diff:\n${approval.WriteFile.diff}` : 'Writing new file content.', isDangerous: false }
35
+ }
36
+ if (approval.ApplyPatch) {
37
+ const stats = parseDiffStats(approval.ApplyPatch.diff)
38
+ return { title: 'Apply Patch', body: `Path: ${approval.ApplyPatch.path}${stats ? ` ${stats}` : ''}`, reason: approval.ApplyPatch.diff ? `Diff:\n${approval.ApplyPatch.diff}` : 'Applying code patch.', isDangerous: false }
39
+ }
21
40
  if (approval.RunShell) return { title: 'Run Shell Command', body: approval.RunShell.command, reason: 'Executing shell commands can modify your system.', isDangerous: true }
22
41
  if (approval.NoteWrite) return { title: 'Write Note', body: `Path: ${approval.NoteWrite.path}`, reason: 'Creating or updating workspace notes.', isDangerous: false }
23
42
  if (approval.RunPlugin) return { title: `Run Plugin: ${approval.RunPlugin.name}`, body: approval.RunPlugin.instruction, reason: 'Executing a native plugin action.', isDangerous: false }
@@ -30,9 +49,35 @@ export function renderApprovalDetails(approval: any): ApprovalDetails {
30
49
  return { title: 'Unknown Action', body: JSON.stringify(approval, null, 2), reason: 'Requires approval to proceed.', isDangerous: false }
31
50
  }
32
51
 
52
+ function parseHunkHeader(line: string): { oldStart: number; newStart: number } | null {
53
+ const trimmed = line.trim()
54
+ if (!trimmed.startsWith('@@')) return null
55
+ const parts = trimmed.split('@@')
56
+ if (parts.length < 3) return null
57
+ const headerBody = parts[1].trim()
58
+ let oldStart = 1
59
+ let newStart = 1
60
+
61
+ for (const token of headerBody.split(/\s+/)) {
62
+ if (token.startsWith('-')) {
63
+ const numStr = token.slice(1).split(',')[0]
64
+ const parsed = parseInt(numStr, 10)
65
+ if (!isNaN(parsed)) oldStart = parsed
66
+ } else if (token.startsWith('+')) {
67
+ const numStr = token.slice(1).split(',')[0]
68
+ const parsed = parseInt(numStr, 10)
69
+ if (!isNaN(parsed)) newStart = parsed
70
+ }
71
+ }
72
+ return { oldStart, newStart }
73
+ }
74
+
33
75
  export function renderDiff(diffText: string): ReactNode {
34
76
  if (!diffText) return null
35
77
  const lines = diffText.split('\n')
78
+ let currentOldLine = 1
79
+ let currentNewLine = 1
80
+
36
81
  return (
37
82
  <div style={{
38
83
  background: '#0b0f19',
@@ -46,18 +91,52 @@ export function renderDiff(diffText: string): ReactNode {
46
91
  lineHeight: '1.4',
47
92
  }}>
48
93
  {lines.map((line, idx) => {
94
+ if (line.startsWith('@@')) {
95
+ const hunk = parseHunkHeader(line)
96
+ if (hunk) {
97
+ currentOldLine = hunk.oldStart
98
+ currentNewLine = hunk.newStart
99
+ }
100
+ return (
101
+ <div key={idx} style={{ color: '#64748b', fontWeight: 'bold', padding: '2px 6px' }}>
102
+ {' ' + line}
103
+ </div>
104
+ )
105
+ }
106
+ if (line.startsWith('---') || line.startsWith('+++')) {
107
+ return (
108
+ <div key={idx} style={{ color: '#64748b', fontWeight: 'bold', padding: '2px 6px' }}>
109
+ {' ' + line}
110
+ </div>
111
+ )
112
+ }
113
+
114
+ let lineNum = ''
49
115
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
- let style: any = { whiteSpace: 'pre-wrap', padding: '2px 6px' }
51
- if (line.startsWith('@@') || line.startsWith('---') || line.startsWith('+++')) {
52
- style = { ...style, color: '#64748b', fontWeight: 'bold' }
53
- } else if (line.startsWith('+')) {
116
+ let style: any = { whiteSpace: 'pre-wrap', padding: '2px 6px', display: 'flex', alignItems: 'center' }
117
+ if (line.startsWith('+')) {
118
+ lineNum = String(currentNewLine)
119
+ currentNewLine++
54
120
  style = { ...style, background: 'rgba(16, 185, 129, 0.12)', borderLeft: '3px solid #10b981', color: '#a7f3d0' }
55
121
  } else if (line.startsWith('-')) {
122
+ lineNum = String(currentOldLine)
123
+ currentOldLine++
56
124
  style = { ...style, background: 'rgba(239, 68, 68, 0.12)', borderLeft: '3px solid #ef4444', color: '#fca5a5' }
57
125
  } else {
126
+ lineNum = String(currentNewLine)
127
+ currentOldLine++
128
+ currentNewLine++
58
129
  style = { ...style, color: '#e2e8f0' }
59
130
  }
60
- return <div key={idx} style={style}>{line}</div>
131
+
132
+ return (
133
+ <div key={idx} style={style}>
134
+ <span style={{ color: '#64748b', marginRight: '12px', userSelect: 'none', display: 'inline-block', width: '36px', textAlign: 'right', flexShrink: 0 }}>
135
+ {lineNum}
136
+ </span>
137
+ <span>{line}</span>
138
+ </div>
139
+ )
61
140
  })}
62
141
  </div>
63
142
  )
@@ -0,0 +1,135 @@
1
+ /**
2
+ * shared/utils/fileIcons.ts
3
+ * Utility mapping and resolution functions for Material Icon Theme icons.
4
+ * Extracted from WorkspacePanel to be shared across multiple components (e.g. AgentActivityTable).
5
+ */
6
+
7
+ import folderIcon from 'material-icon-theme/icons/folder.svg?url'
8
+ import folderOpenIcon from 'material-icon-theme/icons/folder-open.svg?url'
9
+ import folderComponentsIcon from 'material-icon-theme/icons/folder-components.svg?url'
10
+ import folderComponentsOpenIcon from 'material-icon-theme/icons/folder-components-open.svg?url'
11
+ import folderConfigIcon from 'material-icon-theme/icons/folder-config.svg?url'
12
+ import folderConfigOpenIcon from 'material-icon-theme/icons/folder-config-open.svg?url'
13
+ import folderDistIcon from 'material-icon-theme/icons/folder-dist.svg?url'
14
+ import folderDistOpenIcon from 'material-icon-theme/icons/folder-dist-open.svg?url'
15
+ import folderDocsIcon from 'material-icon-theme/icons/folder-docs.svg?url'
16
+ import folderDocsOpenIcon from 'material-icon-theme/icons/folder-docs-open.svg?url'
17
+ import folderGitIcon from 'material-icon-theme/icons/folder-git.svg?url'
18
+ import folderGitOpenIcon from 'material-icon-theme/icons/folder-git-open.svg?url'
19
+ import folderGithubIcon from 'material-icon-theme/icons/folder-github.svg?url'
20
+ import folderGithubOpenIcon from 'material-icon-theme/icons/folder-github-open.svg?url'
21
+ import folderImagesIcon from 'material-icon-theme/icons/folder-images.svg?url'
22
+ import folderImagesOpenIcon from 'material-icon-theme/icons/folder-images-open.svg?url'
23
+ import folderLogIcon from 'material-icon-theme/icons/folder-log.svg?url'
24
+ import folderLogOpenIcon from 'material-icon-theme/icons/folder-log-open.svg?url'
25
+ import folderNodeIcon from 'material-icon-theme/icons/folder-node.svg?url'
26
+ import folderNodeOpenIcon from 'material-icon-theme/icons/folder-node-open.svg?url'
27
+ import folderPublicIcon from 'material-icon-theme/icons/folder-public.svg?url'
28
+ import folderPublicOpenIcon from 'material-icon-theme/icons/folder-public-open.svg?url'
29
+ import folderRustIcon from 'material-icon-theme/icons/folder-rust.svg?url'
30
+ import folderRustOpenIcon from 'material-icon-theme/icons/folder-rust-open.svg?url'
31
+ import folderScriptsIcon from 'material-icon-theme/icons/folder-scripts.svg?url'
32
+ import folderScriptsOpenIcon from 'material-icon-theme/icons/folder-scripts-open.svg?url'
33
+ import folderSrcIcon from 'material-icon-theme/icons/folder-src.svg?url'
34
+ import folderSrcOpenIcon from 'material-icon-theme/icons/folder-src-open.svg?url'
35
+ import folderSrcTauriIcon from 'material-icon-theme/icons/folder-src-tauri.svg?url'
36
+ import folderSrcTauriOpenIcon from 'material-icon-theme/icons/folder-src-tauri-open.svg?url'
37
+ import folderTestIcon from 'material-icon-theme/icons/folder-test.svg?url'
38
+ import folderTestOpenIcon from 'material-icon-theme/icons/folder-test-open.svg?url'
39
+ import folderUiIcon from 'material-icon-theme/icons/folder-ui.svg?url'
40
+ import folderUiOpenIcon from 'material-icon-theme/icons/folder-ui-open.svg?url'
41
+ import folderUtilsIcon from 'material-icon-theme/icons/folder-utils.svg?url'
42
+ import folderUtilsOpenIcon from 'material-icon-theme/icons/folder-utils-open.svg?url'
43
+ import cssIcon from 'material-icon-theme/icons/css.svg?url'
44
+ import documentIcon from 'material-icon-theme/icons/document.svg?url'
45
+ import htmlIcon from 'material-icon-theme/icons/html.svg?url'
46
+ import imageIcon from 'material-icon-theme/icons/image.svg?url'
47
+ import javascriptIcon from 'material-icon-theme/icons/javascript.svg?url'
48
+ import jsonIcon from 'material-icon-theme/icons/json.svg?url'
49
+ import lockIcon from 'material-icon-theme/icons/lock.svg?url'
50
+ import logIcon from 'material-icon-theme/icons/log.svg?url'
51
+ import markdownIcon from 'material-icon-theme/icons/markdown.svg?url'
52
+ import npmIcon from 'material-icon-theme/icons/npm.svg?url'
53
+ import reactIcon from 'material-icon-theme/icons/react.svg?url'
54
+ import reactTsIcon from 'material-icon-theme/icons/react_ts.svg?url'
55
+ import rustIcon from 'material-icon-theme/icons/rust.svg?url'
56
+ import settingsIcon from 'material-icon-theme/icons/settings.svg?url'
57
+ import tauriIcon from 'material-icon-theme/icons/tauri.svg?url'
58
+ import tomlIcon from 'material-icon-theme/icons/toml.svg?url'
59
+ import typescriptIcon from 'material-icon-theme/icons/typescript.svg?url'
60
+ import viteIcon from 'material-icon-theme/icons/vite.svg?url'
61
+ import yamlIcon from 'material-icon-theme/icons/yaml.svg?url'
62
+
63
+ export const FOLDER_ICONS: Record<string, { closed: string; open: string }> = {
64
+ '.cargo_home': { closed: folderRustIcon, open: folderRustOpenIcon },
65
+ '.github': { closed: folderGithubIcon, open: folderGithubOpenIcon },
66
+ '.git': { closed: folderGitIcon, open: folderGitOpenIcon },
67
+ '.rustup': { closed: folderRustIcon, open: folderRustOpenIcon },
68
+ '.rustup_copy': { closed: folderRustIcon, open: folderRustOpenIcon },
69
+ '.rustup_home': { closed: folderRustIcon, open: folderRustOpenIcon },
70
+ assets: { closed: folderImagesIcon, open: folderImagesOpenIcon },
71
+ components: { closed: folderComponentsIcon, open: folderComponentsOpenIcon },
72
+ crates: { closed: folderRustIcon, open: folderRustOpenIcon },
73
+ css: { closed: folderUiIcon, open: folderUiOpenIcon },
74
+ dist: { closed: folderDistIcon, open: folderDistOpenIcon },
75
+ docs: { closed: folderDocsIcon, open: folderDocsOpenIcon },
76
+ logs: { closed: folderLogIcon, open: folderLogOpenIcon },
77
+ node_modules: { closed: folderNodeIcon, open: folderNodeOpenIcon },
78
+ out: { closed: folderDistIcon, open: folderDistOpenIcon },
79
+ public: { closed: folderPublicIcon, open: folderPublicOpenIcon },
80
+ renderer: { closed: folderUiIcon, open: folderUiOpenIcon },
81
+ scripts: { closed: folderScriptsIcon, open: folderScriptsOpenIcon },
82
+ src: { closed: folderSrcIcon, open: folderSrcOpenIcon },
83
+ 'src-tauri': { closed: folderSrcTauriIcon, open: folderSrcTauriOpenIcon },
84
+ tests: { closed: folderTestIcon, open: folderTestOpenIcon },
85
+ utils: { closed: folderUtilsIcon, open: folderUtilsOpenIcon },
86
+ }
87
+
88
+ export const FILE_ICONS_BY_EXTENSION: Record<string, string> = {
89
+ css: cssIcon,
90
+ html: htmlIcon,
91
+ jpeg: imageIcon,
92
+ jpg: imageIcon,
93
+ js: javascriptIcon,
94
+ json: jsonIcon,
95
+ lock: lockIcon,
96
+ log: logIcon,
97
+ md: markdownIcon,
98
+ png: imageIcon,
99
+ rs: rustIcon,
100
+ svg: imageIcon,
101
+ toml: tomlIcon,
102
+ ts: typescriptIcon,
103
+ tsx: reactTsIcon,
104
+ yaml: yamlIcon,
105
+ yml: yamlIcon,
106
+ }
107
+
108
+ export const FILE_ICONS_BY_NAME: Record<string, string> = {
109
+ 'package-lock.json': npmIcon,
110
+ 'package.json': npmIcon,
111
+ 'tauri.conf.json': tauriIcon,
112
+ 'vite.config.ts': viteIcon,
113
+ 'vite.config.web.ts': viteIcon,
114
+ }
115
+
116
+ export function getExtension(name: string): string {
117
+ const index = name.lastIndexOf('.')
118
+ return index === -1 ? '' : name.slice(index + 1).toLowerCase()
119
+ }
120
+
121
+ export function materialFolderIcon(name: string, open: boolean): string {
122
+ const icon = FOLDER_ICONS[name] || FOLDER_ICONS[name.toLowerCase()]
123
+ if (!icon) return open ? folderOpenIcon : folderIcon
124
+ return open ? icon.open : icon.closed
125
+ }
126
+
127
+ export function materialFileIcon(name: string, fileExtension: string): string {
128
+ if (name.endsWith('.config.ts') || name.endsWith('.config.js')) return settingsIcon
129
+ if (name.endsWith('.tsx')) return reactTsIcon
130
+ if (name.endsWith('.jsx')) return reactIcon
131
+ return FILE_ICONS_BY_NAME[name.toLowerCase()] || FILE_ICONS_BY_EXTENSION[fileExtension] || documentIcon
132
+ }
133
+
134
+ export { folderIcon, folderOpenIcon }
135
+