@pheem49/mint 1.16.0 → 1.18.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 (984) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +2 -2
  3. package/README.md +8 -2
  4. package/Release_Note.md +222 -72
  5. package/apps/mint-companion/src-tauri/src/compact.rs +69 -0
  6. package/apps/mint-companion/src-tauri/src/main.rs +218 -11
  7. package/apps/mint-companion/vite.config.ts +2 -1
  8. package/crates/mint-cli/src/agent/diff_render.rs +66 -44
  9. package/crates/mint-cli/src/agent/live_status.rs +64 -6
  10. package/crates/mint-cli/src/agent/markdown_render.rs +342 -5
  11. package/crates/mint-cli/src/agent.rs +202 -47
  12. package/crates/mint-cli/src/commands/agent.rs +223 -117
  13. package/crates/mint-cli/src/commands/browser.rs +114 -0
  14. package/crates/mint-cli/src/commands/config.rs +14 -1
  15. package/crates/mint-cli/src/commands/integrations.rs +3 -3
  16. package/crates/mint-cli/src/commands/knowledge.rs +16 -2
  17. package/crates/mint-cli/src/commands/mod.rs +15 -4
  18. package/crates/mint-cli/src/commands/tailnet_web.rs +3 -7
  19. package/crates/mint-cli/src/companion_navigation.rs +268 -0
  20. package/crates/mint-cli/src/interactive/chat_tui/dialog.rs +572 -0
  21. package/crates/mint-cli/src/interactive/chat_tui/mod.rs +1427 -0
  22. package/crates/mint-cli/src/interactive/chat_tui/render.rs +1524 -0
  23. package/crates/mint-cli/src/interactive/chat_tui/selection.rs +401 -0
  24. package/crates/mint-cli/src/interactive/chat_tui/state.rs +1833 -0
  25. package/crates/mint-cli/src/interactive/chat_tui/tests.rs +3414 -0
  26. package/crates/mint-cli/src/interactive/chat_tui/thought.rs +202 -0
  27. package/crates/mint-cli/src/interactive/chat_tui/transcript.rs +728 -0
  28. package/crates/mint-cli/src/interactive/chat_tui/watermark.rs +148 -0
  29. package/crates/mint-cli/src/interactive/command_ui.rs +92 -5
  30. package/crates/mint-cli/src/interactive/composer.rs +186 -0
  31. package/crates/mint-cli/src/interactive/format.rs +78 -3
  32. package/crates/mint-cli/src/interactive/input_box.rs +61 -1
  33. package/crates/mint-cli/src/interactive/resume_picker.rs +367 -25
  34. package/crates/mint-cli/src/interactive/slash_commands.rs +758 -373
  35. package/crates/mint-cli/src/interactive/tool_activity.rs +468 -56
  36. package/crates/mint-cli/src/interactive.rs +74 -5
  37. package/crates/mint-cli/src/main.rs +26 -2
  38. package/crates/mint-cli/src/markdown.rs +2 -37
  39. package/crates/mint-cli/src/onboard.rs +3897 -1321
  40. package/crates/mint-cli/src/setup.rs +1342 -624
  41. package/crates/mint-cli/src/terminal_theme.rs +159 -35
  42. package/crates/mint-cli/tests/library_intake.rs +1 -0
  43. package/crates/mint-cli/tests/mods_testing.rs +5 -1
  44. package/crates/mint-companion-protocol/src/lib.rs +113 -0
  45. package/crates/mint-core/src/agent/chat/anthropic.rs +318 -0
  46. package/crates/mint-core/src/agent/chat/custom.rs +149 -0
  47. package/crates/mint-core/src/agent/chat/gemini.rs +518 -0
  48. package/crates/mint-core/src/agent/chat/mod.rs +857 -0
  49. package/crates/mint-core/src/agent/chat/ollama.rs +321 -0
  50. package/crates/mint-core/src/agent/chat/openai.rs +582 -0
  51. package/crates/mint-core/src/agent/chat/streaming.rs +469 -0
  52. package/crates/mint-core/src/agent/chat/tests.rs +1479 -0
  53. package/crates/mint-core/src/agent/memory.rs +129 -8
  54. package/crates/mint-core/src/agent/safety.rs +56 -4
  55. package/crates/mint-core/src/agent/skill_inventory.rs +3 -1
  56. package/crates/mint-core/src/agent/skills.rs +1 -0
  57. package/crates/mint-core/src/agent/tasks.rs +2 -2
  58. package/crates/mint-core/src/api_server/routes/auth.rs +1 -4
  59. package/crates/mint-core/src/api_server/routes/chat.rs +53 -17
  60. package/crates/mint-core/src/api_server/routes/gemini_live.rs +14 -1
  61. package/crates/mint-core/src/api_server/routes/html_preview.rs +1 -8
  62. package/crates/mint-core/src/api_server/routes/misc.rs +116 -0
  63. package/crates/mint-core/src/api_server/routes/profile_oauth.rs +43 -26
  64. package/crates/mint-core/src/api_server/routes/sessions.rs +114 -76
  65. package/crates/mint-core/src/api_server/routes/skills_subagents.rs +11 -7
  66. package/crates/mint-core/src/api_server/routes/status_health.rs +18 -8
  67. package/crates/mint-core/src/api_server.rs +211 -65
  68. package/crates/mint-core/src/avatar_bridge.rs +5 -24
  69. package/crates/mint-core/src/browser/cdp.rs +92 -30
  70. package/crates/mint-core/src/browser/headless.rs +582 -0
  71. package/crates/mint-core/src/browser/input.rs +7 -62
  72. package/crates/mint-core/src/browser/interact.rs +0 -14
  73. package/crates/mint-core/src/browser/mod.rs +10 -30
  74. package/crates/mint-core/src/browser/navigate.rs +83 -44
  75. package/crates/mint-core/src/browser/runtime.rs +282 -0
  76. package/crates/mint-core/src/browser/session.rs +240 -400
  77. package/crates/mint-core/src/browser/workflow.rs +496 -0
  78. package/crates/mint-core/src/companion/navigation.rs +115 -0
  79. package/crates/mint-core/src/companion.rs +109 -1
  80. package/crates/mint-core/src/cron/store.rs +2 -2
  81. package/crates/mint-core/src/integrations/mcp.rs +6 -8
  82. package/crates/mint-core/src/integrations/mcp_result.rs +45 -0
  83. package/crates/mint-core/src/lib.rs +10 -11
  84. package/crates/mint-core/src/media/gemini_live.rs +636 -86
  85. package/crates/mint-core/src/media/image_proxy.rs +563 -0
  86. package/crates/mint-core/src/media/library/migration.rs +26 -0
  87. package/crates/mint-core/src/media/library/tests.rs +1 -1
  88. package/crates/mint-core/src/media/library/thumbnails.rs +8 -2
  89. package/crates/mint-core/src/media/pictures.rs +1 -1
  90. package/crates/mint-core/src/media/tool_images.rs +287 -0
  91. package/crates/mint-core/src/media/tts/playback.rs +609 -0
  92. package/crates/mint-core/src/media/tts.rs +1 -0
  93. package/crates/mint-core/src/media.rs +2 -0
  94. package/crates/mint-core/src/mods/manager.rs +101 -35
  95. package/crates/mint-core/src/orchestration/context_render.rs +878 -44
  96. package/crates/mint-core/src/orchestration/decision_parsing.rs +536 -39
  97. package/crates/mint-core/src/orchestration/diagnostics.rs +307 -0
  98. package/crates/mint-core/src/orchestration/full_skill_result_tests.rs +12 -0
  99. package/crates/mint-core/src/orchestration/image_regressions.rs +379 -0
  100. package/crates/mint-core/src/orchestration/loop_guard.rs +479 -0
  101. package/crates/mint-core/src/orchestration/mcp_outcome_tests.rs +864 -0
  102. package/crates/mint-core/src/orchestration/memory_skill.rs +88 -28
  103. package/crates/mint-core/src/orchestration/mod.rs +859 -4244
  104. package/crates/mint-core/src/orchestration/reliability.rs +581 -0
  105. package/crates/mint-core/src/orchestration/repeated_failures.rs +8 -0
  106. package/crates/mint-core/src/orchestration/review_regressions.rs +179 -28
  107. package/crates/mint-core/src/orchestration/tests.rs +1665 -0
  108. package/crates/mint-core/src/orchestration/tools/dispatch.rs +967 -0
  109. package/crates/mint-core/src/orchestration/tools/files.rs +32 -3
  110. package/crates/mint-core/src/orchestration/tools/mod.rs +5 -1
  111. package/crates/mint-core/src/orchestration/tools/planning.rs +102 -42
  112. package/crates/mint-core/src/orchestration/tools/plugins_mcp.rs +19 -4
  113. package/crates/mint-core/src/orchestration/tools/project.rs +1 -1
  114. package/crates/mint-core/src/orchestration/tools/shell.rs +157 -62
  115. package/crates/mint-core/src/orchestration/tools/web.rs +5 -1
  116. package/crates/mint-core/src/orchestration/trajectory_schema.rs +353 -0
  117. package/crates/mint-core/src/orchestration/truncation.rs +368 -0
  118. package/crates/mint-core/src/orchestration/types.rs +648 -0
  119. package/crates/mint-core/src/orchestration/verification.rs +36 -9
  120. package/crates/mint-core/src/orchestration/workspace_helpers.rs +76 -27
  121. package/crates/mint-core/src/prompts/agent.rs +69 -102
  122. package/crates/mint-core/src/prompts/tool_catalog.rs +104 -153
  123. package/crates/mint-core/src/search/files.rs +20 -0
  124. package/crates/mint-core/src/search/web_search.rs +44 -3
  125. package/crates/mint-core/src/slash/mod.rs +481 -55
  126. package/crates/mint-core/src/slash/models.rs +6 -1
  127. package/crates/mint-core/src/swarm/batch.rs +643 -0
  128. package/crates/mint-core/src/swarm/dossier.rs +152 -0
  129. package/crates/mint-core/src/swarm/labels.rs +100 -0
  130. package/crates/mint-core/src/swarm/mod.rs +158 -0
  131. package/crates/mint-core/src/swarm/permissions.rs +167 -0
  132. package/crates/mint-core/src/system/auth.rs +156 -7
  133. package/crates/mint-core/src/system/bg_shell.rs +78 -9
  134. package/crates/mint-core/src/system/config.rs +266 -35
  135. package/crates/mint-core/src/system/mint_web.rs +33 -12
  136. package/crates/mint-core/src/system/shell.rs +179 -10
  137. package/crates/mint-core/src/system/workspace.rs +204 -0
  138. package/crates/mint-core/tests/background_retention.rs +1 -1
  139. package/crates/mint-core/tests/memory_persistence.rs +27 -0
  140. package/crates/mint-mod-runtime/src/lib.rs +37 -23
  141. package/out/web/assets/ArtifactPreviewPanel-Cx6BVOOF.js +2 -0
  142. package/out/web/assets/ArtifactPreviewPanel-DAT_Y7i0.css +1 -0
  143. package/out/web/assets/CalculationCard-D2E74u5q.js +1 -0
  144. package/out/web/assets/CliSessionsView-ZbdtSjvK.css +1 -0
  145. package/out/web/assets/CliSessionsView-z5i6qGyb.js +1 -0
  146. package/out/web/assets/CodeReviewPage-BK2bOScd.css +1 -0
  147. package/out/web/assets/CodeReviewPage-Dp6zHuHu.js +2 -0
  148. package/out/web/assets/CommandPalette-DaKlzbKs.js +1 -0
  149. package/out/web/assets/ImageGenCard-BMCJHP9O.js +1 -0
  150. package/out/web/assets/ImageSearchCard-Cld_SOUg.js +1 -0
  151. package/out/web/assets/ImageStudioPanel-B5N7TjeB.js +1 -0
  152. package/out/web/assets/LibraryPage-CMB89rJ0.css +1 -0
  153. package/out/web/assets/LibraryPage-DbZEi4W_.js +2 -0
  154. package/out/web/assets/LinkedFoldersView-9B9DIPoZ.js +1 -0
  155. package/out/web/assets/McpRegistryPicker-DnXPlzI7.js +1 -0
  156. package/out/web/assets/McpServersView-CiNBNUl4.js +1 -0
  157. package/out/web/assets/MermaidCard-BBDNEXbY.js +2 -0
  158. package/out/web/assets/MintDashboard-C_UYPoNV.js +119 -0
  159. package/out/web/assets/MintDashboard-DTd8C_c8.css +1 -0
  160. package/out/web/assets/PlanCard-C2x5pajq.js +1 -0
  161. package/out/web/assets/PluginsView-DoTg9MVa.js +1 -0
  162. package/out/web/assets/RewindModal-B5oowrR0.js +1 -0
  163. package/out/web/assets/ScheduledTasksView-BD5DVPkZ.js +1 -0
  164. package/out/web/assets/SelectField-Cy6CghrU.js +1 -0
  165. package/out/web/assets/SelectField-lFEkkWCU.css +1 -0
  166. package/out/web/assets/SettingsWindow-BYl_oCFh.js +4 -0
  167. package/out/web/assets/SettingsWindow-Ckcgnjt8.css +1 -0
  168. package/out/web/assets/SkillsView-CIeWwq5z.js +2 -0
  169. package/out/web/assets/StockCard-DxUciKyp.js +1 -0
  170. package/out/web/assets/ToolSurfacePage-qBgs_VHq.css +1 -0
  171. package/out/web/assets/ToolSurfacePage-saVbVrkE.js +38 -0
  172. package/out/web/assets/UiFeatureCard-CdY0hGWy.js +1 -0
  173. package/out/web/assets/UiGridCard-BBK3I5hC.js +1 -0
  174. package/out/web/assets/UiMockupWidget-CpR9EDuc.js +1 -0
  175. package/out/web/assets/{VeoStudioPanel-DOj7_WR3.css → VeoStudioPanel-9kU9YLEN.css} +1 -1
  176. package/out/web/assets/VeoStudioPanel-Co6-wEWM.js +4 -0
  177. package/out/web/assets/WeatherCard-Buzi7Vfu.js +1 -0
  178. package/out/web/assets/WorkspacePanel-BfHKkulU.js +1 -0
  179. package/out/web/assets/WorkspacePdfPreview-Bi8Zzyir.js +2 -0
  180. package/out/web/assets/abnfDiagram-VCTEODGH-CMfR95N2.js +1 -0
  181. package/out/web/assets/architecture-7GRP2DOG-DhAM4HyO.js +1 -0
  182. package/out/web/assets/architectureDiagram-5GKGNRK7-Yk3TUJ_F.js +36 -0
  183. package/out/web/assets/{blockDiagram-NRAW4CY4-BrrqzHW5.js → blockDiagram-I7D4REHJ-X9fBgJik.js} +4 -4
  184. package/out/web/assets/c4Diagram-7LVT6UL2-Bk9x7N-8.js +38 -0
  185. package/out/web/assets/chunk-2Q5K7J3B-D2JLPQzF.js +1 -0
  186. package/out/web/assets/{chunk-TEH6E4GO-G4Cdlyrt.js → chunk-4HAMMTFA-Dx5fk1_0.js} +3 -3
  187. package/out/web/assets/{chunk-5VM5RSS4-BiPuE0io.js → chunk-5VM5RSS4-BZ2oBPYQ.js} +1 -1
  188. package/out/web/assets/chunk-75Z2AOVW-BszTQoM4.js +2 -0
  189. package/out/web/assets/chunk-DU6HZSFF-DK-As3rk.js +128 -0
  190. package/out/web/assets/chunk-F27PBJKO-LmncZZIS.js +1 -0
  191. package/out/web/assets/chunk-FOHPRMQF-JXEeeJ9r.js +161 -0
  192. package/out/web/assets/{chunk-GMAD6QVW-BuhEcgHq.js → chunk-GMAD6QVW-DSQ_sDRJ.js} +12 -12
  193. package/out/web/assets/chunk-GVQU2GXP-Cno3ulje.js +1 -0
  194. package/out/web/assets/chunk-IMKFNOWR-bIzfY9Jc.js +231 -0
  195. package/out/web/assets/chunk-JWPE2WC7-BeEMUFDw.js +1 -0
  196. package/out/web/assets/chunk-L3NEJ4N5-Dl6eOa0D.js +1 -0
  197. package/out/web/assets/chunk-OSK3NFVY-BaUGJ-cM.js +10 -0
  198. package/out/web/assets/chunk-P2QGCYS3-_066ndhH.js +1 -0
  199. package/out/web/assets/chunk-POPQ4Y6H-YT1AKhTv.js +1 -0
  200. package/out/web/assets/chunk-PWAF6VOD-CrEgDsxh.js +1 -0
  201. package/out/web/assets/chunk-SHT3W25Y-_KwaRLOl.js +168 -0
  202. package/out/web/assets/{chunk-SVP7TREG-Bu_yYb-V.js → chunk-SVP7TREG-BQ1qb32L.js} +1 -1
  203. package/out/web/assets/{chunk-LCL6LL3I-BhXV9zyH.js → chunk-TICWLB2K-BDyehCtg.js} +4 -4
  204. package/out/web/assets/chunk-XXDRQBXY-B9YlhOkE.js +1 -0
  205. package/out/web/assets/chunk-Y2CYZVJY-BxCWjNVG.js +1 -0
  206. package/out/web/assets/classDiagram-ZZMXUADV-BoSpzRCJ.js +1 -0
  207. package/out/web/assets/classDiagram-v2-VYDZK3BY-BoSpzRCJ.js +1 -0
  208. package/out/web/assets/config-2suVFPZg.css +1 -0
  209. package/out/web/assets/config-CkRCdkxn.js +2 -0
  210. package/out/web/assets/core-Mi3s5D4_.js +1 -0
  211. package/out/web/assets/cose-bilkent-JH36ORCC-qlna31ty.js +1 -0
  212. package/out/web/assets/cynefin-OW5HDTMX-Dl_31aaN.js +1 -0
  213. package/out/web/assets/cynefinDiagram-5FMLGOSQ-Bpir8jha.js +62 -0
  214. package/out/web/assets/cytoscape.esm-DWtAUG0r.js +321 -0
  215. package/out/web/assets/dagre-CnWJJM5f.js +1 -0
  216. package/out/web/assets/dagre-GXQ25YYZ-D9xu3nsq.js +4 -0
  217. package/out/web/assets/diagram-S7CK7UJ4-DeveoEeB.js +30 -0
  218. package/out/web/assets/diagram-UQ7AKVKN-p4xWRESV.js +41 -0
  219. package/out/web/assets/{diagram-VSXAHHWV-BJ86Ov2Y.js → diagram-VSXAHHWV-C7qz9NDF.js} +1 -1
  220. package/out/web/assets/diagram-VX7I27RA-DRiCa3Vd.js +24 -0
  221. package/out/web/assets/diagram-Z3DM3KII-Bt7zduQX.js +24 -0
  222. package/out/web/assets/dist-9ISJ7r-g.js +1 -0
  223. package/out/web/assets/dist-js-BV3mI7uw.js +1 -0
  224. package/out/web/assets/ebnfDiagram-PWID7BFC-DV9WpwdR.js +1 -0
  225. package/out/web/assets/{erDiagram-SSCWMZ5O-CzzmnZWT.js → erDiagram-RLTQ6QDP-BJ_nwG2m.js} +3 -3
  226. package/out/web/assets/event-D7D-EBy9.js +1 -0
  227. package/out/web/assets/eventmodeling-NTZA5JFV-BTBMrfVa.js +1 -0
  228. package/out/web/assets/flowDiagram-HODETNUW-Cdlq3cJL.js +1 -0
  229. package/out/web/assets/ganttDiagram-EL5Y4UJY-DQLdsXrC.js +292 -0
  230. package/out/web/assets/gitGraph-4MIJSDKK-CL1OEOQ_.js +1 -0
  231. package/out/web/assets/gitGraphDiagram-WWUBYQGX-8cSi42H7.js +106 -0
  232. package/out/web/assets/graphlib-Bh9gHply.js +1 -0
  233. package/out/web/assets/index-BocxnrDG.js +168 -0
  234. package/out/web/assets/index-yeh_r55a.css +1 -0
  235. package/out/web/assets/info-A6RAGUB7-C1zsX5sc.js +1 -0
  236. package/out/web/assets/infoDiagram-27XIBGKW-DsRYtIyI.js +2 -0
  237. package/out/web/assets/{ishikawaDiagram-5VMMS53U-s38yo28y.js → ishikawaDiagram-5VMMS53U-B2crwIXj.js} +4 -4
  238. package/out/web/assets/journeyDiagram-3NMN7TZE-BTH-GyVR.js +139 -0
  239. package/out/web/assets/jsx-runtime-DmUwWVTj.js +1 -0
  240. package/out/web/assets/{kanban-definition-3QL26DDD-DodjgySL.js → kanban-definition-UXKFOSKX-BeRfCnuA.js} +6 -6
  241. package/out/web/assets/katex-MCdQaEAD.js +257 -0
  242. package/out/web/assets/mermaid-parser.core-BJwgCBBc.js +7 -0
  243. package/out/web/assets/mermaid.core-CsDDodN8.js +44 -0
  244. package/out/web/assets/{mindmap-definition-FBJOCRG2-260WBDxZ.js → mindmap-definition-YA3MSWOX-B42efOQ1.js} +4 -4
  245. package/out/web/assets/modelManager-B3ZFMh8v.js +2 -0
  246. package/out/web/assets/oauthManager-Ch5Du2fF.js +1 -0
  247. package/out/web/assets/packet-AYTQ26CC-B6xUyE6r.js +1 -0
  248. package/out/web/assets/pdf-Bcahc2yC.js +56 -0
  249. package/out/web/assets/pdf.worker.min-Dkey6ZUl.mjs +27 -0
  250. package/out/web/assets/pegDiagram-XKGWAZYB-1LabVycL.js +1 -0
  251. package/out/web/assets/pie-WAS4IAKB-DJwo-nf_.js +1 -0
  252. package/out/web/assets/pieDiagram-E7YTZNPT-icmAqzy4.js +39 -0
  253. package/out/web/assets/{quadrantDiagram-AXDQQJYC-B8D3gU5d.js → quadrantDiagram-AXDQQJYC-CZ-wEx41.js} +4 -4
  254. package/out/web/assets/radar-RG4KPBEZ-D7d-Xriw.js +1 -0
  255. package/out/web/assets/railroad-74A4TZTK-viuhdYbs.js +1 -0
  256. package/out/web/assets/railroad-abnf-HS5TGJTU-DNXiCNea.js +1 -0
  257. package/out/web/assets/railroad-ebnf-LZEXJU2U-nkSu1-0C.js +1 -0
  258. package/out/web/assets/railroad-peg-WCYAUIDC-BhbMeQzz.js +1 -0
  259. package/out/web/assets/railroadDiagram-O6MQD6OU-CLpG1jUX.js +1 -0
  260. package/out/web/assets/{requirementDiagram-EFPCY7ZU-D2X-Ewik.js → requirementDiagram-BXWQKSXE-kRDrvWrc.js} +3 -3
  261. package/out/web/assets/rolldown-runtime-CNOWq4Kh.js +1 -0
  262. package/out/web/assets/rough.esm-CNnFptCR.js +1 -0
  263. package/out/web/assets/sankeyDiagram-P5KCCOFB-DxAwmq4x.js +40 -0
  264. package/out/web/assets/sequenceDiagram-WJ2MYXX4-aLgY-6Wz.js +162 -0
  265. package/out/web/assets/sizeCapture-INFHLROL-C8Q8DbV5.js +1 -0
  266. package/out/web/assets/src-CRDfRlHM.js +1 -0
  267. package/out/web/assets/stateDiagram-D77RDMKH-D5QWfOhp.js +1 -0
  268. package/out/web/assets/stateDiagram-v2-MP3YSRHH-D6OiZoAP.js +1 -0
  269. package/out/web/assets/studio-BBZQ_-QK.js +1 -0
  270. package/out/web/assets/swimlanes-42K2YHIH-CRs5UyA-.js +1 -0
  271. package/out/web/assets/swimlanesDiagram-VR7AAH4N-UgEXIsV0.js +8 -0
  272. package/out/web/assets/syntaxHighlight-C-wAGT84.js +3 -0
  273. package/out/web/assets/timeline-definition-24CTP7MA-Denqd9qb.js +120 -0
  274. package/out/web/assets/treeView-Q6P3EWNA-0my3Z3s4.js +1 -0
  275. package/out/web/assets/treemap-WGGIJYW6-B_EjkLYq.js +1 -0
  276. package/out/web/assets/vennDiagram-4TSXK5OY-DxVg3Ewr.js +34 -0
  277. package/out/web/assets/wardley-WFR3VGLG-CGD-Tj4p.js +1 -0
  278. package/out/web/assets/{wardleyDiagram-VM6X3IG4-DAdXELbQ.js → wardleyDiagram-VM6X3IG4-CmfS39Xm.js} +35 -35
  279. package/out/web/assets/window-CkUOP7t0.js +1 -0
  280. package/out/web/assets/workspaces-SYPrmbgC.js +1 -0
  281. package/out/web/assets/{xychartDiagram-S5SC5T6Z-IZIXf3Vo.js → xychartDiagram-S5SC5T6Z-BVM9bKis.js} +4 -4
  282. package/out/web/index-web.html +4 -4
  283. package/out/web/index.html +4 -4
  284. package/out/web/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
  285. package/out/web/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
  286. package/out/web/pdfjs/cmaps/78-H.bcmap +0 -0
  287. package/out/web/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
  288. package/out/web/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
  289. package/out/web/pdfjs/cmaps/78-V.bcmap +0 -0
  290. package/out/web/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
  291. package/out/web/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
  292. package/out/web/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
  293. package/out/web/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
  294. package/out/web/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
  295. package/out/web/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
  296. package/out/web/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
  297. package/out/web/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
  298. package/out/web/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
  299. package/out/web/pdfjs/cmaps/Add-H.bcmap +0 -0
  300. package/out/web/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
  301. package/out/web/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
  302. package/out/web/pdfjs/cmaps/Add-V.bcmap +0 -0
  303. package/out/web/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
  304. package/out/web/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
  305. package/out/web/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
  306. package/out/web/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
  307. package/out/web/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
  308. package/out/web/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
  309. package/out/web/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
  310. package/out/web/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  311. package/out/web/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
  312. package/out/web/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
  313. package/out/web/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
  314. package/out/web/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
  315. package/out/web/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
  316. package/out/web/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
  317. package/out/web/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  318. package/out/web/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
  319. package/out/web/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
  320. package/out/web/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
  321. package/out/web/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
  322. package/out/web/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
  323. package/out/web/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
  324. package/out/web/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
  325. package/out/web/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  326. package/out/web/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
  327. package/out/web/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
  328. package/out/web/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
  329. package/out/web/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  330. package/out/web/pdfjs/cmaps/B5-H.bcmap +0 -0
  331. package/out/web/pdfjs/cmaps/B5-V.bcmap +0 -0
  332. package/out/web/pdfjs/cmaps/B5pc-H.bcmap +0 -0
  333. package/out/web/pdfjs/cmaps/B5pc-V.bcmap +0 -0
  334. package/out/web/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
  335. package/out/web/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
  336. package/out/web/pdfjs/cmaps/CNS1-H.bcmap +0 -0
  337. package/out/web/pdfjs/cmaps/CNS1-V.bcmap +0 -0
  338. package/out/web/pdfjs/cmaps/CNS2-H.bcmap +0 -0
  339. package/out/web/pdfjs/cmaps/CNS2-V.bcmap +3 -0
  340. package/out/web/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
  341. package/out/web/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
  342. package/out/web/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
  343. package/out/web/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
  344. package/out/web/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
  345. package/out/web/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
  346. package/out/web/pdfjs/cmaps/EUC-H.bcmap +0 -0
  347. package/out/web/pdfjs/cmaps/EUC-V.bcmap +0 -0
  348. package/out/web/pdfjs/cmaps/Ext-H.bcmap +0 -0
  349. package/out/web/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
  350. package/out/web/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
  351. package/out/web/pdfjs/cmaps/Ext-V.bcmap +0 -0
  352. package/out/web/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
  353. package/out/web/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
  354. package/out/web/pdfjs/cmaps/GB-H.bcmap +4 -0
  355. package/out/web/pdfjs/cmaps/GB-V.bcmap +0 -0
  356. package/out/web/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
  357. package/out/web/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
  358. package/out/web/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
  359. package/out/web/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
  360. package/out/web/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
  361. package/out/web/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
  362. package/out/web/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
  363. package/out/web/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
  364. package/out/web/pdfjs/cmaps/GBT-H.bcmap +0 -0
  365. package/out/web/pdfjs/cmaps/GBT-V.bcmap +0 -0
  366. package/out/web/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
  367. package/out/web/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
  368. package/out/web/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
  369. package/out/web/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
  370. package/out/web/pdfjs/cmaps/H.bcmap +0 -0
  371. package/out/web/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
  372. package/out/web/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
  373. package/out/web/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
  374. package/out/web/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
  375. package/out/web/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
  376. package/out/web/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
  377. package/out/web/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
  378. package/out/web/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
  379. package/out/web/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
  380. package/out/web/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
  381. package/out/web/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
  382. package/out/web/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
  383. package/out/web/pdfjs/cmaps/Hankaku.bcmap +0 -0
  384. package/out/web/pdfjs/cmaps/Hiragana.bcmap +0 -0
  385. package/out/web/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
  386. package/out/web/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
  387. package/out/web/pdfjs/cmaps/KSC-H.bcmap +0 -0
  388. package/out/web/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
  389. package/out/web/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
  390. package/out/web/pdfjs/cmaps/KSC-V.bcmap +0 -0
  391. package/out/web/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
  392. package/out/web/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  393. package/out/web/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  394. package/out/web/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
  395. package/out/web/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
  396. package/out/web/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
  397. package/out/web/pdfjs/cmaps/Katakana.bcmap +0 -0
  398. package/out/web/pdfjs/cmaps/LICENSE +36 -0
  399. package/out/web/pdfjs/cmaps/NWP-H.bcmap +0 -0
  400. package/out/web/pdfjs/cmaps/NWP-V.bcmap +0 -0
  401. package/out/web/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
  402. package/out/web/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
  403. package/out/web/pdfjs/cmaps/Roman.bcmap +0 -0
  404. package/out/web/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  405. package/out/web/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  406. package/out/web/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  407. package/out/web/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  408. package/out/web/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  409. package/out/web/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  410. package/out/web/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  411. package/out/web/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  412. package/out/web/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
  413. package/out/web/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
  414. package/out/web/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
  415. package/out/web/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
  416. package/out/web/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
  417. package/out/web/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
  418. package/out/web/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
  419. package/out/web/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
  420. package/out/web/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  421. package/out/web/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  422. package/out/web/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  423. package/out/web/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  424. package/out/web/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  425. package/out/web/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  426. package/out/web/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  427. package/out/web/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  428. package/out/web/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  429. package/out/web/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  430. package/out/web/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  431. package/out/web/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  432. package/out/web/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  433. package/out/web/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  434. package/out/web/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  435. package/out/web/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  436. package/out/web/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  437. package/out/web/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  438. package/out/web/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  439. package/out/web/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  440. package/out/web/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  441. package/out/web/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  442. package/out/web/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  443. package/out/web/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
  444. package/out/web/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
  445. package/out/web/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
  446. package/out/web/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
  447. package/out/web/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
  448. package/out/web/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
  449. package/out/web/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
  450. package/out/web/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
  451. package/out/web/pdfjs/cmaps/V.bcmap +0 -0
  452. package/out/web/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
  453. package/out/web/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
  454. package/out/web/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
  455. package/out/web/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
  456. package/out/web/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
  457. package/out/web/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
  458. package/out/web/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
  459. package/out/web/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
  460. package/out/web/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
  461. package/out/web/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
  462. package/out/web/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
  463. package/out/web/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
  464. package/out/web/pdfjs/standard_fonts/LICENSE_LIBERATION +363 -0
  465. package/out/web/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
  466. package/out/web/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
  467. package/out/web/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
  468. package/out/web/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
  469. package/out/web/pdfjs/wasm/LICENSE_JBIG2 +196 -0
  470. package/out/web/pdfjs/wasm/LICENSE_OPENJPEG +39 -0
  471. package/out/web/pdfjs/wasm/LICENSE_PDFJS_JBIG2 +13 -0
  472. package/out/web/pdfjs/wasm/LICENSE_PDFJS_OPENJPEG +22 -0
  473. package/out/web/pdfjs/wasm/LICENSE_PDFJS_QCMS +19 -0
  474. package/out/web/pdfjs/wasm/LICENSE_QCMS +21 -0
  475. package/out/web/pdfjs/wasm/jbig2.wasm +0 -0
  476. package/out/web/pdfjs/wasm/jbig2_nowasm_fallback.js +15 -0
  477. package/out/web/pdfjs/wasm/openjpeg.wasm +0 -0
  478. package/out/web/pdfjs/wasm/openjpeg_nowasm_fallback.js +17 -0
  479. package/out/web/pdfjs/wasm/qcms_bg.wasm +0 -0
  480. package/out/web/pdfjs/wasm/quickjs-eval.js +16 -0
  481. package/out/web/pdfjs/wasm/quickjs-eval.wasm +0 -0
  482. package/package.json +6 -4
  483. package/shared-modules/CODEOWNERS +3 -3
  484. package/shared-modules/SDL2/SDL2_mixer.json +27 -0
  485. package/shared-modules/appstream/README.md +13 -0
  486. package/shared-modules/appstream/appstream-compose.json +111 -0
  487. package/shared-modules/dbus-glib/dbus-glib.json +2 -2
  488. package/shared-modules/libportal/libportal.json +23 -0
  489. package/shared-modules/linux-audio/fluidsynth2-static.json +2 -2
  490. package/shared-modules/linux-audio/fluidsynth2.json +2 -2
  491. package/shared-modules/linux-audio/libinstpatch.json +2 -2
  492. package/shared-modules/linux-audio/liblo-static.json +3 -0
  493. package/shared-modules/luajit/luajit.json +2 -2
  494. package/shared-modules/squashfs-tools/squashfs-tools.json +0 -3
  495. package/slash-commands.json +14 -3
  496. package/src/bin/index.js +4 -1
  497. package/src/bin/pdfjs-assets.mjs +31 -0
  498. package/src/renderer/node_modules/.vite/vitest/CACHEDIR.TAG +3 -0
  499. package/src/renderer/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  500. package/src/renderer/shared/agentProgress.ts +62 -5
  501. package/src/renderer/shared/components/Settings/AgentsTab.tsx +70 -6
  502. package/src/renderer/shared/components/Settings/AudioTab.tsx +1 -1
  503. package/src/renderer/shared/components/Settings/AutomationTab.tsx +32 -29
  504. package/src/renderer/shared/components/Settings/AvatarCropModal.tsx +32 -12
  505. package/src/renderer/shared/components/Settings/CustomProviderLogoPicker.test.tsx +65 -0
  506. package/src/renderer/shared/components/Settings/CustomProviderLogoPicker.tsx +456 -0
  507. package/src/renderer/shared/components/Settings/GeneralTab.tsx +65 -94
  508. package/src/renderer/shared/components/Settings/PluginsTab.tsx +101 -24
  509. package/src/renderer/shared/components/Settings/ProfileTab.tsx +1 -1
  510. package/src/renderer/shared/components/Settings/SettingsWindow.tsx +796 -0
  511. package/src/renderer/shared/components/Settings/ThemeTab.test.tsx +45 -0
  512. package/src/renderer/shared/components/Settings/ThemeTab.tsx +119 -4
  513. package/src/renderer/shared/components/activity/AgentActivityDrawer.tsx +270 -0
  514. package/src/renderer/shared/components/activity/AgentActivityTable.tsx +582 -0
  515. package/src/renderer/shared/components/activity/AskUserActivityCard.test.tsx +122 -0
  516. package/src/renderer/shared/components/activity/AskUserActivityCard.tsx +70 -0
  517. package/src/renderer/shared/components/{McpToolResults.tsx → activity/McpToolResults.tsx} +69 -13
  518. package/src/renderer/shared/components/{PlanChecklistWidget.tsx → activity/PlanChecklistWidget.tsx} +2 -2
  519. package/src/renderer/shared/components/activity/RunSummaryDashboard.test.tsx +127 -0
  520. package/src/renderer/shared/components/activity/RunSummaryDashboard.tsx +222 -0
  521. package/src/renderer/shared/components/activity/SubagentDagView.test.ts +123 -0
  522. package/src/renderer/shared/components/{SubagentDagView.tsx → activity/SubagentDagView.tsx} +104 -50
  523. package/src/renderer/shared/components/activity/SubagentStatusStrip.test.tsx +82 -0
  524. package/src/renderer/shared/components/activity/SubagentStatusStrip.tsx +107 -0
  525. package/src/renderer/shared/components/activity/ViewedImages.tsx +179 -0
  526. package/src/renderer/shared/components/{AuthGate.tsx → app/AuthGate.tsx} +3 -3
  527. package/src/renderer/shared/components/{ChunkErrorBoundary.tsx → app/ChunkErrorBoundary.tsx} +13 -13
  528. package/src/renderer/shared/components/{CommandPalette.tsx → app/CommandPalette.tsx} +30 -30
  529. package/src/renderer/shared/components/{DashboardSidebar.tsx → app/DashboardSidebar.tsx} +7 -5
  530. package/src/renderer/shared/components/{DesktopTitlebar.tsx → app/DesktopTitlebar.tsx} +9 -26
  531. package/src/renderer/shared/components/{ErrorNotice.tsx → app/ErrorNotice.tsx} +1 -1
  532. package/src/renderer/shared/components/{MintDashboard.tsx → app/MintDashboard.tsx} +217 -228
  533. package/src/renderer/shared/components/{ModalErrorBoundary.tsx → app/ModalErrorBoundary.tsx} +8 -8
  534. package/src/renderer/shared/components/{CalculationCard.tsx → cards/CalculationCard.tsx} +16 -30
  535. package/src/renderer/shared/components/{ImageGenCard.tsx → cards/ImageGenCard.tsx} +11 -24
  536. package/src/renderer/shared/components/{ImageSearchCard.tsx → cards/ImageSearchCard.tsx} +11 -24
  537. package/src/renderer/shared/components/{MermaidCard.tsx → cards/MermaidCard.tsx} +38 -22
  538. package/src/renderer/shared/components/cards/PlanCard.test.tsx +41 -0
  539. package/src/renderer/shared/components/cards/PlanCard.tsx +32 -0
  540. package/src/renderer/shared/components/cards/ReleaseNotesCard.test.tsx +29 -0
  541. package/src/renderer/shared/components/cards/ReleaseNotesCard.tsx +137 -0
  542. package/src/renderer/shared/components/{StockCard.tsx → cards/StockCard.tsx} +22 -38
  543. package/src/renderer/shared/components/cards/ToolResultCards.tsx +94 -0
  544. package/src/renderer/shared/components/{UiFeatureCard.tsx → cards/UiFeatureCard.tsx} +6 -6
  545. package/src/renderer/shared/components/{UiGridCard.tsx → cards/UiGridCard.tsx} +3 -3
  546. package/src/renderer/shared/components/{UiMockupWidget.tsx → cards/UiMockupWidget.tsx} +4 -4
  547. package/src/renderer/shared/components/{WeatherCard.tsx → cards/WeatherCard.tsx} +6 -6
  548. package/src/renderer/shared/components/{ApprovalCard.test.tsx → chat/ApprovalCard.test.tsx} +12 -1
  549. package/src/renderer/shared/components/{ApprovalCard.tsx → chat/ApprovalCard.tsx} +26 -7
  550. package/src/renderer/shared/components/chat/ArtifactCards.test.tsx +69 -0
  551. package/src/renderer/shared/components/chat/ArtifactCards.tsx +238 -0
  552. package/src/renderer/shared/components/chat/AttachedDocumentCard.test.tsx +39 -0
  553. package/src/renderer/shared/components/chat/AttachedDocumentCard.tsx +220 -0
  554. package/src/renderer/shared/components/chat/ChatCodeBlock.test.tsx +33 -0
  555. package/src/renderer/shared/components/{ChatCodeBlock.tsx → chat/ChatCodeBlock.tsx} +16 -2
  556. package/src/renderer/shared/components/chat/ChatMessageItem.test.tsx +183 -0
  557. package/src/renderer/shared/components/chat/ChatMessageItem.tsx +388 -0
  558. package/src/renderer/shared/components/chat/ChatModeSelector.css +227 -0
  559. package/src/renderer/shared/components/chat/ChatModeSelector.test.tsx +45 -0
  560. package/src/renderer/shared/components/chat/ChatModeSelector.tsx +198 -0
  561. package/src/renderer/shared/components/chat/ChatPanel.test.tsx +53 -0
  562. package/src/renderer/shared/components/{ChatPanel.tsx → chat/ChatPanel.tsx} +462 -537
  563. package/src/renderer/shared/components/{CollapsiblePasteInput.tsx → chat/CollapsiblePasteInput.tsx} +2 -2
  564. package/src/renderer/shared/components/{DiffReviewModal.tsx → chat/DiffReviewModal.tsx} +44 -44
  565. package/src/renderer/shared/components/chat/GeminiLiveOverlay.test.tsx +165 -0
  566. package/src/renderer/shared/components/{GeminiLiveOverlay.tsx → chat/GeminiLiveOverlay.tsx} +47 -5
  567. package/src/renderer/shared/components/{ModelSelectorPopover.tsx → chat/ModelSelectorPopover.tsx} +44 -16
  568. package/src/renderer/shared/components/chat/QueuedMessagesBanner.test.tsx +89 -0
  569. package/src/renderer/shared/components/chat/QueuedMessagesBanner.tsx +201 -0
  570. package/src/renderer/shared/components/{RewindModal.tsx → chat/RewindModal.tsx} +14 -14
  571. package/src/renderer/shared/components/{SlashChoicePanel.tsx → chat/SlashChoicePanel.tsx} +2 -2
  572. package/src/renderer/shared/components/{SlashSuggestions.test.tsx → chat/SlashSuggestions.test.tsx} +14 -0
  573. package/src/renderer/shared/components/{SlashSuggestions.tsx → chat/SlashSuggestions.tsx} +4 -4
  574. package/src/renderer/shared/components/chat/SourcesBlock.test.tsx +53 -0
  575. package/src/renderer/shared/components/{SourcesBlock.tsx → chat/SourcesBlock.tsx} +262 -123
  576. package/src/renderer/shared/components/{ThinkingBlock.tsx → chat/ThinkingBlock.tsx} +1 -1
  577. package/src/renderer/shared/components/chat/TurnFileChanges.test.tsx +130 -0
  578. package/src/renderer/shared/components/chat/TurnFileChanges.tsx +463 -0
  579. package/src/renderer/shared/components/{VoicePill.css → chat/VoicePill.css} +1 -1
  580. package/src/renderer/shared/components/{VoicePill.jsx → chat/VoicePill.jsx} +3 -2
  581. package/src/renderer/shared/components/{CliSessionsView.test.tsx → extensions/CliSessionsView.test.tsx} +2 -2
  582. package/src/renderer/shared/components/{CliSessionsView.tsx → extensions/CliSessionsView.tsx} +19 -16
  583. package/src/renderer/shared/components/{LinkedFoldersView.tsx → extensions/LinkedFoldersView.tsx} +3 -3
  584. package/src/renderer/shared/components/{McpRegistryPicker.tsx → extensions/McpRegistryPicker.tsx} +6 -6
  585. package/src/renderer/shared/components/{McpServersView.tsx → extensions/McpServersView.tsx} +105 -36
  586. package/src/renderer/shared/components/{McpToolAllowlist.tsx → extensions/McpToolAllowlist.tsx} +2 -2
  587. package/src/renderer/shared/components/{ModUi.test.tsx → extensions/ModUi.test.tsx} +9 -4
  588. package/src/renderer/shared/components/{ModsPanel.tsx → extensions/ModsPanel.tsx} +59 -7
  589. package/src/renderer/shared/components/{PluginsView.tsx → extensions/PluginsView.tsx} +6 -6
  590. package/src/renderer/shared/components/{ScheduledTasksView.tsx → extensions/ScheduledTasksView.tsx} +5 -5
  591. package/src/renderer/shared/components/extensions/SkillsView.test.tsx +31 -0
  592. package/src/renderer/shared/components/{SkillsView.tsx → extensions/SkillsView.tsx} +20 -4
  593. package/src/renderer/shared/components/{ImageStudioPanel.tsx → studio/ImageStudioPanel.tsx} +6 -6
  594. package/src/renderer/shared/components/{LibraryItemCard.tsx → studio/LibraryItemCard.tsx} +2 -2
  595. package/src/renderer/shared/components/{LibraryPage.tsx → studio/LibraryPage.tsx} +53 -10
  596. package/src/renderer/shared/components/{LibraryPreview.tsx → studio/LibraryPreview.tsx} +2 -2
  597. package/src/renderer/shared/components/{VeoStudioPanel.tsx → studio/VeoStudioPanel.tsx} +7 -7
  598. package/src/renderer/shared/components/{GlideSelect.css → ui/GlideSelect.css} +7 -6
  599. package/src/renderer/shared/components/ui/ProviderBrandIcon.test.tsx +67 -0
  600. package/src/renderer/shared/components/ui/ProviderBrandIcon.tsx +167 -0
  601. package/src/renderer/shared/components/ui/SelectField.tsx +506 -0
  602. package/src/renderer/shared/components/ui/error-recovery.css +10 -10
  603. package/src/renderer/shared/components/ui/error-recovery.tsx +1 -1
  604. package/src/renderer/shared/components/{ArtifactPreviewPanel.tsx → workspace/ArtifactPreviewPanel.tsx} +374 -118
  605. package/src/renderer/shared/components/workspace/BackgroundTerminals.test.tsx +63 -0
  606. package/src/renderer/shared/components/{BackgroundTerminals.tsx → workspace/BackgroundTerminals.tsx} +40 -9
  607. package/src/renderer/shared/components/workspace/CodeReviewPage.test.tsx +50 -0
  608. package/src/renderer/shared/components/{CodeReviewPage.tsx → workspace/CodeReviewPage.tsx} +124 -24
  609. package/src/renderer/shared/components/{GitBranchSelector.tsx → workspace/GitBranchSelector.tsx} +80 -24
  610. package/src/renderer/shared/components/workspace/SessionSources.tsx +113 -0
  611. package/src/renderer/shared/components/{TerminalDock.tsx → workspace/TerminalDock.tsx} +5 -2
  612. package/src/renderer/shared/components/workspace/ToolSurfacePage.test.tsx +81 -0
  613. package/src/renderer/shared/components/{ToolSurfacePage.tsx → workspace/ToolSurfacePage.tsx} +361 -90
  614. package/src/renderer/shared/components/{ToolsPanelToggle.tsx → workspace/ToolsPanelToggle.tsx} +1 -1
  615. package/src/renderer/shared/components/workspace/WorkspaceFileInfo.tsx +28 -0
  616. package/src/renderer/shared/components/workspace/WorkspaceMediaPreview.tsx +17 -0
  617. package/src/renderer/shared/components/{WorkspacePaneResizer.tsx → workspace/WorkspacePaneResizer.tsx} +1 -1
  618. package/src/renderer/shared/components/workspace/WorkspacePdfPreview.tsx +308 -0
  619. package/src/renderer/shared/components/workspace/WorkspacePreviewActions.test.tsx +101 -0
  620. package/src/renderer/shared/components/{WorkspacePreviewActions.tsx → workspace/WorkspacePreviewActions.tsx} +3 -3
  621. package/src/renderer/shared/components/{WorkspaceSelector.tsx → workspace/WorkspaceSelector.tsx} +99 -7
  622. package/src/renderer/shared/components/{background-terminals.css → workspace/background-terminals.css} +11 -11
  623. package/src/renderer/shared/components/{workspace-selector.css → workspace/workspace-selector.css} +84 -2
  624. package/src/renderer/shared/constants/config.ts +6 -3
  625. package/src/renderer/shared/constants/models.test.ts +51 -0
  626. package/src/renderer/shared/constants/models.ts +139 -32
  627. package/src/renderer/shared/conversation/coordinator.test.ts +46 -0
  628. package/src/renderer/shared/conversation/coordinator.ts +50 -2
  629. package/src/renderer/shared/conversation/lifecycle.test.ts +254 -4
  630. package/src/renderer/shared/conversation/lifecycle.ts +268 -12
  631. package/src/renderer/shared/conversation/progressiveStream.test.ts +83 -0
  632. package/src/renderer/shared/conversation/progressiveStream.ts +219 -0
  633. package/src/renderer/shared/conversation/projectNavigation.test.ts +18 -0
  634. package/src/renderer/shared/conversation/projectNavigation.ts +11 -0
  635. package/src/renderer/shared/conversation/transport.test.ts +20 -0
  636. package/src/renderer/shared/conversation/transport.ts +51 -9
  637. package/src/renderer/shared/conversation/useConversationCoordinator.ts +42 -12
  638. package/src/renderer/shared/css/agent-observability.css +205 -11
  639. package/src/renderer/shared/css/chat-ui-cards.css +48 -48
  640. package/src/renderer/shared/css/code-review.css +139 -110
  641. package/src/renderer/shared/css/code-sessions.css +56 -27
  642. package/src/renderer/shared/css/collapsed-paste.css +1 -1
  643. package/src/renderer/shared/css/error-notice.css +1 -1
  644. package/src/renderer/shared/css/file-changes.css +298 -51
  645. package/src/renderer/shared/css/library.css +105 -10
  646. package/src/renderer/shared/css/management-views.css +79 -38
  647. package/src/renderer/shared/css/mcp-results.css +48 -5
  648. package/src/renderer/shared/css/model-selector.css +18 -18
  649. package/src/renderer/shared/css/mods.css +7 -7
  650. package/src/renderer/shared/css/notification-notice.css +2 -2
  651. package/src/renderer/shared/css/queued-messages.css +221 -0
  652. package/src/renderer/shared/css/session-sources.css +12 -7
  653. package/src/renderer/shared/css/settings/agents.css +2 -2
  654. package/src/renderer/{src → shared}/css/settings/base.css +101 -107
  655. package/src/renderer/shared/css/settings/general.css +397 -100
  656. package/src/renderer/shared/css/settings/plugins.css +8 -8
  657. package/src/renderer/shared/css/settings/profile.css +5 -5
  658. package/src/renderer/shared/css/settings/theme.css +49 -37
  659. package/src/renderer/shared/css/slash-choice.css +2 -2
  660. package/src/renderer/shared/css/slash-suggestions.css +3 -3
  661. package/src/renderer/shared/css/sources-block.css +41 -2
  662. package/src/renderer/shared/css/tools-panel.css +2 -2
  663. package/src/renderer/shared/css/ui-tailwind.css +10 -2
  664. package/src/renderer/shared/css/veo-studio.css +2 -2
  665. package/src/renderer/shared/css/viewed-images.css +93 -4
  666. package/src/renderer/shared/css/workspace-file-info.css +12 -0
  667. package/src/renderer/shared/css/workspace-media-preview.css +13 -0
  668. package/src/renderer/shared/env.d.ts +9 -0
  669. package/src/renderer/shared/platform.test.ts +143 -0
  670. package/src/renderer/shared/platform.ts +12 -5
  671. package/src/renderer/shared/speech/player.ts +160 -0
  672. package/src/renderer/shared/speech/types.ts +11 -0
  673. package/src/renderer/shared/theme/themeManager.test.ts +37 -0
  674. package/src/renderer/shared/theme/themeManager.ts +24 -5
  675. package/src/renderer/shared/theme/themes.ts +71 -4
  676. package/src/renderer/shared/theme/tokens.css +452 -53
  677. package/src/renderer/shared/types.ts +57 -2
  678. package/src/renderer/shared/utils/agentActivity.test.ts +633 -8
  679. package/src/renderer/shared/utils/agentActivity.ts +776 -59
  680. package/src/renderer/shared/utils/artifactBrowser.test.ts +37 -32
  681. package/src/renderer/shared/utils/artifactBrowser.ts +18 -20
  682. package/src/renderer/shared/utils/artifacts.ts +55 -0
  683. package/src/renderer/shared/utils/attachedDocument.test.ts +73 -0
  684. package/src/renderer/shared/utils/attachedDocument.ts +49 -0
  685. package/src/renderer/shared/utils/contextCompaction.test.tsx +1 -1
  686. package/src/renderer/shared/utils/externalLinks.ts +91 -0
  687. package/src/renderer/shared/utils/fileIcons.test.ts +59 -0
  688. package/src/renderer/shared/utils/fileIcons.ts +149 -16
  689. package/src/renderer/shared/utils/lazyWithRetry.ts +13 -0
  690. package/src/renderer/shared/utils/markdown.tsx +238 -51
  691. package/src/renderer/shared/utils/markdown.webImages.test.tsx +140 -2
  692. package/src/renderer/shared/utils/mcpResults.test.tsx +53 -1
  693. package/src/renderer/shared/utils/pdfRange.ts +16 -0
  694. package/src/renderer/shared/utils/previewObjectUrls.ts +20 -0
  695. package/src/renderer/shared/utils/sessionSources.test.ts +41 -1
  696. package/src/renderer/shared/utils/sessionSources.ts +2 -0
  697. package/src/renderer/shared/utils/slashCommandProcessor.ts +7 -1
  698. package/src/renderer/shared/utils/speech.ts +18 -4
  699. package/src/renderer/shared/utils/syntaxHighlight.test.tsx +11 -1
  700. package/src/renderer/shared/utils/syntaxHighlight.tsx +51 -3
  701. package/src/renderer/shared/utils/useGeminiLiveVoice.ts +324 -51
  702. package/src/renderer/shared/utils/useNativeVoiceInput.ts +19 -5
  703. package/src/renderer/shared/utils/useSessionSources.ts +4 -0
  704. package/src/renderer/shared/utils/viewedImages.test.ts +129 -0
  705. package/src/renderer/shared/utils/viewedImages.ts +81 -10
  706. package/src/renderer/shared/utils/workspaceDrag.test.ts +153 -0
  707. package/src/renderer/shared/utils/workspaceDrag.ts +172 -0
  708. package/src/renderer/shared/utils/workspaceImage.ts +17 -0
  709. package/src/renderer/shared/utils/workspaceListPreferences.test.ts +64 -0
  710. package/src/renderer/shared/utils/workspaceListPreferences.ts +54 -0
  711. package/src/renderer/shared/utils/workspaceMedia.ts +49 -0
  712. package/src/renderer/shared/utils/workspacePreview.test.ts +147 -2
  713. package/src/renderer/shared/utils/workspacePreview.ts +34 -3
  714. package/src/renderer/shared/utils/workspaces.ts +3 -2
  715. package/src/renderer/src/App.tsx +20 -16
  716. package/src/renderer/src/agentProgress.ts +1 -0
  717. package/src/renderer/src/components/ChatPanel.tsx +1 -1
  718. package/src/renderer/src/components/DashboardSidebar.tsx +2 -2
  719. package/src/renderer/src/components/ImageStudioPanel.tsx +1 -1
  720. package/src/renderer/src/components/LiveTranslateControls.tsx +1 -1
  721. package/src/renderer/src/components/MintDashboard.tsx +1 -1
  722. package/src/renderer/src/components/ProactiveGlow.tsx +1 -1
  723. package/src/renderer/src/components/ScreenPicker.css +1 -1
  724. package/src/renderer/src/components/ScreenPicker.tsx +1 -1
  725. package/src/renderer/src/components/SettingsWindow.tsx +6 -830
  726. package/src/renderer/src/components/SpotlightWindow.tsx +4 -32
  727. package/src/renderer/src/components/VeoStudioPanel.tsx +1 -1
  728. package/src/renderer/src/components/WorkspacePanel.tsx +49 -15
  729. package/src/renderer/src/components/browser-suggestions-popup.css +8 -8
  730. package/src/renderer/src/css/app-shell.css +26 -106
  731. package/src/renderer/src/css/chat.css +1499 -292
  732. package/src/renderer/src/css/desktop-tools.css +17 -348
  733. package/src/renderer/src/css/sidebar-workspace.css +22 -2
  734. package/src/renderer/src/css/spotlight.css +2 -2
  735. package/src/renderer/src/css/tool-surfaces.css +335 -65
  736. package/src/renderer/src/css/widget.css +6 -5
  737. package/src/renderer/src/env.d.ts +13 -0
  738. package/src/renderer/src/index.css +53 -83
  739. package/src/renderer/src/tauri.ts +99 -13
  740. package/src/renderer/src/voiceInput.ts +3 -1
  741. package/src/renderer/src-web/App.tsx +20 -8
  742. package/src/renderer/src-web/agentProgress.ts +1 -0
  743. package/src/renderer/src-web/components/ChatPanel.tsx +1 -1
  744. package/src/renderer/src-web/components/DashboardSidebar.tsx +2 -2
  745. package/src/renderer/src-web/components/ImageStudioPanel.tsx +1 -1
  746. package/src/renderer/src-web/components/MintDashboard.tsx +1 -1
  747. package/src/renderer/src-web/components/SettingsWindow.tsx +6 -851
  748. package/src/renderer/src-web/components/VeoStudioPanel.tsx +1 -1
  749. package/src/renderer/src-web/css/app-shell.css +20 -104
  750. package/src/renderer/src-web/css/chat.css +1536 -261
  751. package/src/renderer/src-web/css/mobile.css +29 -13
  752. package/src/renderer/src-web/css/settings/mobile.css +2 -2
  753. package/src/renderer/src-web/css/sidebar-workspace.css +21 -1
  754. package/src/renderer/src-web/env.d.ts +14 -0
  755. package/src/renderer/src-web/index.css +53 -83
  756. package/src/renderer/src-web/tauri.ts +93 -15
  757. package/src/renderer/tests/browserArtifacts.browser.html +1 -0
  758. package/src/renderer/tests/browserArtifacts.browser.tsx +68 -0
  759. package/src/renderer/tests/chatEscape.browser.html +1 -0
  760. package/src/renderer/tests/chatEscape.browser.tsx +25 -0
  761. package/src/renderer/tests/chatPerformance.browser.html +1 -1
  762. package/src/renderer/tests/collapsedPaste.browser.html +1 -1
  763. package/src/renderer/tests/composerMenus.browser.html +3 -3
  764. package/src/renderer/tests/contextCompaction.browser.html +1 -1
  765. package/src/renderer/tests/errorRecovery.browser.html +1 -1
  766. package/src/renderer/tests/fixtures/preview.webm +0 -0
  767. package/src/renderer/tests/glideSelect.browser.html +1 -2
  768. package/src/renderer/tests/glideSelect.preview.html +1 -1
  769. package/src/renderer/tests/library.browser.tsx +4 -1
  770. package/src/renderer/tests/liveOrb.browser.html +1 -1
  771. package/src/renderer/tests/liveVoice.browser.html +1 -0
  772. package/src/renderer/tests/mediaFixtures.ts +32 -0
  773. package/src/renderer/tests/modInput.browser.html +1 -1
  774. package/src/renderer/tests/sessionSources.browser.tsx +76 -15
  775. package/src/renderer/tests/toolsPanel.browser.tsx +5 -5
  776. package/src/renderer/tests/viewedImages.browser.tsx +3 -3
  777. package/src/renderer/tests/voiceDictation.browser.html +101 -0
  778. package/src/renderer/tests/voicePill.browser.html +3 -3
  779. package/src/renderer/tests/voicePill.preview.html +1 -1
  780. package/src/renderer/tests/webSources.browser.tsx +2 -98
  781. package/src/renderer/tests/workspaceDrop.browser.html +1 -0
  782. package/src/renderer/tests/workspaceDrop.browser.tsx +183 -0
  783. package/src/renderer/tests/workspaceExpansion.browser.html +1 -0
  784. package/src/renderer/tests/workspaceExpansion.browser.tsx +66 -0
  785. package/src/renderer/tests/workspaceRemoval.browser.html +66 -0
  786. package/src/renderer/tests/workspaceUndo.browser.html +10 -2
  787. package/src-tauri/src/app/lifecycle.rs +45 -15
  788. package/src-tauri/src/background/headless.rs +14 -8
  789. package/src-tauri/src/commands/conversation.rs +137 -73
  790. package/src-tauri/src/commands/integrations.rs +7 -1
  791. package/src-tauri/src/commands/media.rs +7 -0
  792. package/src-tauri/src/commands/workspace.rs +38 -0
  793. package/src-tauri/src/native/mod.rs +0 -1
  794. package/src-tauri/src/native/system.rs +4 -47
  795. package/tsconfig.json +15 -1
  796. package/vite.config.ts +2 -1
  797. package/vite.config.web.ts +2 -0
  798. package/crates/mint-cli/src/interactive/chat_tui.rs +0 -7853
  799. package/crates/mint-core/src/agent/chat.rs +0 -4155
  800. package/crates/mint-core/src/browser/lifecycle.rs +0 -191
  801. package/crates/mint-core/src/browser/logging.rs +0 -20
  802. package/crates/mint-core/src/orchestration/tools/browser.rs +0 -31
  803. package/out/web/assets/CliSessionsView-CtzUJpK_.css +0 -1
  804. package/out/web/assets/CliSessionsView-D7hw0Mgj.js +0 -1
  805. package/out/web/assets/CodeReviewPage-C7bEOJfd.js +0 -4
  806. package/out/web/assets/CodeReviewPage-Dte9uLiL.css +0 -1
  807. package/out/web/assets/CommandPalette-0RrF-AOT.js +0 -1
  808. package/out/web/assets/ImageStudioPanel-CdxkAf_r.js +0 -1
  809. package/out/web/assets/LibraryPage--Qfisp6D.js +0 -2
  810. package/out/web/assets/LibraryPage-DAxUTAg7.css +0 -1
  811. package/out/web/assets/LinkedFoldersView-2nfZbDgN.js +0 -1
  812. package/out/web/assets/McpRegistryPicker-h9uS8_wT.js +0 -1
  813. package/out/web/assets/McpServersView-slfQJzNB.js +0 -1
  814. package/out/web/assets/MintDashboard-BSR5XpXh.js +0 -25
  815. package/out/web/assets/MintDashboard-CWUcCGhd.css +0 -1
  816. package/out/web/assets/PluginsView-l7LSm503.js +0 -1
  817. package/out/web/assets/ScheduledTasksView-CttmLbq1.js +0 -1
  818. package/out/web/assets/SettingsWindow-BCMLYvEV.js +0 -4
  819. package/out/web/assets/SettingsWindow-DcMVmU9u.css +0 -1
  820. package/out/web/assets/SkillsView-rNs1qr_U.js +0 -2
  821. package/out/web/assets/ToolSurfacePage-B9DzEPWN.js +0 -37
  822. package/out/web/assets/ToolSurfacePage-CTfgggF2.css +0 -1
  823. package/out/web/assets/VeoStudioPanel-DiJs_Cg2.js +0 -4
  824. package/out/web/assets/WorkspacePanel-CbJ1PFoZ.js +0 -1
  825. package/out/web/assets/WorkspaceSelector-B2xIp04D.js +0 -1
  826. package/out/web/assets/WorkspaceSelector-Ca8uJOsS.css +0 -1
  827. package/out/web/assets/abnfDiagram-VCTEODGH-CD0CjoGl.js +0 -1
  828. package/out/web/assets/architecture-7GRP2DOG-RKV83OeZ.js +0 -1
  829. package/out/web/assets/architectureDiagram-5GKGNRK7-nN_HAFct.js +0 -36
  830. package/out/web/assets/c4Diagram-UCG6FXSJ-DTdV8_zE.js +0 -38
  831. package/out/web/assets/chunk-2Q5K7J3B-Dmqrq8Tp.js +0 -1
  832. package/out/web/assets/chunk-75Z2AOVW-_M0z5olV.js +0 -2
  833. package/out/web/assets/chunk-CKBBP62Z-BpQEgeHF.js +0 -2
  834. package/out/web/assets/chunk-CLGD4ZFX-D54yGvGS.js +0 -1
  835. package/out/web/assets/chunk-CacB07dV.js +0 -1
  836. package/out/web/assets/chunk-DSNCTWBM-C-gvT2ru.js +0 -2
  837. package/out/web/assets/chunk-DU6HZSFF-CnFngIhW.js +0 -128
  838. package/out/web/assets/chunk-F27PBJKO-BqtWrnEO.js +0 -1
  839. package/out/web/assets/chunk-FOHPRMQF-BaVC1uA7.js +0 -161
  840. package/out/web/assets/chunk-G27WJ6UU-Ckoq5mi2.js +0 -231
  841. package/out/web/assets/chunk-JQ64N6SF-CF8T7GiZ.js +0 -1
  842. package/out/web/assets/chunk-JWPE2WC7-BwtYOHqU.js +0 -1
  843. package/out/web/assets/chunk-LNGE3PJU-AdBoOWw2.js +0 -34
  844. package/out/web/assets/chunk-OBVCFTLP-CRIkunUB.js +0 -1
  845. package/out/web/assets/chunk-P2QGCYS3-UFEU6BxZ.js +0 -1
  846. package/out/web/assets/chunk-POPQ4Y6H-CQcrNbjr.js +0 -1
  847. package/out/web/assets/chunk-PWAF6VOD-Cc2uVDsG.js +0 -1
  848. package/out/web/assets/chunk-R7TYR2AO-DiCxzdAY.js +0 -10
  849. package/out/web/assets/chunk-RHFEMEQ7-b7hnPmrR.js +0 -168
  850. package/out/web/assets/chunk-XXDRQBXY-DnJkbUaw.js +0 -1
  851. package/out/web/assets/chunk-Y2CYZVJY-C9o-s6OY.js +0 -1
  852. package/out/web/assets/classDiagram-DTDB5LWJ-RnC2mYO4.js +0 -1
  853. package/out/web/assets/classDiagram-v2-JRS7N3AN-RnC2mYO4.js +0 -1
  854. package/out/web/assets/config-CnvCChUM.js +0 -2
  855. package/out/web/assets/config-DM6oIVO3.css +0 -1
  856. package/out/web/assets/core-DsKkQ7ig.js +0 -1
  857. package/out/web/assets/cose-bilkent-JH36ORCC--UN4aw5-.js +0 -1
  858. package/out/web/assets/cynefin-OW5HDTMX-CnzQfOGp.js +0 -1
  859. package/out/web/assets/cynefinDiagram-5FMLGOSQ-DVtQnfpm.js +0 -62
  860. package/out/web/assets/cytoscape.esm-Df1UN4Y1.js +0 -321
  861. package/out/web/assets/dagre-3AP2YEHR-DG76_k5_.js +0 -4
  862. package/out/web/assets/dagre-BWadQu2F.js +0 -1
  863. package/out/web/assets/diagram-S7CK7UJ4-CaouUoze.js +0 -30
  864. package/out/web/assets/diagram-UQ7AKVKN-B4bEc26B.js +0 -41
  865. package/out/web/assets/diagram-VX7I27RA-7hH5IPYf.js +0 -24
  866. package/out/web/assets/diagram-Z3DM3KII-CCM4clo8.js +0 -24
  867. package/out/web/assets/dist-BoTOASLF.js +0 -1
  868. package/out/web/assets/dist-js-BkYNznHX.js +0 -1
  869. package/out/web/assets/ebnfDiagram-PWID7BFC-Da7WAM3k.js +0 -1
  870. package/out/web/assets/event-Cdt7Byyj.js +0 -1
  871. package/out/web/assets/eventmodeling-NTZA5JFV-xcfsA0XD.js +0 -1
  872. package/out/web/assets/flowDiagram-A5DVABFB-JdtH1673.js +0 -1
  873. package/out/web/assets/ganttDiagram-EL5Y4UJY-B3ClFGmL.js +0 -292
  874. package/out/web/assets/gitGraph-4MIJSDKK-BZ_y_cDU.js +0 -1
  875. package/out/web/assets/gitGraphDiagram-WWUBYQGX-drhRSYm3.js +0 -106
  876. package/out/web/assets/graphlib-CTR5ZhLf.js +0 -1
  877. package/out/web/assets/index-BZ9xgmMP.css +0 -1
  878. package/out/web/assets/index-Ca8cCS4h.js +0 -168
  879. package/out/web/assets/info-A6RAGUB7-B7I3K0F1.js +0 -1
  880. package/out/web/assets/infoDiagram-RXCK75RN-deX_1MPh.js +0 -2
  881. package/out/web/assets/journeyDiagram-EYS64GPL-CZn2kB8b.js +0 -139
  882. package/out/web/assets/jsx-runtime-DN4NhQUK.js +0 -1
  883. package/out/web/assets/katex-O3jvqgnU.js +0 -257
  884. package/out/web/assets/markdown-CMhYIcWB.js +0 -39
  885. package/out/web/assets/mermaid-parser.core-Whu2rlJC.js +0 -7
  886. package/out/web/assets/mermaid.core-CKyPo8VH.js +0 -11
  887. package/out/web/assets/modelManager-CUaGWMWu.js +0 -2
  888. package/out/web/assets/oauthManager-DlNhXJr6.js +0 -1
  889. package/out/web/assets/packet-AYTQ26CC-CEeIRKRG.js +0 -1
  890. package/out/web/assets/pegDiagram-XKGWAZYB-BYadI-KO.js +0 -1
  891. package/out/web/assets/pie-WAS4IAKB-D8uf4dh5.js +0 -1
  892. package/out/web/assets/pieDiagram-E7YTZNPT-CjSDy4hO.js +0 -39
  893. package/out/web/assets/radar-RG4KPBEZ-DZJxnokY.js +0 -1
  894. package/out/web/assets/railroad-74A4TZTK-DBysUiUi.js +0 -1
  895. package/out/web/assets/railroad-abnf-HS5TGJTU-CHWxoJYg.js +0 -1
  896. package/out/web/assets/railroad-ebnf-LZEXJU2U-DDv7X6bY.js +0 -1
  897. package/out/web/assets/railroad-peg-WCYAUIDC-DpfcYYNJ.js +0 -1
  898. package/out/web/assets/railroadDiagram-O6MQD6OU-CYtyv0t3.js +0 -1
  899. package/out/web/assets/rough.esm-DFbTUy8a.js +0 -1
  900. package/out/web/assets/sankeyDiagram-P5KCCOFB-Bn2x6C4Y.js +0 -40
  901. package/out/web/assets/sequenceDiagram-WJ2MYXX4-BvFLKtPd.js +0 -162
  902. package/out/web/assets/sizeCapture-X5ZJPWSS-DKeOCaN2.js +0 -1
  903. package/out/web/assets/src-DnfeXQPr.js +0 -1
  904. package/out/web/assets/stateDiagram-HBIQ2CUA-Bx0GWeaY.js +0 -1
  905. package/out/web/assets/stateDiagram-v2-4QOOHH4V--hol1yg_.js +0 -1
  906. package/out/web/assets/studio-B1mk40wD.js +0 -1
  907. package/out/web/assets/swimlanes-XN3QIQJK-CCma0T7L.js +0 -1
  908. package/out/web/assets/swimlanesDiagram-VK2B7HYN-BkohoVsU.js +0 -8
  909. package/out/web/assets/timeline-definition-24CTP7MA-iCRhhJ7C.js +0 -120
  910. package/out/web/assets/treeView-Q6P3EWNA-DZ-y08qp.js +0 -1
  911. package/out/web/assets/treemap-WGGIJYW6-De0CxzMx.js +0 -1
  912. package/out/web/assets/vennDiagram-4TSXK5OY-w_7aZ6xe.js +0 -34
  913. package/out/web/assets/wardley-WFR3VGLG-DT0y0xJ4.js +0 -1
  914. package/out/web/assets/window-BJCVtyET.js +0 -1
  915. package/shared-modules/libsecret/libsecret.json +0 -23
  916. package/shared-modules/libusb/libusb.json +0 -27
  917. package/shared-modules/lzo/lzo.json +0 -27
  918. package/src/renderer/shared/components/AgentActivityDrawer.tsx +0 -204
  919. package/src/renderer/shared/components/AgentActivityTable.tsx +0 -301
  920. package/src/renderer/shared/components/ChatMessageItem.test.tsx +0 -68
  921. package/src/renderer/shared/components/ChatMessageItem.tsx +0 -326
  922. package/src/renderer/shared/components/ChatPanel.test.tsx +0 -21
  923. package/src/renderer/shared/components/FormattedMessage.tsx +0 -11
  924. package/src/renderer/shared/components/GeminiLiveOverlay.test.tsx +0 -24
  925. package/src/renderer/shared/components/GlideSelect.d.ts +0 -31
  926. package/src/renderer/shared/components/GlideSelect.jsx +0 -266
  927. package/src/renderer/shared/components/InlineThoughtNote.tsx +0 -27
  928. package/src/renderer/shared/components/ModSessionControls.tsx +0 -8
  929. package/src/renderer/shared/components/ModUi.tsx +0 -26
  930. package/src/renderer/shared/components/ProgressActivity.tsx +0 -17
  931. package/src/renderer/shared/components/ReviewHistory.tsx +0 -42
  932. package/src/renderer/shared/components/RunSummaryDashboard.tsx +0 -143
  933. package/src/renderer/shared/components/SelectField.tsx +0 -38
  934. package/src/renderer/shared/components/SessionSources.tsx +0 -48
  935. package/src/renderer/shared/components/SessionSourcesPopover.tsx +0 -61
  936. package/src/renderer/shared/components/ToolSurfacePage.test.tsx +0 -32
  937. package/src/renderer/shared/components/ViewedImages.tsx +0 -36
  938. package/src/renderer/shared/components/WorkspaceContextPopover.tsx +0 -138
  939. package/src/renderer/shared/components/WorkspacePreviewActions.test.tsx +0 -44
  940. package/src/renderer/shared/components/ui/demo.tsx +0 -26
  941. package/src/renderer/shared/css/session-sources-popover.css +0 -9
  942. package/src/renderer/src/components/BrowserShell.tsx +0 -108
  943. package/src/renderer/src/components/BrowserSuggestionsPopup.tsx +0 -91
  944. package/src/renderer/src/components/CalculationCard.tsx +0 -3
  945. package/src/renderer/src/components/ImageGenCard.tsx +0 -2
  946. package/src/renderer/src/components/ImageSearchCard.tsx +0 -2
  947. package/src/renderer/src/components/MermaidCard.tsx +0 -1
  948. package/src/renderer/src/components/PicturesLibrary.tsx +0 -2
  949. package/src/renderer/src/components/Settings/AgentsTab.tsx +0 -4
  950. package/src/renderer/src/components/Settings/AudioTab.tsx +0 -3
  951. package/src/renderer/src/components/Settings/AutomationTab.tsx +0 -3
  952. package/src/renderer/src/components/Settings/GeneralTab.tsx +0 -3
  953. package/src/renderer/src/components/Settings/MemoryTab.tsx +0 -3
  954. package/src/renderer/src/components/Settings/PluginsTab.tsx +0 -3
  955. package/src/renderer/src/components/Settings/ProfileTab.tsx +0 -3
  956. package/src/renderer/src/components/Settings/ShortcutsTab.tsx +0 -3
  957. package/src/renderer/src/components/Settings/ThemeTab.tsx +0 -3
  958. package/src/renderer/src/components/StockCard.tsx +0 -3
  959. package/src/renderer/src/components/WeatherCard.tsx +0 -3
  960. package/src/renderer/src/components/browser-shell.css +0 -185
  961. package/src/renderer/src/css/pictures.css +0 -2
  962. package/src/renderer/src-web/components/PicturesLibrary.tsx +0 -2
  963. package/src/renderer/src-web/components/Settings/AgentsTab.tsx +0 -4
  964. package/src/renderer/src-web/components/Settings/AudioTab.tsx +0 -3
  965. package/src/renderer/src-web/components/Settings/AutomationTab.tsx +0 -3
  966. package/src/renderer/src-web/components/Settings/GeneralTab.tsx +0 -3
  967. package/src/renderer/src-web/components/Settings/MemoryTab.tsx +0 -3
  968. package/src/renderer/src-web/components/Settings/PluginsTab.tsx +0 -3
  969. package/src/renderer/src-web/components/Settings/ProfileTab.tsx +0 -3
  970. package/src/renderer/src-web/components/Settings/ShortcutsTab.tsx +0 -3
  971. package/src/renderer/src-web/components/Settings/ThemeTab.tsx +0 -3
  972. package/src/renderer/src-web/css/pictures.css +0 -2
  973. package/src/renderer/src-web/css/settings/base.css +0 -1266
  974. package/src/renderer/src-web/css/spotlight.css +0 -102
  975. package/src/renderer/src-web/css/widget.css +0 -213
  976. package/src-tauri/src/native/browser.rs +0 -252
  977. /package/src/renderer/shared/components/{ContextCompactionStatus.tsx → chat/ContextCompactionStatus.tsx} +0 -0
  978. /package/src/renderer/shared/components/{GeminiLiveOverlay.css → chat/GeminiLiveOverlay.css} +0 -0
  979. /package/src/renderer/shared/components/{VirtualChatHistory.tsx → chat/VirtualChatHistory.tsx} +0 -0
  980. /package/src/renderer/shared/components/{VoicePill.d.ts → chat/VoicePill.d.ts} +0 -0
  981. /package/src/renderer/shared/components/{VoicePill.test.tsx → chat/VoicePill.test.tsx} +0 -0
  982. /package/src/renderer/shared/components/{ScreenCaptureDialog.css → studio/ScreenCaptureDialog.css} +0 -0
  983. /package/src/renderer/shared/components/{ScreenCaptureDialog.tsx → studio/ScreenCaptureDialog.tsx} +0 -0
  984. /package/src/renderer/shared/components/{SelectField.test.tsx → ui/SelectField.test.tsx} +0 -0
