@pheem49/mint 1.15.0 → 1.17.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 (1162) hide show
  1. package/Cargo.lock +1012 -69
  2. package/Cargo.toml +5 -2
  3. package/LICENSE +21 -654
  4. package/README.md +92 -817
  5. package/Release_Note.md +71 -579
  6. package/apps/mint-companion/src-tauri/Cargo.toml +20 -0
  7. package/apps/mint-companion/src-tauri/build.rs +3 -0
  8. package/apps/mint-companion/src-tauri/src/compact.rs +69 -0
  9. package/apps/mint-companion/src-tauri/src/main.rs +675 -0
  10. package/apps/mint-companion/tsconfig.json +20 -0
  11. package/apps/mint-companion/vite.config.ts +4 -0
  12. package/components.json +19 -0
  13. package/crates/mint-cli/Cargo.toml +6 -0
  14. package/crates/mint-cli/src/agent/live_status.rs +230 -41
  15. package/crates/mint-cli/src/agent.rs +476 -108
  16. package/crates/mint-cli/src/commands/agent.rs +294 -86
  17. package/crates/mint-cli/src/commands/browser.rs +127 -0
  18. package/crates/mint-cli/src/commands/config.rs +14 -1
  19. package/crates/mint-cli/src/commands/integrations.rs +7 -0
  20. package/crates/mint-cli/src/commands/knowledge.rs +28 -34
  21. package/crates/mint-cli/src/commands/mod.rs +59 -1
  22. package/crates/mint-cli/src/commands/tailnet_web.rs +2 -7
  23. package/crates/mint-cli/src/companion_navigation.rs +268 -0
  24. package/crates/mint-cli/src/image.rs +31 -33
  25. package/crates/mint-cli/src/interactive/chat_tui.rs +4977 -864
  26. package/crates/mint-cli/src/interactive/command_ui.rs +39 -0
  27. package/crates/mint-cli/src/interactive/input_box.rs +56 -6
  28. package/crates/mint-cli/src/interactive/slash_commands.rs +321 -61
  29. package/crates/mint-cli/src/interactive/tool_activity.rs +667 -0
  30. package/crates/mint-cli/src/interactive.rs +93 -37
  31. package/crates/mint-cli/src/main.rs +36 -9
  32. package/crates/mint-cli/src/mcp.rs +5 -0
  33. package/crates/mint-cli/src/mod_host.rs +6 -0
  34. package/crates/mint-cli/src/mods_cli.rs +62 -0
  35. package/crates/mint-cli/src/mods_ui.rs +187 -0
  36. package/crates/mint-cli/src/onboard.rs +3492 -1360
  37. package/crates/mint-cli/src/terminal_theme.rs +3 -1
  38. package/crates/mint-cli/src/updater.rs +56 -2
  39. package/crates/mint-cli/tests/library_intake.rs +77 -0
  40. package/crates/mint-cli/tests/mods_testing.rs +137 -0
  41. package/crates/mint-companion-protocol/Cargo.toml +10 -0
  42. package/crates/mint-companion-protocol/src/lib.rs +276 -0
  43. package/crates/mint-core/Cargo.toml +6 -3
  44. package/crates/mint-core/build.rs +26 -0
  45. package/crates/mint-core/src/agent/chat.rs +406 -39
  46. package/crates/mint-core/src/agent/code_tools.rs +15 -0
  47. package/crates/mint-core/src/agent/memory/sources.rs +240 -0
  48. package/crates/mint-core/src/agent/memory.rs +220 -19
  49. package/crates/mint-core/src/agent/safety.rs +56 -4
  50. package/crates/mint-core/src/agent/skill_inventory.rs +327 -0
  51. package/crates/mint-core/src/agent/skills.rs +438 -90
  52. package/crates/mint-core/src/agent.rs +1 -0
  53. package/crates/mint-core/src/api_server/routes/auth.rs +1 -4
  54. package/crates/mint-core/src/api_server/routes/background_jobs.rs +310 -0
  55. package/crates/mint-core/src/api_server/routes/chat.rs +245 -214
  56. package/crates/mint-core/src/api_server/routes/html_preview.rs +77 -0
  57. package/crates/mint-core/src/api_server/routes/library.rs +97 -0
  58. package/crates/mint-core/src/api_server/routes/media_gen.rs +10 -57
  59. package/crates/mint-core/src/api_server/routes/misc.rs +220 -107
  60. package/crates/mint-core/src/api_server/routes/mod.rs +6 -0
  61. package/crates/mint-core/src/api_server/routes/mods.rs +31 -0
  62. package/crates/mint-core/src/api_server/routes/profile_oauth.rs +43 -26
  63. package/crates/mint-core/src/api_server/routes/sessions.rs +127 -69
  64. package/crates/mint-core/src/api_server/routes/skills_subagents.rs +16 -42
  65. package/crates/mint-core/src/api_server/routes/slash.rs +50 -1
  66. package/crates/mint-core/src/api_server/routes/sources.rs +101 -0
  67. package/crates/mint-core/src/api_server/routes/status_health.rs +18 -8
  68. package/crates/mint-core/src/api_server.rs +327 -72
  69. package/crates/mint-core/src/avatar_bridge.rs +15 -3
  70. package/crates/mint-core/src/browser/cdp.rs +82 -26
  71. package/crates/mint-core/src/browser/lifecycle.rs +143 -43
  72. package/crates/mint-core/src/browser/mod.rs +5 -4
  73. package/crates/mint-core/src/browser/navigate.rs +82 -9
  74. package/crates/mint-core/src/browser/runtime.rs +284 -0
  75. package/crates/mint-core/src/browser/session.rs +315 -7
  76. package/crates/mint-core/src/browser/workflow.rs +386 -0
  77. package/crates/mint-core/src/companion/navigation.rs +115 -0
  78. package/crates/mint-core/src/companion.rs +824 -0
  79. package/crates/mint-core/src/cron/scheduler.rs +34 -2
  80. package/crates/mint-core/src/integrations/channels.rs +87 -345
  81. package/crates/mint-core/src/integrations/mcp.rs +397 -50
  82. package/crates/mint-core/src/integrations/mcp_calls.rs +823 -0
  83. package/crates/mint-core/src/integrations/mcp_catalog.rs +267 -0
  84. package/crates/mint-core/src/integrations/mcp_device.rs +319 -0
  85. package/crates/mint-core/src/integrations/mcp_result.rs +401 -0
  86. package/crates/mint-core/src/integrations/webhooks.rs +539 -0
  87. package/crates/mint-core/src/integrations.rs +2 -0
  88. package/crates/mint-core/src/lib.rs +95 -32
  89. package/crates/mint-core/src/media/gemini_live.rs +134 -14
  90. package/crates/mint-core/src/media/image_models.rs +3 -27
  91. package/crates/mint-core/src/media/image_proxy.rs +563 -0
  92. package/crates/mint-core/src/media/image_workflow.rs +216 -0
  93. package/crates/mint-core/src/media/library/intake.rs +144 -0
  94. package/crates/mint-core/src/media/library/migration.rs +56 -0
  95. package/crates/mint-core/src/media/library/removal.rs +55 -0
  96. package/crates/mint-core/src/media/library/tests.rs +730 -0
  97. package/crates/mint-core/src/media/library/thumbnails.rs +72 -0
  98. package/crates/mint-core/src/media/library.rs +545 -0
  99. package/crates/mint-core/src/media/model_catalog.rs +375 -0
  100. package/crates/mint-core/src/media/pictures.rs +79 -222
  101. package/crates/mint-core/src/media/tool_images.rs +196 -0
  102. package/crates/mint-core/src/media/tts/playback.rs +609 -0
  103. package/crates/mint-core/src/media/tts.rs +1 -0
  104. package/crates/mint-core/src/media/video_models.rs +3 -22
  105. package/crates/mint-core/src/media.rs +5 -0
  106. package/crates/mint-core/src/mods/manager.rs +1124 -0
  107. package/crates/mint-core/src/mods/mod.rs +569 -0
  108. package/crates/mint-core/src/mods/operations.rs +204 -0
  109. package/crates/mint-core/src/mods/session.rs +146 -0
  110. package/crates/mint-core/src/mods/slash.rs +59 -0
  111. package/crates/mint-core/src/mods/telemetry.rs +306 -0
  112. package/crates/mint-core/src/orchestration/approvals.rs +344 -0
  113. package/crates/mint-core/src/orchestration/context_render.rs +553 -32
  114. package/crates/mint-core/src/orchestration/decision_parsing.rs +5 -23
  115. package/crates/mint-core/src/orchestration/diagnostics.rs +307 -0
  116. package/crates/mint-core/src/orchestration/image_regressions.rs +379 -0
  117. package/crates/mint-core/src/orchestration/loop_guard.rs +334 -0
  118. package/crates/mint-core/src/orchestration/mcp_review.rs +153 -0
  119. package/crates/mint-core/src/orchestration/memory_skill.rs +104 -32
  120. package/crates/mint-core/src/orchestration/mod.rs +3194 -755
  121. package/crates/mint-core/src/orchestration/mod_authoring_regressions.rs +209 -0
  122. package/crates/mint-core/src/orchestration/progress_clock.rs +80 -0
  123. package/crates/mint-core/src/orchestration/reliability.rs +541 -0
  124. package/crates/mint-core/src/orchestration/repeated_failures.rs +124 -0
  125. package/crates/mint-core/src/orchestration/review_regressions.rs +595 -0
  126. package/crates/mint-core/src/orchestration/run_control.rs +196 -0
  127. package/crates/mint-core/src/orchestration/tools/files.rs +191 -16
  128. package/crates/mint-core/src/orchestration/tools/media.rs +37 -41
  129. package/crates/mint-core/src/orchestration/tools/misc.rs +3 -2
  130. package/crates/mint-core/src/orchestration/tools/mod.rs +2 -0
  131. package/crates/mint-core/src/orchestration/tools/mods.rs +161 -0
  132. package/crates/mint-core/src/orchestration/tools/plugins_mcp.rs +321 -74
  133. package/crates/mint-core/src/orchestration/tools/project.rs +1 -1
  134. package/crates/mint-core/src/orchestration/tools/shell.rs +124 -7
  135. package/crates/mint-core/src/orchestration/tools/web.rs +5 -1
  136. package/crates/mint-core/src/orchestration/trajectory_schema.rs +353 -0
  137. package/crates/mint-core/src/orchestration/truncation.rs +211 -0
  138. package/crates/mint-core/src/orchestration/verification.rs +36 -6
  139. package/crates/mint-core/src/orchestration/workspace_helpers.rs +57 -23
  140. package/crates/mint-core/src/prompts/agent.rs +47 -41
  141. package/crates/mint-core/src/prompts/tool_catalog.rs +158 -38
  142. package/crates/mint-core/src/search/knowledge.rs +7 -1
  143. package/crates/mint-core/src/search/linked_folders/index.rs +177 -13
  144. package/crates/mint-core/src/search/linked_folders.rs +83 -2
  145. package/crates/mint-core/src/search/web_search.rs +44 -3
  146. package/crates/mint-core/src/slash/catalog.rs +1 -1
  147. package/crates/mint-core/src/slash/mod.rs +638 -12
  148. package/crates/mint-core/src/slash/mods.rs +155 -0
  149. package/crates/mint-core/src/swarm/batch.rs +643 -0
  150. package/crates/mint-core/src/swarm/dossier.rs +152 -0
  151. package/crates/mint-core/src/swarm/labels.rs +100 -0
  152. package/crates/mint-core/src/swarm/mod.rs +158 -0
  153. package/crates/mint-core/src/swarm/permissions.rs +167 -0
  154. package/crates/mint-core/src/system/auth.rs +156 -7
  155. package/crates/mint-core/src/system/bg_shell.rs +1197 -95
  156. package/crates/mint-core/src/system/config.rs +97 -13
  157. package/crates/mint-core/src/system/html_preview.rs +398 -0
  158. package/crates/mint-core/src/system/mint_web.rs +185 -0
  159. package/crates/mint-core/src/system/release_notes.rs +18 -0
  160. package/crates/mint-core/src/system/shell.rs +200 -16
  161. package/crates/mint-core/src/system/workspace.rs +276 -8
  162. package/crates/mint-core/src/system/workspace_history.rs +604 -0
  163. package/crates/mint-core/src/system.rs +4 -0
  164. package/crates/mint-core/tests/background_retention.rs +34 -0
  165. package/crates/mint-core/tests/background_shutdown.rs +24 -0
  166. package/crates/mint-core/tests/library_http.rs +209 -0
  167. package/crates/mint-core/tests/mcp_contract.rs +283 -0
  168. package/crates/mint-core/tests/mcp_stdio.rs +11 -10
  169. package/crates/mint-core/tests/media_model_catalog_http.rs +403 -0
  170. package/crates/mint-core/tests/memory_persistence.rs +27 -0
  171. package/crates/mint-core/tests/mods_http.rs +215 -0
  172. package/crates/mint-core/tests/terminal_shutdown.rs +109 -0
  173. package/crates/mint-mod-runtime/Cargo.toml +18 -0
  174. package/crates/mint-mod-runtime/src/lib.rs +507 -0
  175. package/crates/mint-mod-runtime/src/sdk.js +56 -0
  176. package/crates/mint-mod-runtime/src/test_sdk.js +32 -0
  177. package/crates/mint-mod-runtime/src/testing.rs +94 -0
  178. package/crates/mint-mod-runtime/tests/runtime.rs +156 -0
  179. package/docs/mods/AUTHORING.md +61 -0
  180. package/docs/mods/README.md +139 -0
  181. package/docs/mods/TESTING.md +56 -0
  182. package/docs/mods/mint-mods.d.ts +66 -0
  183. package/examples/device-mcp/README.md +22 -0
  184. package/examples/device-mcp/contract.md +49 -0
  185. package/examples/device-mcp/mock-device.mjs +66 -0
  186. package/examples/device-mcp/server.mjs +48 -0
  187. package/examples/device-mcp/server.test.mjs +78 -0
  188. package/examples/mods/task-checklist/README.md +1 -0
  189. package/examples/mods/task-checklist/main.ts +17 -0
  190. package/examples/mods/task-checklist/manifest.json +1 -0
  191. package/examples/mods/task-checklist/tests/checklist.test.ts +15 -0
  192. package/examples/mods/token-meter/README.md +1 -0
  193. package/examples/mods/token-meter/main.ts +8 -0
  194. package/examples/mods/token-meter/manifest.json +1 -0
  195. package/examples/mods/tool-guard/README.md +1 -0
  196. package/examples/mods/tool-guard/main.ts +9 -0
  197. package/examples/mods/tool-guard/manifest.json +1 -0
  198. package/out/web/assets/ArtifactPreviewPanel-DAT_Y7i0.css +1 -0
  199. package/out/web/assets/ArtifactPreviewPanel-S0dPfYFx.js +2 -0
  200. package/out/web/assets/CalculationCard-CRjP3E1j.js +1 -0
  201. package/out/web/assets/CliSessionsView-C3Br2v5Y.css +1 -0
  202. package/out/web/assets/CliSessionsView-MI8kXMAb.js +1 -0
  203. package/out/web/assets/CodeReviewPage-CUJceQme.css +1 -0
  204. package/out/web/assets/CodeReviewPage-VCUgPla_.js +2 -0
  205. package/out/web/assets/CommandPalette-BD8SlTE5.js +1 -0
  206. package/out/web/assets/ImageGenCard-BCXxkVJX.js +1 -0
  207. package/out/web/assets/ImageSearchCard-N3yQOr8Z.js +1 -0
  208. package/out/web/assets/ImageStudioPanel-BP7N4rLr.js +1 -0
  209. package/out/web/assets/LibraryPage-DAxUTAg7.css +1 -0
  210. package/out/web/assets/LibraryPage-WlviRdR8.js +2 -0
  211. package/out/web/assets/LinkedFoldersView-CKRgroWK.js +1 -0
  212. package/out/web/assets/McpRegistryPicker-CoS83vtc.js +1 -0
  213. package/out/web/assets/McpServersView-D3wHnuHg.js +1 -0
  214. package/out/web/assets/MermaidCard-NqrHdyJW.js +2 -0
  215. package/out/web/assets/MintDashboard-B2DJsDDO.css +1 -0
  216. package/out/web/assets/MintDashboard-COw_xol7.js +63 -0
  217. package/out/web/assets/PluginsView-jBYmcKh4.js +1 -0
  218. package/out/web/assets/RewindModal-DempuDod.js +1 -0
  219. package/out/web/assets/ScheduledTasksView-5uw9-V9H.js +1 -0
  220. package/out/web/assets/SelectField-DXBQtrUK.js +1 -0
  221. package/out/web/assets/SelectField-lFEkkWCU.css +1 -0
  222. package/out/web/assets/SettingsWindow-2YP3yj7s.js +4 -0
  223. package/out/web/assets/SettingsWindow-Dz8LQVgD.css +1 -0
  224. package/out/web/assets/SkillsView-C-gp9J7m.js +2 -0
  225. package/out/web/assets/StockCard-BDMCe7mx.js +1 -0
  226. package/out/web/assets/ToolSurfacePage-B0J_q0Jl.js +38 -0
  227. package/out/web/assets/ToolSurfacePage-CRIQ1owD.css +1 -0
  228. package/out/web/assets/UiFeatureCard-6BwxTOCe.js +1 -0
  229. package/out/web/assets/UiGridCard-DvZcLNxb.js +1 -0
  230. package/out/web/assets/UiMockupWidget-DD73QWg1.js +1 -0
  231. package/out/web/assets/VeoStudioPanel-DfsOJKv0.js +4 -0
  232. package/out/web/assets/WeatherCard-CPucIfq8.js +1 -0
  233. package/out/web/assets/WorkspacePanel-BfHKkulU.js +1 -0
  234. package/out/web/assets/WorkspacePdfPreview-Dx_lkjNu.js +2 -0
  235. package/out/web/assets/abnfDiagram-VCTEODGH-BOo3Ckng.js +1 -0
  236. package/out/web/assets/architecture-7GRP2DOG-ZOwheW3n.js +1 -0
  237. package/out/web/assets/architectureDiagram-5GKGNRK7-DC2FDzVd.js +36 -0
  238. package/out/web/assets/blockDiagram-I7D4REHJ-BrKFWdAH.js +129 -0
  239. package/out/web/assets/c4Diagram-7LVT6UL2-BHTHK1Yd.js +38 -0
  240. package/out/web/assets/chunk-2Q5K7J3B-D2JLPQzF.js +1 -0
  241. package/out/web/assets/chunk-4HAMMTFA-BYFkZwNj.js +62 -0
  242. package/out/web/assets/chunk-5VM5RSS4-BZ2oBPYQ.js +15 -0
  243. package/out/web/assets/chunk-75Z2AOVW-BSKfSKSA.js +2 -0
  244. package/out/web/assets/chunk-DU6HZSFF-vIhsiW6n.js +128 -0
  245. package/out/web/assets/chunk-F27PBJKO-BlYpW9xq.js +1 -0
  246. package/out/web/assets/chunk-FOHPRMQF-JXEeeJ9r.js +161 -0
  247. package/out/web/assets/chunk-GMAD6QVW-BIBz_Ug5.js +72 -0
  248. package/out/web/assets/chunk-GVQU2GXP-NPSHxeqC.js +1 -0
  249. package/out/web/assets/chunk-IMKFNOWR-YlJK8_Fp.js +231 -0
  250. package/out/web/assets/chunk-JWPE2WC7-BeEMUFDw.js +1 -0
  251. package/out/web/assets/chunk-L3NEJ4N5-DMYi4-af.js +1 -0
  252. package/out/web/assets/chunk-OSK3NFVY-Begd3-jA.js +10 -0
  253. package/out/web/assets/chunk-P2QGCYS3-D1I4xFHN.js +1 -0
  254. package/out/web/assets/chunk-POPQ4Y6H-CafZZzNE.js +1 -0
  255. package/out/web/assets/chunk-PWAF6VOD-Dwh6_Lnt.js +1 -0
  256. package/out/web/assets/chunk-SHT3W25Y-B0XrCSEb.js +168 -0
  257. package/out/web/assets/chunk-SVP7TREG-CxLsddxC.js +88 -0
  258. package/out/web/assets/chunk-TICWLB2K-y156QInb.js +206 -0
  259. package/out/web/assets/chunk-XXDRQBXY-B9YlhOkE.js +1 -0
  260. package/out/web/assets/chunk-Y2CYZVJY-BxCWjNVG.js +1 -0
  261. package/out/web/assets/classDiagram-ZZMXUADV-BgJ5WtSp.js +1 -0
  262. package/out/web/assets/classDiagram-v2-VYDZK3BY-BgJ5WtSp.js +1 -0
  263. package/out/web/assets/config-CvIO5hkn.js +2 -0
  264. package/out/web/assets/config-DqXJXeZM.css +1 -0
  265. package/out/web/assets/core-Mi3s5D4_.js +1 -0
  266. package/out/web/assets/cose-bilkent-JH36ORCC-qlna31ty.js +1 -0
  267. package/out/web/assets/cynefin-OW5HDTMX-SMVvrLf-.js +1 -0
  268. package/out/web/assets/cynefinDiagram-5FMLGOSQ-rZznN24T.js +62 -0
  269. package/out/web/assets/cytoscape.esm-DWtAUG0r.js +321 -0
  270. package/out/web/assets/dagre-CnWJJM5f.js +1 -0
  271. package/out/web/assets/dagre-GXQ25YYZ-BOToydFu.js +4 -0
  272. package/out/web/assets/diagram-S7CK7UJ4-TMVaGQEJ.js +30 -0
  273. package/out/web/assets/diagram-UQ7AKVKN-C2JK-GP2.js +41 -0
  274. package/out/web/assets/diagram-VSXAHHWV-Cw8VSi2K.js +3 -0
  275. package/out/web/assets/diagram-VX7I27RA-DXp4fKk9.js +24 -0
  276. package/out/web/assets/diagram-Z3DM3KII-CzSicYiW.js +24 -0
  277. package/out/web/assets/dist-9ISJ7r-g.js +1 -0
  278. package/out/web/assets/dist-js-BV3mI7uw.js +1 -0
  279. package/out/web/assets/ebnfDiagram-PWID7BFC-Dqj-zqPu.js +1 -0
  280. package/out/web/assets/erDiagram-RLTQ6QDP-DZwFZAuD.js +99 -0
  281. package/out/web/assets/event-D7D-EBy9.js +1 -0
  282. package/out/web/assets/eventmodeling-NTZA5JFV-C0O-Lvgp.js +1 -0
  283. package/out/web/assets/flowDiagram-HODETNUW-uqCHJond.js +1 -0
  284. package/out/web/assets/ganttDiagram-EL5Y4UJY-Da4ULXTl.js +292 -0
  285. package/out/web/assets/gitGraph-4MIJSDKK-B9wNFZIP.js +1 -0
  286. package/out/web/assets/gitGraphDiagram-WWUBYQGX-RjrhB0MP.js +106 -0
  287. package/out/web/assets/graphlib-Bh9gHply.js +1 -0
  288. package/out/web/assets/index-BmpulIkg.js +168 -0
  289. package/out/web/assets/index-CkvBwhX_.css +1 -0
  290. package/out/web/assets/info-A6RAGUB7-B3dfCpeY.js +1 -0
  291. package/out/web/assets/infoDiagram-27XIBGKW-QVWkLVx7.js +2 -0
  292. package/out/web/assets/ishikawaDiagram-5VMMS53U-CLFNIRI9.js +70 -0
  293. package/out/web/assets/journeyDiagram-3NMN7TZE-fgOQadYF.js +139 -0
  294. package/out/web/assets/jsx-runtime-DmUwWVTj.js +1 -0
  295. package/out/web/assets/kanban-definition-UXKFOSKX-G3Q_9g9O.js +89 -0
  296. package/out/web/assets/katex-MCdQaEAD.js +257 -0
  297. package/out/web/assets/mermaid-parser.core-v3Jm7nco.js +7 -0
  298. package/out/web/assets/mermaid.core-DTEjLUA1.js +44 -0
  299. package/out/web/assets/mindmap-definition-YA3MSWOX-C_D42_fF.js +96 -0
  300. package/out/web/assets/modelManager-BPLycQrN.js +2 -0
  301. package/out/web/assets/oauthManager-BUK7ic7q.js +1 -0
  302. package/out/web/assets/packet-AYTQ26CC-B-R-SHlF.js +1 -0
  303. package/out/web/assets/pdf-Bcahc2yC.js +56 -0
  304. package/out/web/assets/pdf.worker.min-Dkey6ZUl.mjs +27 -0
  305. package/out/web/assets/pegDiagram-XKGWAZYB-Da2q_giz.js +1 -0
  306. package/out/web/assets/pie-WAS4IAKB-BC8F0N7-.js +1 -0
  307. package/out/web/assets/pieDiagram-E7YTZNPT-Iy9REJNC.js +39 -0
  308. package/out/web/assets/quadrantDiagram-AXDQQJYC-ztsiYw7h.js +7 -0
  309. package/out/web/assets/radar-RG4KPBEZ-B_1P8Z_z.js +1 -0
  310. package/out/web/assets/railroad-74A4TZTK-DuTqczSo.js +1 -0
  311. package/out/web/assets/railroad-abnf-HS5TGJTU-Df29WLmp.js +1 -0
  312. package/out/web/assets/railroad-ebnf-LZEXJU2U-ePFMo-qp.js +1 -0
  313. package/out/web/assets/railroad-peg-WCYAUIDC-DYXLW4_E.js +1 -0
  314. package/out/web/assets/railroadDiagram-O6MQD6OU-LStnZf2S.js +1 -0
  315. package/out/web/assets/requirementDiagram-BXWQKSXE-DmzonMH-.js +84 -0
  316. package/out/web/assets/rolldown-runtime-CNOWq4Kh.js +1 -0
  317. package/out/web/assets/rough.esm-CNnFptCR.js +1 -0
  318. package/out/web/assets/sankeyDiagram-P5KCCOFB-B8vjmUU1.js +40 -0
  319. package/out/web/assets/sequenceDiagram-WJ2MYXX4-CTnplOAX.js +162 -0
  320. package/out/web/assets/sizeCapture-INFHLROL-C8Q8DbV5.js +1 -0
  321. package/out/web/assets/src-CRDfRlHM.js +1 -0
  322. package/out/web/assets/stateDiagram-D77RDMKH-BFrIaLdI.js +1 -0
  323. package/out/web/assets/stateDiagram-v2-MP3YSRHH-uf3kdNW4.js +1 -0
  324. package/out/web/assets/studio-BBZQ_-QK.js +1 -0
  325. package/out/web/assets/swimlanes-42K2YHIH-BXGngUNU.js +1 -0
  326. package/out/web/assets/swimlanesDiagram-VR7AAH4N-Y8q5bQce.js +8 -0
  327. package/out/web/assets/timeline-definition-24CTP7MA-CegH7n43.js +120 -0
  328. package/out/web/assets/treeView-Q6P3EWNA-DTpJv2Tw.js +1 -0
  329. package/out/web/assets/treemap-WGGIJYW6-i8M9iJU6.js +1 -0
  330. package/out/web/assets/vennDiagram-4TSXK5OY-Dpzo1S8U.js +34 -0
  331. package/out/web/assets/wardley-WFR3VGLG-BrNsgtfw.js +1 -0
  332. package/out/web/assets/wardleyDiagram-VM6X3IG4-lQ92LFE1.js +78 -0
  333. package/out/web/assets/window-CkUOP7t0.js +1 -0
  334. package/out/web/assets/workspaces-SYPrmbgC.js +1 -0
  335. package/out/web/assets/xychartDiagram-S5SC5T6Z-BaOaEoBd.js +7 -0
  336. package/out/web/index-web.html +4 -3
  337. package/out/web/index.html +4 -3
  338. package/out/web/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
  339. package/out/web/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
  340. package/out/web/pdfjs/cmaps/78-H.bcmap +0 -0
  341. package/out/web/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
  342. package/out/web/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
  343. package/out/web/pdfjs/cmaps/78-V.bcmap +0 -0
  344. package/out/web/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
  345. package/out/web/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
  346. package/out/web/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
  347. package/out/web/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
  348. package/out/web/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
  349. package/out/web/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
  350. package/out/web/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
  351. package/out/web/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
  352. package/out/web/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
  353. package/out/web/pdfjs/cmaps/Add-H.bcmap +0 -0
  354. package/out/web/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
  355. package/out/web/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
  356. package/out/web/pdfjs/cmaps/Add-V.bcmap +0 -0
  357. package/out/web/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
  358. package/out/web/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
  359. package/out/web/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
  360. package/out/web/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
  361. package/out/web/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
  362. package/out/web/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
  363. package/out/web/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
  364. package/out/web/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  365. package/out/web/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
  366. package/out/web/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
  367. package/out/web/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
  368. package/out/web/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
  369. package/out/web/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
  370. package/out/web/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
  371. package/out/web/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  372. package/out/web/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
  373. package/out/web/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
  374. package/out/web/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
  375. package/out/web/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
  376. package/out/web/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
  377. package/out/web/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
  378. package/out/web/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
  379. package/out/web/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  380. package/out/web/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
  381. package/out/web/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
  382. package/out/web/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
  383. package/out/web/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  384. package/out/web/pdfjs/cmaps/B5-H.bcmap +0 -0
  385. package/out/web/pdfjs/cmaps/B5-V.bcmap +0 -0
  386. package/out/web/pdfjs/cmaps/B5pc-H.bcmap +0 -0
  387. package/out/web/pdfjs/cmaps/B5pc-V.bcmap +0 -0
  388. package/out/web/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
  389. package/out/web/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
  390. package/out/web/pdfjs/cmaps/CNS1-H.bcmap +0 -0
  391. package/out/web/pdfjs/cmaps/CNS1-V.bcmap +0 -0
  392. package/out/web/pdfjs/cmaps/CNS2-H.bcmap +0 -0
  393. package/out/web/pdfjs/cmaps/CNS2-V.bcmap +3 -0
  394. package/out/web/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
  395. package/out/web/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
  396. package/out/web/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
  397. package/out/web/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
  398. package/out/web/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
  399. package/out/web/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
  400. package/out/web/pdfjs/cmaps/EUC-H.bcmap +0 -0
  401. package/out/web/pdfjs/cmaps/EUC-V.bcmap +0 -0
  402. package/out/web/pdfjs/cmaps/Ext-H.bcmap +0 -0
  403. package/out/web/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
  404. package/out/web/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
  405. package/out/web/pdfjs/cmaps/Ext-V.bcmap +0 -0
  406. package/out/web/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
  407. package/out/web/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
  408. package/out/web/pdfjs/cmaps/GB-H.bcmap +4 -0
  409. package/out/web/pdfjs/cmaps/GB-V.bcmap +0 -0
  410. package/out/web/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
  411. package/out/web/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
  412. package/out/web/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
  413. package/out/web/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
  414. package/out/web/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
  415. package/out/web/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
  416. package/out/web/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
  417. package/out/web/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
  418. package/out/web/pdfjs/cmaps/GBT-H.bcmap +0 -0
  419. package/out/web/pdfjs/cmaps/GBT-V.bcmap +0 -0
  420. package/out/web/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
  421. package/out/web/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
  422. package/out/web/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
  423. package/out/web/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
  424. package/out/web/pdfjs/cmaps/H.bcmap +0 -0
  425. package/out/web/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
  426. package/out/web/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
  427. package/out/web/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
  428. package/out/web/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
  429. package/out/web/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
  430. package/out/web/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
  431. package/out/web/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
  432. package/out/web/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
  433. package/out/web/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
  434. package/out/web/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
  435. package/out/web/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
  436. package/out/web/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
  437. package/out/web/pdfjs/cmaps/Hankaku.bcmap +0 -0
  438. package/out/web/pdfjs/cmaps/Hiragana.bcmap +0 -0
  439. package/out/web/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
  440. package/out/web/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
  441. package/out/web/pdfjs/cmaps/KSC-H.bcmap +0 -0
  442. package/out/web/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
  443. package/out/web/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
  444. package/out/web/pdfjs/cmaps/KSC-V.bcmap +0 -0
  445. package/out/web/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
  446. package/out/web/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  447. package/out/web/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  448. package/out/web/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
  449. package/out/web/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
  450. package/out/web/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
  451. package/out/web/pdfjs/cmaps/Katakana.bcmap +0 -0
  452. package/out/web/pdfjs/cmaps/LICENSE +36 -0
  453. package/out/web/pdfjs/cmaps/NWP-H.bcmap +0 -0
  454. package/out/web/pdfjs/cmaps/NWP-V.bcmap +0 -0
  455. package/out/web/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
  456. package/out/web/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
  457. package/out/web/pdfjs/cmaps/Roman.bcmap +0 -0
  458. package/out/web/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  459. package/out/web/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  460. package/out/web/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  461. package/out/web/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  462. package/out/web/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  463. package/out/web/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  464. package/out/web/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  465. package/out/web/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  466. package/out/web/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
  467. package/out/web/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
  468. package/out/web/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
  469. package/out/web/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
  470. package/out/web/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
  471. package/out/web/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
  472. package/out/web/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
  473. package/out/web/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
  474. package/out/web/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  475. package/out/web/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  476. package/out/web/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  477. package/out/web/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  478. package/out/web/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  479. package/out/web/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  480. package/out/web/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  481. package/out/web/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  482. package/out/web/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  483. package/out/web/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  484. package/out/web/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  485. package/out/web/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  486. package/out/web/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  487. package/out/web/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  488. package/out/web/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  489. package/out/web/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  490. package/out/web/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  491. package/out/web/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  492. package/out/web/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  493. package/out/web/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  494. package/out/web/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  495. package/out/web/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  496. package/out/web/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  497. package/out/web/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
  498. package/out/web/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
  499. package/out/web/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
  500. package/out/web/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
  501. package/out/web/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
  502. package/out/web/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
  503. package/out/web/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
  504. package/out/web/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
  505. package/out/web/pdfjs/cmaps/V.bcmap +0 -0
  506. package/out/web/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
  507. package/out/web/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
  508. package/out/web/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
  509. package/out/web/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
  510. package/out/web/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
  511. package/out/web/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
  512. package/out/web/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
  513. package/out/web/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
  514. package/out/web/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
  515. package/out/web/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
  516. package/out/web/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
  517. package/out/web/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
  518. package/out/web/pdfjs/standard_fonts/LICENSE_LIBERATION +363 -0
  519. package/out/web/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
  520. package/out/web/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
  521. package/out/web/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
  522. package/out/web/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
  523. package/out/web/pdfjs/wasm/LICENSE_JBIG2 +196 -0
  524. package/out/web/pdfjs/wasm/LICENSE_OPENJPEG +39 -0
  525. package/out/web/pdfjs/wasm/LICENSE_PDFJS_JBIG2 +13 -0
  526. package/out/web/pdfjs/wasm/LICENSE_PDFJS_OPENJPEG +22 -0
  527. package/out/web/pdfjs/wasm/LICENSE_PDFJS_QCMS +19 -0
  528. package/out/web/pdfjs/wasm/LICENSE_QCMS +21 -0
  529. package/out/web/pdfjs/wasm/jbig2.wasm +0 -0
  530. package/out/web/pdfjs/wasm/jbig2_nowasm_fallback.js +15 -0
  531. package/out/web/pdfjs/wasm/openjpeg.wasm +0 -0
  532. package/out/web/pdfjs/wasm/openjpeg_nowasm_fallback.js +17 -0
  533. package/out/web/pdfjs/wasm/qcms_bg.wasm +0 -0
  534. package/out/web/pdfjs/wasm/quickjs-eval.js +16 -0
  535. package/out/web/pdfjs/wasm/quickjs-eval.wasm +0 -0
  536. package/package.json +74 -23
  537. package/shared-modules/.github/workflows/ci.yml +120 -0
  538. package/shared-modules/.github/workflows/update.yaml +28 -0
  539. package/slash-commands.json +14 -3
  540. package/src/bin/auto-build.js +66 -0
  541. package/src/bin/desktop.js +113 -0
  542. package/src/bin/desktop.test.js +113 -0
  543. package/src/bin/index.js +21 -25
  544. package/src/bin/index.test.js +89 -0
  545. package/src/bin/measure-effects.mjs +53 -0
  546. package/src/bin/package.test.js +28 -0
  547. package/src/bin/pdfjs-assets.mjs +31 -0
  548. package/src/bin/release-notes.test.js +84 -0
  549. package/src/bin/styles.test.js +20 -0
  550. package/src/bin/web.js +5 -0
  551. package/src/bin/web.test.js +39 -0
  552. package/src/renderer/index.html +0 -2
  553. package/src/renderer/node_modules/.vite/vitest/CACHEDIR.TAG +3 -0
  554. package/src/renderer/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  555. package/src/renderer/shared/agentProgress.ts +42 -0
  556. package/src/renderer/shared/components/Settings/AgentsTab.tsx +76 -11
  557. package/src/renderer/shared/components/Settings/AudioTab.tsx +8 -7
  558. package/src/renderer/shared/components/Settings/AutomationTab.tsx +59 -2
  559. package/src/renderer/shared/components/Settings/AvatarCropModal.tsx +18 -12
  560. package/src/renderer/shared/components/Settings/CustomProviderLogoPicker.test.tsx +65 -0
  561. package/src/renderer/shared/components/Settings/CustomProviderLogoPicker.tsx +456 -0
  562. package/src/renderer/shared/components/Settings/GeneralTab.tsx +72 -113
  563. package/src/renderer/shared/components/Settings/MemoryTab.test.tsx +37 -0
  564. package/src/renderer/shared/components/Settings/MemoryTab.tsx +71 -31
  565. package/src/renderer/shared/components/Settings/NotificationsTab.test.tsx +15 -0
  566. package/src/renderer/shared/components/Settings/NotificationsTab.tsx +84 -0
  567. package/src/renderer/shared/components/Settings/PluginsTab.tsx +101 -24
  568. package/src/renderer/shared/components/Settings/ProfileTab.tsx +1 -1
  569. package/src/renderer/shared/components/Settings/SettingsWindow.tsx +796 -0
  570. package/src/renderer/shared/components/Settings/ThemeTab.tsx +31 -11
  571. package/src/renderer/shared/components/activity/AgentActivityDrawer.tsx +220 -0
  572. package/src/renderer/shared/components/activity/AgentActivityTable.tsx +340 -0
  573. package/src/renderer/shared/components/activity/McpToolResults.tsx +118 -0
  574. package/src/renderer/shared/components/activity/PlanChecklistWidget.tsx +77 -0
  575. package/src/renderer/shared/components/activity/RunSummaryDashboard.test.tsx +127 -0
  576. package/src/renderer/shared/components/activity/RunSummaryDashboard.tsx +222 -0
  577. package/src/renderer/shared/components/activity/SubagentDagView.test.ts +123 -0
  578. package/src/renderer/shared/components/activity/SubagentDagView.tsx +420 -0
  579. package/src/renderer/shared/components/activity/SubagentStatusStrip.test.tsx +82 -0
  580. package/src/renderer/shared/components/activity/SubagentStatusStrip.tsx +107 -0
  581. package/src/renderer/shared/components/activity/ViewedImages.tsx +140 -0
  582. package/src/renderer/shared/components/app/AuthGate.tsx +227 -0
  583. package/src/renderer/shared/components/app/ChunkErrorBoundary.tsx +186 -0
  584. package/src/renderer/shared/components/app/CommandPalette.tsx +593 -0
  585. package/src/renderer/shared/components/app/DashboardSidebar.tsx +1230 -0
  586. package/src/renderer/shared/components/app/DesktopTitlebar.tsx +354 -0
  587. package/src/renderer/shared/components/app/ErrorNotice.tsx +29 -0
  588. package/src/renderer/shared/components/app/MintDashboard.tsx +2182 -0
  589. package/src/renderer/shared/components/app/ModalErrorBoundary.tsx +127 -0
  590. package/src/renderer/shared/components/cards/CalculationCard.tsx +86 -0
  591. package/src/renderer/shared/components/cards/ImageGenCard.tsx +146 -0
  592. package/src/renderer/shared/components/cards/ImageSearchCard.tsx +145 -0
  593. package/src/renderer/shared/components/cards/MermaidCard.tsx +283 -0
  594. package/src/renderer/shared/components/cards/StockCard.tsx +152 -0
  595. package/src/renderer/shared/components/cards/ToolResultCards.tsx +86 -0
  596. package/src/renderer/shared/components/cards/UiFeatureCard.tsx +267 -0
  597. package/src/renderer/shared/components/cards/UiGridCard.tsx +188 -0
  598. package/src/renderer/shared/components/cards/WeatherCard.tsx +224 -0
  599. package/src/renderer/shared/components/chat/ApprovalCard.test.tsx +24 -0
  600. package/src/renderer/shared/components/chat/ApprovalCard.tsx +347 -0
  601. package/src/renderer/shared/components/chat/ChatCodeBlock.tsx +109 -0
  602. package/src/renderer/shared/components/chat/ChatMessageItem.test.tsx +145 -0
  603. package/src/renderer/shared/components/chat/ChatMessageItem.tsx +242 -0
  604. package/src/renderer/shared/components/chat/ChatPanel.test.tsx +31 -0
  605. package/src/renderer/shared/components/chat/ChatPanel.tsx +1999 -0
  606. package/src/renderer/shared/components/chat/CollapsiblePasteInput.tsx +90 -0
  607. package/src/renderer/shared/components/chat/ContextCompactionStatus.tsx +17 -0
  608. package/src/renderer/shared/components/chat/DiffReviewModal.tsx +565 -0
  609. package/src/renderer/shared/components/chat/GeminiLiveOverlay.css +86 -0
  610. package/src/renderer/shared/components/chat/GeminiLiveOverlay.test.tsx +24 -0
  611. package/src/renderer/shared/components/chat/GeminiLiveOverlay.tsx +112 -0
  612. package/src/renderer/shared/components/chat/ModelSelectorPopover.tsx +936 -0
  613. package/src/renderer/shared/components/chat/QueuedMessagesBanner.test.tsx +89 -0
  614. package/src/renderer/shared/components/chat/QueuedMessagesBanner.tsx +201 -0
  615. package/src/renderer/shared/components/chat/RewindModal.tsx +414 -0
  616. package/src/renderer/shared/components/chat/SlashChoicePanel.tsx +88 -0
  617. package/src/renderer/shared/components/chat/SlashSuggestions.test.tsx +33 -0
  618. package/src/renderer/shared/components/chat/SlashSuggestions.tsx +346 -0
  619. package/src/renderer/shared/components/chat/SourcesBlock.tsx +435 -0
  620. package/src/renderer/shared/components/chat/ThinkingBlock.tsx +102 -0
  621. package/src/renderer/shared/components/chat/TurnFileChanges.test.tsx +86 -0
  622. package/src/renderer/shared/components/chat/TurnFileChanges.tsx +430 -0
  623. package/src/renderer/shared/components/chat/VirtualChatHistory.tsx +82 -0
  624. package/src/renderer/shared/components/chat/VoicePill.css +85 -0
  625. package/src/renderer/shared/components/chat/VoicePill.d.ts +12 -0
  626. package/src/renderer/shared/components/chat/VoicePill.jsx +191 -0
  627. package/src/renderer/shared/components/chat/VoicePill.test.tsx +22 -0
  628. package/src/renderer/shared/components/extensions/CliSessionsView.test.tsx +125 -0
  629. package/src/renderer/shared/components/extensions/CliSessionsView.tsx +545 -0
  630. package/src/renderer/shared/components/extensions/LinkedFoldersView.tsx +614 -0
  631. package/src/renderer/shared/components/extensions/McpRegistryPicker.tsx +302 -0
  632. package/src/renderer/shared/components/extensions/McpServersView.tsx +1040 -0
  633. package/src/renderer/shared/components/extensions/McpToolAllowlist.tsx +182 -0
  634. package/src/renderer/shared/components/extensions/ModUi.test.tsx +70 -0
  635. package/src/renderer/shared/components/extensions/ModsPanel.tsx +208 -0
  636. package/src/renderer/shared/components/extensions/PluginsView.tsx +325 -0
  637. package/src/renderer/shared/components/extensions/ScheduledTasksView.tsx +614 -0
  638. package/src/renderer/shared/components/extensions/SkillsView.test.tsx +31 -0
  639. package/src/renderer/shared/components/extensions/SkillsView.tsx +363 -0
  640. package/src/renderer/shared/components/studio/ImageStudioPanel.tsx +740 -0
  641. package/src/renderer/shared/components/studio/LibraryItemCard.tsx +72 -0
  642. package/src/renderer/shared/components/studio/LibraryPage.tsx +165 -0
  643. package/src/renderer/shared/components/studio/LibraryPreview.tsx +59 -0
  644. package/src/renderer/shared/components/studio/VeoStudioPanel.tsx +708 -0
  645. package/src/renderer/shared/components/ui/GlideSelect.css +139 -0
  646. package/src/renderer/shared/components/ui/ProviderBrandIcon.test.tsx +67 -0
  647. package/src/renderer/shared/components/ui/ProviderBrandIcon.tsx +167 -0
  648. package/src/renderer/shared/components/ui/README.md +52 -0
  649. package/src/renderer/shared/components/ui/SelectField.test.tsx +27 -0
  650. package/src/renderer/shared/components/ui/SelectField.tsx +506 -0
  651. package/src/renderer/shared/components/ui/button.tsx +37 -0
  652. package/src/renderer/shared/components/ui/error-recovery.css +35 -0
  653. package/src/renderer/shared/components/ui/error-recovery.tsx +35 -0
  654. package/src/renderer/shared/components/ui/voice-powered-orb.css +6 -0
  655. package/src/renderer/shared/components/ui/voice-powered-orb.tsx +374 -0
  656. package/src/renderer/shared/components/workspace/ArtifactPreviewPanel.tsx +700 -0
  657. package/src/renderer/shared/components/workspace/BackgroundTerminals.test.tsx +63 -0
  658. package/src/renderer/shared/components/workspace/BackgroundTerminals.tsx +212 -0
  659. package/src/renderer/shared/components/workspace/CodeReviewPage.tsx +707 -0
  660. package/src/renderer/shared/components/workspace/GitBranchSelector.tsx +442 -0
  661. package/src/renderer/shared/components/workspace/SessionSources.tsx +113 -0
  662. package/src/renderer/shared/components/workspace/TerminalDock.tsx +244 -0
  663. package/src/renderer/shared/components/workspace/ToolSurfacePage.test.tsx +68 -0
  664. package/src/renderer/shared/components/workspace/ToolSurfacePage.tsx +819 -0
  665. package/src/renderer/shared/components/workspace/ToolsPanelToggle.tsx +13 -0
  666. package/src/renderer/shared/components/workspace/WorkspaceFileInfo.tsx +28 -0
  667. package/src/renderer/shared/components/workspace/WorkspaceMediaPreview.tsx +17 -0
  668. package/src/renderer/shared/components/workspace/WorkspacePaneResizer.tsx +92 -0
  669. package/src/renderer/shared/components/workspace/WorkspacePdfPreview.tsx +89 -0
  670. package/src/renderer/shared/components/workspace/WorkspacePreviewActions.test.tsx +89 -0
  671. package/src/renderer/shared/components/workspace/WorkspacePreviewActions.tsx +19 -0
  672. package/src/renderer/shared/components/workspace/WorkspaceSelector.tsx +246 -0
  673. package/src/renderer/shared/components/workspace/background-terminals.css +292 -0
  674. package/src/renderer/shared/components/workspace/workspace-selector.css +330 -0
  675. package/src/renderer/shared/constants/config.ts +37 -1
  676. package/src/renderer/shared/constants/slashCommands.ts +1 -1
  677. package/src/renderer/shared/conversation/coordinator.test.ts +46 -0
  678. package/src/renderer/shared/conversation/coordinator.ts +50 -2
  679. package/src/renderer/shared/conversation/lifecycle.test.ts +524 -0
  680. package/src/renderer/shared/conversation/lifecycle.ts +575 -0
  681. package/src/renderer/shared/conversation/projectNavigation.test.ts +123 -0
  682. package/src/renderer/shared/conversation/projectNavigation.ts +50 -0
  683. package/src/renderer/shared/conversation/sourcesTransport.ts +27 -0
  684. package/src/renderer/shared/conversation/syncView.test.ts +1 -34
  685. package/src/renderer/shared/conversation/syncView.ts +0 -37
  686. package/src/renderer/shared/conversation/transport.test.ts +183 -0
  687. package/src/renderer/shared/conversation/transport.ts +201 -0
  688. package/src/renderer/shared/conversation/useConversationCoordinator.ts +67 -79
  689. package/src/renderer/shared/css/agent-observability.css +90 -30
  690. package/src/renderer/shared/css/auth-gate.css +112 -13
  691. package/src/renderer/shared/css/chat-ui-cards.css +35 -35
  692. package/src/renderer/shared/css/code-review.css +119 -86
  693. package/src/renderer/shared/css/code-sessions.css +37 -10
  694. package/src/renderer/shared/css/collapsed-paste.css +35 -0
  695. package/src/renderer/shared/css/effects.css +15 -0
  696. package/src/renderer/shared/css/error-notice.css +60 -0
  697. package/src/renderer/shared/css/file-changes.css +167 -5
  698. package/src/renderer/shared/css/library.css +476 -0
  699. package/src/renderer/shared/css/management-views.css +216 -37
  700. package/src/renderer/shared/css/mcp-results.css +54 -0
  701. package/src/renderer/shared/css/model-selector.css +163 -105
  702. package/src/renderer/shared/css/mods.css +77 -0
  703. package/src/renderer/shared/css/notification-notice.css +28 -0
  704. package/src/renderer/shared/css/queued-messages.css +221 -0
  705. package/src/renderer/shared/css/session-sources.css +28 -0
  706. package/src/renderer/shared/css/settings/agents.css +2 -2
  707. package/src/renderer/shared/css/settings/base.css +1260 -0
  708. package/src/renderer/shared/css/settings/general.css +384 -92
  709. package/src/renderer/shared/css/settings/memory.css +154 -0
  710. package/src/renderer/shared/css/settings/notifications.css +11 -0
  711. package/src/renderer/shared/css/settings/plugins.css +8 -8
  712. package/src/renderer/shared/css/settings/theme.css +49 -32
  713. package/src/renderer/shared/css/slash-choice.css +95 -0
  714. package/src/renderer/shared/css/slash-suggestions.css +68 -0
  715. package/src/renderer/shared/css/sources-block.css +91 -0
  716. package/src/renderer/shared/css/thinking-status.css +46 -0
  717. package/src/renderer/shared/css/tools-panel.css +7 -0
  718. package/src/renderer/shared/css/ui-tailwind.css +19 -0
  719. package/src/renderer/shared/css/viewed-images.css +104 -0
  720. package/src/renderer/shared/css/workspace-file-info.css +12 -0
  721. package/src/renderer/shared/css/workspace-media-preview.css +13 -0
  722. package/src/renderer/shared/env.d.ts +9 -0
  723. package/src/renderer/shared/fonts.css +46 -0
  724. package/src/renderer/shared/fonts.ts +3 -12
  725. package/src/renderer/shared/lib/utils.ts +6 -0
  726. package/src/renderer/shared/library/controller.test.ts +61 -0
  727. package/src/renderer/shared/library/controller.ts +57 -0
  728. package/src/renderer/shared/library/transport.test.ts +26 -0
  729. package/src/renderer/shared/library/transport.ts +60 -0
  730. package/src/renderer/shared/library/useLibraryFile.ts +38 -0
  731. package/src/renderer/shared/mods.ts +58 -0
  732. package/src/renderer/shared/platform.test.ts +200 -1
  733. package/src/renderer/shared/platform.ts +55 -4
  734. package/src/renderer/shared/speech/player.ts +160 -0
  735. package/src/renderer/shared/speech/types.ts +11 -0
  736. package/src/renderer/shared/theme/themeManager.test.ts +63 -0
  737. package/src/renderer/shared/theme/themeManager.ts +20 -5
  738. package/src/renderer/shared/theme/themes.ts +17 -4
  739. package/src/renderer/shared/theme/tokens.css +448 -51
  740. package/src/renderer/shared/types.ts +111 -2
  741. package/src/renderer/shared/utils/agentActivity.test.ts +234 -0
  742. package/src/renderer/shared/utils/agentActivity.ts +221 -32
  743. package/src/renderer/shared/utils/approval.tsx +7 -0
  744. package/src/renderer/shared/utils/artifactBrowser.test.ts +80 -0
  745. package/src/renderer/shared/utils/artifactBrowser.ts +41 -0
  746. package/src/renderer/shared/utils/artifacts.ts +55 -0
  747. package/src/renderer/shared/utils/contextCompaction.test.tsx +48 -0
  748. package/src/renderer/shared/utils/contextCompaction.ts +21 -0
  749. package/src/renderer/shared/utils/effectVisibility.ts +9 -0
  750. package/src/renderer/shared/utils/externalLinks.ts +91 -0
  751. package/src/renderer/shared/utils/lazyWithRetry.ts +13 -0
  752. package/src/renderer/shared/utils/markdown.tsx +172 -38
  753. package/src/renderer/shared/utils/markdown.webImages.test.tsx +124 -2
  754. package/src/renderer/shared/utils/mcpResults.test.tsx +114 -0
  755. package/src/renderer/shared/utils/modViewport.ts +7 -0
  756. package/src/renderer/shared/utils/nativeRecordingSession.test.ts +61 -0
  757. package/src/renderer/shared/utils/nativeRecordingSession.ts +46 -0
  758. package/src/renderer/shared/utils/notificationSettings.test.ts +14 -0
  759. package/src/renderer/shared/utils/notificationSettings.ts +23 -0
  760. package/src/renderer/shared/utils/notifications.test.ts +51 -0
  761. package/src/renderer/shared/utils/notifications.ts +54 -0
  762. package/src/renderer/shared/utils/pastedText.test.ts +20 -0
  763. package/src/renderer/shared/utils/pastedText.ts +13 -0
  764. package/src/renderer/shared/utils/pdfRange.ts +16 -0
  765. package/src/renderer/shared/utils/previewObjectUrls.ts +20 -0
  766. package/src/renderer/shared/utils/progressCache.test.ts +32 -0
  767. package/src/renderer/shared/utils/sessionSources.test.ts +71 -0
  768. package/src/renderer/shared/utils/sessionSources.ts +36 -0
  769. package/src/renderer/shared/utils/slashCommandProcessor.ts +5 -2
  770. package/src/renderer/shared/utils/sourceAttachments.ts +28 -0
  771. package/src/renderer/shared/utils/speech.ts +65 -9
  772. package/src/renderer/shared/utils/syntaxHighlight.test.tsx +22 -0
  773. package/src/renderer/shared/utils/syntaxHighlight.tsx +26 -0
  774. package/src/renderer/shared/utils/useGeminiLiveVoice.ts +49 -5
  775. package/src/renderer/shared/utils/useLatestCallback.ts +9 -0
  776. package/src/renderer/shared/utils/useNativeVoiceInput.ts +95 -73
  777. package/src/renderer/shared/utils/useSessionSources.ts +37 -0
  778. package/src/renderer/shared/utils/viewedImages.test.ts +129 -0
  779. package/src/renderer/shared/utils/viewedImages.ts +97 -0
  780. package/src/renderer/shared/utils/workspaceDrag.ts +30 -0
  781. package/src/renderer/shared/utils/workspaceImage.ts +17 -0
  782. package/src/renderer/shared/utils/workspaceListPreferences.test.ts +64 -0
  783. package/src/renderer/shared/utils/workspaceListPreferences.ts +54 -0
  784. package/src/renderer/shared/utils/workspaceMedia.ts +41 -0
  785. package/src/renderer/shared/utils/workspacePane.test.ts +33 -0
  786. package/src/renderer/shared/utils/workspacePane.ts +20 -0
  787. package/src/renderer/shared/utils/workspacePreview.test.ts +169 -0
  788. package/src/renderer/shared/utils/workspacePreview.ts +47 -0
  789. package/src/renderer/shared/utils/workspaceSnapshot.test.ts +49 -0
  790. package/src/renderer/shared/utils/workspaceSnapshot.ts +26 -0
  791. package/src/renderer/shared/utils/workspaces.ts +11 -0
  792. package/src/renderer/shared/workspaceUndo.ts +88 -0
  793. package/src/renderer/src/App.tsx +15 -9
  794. package/src/renderer/src/components/ChatPanel.tsx +1 -1
  795. package/src/renderer/src/components/DashboardSidebar.tsx +2 -2
  796. package/src/renderer/src/components/ImageStudioPanel.tsx +1 -1
  797. package/src/renderer/src/components/LiveTranslateControls.tsx +65 -0
  798. package/src/renderer/src/components/MintDashboard.tsx +1 -1
  799. package/src/renderer/src/components/ProactiveGlow.tsx +1 -1
  800. package/src/renderer/src/components/ScreenPicker.css +67 -30
  801. package/src/renderer/src/components/ScreenPicker.tsx +201 -60
  802. package/src/renderer/src/components/SettingsWindow.tsx +6 -825
  803. package/src/renderer/src/components/SpotlightWindow.tsx +4 -32
  804. package/src/renderer/src/components/VeoStudioPanel.tsx +1 -1
  805. package/src/renderer/src/components/WorkspacePanel.tsx +261 -38
  806. package/src/renderer/src/components/browser-shell.css +27 -27
  807. package/src/renderer/src/components/browser-suggestions-popup.css +9 -9
  808. package/src/renderer/src/components/liveTranslateFrames.test.ts +26 -5
  809. package/src/renderer/src/components/liveTranslateFrames.ts +18 -7
  810. package/src/renderer/src/components/liveTranslateLanguage.ts +6 -0
  811. package/src/renderer/src/css/app-shell.css +69 -234
  812. package/src/renderer/src/css/chat.css +207 -494
  813. package/src/renderer/src/css/desktop-tools.css +6 -139
  814. package/src/renderer/src/css/misc.css +0 -52
  815. package/src/renderer/src/css/sidebar-workspace.css +171 -94
  816. package/src/renderer/src/css/spotlight.css +2 -2
  817. package/src/renderer/src/css/tool-surfaces.css +387 -61
  818. package/src/renderer/src/css/widget.css +7 -6
  819. package/src/renderer/src/env.d.ts +14 -9
  820. package/src/renderer/src/index.css +16 -64
  821. package/src/renderer/src/main.tsx +2 -1
  822. package/src/renderer/src/proactiveSuggestions.ts +29 -0
  823. package/src/renderer/src/tauri.ts +195 -337
  824. package/src/renderer/src/voiceInput.ts +5 -2
  825. package/src/renderer/src-web/App.tsx +6 -8
  826. package/src/renderer/src-web/components/ChatPanel.tsx +1 -1
  827. package/src/renderer/src-web/components/DashboardSidebar.tsx +2 -2
  828. package/src/renderer/src-web/components/ImageStudioPanel.tsx +1 -1
  829. package/src/renderer/src-web/components/MintDashboard.tsx +1 -1
  830. package/src/renderer/src-web/components/PullToRefresh.tsx +1 -1
  831. package/src/renderer/src-web/components/SettingsWindow.tsx +6 -846
  832. package/src/renderer/src-web/components/VeoStudioPanel.tsx +1 -1
  833. package/src/renderer/src-web/css/app-shell.css +40 -221
  834. package/src/renderer/src-web/css/chat.css +207 -494
  835. package/src/renderer/src-web/css/misc.css +0 -52
  836. package/src/renderer/src-web/css/mobile.css +15 -14
  837. package/src/renderer/src-web/css/sidebar-workspace.css +29 -95
  838. package/src/renderer/src-web/env.d.ts +15 -12
  839. package/src/renderer/src-web/index.css +16 -64
  840. package/src/renderer/src-web/main.tsx +2 -1
  841. package/src/renderer/src-web/proactiveSuggestions.ts +3 -0
  842. package/src/renderer/src-web/tauri.ts +194 -361
  843. package/src/renderer/tests/backgroundTerminals.browser.html +81 -0
  844. package/src/renderer/tests/browserArtifacts.browser.html +1 -0
  845. package/src/renderer/tests/browserArtifacts.browser.tsx +68 -0
  846. package/src/renderer/tests/chatEscape.browser.html +1 -0
  847. package/src/renderer/tests/chatEscape.browser.tsx +25 -0
  848. package/src/renderer/tests/chatPanelFixture.ts +23 -0
  849. package/src/renderer/tests/chatPerformance.browser.html +71 -0
  850. package/src/renderer/tests/cliSessions.browser.html +82 -0
  851. package/src/renderer/tests/collapsedPaste.browser.html +70 -0
  852. package/src/renderer/tests/composerMenus.browser.html +116 -0
  853. package/src/renderer/tests/contextCompaction.browser.html +17 -0
  854. package/src/renderer/tests/conversationLifecycle.browser.html +70 -0
  855. package/src/renderer/tests/effectsPerformance.browser.html +13 -0
  856. package/src/renderer/tests/errorRecovery.browser.html +21 -0
  857. package/src/renderer/tests/fixtures/preview.webm +0 -0
  858. package/src/renderer/tests/glideSelect.browser.html +70 -0
  859. package/src/renderer/tests/glideSelect.preview.html +27 -0
  860. package/src/renderer/tests/library.browser.html +1 -0
  861. package/src/renderer/tests/library.browser.tsx +78 -0
  862. package/src/renderer/tests/liveOrb.browser.html +49 -0
  863. package/src/renderer/tests/liveVoice.browser.html +29 -0
  864. package/src/renderer/tests/mediaFixtures.ts +32 -0
  865. package/src/renderer/tests/modInput.browser.html +22 -0
  866. package/src/renderer/tests/sessionSources.browser.html +1 -0
  867. package/src/renderer/tests/sessionSources.browser.tsx +168 -0
  868. package/src/renderer/tests/skills.browser.html +24 -0
  869. package/src/renderer/tests/toolsPanel.browser.html +1 -0
  870. package/src/renderer/tests/toolsPanel.browser.tsx +113 -0
  871. package/src/renderer/tests/viewedImages.browser.html +1 -0
  872. package/src/renderer/tests/viewedImages.browser.tsx +93 -0
  873. package/src/renderer/tests/voiceDictation.browser.html +101 -0
  874. package/src/renderer/tests/voicePill.browser.html +80 -0
  875. package/src/renderer/tests/voicePill.preview.html +34 -0
  876. package/src/renderer/tests/webSources.browser.html +1 -0
  877. package/src/renderer/tests/webSources.browser.tsx +2 -0
  878. package/src/renderer/tests/workspaceDrop.browser.html +1 -0
  879. package/src/renderer/tests/workspaceDrop.browser.tsx +183 -0
  880. package/src/renderer/tests/workspaceExpansion.browser.html +1 -0
  881. package/src/renderer/tests/workspaceExpansion.browser.tsx +66 -0
  882. package/src/renderer/tests/workspaceRemoval.browser.html +66 -0
  883. package/src/renderer/tests/workspaceUndo.browser.html +100 -0
  884. package/src-tauri/Cargo.toml +40 -0
  885. package/src-tauri/build.rs +3 -0
  886. package/src-tauri/src/app/lifecycle.rs +320 -0
  887. package/src-tauri/src/app/mod.rs +4 -0
  888. package/src-tauri/src/app/shortcuts.rs +40 -0
  889. package/src-tauri/src/app/tray.rs +52 -0
  890. package/src-tauri/src/background/events.rs +70 -0
  891. package/src-tauri/src/background/headless.rs +230 -0
  892. package/src-tauri/src/background/mod.rs +4 -0
  893. package/src-tauri/src/background/proactive.rs +262 -0
  894. package/src-tauri/src/commands/approvals.rs +20 -0
  895. package/src-tauri/src/commands/auth.rs +127 -0
  896. package/src-tauri/src/commands/conversation.rs +668 -0
  897. package/src-tauri/src/commands/integrations.rs +163 -0
  898. package/src-tauri/src/commands/media.rs +186 -0
  899. package/src-tauri/src/commands/mod.rs +11 -0
  900. package/src-tauri/src/commands/mods.rs +6 -0
  901. package/src-tauri/src/commands/settings.rs +141 -0
  902. package/src-tauri/src/commands/voice.rs +153 -0
  903. package/src-tauri/src/commands/workspace.rs +539 -0
  904. package/src-tauri/src/lib.rs +10 -0
  905. package/src-tauri/src/main.rs +3 -0
  906. package/src-tauri/src/native/actions.rs +206 -0
  907. package/src-tauri/src/native/browser.rs +252 -0
  908. package/src-tauri/src/native/capture.rs +695 -0
  909. package/src-tauri/src/native/discord_rpc.rs +99 -0
  910. package/src-tauri/src/native/integrations.rs +126 -0
  911. package/src-tauri/src/native/mod.rs +12 -0
  912. package/src-tauri/src/native/notifications.rs +51 -0
  913. package/src-tauri/src/native/plugins.rs +14 -0
  914. package/src-tauri/src/native/system.rs +250 -0
  915. package/src-tauri/src/native/terminal.rs +201 -0
  916. package/src-tauri/src/native/updater.rs +148 -0
  917. package/src-tauri/src/native/windows.rs +143 -0
  918. package/tsconfig.json +17 -1
  919. package/vite.config.ts +5 -1
  920. package/vite.config.web.ts +6 -0
  921. package/.cargo/config.toml +0 -13
  922. package/.claude/settings.local.json +0 -182
  923. package/.codex +0 -0
  924. package/CLAUDE.md +0 -102
  925. package/ROADMAP.md +0 -30
  926. package/mint-presentation-demo.mp4 +0 -0
  927. package/out/web/Live2DCubismCore.js +0 -9
  928. package/out/web/assets/CliSessionsView-BxUVWEEL.js +0 -1
  929. package/out/web/assets/CliSessionsView-CBDTXuQv.css +0 -1
  930. package/out/web/assets/CodeReviewPage-DkMgfcos.css +0 -1
  931. package/out/web/assets/CodeReviewPage-jKlmLJwT.js +0 -3
  932. package/out/web/assets/CommandPalette-DnwPA5hy.js +0 -1
  933. package/out/web/assets/ImageStudioPanel-BR1XFwhR.js +0 -1
  934. package/out/web/assets/LinkedFoldersView-bi-CWQ-I.js +0 -1
  935. package/out/web/assets/McpRegistryPicker-D5tEFnyq.js +0 -1
  936. package/out/web/assets/McpServersView-BGG1dhFM.js +0 -1
  937. package/out/web/assets/MintDashboard-BIglDEWX.js +0 -20
  938. package/out/web/assets/MintDashboard-DSMywSgs.css +0 -1
  939. package/out/web/assets/ModelPanel-CbJ1PFoZ.js +0 -1
  940. package/out/web/assets/PicturesLibrary-CQP0fLP1.css +0 -1
  941. package/out/web/assets/PicturesLibrary-DoYN7T1p.js +0 -1
  942. package/out/web/assets/PluginsView-CU1HWXGk.js +0 -1
  943. package/out/web/assets/ScheduledTasksView-Bnl36y2N.js +0 -1
  944. package/out/web/assets/SettingsWindow-DhWLAn9T.css +0 -1
  945. package/out/web/assets/SettingsWindow-Dw8L01pw.js +0 -2
  946. package/out/web/assets/SkillsView-cnl8aY5p.js +0 -2
  947. package/out/web/assets/ToolSurfacePage-CgsQw6l5.css +0 -1
  948. package/out/web/assets/ToolSurfacePage-Cz2VJAPZ.js +0 -37
  949. package/out/web/assets/VeoStudioPanel-D4JnERWR.js +0 -4
  950. package/out/web/assets/WorkspacePanel-CbJ1PFoZ.js +0 -1
  951. package/out/web/assets/abnfDiagram-VCTEODGH-ByMyfanq.js +0 -1
  952. package/out/web/assets/architecture-7GRP2DOG-CLq2kEOB.js +0 -1
  953. package/out/web/assets/architectureDiagram-5GKGNRK7-6PoVSmNG.js +0 -36
  954. package/out/web/assets/blockDiagram-NRAW4CY4-cKw9mAKi.js +0 -129
  955. package/out/web/assets/c4Diagram-UCG6FXSJ-Cqy8N5Ge.js +0 -38
  956. package/out/web/assets/chunk-2Q5K7J3B-Dmqrq8Tp.js +0 -1
  957. package/out/web/assets/chunk-5VM5RSS4-BiPuE0io.js +0 -15
  958. package/out/web/assets/chunk-75Z2AOVW-3qybijF5.js +0 -2
  959. package/out/web/assets/chunk-CKBBP62Z-cIrXq6jN.js +0 -2
  960. package/out/web/assets/chunk-CLGD4ZFX-CndDQkww.js +0 -1
  961. package/out/web/assets/chunk-CacB07dV.js +0 -1
  962. package/out/web/assets/chunk-DSNCTWBM-Cc4NxvgD.js +0 -2
  963. package/out/web/assets/chunk-DU6HZSFF-drzh6GsU.js +0 -128
  964. package/out/web/assets/chunk-F27PBJKO-FiY35l4v.js +0 -1
  965. package/out/web/assets/chunk-FOHPRMQF-BaVC1uA7.js +0 -161
  966. package/out/web/assets/chunk-G27WJ6UU-Bjt9Il4A.js +0 -231
  967. package/out/web/assets/chunk-GMAD6QVW-Bppi3Ctd.js +0 -72
  968. package/out/web/assets/chunk-JQ64N6SF-DGg5kcEV.js +0 -1
  969. package/out/web/assets/chunk-JWPE2WC7-BwtYOHqU.js +0 -1
  970. package/out/web/assets/chunk-LCL6LL3I-CCYcjd7p.js +0 -206
  971. package/out/web/assets/chunk-LNGE3PJU-AdBoOWw2.js +0 -34
  972. package/out/web/assets/chunk-OBVCFTLP-DJeNil5M.js +0 -1
  973. package/out/web/assets/chunk-P2QGCYS3-lRzkRjBB.js +0 -1
  974. package/out/web/assets/chunk-POPQ4Y6H-DouASMBW.js +0 -1
  975. package/out/web/assets/chunk-PWAF6VOD-DMmD1vVf.js +0 -1
  976. package/out/web/assets/chunk-R7TYR2AO-CYLA5UC5.js +0 -10
  977. package/out/web/assets/chunk-RHFEMEQ7-BiXwEcUT.js +0 -168
  978. package/out/web/assets/chunk-SVP7TREG-BV-zPslF.js +0 -88
  979. package/out/web/assets/chunk-TEH6E4GO-B2RGEohZ.js +0 -62
  980. package/out/web/assets/chunk-XXDRQBXY-DnJkbUaw.js +0 -1
  981. package/out/web/assets/chunk-Y2CYZVJY-C9o-s6OY.js +0 -1
  982. package/out/web/assets/classDiagram-DTDB5LWJ-Dq0smRWk.js +0 -1
  983. package/out/web/assets/classDiagram-v2-JRS7N3AN-Dq0smRWk.js +0 -1
  984. package/out/web/assets/config-C-ZFtipU.css +0 -1
  985. package/out/web/assets/config-eEDngJXO.js +0 -1
  986. package/out/web/assets/core-hQ2VA9Ti.js +0 -1
  987. package/out/web/assets/cose-bilkent-JH36ORCC--UN4aw5-.js +0 -1
  988. package/out/web/assets/cynefin-OW5HDTMX-Cas4csHM.js +0 -1
  989. package/out/web/assets/cynefinDiagram-5FMLGOSQ-Bbpj66Y3.js +0 -62
  990. package/out/web/assets/cytoscape.esm-Df1UN4Y1.js +0 -321
  991. package/out/web/assets/dagre-3AP2YEHR-B0-2RDEd.js +0 -4
  992. package/out/web/assets/dagre-BWadQu2F.js +0 -1
  993. package/out/web/assets/diagram-S7CK7UJ4-BdzEjUyH.js +0 -30
  994. package/out/web/assets/diagram-UQ7AKVKN-CJDwyPxF.js +0 -41
  995. package/out/web/assets/diagram-VSXAHHWV-BxaG1r_B.js +0 -3
  996. package/out/web/assets/diagram-VX7I27RA-BMmUqm8f.js +0 -24
  997. package/out/web/assets/diagram-Z3DM3KII-CFtd03eE.js +0 -24
  998. package/out/web/assets/dist-BoTOASLF.js +0 -1
  999. package/out/web/assets/dist-js-Bf2bhoku.js +0 -1
  1000. package/out/web/assets/ebnfDiagram-PWID7BFC-DwAen1hO.js +0 -1
  1001. package/out/web/assets/erDiagram-SSCWMZ5O-BCZSHACj.js +0 -99
  1002. package/out/web/assets/event-ChBVpRy0.js +0 -1
  1003. package/out/web/assets/eventmodeling-NTZA5JFV-XHStE4E_.js +0 -1
  1004. package/out/web/assets/flowDiagram-A5DVABFB-BLg7nEcd.js +0 -1
  1005. package/out/web/assets/ganttDiagram-EL5Y4UJY-BAEu1Cql.js +0 -292
  1006. package/out/web/assets/gitGraph-4MIJSDKK-r32Vyper.js +0 -1
  1007. package/out/web/assets/gitGraphDiagram-WWUBYQGX-z9o9qt6V.js +0 -106
  1008. package/out/web/assets/graphlib-CTR5ZhLf.js +0 -1
  1009. package/out/web/assets/index-Btsf4L8M.css +0 -1
  1010. package/out/web/assets/index-D8D9MeRA.js +0 -11
  1011. package/out/web/assets/info-A6RAGUB7-BvLaC2cw.js +0 -1
  1012. package/out/web/assets/infoDiagram-RXCK75RN-Be6WAXXw.js +0 -2
  1013. package/out/web/assets/ishikawaDiagram-5VMMS53U-DZjoHJHC.js +0 -70
  1014. package/out/web/assets/journeyDiagram-EYS64GPL-CtVkjkGm.js +0 -139
  1015. package/out/web/assets/kanban-definition-3QL26DDD-CW5FMv1w.js +0 -89
  1016. package/out/web/assets/katex-O3jvqgnU.js +0 -257
  1017. package/out/web/assets/lucide-react-Ip3L6Jtn.js +0 -1
  1018. package/out/web/assets/markdown-BSeeUbJH.js +0 -38
  1019. package/out/web/assets/mermaid-parser.core-aUgCqsZ5.js +0 -7
  1020. package/out/web/assets/mermaid.core-D57sEiSL.js +0 -11
  1021. package/out/web/assets/mindmap-definition-FBJOCRG2-DAmSpM3a.js +0 -96
  1022. package/out/web/assets/modelManager-tMVmJVAW.js +0 -2
  1023. package/out/web/assets/oauthManager-9jxicDWJ.js +0 -1
  1024. package/out/web/assets/packet-AYTQ26CC-DJ27iIoJ.js +0 -1
  1025. package/out/web/assets/pegDiagram-XKGWAZYB-Bj3ii-gS.js +0 -1
  1026. package/out/web/assets/pie-WAS4IAKB-DG5mCUbc.js +0 -1
  1027. package/out/web/assets/pieDiagram-E7YTZNPT-BrJLPnDA.js +0 -39
  1028. package/out/web/assets/prompt-latin-400-normal-BQ9zjSN8.woff2 +0 -0
  1029. package/out/web/assets/prompt-latin-400-normal-CxU4ec_r.woff +0 -0
  1030. package/out/web/assets/prompt-latin-500-normal-B1edBDQ9.woff +0 -0
  1031. package/out/web/assets/prompt-latin-500-normal-CxzxEHZc.woff2 +0 -0
  1032. package/out/web/assets/prompt-latin-600-normal-CSDDLorv.woff +0 -0
  1033. package/out/web/assets/prompt-latin-600-normal-hKZWXsc1.woff2 +0 -0
  1034. package/out/web/assets/prompt-latin-700-normal-BfbftDSq.woff +0 -0
  1035. package/out/web/assets/prompt-latin-700-normal-I2gc831J.woff2 +0 -0
  1036. package/out/web/assets/prompt-thai-400-normal-BrkKv8cO.woff2 +0 -0
  1037. package/out/web/assets/prompt-thai-400-normal-DJypnfr3.woff +0 -0
  1038. package/out/web/assets/prompt-thai-500-normal-C18pDUoL.woff2 +0 -0
  1039. package/out/web/assets/prompt-thai-500-normal-D7YNqUJD.woff +0 -0
  1040. package/out/web/assets/prompt-thai-600-normal-CYPViXds.woff +0 -0
  1041. package/out/web/assets/prompt-thai-600-normal-MrdfU7zR.woff2 +0 -0
  1042. package/out/web/assets/prompt-thai-700-normal-Cb5ilR-c.woff +0 -0
  1043. package/out/web/assets/prompt-thai-700-normal-Cg4aQ0Nn.woff2 +0 -0
  1044. package/out/web/assets/quadrantDiagram-AXDQQJYC-3x_TmF3j.js +0 -7
  1045. package/out/web/assets/radar-RG4KPBEZ-CrXaTTuV.js +0 -1
  1046. package/out/web/assets/railroad-74A4TZTK-BpqMH9BE.js +0 -1
  1047. package/out/web/assets/railroad-abnf-HS5TGJTU-DHI0uLlV.js +0 -1
  1048. package/out/web/assets/railroad-ebnf-LZEXJU2U-CQCb316y.js +0 -1
  1049. package/out/web/assets/railroad-peg-WCYAUIDC-Ph1giKkH.js +0 -1
  1050. package/out/web/assets/railroadDiagram-O6MQD6OU-BRq6wIxx.js +0 -1
  1051. package/out/web/assets/requirementDiagram-EFPCY7ZU-DIJj6G1g.js +0 -84
  1052. package/out/web/assets/rough.esm-DFbTUy8a.js +0 -1
  1053. package/out/web/assets/sankeyDiagram-P5KCCOFB-DZajSryq.js +0 -40
  1054. package/out/web/assets/sequenceDiagram-WJ2MYXX4-DxJCWyZS.js +0 -162
  1055. package/out/web/assets/sizeCapture-X5ZJPWSS-DKeOCaN2.js +0 -1
  1056. package/out/web/assets/src-DnfeXQPr.js +0 -1
  1057. package/out/web/assets/stateDiagram-HBIQ2CUA-k6voi8Vf.js +0 -1
  1058. package/out/web/assets/stateDiagram-v2-4QOOHH4V-DeEc0lGZ.js +0 -1
  1059. package/out/web/assets/studio-B1mk40wD.js +0 -1
  1060. package/out/web/assets/swimlanes-XN3QIQJK-igYhTjXB.js +0 -1
  1061. package/out/web/assets/swimlanesDiagram-VK2B7HYN-DpVik9v7.js +0 -8
  1062. package/out/web/assets/timeline-definition-24CTP7MA-Dke2p93p.js +0 -120
  1063. package/out/web/assets/treeView-Q6P3EWNA-hNELnXUs.js +0 -1
  1064. package/out/web/assets/treemap-WGGIJYW6-D2B6wDsx.js +0 -1
  1065. package/out/web/assets/vennDiagram-4TSXK5OY-CDkW7rUP.js +0 -34
  1066. package/out/web/assets/wardley-WFR3VGLG-BECdXETi.js +0 -1
  1067. package/out/web/assets/wardleyDiagram-VM6X3IG4-B5BycpQ4.js +0 -78
  1068. package/out/web/assets/window-5qYXKoW7.js +0 -1
  1069. package/out/web/assets/xychartDiagram-S5SC5T6Z-CtJS3b--.js +0 -7
  1070. package/skills-lock.json +0 -77
  1071. package/src/renderer/public/Live2DCubismCore.js +0 -9
  1072. package/src/renderer/shared/components/AgentActivityDrawer.tsx +0 -203
  1073. package/src/renderer/shared/components/AgentActivityTable.tsx +0 -301
  1074. package/src/renderer/shared/components/ApprovalCard.tsx +0 -340
  1075. package/src/renderer/shared/components/ArtifactPreviewPanel.tsx +0 -505
  1076. package/src/renderer/shared/components/AuthGate.tsx +0 -191
  1077. package/src/renderer/shared/components/CalculationCard.tsx +0 -100
  1078. package/src/renderer/shared/components/ChatCodeBlock.tsx +0 -105
  1079. package/src/renderer/shared/components/ChatMessageItem.tsx +0 -306
  1080. package/src/renderer/shared/components/ChatPanel.tsx +0 -2226
  1081. package/src/renderer/shared/components/ChunkErrorBoundary.tsx +0 -284
  1082. package/src/renderer/shared/components/CliSessionsView.tsx +0 -495
  1083. package/src/renderer/shared/components/CodeReviewPage.tsx +0 -565
  1084. package/src/renderer/shared/components/CommandPalette.tsx +0 -593
  1085. package/src/renderer/shared/components/DashboardSidebar.tsx +0 -1341
  1086. package/src/renderer/shared/components/DesktopTitlebar.tsx +0 -345
  1087. package/src/renderer/shared/components/DiffReviewModal.tsx +0 -565
  1088. package/src/renderer/shared/components/GeminiLiveOverlay.tsx +0 -128
  1089. package/src/renderer/shared/components/GitBranchSelector.tsx +0 -442
  1090. package/src/renderer/shared/components/ImageGenCard.tsx +0 -159
  1091. package/src/renderer/shared/components/ImageSearchCard.tsx +0 -168
  1092. package/src/renderer/shared/components/ImageStudioPanel.tsx +0 -739
  1093. package/src/renderer/shared/components/InlineThoughtNote.tsx +0 -27
  1094. package/src/renderer/shared/components/LinkedFoldersView.tsx +0 -614
  1095. package/src/renderer/shared/components/McpRegistryPicker.tsx +0 -302
  1096. package/src/renderer/shared/components/McpServersView.tsx +0 -952
  1097. package/src/renderer/shared/components/McpToolAllowlist.tsx +0 -184
  1098. package/src/renderer/shared/components/MermaidCard.tsx +0 -267
  1099. package/src/renderer/shared/components/MintDashboard.tsx +0 -2665
  1100. package/src/renderer/shared/components/ModalErrorBoundary.tsx +0 -127
  1101. package/src/renderer/shared/components/ModelSelectorPopover.tsx +0 -929
  1102. package/src/renderer/shared/components/PlanChecklistWidget.tsx +0 -77
  1103. package/src/renderer/shared/components/PluginsView.tsx +0 -325
  1104. package/src/renderer/shared/components/RewindModal.tsx +0 -421
  1105. package/src/renderer/shared/components/RunSummaryDashboard.tsx +0 -149
  1106. package/src/renderer/shared/components/ScheduledTasksView.tsx +0 -613
  1107. package/src/renderer/shared/components/SkillsView.tsx +0 -347
  1108. package/src/renderer/shared/components/SlashSuggestions.tsx +0 -318
  1109. package/src/renderer/shared/components/SourcesBlock.tsx +0 -450
  1110. package/src/renderer/shared/components/StockCard.tsx +0 -168
  1111. package/src/renderer/shared/components/SubagentDagView.tsx +0 -366
  1112. package/src/renderer/shared/components/TerminalDock.tsx +0 -241
  1113. package/src/renderer/shared/components/ThinkingBlock.tsx +0 -102
  1114. package/src/renderer/shared/components/ToolSurfacePage.tsx +0 -520
  1115. package/src/renderer/shared/components/UiFeatureCard.tsx +0 -267
  1116. package/src/renderer/shared/components/UiGridCard.tsx +0 -188
  1117. package/src/renderer/shared/components/VeoStudioPanel.tsx +0 -707
  1118. package/src/renderer/shared/components/WeatherCard.tsx +0 -224
  1119. package/src/renderer/shared/components/WorkspaceContextPopover.tsx +0 -133
  1120. package/src/renderer/shared/components/WorkspaceSelector.tsx +0 -149
  1121. package/src/renderer/src/companionWidget.ts +0 -95
  1122. package/src/renderer/src/components/CalculationCard.tsx +0 -3
  1123. package/src/renderer/src/components/ImageGenCard.tsx +0 -2
  1124. package/src/renderer/src/components/ImageSearchCard.tsx +0 -2
  1125. package/src/renderer/src/components/Live2DStage.tsx +0 -412
  1126. package/src/renderer/src/components/MermaidCard.tsx +0 -1
  1127. package/src/renderer/src/components/ModelPanel.tsx +0 -155
  1128. package/src/renderer/src/components/PicturesLibrary.tsx +0 -288
  1129. package/src/renderer/src/components/Settings/AgentsTab.tsx +0 -4
  1130. package/src/renderer/src/components/Settings/AudioTab.tsx +0 -3
  1131. package/src/renderer/src/components/Settings/AutomationTab.tsx +0 -3
  1132. package/src/renderer/src/components/Settings/GeneralTab.tsx +0 -3
  1133. package/src/renderer/src/components/Settings/MemoryTab.tsx +0 -3
  1134. package/src/renderer/src/components/Settings/PluginsTab.tsx +0 -3
  1135. package/src/renderer/src/components/Settings/ProfileTab.tsx +0 -3
  1136. package/src/renderer/src/components/Settings/ShortcutsTab.tsx +0 -3
  1137. package/src/renderer/src/components/Settings/ThemeTab.tsx +0 -3
  1138. package/src/renderer/src/components/StockCard.tsx +0 -3
  1139. package/src/renderer/src/components/WeatherCard.tsx +0 -3
  1140. package/src/renderer/src/css/model.css +0 -628
  1141. package/src/renderer/src/css/pictures.css +0 -415
  1142. package/src/renderer/src/css/settings/base.css +0 -1263
  1143. package/src/renderer/src-web/companionWidget.ts +0 -37
  1144. package/src/renderer/src-web/components/ModelPanel.tsx +0 -12
  1145. package/src/renderer/src-web/components/PicturesLibrary.tsx +0 -285
  1146. package/src/renderer/src-web/components/Settings/AgentsTab.tsx +0 -4
  1147. package/src/renderer/src-web/components/Settings/AudioTab.tsx +0 -3
  1148. package/src/renderer/src-web/components/Settings/AutomationTab.tsx +0 -3
  1149. package/src/renderer/src-web/components/Settings/GeneralTab.tsx +0 -3
  1150. package/src/renderer/src-web/components/Settings/MemoryTab.tsx +0 -3
  1151. package/src/renderer/src-web/components/Settings/PluginsTab.tsx +0 -3
  1152. package/src/renderer/src-web/components/Settings/ProfileTab.tsx +0 -3
  1153. package/src/renderer/src-web/components/Settings/ShortcutsTab.tsx +0 -3
  1154. package/src/renderer/src-web/components/Settings/ThemeTab.tsx +0 -3
  1155. package/src/renderer/src-web/css/model.css +0 -628
  1156. package/src/renderer/src-web/css/pictures.css +0 -462
  1157. package/src/renderer/src-web/css/settings/base.css +0 -1263
  1158. package/src/renderer/src-web/css/spotlight.css +0 -102
  1159. package/src/renderer/src-web/css/widget.css +0 -213
  1160. /package/src/renderer/shared/components/{UiMockupWidget.tsx → cards/UiMockupWidget.tsx} +0 -0
  1161. /package/src/renderer/shared/components/{ScreenCaptureDialog.css → studio/ScreenCaptureDialog.css} +0 -0
  1162. /package/src/renderer/shared/components/{ScreenCaptureDialog.tsx → studio/ScreenCaptureDialog.tsx} +0 -0
@@ -1,2226 +0,0 @@
1
- import { useEffect, useMemo, useRef, useState, useCallback, Fragment, type ChangeEvent, type ClipboardEvent, type DragEvent, type FormEvent, type KeyboardEvent, type RefObject } from 'react'
2
- import { hasAgentToolActivity, thoughtsFrom, extendedThoughtsFrom, mergeFileChanges, parseFileChangesFromProgress } from '../agentProgress'
3
- import { visibleInteractionsDuringRun } from '../conversation/syncView'
4
- import {
5
- GEMINI_MODELS,
6
- OPENAI_MODELS,
7
- ANTHROPIC_MODELS,
8
- OPENROUTER_MODELS,
9
- DEEPSEEK_MODELS,
10
- HF_MODELS,
11
- LOCAL_MODELS,
12
- OLLAMA_MODELS,
13
- GEMINI_LIVE_VOICES,
14
- } from '../constants/models'
15
- import { badge, providerLabel, fallbackNotice } from '../utils/providers'
16
- import { activitiesFrom, parseWebSearchSources, type AgentActivity, type AgentActivityView } from '../utils/agentActivity'
17
- import SlashSuggestions, { type SlashSuggestionsHandle } from './SlashSuggestions'
18
- import { AgentActivityTable } from './AgentActivityTable'
19
- import { ChatCodeBlock } from './ChatCodeBlock'
20
- import { renderApprovalDetails, renderDiff, type ApprovalDetails } from '../utils/approval'
21
- import { ApprovalCard } from './ApprovalCard'
22
- import { renderFormattedMessage, readableAssistantText, cleanSpeechText, renderSpeakerIcon, renderCopyIcon } from '../utils/markdown'
23
- import { ThinkingBlock } from './ThinkingBlock'
24
- import SourcesBlock from './SourcesBlock'
25
- import ChatMessageItem from './ChatMessageItem'
26
- import { AgentActivityDrawer } from './AgentActivityDrawer'
27
- import { ArtifactPreviewPanel, type ArtifactFile } from './ArtifactPreviewPanel'
28
- import { RewindModal } from './RewindModal'
29
- import type { AgentProgress, ChatResponse, DiffHunk, FileChange, GitCheckpoint, RuntimeStatus } from '../types'
30
- import { numericSetting, shouldShowSessionDivider, formatSessionDividerLabel } from '../utils/ui'
31
- import { useVoiceInput } from '@/voiceInput'
32
- import { useGeminiLiveVoice } from '../utils/useGeminiLiveVoice'
33
- import GeminiLiveOverlay from './GeminiLiveOverlay'
34
- import { isSupportedDocument, SUPPORTED_DOCUMENT_ACCEPT } from '../utils/documentTypes'
35
- import ModelSelectorPopover from './ModelSelectorPopover'
36
- import GitBranchSelector from './GitBranchSelector'
37
- import WorkspaceSelector from './WorkspaceSelector'
38
- import CodeReviewPage from './CodeReviewPage'
39
- import type { ToolSurface } from './ToolSurfacePage'
40
-
41
- import { catalogPlatform, conversationPlatform, runtimePlatform } from '../platform'
42
-
43
- const STREAM_MARKDOWN_UPDATE_MS = 120
44
-
45
- function useThrottledValue<T>(value: T, intervalMs: number): T {
46
- const [throttledValue, setThrottledValue] = useState(value)
47
- const latestValueRef = useRef(value)
48
- const lastUpdateRef = useRef(0)
49
- const timerRef = useRef<ReturnType<typeof window.setTimeout> | null>(null)
50
-
51
- useEffect(() => {
52
- latestValueRef.current = value
53
- if (Object.is(value, throttledValue)) return
54
-
55
- const flush = () => {
56
- timerRef.current = null
57
- lastUpdateRef.current = Date.now()
58
- setThrottledValue(latestValueRef.current)
59
- }
60
- const remaining = intervalMs - (Date.now() - lastUpdateRef.current)
61
- if (remaining <= 0) flush()
62
- else if (timerRef.current === null) timerRef.current = window.setTimeout(flush, remaining)
63
- }, [value, throttledValue, intervalMs])
64
-
65
- useEffect(() => () => {
66
- if (timerRef.current !== null) window.clearTimeout(timerRef.current)
67
- }, [])
68
-
69
- return throttledValue
70
- }
71
-
72
- const {
73
- getTtsUrls, startGeminiLiveSession, sendGeminiLiveAudioChunk, stopGeminiLiveSession,
74
- listGitCheckpoints, rollbackGitCheckpoint, undoGitCheckpoint,
75
- } = conversationPlatform
76
- const { fetchProviderModels } = catalogPlatform
77
-
78
-
79
- interface ChatPanelContract {
80
- interactions: any[]
81
- sendingInteractionId?: number | null
82
- hasOlder?: boolean
83
- onLoadOlder?: () => Promise<void>
84
- sending: boolean
85
- sendingMessage: string
86
- sendingImageCount: number
87
- sendingVideoCount?: number
88
- streamedReply: string
89
- streamedResponse: ChatResponse | null
90
- agentProgress: AgentProgress[]
91
- agentActivitySnapshots: Record<string, AgentProgress[]>
92
- thinkingExpanded: Record<string, boolean>
93
- onThinkingExpandedChange: (key: string, open: boolean) => void
94
- message: string
95
- imageAttachments: Array<{ dataUri: string; name: string; previewDataUri?: string; objectUrl?: string }>
96
- videoAttachments: Array<{ dataUri: string; name: string }>
97
- documentName: string
98
- pendingApproval: any | null
99
- agentMode: boolean
100
- /** Desktop only — plan-mode-approval UI isn't available on web. */
101
- planMode?: boolean
102
- status: RuntimeStatus | null
103
- /** Desktop only — web has no local workspace-folder concept to select from. */
104
- workspacePath?: string
105
- chatId: string
106
- chatEnd: RefObject<HTMLDivElement | null>
107
- welcomeInteraction: any
108
- onSubmit: (event: FormEvent<HTMLFormElement>) => void
109
- onSelectImage: (event: ChangeEvent<HTMLInputElement>) => void
110
- onSelectVideo: (event: ChangeEvent<HTMLInputElement>) => void
111
- onSelectDocument: (event: ChangeEvent<HTMLInputElement>) => void
112
- onPasteImage: (clipboardData: DataTransfer) => boolean
113
- onSetMessage: (message: string) => void
114
- onSendVoiceMessage: (message: string, audioDataUri?: string | null) => Promise<void>
115
- onRemoveImage: (idx: number) => void
116
- onRemoveVideo: (idx: number) => void
117
- onRemoveDocument: () => void
118
- onStartWebSearch: () => void
119
- onCaptureScreen: () => void
120
- onSetAgentMode: (enabled: boolean) => void
121
- /** Desktop only — see `planMode`. */
122
- onSetPlanMode?: (enabled: boolean) => void
123
- onSetProvider: (provider: string) => void
124
- /** Desktop only — see `workspacePath`. */
125
- onSelectWorkspace?: (path?: string) => void
126
- onWorkspaceChanged?: () => void
127
- recentWorkspacePaths: string[]
128
- onApproval: (approved: boolean, autoApproveSession?: boolean, answer?: string) => void
129
- settingsConfig: any
130
- onUpdateSettings?: (config: any) => void
131
- onSetModel: (model: string) => void
132
- onSelectModelAndProvider?: (provider: string, model: string) => void
133
- onCancelMessage: () => void
134
- onClearMessages: () => void
135
- onSetGeminiLiveVoice: (voice: string) => Promise<void>
136
- /** Web only — desktop's window has no mobile-width sidebar to toggle. */
137
- onToggleMobileSidebar?: () => void
138
- isCliSession?: boolean
139
- cliSessionId?: string
140
- onBackToCode?: () => void
141
- conversationTitle?: string
142
- onOpenArtifact?: (artifact: ArtifactFile) => void
143
- onOpenReview?: (review: Extract<ToolSurface, { kind: 'review' }>) => void
144
- }
145
-
146
- export type ConversationViewModel = Pick<ChatPanelContract,
147
- | 'interactions' | 'sending' | 'sendingMessage' | 'sendingImageCount' | 'sendingVideoCount'
148
- | 'streamedReply' | 'streamedResponse' | 'agentProgress' | 'agentActivitySnapshots'
149
- | 'thinkingExpanded' | 'message' | 'imageAttachments' | 'videoAttachments' | 'documentName'
150
- | 'pendingApproval' | 'agentMode' | 'planMode' | 'status' | 'workspacePath' | 'chatId' | 'recentWorkspacePaths'
151
- | 'chatEnd' | 'welcomeInteraction' | 'settingsConfig' | 'isCliSession' | 'cliSessionId'
152
- | 'conversationTitle'
153
- | 'hasOlder'
154
- | 'sendingInteractionId'
155
- >
156
- export type ConversationActions = Omit<ChatPanelContract, keyof ConversationViewModel>
157
-
158
- interface ChatPanelProps {
159
- conversation: ConversationViewModel
160
- actions: ConversationActions
161
- }
162
-
163
- export default function ChatPanel({
164
- conversation,
165
- actions,
166
- }: ChatPanelProps) {
167
- const {
168
- interactions,
169
- sendingInteractionId,
170
- hasOlder,
171
- onLoadOlder,
172
- sending,
173
- sendingMessage,
174
- sendingImageCount,
175
- sendingVideoCount,
176
- streamedReply,
177
- streamedResponse,
178
- agentProgress,
179
- agentActivitySnapshots,
180
- thinkingExpanded,
181
- onThinkingExpandedChange,
182
- message,
183
- imageAttachments,
184
- videoAttachments,
185
- documentName,
186
- pendingApproval,
187
- agentMode,
188
- planMode,
189
- status,
190
- workspacePath,
191
- chatId,
192
- recentWorkspacePaths,
193
- chatEnd,
194
- welcomeInteraction,
195
- onSubmit,
196
- onSelectImage,
197
- onSelectVideo,
198
- onSelectDocument,
199
- onPasteImage,
200
- onSetMessage,
201
- onSendVoiceMessage,
202
- onRemoveImage,
203
- onRemoveVideo,
204
- onRemoveDocument,
205
- onStartWebSearch,
206
- onCaptureScreen,
207
- onSetAgentMode,
208
- onSetPlanMode,
209
- onSetProvider,
210
- onSelectWorkspace,
211
- onWorkspaceChanged,
212
- onApproval,
213
- settingsConfig,
214
- onUpdateSettings,
215
- onSetModel,
216
- onSelectModelAndProvider,
217
- onCancelMessage,
218
- onClearMessages,
219
- onSetGeminiLiveVoice,
220
- onToggleMobileSidebar,
221
- isCliSession,
222
- cliSessionId,
223
- onBackToCode,
224
- conversationTitle,
225
- onOpenArtifact,
226
- onOpenReview,
227
- } = { ...conversation, ...actions }
228
- // The initiating surface already renders a live prompt/reply pair below.
229
- // Hide its matching persisted turn until the local stream settles, while
230
- // still showing turns submitted from other surfaces during that time.
231
- const visibleInteractions = useMemo(
232
- () => visibleInteractionsDuringRun(interactions, sending, sendingInteractionId),
233
- [interactions, sending, sendingInteractionId],
234
- )
235
- const agentActivities = activitiesFrom(agentProgress)
236
- // Keep the composer summary tied to the active/latest agent run. This is cheap
237
- // metadata, but memoizing it avoids parsing the activity stream while typing.
238
- const composerChanges = useMemo(() => parseFileChangesFromProgress(agentProgress), [agentProgress])
239
- const composerAdditions = useMemo(
240
- () => composerChanges.reduce((sum, change) => sum + change.additions, 0),
241
- [composerChanges],
242
- )
243
- const composerDeletions = useMemo(
244
- () => composerChanges.reduce((sum, change) => sum + change.deletions, 0),
245
- [composerChanges],
246
- )
247
- const composerChangeLabel = composerChanges.length === 1
248
- ? '1 file changed'
249
- : `${composerChanges.length} files changed`
250
- const conversationChanges = useMemo(() => {
251
- const historicalGroups = interactions.map((interaction) => {
252
- const interactionId = String(interaction.id)
253
- const progress = agentActivitySnapshots[interactionId] ?? interaction.agentActivity ?? []
254
- return parseFileChangesFromProgress(progress)
255
- })
256
- const activeChanges = parseFileChangesFromProgress(agentProgress)
257
- return mergeFileChanges([...historicalGroups, activeChanges])
258
- }, [agentActivitySnapshots, agentProgress, interactions])
259
-
260
- const projectName = useMemo(() => {
261
- if (!workspacePath) return null
262
- const clean = workspacePath.replace(/[\\/]+$/, '').trim()
263
- if (!clean) return null
264
- const parts = clean.split(/[\\/]/)
265
- return parts[parts.length - 1] || clean
266
- }, [workspacePath])
267
-
268
- // Markdown parsing can be expensive for code, tables, and interactive cards.
269
- // Limit it to a steady cadence instead of parsing on every stream chunk.
270
- const throttledStreamedReply = useThrottledValue(streamedReply, STREAM_MARKDOWN_UPDATE_MS)
271
- const liveWebSources = useMemo(() => parseWebSearchSources(agentProgress), [agentProgress])
272
- const activeFallbackNotice = fallbackNotice(streamedResponse)
273
- const lastThinkingProgress = [...agentProgress].reverse().find(p => p.type === 'Thinking')
274
- let activeAgentName: string | null = null
275
- let activeModelName: string | null = null
276
- if (lastThinkingProgress && lastThinkingProgress.type === 'Thinking') {
277
- activeAgentName = (lastThinkingProgress.data as any).agent_name || null
278
- activeModelName = (lastThinkingProgress.data as any).model_name || null
279
- }
280
- const [openActivityIds, setOpenActivityIds] = useState<Record<string, boolean>>({})
281
- const [openReviewIds, setOpenReviewIds] = useState<Record<string, boolean>>({})
282
- const [openFileDiffs, setOpenFileDiffs] = useState<Record<string, boolean>>({})
283
- const [showAllFileChanges, setShowAllFileChanges] = useState<Record<string, boolean>>({})
284
- const [reviewPage, setReviewPage] = useState<{ title: string; changes: FileChange[] } | null>(null)
285
- const [activeArtifact, setActiveArtifact] = useState<ArtifactFile | null>(null)
286
- const [toolMenuOpen, setToolMenuOpen] = useState(false)
287
- const [elapsedSeconds, setElapsedSeconds] = useState(0)
288
- const [dynamicOllamaModels, setDynamicOllamaModels] = useState<string[]>(OLLAMA_MODELS)
289
-
290
- const openArtifact = (artifact: ArtifactFile) => {
291
- if (onOpenArtifact) onOpenArtifact(artifact)
292
- else setActiveArtifact(artifact)
293
- }
294
-
295
- const openReview = (title: string, changes: FileChange[]) => {
296
- if (onOpenReview) onOpenReview({ id: `review:${Date.now()}`, kind: 'review', title: 'Review', reviewTitle: title, changes })
297
- else setReviewPage({ title, changes })
298
- }
299
-
300
- useEffect(() => {
301
- let cancelled = false
302
- const fetchOllamaModels = async () => {
303
- const host = settingsConfig?.ollamaHost || 'http://localhost:11434'
304
- const cleanHost = host.endsWith('/') ? host.slice(0, -1) : host
305
- try {
306
- const controller = new AbortController()
307
- const timer = setTimeout(() => controller.abort(), 2000)
308
- const res = await fetch(`${cleanHost}/api/tags`, { signal: controller.signal })
309
- clearTimeout(timer)
310
- if (res.ok) {
311
- const data = await res.json()
312
- if (!cancelled && data && Array.isArray(data.models) && data.models.length > 0) {
313
- setDynamicOllamaModels(data.models.map((m: any) => m.name))
314
- return
315
- }
316
- }
317
- } catch (err) {
318
- // Direct fetch failed or timed out — try backend Tauri / API server
319
- }
320
-
321
- try {
322
- const live = await fetchProviderModels('ollama', '')
323
- if (!cancelled && live && live.length > 0) {
324
- setDynamicOllamaModels(live)
325
- return
326
- }
327
- } catch (err) {
328
- // Fallback
329
- }
330
-
331
- if (!cancelled) {
332
- if (settingsConfig?.ollamaModel) {
333
- setDynamicOllamaModels([settingsConfig.ollamaModel])
334
- } else {
335
- setDynamicOllamaModels(OLLAMA_MODELS)
336
- }
337
- }
338
- }
339
- fetchOllamaModels()
340
- return () => {
341
- cancelled = true
342
- }
343
- }, [settingsConfig?.ollamaHost, settingsConfig?.ollamaModel])
344
-
345
- useEffect(() => {
346
- if (!sending) {
347
- setElapsedSeconds(0)
348
- return
349
- }
350
-
351
- const startTime = Date.now()
352
- setElapsedSeconds(0)
353
-
354
- const timer = setInterval(() => {
355
- const elapsed = Math.round((Date.now() - startTime) / 1000)
356
- setElapsedSeconds(elapsed)
357
- }, 1000)
358
-
359
- return () => clearInterval(timer)
360
- }, [sending])
361
- const [speakingText, setSpeakingText] = useState<string | null>(null)
362
- const [copiedId, setCopiedId] = useState<string | number | null>(null)
363
-
364
- const chatContainerRef = useRef<HTMLDivElement | null>(null)
365
- const [showScrollToBottom, setShowScrollToBottom] = useState(false)
366
- const [loadingOlder, setLoadingOlder] = useState(false)
367
- const isNearBottomRef = useRef(true)
368
- const scrollFrameRef = useRef<number | null>(null)
369
-
370
- const handleChatScroll = useCallback(() => {
371
- const el = chatContainerRef.current
372
- if (!el) return
373
- const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight
374
- const nearBottom = distanceFromBottom <= 240
375
- setShowScrollToBottom(!nearBottom)
376
- isNearBottomRef.current = nearBottom
377
- }, [])
378
-
379
- const scheduleScrollToLatest = useCallback(() => {
380
- if (!isNearBottomRef.current || scrollFrameRef.current !== null) return
381
- scrollFrameRef.current = window.requestAnimationFrame(() => {
382
- scrollFrameRef.current = null
383
- const element = chatContainerRef.current
384
- if (element && isNearBottomRef.current) {
385
- // Smooth scrolling on every token restarts animations and causes jank.
386
- element.scrollTop = element.scrollHeight
387
- }
388
- })
389
- }, [])
390
-
391
- useEffect(() => {
392
- scheduleScrollToLatest()
393
- }, [interactions, sending, streamedReply, pendingApproval, agentProgress, scheduleScrollToLatest])
394
-
395
- useEffect(() => () => {
396
- if (scrollFrameRef.current !== null) window.cancelAnimationFrame(scrollFrameRef.current)
397
- }, [])
398
-
399
- useEffect(() => {
400
- const el = chatContainerRef.current
401
- if (!el) return
402
- if (isNearBottomRef.current) {
403
- el.scrollTop = el.scrollHeight
404
- }
405
- }, [activeArtifact])
406
-
407
- const scrollToBottom = useCallback(() => {
408
- chatEnd.current?.scrollIntoView({ behavior: 'smooth' })
409
- }, [chatEnd])
410
-
411
- const [splitRatio, setSplitRatio] = useState<number>(() => {
412
- try {
413
- const saved = localStorage.getItem('mint_preview_split_ratio')
414
- if (saved) {
415
- const val = parseFloat(saved)
416
- if (!isNaN(val) && val >= 0.2 && val <= 0.8) {
417
- return val
418
- }
419
- }
420
- } catch {
421
- // ignore
422
- }
423
- return 0.5
424
- })
425
- const currentRatioRef = useRef(splitRatio)
426
- const wrapperRef = useRef<HTMLDivElement | null>(null)
427
-
428
- const handleResizerMouseDown = useCallback((e: React.MouseEvent) => {
429
- e.preventDefault()
430
- const wrapper = wrapperRef.current
431
- if (!wrapper) return
432
-
433
- wrapper.classList.add('is-resizing')
434
-
435
- const onMouseMove = (moveEvent: MouseEvent) => {
436
- const rect = wrapper.getBoundingClientRect()
437
- const totalWidth = rect.width
438
- if (totalWidth <= 0) return
439
-
440
- const previewPx = rect.right - moveEvent.clientX
441
- const clampedPreviewPx = Math.max(280, Math.min(totalWidth - 340, previewPx))
442
- const newRatio = clampedPreviewPx / totalWidth
443
- currentRatioRef.current = newRatio
444
- wrapper.style.setProperty('--preview-width', `${(newRatio * 100).toFixed(2)}%`)
445
- }
446
-
447
- const onMouseUp = () => {
448
- window.removeEventListener('mousemove', onMouseMove)
449
- window.removeEventListener('mouseup', onMouseUp)
450
- wrapper.classList.remove('is-resizing')
451
- setSplitRatio(currentRatioRef.current)
452
- try {
453
- localStorage.setItem('mint_preview_split_ratio', currentRatioRef.current.toFixed(4))
454
- } catch {
455
- // ignore
456
- }
457
- }
458
-
459
- window.addEventListener('mousemove', onMouseMove)
460
- window.addEventListener('mouseup', onMouseUp)
461
- }, [])
462
-
463
- const handleResizerDoubleClick = useCallback(() => {
464
- const defaultRatio = 0.5
465
- currentRatioRef.current = defaultRatio
466
- setSplitRatio(defaultRatio)
467
- if (wrapperRef.current) {
468
- wrapperRef.current.style.setProperty('--preview-width', '50%')
469
- }
470
- try {
471
- localStorage.setItem('mint_preview_split_ratio', '0.5')
472
- } catch {
473
- // ignore
474
- }
475
- }, [])
476
-
477
- const [rewindModalState, setRewindModalState] = useState<{
478
- isOpen: boolean
479
- interaction: any | null
480
- changes: FileChange[]
481
- checkpoints: GitCheckpoint[]
482
- targetCheckpoint: GitCheckpoint | null
483
- isLoading: boolean
484
- }>({
485
- isOpen: false,
486
- interaction: null,
487
- changes: [],
488
- checkpoints: [],
489
- targetCheckpoint: null,
490
- isLoading: false,
491
- })
492
-
493
- const [undoToastState, setUndoToastState] = useState<{
494
- visible: boolean
495
- message: string
496
- canUndo: boolean
497
- isUndoing: boolean
498
- type: 'success' | 'error'
499
- }>({
500
- visible: false,
501
- message: '',
502
- canUndo: false,
503
- isUndoing: false,
504
- type: 'success',
505
- })
506
-
507
- useEffect(() => {
508
- if (!undoToastState.visible) return
509
- const timer = setTimeout(() => {
510
- setUndoToastState((prev) => ({ ...prev, visible: false }))
511
- }, 16000)
512
- return () => clearTimeout(timer)
513
- }, [undoToastState.visible, undoToastState.message])
514
-
515
- const handleConfirmRewind = useCallback(async (targetStep: number) => {
516
- const interaction = rewindModalState.interaction
517
- if (!interaction) return
518
- const chatId = interaction.chatId || 'cli'
519
-
520
- setRewindModalState((prev) => ({ ...prev, isLoading: true }))
521
- try {
522
- const res = await rollbackGitCheckpoint(chatId, targetStep, workspacePath)
523
- setRewindModalState((prev) => ({ ...prev, isOpen: false, isLoading: false }))
524
- if (res.status === 'ok') {
525
- setUndoToastState({
526
- visible: true,
527
- message: res.message,
528
- canUndo: true,
529
- isUndoing: false,
530
- type: 'success',
531
- })
532
- } else {
533
- setUndoToastState({
534
- visible: true,
535
- message: `Failed to rewind: ${res.message}`,
536
- canUndo: false,
537
- isUndoing: false,
538
- type: 'error',
539
- })
540
- }
541
- } catch (err: any) {
542
- setRewindModalState((prev) => ({ ...prev, isOpen: false, isLoading: false }))
543
- setUndoToastState({
544
- visible: true,
545
- message: `Error: ${err.message || String(err)}`,
546
- canUndo: false,
547
- isUndoing: false,
548
- type: 'error',
549
- })
550
- }
551
- }, [rewindModalState.interaction, workspacePath])
552
-
553
- const handleUndoRewind = useCallback(async () => {
554
- setUndoToastState((prev) => ({ ...prev, isUndoing: true }))
555
- try {
556
- const res = await undoGitCheckpoint(workspacePath)
557
- if (res.status === 'ok') {
558
- setUndoToastState({
559
- visible: true,
560
- message: res.message,
561
- canUndo: false,
562
- isUndoing: false,
563
- type: 'success',
564
- })
565
- } else {
566
- setUndoToastState({
567
- visible: true,
568
- message: `Failed to undo rewind: ${res.message}`,
569
- canUndo: true,
570
- isUndoing: false,
571
- type: 'error',
572
- })
573
- }
574
- } catch (err: any) {
575
- setUndoToastState({
576
- visible: true,
577
- message: `Error: ${err.message || String(err)}`,
578
- canUndo: true,
579
- isUndoing: false,
580
- type: 'error',
581
- })
582
- }
583
- }, [workspacePath])
584
-
585
- const handleCopyMessage = useCallback(async (id: string | number, text: string) => {
586
- try {
587
- const cleanText = readableAssistantText(text) || text
588
- await navigator.clipboard.writeText(cleanText)
589
- setCopiedId(id)
590
- setTimeout(() => {
591
- setCopiedId((current) => (current === id ? null : current))
592
- }, 2000)
593
- } catch (err) {
594
- console.error('Failed to copy message:', err)
595
- }
596
- }, [])
597
-
598
-
599
- const toolMenuRef = useRef<HTMLDivElement | null>(null)
600
- const audioRef = useRef<HTMLAudioElement | null>(null)
601
- const speechRunRef = useRef(0)
602
- const historyReadyRef = useRef(false)
603
- const submittedDuringSessionRef = useRef(false)
604
- const lastAutoSpokenIdRef = useRef<number | string | null>(null)
605
- const speakingRef = useRef<string | null>(null)
606
-
607
- // Drag and Drop Zone Overlay
608
- const [isDragging, setIsDragging] = useState(false)
609
- const dragCounter = useRef(0)
610
- const lastDropTimeRef = useRef(0)
611
-
612
- const handleDragEnter = (e: DragEvent<HTMLElement>) => {
613
- e.preventDefault()
614
- if (e.dataTransfer?.types?.includes('Files')) {
615
- dragCounter.current++
616
- setIsDragging(true)
617
- }
618
- }
619
-
620
- const handleDragLeave = (e: DragEvent<HTMLElement>) => {
621
- e.preventDefault()
622
- if (e.dataTransfer?.types?.includes('Files')) {
623
- dragCounter.current--
624
- if (dragCounter.current === 0) {
625
- setIsDragging(false)
626
- }
627
- }
628
- }
629
-
630
- const handleDragOver = (e: DragEvent<HTMLElement>) => {
631
- e.preventDefault()
632
- }
633
-
634
- const handleDrop = (e: DragEvent<HTMLElement>) => {
635
- e.preventDefault()
636
- e.stopPropagation()
637
- dragCounter.current = 0
638
- setIsDragging(false)
639
-
640
- const now = Date.now()
641
- if (now - lastDropTimeRef.current < 250) {
642
- return
643
- }
644
- lastDropTimeRef.current = now
645
-
646
- const files = e.dataTransfer?.files
647
- if (files && files.length > 0) {
648
- const file = files[0]
649
- if (file.type.startsWith('image/')) {
650
- const input = document.getElementById('vision-file-input') as HTMLInputElement | null
651
- if (input) {
652
- const dt = new DataTransfer()
653
- dt.items.add(file)
654
- input.files = dt.files
655
- const event = { target: input } as ChangeEvent<HTMLInputElement>
656
- onSelectImage(event)
657
- }
658
- } else if (file.type.startsWith('video/')) {
659
- const input = document.getElementById('video-file-input') as HTMLInputElement | null
660
- if (input) {
661
- const dt = new DataTransfer()
662
- dt.items.add(file)
663
- input.files = dt.files
664
- const event = { target: input } as ChangeEvent<HTMLInputElement>
665
- onSelectVideo(event)
666
- }
667
- } else if (isSupportedDocument(file.name)) {
668
- const input = document.getElementById('document-file-input') as HTMLInputElement | null
669
- if (input) {
670
- const dt = new DataTransfer()
671
- dt.items.add(file)
672
- input.files = dt.files
673
- const event = { target: input } as ChangeEvent<HTMLInputElement>
674
- onSelectDocument(event)
675
- }
676
- }
677
- }
678
- }
679
-
680
-
681
- // The mic button always drives native push-to-talk transcription; Gemini Live is a
682
- // separate "Live" button + overlay (see below), not a swap-in replacement for the
683
- // same button.
684
- const {
685
- isRecording,
686
- voiceMode,
687
- setVoiceMode,
688
- voiceTranscript,
689
- setVoiceTranscript,
690
- voiceAwaitingResponse,
691
- voiceAwaitingResponseRef,
692
- voiceModeRef,
693
- startRecognition,
694
- stopRecognition,
695
- scheduleVoiceListen,
696
- clearRestartTimer
697
- } = useVoiceInput({
698
- onSendVoiceMessage,
699
- language: settingsConfig?.language,
700
- message,
701
- sending,
702
- isSpeaking: Boolean(speakingText),
703
- onSetMessage,
704
- })
705
-
706
- const geminiLiveEnabled = settingsConfig?.voiceMode === 'geminiLive'
707
- const geminiLive = useGeminiLiveVoice({
708
- workspacePath,
709
- chatId,
710
- startSession: startGeminiLiveSession,
711
- sendAudioChunk: sendGeminiLiveAudioChunk,
712
- stopSession: stopGeminiLiveSession
713
- })
714
-
715
- const canSubmit = Boolean(message.trim() || imageAttachments.length > 0 || videoAttachments.length > 0 || documentName)
716
- const sendingImageMarkers = Array.from({ length: sendingImageCount }, (_, index) => `[Image #${index + 1}]`).join(' ')
717
- const sendingVideoMarkers = Array.from({ length: sendingVideoCount || 0 }, (_, index) => `[Video #${index + 1}]`).join(' ')
718
- const voiceStatus = speakingText ? 'speaking' : (sending || voiceAwaitingResponse) ? 'thinking' : isRecording ? 'listening' : voiceMode ? 'ready' : 'off'
719
- const voiceStatusLabel = voiceStatus === 'speaking' ? 'Speaking' : voiceStatus === 'thinking' ? 'Thinking' : voiceStatus === 'listening' ? 'Listening' : 'Ready'
720
-
721
- const getAvailableModels = (provider: string) => {
722
- switch (provider) {
723
- case 'gemini':
724
- return GEMINI_MODELS
725
- case 'openai':
726
- return OPENAI_MODELS
727
- case 'openrouter':
728
- return OPENROUTER_MODELS
729
- case 'deepseek':
730
- return DEEPSEEK_MODELS
731
- case 'anthropic':
732
- return ANTHROPIC_MODELS
733
- case 'huggingface':
734
- return HF_MODELS
735
- case 'local_openai':
736
- return LOCAL_MODELS
737
- case 'ollama':
738
- return dynamicOllamaModels
739
- default:
740
- if (provider.startsWith('custom:')) {
741
- const id = provider.replace(/^custom:/, '')
742
- const cp = (settingsConfig?.customProviders ?? []).find(p => p.id === id)
743
- return cp?.models.map(m => m.modelId) ?? []
744
- }
745
- return []
746
- }
747
- }
748
-
749
- const activeProvider = status?.activeProvider ?? ''
750
- const availableModels = getAvailableModels(activeProvider)
751
-
752
- const getActiveModel = (provider: string) => {
753
- if (!settingsConfig) return ''
754
- switch (provider) {
755
- case 'gemini':
756
- return settingsConfig.geminiModel
757
- case 'openai':
758
- return settingsConfig.openaiModel
759
- case 'openrouter':
760
- return settingsConfig.openrouterModel
761
- case 'deepseek':
762
- return settingsConfig.deepseekModel
763
- case 'anthropic':
764
- return settingsConfig.anthropicModel
765
- case 'huggingface':
766
- return settingsConfig.hfModel
767
- case 'local_openai':
768
- return settingsConfig.localModelName
769
- case 'ollama':
770
- return settingsConfig.ollamaModel
771
- default:
772
- if (provider.startsWith('custom:')) {
773
- const id = provider.replace(/^custom:/, '')
774
- const cp = (settingsConfig?.customProviders ?? []).find(p => p.id === id)
775
- return (settingsConfig.customModelSelections ?? {})[id] ?? cp?.models[0]?.modelId ?? ''
776
- }
777
- return ''
778
- }
779
- }
780
- const activeModel = getActiveModel(activeProvider)
781
- const textareaRef = useRef<HTMLTextAreaElement>(null)
782
- const slashRef = useRef<SlashSuggestionsHandle>(null)
783
-
784
- // Up/Down input history: recall previously submitted prompts (parity with the CLI).
785
- // `historyIndex` counts back from the newest entry — 0 is the most recent, null
786
- // means "editing the live draft". The draft is stashed the moment browsing starts.
787
- const promptHistory = useMemo(() => {
788
- const out: string[] = []
789
- for (const it of interactions) {
790
- if (it?.provider === 'system') continue
791
- const text = typeof it?.userText === 'string' ? it.userText.trim() : ''
792
- if (!text) continue
793
- if (out[out.length - 1] === text) continue
794
- out.push(text)
795
- }
796
- return out
797
- }, [interactions])
798
- const [historyIndex, setHistoryIndex] = useState<number | null>(null)
799
- const historyDraftRef = useRef('')
800
-
801
- const applyHistoryValue = (value: string) => {
802
- onSetMessage(value)
803
- setTimeout(() => {
804
- const el = textareaRef.current
805
- if (!el) return
806
- resizeInput(el)
807
- const end = el.value.length
808
- el.setSelectionRange(end, end)
809
- el.focus()
810
- }, 0)
811
- }
812
- const navigateHistoryPrev = () => {
813
- if (promptHistory.length === 0) return false
814
- if (historyIndex === null) {
815
- historyDraftRef.current = message
816
- setHistoryIndex(0)
817
- applyHistoryValue(promptHistory[promptHistory.length - 1])
818
- return true
819
- }
820
- if (historyIndex + 1 < promptHistory.length) {
821
- const next = historyIndex + 1
822
- setHistoryIndex(next)
823
- applyHistoryValue(promptHistory[promptHistory.length - 1 - next])
824
- }
825
- return true
826
- }
827
- // "Edit" on a past user message: drop its text back into the composer so it
828
- // can be tweaked and sent again as a new message (nothing is deleted).
829
- const handleEditMessage = (text: string) => {
830
- setHistoryIndex(null)
831
- applyHistoryValue(text)
832
- }
833
- const navigateHistoryNext = () => {
834
- if (historyIndex === null) return false
835
- if (historyIndex === 0) {
836
- setHistoryIndex(null)
837
- applyHistoryValue(historyDraftRef.current)
838
- return true
839
- }
840
- const next = historyIndex - 1
841
- setHistoryIndex(next)
842
- applyHistoryValue(promptHistory[promptHistory.length - 1 - next])
843
- return true
844
- }
845
-
846
- // Fill a menu pick into the composer (never submits — a separate Enter runs
847
- // it). The <SlashSuggestions> child owns all the menu state; this only writes
848
- // the completed text back and refocuses.
849
- const completeSuggestion = (text: string) => {
850
- onSetMessage(text)
851
- setTimeout(() => {
852
- const el = textareaRef.current
853
- if (!el) return
854
- resizeInput(el)
855
- el.focus()
856
- }, 0)
857
- }
858
-
859
- const submitOnEnter = (event: KeyboardEvent<HTMLTextAreaElement>) => {
860
- if (event.key !== 'Enter' || event.shiftKey || event.nativeEvent.isComposing) return
861
- event.preventDefault()
862
- event.currentTarget.form?.requestSubmit()
863
- }
864
- const handleInputKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {
865
- const isPlainArrowNav =
866
- (event.key === 'ArrowUp' || event.key === 'ArrowDown') &&
867
- !event.shiftKey && !event.altKey && !event.metaKey && !event.ctrlKey &&
868
- !event.nativeEvent.isComposing
869
-
870
- // History browsing wins over the suggestion menu: once it's started, ↑/↓
871
- // keep stepping through history even if a recalled slash/@ entry would
872
- // otherwise open the menu (the menu is `suppressed` while browsing anyway).
873
- if (
874
- isPlainArrowNav && historyIndex !== null &&
875
- event.currentTarget.selectionStart === event.currentTarget.selectionEnd
876
- ) {
877
- if (event.key === 'ArrowUp' ? navigateHistoryPrev() : navigateHistoryNext()) {
878
- event.preventDefault()
879
- return
880
- }
881
- }
882
-
883
- // Let the suggestion menu claim ↑/↓/Enter/Tab/Escape when it's showing.
884
- if (slashRef.current?.handleKeyDown(event)) return
885
-
886
- if (isPlainArrowNav) {
887
- const el = event.currentTarget
888
- if (el.selectionStart === el.selectionEnd) {
889
- if (event.key === 'ArrowUp') {
890
- const onFirstLine = !el.value.slice(0, el.selectionStart).includes('\n')
891
- if (onFirstLine && navigateHistoryPrev()) {
892
- event.preventDefault()
893
- return
894
- }
895
- } else {
896
- const onLastLine = !el.value.slice(el.selectionEnd).includes('\n')
897
- if (onLastLine && navigateHistoryNext()) {
898
- event.preventDefault()
899
- return
900
- }
901
- }
902
- }
903
- }
904
-
905
- submitOnEnter(event)
906
- }
907
-
908
- const cancelSpeech = () => {
909
- speechRunRef.current += 1
910
- audioRef.current?.pause()
911
- audioRef.current = null
912
- speakingRef.current = null
913
- if (typeof window !== 'undefined' && window.speechSynthesis) {
914
- window.speechSynthesis.cancel()
915
- }
916
- setSpeakingText(null)
917
- }
918
- const speakNative = (text: string, displayText: string) => {
919
- if (typeof window === 'undefined' || !window.speechSynthesis) {
920
- setSpeakingText(null)
921
- return
922
- }
923
- const hasThai = /[\u0e00-\u0e7f]/.test(text)
924
- const utterance = new SpeechSynthesisUtterance(text)
925
- utterance.lang = hasThai ? 'th-TH' : 'en-US'
926
- utterance.volume = Math.max(0, Math.min(1, numericSetting(settingsConfig?.ttsVolume, 1)))
927
- utterance.rate = Math.max(0.1, Math.min(10, numericSetting(settingsConfig?.ttsSpeed, 1)))
928
- utterance.pitch = Math.max(0, Math.min(2, numericSetting(settingsConfig?.ttsPitch, 1)))
929
- const voice = window.speechSynthesis.getVoices().find((item) => {
930
- const lang = item.lang.toLowerCase()
931
- const target = hasThai ? 'th' : 'en'
932
- return lang.startsWith(target) || lang.includes(target)
933
- })
934
- if (voice) utterance.voice = voice
935
- const finishSpeech = () => {
936
- setSpeakingText((current) => (current === displayText ? null : current))
937
- speakingRef.current = null
938
- scheduleVoiceListen(900)
939
- }
940
- utterance.onend = finishSpeech
941
- utterance.onerror = finishSpeech
942
- speakingRef.current = displayText
943
- setSpeakingText(displayText)
944
- window.speechSynthesis.speak(utterance)
945
- }
946
- const playGoogleTts = async (text: string, displayText: string, runId: number) => {
947
- const chunks = await getTtsUrls(text)
948
- for (const chunk of chunks) {
949
- if (speechRunRef.current !== runId) return
950
- await new Promise<void>((resolve, reject) => {
951
- const audio = new Audio(chunk.url)
952
- audio.volume = Math.max(0, Math.min(1, numericSetting(settingsConfig?.ttsVolume, 1)))
953
- audio.playbackRate = Math.max(0.25, Math.min(4, numericSetting(settingsConfig?.ttsSpeed, 1)))
954
- audioRef.current = audio
955
- audio.onended = () => resolve()
956
- audio.onpause = () => resolve()
957
- audio.onerror = () => reject(new Error('Google TTS playback failed'))
958
- audio.play().catch(reject)
959
- })
960
- }
961
- if (speechRunRef.current === runId) {
962
- setSpeakingText((current) => (current === displayText ? null : current))
963
- speakingRef.current = null
964
- scheduleVoiceListen(900)
965
- }
966
- }
967
- const speak = (text: string) => {
968
- const speechText = cleanSpeechText(text)
969
- if (!speechText) return
970
- if (speakingText === text) {
971
- cancelSpeech()
972
- return
973
- }
974
-
975
- cancelSpeech()
976
- const runId = speechRunRef.current
977
- speakingRef.current = text
978
- setSpeakingText(text)
979
- if (settingsConfig?.ttsProvider === 'google') {
980
- playGoogleTts(speechText, text, runId).catch((error) => {
981
- console.warn('Google TTS failed, falling back to native speech synthesis:', error)
982
- if (speechRunRef.current === runId) speakNative(speechText, text)
983
- })
984
- return
985
- }
986
- speakNative(speechText, text)
987
- }
988
-
989
- const toggleRecording = () => {
990
- if (isRecording) {
991
- stopRecognition()
992
- cancelSpeech()
993
- return
994
- }
995
-
996
- cancelSpeech()
997
- startRecognition()
998
- }
999
- const resizeInput = (element: HTMLTextAreaElement) => {
1000
- element.style.height = 'auto'
1001
- element.style.height = `${Math.min(element.scrollHeight, 120)}px`
1002
- }
1003
- useEffect(() => {
1004
- return () => {
1005
- cancelSpeech()
1006
- }
1007
- }, [])
1008
- useEffect(() => {
1009
- if (sending) submittedDuringSessionRef.current = true
1010
- }, [sending])
1011
- useEffect(() => {
1012
- if (interactions.length === 0) return
1013
- const latest = interactions[interactions.length - 1]
1014
- if (!historyReadyRef.current) {
1015
- historyReadyRef.current = true
1016
- if (!submittedDuringSessionRef.current) {
1017
- lastAutoSpokenIdRef.current = latest?.id ?? null
1018
- return
1019
- }
1020
- }
1021
- if (sending) return
1022
- if (!latest?.aiText || latest.id === lastAutoSpokenIdRef.current) return
1023
-
1024
- // Gemini Live speaks its own replies over the realtime audio stream — skip the
1025
- // separate browser/Google TTS pass while a live voice session is active.
1026
- if (!settingsConfig?.enableVoiceReply || geminiLive.voiceMode) {
1027
- lastAutoSpokenIdRef.current = latest.id
1028
- if (voiceMode) {
1029
- scheduleVoiceListen(350)
1030
- }
1031
- return
1032
- }
1033
-
1034
- lastAutoSpokenIdRef.current = latest.id
1035
- speak(latest.aiText)
1036
- }, [interactions, sending, settingsConfig?.enableVoiceReply, geminiLive.voiceMode, voiceMode])
1037
- useEffect(() => {
1038
- if (!toolMenuOpen) return
1039
- const closeMenu = (event: MouseEvent) => {
1040
- if (toolMenuRef.current?.contains(event.target as Node)) return
1041
- setToolMenuOpen(false)
1042
- }
1043
- window.addEventListener('mousedown', closeMenu)
1044
- return () => window.removeEventListener('mousedown', closeMenu)
1045
- }, [toolMenuOpen])
1046
- useEffect(() => {
1047
- const handleWindowPaste = (event: globalThis.ClipboardEvent) => {
1048
- if (!event.clipboardData) return
1049
- if (onPasteImage(event.clipboardData)) {
1050
- event.preventDefault()
1051
- event.stopPropagation()
1052
- }
1053
- }
1054
- window.addEventListener('paste', handleWindowPaste, true)
1055
- return () => window.removeEventListener('paste', handleWindowPaste, true)
1056
- }, [onPasteImage])
1057
- useEffect(() => {
1058
- const handleEscapeKeyDown = (event: globalThis.KeyboardEvent) => {
1059
- if (event.key === 'Escape' && sending) {
1060
- onCancelMessage()
1061
- }
1062
- }
1063
- window.addEventListener('keydown', handleEscapeKeyDown, true)
1064
- return () => window.removeEventListener('keydown', handleEscapeKeyDown, true)
1065
- }, [sending, onCancelMessage])
1066
- useEffect(() => {
1067
- if (message) return
1068
- const input = document.getElementById('chat-input') as HTMLTextAreaElement | null
1069
- if (input) input.style.height = ''
1070
- }, [message])
1071
-
1072
- const openImagePicker = () => {
1073
- setToolMenuOpen(false)
1074
- document.getElementById('vision-file-input')?.click()
1075
- }
1076
- const openVideoPicker = () => {
1077
- setToolMenuOpen(false)
1078
- document.getElementById('video-file-input')?.click()
1079
- }
1080
- const openDocumentPicker = () => {
1081
- setToolMenuOpen(false)
1082
- document.getElementById('document-file-input')?.click()
1083
- }
1084
- const startWebSearch = () => {
1085
- setToolMenuOpen(false)
1086
- onStartWebSearch()
1087
- }
1088
- const startGenerateImagePrompt = () => {
1089
- setToolMenuOpen(false)
1090
- if (!agentMode) onSetAgentMode(true)
1091
- onSetMessage('Generate an image of ')
1092
- textareaRef.current?.focus()
1093
- }
1094
- const startGenerateVideoPrompt = () => {
1095
- setToolMenuOpen(false)
1096
- if (!agentMode) onSetAgentMode(true)
1097
- onSetMessage('Generate a video of ')
1098
- textareaRef.current?.focus()
1099
- }
1100
- const appendWorkspaceReference = (reference: string) => {
1101
- const trimmed = reference.trim()
1102
- if (!trimmed) return
1103
- onSetMessage(message.trim() ? `${message.trimEnd()} ${trimmed} ` : `${trimmed} `)
1104
- }
1105
- const handleWorkspaceDrop = (event: DragEvent<HTMLElement>) => {
1106
- const reference =
1107
- event.dataTransfer.getData('application/x-mint-workspace-path') ||
1108
- event.dataTransfer.getData('text/plain')
1109
- if (!reference.trim().startsWith('@')) return
1110
- event.preventDefault()
1111
- appendWorkspaceReference(reference)
1112
- }
1113
- const isEmptyChat = interactions.length === 0 && !sending && !pendingApproval
1114
- const renderCompletedActivity = useCallback((interaction: any) => {
1115
- const interactionId = String(interaction.id)
1116
- const progress = agentActivitySnapshots[interactionId] ?? interaction.agentActivity ?? []
1117
- const activityView = activitiesFrom(progress)
1118
- const isOpen = Boolean(openActivityIds[interactionId])
1119
- return (
1120
- <AgentActivityDrawer
1121
- activityView={activityView}
1122
- isOpen={isOpen}
1123
- onToggle={() => setOpenActivityIds((current) => ({ ...current, [interactionId]: !current[interactionId] }))}
1124
- isHistorical={true}
1125
- rawProgress={progress}
1126
- />
1127
- )
1128
- }, [agentActivitySnapshots, openActivityIds])
1129
-
1130
- const renderWebSearchSources = useCallback((interaction: any) => {
1131
- const interactionId = String(interaction.id)
1132
- const progress = agentActivitySnapshots[interactionId] ?? interaction.agentActivity ?? []
1133
- const sources = parseWebSearchSources(progress)
1134
- if (sources.length === 0) return null
1135
-
1136
- return <SourcesBlock sources={sources} />
1137
- }, [agentActivitySnapshots])
1138
-
1139
- const renderChangesList = (changes: ReturnType<typeof parseFileChangesFromProgress>, idPrefix: string) => {
1140
- const showAll = Boolean(showAllFileChanges[idPrefix])
1141
- const visibleChanges = showAll ? changes : changes.slice(0, 3)
1142
- return (
1143
- <div className="file-changes-list" style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
1144
- {visibleChanges.map((change) => {
1145
- const fileKey = `${idPrefix}-${change.path}`
1146
- const isDiffOpen = Boolean(openFileDiffs[fileKey])
1147
- const fileName = change.path.split('/').pop() || change.path
1148
- const dirPath = change.path.includes('/') ? change.path.substring(0, change.path.lastIndexOf('/')) : ''
1149
-
1150
- return (
1151
- <div key={change.path} className="file-changes-item">
1152
- <div
1153
- className="file-changes-item-header"
1154
- onClick={() => setOpenFileDiffs((current) => ({ ...current, [fileKey]: !current[fileKey] }))}
1155
- >
1156
- <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
1157
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ color: 'var(--text-muted)' }}>
1158
- <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z" />
1159
- <polyline points="14 2 14 8 20 8" />
1160
- </svg>
1161
- <span className={`file-changes-filename ${change.created ? 'is-new' : ''}`}>
1162
- {fileName}
1163
- {dirPath && <span className="file-changes-dirpath">{dirPath}</span>}
1164
- {change.created && (
1165
- <span className="file-changes-badge-new">
1166
- [NEW FILE]
1167
- </span>
1168
- )}
1169
- </span>
1170
- </div>
1171
- <div style={{ display: 'flex', alignItems: 'center', gap: '6px', fontSize: '0.76rem' }}>
1172
- {change.created ? (
1173
- <span className="file-changes-count-add">
1174
- +{change.additions} {change.additions === 1 ? 'line' : 'lines'}
1175
- </span>
1176
- ) : (
1177
- <>
1178
- {change.additions > 0 && <span className="file-changes-count-add">+{change.additions}</span>}
1179
- {change.deletions > 0 && <span className="file-changes-count-del">-{change.deletions}</span>}
1180
- </>
1181
- )}
1182
- <button
1183
- type="button"
1184
- className="file-changes-preview-btn"
1185
- onClick={(e) => {
1186
- e.stopPropagation()
1187
- const fallbackContent =
1188
- change.created && change.hunks.length > 0
1189
- ? change.hunks.map((h) => h.newText).filter(Boolean).join('\n')
1190
- : undefined
1191
- openArtifact({ path: change.path, content: fallbackContent })
1192
- }}
1193
- title="Open Live Preview Split View"
1194
- >
1195
- Preview
1196
- </button>
1197
- <span style={{ color: 'var(--text-muted)', transform: isDiffOpen ? 'rotate(90deg)' : 'none', display: 'inline-block', transition: 'transform 0.15s' }}>&gt;</span>
1198
- </div>
1199
- </div>
1200
-
1201
- {isDiffOpen && (
1202
- <div className="file-changes-diff-box">
1203
- {change.hunks.map((hunk, hIdx) => (
1204
- <div key={hIdx} className="file-changes-diff-hunk" style={{ marginBottom: hIdx < change.hunks.length - 1 ? '10px' : 0 }}>
1205
- {hunk.oldText && (
1206
- <div className="file-changes-diff-del">
1207
- {hunk.oldText.split('\n').map((line, lIdx) => (
1208
- <div key={lIdx} style={{ display: 'flex' }}>
1209
- <span className="file-changes-diff-sign-del">-</span>
1210
- <span>{line}</span>
1211
- </div>
1212
- ))}
1213
- </div>
1214
- )}
1215
- {hunk.newText && (
1216
- <div className="file-changes-diff-add">
1217
- {hunk.newText.split('\n').map((line, lIdx) => (
1218
- <div key={lIdx} style={{ display: 'flex' }}>
1219
- <span className="file-changes-diff-sign-add">+</span>
1220
- <span>{line}</span>
1221
- </div>
1222
- ))}
1223
- </div>
1224
- )}
1225
- </div>
1226
- ))}
1227
- </div>
1228
- )}
1229
- </div>
1230
- )
1231
- })}
1232
- {changes.length > 3 && (
1233
- <button
1234
- type="button"
1235
- className="file-changes-show-more"
1236
- aria-expanded={showAll}
1237
- onClick={() => setShowAllFileChanges((current) => ({ ...current, [idPrefix]: !current[idPrefix] }))}
1238
- >
1239
- {showAll ? 'Show fewer files' : `Show ${changes.length - 3} more ${changes.length - 3 === 1 ? 'file' : 'files'}`}
1240
- <span aria-hidden="true" className={showAll ? 'is-expanded' : ''}>⌄</span>
1241
- </button>
1242
- )}
1243
- </div>
1244
- )
1245
- }
1246
-
1247
- const renderFileChanges = useCallback((interaction: any) => {
1248
- const interactionId = String(interaction.id)
1249
- const progress = agentActivitySnapshots[interactionId] ?? interaction.agentActivity ?? []
1250
- const changes = parseFileChangesFromProgress(progress)
1251
- if (changes.length === 0) return null
1252
-
1253
- const totalAdditions = changes.reduce((sum, c) => sum + c.additions, 0)
1254
- const totalDeletions = changes.reduce((sum, c) => sum + c.deletions, 0)
1255
- const createdCount = changes.filter((c) => c.created).length
1256
- const modifiedCount = changes.length - createdCount
1257
- const isOpen = openReviewIds[interactionId] ?? true
1258
-
1259
- let summaryLabel = ''
1260
- if (createdCount > 0 && modifiedCount === 0) {
1261
- summaryLabel = `${createdCount} ${createdCount === 1 ? 'file created' : 'files created'}`
1262
- } else if (createdCount > 0 && modifiedCount > 0) {
1263
- summaryLabel = `${createdCount} created, ${modifiedCount} modified`
1264
- } else {
1265
- summaryLabel = `${changes.length} ${changes.length === 1 ? 'file changed' : 'files changed'}`
1266
- }
1267
-
1268
- const handleRewind = async (e: React.MouseEvent) => {
1269
- e.stopPropagation()
1270
- const chatId = interaction.chatId || 'cli'
1271
- try {
1272
- const checkpoints = await listGitCheckpoints(chatId)
1273
- if (!checkpoints || checkpoints.length === 0) {
1274
- setUndoToastState({
1275
- visible: true,
1276
- message: 'No git checkpoints recorded for this session.',
1277
- canUndo: false,
1278
- isUndoing: false,
1279
- type: 'error',
1280
- })
1281
- return
1282
- }
1283
-
1284
- // Targeted Step Checkpoint resolution:
1285
- // Match checkpoints against files touched in this specific interaction
1286
- const changePaths = new Set(changes.map((c) => c.path.trim().toLowerCase()))
1287
- const matching = checkpoints.filter((cp) => {
1288
- if (!cp.targetPath) return false
1289
- const cpPath = cp.targetPath.trim().toLowerCase()
1290
- return changePaths.has(cpPath) || Array.from(changePaths).some((p) => cpPath.endsWith(p) || p.endsWith(cpPath))
1291
- })
1292
-
1293
- let targetCp: GitCheckpoint | null = null
1294
- if (matching.length > 0) {
1295
- // Earliest matching checkpoint represents the state immediately before this turn's modifications
1296
- targetCp = matching.reduce((earliest, curr) => (curr.step < earliest.step ? curr : earliest), matching[0])
1297
- } else {
1298
- // If no direct target path, try matching by timestamp
1299
- const interactionTs = interaction.createdAt ? new Date(interaction.createdAt).getTime() / 1000 : 0
1300
- if (interactionTs > 0) {
1301
- const afterCreated = checkpoints.filter((cp) => cp.timestamp >= interactionTs - 10)
1302
- if (afterCreated.length > 0) {
1303
- targetCp = afterCreated[0]
1304
- }
1305
- }
1306
- if (!targetCp) {
1307
- targetCp = checkpoints[checkpoints.length - 1]
1308
- }
1309
- }
1310
-
1311
- setRewindModalState({
1312
- isOpen: true,
1313
- interaction,
1314
- changes,
1315
- checkpoints,
1316
- targetCheckpoint: targetCp,
1317
- isLoading: false,
1318
- })
1319
- } catch (err: any) {
1320
- setUndoToastState({
1321
- visible: true,
1322
- message: `Failed to load checkpoints: ${err.message || String(err)}`,
1323
- canUndo: false,
1324
- isUndoing: false,
1325
- type: 'error',
1326
- })
1327
- }
1328
- }
1329
-
1330
- return (
1331
- <div className="file-changes-summary-container">
1332
- <div className="agent-activity-card file-changes-card">
1333
- <div className="file-changes-summary-row">
1334
- <div className="file-changes-summary">
1335
- <span className="file-changes-summary-icon" aria-hidden="true">⊞</span>
1336
- <span className="file-changes-summary-copy">
1337
- <span className="file-changes-summary-label">{summaryLabel}</span>
1338
- <span className="file-changes-summary-counts">
1339
- {totalAdditions > 0 && <span className="file-changes-count-add">+{totalAdditions}</span>}
1340
- {totalDeletions > 0 && <span className="file-changes-count-del">-{totalDeletions}</span>}
1341
- </span>
1342
- </span>
1343
- </div>
1344
- <div className="file-changes-summary-actions">
1345
- <button
1346
- type="button"
1347
- className="file-changes-rewind-btn"
1348
- onClick={handleRewind}
1349
- title="Rewind workspace to before these file edits (Git Checkpoint)"
1350
- >
1351
- <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
1352
- <polyline points="1 4 7 4 7 10" />
1353
- <path d="M3 9a9 9 0 1 0 2.13-5.36L1 7" />
1354
- </svg>
1355
- Undo
1356
- </button>
1357
- <button
1358
- type="button"
1359
- className="file-changes-review-btn"
1360
- title="Review changed code"
1361
- onClick={() => {
1362
- openReview('All conversation changes', conversationChanges)
1363
- }}
1364
- >
1365
- Review
1366
- </button>
1367
- </div>
1368
- </div>
1369
- {isOpen && <div className="file-changes-list-container">{renderChangesList(changes, interactionId)}</div>}
1370
- </div>
1371
- </div>
1372
- )
1373
- }, [agentActivitySnapshots, conversationChanges, openReviewIds, openFileDiffs, showAllFileChanges, workspacePath])
1374
-
1375
- const renderActiveFileChanges = () => {
1376
- const changes = parseFileChangesFromProgress(agentProgress)
1377
- if (changes.length === 0) return null
1378
-
1379
- const totalAdditions = changes.reduce((sum, c) => sum + c.additions, 0)
1380
- const totalDeletions = changes.reduce((sum, c) => sum + c.deletions, 0)
1381
- const createdCount = changes.filter((c) => c.created).length
1382
- const modifiedCount = changes.length - createdCount
1383
- const isOpen = openReviewIds['active-run'] ?? true
1384
-
1385
- let summaryLabel = ''
1386
- if (createdCount > 0 && modifiedCount === 0) {
1387
- summaryLabel = `${createdCount} ${createdCount === 1 ? 'file created' : 'files created'}`
1388
- } else if (createdCount > 0 && modifiedCount > 0) {
1389
- summaryLabel = `${createdCount} created, ${modifiedCount} modified`
1390
- } else {
1391
- summaryLabel = `${changes.length} ${changes.length === 1 ? 'file changed' : 'files changed'}`
1392
- }
1393
-
1394
- return (
1395
- <div className="message ai-message agent-activity-message" style={{ marginTop: '4px', marginBottom: '8px' }}>
1396
- <div className="agent-activity-card file-changes-card">
1397
- <div className="file-changes-summary-row">
1398
- <div className="file-changes-summary">
1399
- <span className="file-changes-summary-icon" aria-hidden="true">✓</span>
1400
- <span className="file-changes-summary-copy">
1401
- <span className="file-changes-summary-label">{summaryLabel}</span>
1402
- <span className="file-changes-summary-counts">
1403
- {totalAdditions > 0 && <span className="file-changes-count-add">+{totalAdditions}</span>}
1404
- {totalDeletions > 0 && <span className="file-changes-count-del">-{totalDeletions}</span>}
1405
- </span>
1406
- </span>
1407
- </div>
1408
- <button
1409
- type="button"
1410
- className="file-changes-review-btn"
1411
- title="Review changed code"
1412
- onClick={() => {
1413
- openReview('All conversation changes', conversationChanges)
1414
- }}
1415
- >
1416
- Review
1417
- </button>
1418
- </div>
1419
-
1420
- {isOpen && (
1421
- <div className="file-changes-list-container" style={{ marginTop: '8px' }}>
1422
- {renderChangesList(changes, 'active')}
1423
- </div>
1424
- )}
1425
- </div>
1426
- </div>
1427
- )
1428
- }
1429
-
1430
- const sectionContent = (
1431
- <section
1432
- className={`conversation-panel ${isEmptyChat ? 'is-empty' : ''}`}
1433
- onDragEnter={handleDragEnter}
1434
- onDragOver={handleDragOver}
1435
- onDragLeave={handleDragLeave}
1436
- onDrop={handleDrop}
1437
- style={activeArtifact || reviewPage ? { flex: '0 0 calc(100% - var(--preview-width, 50%))', width: 'calc(100% - var(--preview-width, 50%))', minWidth: '320px', maxWidth: 'none', margin: 0, position: 'relative' } : undefined}
1438
- >
1439
- {isDragging && (
1440
- <div
1441
- className="drag-drop-overlay"
1442
- onDragEnter={handleDragEnter}
1443
- onDragLeave={handleDragLeave}
1444
- onDragOver={handleDragOver}
1445
- onDrop={handleDrop}
1446
- style={{
1447
- position: 'absolute',
1448
- top: 0,
1449
- left: 0,
1450
- right: 0,
1451
- bottom: 0,
1452
- background: 'rgba(15, 23, 42, 0.82)',
1453
- backdropFilter: 'blur(8px)',
1454
- border: '2px dashed var(--accent)',
1455
- borderRadius: '16px',
1456
- margin: '12px',
1457
- display: 'flex',
1458
- flexDirection: 'column',
1459
- alignItems: 'center',
1460
- justifyContent: 'center',
1461
- color: 'white',
1462
- zIndex: 1000,
1463
- pointerEvents: 'auto',
1464
- }}
1465
- >
1466
- <div style={{ marginBottom: '16px', color: 'var(--interactive-fg)' }}>
1467
- <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
1468
- <rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
1469
- <circle cx="8.5" cy="8.5" r="1.5" />
1470
- <polyline points="21 15 16 10 5 21" />
1471
- </svg>
1472
- </div>
1473
- <div style={{ fontSize: '1.25rem', fontWeight: 'bold', letterSpacing: '0.5px' }}>Drag files to attach data</div>
1474
- <div style={{ fontSize: '0.85rem', color: 'var(--text-muted)', marginTop: '8px' }}>Supports images (PNG, JPEG, WebP, GIF), videos (MP4, WebM, MOV, MKV), and PDF files</div>
1475
- </div>
1476
- )}
1477
- <div className="chat-header">
1478
- {onToggleMobileSidebar && (
1479
- <button
1480
- className="mobile-menu-btn"
1481
- type="button"
1482
- onClick={onToggleMobileSidebar}
1483
- aria-label="Open navigation menu"
1484
- >
1485
- <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
1486
- <line x1="3" y1="12" x2="21" y2="12" />
1487
- <line x1="3" y1="6" x2="21" y2="6" />
1488
- <line x1="3" y1="18" x2="21" y2="18" />
1489
- </svg>
1490
- </button>
1491
- )}
1492
- <div className="chat-header-title">
1493
- {isCliSession ? (
1494
- <div className="chat-header-breadcrumb">
1495
- {onBackToCode && (
1496
- <button
1497
- type="button"
1498
- className="chat-header-back-btn"
1499
- onClick={onBackToCode}
1500
- title="Back to Code sessions Hub"
1501
- >
1502
- <svg
1503
- width="12"
1504
- height="12"
1505
- viewBox="0 0 24 24"
1506
- fill="none"
1507
- stroke="currentColor"
1508
- strokeWidth="2.5"
1509
- strokeLinecap="round"
1510
- strokeLinejoin="round"
1511
- >
1512
- <path d="M15 18l-6-6 6-6" />
1513
- </svg>
1514
- <span>Code</span>
1515
- </button>
1516
- )}
1517
- <span className="chat-header-sep">/</span>
1518
- <span className="chat-header-session-tag" title={cliSessionId || 'CLI Session'}>
1519
- <span>
1520
- {cliSessionId ? (cliSessionId === 'cli' ? 'cli' : `cli::${cliSessionId.replace(/^cli::/, '').slice(0, 7)}`) : 'cli'}
1521
- </span>
1522
- </span>
1523
- </div>
1524
- ) : (
1525
- <div className="chat-header-brand-group">
1526
- <img src={runtimePlatform.appIconPath()} alt="Logo" className="chat-header-logo" />
1527
- <span
1528
- className="chat-header-title-text"
1529
- title={conversationTitle && conversationTitle.trim() && conversationTitle !== 'New chat' ? conversationTitle.trim() : 'Mint Agent'}
1530
- >
1531
- {conversationTitle && conversationTitle.trim() && conversationTitle !== 'New chat' ? conversationTitle.trim() : 'Mint Agent'}
1532
- </span>
1533
- {projectName && (
1534
- <span className="chat-header-project-pill" title={`Project: ${workspacePath}`}>
1535
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
1536
- <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path>
1537
- </svg>
1538
- <span>{projectName}</span>
1539
- </span>
1540
- )}
1541
- </div>
1542
- )}
1543
- </div>
1544
- <div className="chat-header-actions">
1545
- {activeArtifact && (
1546
- <button
1547
- type="button"
1548
- className="chat-header-pill-btn"
1549
- title="Close Live Preview"
1550
- onClick={() => setActiveArtifact(null)}
1551
- >
1552
- <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
1553
- <line x1="18" y1="6" x2="6" y2="18"></line>
1554
- <line x1="6" y1="6" x2="18" y2="18"></line>
1555
- </svg>
1556
- <span>Close preview</span>
1557
- </button>
1558
- )}
1559
- <button
1560
- type="button"
1561
- className="chat-header-action-btn"
1562
- title="Clear conversation"
1563
- onClick={onClearMessages}
1564
- >
1565
- <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
1566
- <polyline points="3 6 5 6 21 6"></polyline>
1567
- <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
1568
- <line x1="10" y1="11" x2="10" y2="17"></line>
1569
- <line x1="14" y1="11" x2="14" y2="17"></line>
1570
- </svg>
1571
- </button>
1572
- </div>
1573
- </div>
1574
- <div className="chat-container" ref={chatContainerRef} onScroll={handleChatScroll}>
1575
- {hasOlder && onLoadOlder && (
1576
- <button type="button" className="load-older-messages" disabled={loadingOlder}
1577
- style={{ display: 'block', margin: '0 auto 20px', padding: '7px 14px', borderRadius: 8,
1578
- border: '1px solid var(--border-color, #3a3a3a)', background: 'var(--surface, #252525)',
1579
- color: 'var(--text-secondary, #c0c0c0)', cursor: loadingOlder ? 'default' : 'pointer' }}
1580
- onClick={async () => {
1581
- const container = chatContainerRef.current
1582
- const previousHeight = container?.scrollHeight ?? 0
1583
- setLoadingOlder(true)
1584
- try {
1585
- await onLoadOlder()
1586
- window.requestAnimationFrame(() => {
1587
- if (container) container.scrollTop += container.scrollHeight - previousHeight
1588
- })
1589
- } finally { setLoadingOlder(false) }
1590
- }}>
1591
- {loadingOlder ? 'Loading earlier messages…' : 'Load earlier messages'}
1592
- </button>
1593
- )}
1594
- {visibleInteractions.map((interaction, index) => (
1595
- <Fragment key={interaction.id}>
1596
- {index > 0 && shouldShowSessionDivider(visibleInteractions[index - 1].createdAt, interaction.createdAt) && (
1597
- <div className="system-event-divider">
1598
- <div className="system-event-line" />
1599
- <div className="system-event-pill">
1600
- <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
1601
- <circle cx="12" cy="12" r="9" />
1602
- <polyline points="12 7 12 12 15.5 14" />
1603
- </svg>
1604
- <span>{formatSessionDividerLabel(interaction.createdAt)}</span>
1605
- </div>
1606
- <div className="system-event-line" />
1607
- </div>
1608
- )}
1609
- <ChatMessageItem
1610
- interaction={interaction}
1611
- copiedId={copiedId}
1612
- onEditMessage={handleEditMessage}
1613
- speakingText={speakingText}
1614
- agentActivitySnapshots={agentActivitySnapshots}
1615
- thinkingExpanded={thinkingExpanded}
1616
- openActivityIds={openActivityIds}
1617
- openReviewIds={openReviewIds}
1618
- openFileDiffs={openFileDiffs}
1619
- onThinkingExpandedChange={onThinkingExpandedChange}
1620
- handleCopyMessage={handleCopyMessage}
1621
- speak={speak}
1622
- renderCompletedActivity={renderCompletedActivity}
1623
- renderFileChanges={renderFileChanges}
1624
- renderWebSearchSources={renderWebSearchSources}
1625
- />
1626
- </Fragment>
1627
- ))}
1628
-
1629
- {sending && (
1630
- <div style={{ display: 'flex', flexDirection: 'column', gap: '8px', marginBottom: '16px' }}>
1631
- <div className="message user-message"><div className="bubble-wrapper"><div className="message-bubble">{renderFormattedMessage([sendingMessage, sendingImageMarkers, sendingVideoMarkers].filter(Boolean).join(' '))}</div></div></div>
1632
- {agentMode && (
1633
- <AgentActivityDrawer
1634
- activityView={agentActivities}
1635
- isOpen={openActivityIds['live'] ?? true}
1636
- onToggle={() => setOpenActivityIds((current) => ({ ...current, live: !(current['live'] ?? true) }))}
1637
- pendingApproval={!!pendingApproval}
1638
- rawProgress={agentProgress}
1639
- />
1640
- )}
1641
- {renderActiveFileChanges()}
1642
- <div className="message ai-message thinking-message">
1643
- <div className="bubble-wrapper">
1644
- {!agentMode && extendedThoughtsFrom(agentProgress).length > 0 && (
1645
- <ThinkingBlock
1646
- blockKey="live-extended"
1647
- thoughts={extendedThoughtsFrom(agentProgress)}
1648
- isLive={true}
1649
- variant="extended"
1650
- expanded={thinkingExpanded['live-extended'] ?? true}
1651
- onExpandedChange={onThinkingExpandedChange}
1652
- />
1653
- )}
1654
- {!agentMode && thoughtsFrom(agentProgress).length > 0 && extendedThoughtsFrom(agentProgress).length === 0 && (
1655
- <ThinkingBlock
1656
- blockKey="live"
1657
- thoughts={thoughtsFrom(agentProgress)}
1658
- isLive={true}
1659
- expanded={thinkingExpanded.live ?? true}
1660
- onExpandedChange={onThinkingExpandedChange}
1661
- />
1662
- )}
1663
- <div className="message-bubble">
1664
- <span>
1665
- {throttledStreamedReply ? (
1666
- renderFormattedMessage(throttledStreamedReply, liveWebSources)
1667
- ) : (
1668
- <div style={{ display: 'flex', alignItems: 'center', gap: '8px', color: 'var(--text-soft, #94a3b8)' }}>
1669
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" style={{ display: 'inline-block', flexShrink: 0 }}>
1670
- <circle cx="12" cy="12" r="10" stroke="rgba(255, 255, 255, 0.12)" />
1671
- <path d="M12 2a10 10 0 0 1 10 10" stroke="var(--accent)" strokeLinecap="round">
1672
- <animateTransform
1673
- attributeName="transform"
1674
- type="rotate"
1675
- from="0 12 12"
1676
- to="360 12 12"
1677
- dur="0.9s"
1678
- repeatCount="indefinite"
1679
- />
1680
- </path>
1681
- </svg>
1682
- <span>
1683
- {activeAgentName && activeModelName
1684
- ? `${activeAgentName} (${activeModelName}) is thinking... (${elapsedSeconds}s)`
1685
- : `Thinking for ${elapsedSeconds}s (Esc to cancel)`
1686
- }
1687
- </span>
1688
- </div>
1689
- )}
1690
- </span>
1691
- </div>
1692
- {streamedResponse && (
1693
- <div className="message-time" style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
1694
- <span className="provider-model-chip" data-provider={(streamedResponse.provider || '').toLowerCase()} title={`${streamedResponse.provider} • ${streamedResponse.model}`}>
1695
- <span className="provider-chip-dot" aria-hidden="true" />
1696
- <span className="provider-chip-name">{streamedResponse.provider}</span>
1697
- <span className="provider-chip-divider">/</span>
1698
- <span className="provider-chip-model">{streamedResponse.model}</span>
1699
- </span>
1700
- {activeFallbackNotice && <span className="provider-fallback-notice">{activeFallbackNotice}</span>}
1701
- {throttledStreamedReply && (
1702
- <div className="message-action-buttons" style={{ display: 'flex', alignItems: 'center', gap: '4px', marginLeft: 'auto' }}>
1703
- <button
1704
- type="button"
1705
- className={`msg-action-btn copy-btn ${copiedId === 'live' ? 'is-copied' : ''}`}
1706
- onClick={() => handleCopyMessage('live', throttledStreamedReply)}
1707
- title={copiedId === 'live' ? 'คัดลอกแล้ว (Copied!)' : 'คัดลอกข้อความ (Copy message)'}
1708
- >
1709
- {renderCopyIcon(copiedId === 'live')}
1710
- </button>
1711
- <button
1712
- type="button"
1713
- className={`msg-action-btn tts-btn ${speakingText === throttledStreamedReply ? 'is-speaking' : ''}`}
1714
- onClick={() => speak(throttledStreamedReply)}
1715
- title={speakingText === throttledStreamedReply ? 'Stop reading' : 'Read aloud'}
1716
- >
1717
- {renderSpeakerIcon(speakingText === throttledStreamedReply)}
1718
- </button>
1719
- </div>
1720
- )}
1721
- </div>
1722
- )}
1723
- </div>
1724
- </div>
1725
- </div>
1726
- )}
1727
-
1728
- {pendingApproval && (
1729
- <ApprovalCard
1730
- key={pendingApproval.id || JSON.stringify(pendingApproval)}
1731
- pendingApproval={pendingApproval}
1732
- onApproval={onApproval}
1733
- />
1734
- )}
1735
- {showScrollToBottom && (
1736
- <button
1737
- type="button"
1738
- className="scroll-to-bottom-btn"
1739
- onClick={scrollToBottom}
1740
- title="Scroll to latest message"
1741
- aria-label="Scroll to latest message"
1742
- >
1743
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
1744
- <path d="M12 5v14"></path>
1745
- <path d="M6 13l6 6 6-6"></path>
1746
- </svg>
1747
- </button>
1748
- )}
1749
- <div ref={chatEnd} />
1750
- </div>
1751
-
1752
- <div className={`input-area ${voiceMode ? 'voice-active' : ''}`}>
1753
- {composerChanges.length > 0 && (
1754
- <button
1755
- type="button"
1756
- className={`composer-change-status ${sending ? 'is-live' : ''}`}
1757
- onClick={() => openReview(composerChangeLabel, composerChanges)}
1758
- title="Review recent agent changes"
1759
- >
1760
- <span className="composer-change-status-copy">
1761
- {sending && <span className="composer-change-status-spinner" aria-hidden="true" />}
1762
- <span>{sending ? `Editing ${composerChanges.length} ${composerChanges.length === 1 ? 'file' : 'files'}…` : composerChangeLabel}</span>
1763
- </span>
1764
- <span className="composer-change-status-counts" aria-label={`${composerAdditions} additions and ${composerDeletions} deletions`}>
1765
- {composerAdditions > 0 && <strong>+{composerAdditions}</strong>}
1766
- {composerDeletions > 0 && <em>-{composerDeletions}</em>}
1767
- </span>
1768
- </button>
1769
- )}
1770
- {isEmptyChat && (
1771
- <div className="empty-chat-prompt-wrap">
1772
- <div className="empty-chat-prompt">Mint Agent is ready to work</div>
1773
- {projectName && (
1774
- <div className="empty-chat-project-badge">
1775
- <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
1776
- <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path>
1777
- </svg>
1778
- <span>Working in <strong>{projectName}</strong></span>
1779
- </div>
1780
- )}
1781
- </div>
1782
- )}
1783
- {onSelectWorkspace && (
1784
- <div className="project-context-row">
1785
- <WorkspaceSelector
1786
- currentPath={workspacePath || ''}
1787
- recentPaths={recentWorkspacePaths}
1788
- onSelectWorkspace={onSelectWorkspace}
1789
- />
1790
- {workspacePath && <GitBranchSelector workspacePath={workspacePath} disabled={sending} onBranchChanged={onWorkspaceChanged} />}
1791
- </div>
1792
- )}
1793
- <div className="smart-context-bar">
1794
- <label className={`smart-context-option ${agentMode ? 'is-active' : ''}`}>
1795
- <span className="toggle-switch">
1796
- <input type="checkbox" checked={agentMode} onChange={(event) => onSetAgentMode(event.target.checked)} />
1797
- <span className="slider round" />
1798
- </span>
1799
- <span className="smart-context-copy"><span className="smart-context-title">Agent mode</span></span>
1800
- </label>
1801
- {onSetPlanMode && (
1802
- <label className={`smart-context-option ${planMode ? 'is-active' : ''}`} title="Investigate read-only and require plan approval before editing files or running commands">
1803
- <span className="toggle-switch">
1804
- <input type="checkbox" checked={Boolean(planMode)} onChange={(event) => onSetPlanMode(event.target.checked)} />
1805
- <span className="slider round" />
1806
- </span>
1807
- <span className="smart-context-copy"><span className="smart-context-title">Plan mode</span></span>
1808
- </label>
1809
- )}
1810
- </div>
1811
- {voiceMode && (
1812
- <div className="voice-mode-bar" data-state={voiceStatus}>
1813
- <span className="voice-mode-dot" />
1814
- {voiceTranscript ? (
1815
- voiceTranscript === 'Listening to microphone...' ||
1816
- voiceTranscript === 'Listening...' ||
1817
- voiceTranscript === 'No speech detected' ||
1818
- voiceTranscript === 'Sent audio to AI' ? (
1819
- <span>{voiceTranscript}</span>
1820
- ) : (
1821
- <span style={{ display: 'flex', alignItems: 'center', gap: '4px', minWidth: 0, overflow: 'hidden' }}>
1822
- <span>{voiceStatusLabel}:</span>
1823
- <span className="voice-mode-transcript">"{voiceTranscript}"</span>
1824
- </span>
1825
- )
1826
- ) : (
1827
- <span>{voiceStatusLabel}</span>
1828
- )}
1829
- </div>
1830
- )}
1831
-
1832
- <SlashSuggestions
1833
- ref={slashRef}
1834
- message={message}
1835
- onComplete={completeSuggestion}
1836
- workspacePath={workspacePath}
1837
- mcpServers={settingsConfig?.mcpServers}
1838
- anchorRef={textareaRef}
1839
- suppressed={historyIndex !== null}
1840
- />
1841
-
1842
- <form
1843
- id="chat-form"
1844
- className={geminiLiveEnabled ? 'has-live-btn' : ''}
1845
- style={{ position: 'relative' }}
1846
- onSubmit={(event) => {
1847
- setHistoryIndex(null)
1848
- historyDraftRef.current = ''
1849
- onSubmit(event)
1850
- }}
1851
- onDragOver={(event) => {
1852
- if (event.dataTransfer.types.includes('application/x-mint-workspace-path')) {
1853
- event.preventDefault()
1854
- event.dataTransfer.dropEffect = 'copy'
1855
- }
1856
- }}
1857
- onDrop={handleWorkspaceDrop}
1858
- onPaste={(event: ClipboardEvent<HTMLElement>) => {
1859
- if (onPasteImage(event.clipboardData)) event.preventDefault()
1860
- }}
1861
- >
1862
- {(imageAttachments.length > 0 || videoAttachments.length > 0 || documentName) && (
1863
- <div className="mint-attachment">
1864
- {imageAttachments.map((attachment, idx) => (
1865
- <div className="mint-image-attachment" key={idx}>
1866
- <img className="mint-image-preview" src={attachment.objectUrl || attachment.previewDataUri || attachment.dataUri} alt={attachment.name || 'Image attachment'} />
1867
- <button className="mint-attachment-remove" type="button" onClick={() => onRemoveImage(idx)} aria-label="Remove image">
1868
- <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
1869
- <line x1="18" y1="6" x2="6" y2="18"></line>
1870
- <line x1="6" y1="6" x2="18" y2="18"></line>
1871
- </svg>
1872
- </button>
1873
- </div>
1874
- ))}
1875
- {videoAttachments.map((attachment, idx) => (
1876
- <div className="mint-image-attachment" key={idx}>
1877
- <video className="mint-image-preview" src={attachment.dataUri} muted playsInline preload="metadata" />
1878
- <div className="mint-video-play-indicator" style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', pointerEvents: 'none', background: 'rgba(0,0,0,0.6)', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', width: '22px', height: '22px', border: '1.5px solid white' }}>
1879
- <svg width="10" height="10" viewBox="0 0 24 24" fill="white" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
1880
- <polygon points="5 3 19 12 5 21 5 3"></polygon>
1881
- </svg>
1882
- </div>
1883
- <button className="mint-attachment-remove" type="button" onClick={() => onRemoveVideo(idx)} aria-label="Remove video">
1884
- <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
1885
- <line x1="18" y1="6" x2="6" y2="18"></line>
1886
- <line x1="6" y1="6" x2="18" y2="18"></line>
1887
- </svg>
1888
- </button>
1889
- </div>
1890
- ))}
1891
- {documentName && (
1892
- <div style={{ display: 'flex', alignItems: 'center', gap: '8px', minWidth: 0 }}>
1893
- <span aria-hidden="true" style={{ display: 'inline-flex', alignItems: 'center', color: 'var(--text-soft)' }}>
1894
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
1895
- <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
1896
- <polyline points="14 2 14 8 20 8"></polyline>
1897
- </svg>
1898
- </span>
1899
- <span style={{ fontSize: '0.76rem', color: 'var(--text-soft)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '220px' }}>{documentName}</span>
1900
- <button type="button" onClick={onRemoveDocument} style={{ background: 'transparent', border: 0, color: '#ef4444', cursor: 'pointer', display: 'inline-flex', alignItems: 'center' }}>
1901
- <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
1902
- <line x1="18" y1="6" x2="6" y2="18"></line>
1903
- <line x1="6" y1="6" x2="18" y2="18"></line>
1904
- </svg>
1905
- </button>
1906
- </div>
1907
- )}
1908
- </div>
1909
- )}
1910
- {historyIndex !== null && promptHistory.length > 0 && (
1911
- <div
1912
- className="chat-history-indicator"
1913
- aria-live="polite"
1914
- style={{
1915
- position: 'absolute',
1916
- top: '-9px',
1917
- right: '14px',
1918
- padding: '1px 8px',
1919
- fontSize: '0.68rem',
1920
- fontWeight: 600,
1921
- letterSpacing: '0.02em',
1922
- color: 'var(--text-soft, #94a3b8)',
1923
- background: 'var(--input-bg, #1e293b)',
1924
- border: '1px solid var(--border, rgba(148,163,184,0.25))',
1925
- borderRadius: '999px',
1926
- pointerEvents: 'none',
1927
- zIndex: 2,
1928
- }}
1929
- >
1930
- History {historyIndex + 1}/{promptHistory.length}
1931
- </div>
1932
- )}
1933
- <textarea
1934
- id="chat-input"
1935
- ref={textareaRef}
1936
- value={message}
1937
- onChange={(event) => {
1938
- resizeInput(event.currentTarget)
1939
- onSetMessage(event.target.value)
1940
- // A real keystroke drops out of history browsing (recalled text kept).
1941
- if (historyIndex !== null) setHistoryIndex(null)
1942
- }}
1943
- onKeyDown={handleInputKeyDown}
1944
- onDrop={handleWorkspaceDrop}
1945
- placeholder="Ask anything, @ to mention, / for actions"
1946
- rows={1}
1947
- />
1948
- <div className="chat-tool-menu-wrap" ref={toolMenuRef}>
1949
- <button id="chat-tool-btn" type="button" aria-haspopup="menu" aria-expanded={toolMenuOpen} onClick={() => setToolMenuOpen((open) => !open)} style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
1950
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
1951
- <line x1="12" y1="5" x2="12" y2="19"></line>
1952
- <line x1="5" y1="12" x2="19" y2="12"></line>
1953
- </svg>
1954
- </button>
1955
- {toolMenuOpen && (
1956
- <div className="chat-tool-menu" role="menu">
1957
- <button type="button" role="menuitem" onClick={openImagePicker}>
1958
- <span aria-hidden="true">
1959
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
1960
- <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
1961
- <circle cx="8.5" cy="8.5" r="1.5"></circle>
1962
- <polyline points="21 15 16 10 5 21"></polyline>
1963
- </svg>
1964
- </span>
1965
- <span>Add image</span>
1966
- </button>
1967
- <button type="button" role="menuitem" onClick={openVideoPicker}>
1968
- <span aria-hidden="true">
1969
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
1970
- <polygon points="23 7 16 12 23 17 23 7"></polygon>
1971
- <rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect>
1972
- </svg>
1973
- </span>
1974
- <span>Add video</span>
1975
- </button>
1976
- <button type="button" role="menuitem" onClick={openDocumentPicker}>
1977
- <span aria-hidden="true">
1978
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
1979
- <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
1980
- <polyline points="14 2 14 8 20 8"></polyline>
1981
- </svg>
1982
- </span>
1983
- <span>Add file</span>
1984
- </button>
1985
- <button type="button" role="menuitem" onClick={startWebSearch}>
1986
- <span aria-hidden="true">
1987
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
1988
- <circle cx="11" cy="11" r="8"></circle>
1989
- <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
1990
- </svg>
1991
- </span>
1992
- <span>Search web</span>
1993
- </button>
1994
- <button type="button" role="menuitem" onClick={startGenerateImagePrompt}>
1995
- <span aria-hidden="true">
1996
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
1997
- <path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"></path>
1998
- </svg>
1999
- </span>
2000
- <span>Generate image</span>
2001
- </button>
2002
- <button type="button" role="menuitem" onClick={startGenerateVideoPrompt}>
2003
- <span aria-hidden="true">
2004
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
2005
- <polygon points="23 7 16 12 23 17 23 7"></polygon>
2006
- <rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect>
2007
- </svg>
2008
- </span>
2009
- <span>Generate video</span>
2010
- </button>
2011
- </div>
2012
- )}
2013
- </div>
2014
- <input id="vision-file-input" type="file" accept="image/png,image/jpeg,image/webp,image/gif" onChange={onSelectImage} style={{ display: 'none' }} />
2015
- <input id="video-file-input" type="file" accept="video/mp4,video/webm,video/quicktime,video/x-matroska" onChange={onSelectVideo} style={{ display: 'none' }} />
2016
- <input id="document-file-input" type="file" accept={SUPPORTED_DOCUMENT_ACCEPT} onChange={onSelectDocument} style={{ display: 'none' }} />
2017
- <button id="screen-capture-btn" type="button" onClick={onCaptureScreen} aria-label="Capture screen">
2018
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
2019
- <rect x="2" y="3" width="20" height="15" rx="2" />
2020
- <path d="M8 22h8M12 18v4M7 10h10M12 5v10" />
2021
- </svg>
2022
- </button>
2023
- <ModelSelectorPopover
2024
- activeProvider={status?.activeProvider ?? ''}
2025
- activeModel={activeModel}
2026
- availableProviders={status?.availableProviders ?? []}
2027
- settingsConfig={settingsConfig}
2028
- dynamicOllamaModels={dynamicOllamaModels}
2029
- onSelect={(provider, model) => {
2030
- if (onSelectModelAndProvider) {
2031
- onSelectModelAndProvider(provider, model)
2032
- } else {
2033
- onSetProvider(provider)
2034
- onSetModel(model)
2035
- }
2036
- }}
2037
- onUpdateSettings={onUpdateSettings}
2038
- />
2039
- <button
2040
- id="mic-btn"
2041
- className={`${isRecording ? 'is-recording' : ''} ${voiceMode ? 'voice-mode-active' : ''}`}
2042
- type="button"
2043
- onClick={toggleRecording}
2044
- title={voiceMode ? 'Disable voice conversation' : 'Enable voice conversation'}
2045
- style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}
2046
- >
2047
- {isRecording ? (
2048
- <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
2049
- <rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect>
2050
- </svg>
2051
- ) : (
2052
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
2053
- <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"></path>
2054
- <path d="M19 10v2a7 7 0 0 1-14 0v-2"></path>
2055
- <line x1="12" y1="19" x2="12" y2="23"></line>
2056
- <line x1="8" y1="23" x2="16" y2="23"></line>
2057
- </svg>
2058
- )}
2059
- </button>
2060
- {geminiLiveEnabled && (
2061
- <button
2062
- id="gemini-live-btn"
2063
- type="button"
2064
- onClick={() => geminiLive.setVoiceMode(true)}
2065
- title="Start Gemini Live conversation"
2066
- style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}
2067
- >
2068
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
2069
- <circle cx="12" cy="12" r="2"></circle>
2070
- <path d="M8.5 8.5a5 5 0 0 0 0 7"></path>
2071
- <path d="M15.5 8.5a5 5 0 0 1 0 7"></path>
2072
- <path d="M5.5 5.5a9 9 0 0 0 0 13"></path>
2073
- <path d="M18.5 5.5a9 9 0 0 1 0 13"></path>
2074
- </svg>
2075
- </button>
2076
- )}
2077
- {sending ? (
2078
- <button
2079
- id="send-btn"
2080
- className="stop-btn"
2081
- type="button"
2082
- onClick={onCancelMessage}
2083
- title="Stop generating"
2084
- style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}
2085
- >
2086
- <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
2087
- <rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect>
2088
- </svg>
2089
- </button>
2090
- ) : (
2091
- <button id="send-btn" type="submit" disabled={!canSubmit} style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
2092
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
2093
- <line x1="22" y1="2" x2="11" y2="13"></line>
2094
- <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
2095
- </svg>
2096
- </button>
2097
- )}
2098
- </form>
2099
- </div>
2100
- <p className="input-disclaimer">
2101
- Mint Agent is an AI gateway. Responses via third-party APIs. Verify critical info.
2102
- </p>
2103
- {geminiLive.voiceMode && (
2104
- <GeminiLiveOverlay
2105
- status={
2106
- geminiLive.isPaused
2107
- ? 'paused'
2108
- : geminiLive.isSpeaking
2109
- ? 'speaking'
2110
- : geminiLive.voiceAwaitingResponse
2111
- ? 'thinking'
2112
- : 'listening'
2113
- }
2114
- userTranscript={geminiLive.userTranscript}
2115
- assistantTranscript={geminiLive.assistantTranscript}
2116
- isPaused={geminiLive.isPaused}
2117
- onTogglePause={geminiLive.togglePause}
2118
- onEndCall={() => geminiLive.setVoiceMode(false)}
2119
- voice={settingsConfig?.geminiLiveVoice || 'Puck'}
2120
- voices={GEMINI_LIVE_VOICES}
2121
- onChangeVoice={async (voiceName) => {
2122
- await onSetGeminiLiveVoice(voiceName)
2123
- geminiLive.restart()
2124
- }}
2125
- />
2126
- )}
2127
- {undoToastState.visible && (
2128
- <div className={`rewind-toast-banner ${undoToastState.type === 'error' ? 'is-error' : 'is-success'}`}>
2129
- <div className="rewind-toast-content">
2130
- {undoToastState.type === 'error' ? (
2131
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--status-error, #ef4444)" strokeWidth="2">
2132
- <circle cx="12" cy="12" r="10" />
2133
- <line x1="12" y1="8" x2="12" y2="12" />
2134
- <line x1="12" y1="16" x2="12.01" y2="16" />
2135
- </svg>
2136
- ) : (
2137
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" strokeWidth="2.2">
2138
- <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
2139
- <polyline points="22 4 12 14.01 9 11.01" />
2140
- </svg>
2141
- )}
2142
- <span className="rewind-toast-text">{undoToastState.message}</span>
2143
- </div>
2144
-
2145
- <div className="rewind-toast-actions">
2146
- {undoToastState.canUndo && (
2147
- <button
2148
- type="button"
2149
- className="rewind-toast-undo-btn"
2150
- onClick={handleUndoRewind}
2151
- disabled={undoToastState.isUndoing}
2152
- title="Restore workspace files back from safety rescue snapshot"
2153
- >
2154
- {undoToastState.isUndoing ? (
2155
- 'Undoing...'
2156
- ) : (
2157
- <>
2158
- <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
2159
- <polyline points="9 14 4 9 9 4" />
2160
- <path d="M20 20v-7a4 4 0 0 0-4-4H4" />
2161
- </svg>
2162
- Undo Rewind
2163
- </>
2164
- )}
2165
- </button>
2166
- )}
2167
- <button
2168
- type="button"
2169
- className="rewind-toast-close-btn"
2170
- onClick={() => setUndoToastState((prev) => ({ ...prev, visible: false }))}
2171
- aria-label="Close notification"
2172
- >
2173
- ✕
2174
- </button>
2175
- </div>
2176
- </div>
2177
- )}
2178
- </section>
2179
- )
2180
-
2181
- return (
2182
- <div
2183
- ref={wrapperRef}
2184
- className={`chat-panel-split-wrapper ${activeArtifact || reviewPage ? 'has-preview' : 'no-preview'}`}
2185
- style={{
2186
- '--preview-width': `${(splitRatio * 100).toFixed(2)}%`,
2187
- } as React.CSSProperties}
2188
- >
2189
- {sectionContent}
2190
- {(activeArtifact || reviewPage) && (
2191
- <>
2192
- <div
2193
- className="preview-split-resizer"
2194
- onMouseDown={handleResizerMouseDown}
2195
- onDoubleClick={handleResizerDoubleClick}
2196
- title="Drag to resize · Double-click to reset 50/50"
2197
- >
2198
- <div className="preview-split-resizer-grip" />
2199
- </div>
2200
- {reviewPage ? (
2201
- <CodeReviewPage
2202
- title={reviewPage.title}
2203
- changes={reviewPage.changes}
2204
- workspacePath={workspacePath}
2205
- onBack={() => setReviewPage(null)}
2206
- />
2207
- ) : activeArtifact ? (
2208
- <ArtifactPreviewPanel artifact={activeArtifact} onClose={() => setActiveArtifact(null)} workspacePath={workspacePath} />
2209
- ) : null}
2210
- </>
2211
- )}
2212
- {rewindModalState.isOpen && (
2213
- <RewindModal
2214
- isOpen={rewindModalState.isOpen}
2215
- onClose={() => setRewindModalState((prev) => ({ ...prev, isOpen: false }))}
2216
- onConfirm={handleConfirmRewind}
2217
- checkpoints={rewindModalState.checkpoints}
2218
- targetCheckpoint={rewindModalState.targetCheckpoint}
2219
- changes={rewindModalState.changes}
2220
- workspacePath={workspacePath}
2221
- isLoading={rewindModalState.isLoading}
2222
- />
2223
- )}
2224
- </div>
2225
- )
2226
- }