@@ -0,0 +1,1833 @@
1
+ //! ChatViewState, TuiHandle, composer editing, tool progress, and SQLite transcript sync.
2
+ use super::*;
3
+
4
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
5
+ pub(crate) enum SwarmUiStatus {
6
+ Running,
7
+ Success,
8
+ Failed,
9
+ }
10
+
11
+ #[derive(Debug, Clone)]
12
+ pub(crate) struct SwarmAgentUiState {
13
+ pub label: String,
14
+ pub profile: String,
15
+ pub task: String,
16
+ pub current_action: Option<String>,
17
+ pub status: SwarmUiStatus,
18
+ pub started_at: std::time::Instant,
19
+ pub finished_elapsed_ms: Option<u64>,
20
+ pub summary_preview: Option<String>,
21
+ }
22
+
23
+ pub(super) fn clip_text(text: &str, width: usize) -> String {
24
+ super::format::truncate_to_width(text, width)
25
+ }
26
+
27
+ pub(super) fn format_subagent_tool_action(action: &str, input: &serde_json::Value) -> String {
28
+ super::tool_activity::ToolActivity::subagent_action_summary(action, input)
29
+ }
30
+
31
+ #[derive(Debug, Default)]
32
+ pub(crate) struct ChatViewState {
33
+ pub(crate) transcript: Vec<TranscriptEntry>,
34
+ pub(crate) swarm_agents: Vec<SwarmAgentUiState>,
35
+ pub(super) chat_id: String,
36
+ pub(super) mods: Vec<serde_json::Value>,
37
+ pub(super) mods_open: bool,
38
+ pub(super) mod_focus: usize,
39
+ pub(super) mod_input: Option<(String, String)>,
40
+ pub(super) sync_cursor: i64,
41
+ pub(super) last_interaction_id: i64,
42
+ pub(super) composer: Vec<char>,
43
+ pub(super) collapsed_pastes: Vec<CollapsedPaste>,
44
+ pub(super) expanded_paste: Option<std::ops::Range<usize>>,
45
+ pub(super) cursor: usize,
46
+ pub(super) history: Vec<String>,
47
+ pub(super) history_index: Option<usize>,
48
+ pub(super) draft_before_history: Vec<char>,
49
+ pub(super) slash_selected: usize,
50
+ pub(super) status: Vec<String>,
51
+ pub(super) model: String,
52
+ pub(super) thinking_status: String,
53
+ pub(super) provider: String,
54
+ pub(super) workspace: String,
55
+ pub(super) current_dir: std::path::PathBuf,
56
+ pub(super) plan_mode: bool,
57
+ pub(super) scroll_from_bottom: usize,
58
+ pub(super) selection_mode: bool,
59
+ pub(super) selection_anchor: usize,
60
+ pub(super) selection_head: usize,
61
+ pub(super) notice: Option<(String, std::time::Instant)>,
62
+ pub(super) editing_turn: Option<(i64, usize)>,
63
+ pub(super) dialog: Option<DialogState>,
64
+ pub(super) thought_modal: Option<ThoughtModalState>,
65
+ pub(super) thoughts: Vec<ThinkingSegment>,
66
+ pub(super) tools: Vec<super::tool_activity::ToolActivity>,
67
+ pub(super) tool_viewer: Option<usize>,
68
+ pub(super) watermark_frame: u8,
69
+ pub(super) watermark_animating: bool,
70
+ pub(crate) queued_count: usize,
71
+ pub(crate) queued_preview: Option<String>,
72
+ pub(crate) theme: crate::terminal_theme::TuiTheme,
73
+ pub(super) assistant_pacer: Option<crate::agent::ProgressiveMarkdownPacer>,
74
+ pub(super) active_assistant_entry: Option<usize>,
75
+ pub(super) active_plan_entry: Option<usize>,
76
+ pub(super) pending_post_assistant_notices: Vec<String>,
77
+ pub(super) last_assistant_tick: Option<std::time::Instant>,
78
+ }
79
+
80
+ #[derive(Clone, Debug)]
81
+ pub(crate) struct TuiHandle {
82
+ pub(crate) state: Arc<Mutex<ChatViewState>>,
83
+ pub(super) interrupted: Arc<AtomicBool>,
84
+ pub(super) queued: Arc<Mutex<Vec<String>>>,
85
+ }
86
+
87
+ impl TuiHandle {
88
+ pub fn tool_progress(&self, progress: &mint_core::AgentProgress) {
89
+ if let Ok(mut state) = self.state.lock() {
90
+ state.record_tool_progress(progress);
91
+ }
92
+ }
93
+
94
+ pub fn reset_thoughts(&self) {
95
+ if let Ok(mut state) = self.state.lock() {
96
+ state.finish_live_thought();
97
+ state.thought_modal = None;
98
+ }
99
+ }
100
+
101
+ pub fn append_thought_delta(&self, id: &str, delta: &str, elapsed: std::time::Duration) {
102
+ if delta.is_empty() {
103
+ return;
104
+ }
105
+ if let Ok(mut state) = self.state.lock() {
106
+ if let Some(thought) = state.thoughts.last_mut()
107
+ && thought.id.as_deref() == Some(id)
108
+ && thought.updated_at.is_some()
109
+ {
110
+ thought.text.push_str(delta);
111
+ thought.elapsed = elapsed.max(thought.elapsed());
112
+ thought.updated_at = Some(std::time::Instant::now());
113
+ } else {
114
+ state.finish_live_thought();
115
+ state.thoughts.push(ThinkingSegment {
116
+ id: Some(id.to_owned()),
117
+ text: delta.to_owned(),
118
+ elapsed,
119
+ elapsed_known: true,
120
+ updated_at: Some(std::time::Instant::now()),
121
+ });
122
+ }
123
+ state.refresh_thought_modal();
124
+ }
125
+ }
126
+
127
+ pub fn finish_thought(&self, id: Option<&str>, text: &str, elapsed: std::time::Duration) {
128
+ if let Ok(mut state) = self.state.lock() {
129
+ if let Some(thought) = state.thoughts.last_mut()
130
+ && thought.updated_at.is_some()
131
+ && (id.is_none() || thought.id.as_deref() == id)
132
+ {
133
+ if !text.trim().is_empty() {
134
+ thought.text = text.to_owned();
135
+ }
136
+ thought.elapsed = elapsed;
137
+ thought.updated_at = None;
138
+ } else if let Some(thought) = state.thoughts.last_mut()
139
+ && thought.updated_at.is_none()
140
+ && id.is_some()
141
+ && thought.id.as_deref() == id
142
+ {
143
+ if !text.trim().is_empty() && !thought.text.contains(text.trim()) {
144
+ if thought.text.trim().is_empty() {
145
+ thought.text = text.to_owned();
146
+ } else {
147
+ thought.text = format!("{}\n\n{}", thought.text, text.trim());
148
+ }
149
+ }
150
+ thought.elapsed = elapsed.max(thought.elapsed);
151
+ state.refresh_thought_modal();
152
+ return;
153
+ } else if !text.trim().is_empty() {
154
+ // Close any in-flight live thought segment without emitting a separate
155
+ // duplicate "Thought · Xs (Ctrl+T to view)" notice before our own notice.
156
+ if let Some(live) = state.thoughts.last_mut()
157
+ && live.updated_at.is_some()
158
+ {
159
+ live.elapsed = live.elapsed();
160
+ live.updated_at = None;
161
+ }
162
+ state.thoughts.push(ThinkingSegment {
163
+ id: id.map(str::to_owned),
164
+ text: text.to_owned(),
165
+ elapsed,
166
+ elapsed_known: true,
167
+ updated_at: None,
168
+ });
169
+ } else {
170
+ state.finish_live_thought();
171
+ return;
172
+ }
173
+ state.push_notice(format!(
174
+ "Thought · {} (Ctrl+T to view)",
175
+ super::format_thought_elapsed(elapsed)
176
+ ));
177
+ state.refresh_thought_modal();
178
+ }
179
+ }
180
+
181
+ pub fn set_status(&self, lines: Vec<String>) {
182
+ if let Ok(mut state) = self.state.lock() {
183
+ state.status = lines;
184
+ }
185
+ }
186
+ pub fn clear_status(&self) {
187
+ self.set_status(Vec::new());
188
+ }
189
+ pub fn push_notice(&self, text: impl Into<String>) {
190
+ if let Ok(mut state) = self.state.lock() {
191
+ state.push_notice(text);
192
+ }
193
+ }
194
+ pub fn append_assistant_delta(&self, delta: &str) {
195
+ if delta.is_empty() {
196
+ return;
197
+ }
198
+ if let Ok(mut state) = self.state.lock() {
199
+ state.append_assistant_delta(delta);
200
+ }
201
+ }
202
+ pub fn begin_assistant_reveal(&self, summary: String, post_notices: Vec<String>) {
203
+ if let Ok(mut state) = self.state.lock() {
204
+ state.begin_assistant_reveal(summary, post_notices);
205
+ }
206
+ }
207
+ pub fn push_command(&self, text: impl Into<String>) {
208
+ if let Ok(mut state) = self.state.lock() {
209
+ state
210
+ .transcript
211
+ .push(TranscriptEntry::new(TranscriptRole::Command, text));
212
+ state.scroll_from_bottom = 0;
213
+ }
214
+ }
215
+ pub fn push_release_notes(&self, text: impl Into<String>) {
216
+ if let Ok(mut state) = self.state.lock() {
217
+ state
218
+ .transcript
219
+ .push(TranscriptEntry::new(TranscriptRole::ReleaseNotes, text));
220
+ state.scroll_from_bottom = 0;
221
+ }
222
+ }
223
+ pub fn push_plan(&self, text: impl Into<String>) {
224
+ if let Ok(mut state) = self.state.lock() {
225
+ state.push_plan(text);
226
+ }
227
+ }
228
+ #[allow(dead_code)]
229
+ pub fn push_system(&self, text: impl Into<String>) {
230
+ if let Ok(mut state) = self.state.lock() {
231
+ state
232
+ .transcript
233
+ .push(TranscriptEntry::new(TranscriptRole::System, text));
234
+ state.scroll_from_bottom = 0;
235
+ }
236
+ }
237
+ pub fn set_notice(&self, text: impl Into<String>) {
238
+ if let Ok(mut state) = self.state.lock() {
239
+ state.set_notice(text);
240
+ }
241
+ }
242
+ pub fn take_interrupted(&self) -> bool {
243
+ self.interrupted.swap(false, Ordering::Relaxed)
244
+ }
245
+ pub fn take_queued(&self) -> Vec<String> {
246
+ self.queued
247
+ .lock()
248
+ .map(|mut queued| std::mem::take(&mut *queued))
249
+ .unwrap_or_default()
250
+ }
251
+ pub fn draft(&self) -> Option<String> {
252
+ self.state
253
+ .lock()
254
+ .ok()
255
+ .and_then(|state| (!state.composer.is_empty()).then(|| state.composer_text()))
256
+ }
257
+ pub fn choose(
258
+ &self,
259
+ title: impl Into<String>,
260
+ body: impl Into<String>,
261
+ options: Vec<String>,
262
+ ) -> Option<usize> {
263
+ let (reply, response) = mpsc::channel();
264
+ if let Ok(mut state) = self.state.lock() {
265
+ state.dialog = Some(DialogState::new_choice(title, body, options, None, reply));
266
+ } else {
267
+ return None;
268
+ }
269
+ match response.recv() {
270
+ Ok(DialogAnswer::Choice(choice)) => Some(choice),
271
+ _ => None,
272
+ }
273
+ }
274
+ pub fn choose_many(
275
+ &self,
276
+ title: impl Into<String>,
277
+ body: impl Into<String>,
278
+ options: Vec<String>,
279
+ ) -> Vec<usize> {
280
+ let (reply, response) = mpsc::channel();
281
+ let checked = vec![false; options.len()];
282
+ if let Ok(mut state) = self.state.lock() {
283
+ state.dialog = Some(DialogState::new_choice(
284
+ title,
285
+ body,
286
+ options,
287
+ Some(checked),
288
+ reply,
289
+ ));
290
+ } else {
291
+ return Vec::new();
292
+ }
293
+ match response.recv() {
294
+ Ok(DialogAnswer::Choices(choices)) => choices,
295
+ _ => Vec::new(),
296
+ }
297
+ }
298
+ pub fn prompt_text(&self, title: impl Into<String>, body: impl Into<String>) -> Option<String> {
299
+ let (reply, response) = mpsc::channel();
300
+ if let Ok(mut state) = self.state.lock() {
301
+ state.dialog = Some(DialogState::new_text(title, body, reply));
302
+ } else {
303
+ return None;
304
+ }
305
+ match response.recv() {
306
+ Ok(DialogAnswer::Text(text)) if !text.trim().is_empty() => Some(text),
307
+ _ => None,
308
+ }
309
+ }
310
+ }
311
+
312
+ impl ChatViewState {
313
+ pub(super) fn record_tool_progress(
314
+ &mut self,
315
+ progress: &mint_core::AgentProgress,
316
+ ) -> Option<usize> {
317
+ use mint_core::AgentProgress;
318
+ match progress {
319
+ AgentProgress::SubagentStarted {
320
+ subagent,
321
+ profile,
322
+ task,
323
+ } => {
324
+ if let Some(existing) = self.swarm_agents.iter_mut().find(|a| a.label == *subagent)
325
+ {
326
+ existing.profile = profile.clone();
327
+ existing.task = task.clone();
328
+ existing.status = SwarmUiStatus::Running;
329
+ existing.started_at = std::time::Instant::now();
330
+ } else {
331
+ self.swarm_agents.push(SwarmAgentUiState {
332
+ label: subagent.clone(),
333
+ profile: profile.clone(),
334
+ task: task.clone(),
335
+ current_action: None,
336
+ status: SwarmUiStatus::Running,
337
+ started_at: std::time::Instant::now(),
338
+ finished_elapsed_ms: None,
339
+ summary_preview: None,
340
+ });
341
+ }
342
+ return None;
343
+ }
344
+ AgentProgress::SubagentFinished {
345
+ subagent,
346
+ status,
347
+ elapsed_ms,
348
+ summary_preview,
349
+ ..
350
+ } => {
351
+ let ui_status = if status == "failed" {
352
+ SwarmUiStatus::Failed
353
+ } else {
354
+ SwarmUiStatus::Success
355
+ };
356
+ if let Some(existing) = self.swarm_agents.iter_mut().find(|a| a.label == *subagent)
357
+ {
358
+ existing.status = ui_status;
359
+ existing.finished_elapsed_ms = Some(*elapsed_ms);
360
+ if !summary_preview.is_empty() {
361
+ existing.summary_preview = Some(summary_preview.clone());
362
+ }
363
+ } else {
364
+ self.swarm_agents.push(SwarmAgentUiState {
365
+ label: subagent.clone(),
366
+ profile: String::new(),
367
+ task: String::new(),
368
+ current_action: None,
369
+ status: ui_status,
370
+ started_at: std::time::Instant::now(),
371
+ finished_elapsed_ms: Some(*elapsed_ms),
372
+ summary_preview: if summary_preview.is_empty() {
373
+ None
374
+ } else {
375
+ Some(summary_preview.clone())
376
+ },
377
+ });
378
+ }
379
+ return None;
380
+ }
381
+ AgentProgress::ToolStart {
382
+ subagent: Some(agent_label),
383
+ action,
384
+ input,
385
+ ..
386
+ } => {
387
+ if let Some(agent) = self
388
+ .swarm_agents
389
+ .iter_mut()
390
+ .find(|a| a.label == *agent_label)
391
+ {
392
+ agent.current_action = Some(format_subagent_tool_action(action, input));
393
+ }
394
+ return None;
395
+ }
396
+ AgentProgress::ToolEnd {
397
+ subagent: Some(_), ..
398
+ } => {
399
+ return None;
400
+ }
401
+ AgentProgress::PlanUpdated { plan } => {
402
+ self.upsert_active_plan_card(plan);
403
+ return None;
404
+ }
405
+ _ => {}
406
+ }
407
+ let (call_id, action, input, subagent, end) = match progress {
408
+ AgentProgress::ToolStart {
409
+ call_id,
410
+ action,
411
+ input,
412
+ subagent,
413
+ } => (call_id, action, input, subagent, None),
414
+ AgentProgress::ToolEnd {
415
+ call_id,
416
+ action,
417
+ input,
418
+ subagent,
419
+ result,
420
+ status,
421
+ elapsed_ms,
422
+ } => (
423
+ call_id,
424
+ action,
425
+ input,
426
+ subagent,
427
+ Some((result, status, elapsed_ms)),
428
+ ),
429
+ _ => return None,
430
+ };
431
+ if action == "exit_plan_mode" {
432
+ return None;
433
+ }
434
+ let existing = self.tools.iter().position(|tool| {
435
+ (end.is_some() || call_id.is_some())
436
+ && tool.running()
437
+ && tool.matches(call_id.as_deref(), action, input, subagent.as_deref())
438
+ });
439
+ let index = existing.unwrap_or_else(|| {
440
+ let entry = if let Some(idx) = self.active_assistant_entry
441
+ && idx <= self.transcript.len()
442
+ {
443
+ self.transcript
444
+ .insert(idx, TranscriptEntry::new(TranscriptRole::Notice, ""));
445
+ self.active_assistant_entry = Some(idx + 1);
446
+ if let Some(plan_idx) = &mut self.active_plan_entry
447
+ && *plan_idx >= idx
448
+ {
449
+ *plan_idx += 1;
450
+ }
451
+ for tool in &mut self.tools {
452
+ if tool.entry >= idx {
453
+ tool.entry += 1;
454
+ }
455
+ }
456
+ idx
457
+ } else {
458
+ let entry = self.transcript.len();
459
+ self.transcript
460
+ .push(TranscriptEntry::new(TranscriptRole::Notice, ""));
461
+ entry
462
+ };
463
+ self.tools.push(super::tool_activity::ToolActivity {
464
+ entry,
465
+ call_id: call_id.clone(),
466
+ action: action.clone(),
467
+ input: input.clone(),
468
+ subagent: subagent.clone(),
469
+ started: std::time::Instant::now(),
470
+ elapsed: None,
471
+ result: None,
472
+ status: None,
473
+ });
474
+ self.tools.len() - 1
475
+ });
476
+ if let Some((result, status, elapsed_ms)) = end {
477
+ self.tools[index].finish(result, status.clone());
478
+ if let Some(elapsed_ms) = elapsed_ms {
479
+ self.tools[index].elapsed = Some(std::time::Duration::from_millis(*elapsed_ms));
480
+ }
481
+ }
482
+ self.refresh_tool_card(index);
483
+ if self.tool_viewer == Some(index) {
484
+ self.refresh_tool_viewer(false);
485
+ }
486
+ Some(index)
487
+ }
488
+
489
+ pub(super) fn refresh_tool_card(&mut self, index: usize) {
490
+ let (default_width, _) = crate::markdown::terminal_size_or_default();
491
+ let tool = &self.tools[index];
492
+ let lines = tool.lines(default_width);
493
+ let unclipped_lines = tool.lines(u16::MAX);
494
+ let plain: Vec<String> = unclipped_lines
495
+ .iter()
496
+ .map(|line| {
497
+ line.spans
498
+ .iter()
499
+ .map(|span| span.content.as_ref())
500
+ .collect()
501
+ })
502
+ .collect();
503
+ if let Some(entry) = self.transcript.get_mut(tool.entry) {
504
+ *entry = TranscriptEntry {
505
+ role: TranscriptRole::Notice,
506
+ text: plain.join("\n"),
507
+ rendered_lines: lines,
508
+ plain_lines: plain,
509
+ rendered_width: default_width,
510
+ };
511
+ }
512
+ }
513
+
514
+ pub(super) fn finish_running_tools(&mut self) {
515
+ for index in 0..self.tools.len() {
516
+ if self.tools[index].running() {
517
+ self.tools[index].finish(
518
+ "Interrupted before a result was received.",
519
+ Some(mint_core::integrations::mcp_result::ToolStatus::Cancelled),
520
+ );
521
+ self.refresh_tool_card(index);
522
+ }
523
+ }
524
+ if self.tool_viewer.is_some() {
525
+ self.refresh_tool_viewer(false);
526
+ }
527
+ }
528
+
529
+ pub(super) fn refresh_tool_viewer(&mut self, reset_scroll: bool) {
530
+ if self
531
+ .dialog
532
+ .as_ref()
533
+ .is_some_and(|dialog| !dialog.is_tool_details())
534
+ {
535
+ self.tool_viewer = None;
536
+ return;
537
+ }
538
+ let Some(index) = self.tool_viewer else {
539
+ return;
540
+ };
541
+ let Some(tool) = self.tools.get(index) else {
542
+ return;
543
+ };
544
+ let body = tool.details();
545
+ if let Some(dialog) = self.dialog.as_mut() {
546
+ dialog.title = format!("Tool details · {}/{}", index + 1, self.tools.len());
547
+ dialog.body = body;
548
+ if reset_scroll {
549
+ dialog.body_scroll = 0;
550
+ }
551
+ } else {
552
+ let (reply, _) = mpsc::channel();
553
+ self.dialog = Some(DialogState::new_tool_details(
554
+ format!("Tool details · {}/{}", index + 1, self.tools.len()),
555
+ body,
556
+ reply,
557
+ ));
558
+ }
559
+ }
560
+
561
+ pub(super) fn handle_tool_key(&mut self, key: event::KeyEvent) -> bool {
562
+ if self
563
+ .dialog
564
+ .as_ref()
565
+ .is_some_and(|dialog| !dialog.is_tool_details())
566
+ {
567
+ self.tool_viewer = None;
568
+ return false;
569
+ }
570
+ use event::{KeyCode, KeyModifiers};
571
+ if self.dialog.is_none() {
572
+ self.tool_viewer = None;
573
+ }
574
+ let toggle =
575
+ key.code == KeyCode::Char('o') && key.modifiers.contains(KeyModifiers::CONTROL);
576
+ if let Some(index) = self.tool_viewer {
577
+ if toggle || key.code == KeyCode::Esc {
578
+ self.tool_viewer = None;
579
+ self.dialog = None;
580
+ return true;
581
+ }
582
+ if matches!(key.code, KeyCode::Tab | KeyCode::BackTab) {
583
+ self.tool_viewer = Some(if key.code == KeyCode::Tab {
584
+ (index + 1) % self.tools.len()
585
+ } else {
586
+ (index + self.tools.len() - 1) % self.tools.len()
587
+ });
588
+ self.refresh_tool_viewer(true);
589
+ return true;
590
+ }
591
+ } else if toggle && self.dialog.is_none() && self.thought_modal.is_none() {
592
+ if self.tools.is_empty() {
593
+ self.set_notice("No tool activity recorded yet");
594
+ } else {
595
+ let target = self.preferred_tool_viewer_index();
596
+ self.tool_viewer = Some(target);
597
+ self.refresh_tool_viewer(true);
598
+ }
599
+ return true;
600
+ }
601
+ false
602
+ }
603
+
604
+ pub(super) fn preferred_tool_viewer_index(&self) -> usize {
605
+ let has_recent_sources = self.transcript.iter().rev().take(6).any(|entry| {
606
+ entry.text.starts_with("Sources:\n") || entry.text.contains("Ctrl+O view web_search")
607
+ });
608
+
609
+ if has_recent_sources {
610
+ if let Some(pos) = self
611
+ .tools
612
+ .iter()
613
+ .rposition(|tool| tool.action == "web_search")
614
+ {
615
+ return pos;
616
+ }
617
+ }
618
+ self.tools.len() - 1
619
+ }
620
+
621
+ pub fn from_session(session: &InteractiveSession) -> Self {
622
+ let mut state = Self {
623
+ model: active_model(&session.config.ai_provider, &session.config).to_owned(),
624
+ thinking_status: format_thinking_status(&session.config),
625
+ provider: format_provider_display_name(&session.config.ai_provider, &session.config),
626
+ workspace: format_workspace_with_branch(&session.current_dir),
627
+ current_dir: session.current_dir.clone(),
628
+ theme: crate::terminal_theme::TuiTheme::from_config(&session.config.tui_theme),
629
+ plan_mode: session.plan_mode,
630
+ history: session.history.clone(),
631
+ ..Self::default()
632
+ };
633
+ state.reload_transcript(&session.chat_id, &session.current_dir);
634
+ state
635
+ }
636
+ pub fn set_draft(&mut self, draft: String) {
637
+ self.composer = normalize_prompt_newlines(&draft).chars().collect();
638
+ self.collapsed_pastes.clear();
639
+ self.expanded_paste = None;
640
+ self.cursor = self.composer.len();
641
+ }
642
+ pub(super) fn composer_text(&self) -> String {
643
+ self.composer_text_range(0..self.composer.len())
644
+ }
645
+ pub(super) fn composer_text_range(&self, range: std::ops::Range<usize>) -> String {
646
+ let mut text = String::new();
647
+ let mut start = range.start;
648
+ for paste in self
649
+ .collapsed_pastes
650
+ .iter()
651
+ .filter(|paste| paste.range.start >= range.start && paste.range.end <= range.end)
652
+ {
653
+ text.extend(self.composer[start..paste.range.start].iter());
654
+ text.push_str(&paste.text);
655
+ start = paste.range.end;
656
+ }
657
+ text.extend(self.composer[start..range.end].iter());
658
+ text
659
+ }
660
+ pub(super) fn take_prompt(&mut self) -> String {
661
+ let text = self.composer_text();
662
+ self.composer.clear();
663
+ self.collapsed_pastes.clear();
664
+ self.expanded_paste = None;
665
+ self.cursor = 0;
666
+ text
667
+ }
668
+ pub(super) fn open_paste(&mut self) -> bool {
669
+ let Some(index) = self
670
+ .collapsed_pastes
671
+ .iter()
672
+ .enumerate()
673
+ .min_by_key(|(_, paste)| {
674
+ if self.cursor < paste.range.start {
675
+ paste.range.start - self.cursor
676
+ } else {
677
+ self.cursor.saturating_sub(paste.range.end)
678
+ }
679
+ })
680
+ .map(|(index, _)| index)
681
+ else {
682
+ return false;
683
+ };
684
+ self.expand_paste(index);
685
+ true
686
+ }
687
+ pub(super) fn expand_paste(&mut self, index: usize) {
688
+ let paste = self.collapsed_pastes.remove(index);
689
+ let start = paste.range.start;
690
+ self.replace_composer(paste.range, &paste.text);
691
+ self.expanded_paste = Some(start..self.cursor);
692
+ self.cursor = start;
693
+ }
694
+ pub(super) fn toggle_paste(&mut self) {
695
+ if let Some(range) = self.expanded_paste.take() {
696
+ let text = self.composer_text_range(range.clone());
697
+ let start = range.start;
698
+ let badge = format!("[Pasted Content {} chars]", text.chars().count());
699
+ self.replace_composer(range, &badge);
700
+ self.collapsed_pastes.push(CollapsedPaste {
701
+ range: start..self.cursor,
702
+ text,
703
+ });
704
+ self.collapsed_pastes.sort_by_key(|paste| paste.range.start);
705
+ } else {
706
+ self.open_paste();
707
+ }
708
+ }
709
+ pub(super) fn replace_composer(&mut self, mut range: std::ops::Range<usize>, text: &str) {
710
+ // Badges are atomic. Deleting any part removes the attached paste;
711
+ // inserting inside one opens it before editing the actual text.
712
+ if range.is_empty() {
713
+ if let Some(index) = self
714
+ .collapsed_pastes
715
+ .iter()
716
+ .position(|paste| range.start > paste.range.start && range.start < paste.range.end)
717
+ {
718
+ self.expand_paste(index);
719
+ range = self.cursor..self.cursor;
720
+ }
721
+ } else {
722
+ for paste in &self.collapsed_pastes {
723
+ if range.start < paste.range.end && range.end > paste.range.start {
724
+ range.start = range.start.min(paste.range.start);
725
+ range.end = range.end.max(paste.range.end);
726
+ }
727
+ }
728
+ }
729
+ let start = range.start;
730
+ let removed = range.end - start;
731
+ let added = text.chars().count();
732
+ self.collapsed_pastes.retain_mut(|paste| {
733
+ if start < paste.range.end && range.end > paste.range.start {
734
+ return false;
735
+ }
736
+ if paste.range.start >= range.end {
737
+ paste.range.start = paste.range.start - removed + added;
738
+ paste.range.end = paste.range.end - removed + added;
739
+ }
740
+ true
741
+ });
742
+ // Keep the open block's bounds attached to edits, including insertions
743
+ // at either edge. Fully deleting it must also remove the F2 target.
744
+ if let Some(open) = self.expanded_paste.as_mut() {
745
+ if !range.is_empty() && start <= open.start && range.end >= open.end {
746
+ self.expanded_paste = None;
747
+ } else {
748
+ if open.start > range.end || (!range.is_empty() && open.start == range.end) {
749
+ open.start = open.start - removed + added;
750
+ } else if open.start >= start {
751
+ open.start = start;
752
+ }
753
+ if open.end >= range.end {
754
+ open.end = open.end - removed + added;
755
+ } else if open.end >= start {
756
+ open.end = start + added;
757
+ }
758
+ }
759
+ }
760
+ self.composer.splice(range, text.chars());
761
+ self.cursor = start + added;
762
+ self.slash_selected = 0;
763
+ }
764
+ pub(super) fn edit_composer_key(&mut self, key: event::KeyEvent) -> bool {
765
+ use event::{KeyCode, KeyModifiers};
766
+ match key.code {
767
+ KeyCode::F(2) => {
768
+ self.toggle_paste();
769
+ }
770
+ KeyCode::Left => {
771
+ self.cursor = self
772
+ .collapsed_pastes
773
+ .iter()
774
+ .find(|paste| self.cursor > paste.range.start && self.cursor <= paste.range.end)
775
+ .map_or(self.cursor.saturating_sub(1), |paste| paste.range.start);
776
+ }
777
+ KeyCode::Right => {
778
+ self.cursor = self
779
+ .collapsed_pastes
780
+ .iter()
781
+ .find(|paste| self.cursor >= paste.range.start && self.cursor < paste.range.end)
782
+ .map_or((self.cursor + 1).min(self.composer.len()), |paste| {
783
+ paste.range.end
784
+ });
785
+ }
786
+ KeyCode::Backspace if self.cursor > 0 => {
787
+ self.replace_composer(self.cursor - 1..self.cursor, "");
788
+ }
789
+ KeyCode::Delete if self.cursor < self.composer.len() => {
790
+ self.replace_composer(self.cursor..self.cursor + 1, "");
791
+ }
792
+ KeyCode::Enter if key.modifiers.contains(KeyModifiers::ALT) => {
793
+ self.replace_composer(self.cursor..self.cursor, "\n");
794
+ }
795
+ KeyCode::Char(character)
796
+ if !key
797
+ .modifiers
798
+ .intersects(KeyModifiers::CONTROL | KeyModifiers::ALT) =>
799
+ {
800
+ if self.active_notice().is_some_and(|notice| {
801
+ !notice.starts_with("Terminal too small") && !notice.starts_with("[Mode: Edit")
802
+ }) {
803
+ self.clear_notice();
804
+ }
805
+ self.replace_composer(self.cursor..self.cursor, &character.to_string());
806
+ }
807
+ _ => return false,
808
+ }
809
+ true
810
+ }
811
+ pub(super) fn insert_paste(&mut self, text: &str) {
812
+ let text = normalize_prompt_newlines(text);
813
+ if let Some(index) = self
814
+ .collapsed_pastes
815
+ .iter()
816
+ .position(|paste| self.cursor > paste.range.start && self.cursor < paste.range.end)
817
+ {
818
+ self.expand_paste(index);
819
+ }
820
+ let cursor = self.cursor.min(self.composer.len());
821
+ if text.lines().count() > 6 {
822
+ let badge = format!("[Pasted Content {} chars]", text.chars().count());
823
+ self.replace_composer(cursor..cursor, &badge);
824
+ self.collapsed_pastes.push(CollapsedPaste {
825
+ range: cursor..self.cursor,
826
+ text: text.clone(),
827
+ });
828
+ self.collapsed_pastes.sort_by_key(|paste| paste.range.start);
829
+ } else {
830
+ self.replace_composer(cursor..cursor, &text);
831
+ }
832
+ self.slash_selected = 0;
833
+ }
834
+ pub(super) fn composer_width(&self, area: Rect) -> usize {
835
+ let (main, _) = mod_layout(
836
+ area,
837
+ self.mods_open && self.dialog.is_none() && self.thought_modal.is_none(),
838
+ );
839
+ main.width.saturating_sub(4).max(1) as usize
840
+ }
841
+ pub(super) fn navigate_composer(&mut self, key: event::KeyEvent, row_width: usize) -> bool {
842
+ use event::{KeyCode, KeyModifiers};
843
+ if key
844
+ .modifiers
845
+ .intersects(KeyModifiers::CONTROL | KeyModifiers::ALT)
846
+ {
847
+ return false;
848
+ }
849
+ // Honor the transcript's visible End shortcut before draft navigation.
850
+ if key.code == KeyCode::End && self.scroll_from_bottom > 0 {
851
+ self.scroll_from_bottom = 0;
852
+ return true;
853
+ }
854
+ if self.composer.is_empty() {
855
+ return false;
856
+ }
857
+ let (rows, row, col) = wrap_input_visual_into_rows(&self.composer, row_width, self.cursor);
858
+ let target_row = match key.code {
859
+ KeyCode::Up if rows.len() > 1 => row.saturating_sub(1),
860
+ KeyCode::Down if rows.len() > 1 => (row + 1).min(rows.len() - 1),
861
+ KeyCode::Home | KeyCode::End => row,
862
+ _ => return false,
863
+ };
864
+ let start = input_row_start(&self.composer, &rows, target_row);
865
+ let target: Vec<char> = rows[target_row].chars().collect();
866
+ self.cursor = match key.code {
867
+ KeyCode::Home => start,
868
+ KeyCode::End => start + target.len(),
869
+ _ => {
870
+ let column = UnicodeWidthStr::width(
871
+ rows[row].chars().take(col).collect::<String>().as_str(),
872
+ );
873
+ let mut width = 0;
874
+ let mut count = 0;
875
+ for character in target {
876
+ let next = UnicodeWidthStr::width(character.to_string().as_str());
877
+ if width + next > column {
878
+ break;
879
+ }
880
+ width += next;
881
+ count += 1;
882
+ }
883
+ start + count
884
+ }
885
+ };
886
+ true
887
+ }
888
+ pub fn reload_transcript(&mut self, chat_id: &str, workspace: &Path) {
889
+ if self.chat_id != chat_id || self.current_dir != workspace {
890
+ self.stop_mods();
891
+ }
892
+ if let Ok(memory) = mint_core::MemoryStore::open_default() {
893
+ self.reload_transcript_with(&memory, chat_id, workspace);
894
+ } else {
895
+ self.chat_id = chat_id.to_owned();
896
+ self.transcript.clear();
897
+ self.tools.clear();
898
+ if self
899
+ .dialog
900
+ .as_ref()
901
+ .is_some_and(|dialog| dialog.is_tool_details())
902
+ {
903
+ self.dialog = None;
904
+ }
905
+ self.thoughts.clear();
906
+ self.thought_modal = None;
907
+ self.tool_viewer = None;
908
+ self.assistant_pacer = None;
909
+ self.active_assistant_entry = None;
910
+ self.active_plan_entry = None;
911
+ self.pending_post_assistant_notices.clear();
912
+ self.last_interaction_id = 0;
913
+ self.sync_cursor = 0;
914
+ }
915
+ }
916
+
917
+ pub(super) fn push_saved_activity(
918
+ &mut self,
919
+ activity: Option<&serde_json::Value>,
920
+ turn_status: &str,
921
+ ) -> Vec<(String, String)> {
922
+ let mut sources = Vec::new();
923
+ let Some(events) = activity.and_then(serde_json::Value::as_array) else {
924
+ return sources;
925
+ };
926
+ self.active_plan_entry = None;
927
+ let first_tool = self.tools.len();
928
+ for value in events {
929
+ let Ok(event) = serde_json::from_value::<mint_core::AgentProgress>(value.clone())
930
+ else {
931
+ continue;
932
+ };
933
+ // In-flight external turns use the shared-sync placeholder until a terminal snapshot arrives.
934
+ if !matches!(turn_status, "running" | "queued") {
935
+ match &event {
936
+ mint_core::AgentProgress::ToolStart { action, .. } => {
937
+ if action != "exit_plan_mode" {
938
+ self.record_tool_progress(&event);
939
+ }
940
+ }
941
+ mint_core::AgentProgress::ToolEnd {
942
+ action,
943
+ input,
944
+ result,
945
+ ..
946
+ } => {
947
+ if action == "exit_plan_mode" {
948
+ let plan_str = input
949
+ .get("plan")
950
+ .and_then(|p| p.as_str())
951
+ .or_else(|| value["data"]["input"]["plan"].as_str());
952
+ if let Some(plan) = plan_str {
953
+ if !plan.trim().is_empty() {
954
+ self.push_plan(plan);
955
+ }
956
+ }
957
+ } else {
958
+ if let Some(index) = self.record_tool_progress(&event) {
959
+ self.tools[index].elapsed = value["data"]["elapsed_ms"]
960
+ .as_u64()
961
+ .or_else(|| value["data"]["elapsedMs"].as_u64())
962
+ .map(std::time::Duration::from_millis);
963
+ self.refresh_tool_card(index);
964
+ }
965
+ if action == "web_search"
966
+ && !result.starts_with("Web search error:")
967
+ && result != "No web search results found."
968
+ {
969
+ sources.extend(crate::agent::parse_web_search_sources(result));
970
+ }
971
+ }
972
+ }
973
+ mint_core::AgentProgress::SubagentStarted { .. }
974
+ | mint_core::AgentProgress::SubagentFinished { .. } => {
975
+ self.record_tool_progress(&event);
976
+ }
977
+ mint_core::AgentProgress::ExtendedThinking {
978
+ id,
979
+ thought,
980
+ elapsed_ms,
981
+ } if !thought.trim().is_empty() => {
982
+ self.thoughts.push(ThinkingSegment {
983
+ id: id.clone(),
984
+ text: thought.clone(),
985
+ elapsed: std::time::Duration::from_millis(elapsed_ms.unwrap_or(0)),
986
+ elapsed_known: elapsed_ms.is_some(),
987
+ updated_at: None,
988
+ });
989
+ let elapsed = self.thoughts.last().unwrap().elapsed_label();
990
+ self.push_notice(format!("Thought · {elapsed} (Ctrl+T to view)"));
991
+ }
992
+ mint_core::AgentProgress::Thought { thought } if !thought.trim().is_empty() => {
993
+ if mint_core::orchestration::is_internal_cot(thought) {
994
+ self.thoughts.push(ThinkingSegment {
995
+ id: None,
996
+ text: thought.clone(),
997
+ elapsed: std::time::Duration::ZERO,
998
+ elapsed_known: false,
999
+ updated_at: None,
1000
+ });
1001
+ self.push_notice("Thought · time unavailable (Ctrl+T to view)");
1002
+ } else {
1003
+ self.push_notice(thought.clone());
1004
+ }
1005
+ }
1006
+ mint_core::AgentProgress::RunCompleted { summary } => {
1007
+ self.push_notice(crate::agent::run_summary_lines(summary).join("\n"));
1008
+ }
1009
+ mint_core::AgentProgress::PlanUpdated { plan } => {
1010
+ self.upsert_active_plan_card(plan);
1011
+ }
1012
+ _ => {}
1013
+ }
1014
+ }
1015
+ // Suppress redundant ExecutionUpdate notices in TUI (handled by ToolActivity cards)
1016
+ if !matches!(event, mint_core::AgentProgress::ExecutionUpdate { .. }) {
1017
+ if let Some(notes) = crate::agent::mcp_progress_notes(&event) {
1018
+ for note in notes {
1019
+ self.push_notice(note);
1020
+ }
1021
+ }
1022
+ }
1023
+ }
1024
+ for index in first_tool..self.tools.len() {
1025
+ if self.tools[index].running() {
1026
+ self.tools[index].finish(
1027
+ "Interrupted before a result was recorded.",
1028
+ Some(mint_core::integrations::mcp_result::ToolStatus::Cancelled),
1029
+ );
1030
+ self.tools[index].elapsed = None;
1031
+ self.refresh_tool_card(index);
1032
+ }
1033
+ }
1034
+ sources
1035
+ }
1036
+
1037
+ pub(super) fn append_interaction_row(&mut self, row: &mint_core::InteractionMemory) {
1038
+ let user_display = format_user_transcript_text(&row.user_text);
1039
+ self.transcript
1040
+ .push(TranscriptEntry::new(TranscriptRole::User, user_display));
1041
+ let sources = self.push_saved_activity(row.agent_activity.as_ref(), &row.status);
1042
+ match row.status.as_str() {
1043
+ "completed" => {
1044
+ self.transcript.push(TranscriptEntry::new(
1045
+ TranscriptRole::Assistant,
1046
+ &row.ai_text,
1047
+ ));
1048
+ if !sources.is_empty() {
1049
+ let text = crate::agent::format_tui_web_sources(
1050
+ &sources,
1051
+ crate::agent::MAX_TUI_WEB_SOURCES,
1052
+ );
1053
+ if !text.is_empty() {
1054
+ self.transcript
1055
+ .push(TranscriptEntry::new(TranscriptRole::Notice, text));
1056
+ }
1057
+ }
1058
+ }
1059
+ "queued" => self.transcript.push(TranscriptEntry::new(
1060
+ TranscriptRole::Notice,
1061
+ "Queued for this session…",
1062
+ )),
1063
+ "running" => self.transcript.push(TranscriptEntry::new(
1064
+ TranscriptRole::Notice,
1065
+ "Mint is responding…",
1066
+ )),
1067
+ "failed" => self.transcript.push(TranscriptEntry::new(
1068
+ TranscriptRole::Notice,
1069
+ "This turn failed. Send it again to retry.",
1070
+ )),
1071
+ _ => self.transcript.push(TranscriptEntry::new(
1072
+ TranscriptRole::Notice,
1073
+ "This turn was interrupted. Send it again to retry.",
1074
+ )),
1075
+ }
1076
+ }
1077
+
1078
+ pub fn reload_transcript_with(
1079
+ &mut self,
1080
+ memory: &mint_core::MemoryStore,
1081
+ chat_id: &str,
1082
+ workspace: &Path,
1083
+ ) {
1084
+ if self.chat_id != chat_id || self.current_dir != workspace {
1085
+ self.stop_mods();
1086
+ }
1087
+ self.chat_id = chat_id.to_owned();
1088
+ self.transcript.clear();
1089
+ self.tools.clear();
1090
+ if self
1091
+ .dialog
1092
+ .as_ref()
1093
+ .is_some_and(|dialog| dialog.is_tool_details())
1094
+ {
1095
+ self.dialog = None;
1096
+ }
1097
+ self.thoughts.clear();
1098
+ self.thought_modal = None;
1099
+ self.tool_viewer = None;
1100
+ self.assistant_pacer = None;
1101
+ self.active_assistant_entry = None;
1102
+ self.active_plan_entry = None;
1103
+ self.pending_post_assistant_notices.clear();
1104
+ let scoped = mint_core::scoped_chat_id(chat_id, Some(&workspace.to_string_lossy()));
1105
+ self.sync_cursor = memory.latest_conversation_sequence(&scoped).unwrap_or(0);
1106
+ if let Ok(rows) = memory.interactions_for_chat(&scoped) {
1107
+ self.last_interaction_id = rows.last().map(|row| row.id).unwrap_or(0);
1108
+ for row in &rows {
1109
+ self.append_interaction_row(row);
1110
+ }
1111
+ } else {
1112
+ self.last_interaction_id = 0;
1113
+ }
1114
+ }
1115
+
1116
+ pub fn advance_sync_cursor(&mut self) {
1117
+ if let Ok(memory) = mint_core::MemoryStore::open_default() {
1118
+ self.advance_sync_cursor_with(&memory);
1119
+ }
1120
+ }
1121
+
1122
+ pub fn advance_sync_cursor_with(&mut self, memory: &mint_core::MemoryStore) {
1123
+ if self.chat_id.is_empty() {
1124
+ return;
1125
+ }
1126
+ let scoped =
1127
+ mint_core::scoped_chat_id(&self.chat_id, Some(&self.current_dir.to_string_lossy()));
1128
+ if let Ok(seq) = memory.latest_conversation_sequence(&scoped) {
1129
+ self.sync_cursor = seq;
1130
+ }
1131
+ if let Ok(rows) = memory.interactions_for_chat(&scoped) {
1132
+ if let Some(last) = rows.last() {
1133
+ self.last_interaction_id = last.id;
1134
+ }
1135
+ }
1136
+ }
1137
+
1138
+ pub(super) fn refresh_shared_transcript(&mut self) -> bool {
1139
+ if self.chat_id.is_empty() {
1140
+ return false;
1141
+ }
1142
+ let Ok(memory) = mint_core::MemoryStore::open_default() else {
1143
+ return false;
1144
+ };
1145
+ self.refresh_shared_transcript_with(&memory)
1146
+ }
1147
+
1148
+ pub fn refresh_shared_transcript_with(&mut self, memory: &mint_core::MemoryStore) -> bool {
1149
+ if self.chat_id.is_empty() {
1150
+ return false;
1151
+ }
1152
+ let scoped =
1153
+ mint_core::scoped_chat_id(&self.chat_id, Some(&self.current_dir.to_string_lossy()));
1154
+ let Ok(changes) = memory.conversation_changes(&scoped, self.sync_cursor, 200) else {
1155
+ return false;
1156
+ };
1157
+ if changes.changes.is_empty() {
1158
+ return false;
1159
+ }
1160
+
1161
+ // If any interaction was deleted (e.g. /clear on Web or Desktop), do a full reload.
1162
+ if changes
1163
+ .changes
1164
+ .iter()
1165
+ .any(|change| change.interaction.is_none())
1166
+ {
1167
+ let chat_id = self.chat_id.clone();
1168
+ let workspace = self.current_dir.clone();
1169
+ self.reload_transcript_with(memory, &chat_id, &workspace);
1170
+ return true;
1171
+ }
1172
+
1173
+ let mut changed = false;
1174
+ for change in &changes.changes {
1175
+ let Some(row) = &change.interaction else {
1176
+ continue;
1177
+ };
1178
+ if row.id > self.last_interaction_id {
1179
+ // Incoming new interaction from an external client (Web/Desktop)
1180
+ self.append_interaction_row(row);
1181
+ self.last_interaction_id = row.id;
1182
+ changed = true;
1183
+ } else if row.id == self.last_interaction_id {
1184
+ // Status update for an external interaction that was previously queued/running
1185
+ if row.status == "completed" {
1186
+ let mut append = false;
1187
+ if let Some(last) = self.transcript.last() {
1188
+ if last.role == TranscriptRole::Notice
1189
+ && (last.text.starts_with("Mint is responding")
1190
+ || last.text.starts_with("Queued for this session"))
1191
+ {
1192
+ self.transcript.pop();
1193
+ append = true;
1194
+ } else if last.role != TranscriptRole::Assistant {
1195
+ append = true;
1196
+ }
1197
+ }
1198
+ if append {
1199
+ let sources =
1200
+ self.push_saved_activity(row.agent_activity.as_ref(), &row.status);
1201
+ self.transcript.push(TranscriptEntry::new(
1202
+ TranscriptRole::Assistant,
1203
+ &row.ai_text,
1204
+ ));
1205
+ if !sources.is_empty() {
1206
+ let text = crate::agent::format_tui_web_sources(
1207
+ &sources,
1208
+ crate::agent::MAX_TUI_WEB_SOURCES,
1209
+ );
1210
+ if !text.is_empty() {
1211
+ self.transcript
1212
+ .push(TranscriptEntry::new(TranscriptRole::Notice, text));
1213
+ }
1214
+ }
1215
+ changed = true;
1216
+ }
1217
+ } else if row.status == "running" {
1218
+ if let Some(last) = self.transcript.last_mut() {
1219
+ if last.role == TranscriptRole::Notice
1220
+ && last.text.starts_with("Queued for this session")
1221
+ {
1222
+ *last =
1223
+ TranscriptEntry::new(TranscriptRole::Notice, "Mint is responding…");
1224
+ changed = true;
1225
+ }
1226
+ }
1227
+ } else if matches!(row.status.as_str(), "failed" | "interrupted") {
1228
+ let msg = if row.status == "failed" {
1229
+ "This turn failed. Send it again to retry."
1230
+ } else {
1231
+ "This turn was interrupted. Send it again to retry."
1232
+ };
1233
+ if let Some(last) = self.transcript.last_mut() {
1234
+ if last.role == TranscriptRole::Notice
1235
+ && (last.text.starts_with("Mint is responding")
1236
+ || last.text.starts_with("Queued for this session"))
1237
+ {
1238
+ *last = TranscriptEntry::new(TranscriptRole::Notice, msg);
1239
+ changed = true;
1240
+ self.push_saved_activity(row.agent_activity.as_ref(), &row.status);
1241
+ }
1242
+ }
1243
+ }
1244
+ }
1245
+ }
1246
+
1247
+ self.sync_cursor = changes.cursor;
1248
+ changed
1249
+ }
1250
+ pub fn push_user(&mut self, text: String) {
1251
+ self.flush_assistant_reveal();
1252
+ self.transcript
1253
+ .push(TranscriptEntry::new(TranscriptRole::User, text));
1254
+ self.scroll_from_bottom = 0;
1255
+ self.swarm_agents.clear();
1256
+ self.active_plan_entry = None;
1257
+ }
1258
+ fn format_plan_checklist_card(plan: &mint_core::ActivePlan) -> String {
1259
+ if plan.tasks.is_empty() {
1260
+ return String::new();
1261
+ }
1262
+ let completed = plan
1263
+ .tasks
1264
+ .iter()
1265
+ .filter(|task| task.status == "completed")
1266
+ .count();
1267
+ let total = plan.tasks.len();
1268
+ let percent = (completed * 100) / total;
1269
+ let objective = if plan.objective.trim().is_empty() {
1270
+ "Implementation Plan"
1271
+ } else {
1272
+ plan.objective.trim()
1273
+ };
1274
+ let mut lines = Vec::with_capacity(plan.tasks.len() + 1);
1275
+ lines.push(format!(
1276
+ "{}📋 Plan: {}{} \x1b[90m· {completed}/{total} ({percent}%)\x1b[0m",
1277
+ crate::terminal_theme::ANSI_ACCENT_BOLD,
1278
+ objective,
1279
+ crate::terminal_theme::ANSI_RESET
1280
+ ));
1281
+ for task in &plan.tasks {
1282
+ let mark = match task.status.as_str() {
1283
+ "completed" => "\x1b[32m[✓]\x1b[0m",
1284
+ "in_progress" => "\x1b[33m[>]\x1b[0m",
1285
+ "failed" => "\x1b[31m[✗]\x1b[0m",
1286
+ _ => "\x1b[90m[ ]\x1b[0m",
1287
+ };
1288
+ lines.push(format!(" {} {}", mark, task.title));
1289
+ }
1290
+ lines.join("\n")
1291
+ }
1292
+ pub(super) fn upsert_active_plan_card(&mut self, plan: &mint_core::ActivePlan) {
1293
+ let text = Self::format_plan_checklist_card(plan);
1294
+ if text.is_empty() {
1295
+ return;
1296
+ }
1297
+ let entry = TranscriptEntry::new(TranscriptRole::Notice, text);
1298
+ if let Some(idx) = self.active_plan_entry
1299
+ && idx < self.transcript.len()
1300
+ {
1301
+ self.transcript[idx] = entry;
1302
+ } else if let Some(idx) = self.active_assistant_entry
1303
+ && idx <= self.transcript.len()
1304
+ {
1305
+ self.transcript.insert(idx, entry);
1306
+ self.active_plan_entry = Some(idx);
1307
+ self.active_assistant_entry = Some(idx + 1);
1308
+ for tool in &mut self.tools {
1309
+ if tool.entry >= idx {
1310
+ tool.entry += 1;
1311
+ }
1312
+ }
1313
+ } else {
1314
+ let idx = self.transcript.len();
1315
+ self.transcript.push(entry);
1316
+ self.active_plan_entry = Some(idx);
1317
+ }
1318
+ self.scroll_from_bottom = 0;
1319
+ }
1320
+ pub fn push_notice(&mut self, text: impl Into<String>) {
1321
+ let entry = TranscriptEntry::new(TranscriptRole::Notice, text);
1322
+ if let Some(idx) = self.active_assistant_entry
1323
+ && idx <= self.transcript.len()
1324
+ {
1325
+ self.transcript.insert(idx, entry);
1326
+ self.active_assistant_entry = Some(idx + 1);
1327
+ if let Some(plan_idx) = &mut self.active_plan_entry
1328
+ && *plan_idx >= idx
1329
+ {
1330
+ *plan_idx += 1;
1331
+ }
1332
+ for tool in &mut self.tools {
1333
+ if tool.entry >= idx {
1334
+ tool.entry += 1;
1335
+ }
1336
+ }
1337
+ } else {
1338
+ self.transcript.push(entry);
1339
+ }
1340
+ self.scroll_from_bottom = 0;
1341
+ }
1342
+ pub fn push_plan(&mut self, text: impl Into<String>) {
1343
+ self.transcript
1344
+ .push(TranscriptEntry::new(TranscriptRole::Plan, text));
1345
+ self.scroll_from_bottom = 0;
1346
+ }
1347
+ #[cfg(test)]
1348
+ pub fn push_assistant(&mut self, text: impl Into<String>) {
1349
+ self.flush_assistant_reveal();
1350
+ self.transcript
1351
+ .push(TranscriptEntry::new(TranscriptRole::Assistant, text));
1352
+ self.scroll_from_bottom = 0;
1353
+ }
1354
+ fn upsert_active_assistant_entry(&mut self, text: String) {
1355
+ if let Some(idx) = self.active_assistant_entry
1356
+ && idx < self.transcript.len()
1357
+ && self.transcript[idx].role == TranscriptRole::Assistant
1358
+ {
1359
+ self.transcript[idx] = TranscriptEntry::new(TranscriptRole::Assistant, text);
1360
+ } else {
1361
+ let idx = self.transcript.len();
1362
+ self.transcript
1363
+ .push(TranscriptEntry::new(TranscriptRole::Assistant, text));
1364
+ self.active_assistant_entry = Some(idx);
1365
+ }
1366
+ }
1367
+ pub fn append_assistant_delta(&mut self, delta: &str) {
1368
+ if delta.is_empty() {
1369
+ return;
1370
+ }
1371
+ let pacer = self
1372
+ .assistant_pacer
1373
+ .get_or_insert_with(crate::agent::ProgressiveMarkdownPacer::default);
1374
+ pacer.push_delta(delta);
1375
+ if self.active_assistant_entry.is_none() {
1376
+ self.tick_assistant_reveal();
1377
+ } else {
1378
+ self.tick_assistant_reveal_if_due(std::time::Duration::from_millis(18));
1379
+ }
1380
+ }
1381
+ pub fn begin_assistant_reveal(&mut self, summary: String, post_notices: Vec<String>) {
1382
+ let pacer = self
1383
+ .assistant_pacer
1384
+ .get_or_insert_with(crate::agent::ProgressiveMarkdownPacer::default);
1385
+ pacer.finish_with_authoritative(&summary);
1386
+ self.pending_post_assistant_notices = post_notices;
1387
+ self.tick_assistant_reveal();
1388
+ }
1389
+ pub fn tick_assistant_reveal(&mut self) -> bool {
1390
+ let Some(mut pacer) = self.assistant_pacer.take() else {
1391
+ return false;
1392
+ };
1393
+ let revealed = pacer.step();
1394
+ let done = pacer.is_done();
1395
+ let mut changed = false;
1396
+ if let Some(text) = revealed {
1397
+ self.upsert_active_assistant_entry(text);
1398
+ self.last_assistant_tick = Some(std::time::Instant::now());
1399
+ changed = true;
1400
+ }
1401
+ if done {
1402
+ self.active_assistant_entry = None;
1403
+ self.last_assistant_tick = None;
1404
+ let notices = std::mem::take(&mut self.pending_post_assistant_notices);
1405
+ for notice in notices {
1406
+ self.transcript
1407
+ .push(TranscriptEntry::new(TranscriptRole::Notice, notice));
1408
+ changed = true;
1409
+ }
1410
+ } else {
1411
+ self.assistant_pacer = Some(pacer);
1412
+ }
1413
+ changed
1414
+ }
1415
+ pub(super) fn tick_assistant_reveal_if_due(
1416
+ &mut self,
1417
+ min_interval: std::time::Duration,
1418
+ ) -> bool {
1419
+ if self.assistant_pacer.is_none() {
1420
+ return false;
1421
+ }
1422
+ if self
1423
+ .last_assistant_tick
1424
+ .is_some_and(|last| last.elapsed() < min_interval)
1425
+ {
1426
+ return false;
1427
+ }
1428
+ self.tick_assistant_reveal()
1429
+ }
1430
+ pub fn flush_assistant_reveal(&mut self) {
1431
+ if let Some(mut pacer) = self.assistant_pacer.take()
1432
+ && let Some(text) = pacer.flush_all()
1433
+ {
1434
+ self.upsert_active_assistant_entry(text);
1435
+ }
1436
+ self.active_assistant_entry = None;
1437
+ self.last_assistant_tick = None;
1438
+ let notices = std::mem::take(&mut self.pending_post_assistant_notices);
1439
+ for notice in notices {
1440
+ self.transcript
1441
+ .push(TranscriptEntry::new(TranscriptRole::Notice, notice));
1442
+ }
1443
+ }
1444
+ pub fn has_pending_assistant_reveal(&self) -> bool {
1445
+ self.assistant_pacer.is_some()
1446
+ }
1447
+ pub(super) fn has_unfinished_assistant_stream(&self) -> bool {
1448
+ self.assistant_pacer
1449
+ .as_ref()
1450
+ .is_some_and(|pacer| !pacer.is_completed())
1451
+ }
1452
+ pub fn clear_transcript(&mut self) {
1453
+ self.transcript.clear();
1454
+ self.tools.clear();
1455
+ self.swarm_agents.clear();
1456
+ self.assistant_pacer = None;
1457
+ self.active_assistant_entry = None;
1458
+ self.active_plan_entry = None;
1459
+ self.pending_post_assistant_notices.clear();
1460
+ if self
1461
+ .dialog
1462
+ .as_ref()
1463
+ .is_some_and(|dialog| dialog.is_tool_details())
1464
+ {
1465
+ self.dialog = None;
1466
+ }
1467
+ self.thoughts.clear();
1468
+ self.thought_modal = None;
1469
+ self.tool_viewer = None;
1470
+ self.scroll_from_bottom = 0;
1471
+ self.selection_mode = false;
1472
+ }
1473
+ pub fn sync_session(&mut self, session: &InteractiveSession) {
1474
+ if self.current_dir != session.current_dir {
1475
+ self.stop_mods();
1476
+ }
1477
+ self.model = active_model(&session.config.ai_provider, &session.config).to_owned();
1478
+ self.thinking_status = format_thinking_status(&session.config);
1479
+ self.provider = format_provider_display_name(&session.config.ai_provider, &session.config);
1480
+ self.workspace = format_workspace_with_branch(&session.current_dir);
1481
+ self.current_dir = session.current_dir.clone();
1482
+ self.plan_mode = session.plan_mode;
1483
+ self.history = session.history.clone();
1484
+ }
1485
+ pub(super) fn stop_mods(&mut self) {
1486
+ if !self.chat_id.is_empty() {
1487
+ let chat =
1488
+ mint_core::scoped_chat_id(&self.chat_id, Some(&self.current_dir.to_string_lossy()));
1489
+ mint_core::mods::close_chat(&self.current_dir, &chat);
1490
+ }
1491
+ self.mods.clear();
1492
+ self.mods_open = false;
1493
+ }
1494
+ pub(super) fn mod_input_value<'a>(&'a self, control: &'a crate::mods_ui::Control) -> &'a str {
1495
+ self.mod_input
1496
+ .as_ref()
1497
+ .filter(|(initial, _)| initial == &control.value)
1498
+ .map_or(&control.value, |(_, draft)| draft)
1499
+ }
1500
+ pub(super) fn mod_input_draft(&mut self, control: &crate::mods_ui::Control) -> &mut String {
1501
+ if self
1502
+ .mod_input
1503
+ .as_ref()
1504
+ .is_none_or(|(initial, _)| initial != &control.value)
1505
+ {
1506
+ self.mod_input = Some((control.value.clone(), control.value.clone()));
1507
+ }
1508
+ &mut self.mod_input.as_mut().unwrap().1
1509
+ }
1510
+ pub(super) fn history_previous(&mut self) {
1511
+ if self.history.is_empty() {
1512
+ return;
1513
+ }
1514
+ if self.history_index.is_none() {
1515
+ self.draft_before_history = self.composer_text().chars().collect();
1516
+ }
1517
+ let index = self
1518
+ .history_index
1519
+ .map(|index| index.saturating_sub(1))
1520
+ .unwrap_or(self.history.len() - 1);
1521
+ self.history_index = Some(index);
1522
+ self.set_draft(self.history[index].clone());
1523
+ }
1524
+ pub(super) fn history_next(&mut self) {
1525
+ let Some(index) = self.history_index else {
1526
+ return;
1527
+ };
1528
+ if index + 1 < self.history.len() {
1529
+ let next = index + 1;
1530
+ self.history_index = Some(next);
1531
+ self.set_draft(self.history[next].clone());
1532
+ } else {
1533
+ self.history_index = None;
1534
+ self.composer = std::mem::take(&mut self.draft_before_history);
1535
+ }
1536
+ self.cursor = self.composer.len();
1537
+ }
1538
+ pub(super) fn suggestions(&self) -> Vec<(String, String)> {
1539
+ match_composer_suggestions(&self.composer, self.cursor, &self.current_dir)
1540
+ }
1541
+ pub(super) fn apply_selected_suggestion(&mut self) -> bool {
1542
+ let suggestions = self.suggestions();
1543
+ let Some((token, _)) = suggestions.get(self.slash_selected % suggestions.len().max(1))
1544
+ else {
1545
+ return false;
1546
+ };
1547
+ let cursor = self.cursor.min(self.composer.len());
1548
+ let start = if token.starts_with('@') {
1549
+ self.composer[..cursor]
1550
+ .iter()
1551
+ .rposition(|c| c.is_whitespace())
1552
+ .map_or(0, |index| index + 1)
1553
+ } else {
1554
+ 0
1555
+ };
1556
+ let word_end = self.composer[cursor..]
1557
+ .iter()
1558
+ .position(|c| c.is_whitespace())
1559
+ .map_or(self.composer.len(), |offset| cursor + offset);
1560
+ let paste_start = self
1561
+ .collapsed_pastes
1562
+ .iter()
1563
+ .find(|paste| paste.range.start >= cursor)
1564
+ .map(|paste| paste.range.start);
1565
+ let end = paste_start.map_or(word_end, |start| word_end.min(start));
1566
+ let needs_space = end == self.composer.len() || paste_start == Some(end);
1567
+ let replacement = if needs_space {
1568
+ format!("{token} ")
1569
+ } else {
1570
+ token.clone()
1571
+ };
1572
+ self.replace_composer(start..end, &replacement);
1573
+ if !needs_space {
1574
+ self.cursor += 1;
1575
+ }
1576
+ self.slash_selected = 0;
1577
+ true
1578
+ }
1579
+ pub fn set_notice(&mut self, text: impl Into<String>) {
1580
+ self.notice = Some((text.into(), std::time::Instant::now()));
1581
+ }
1582
+ pub fn clear_notice(&mut self) {
1583
+ self.notice = None;
1584
+ }
1585
+ pub fn active_notice(&self) -> Option<&str> {
1586
+ self.notice.as_ref().and_then(|(text, created)| {
1587
+ if text.starts_with("Terminal too small")
1588
+ || self.editing_turn.is_some()
1589
+ || created.elapsed() < std::time::Duration::from_millis(2500)
1590
+ {
1591
+ Some(text.as_str())
1592
+ } else {
1593
+ None
1594
+ }
1595
+ })
1596
+ }
1597
+
1598
+ pub(super) fn enter_edit_turn(&mut self) -> bool {
1599
+ let scoped =
1600
+ mint_core::scoped_chat_id(&self.chat_id, Some(&self.current_dir.to_string_lossy()));
1601
+ let Ok(memory) = mint_core::MemoryStore::open_default() else {
1602
+ self.set_notice("Failed to open conversation memory");
1603
+ return false;
1604
+ };
1605
+ let Ok(rows) = memory.interactions_for_chat(&scoped) else {
1606
+ self.set_notice("No previous turn found to edit or retry");
1607
+ return false;
1608
+ };
1609
+ if let Some(last) = rows.last() {
1610
+ let turn_num = rows.len();
1611
+ self.editing_turn = Some((last.id, turn_num));
1612
+ self.set_draft(clean_user_prompt(&last.user_text));
1613
+ self.cursor = self.composer.len();
1614
+ self.set_notice(format!(
1615
+ "[Mode: Edit Turn #{} — Resending will replace this turn and clear its previous response | Esc to cancel]",
1616
+ turn_num
1617
+ ));
1618
+ true
1619
+ } else {
1620
+ self.set_notice("No previous turn found to edit or retry");
1621
+ false
1622
+ }
1623
+ }
1624
+
1625
+ pub(super) fn cancel_edit_turn(&mut self) {
1626
+ self.editing_turn = None;
1627
+ self.take_prompt();
1628
+ self.clear_notice();
1629
+ }
1630
+ pub(super) fn notice_visibility_changed_since_draw(&self, painted_visible: bool) -> bool {
1631
+ painted_visible != self.active_notice().is_some()
1632
+ }
1633
+ pub(super) fn transcript_text(&self, width: u16) -> Text<'static> {
1634
+ let mut lines = Vec::new();
1635
+ let selected_start = self.selection_anchor.min(self.selection_head);
1636
+ let selected_end = self.selection_anchor.max(self.selection_head);
1637
+ let mut line_index = 0usize;
1638
+ for (entry_index, entry) in self.transcript.iter().enumerate() {
1639
+ let role_header = match entry.role {
1640
+ TranscriptRole::User => None,
1641
+ TranscriptRole::Assistant => Some(("Mint", crate::terminal_theme::ACCENT)),
1642
+ TranscriptRole::Notice => None,
1643
+ TranscriptRole::Command => Some(("Command", crate::terminal_theme::ACCENT)),
1644
+ TranscriptRole::ReleaseNotes | TranscriptRole::Plan => None,
1645
+ TranscriptRole::System => Some(("System", crate::terminal_theme::WARNING)),
1646
+ };
1647
+ if let Some((label, color)) = role_header {
1648
+ let selected =
1649
+ self.selection_mode && (selected_start..=selected_end).contains(&line_index);
1650
+ let header = Line::from(Span::styled(
1651
+ format!("{label} ›"),
1652
+ Style::default()
1653
+ .fg(color)
1654
+ .add_modifier(Modifier::BOLD)
1655
+ .add_modifier(if selected {
1656
+ Modifier::REVERSED
1657
+ } else {
1658
+ Modifier::empty()
1659
+ }),
1660
+ ));
1661
+ lines.push(header);
1662
+ line_index += 1;
1663
+ }
1664
+ let tool_lines = self
1665
+ .tools
1666
+ .iter()
1667
+ .find(|tool| tool.entry == entry_index)
1668
+ .map(|tool| tool.lines(width));
1669
+ let reflowed_entry_lines = entry.lines_for_width(width);
1670
+ let empty_user_line = [Line::default()];
1671
+ let content_lines = if let Some(lines) = tool_lines.as_ref() {
1672
+ lines.as_slice()
1673
+ } else if entry.role == TranscriptRole::User && reflowed_entry_lines.is_empty() {
1674
+ &empty_user_line[..]
1675
+ } else {
1676
+ reflowed_entry_lines.as_ref()
1677
+ };
1678
+ for (content_index, content_line) in content_lines.iter().enumerate() {
1679
+ let selected =
1680
+ self.selection_mode && (selected_start..=selected_end).contains(&line_index);
1681
+ let mut sel_line = content_line.clone();
1682
+ if entry.role == TranscriptRole::User && content_index == 0 {
1683
+ sel_line.spans.insert(
1684
+ 0,
1685
+ Span::styled(
1686
+ "You › ",
1687
+ Style::default()
1688
+ .fg(crate::terminal_theme::BLUE)
1689
+ .add_modifier(Modifier::BOLD),
1690
+ ),
1691
+ );
1692
+ }
1693
+ if entry.role == TranscriptRole::Notice {
1694
+ for span in &mut sel_line.spans {
1695
+ if span.style.fg.is_none() {
1696
+ span.style = span.style.fg(crate::terminal_theme::TEXT);
1697
+ }
1698
+ }
1699
+ }
1700
+ // Short AI text replies have no explicit ANSI color — default
1701
+ // them to plain white so they don't appear in the terminal's
1702
+ // dim default foreground.
1703
+ if entry.role == TranscriptRole::Assistant {
1704
+ for span in &mut sel_line.spans {
1705
+ if span.style.fg.is_none() {
1706
+ span.style = span.style.fg(crate::terminal_theme::TEXT);
1707
+ }
1708
+ }
1709
+ }
1710
+ if selected {
1711
+ for span in &mut sel_line.spans {
1712
+ span.style = span.style.add_modifier(Modifier::REVERSED);
1713
+ }
1714
+ }
1715
+ if entry.role == TranscriptRole::User {
1716
+ lines.extend(highlighted_user_content(sel_line, width));
1717
+ } else {
1718
+ lines.push(sel_line);
1719
+ }
1720
+ line_index += 1;
1721
+ }
1722
+ let selected =
1723
+ self.selection_mode && (selected_start..=selected_end).contains(&line_index);
1724
+ let mut style = Style::default();
1725
+ if selected {
1726
+ style = style.add_modifier(Modifier::REVERSED);
1727
+ }
1728
+ lines.push(Line::styled("", style));
1729
+ line_index += 1;
1730
+ }
1731
+ Text::from(lines)
1732
+ }
1733
+ pub(super) fn plain_transcript_lines(&self) -> Vec<String> {
1734
+ let mut lines = Vec::new();
1735
+ for entry in &self.transcript {
1736
+ if entry.role == TranscriptRole::User {
1737
+ let mut content = entry.plain_lines.iter();
1738
+ lines.push(format!(
1739
+ "You › {}",
1740
+ content.next().map_or("", String::as_str)
1741
+ ));
1742
+ lines.extend(content.cloned());
1743
+ lines.push(String::new());
1744
+ continue;
1745
+ }
1746
+ if let Some(header) = match entry.role {
1747
+ TranscriptRole::User => None,
1748
+ TranscriptRole::Assistant => Some("Mint ›"),
1749
+ TranscriptRole::Notice => None,
1750
+ TranscriptRole::Command => Some("Command ›"),
1751
+ TranscriptRole::ReleaseNotes | TranscriptRole::Plan => None,
1752
+ TranscriptRole::System => Some("System ›"),
1753
+ } {
1754
+ lines.push(header.to_owned());
1755
+ }
1756
+ lines.extend(entry.plain_lines.iter().cloned());
1757
+ lines.push(String::new());
1758
+ }
1759
+ lines
1760
+ }
1761
+ pub(super) fn selected_text(&self) -> String {
1762
+ let lines = self.plain_transcript_lines();
1763
+ if lines.is_empty() {
1764
+ return String::new();
1765
+ }
1766
+ let start = self
1767
+ .selection_anchor
1768
+ .min(self.selection_head)
1769
+ .min(lines.len() - 1);
1770
+ let end = self
1771
+ .selection_anchor
1772
+ .max(self.selection_head)
1773
+ .min(lines.len() - 1);
1774
+ lines[start..=end].join("\n")
1775
+ }
1776
+ pub(super) fn mod_viewport(&self, area: Rect) -> mint_core::mods::Viewport {
1777
+ let (body, _) = mod_layout(
1778
+ area,
1779
+ self.mods_open && self.dialog.is_none() && self.thought_modal.is_none(),
1780
+ );
1781
+ mint_core::mods::Viewport {
1782
+ surface: Some("terminal".into()),
1783
+ columns: (body.width > 0).then_some(body.width),
1784
+ rows: (body.height > 0).then_some(body.height),
1785
+ side_panel_open: self.mods_open,
1786
+ ..Default::default()
1787
+ }
1788
+ }
1789
+ }
1790
+
1791
+ pub(crate) fn format_user_transcript_text(raw: &str) -> String {
1792
+ if let Some(doc_idx) = raw.find("Attached document:") {
1793
+ let prefix = raw[..doc_idx].trim();
1794
+ let rest = &raw[doc_idx..];
1795
+ let filename = rest
1796
+ .strip_prefix("Attached document:")
1797
+ .and_then(|s| s.lines().next())
1798
+ .map(|s| s.trim())
1799
+ .unwrap_or("document");
1800
+
1801
+ let line_count = if let Some(start_extract) = rest.find("--- Extracted document text ---\n")
1802
+ {
1803
+ let content_start = start_extract + "--- Extracted document text ---\n".len();
1804
+ if let Some(end_extract) =
1805
+ rest[content_start..].find("\n--- End extracted document text ---")
1806
+ {
1807
+ rest[content_start..content_start + end_extract]
1808
+ .lines()
1809
+ .count()
1810
+ } else {
1811
+ 0
1812
+ }
1813
+ } else {
1814
+ 0
1815
+ };
1816
+
1817
+ if prefix.is_empty() {
1818
+ format!("[Attached document: {filename} ({line_count} lines)]")
1819
+ } else {
1820
+ format!("{prefix}\n\n[Attached document: {filename} ({line_count} lines)]")
1821
+ }
1822
+ } else {
1823
+ raw.to_string()
1824
+ }
1825
+ }
1826
+
1827
+ pub(crate) fn clean_user_prompt(raw: &str) -> String {
1828
+ if let Some(doc_idx) = raw.find("Attached document:") {
1829
+ raw[..doc_idx].trim().to_string()
1830
+ } else {
1831
+ raw.to_string()
1832
+ }
1833
+ }