@pheem49/mint 1.17.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 (421) 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 -71
  5. package/crates/mint-cli/src/agent/diff_render.rs +66 -44
  6. package/crates/mint-cli/src/agent/live_status.rs +21 -4
  7. package/crates/mint-cli/src/agent/markdown_render.rs +342 -5
  8. package/crates/mint-cli/src/agent.rs +25 -10
  9. package/crates/mint-cli/src/commands/agent.rs +4 -92
  10. package/crates/mint-cli/src/commands/browser.rs +2 -15
  11. package/crates/mint-cli/src/commands/integrations.rs +3 -3
  12. package/crates/mint-cli/src/commands/mod.rs +0 -3
  13. package/crates/mint-cli/src/commands/tailnet_web.rs +1 -0
  14. package/crates/mint-cli/src/interactive/chat_tui/dialog.rs +572 -0
  15. package/crates/mint-cli/src/interactive/chat_tui/mod.rs +1427 -0
  16. package/crates/mint-cli/src/interactive/chat_tui/render.rs +1524 -0
  17. package/crates/mint-cli/src/interactive/chat_tui/selection.rs +401 -0
  18. package/crates/mint-cli/src/interactive/chat_tui/state.rs +1833 -0
  19. package/crates/mint-cli/src/interactive/chat_tui/tests.rs +3414 -0
  20. package/crates/mint-cli/src/interactive/chat_tui/thought.rs +202 -0
  21. package/crates/mint-cli/src/interactive/chat_tui/transcript.rs +728 -0
  22. package/crates/mint-cli/src/interactive/chat_tui/watermark.rs +148 -0
  23. package/crates/mint-cli/src/interactive/command_ui.rs +53 -5
  24. package/crates/mint-cli/src/interactive/composer.rs +186 -0
  25. package/crates/mint-cli/src/interactive/format.rs +78 -3
  26. package/crates/mint-cli/src/interactive/input_box.rs +61 -1
  27. package/crates/mint-cli/src/interactive/resume_picker.rs +367 -25
  28. package/crates/mint-cli/src/interactive/slash_commands.rs +648 -371
  29. package/crates/mint-cli/src/interactive/tool_activity.rs +93 -20
  30. package/crates/mint-cli/src/interactive.rs +37 -0
  31. package/crates/mint-cli/src/main.rs +6 -3
  32. package/crates/mint-cli/src/markdown.rs +2 -37
  33. package/crates/mint-cli/src/onboard.rs +700 -282
  34. package/crates/mint-cli/src/setup.rs +1342 -624
  35. package/crates/mint-cli/src/terminal_theme.rs +159 -35
  36. package/crates/mint-cli/tests/library_intake.rs +1 -0
  37. package/crates/mint-core/src/agent/chat/anthropic.rs +318 -0
  38. package/crates/mint-core/src/agent/chat/custom.rs +149 -0
  39. package/crates/mint-core/src/agent/chat/gemini.rs +518 -0
  40. package/crates/mint-core/src/agent/chat/mod.rs +857 -0
  41. package/crates/mint-core/src/agent/chat/ollama.rs +321 -0
  42. package/crates/mint-core/src/agent/chat/openai.rs +582 -0
  43. package/crates/mint-core/src/agent/chat/streaming.rs +469 -0
  44. package/crates/mint-core/src/agent/chat/tests.rs +1479 -0
  45. package/crates/mint-core/src/agent/memory.rs +56 -2
  46. package/crates/mint-core/src/agent/tasks.rs +2 -2
  47. package/crates/mint-core/src/api_server/routes/chat.rs +24 -2
  48. package/crates/mint-core/src/api_server/routes/gemini_live.rs +14 -1
  49. package/crates/mint-core/src/api_server/routes/html_preview.rs +1 -8
  50. package/crates/mint-core/src/api_server/routes/sessions.rs +21 -0
  51. package/crates/mint-core/src/avatar_bridge.rs +0 -22
  52. package/crates/mint-core/src/browser/cdp.rs +10 -4
  53. package/crates/mint-core/src/browser/headless.rs +582 -0
  54. package/crates/mint-core/src/browser/input.rs +7 -62
  55. package/crates/mint-core/src/browser/interact.rs +0 -14
  56. package/crates/mint-core/src/browser/mod.rs +8 -29
  57. package/crates/mint-core/src/browser/navigate.rs +2 -36
  58. package/crates/mint-core/src/browser/runtime.rs +2 -4
  59. package/crates/mint-core/src/browser/session.rs +50 -466
  60. package/crates/mint-core/src/browser/workflow.rs +142 -32
  61. package/crates/mint-core/src/cron/store.rs +2 -2
  62. package/crates/mint-core/src/integrations/mcp.rs +6 -8
  63. package/crates/mint-core/src/lib.rs +0 -5
  64. package/crates/mint-core/src/media/gemini_live.rs +636 -86
  65. package/crates/mint-core/src/media/library/migration.rs +26 -0
  66. package/crates/mint-core/src/media/library/tests.rs +1 -1
  67. package/crates/mint-core/src/media/library/thumbnails.rs +8 -2
  68. package/crates/mint-core/src/media/pictures.rs +1 -1
  69. package/crates/mint-core/src/media/tool_images.rs +92 -1
  70. package/crates/mint-core/src/mods/manager.rs +101 -35
  71. package/crates/mint-core/src/orchestration/context_render.rs +441 -24
  72. package/crates/mint-core/src/orchestration/decision_parsing.rs +531 -16
  73. package/crates/mint-core/src/orchestration/full_skill_result_tests.rs +12 -0
  74. package/crates/mint-core/src/orchestration/loop_guard.rs +145 -0
  75. package/crates/mint-core/src/orchestration/mcp_outcome_tests.rs +864 -0
  76. package/crates/mint-core/src/orchestration/memory_skill.rs +88 -28
  77. package/crates/mint-core/src/orchestration/mod.rs +320 -4249
  78. package/crates/mint-core/src/orchestration/reliability.rs +67 -27
  79. package/crates/mint-core/src/orchestration/review_regressions.rs +5 -0
  80. package/crates/mint-core/src/orchestration/tests.rs +1665 -0
  81. package/crates/mint-core/src/orchestration/tools/dispatch.rs +967 -0
  82. package/crates/mint-core/src/orchestration/tools/files.rs +1 -0
  83. package/crates/mint-core/src/orchestration/tools/mod.rs +5 -1
  84. package/crates/mint-core/src/orchestration/tools/planning.rs +102 -42
  85. package/crates/mint-core/src/orchestration/tools/shell.rs +129 -58
  86. package/crates/mint-core/src/orchestration/truncation.rs +170 -13
  87. package/crates/mint-core/src/orchestration/types.rs +648 -0
  88. package/crates/mint-core/src/orchestration/verification.rs +0 -3
  89. package/crates/mint-core/src/orchestration/workspace_helpers.rs +24 -6
  90. package/crates/mint-core/src/prompts/agent.rs +52 -76
  91. package/crates/mint-core/src/prompts/tool_catalog.rs +8 -151
  92. package/crates/mint-core/src/search/files.rs +20 -0
  93. package/crates/mint-core/src/slash/mod.rs +228 -265
  94. package/crates/mint-core/src/slash/models.rs +6 -1
  95. package/crates/mint-core/src/system/bg_shell.rs +1 -0
  96. package/crates/mint-core/src/system/config.rs +176 -34
  97. package/crates/mint-mod-runtime/src/lib.rs +37 -23
  98. package/out/web/assets/ArtifactPreviewPanel-Cx6BVOOF.js +2 -0
  99. package/out/web/assets/CalculationCard-D2E74u5q.js +1 -0
  100. package/out/web/assets/CliSessionsView-ZbdtSjvK.css +1 -0
  101. package/out/web/assets/{CliSessionsView-MI8kXMAb.js → CliSessionsView-z5i6qGyb.js} +1 -1
  102. package/out/web/assets/CodeReviewPage-BK2bOScd.css +1 -0
  103. package/out/web/assets/CodeReviewPage-Dp6zHuHu.js +2 -0
  104. package/out/web/assets/{CommandPalette-BD8SlTE5.js → CommandPalette-DaKlzbKs.js} +1 -1
  105. package/out/web/assets/ImageGenCard-BMCJHP9O.js +1 -0
  106. package/out/web/assets/ImageSearchCard-Cld_SOUg.js +1 -0
  107. package/out/web/assets/{ImageStudioPanel-BP7N4rLr.js → ImageStudioPanel-B5N7TjeB.js} +1 -1
  108. package/out/web/assets/LibraryPage-CMB89rJ0.css +1 -0
  109. package/out/web/assets/LibraryPage-DbZEi4W_.js +2 -0
  110. package/out/web/assets/LinkedFoldersView-9B9DIPoZ.js +1 -0
  111. package/out/web/assets/{McpRegistryPicker-CoS83vtc.js → McpRegistryPicker-DnXPlzI7.js} +1 -1
  112. package/out/web/assets/McpServersView-CiNBNUl4.js +1 -0
  113. package/out/web/assets/MermaidCard-BBDNEXbY.js +2 -0
  114. package/out/web/assets/MintDashboard-C_UYPoNV.js +119 -0
  115. package/out/web/assets/MintDashboard-DTd8C_c8.css +1 -0
  116. package/out/web/assets/PlanCard-C2x5pajq.js +1 -0
  117. package/out/web/assets/{PluginsView-jBYmcKh4.js → PluginsView-DoTg9MVa.js} +1 -1
  118. package/out/web/assets/RewindModal-B5oowrR0.js +1 -0
  119. package/out/web/assets/{ScheduledTasksView-5uw9-V9H.js → ScheduledTasksView-BD5DVPkZ.js} +1 -1
  120. package/out/web/assets/{SelectField-DXBQtrUK.js → SelectField-Cy6CghrU.js} +1 -1
  121. package/out/web/assets/SettingsWindow-BYl_oCFh.js +4 -0
  122. package/out/web/assets/SettingsWindow-Ckcgnjt8.css +1 -0
  123. package/out/web/assets/{SkillsView-C-gp9J7m.js → SkillsView-CIeWwq5z.js} +1 -1
  124. package/out/web/assets/StockCard-DxUciKyp.js +1 -0
  125. package/out/web/assets/ToolSurfacePage-qBgs_VHq.css +1 -0
  126. package/out/web/assets/ToolSurfacePage-saVbVrkE.js +38 -0
  127. package/out/web/assets/{UiFeatureCard-6BwxTOCe.js → UiFeatureCard-CdY0hGWy.js} +1 -1
  128. package/out/web/assets/{UiGridCard-DvZcLNxb.js → UiGridCard-BBK3I5hC.js} +1 -1
  129. package/out/web/assets/UiMockupWidget-CpR9EDuc.js +1 -0
  130. package/out/web/assets/{VeoStudioPanel-DOj7_WR3.css → VeoStudioPanel-9kU9YLEN.css} +1 -1
  131. package/out/web/assets/{VeoStudioPanel-DfsOJKv0.js → VeoStudioPanel-Co6-wEWM.js} +2 -2
  132. package/out/web/assets/WeatherCard-Buzi7Vfu.js +1 -0
  133. package/out/web/assets/WorkspacePdfPreview-Bi8Zzyir.js +2 -0
  134. package/out/web/assets/{abnfDiagram-VCTEODGH-BOo3Ckng.js → abnfDiagram-VCTEODGH-CMfR95N2.js} +1 -1
  135. package/out/web/assets/{architecture-7GRP2DOG-ZOwheW3n.js → architecture-7GRP2DOG-DhAM4HyO.js} +1 -1
  136. package/out/web/assets/{architectureDiagram-5GKGNRK7-DC2FDzVd.js → architectureDiagram-5GKGNRK7-Yk3TUJ_F.js} +1 -1
  137. package/out/web/assets/{blockDiagram-I7D4REHJ-BrKFWdAH.js → blockDiagram-I7D4REHJ-X9fBgJik.js} +1 -1
  138. package/out/web/assets/{c4Diagram-7LVT6UL2-BHTHK1Yd.js → c4Diagram-7LVT6UL2-Bk9x7N-8.js} +1 -1
  139. package/out/web/assets/{chunk-4HAMMTFA-BYFkZwNj.js → chunk-4HAMMTFA-Dx5fk1_0.js} +1 -1
  140. package/out/web/assets/{chunk-75Z2AOVW-BSKfSKSA.js → chunk-75Z2AOVW-BszTQoM4.js} +1 -1
  141. package/out/web/assets/{chunk-DU6HZSFF-vIhsiW6n.js → chunk-DU6HZSFF-DK-As3rk.js} +1 -1
  142. package/out/web/assets/{chunk-F27PBJKO-BlYpW9xq.js → chunk-F27PBJKO-LmncZZIS.js} +1 -1
  143. package/out/web/assets/{chunk-GMAD6QVW-BIBz_Ug5.js → chunk-GMAD6QVW-DSQ_sDRJ.js} +1 -1
  144. package/out/web/assets/{chunk-GVQU2GXP-NPSHxeqC.js → chunk-GVQU2GXP-Cno3ulje.js} +1 -1
  145. package/out/web/assets/{chunk-IMKFNOWR-YlJK8_Fp.js → chunk-IMKFNOWR-bIzfY9Jc.js} +1 -1
  146. package/out/web/assets/{chunk-L3NEJ4N5-DMYi4-af.js → chunk-L3NEJ4N5-Dl6eOa0D.js} +1 -1
  147. package/out/web/assets/{chunk-OSK3NFVY-Begd3-jA.js → chunk-OSK3NFVY-BaUGJ-cM.js} +1 -1
  148. package/out/web/assets/{chunk-P2QGCYS3-D1I4xFHN.js → chunk-P2QGCYS3-_066ndhH.js} +1 -1
  149. package/out/web/assets/{chunk-POPQ4Y6H-CafZZzNE.js → chunk-POPQ4Y6H-YT1AKhTv.js} +1 -1
  150. package/out/web/assets/{chunk-PWAF6VOD-Dwh6_Lnt.js → chunk-PWAF6VOD-CrEgDsxh.js} +1 -1
  151. package/out/web/assets/{chunk-SHT3W25Y-B0XrCSEb.js → chunk-SHT3W25Y-_KwaRLOl.js} +1 -1
  152. package/out/web/assets/{chunk-SVP7TREG-CxLsddxC.js → chunk-SVP7TREG-BQ1qb32L.js} +1 -1
  153. package/out/web/assets/{chunk-TICWLB2K-y156QInb.js → chunk-TICWLB2K-BDyehCtg.js} +1 -1
  154. package/out/web/assets/classDiagram-ZZMXUADV-BoSpzRCJ.js +1 -0
  155. package/out/web/assets/classDiagram-v2-VYDZK3BY-BoSpzRCJ.js +1 -0
  156. package/out/web/assets/{config-DqXJXeZM.css → config-2suVFPZg.css} +1 -1
  157. package/out/web/assets/config-CkRCdkxn.js +2 -0
  158. package/out/web/assets/{cynefin-OW5HDTMX-SMVvrLf-.js → cynefin-OW5HDTMX-Dl_31aaN.js} +1 -1
  159. package/out/web/assets/{cynefinDiagram-5FMLGOSQ-rZznN24T.js → cynefinDiagram-5FMLGOSQ-Bpir8jha.js} +1 -1
  160. package/out/web/assets/{dagre-GXQ25YYZ-BOToydFu.js → dagre-GXQ25YYZ-D9xu3nsq.js} +1 -1
  161. package/out/web/assets/{diagram-S7CK7UJ4-TMVaGQEJ.js → diagram-S7CK7UJ4-DeveoEeB.js} +1 -1
  162. package/out/web/assets/{diagram-UQ7AKVKN-C2JK-GP2.js → diagram-UQ7AKVKN-p4xWRESV.js} +1 -1
  163. package/out/web/assets/{diagram-VSXAHHWV-Cw8VSi2K.js → diagram-VSXAHHWV-C7qz9NDF.js} +1 -1
  164. package/out/web/assets/{diagram-VX7I27RA-DXp4fKk9.js → diagram-VX7I27RA-DRiCa3Vd.js} +1 -1
  165. package/out/web/assets/{diagram-Z3DM3KII-CzSicYiW.js → diagram-Z3DM3KII-Bt7zduQX.js} +1 -1
  166. package/out/web/assets/{ebnfDiagram-PWID7BFC-Dqj-zqPu.js → ebnfDiagram-PWID7BFC-DV9WpwdR.js} +1 -1
  167. package/out/web/assets/{erDiagram-RLTQ6QDP-DZwFZAuD.js → erDiagram-RLTQ6QDP-BJ_nwG2m.js} +1 -1
  168. package/out/web/assets/{eventmodeling-NTZA5JFV-C0O-Lvgp.js → eventmodeling-NTZA5JFV-BTBMrfVa.js} +1 -1
  169. package/out/web/assets/flowDiagram-HODETNUW-Cdlq3cJL.js +1 -0
  170. package/out/web/assets/{ganttDiagram-EL5Y4UJY-Da4ULXTl.js → ganttDiagram-EL5Y4UJY-DQLdsXrC.js} +1 -1
  171. package/out/web/assets/{gitGraph-4MIJSDKK-B9wNFZIP.js → gitGraph-4MIJSDKK-CL1OEOQ_.js} +1 -1
  172. package/out/web/assets/{gitGraphDiagram-WWUBYQGX-RjrhB0MP.js → gitGraphDiagram-WWUBYQGX-8cSi42H7.js} +1 -1
  173. package/out/web/assets/index-BocxnrDG.js +168 -0
  174. package/out/web/assets/index-yeh_r55a.css +1 -0
  175. package/out/web/assets/{info-A6RAGUB7-B3dfCpeY.js → info-A6RAGUB7-C1zsX5sc.js} +1 -1
  176. package/out/web/assets/{infoDiagram-27XIBGKW-QVWkLVx7.js → infoDiagram-27XIBGKW-DsRYtIyI.js} +1 -1
  177. package/out/web/assets/{ishikawaDiagram-5VMMS53U-CLFNIRI9.js → ishikawaDiagram-5VMMS53U-B2crwIXj.js} +1 -1
  178. package/out/web/assets/{journeyDiagram-3NMN7TZE-fgOQadYF.js → journeyDiagram-3NMN7TZE-BTH-GyVR.js} +1 -1
  179. package/out/web/assets/{kanban-definition-UXKFOSKX-G3Q_9g9O.js → kanban-definition-UXKFOSKX-BeRfCnuA.js} +1 -1
  180. package/out/web/assets/{mermaid-parser.core-v3Jm7nco.js → mermaid-parser.core-BJwgCBBc.js} +3 -3
  181. package/out/web/assets/{mermaid.core-DTEjLUA1.js → mermaid.core-CsDDodN8.js} +4 -4
  182. package/out/web/assets/{mindmap-definition-YA3MSWOX-C_D42_fF.js → mindmap-definition-YA3MSWOX-B42efOQ1.js} +1 -1
  183. package/out/web/assets/{modelManager-BPLycQrN.js → modelManager-B3ZFMh8v.js} +2 -2
  184. package/out/web/assets/{oauthManager-BUK7ic7q.js → oauthManager-Ch5Du2fF.js} +1 -1
  185. package/out/web/assets/{packet-AYTQ26CC-B-R-SHlF.js → packet-AYTQ26CC-B6xUyE6r.js} +1 -1
  186. package/out/web/assets/{pegDiagram-XKGWAZYB-Da2q_giz.js → pegDiagram-XKGWAZYB-1LabVycL.js} +1 -1
  187. package/out/web/assets/{pie-WAS4IAKB-BC8F0N7-.js → pie-WAS4IAKB-DJwo-nf_.js} +1 -1
  188. package/out/web/assets/{pieDiagram-E7YTZNPT-Iy9REJNC.js → pieDiagram-E7YTZNPT-icmAqzy4.js} +1 -1
  189. package/out/web/assets/{quadrantDiagram-AXDQQJYC-ztsiYw7h.js → quadrantDiagram-AXDQQJYC-CZ-wEx41.js} +1 -1
  190. package/out/web/assets/{radar-RG4KPBEZ-B_1P8Z_z.js → radar-RG4KPBEZ-D7d-Xriw.js} +1 -1
  191. package/out/web/assets/{railroad-74A4TZTK-DuTqczSo.js → railroad-74A4TZTK-viuhdYbs.js} +1 -1
  192. package/out/web/assets/{railroad-abnf-HS5TGJTU-Df29WLmp.js → railroad-abnf-HS5TGJTU-DNXiCNea.js} +1 -1
  193. package/out/web/assets/{railroad-ebnf-LZEXJU2U-ePFMo-qp.js → railroad-ebnf-LZEXJU2U-nkSu1-0C.js} +1 -1
  194. package/out/web/assets/{railroad-peg-WCYAUIDC-DYXLW4_E.js → railroad-peg-WCYAUIDC-BhbMeQzz.js} +1 -1
  195. package/out/web/assets/{railroadDiagram-O6MQD6OU-LStnZf2S.js → railroadDiagram-O6MQD6OU-CLpG1jUX.js} +1 -1
  196. package/out/web/assets/{requirementDiagram-BXWQKSXE-DmzonMH-.js → requirementDiagram-BXWQKSXE-kRDrvWrc.js} +1 -1
  197. package/out/web/assets/{sankeyDiagram-P5KCCOFB-B8vjmUU1.js → sankeyDiagram-P5KCCOFB-DxAwmq4x.js} +1 -1
  198. package/out/web/assets/{sequenceDiagram-WJ2MYXX4-CTnplOAX.js → sequenceDiagram-WJ2MYXX4-aLgY-6Wz.js} +1 -1
  199. package/out/web/assets/{stateDiagram-D77RDMKH-BFrIaLdI.js → stateDiagram-D77RDMKH-D5QWfOhp.js} +1 -1
  200. package/out/web/assets/stateDiagram-v2-MP3YSRHH-D6OiZoAP.js +1 -0
  201. package/out/web/assets/{swimlanes-42K2YHIH-BXGngUNU.js → swimlanes-42K2YHIH-CRs5UyA-.js} +1 -1
  202. package/out/web/assets/swimlanesDiagram-VR7AAH4N-UgEXIsV0.js +8 -0
  203. package/out/web/assets/syntaxHighlight-C-wAGT84.js +3 -0
  204. package/out/web/assets/{timeline-definition-24CTP7MA-CegH7n43.js → timeline-definition-24CTP7MA-Denqd9qb.js} +1 -1
  205. package/out/web/assets/{treeView-Q6P3EWNA-DTpJv2Tw.js → treeView-Q6P3EWNA-0my3Z3s4.js} +1 -1
  206. package/out/web/assets/{treemap-WGGIJYW6-i8M9iJU6.js → treemap-WGGIJYW6-B_EjkLYq.js} +1 -1
  207. package/out/web/assets/{vennDiagram-4TSXK5OY-Dpzo1S8U.js → vennDiagram-4TSXK5OY-DxVg3Ewr.js} +1 -1
  208. package/out/web/assets/{wardley-WFR3VGLG-BrNsgtfw.js → wardley-WFR3VGLG-CGD-Tj4p.js} +1 -1
  209. package/out/web/assets/{wardleyDiagram-VM6X3IG4-lQ92LFE1.js → wardleyDiagram-VM6X3IG4-CmfS39Xm.js} +1 -1
  210. package/out/web/assets/{xychartDiagram-S5SC5T6Z-BaOaEoBd.js → xychartDiagram-S5SC5T6Z-BVM9bKis.js} +1 -1
  211. package/out/web/index-web.html +2 -2
  212. package/out/web/index.html +2 -2
  213. package/package.json +2 -2
  214. package/shared-modules/CODEOWNERS +3 -3
  215. package/shared-modules/SDL2/SDL2_mixer.json +27 -0
  216. package/shared-modules/appstream/README.md +13 -0
  217. package/shared-modules/appstream/appstream-compose.json +111 -0
  218. package/shared-modules/dbus-glib/dbus-glib.json +2 -2
  219. package/shared-modules/libportal/libportal.json +23 -0
  220. package/shared-modules/linux-audio/fluidsynth2-static.json +2 -2
  221. package/shared-modules/linux-audio/fluidsynth2.json +2 -2
  222. package/shared-modules/linux-audio/libinstpatch.json +2 -2
  223. package/shared-modules/linux-audio/liblo-static.json +3 -0
  224. package/shared-modules/luajit/luajit.json +2 -2
  225. package/shared-modules/squashfs-tools/squashfs-tools.json +0 -3
  226. package/slash-commands.json +7 -2
  227. package/src/renderer/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  228. package/src/renderer/shared/agentProgress.ts +51 -3
  229. package/src/renderer/shared/components/Settings/AutomationTab.tsx +2 -56
  230. package/src/renderer/shared/components/Settings/AvatarCropModal.tsx +15 -1
  231. package/src/renderer/shared/components/Settings/GeneralTab.tsx +25 -12
  232. package/src/renderer/shared/components/Settings/ThemeTab.test.tsx +45 -0
  233. package/src/renderer/shared/components/Settings/ThemeTab.tsx +106 -3
  234. package/src/renderer/shared/components/activity/AgentActivityDrawer.tsx +67 -17
  235. package/src/renderer/shared/components/activity/AgentActivityTable.tsx +376 -134
  236. package/src/renderer/shared/components/activity/AskUserActivityCard.test.tsx +122 -0
  237. package/src/renderer/shared/components/activity/AskUserActivityCard.tsx +70 -0
  238. package/src/renderer/shared/components/activity/McpToolResults.tsx +39 -2
  239. package/src/renderer/shared/components/activity/SubagentDagView.tsx +7 -7
  240. package/src/renderer/shared/components/activity/ViewedImages.tsx +41 -2
  241. package/src/renderer/shared/components/app/ChunkErrorBoundary.tsx +3 -3
  242. package/src/renderer/shared/components/app/CommandPalette.tsx +7 -7
  243. package/src/renderer/shared/components/app/DesktopTitlebar.tsx +2 -2
  244. package/src/renderer/shared/components/app/MintDashboard.tsx +27 -17
  245. package/src/renderer/shared/components/app/ModalErrorBoundary.tsx +3 -3
  246. package/src/renderer/shared/components/cards/CalculationCard.tsx +3 -3
  247. package/src/renderer/shared/components/cards/ImageGenCard.tsx +2 -2
  248. package/src/renderer/shared/components/cards/ImageSearchCard.tsx +2 -2
  249. package/src/renderer/shared/components/cards/MermaidCard.tsx +1 -1
  250. package/src/renderer/shared/components/cards/PlanCard.test.tsx +41 -0
  251. package/src/renderer/shared/components/cards/PlanCard.tsx +32 -0
  252. package/src/renderer/shared/components/cards/ReleaseNotesCard.test.tsx +29 -0
  253. package/src/renderer/shared/components/cards/ReleaseNotesCard.tsx +137 -0
  254. package/src/renderer/shared/components/cards/StockCard.tsx +7 -7
  255. package/src/renderer/shared/components/cards/ToolResultCards.tsx +11 -3
  256. package/src/renderer/shared/components/cards/UiMockupWidget.tsx +4 -4
  257. package/src/renderer/shared/components/cards/WeatherCard.tsx +5 -5
  258. package/src/renderer/shared/components/chat/ApprovalCard.test.tsx +11 -0
  259. package/src/renderer/shared/components/chat/ApprovalCard.tsx +21 -2
  260. package/src/renderer/shared/components/chat/ArtifactCards.test.tsx +69 -0
  261. package/src/renderer/shared/components/chat/ArtifactCards.tsx +238 -0
  262. package/src/renderer/shared/components/chat/AttachedDocumentCard.test.tsx +39 -0
  263. package/src/renderer/shared/components/chat/AttachedDocumentCard.tsx +220 -0
  264. package/src/renderer/shared/components/chat/ChatCodeBlock.test.tsx +33 -0
  265. package/src/renderer/shared/components/chat/ChatCodeBlock.tsx +15 -1
  266. package/src/renderer/shared/components/chat/ChatMessageItem.test.tsx +40 -2
  267. package/src/renderer/shared/components/chat/ChatMessageItem.tsx +180 -34
  268. package/src/renderer/shared/components/chat/ChatModeSelector.css +227 -0
  269. package/src/renderer/shared/components/chat/ChatModeSelector.test.tsx +45 -0
  270. package/src/renderer/shared/components/chat/ChatModeSelector.tsx +198 -0
  271. package/src/renderer/shared/components/chat/ChatPanel.test.tsx +22 -0
  272. package/src/renderer/shared/components/chat/ChatPanel.tsx +227 -53
  273. package/src/renderer/shared/components/chat/DiffReviewModal.tsx +6 -6
  274. package/src/renderer/shared/components/chat/GeminiLiveOverlay.test.tsx +142 -1
  275. package/src/renderer/shared/components/chat/GeminiLiveOverlay.tsx +45 -3
  276. package/src/renderer/shared/components/chat/ModelSelectorPopover.tsx +42 -14
  277. package/src/renderer/shared/components/chat/RewindModal.tsx +10 -10
  278. package/src/renderer/shared/components/chat/SlashSuggestions.test.tsx +14 -0
  279. package/src/renderer/shared/components/chat/SourcesBlock.test.tsx +53 -0
  280. package/src/renderer/shared/components/chat/SourcesBlock.tsx +236 -90
  281. package/src/renderer/shared/components/chat/TurnFileChanges.test.tsx +45 -1
  282. package/src/renderer/shared/components/chat/TurnFileChanges.tsx +202 -169
  283. package/src/renderer/shared/components/extensions/CliSessionsView.tsx +5 -4
  284. package/src/renderer/shared/components/extensions/McpRegistryPicker.tsx +3 -3
  285. package/src/renderer/shared/components/studio/LibraryPage.tsx +48 -5
  286. package/src/renderer/shared/components/workspace/ArtifactPreviewPanel.tsx +125 -13
  287. package/src/renderer/shared/components/workspace/CodeReviewPage.test.tsx +50 -0
  288. package/src/renderer/shared/components/workspace/CodeReviewPage.tsx +77 -16
  289. package/src/renderer/shared/components/workspace/GitBranchSelector.tsx +78 -22
  290. package/src/renderer/shared/components/workspace/ToolSurfacePage.test.tsx +13 -0
  291. package/src/renderer/shared/components/workspace/ToolSurfacePage.tsx +48 -28
  292. package/src/renderer/shared/components/workspace/WorkspacePdfPreview.tsx +266 -47
  293. package/src/renderer/shared/components/workspace/WorkspacePreviewActions.test.tsx +18 -6
  294. package/src/renderer/shared/components/workspace/background-terminals.css +11 -11
  295. package/src/renderer/shared/constants/config.ts +1 -5
  296. package/src/renderer/shared/constants/models.test.ts +51 -0
  297. package/src/renderer/shared/constants/models.ts +139 -32
  298. package/src/renderer/shared/conversation/lifecycle.test.ts +91 -3
  299. package/src/renderer/shared/conversation/lifecycle.ts +104 -5
  300. package/src/renderer/shared/conversation/progressiveStream.test.ts +83 -0
  301. package/src/renderer/shared/conversation/progressiveStream.ts +219 -0
  302. package/src/renderer/shared/css/agent-observability.css +133 -0
  303. package/src/renderer/shared/css/chat-ui-cards.css +13 -13
  304. package/src/renderer/shared/css/code-review.css +63 -34
  305. package/src/renderer/shared/css/code-sessions.css +47 -18
  306. package/src/renderer/shared/css/collapsed-paste.css +1 -1
  307. package/src/renderer/shared/css/error-notice.css +1 -1
  308. package/src/renderer/shared/css/file-changes.css +165 -80
  309. package/src/renderer/shared/css/library.css +105 -10
  310. package/src/renderer/shared/css/management-views.css +1 -1
  311. package/src/renderer/shared/css/mcp-results.css +5 -5
  312. package/src/renderer/shared/css/model-selector.css +18 -18
  313. package/src/renderer/shared/css/mods.css +7 -7
  314. package/src/renderer/shared/css/notification-notice.css +2 -2
  315. package/src/renderer/shared/css/queued-messages.css +5 -5
  316. package/src/renderer/shared/css/settings/general.css +18 -13
  317. package/src/renderer/shared/css/settings/profile.css +5 -5
  318. package/src/renderer/shared/css/settings/theme.css +5 -5
  319. package/src/renderer/shared/css/slash-choice.css +2 -2
  320. package/src/renderer/shared/css/slash-suggestions.css +3 -3
  321. package/src/renderer/shared/css/sources-block.css +41 -2
  322. package/src/renderer/shared/css/tools-panel.css +2 -2
  323. package/src/renderer/shared/css/ui-tailwind.css +8 -0
  324. package/src/renderer/shared/css/veo-studio.css +2 -2
  325. package/src/renderer/shared/css/viewed-images.css +7 -7
  326. package/src/renderer/shared/platform.ts +2 -1
  327. package/src/renderer/shared/theme/themeManager.test.ts +17 -0
  328. package/src/renderer/shared/theme/themeManager.ts +21 -4
  329. package/src/renderer/shared/theme/themes.ts +54 -0
  330. package/src/renderer/shared/theme/tokens.css +6 -4
  331. package/src/renderer/shared/types.ts +1 -1
  332. package/src/renderer/shared/utils/agentActivity.test.ts +468 -2
  333. package/src/renderer/shared/utils/agentActivity.ts +602 -35
  334. package/src/renderer/shared/utils/artifactBrowser.test.ts +37 -32
  335. package/src/renderer/shared/utils/artifactBrowser.ts +17 -19
  336. package/src/renderer/shared/utils/attachedDocument.test.ts +73 -0
  337. package/src/renderer/shared/utils/attachedDocument.ts +49 -0
  338. package/src/renderer/shared/utils/fileIcons.test.ts +59 -0
  339. package/src/renderer/shared/utils/fileIcons.ts +149 -16
  340. package/src/renderer/shared/utils/markdown.tsx +72 -19
  341. package/src/renderer/shared/utils/markdown.webImages.test.tsx +16 -0
  342. package/src/renderer/shared/utils/mcpResults.test.tsx +29 -0
  343. package/src/renderer/shared/utils/slashCommandProcessor.ts +4 -1
  344. package/src/renderer/shared/utils/syntaxHighlight.test.tsx +11 -1
  345. package/src/renderer/shared/utils/syntaxHighlight.tsx +51 -3
  346. package/src/renderer/shared/utils/useGeminiLiveVoice.ts +324 -51
  347. package/src/renderer/shared/utils/workspaceDrag.test.ts +153 -0
  348. package/src/renderer/shared/utils/workspaceDrag.ts +142 -0
  349. package/src/renderer/shared/utils/workspaceMedia.ts +10 -2
  350. package/src/renderer/src/App.tsx +14 -8
  351. package/src/renderer/src/agentProgress.ts +1 -0
  352. package/src/renderer/src/components/ScreenPicker.css +1 -1
  353. package/src/renderer/src/components/WorkspacePanel.tsx +6 -5
  354. package/src/renderer/src/css/app-shell.css +4 -4
  355. package/src/renderer/src/css/chat.css +1261 -241
  356. package/src/renderer/src/css/desktop-tools.css +11 -11
  357. package/src/renderer/src/css/sidebar-workspace.css +2 -2
  358. package/src/renderer/src/css/tool-surfaces.css +12 -12
  359. package/src/renderer/src/index.css +38 -20
  360. package/src/renderer/src/tauri.ts +22 -2
  361. package/src/renderer/src-web/App.tsx +14 -0
  362. package/src/renderer/src-web/agentProgress.ts +1 -0
  363. package/src/renderer/src-web/css/app-shell.css +4 -4
  364. package/src/renderer/src-web/css/chat.css +1278 -190
  365. package/src/renderer/src-web/css/mobile.css +29 -13
  366. package/src/renderer/src-web/css/settings/mobile.css +2 -2
  367. package/src/renderer/src-web/css/sidebar-workspace.css +1 -1
  368. package/src/renderer/src-web/index.css +38 -20
  369. package/src/renderer/src-web/tauri.ts +22 -2
  370. package/src/renderer/tests/browserArtifacts.browser.tsx +1 -1
  371. package/src/renderer/tests/library.browser.tsx +3 -0
  372. package/src/renderer/tests/liveVoice.browser.html +1 -0
  373. package/src/renderer/tests/workspaceDrop.browser.tsx +1 -1
  374. package/src-tauri/src/app/lifecycle.rs +1 -14
  375. package/src-tauri/src/background/headless.rs +14 -8
  376. package/src-tauri/src/commands/conversation.rs +38 -0
  377. package/src-tauri/src/native/mod.rs +0 -1
  378. package/src-tauri/src/native/system.rs +4 -47
  379. package/crates/mint-cli/src/interactive/chat_tui.rs +0 -8855
  380. package/crates/mint-core/src/agent/chat.rs +0 -4394
  381. package/crates/mint-core/src/browser/lifecycle.rs +0 -291
  382. package/crates/mint-core/src/browser/logging.rs +0 -20
  383. package/crates/mint-core/src/orchestration/tools/browser.rs +0 -31
  384. package/out/web/assets/ArtifactPreviewPanel-S0dPfYFx.js +0 -2
  385. package/out/web/assets/CalculationCard-CRjP3E1j.js +0 -1
  386. package/out/web/assets/CliSessionsView-C3Br2v5Y.css +0 -1
  387. package/out/web/assets/CodeReviewPage-CUJceQme.css +0 -1
  388. package/out/web/assets/CodeReviewPage-VCUgPla_.js +0 -2
  389. package/out/web/assets/ImageGenCard-BCXxkVJX.js +0 -1
  390. package/out/web/assets/ImageSearchCard-N3yQOr8Z.js +0 -1
  391. package/out/web/assets/LibraryPage-DAxUTAg7.css +0 -1
  392. package/out/web/assets/LibraryPage-WlviRdR8.js +0 -2
  393. package/out/web/assets/LinkedFoldersView-CKRgroWK.js +0 -1
  394. package/out/web/assets/McpServersView-D3wHnuHg.js +0 -1
  395. package/out/web/assets/MermaidCard-NqrHdyJW.js +0 -2
  396. package/out/web/assets/MintDashboard-B2DJsDDO.css +0 -1
  397. package/out/web/assets/MintDashboard-COw_xol7.js +0 -63
  398. package/out/web/assets/RewindModal-DempuDod.js +0 -1
  399. package/out/web/assets/SettingsWindow-2YP3yj7s.js +0 -4
  400. package/out/web/assets/SettingsWindow-Dz8LQVgD.css +0 -1
  401. package/out/web/assets/StockCard-BDMCe7mx.js +0 -1
  402. package/out/web/assets/ToolSurfacePage-B0J_q0Jl.js +0 -38
  403. package/out/web/assets/ToolSurfacePage-CRIQ1owD.css +0 -1
  404. package/out/web/assets/UiMockupWidget-DD73QWg1.js +0 -1
  405. package/out/web/assets/WeatherCard-CPucIfq8.js +0 -1
  406. package/out/web/assets/WorkspacePdfPreview-Dx_lkjNu.js +0 -2
  407. package/out/web/assets/classDiagram-ZZMXUADV-BgJ5WtSp.js +0 -1
  408. package/out/web/assets/classDiagram-v2-VYDZK3BY-BgJ5WtSp.js +0 -1
  409. package/out/web/assets/config-CvIO5hkn.js +0 -2
  410. package/out/web/assets/flowDiagram-HODETNUW-uqCHJond.js +0 -1
  411. package/out/web/assets/index-BmpulIkg.js +0 -168
  412. package/out/web/assets/index-CkvBwhX_.css +0 -1
  413. package/out/web/assets/stateDiagram-v2-MP3YSRHH-uf3kdNW4.js +0 -1
  414. package/out/web/assets/swimlanesDiagram-VR7AAH4N-Y8q5bQce.js +0 -8
  415. package/shared-modules/libsecret/libsecret.json +0 -23
  416. package/shared-modules/libusb/libusb.json +0 -27
  417. package/shared-modules/lzo/lzo.json +0 -27
  418. package/src/renderer/src/components/BrowserShell.tsx +0 -108
  419. package/src/renderer/src/components/BrowserSuggestionsPopup.tsx +0 -91
  420. package/src/renderer/src/components/browser-shell.css +0 -185
  421. package/src-tauri/src/native/browser.rs +0 -252
@@ -0,0 +1,148 @@
1
+ //! 3D rotating medallion watermark for the empty TUI transcript.
2
+ use super::*;
3
+
4
+ pub(super) const WATERMARK_FRAME_COUNT: u8 = 200;
5
+ pub(super) const WATERMARK_TICK: std::time::Duration = std::time::Duration::from_millis(30);
6
+ pub(super) const WATERMARK_HOLD: std::time::Duration = std::time::Duration::from_secs(1);
7
+ pub(super) const WATERMARK_WIDTH: u16 = 39;
8
+ pub(super) const WATERMARK_HEIGHT: u16 = 17;
9
+
10
+ pub(super) fn watermark_animation_at(
11
+ elapsed: std::time::Duration,
12
+ ) -> (Option<u8>, Option<std::time::Duration>) {
13
+ let half_turn_frames = WATERMARK_FRAME_COUNT / 2;
14
+ let half_turn = WATERMARK_TICK * half_turn_frames as u32;
15
+ let hold_end = half_turn + WATERMARK_HOLD;
16
+ let full_turn_end = hold_end + half_turn;
17
+ // Ease each 3-second half turn into and out of its resting face.
18
+ let eased_frame = |step: u8| {
19
+ let progress = step as f32 / half_turn_frames as f32;
20
+ let eased = (1.0 - (progress * std::f32::consts::PI).cos()) * 0.5;
21
+ (eased * half_turn_frames as f32).round() as u8
22
+ };
23
+ if elapsed < half_turn {
24
+ let step = (elapsed.as_millis() / WATERMARK_TICK.as_millis()) as u8;
25
+ (
26
+ Some(eased_frame(step)),
27
+ Some(WATERMARK_TICK * (step as u32 + 1)),
28
+ )
29
+ } else if elapsed < hold_end {
30
+ (Some(half_turn_frames), Some(hold_end))
31
+ } else if elapsed < full_turn_end {
32
+ let step = ((elapsed - hold_end).as_millis() / WATERMARK_TICK.as_millis()) as u8;
33
+ (
34
+ Some((half_turn_frames + eased_frame(step)) % WATERMARK_FRAME_COUNT),
35
+ Some(hold_end + WATERMARK_TICK * (step as u32 + 1)),
36
+ )
37
+ } else {
38
+ (None, None)
39
+ }
40
+ }
41
+
42
+ pub(super) fn mint_m_watermark(frame: Option<u8>, height: usize) -> Text<'static> {
43
+ const WIDTH: usize = WATERMARK_WIDTH as usize;
44
+ const DESIGN_HEIGHT: usize = WATERMARK_HEIGHT as usize;
45
+ #[derive(Clone, Copy)]
46
+ enum Part {
47
+ Void,
48
+ Rim,
49
+ Band,
50
+ Orbit,
51
+ Mark,
52
+ }
53
+
54
+ let height = height.clamp(1, DESIGN_HEIGHT);
55
+ let angle = frame.unwrap_or(0) as f32 * std::f32::consts::TAU / WATERMARK_FRAME_COUNT as f32;
56
+ let (sin, cos) = angle.sin_cos();
57
+ let mut pixels = vec![vec![None::<(f32, Part)>; WIDTH]; height];
58
+
59
+ // A dotted medallion with an M on its front and a leaf on its back. The
60
+ // two faces and the rim share one depth buffer as they turn in 3D.
61
+ for y in 0..DESIGN_HEIGHT {
62
+ let world_y = y as f32 - 8.0;
63
+ let projected_y = y * height.saturating_sub(1) / (DESIGN_HEIGHT - 1);
64
+ for source_x in 0..WIDTH {
65
+ let world_x = (source_x as f32 - 19.0) / 2.0;
66
+ let radius = world_x.hypot(world_y);
67
+ if radius > 8.15 {
68
+ continue;
69
+ }
70
+
71
+ let stem = (world_x.abs() - 3.7).abs() < 0.5 && world_y.abs() <= 4.2;
72
+ let diagonal_y = world_y + 4.2;
73
+ let diagonal = (-4.2..=2.2).contains(&world_y)
74
+ && [-3.7 + diagonal_y * 3.7 / 6.4, 3.7 - diagonal_y * 3.7 / 6.4]
75
+ .into_iter()
76
+ .any(|line_x| (world_x - line_x).abs() < 0.5);
77
+ let front_mark = stem || diagonal;
78
+
79
+ let leaf_x = world_x * 0.8 + world_y * 0.6;
80
+ let leaf_y = -world_x * 0.6 + world_y * 0.8;
81
+ let leaf_radius = (leaf_x / 4.2).powi(2) + (leaf_y / 2.5).powi(2);
82
+ let back_mark =
83
+ (leaf_radius - 1.0).abs() < 0.22 || (leaf_y.abs() < 0.32 && leaf_x.abs() < 3.8);
84
+
85
+ for (z, mark) in [(-1.2, back_mark), (0.0, false), (1.2, front_mark)] {
86
+ let part = if radius > 7.25 {
87
+ Part::Rim
88
+ } else if radius > 6.2 {
89
+ Part::Band
90
+ } else if mark {
91
+ Part::Mark
92
+ } else if radius > 5.75 && radius < 6.05 && (source_x + y) % 3 != 0 {
93
+ Part::Orbit
94
+ } else {
95
+ Part::Void
96
+ };
97
+ let projected_x = (19.0 + (world_x * cos + z * sin) * 2.0).round() as isize;
98
+ if !(0..WIDTH as isize).contains(&projected_x) {
99
+ continue;
100
+ }
101
+ let depth = -world_x * sin + z * cos;
102
+ let cell = &mut pixels[projected_y][projected_x as usize];
103
+ if cell.is_none_or(|(existing_depth, _)| depth > existing_depth) {
104
+ *cell = Some((depth, part));
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+ let lines = pixels
111
+ .into_iter()
112
+ .map(|row| {
113
+ let spans = row
114
+ .into_iter()
115
+ .map(|pixel| match pixel {
116
+ Some((_, Part::Void)) | None => Span::raw(" "),
117
+ Some((depth, part)) => {
118
+ let lit = frame.is_some() && depth > 0.5;
119
+ let (glyph, color) = match part {
120
+ Part::Mark => (
121
+ "⣿",
122
+ if lit {
123
+ crate::terminal_theme::ACCENT
124
+ } else {
125
+ crate::terminal_theme::WATERMARK_MARK
126
+ },
127
+ ),
128
+ Part::Rim => (
129
+ "⠿",
130
+ if lit {
131
+ crate::terminal_theme::BLUE
132
+ } else {
133
+ crate::terminal_theme::WATERMARK_RIM
134
+ },
135
+ ),
136
+ Part::Band => ("⠿", crate::terminal_theme::WATERMARK_BAND),
137
+ Part::Orbit => ("⠂", crate::terminal_theme::WATERMARK_ORBIT),
138
+ Part::Void => unreachable!(),
139
+ };
140
+ Span::styled(glyph, Style::default().fg(color))
141
+ }
142
+ })
143
+ .collect::<Vec<_>>();
144
+ Line::from(spans)
145
+ })
146
+ .collect::<Vec<_>>();
147
+ Text::from(lines)
148
+ }
@@ -36,13 +36,20 @@ impl ChoiceItem {
36
36
  }
37
37
 
38
38
  pub trait CommandUi {
39
- fn uses_inline_resume_picker(&self) -> bool {
40
- false
41
- }
42
39
  fn push_notice_str(&mut self, text: &str);
43
40
  fn push_command_output_str(&mut self, text: &str);
41
+ fn push_release_notes(&mut self, notes: &str) {
42
+ self.push_command_output(notes);
43
+ }
44
+ #[allow(dead_code)]
45
+ fn push_plan(&mut self, plan: &str) {
46
+ self.push_command_output(plan);
47
+ }
44
48
  fn set_status(&mut self, lines: Vec<String>);
45
49
  fn clear_status(&mut self);
50
+ fn open_thought_viewer(&mut self) -> Result<()> {
51
+ Ok(())
52
+ }
46
53
  fn prompt_choice(
47
54
  &mut self,
48
55
  title: &str,
@@ -152,6 +159,14 @@ impl CommandUi for FullScreenCommandUi<'_> {
152
159
  self.handle.push_command(text);
153
160
  }
154
161
 
162
+ fn push_release_notes(&mut self, notes: &str) {
163
+ self.handle.push_release_notes(notes);
164
+ }
165
+
166
+ fn push_plan(&mut self, plan: &str) {
167
+ self.handle.push_plan(plan);
168
+ }
169
+
155
170
  fn set_status(&mut self, lines: Vec<String>) {
156
171
  self.handle.set_status(lines);
157
172
  }
@@ -243,6 +258,13 @@ impl CommandUi for FullScreenCommandUi<'_> {
243
258
  }
244
259
  }
245
260
 
261
+ fn open_thought_viewer(&mut self) -> Result<()> {
262
+ if let Ok(mut state) = self.state.lock() {
263
+ state.open_thought_modal();
264
+ }
265
+ Ok(())
266
+ }
267
+
246
268
  fn reload_transcript(&mut self, chat_id: &str, workspace: &std::path::Path) {
247
269
  if let Ok(mut state) = self.state.lock() {
248
270
  state.reload_transcript(chat_id, workspace);
@@ -260,8 +282,23 @@ impl ClassicCommandUi {
260
282
  }
261
283
 
262
284
  impl CommandUi for ClassicCommandUi {
263
- fn uses_inline_resume_picker(&self) -> bool {
264
- true
285
+ fn open_thought_viewer(&mut self) -> Result<()> {
286
+ if let Some(record) = crate::interactive::get_last_thought() {
287
+ let _ = crate::interactive::show_thought_viewer(&record.thought, &record.elapsed_str);
288
+ } else {
289
+ self.push_notice_str("No recent thought process recorded for this turn.");
290
+ }
291
+ Ok(())
292
+ }
293
+
294
+ fn prompt_resume_picker(
295
+ &mut self,
296
+ _sessions: Vec<mint_core::ChatSession>,
297
+ current_dir: &std::path::Path,
298
+ _current_branch: Option<String>,
299
+ active_chat_id: &str,
300
+ ) -> Result<Option<String>> {
301
+ crate::interactive::prompt_resume_session_picker(current_dir, active_chat_id)
265
302
  }
266
303
 
267
304
  fn push_notice_str(&mut self, text: &str) {
@@ -411,6 +448,8 @@ impl CommandUi for ClassicCommandUi {
411
448
  pub struct MockCommandUi {
412
449
  pub notices: Vec<String>,
413
450
  pub command_outputs: Vec<String>,
451
+ pub release_notes: Vec<String>,
452
+ pub plans: Vec<String>,
414
453
  pub status: Vec<String>,
415
454
  pub choice_answers: Vec<Option<usize>>,
416
455
  pub text_answers: Vec<Option<String>>,
@@ -433,6 +472,15 @@ impl CommandUi for MockCommandUi {
433
472
  self.command_outputs.push(text.to_string());
434
473
  }
435
474
 
475
+ fn push_release_notes(&mut self, notes: &str) {
476
+ self.release_notes.push(notes.to_string());
477
+ }
478
+
479
+ fn push_plan(&mut self, plan: &str) {
480
+ self.plans.push(plan.to_string());
481
+ self.command_outputs.push(plan.to_string());
482
+ }
483
+
436
484
  fn set_status(&mut self, lines: Vec<String>) {
437
485
  self.status = lines;
438
486
  }
@@ -0,0 +1,186 @@
1
+ use std::ops::Range;
2
+ use std::path::Path;
3
+ use unicode_width::UnicodeWidthStr;
4
+
5
+ #[derive(Debug, Clone, PartialEq, Eq)]
6
+ pub(crate) struct CollapsedPaste {
7
+ pub range: Range<usize>,
8
+ pub text: String,
9
+ }
10
+
11
+ pub(crate) fn normalize_prompt_newlines(text: &str) -> String {
12
+ text.replace("\r\n", "\n").replace('\r', "\n")
13
+ }
14
+
15
+ pub(crate) fn input_row_start(chars: &[char], rows: &[String], row_index: usize) -> usize {
16
+ let mut start = 0;
17
+ for row in rows.iter().take(row_index) {
18
+ start += row.chars().count();
19
+ if chars.get(start) == Some(&'\n') {
20
+ start += 1;
21
+ }
22
+ }
23
+ start
24
+ }
25
+
26
+ pub(crate) fn wrap_input_visual_into_rows(
27
+ input_chars: &[char],
28
+ row_width: usize,
29
+ cursor_pos: usize,
30
+ ) -> (Vec<String>, usize, usize) {
31
+ let row_width = row_width.max(1);
32
+ let mut rows: Vec<String> = Vec::new();
33
+ let mut row_starts = Vec::new();
34
+ let mut row_start = 0usize;
35
+ let mut visual_width = 0usize;
36
+
37
+ for (index, character) in input_chars.iter().copied().enumerate() {
38
+ if character == '\n' {
39
+ rows.push(input_chars[row_start..index].iter().collect());
40
+ row_starts.push(row_start);
41
+ row_start = index + 1;
42
+ visual_width = 0;
43
+ continue;
44
+ }
45
+
46
+ let character_width = UnicodeWidthStr::width(character.to_string().as_str());
47
+ if visual_width > 0 && visual_width + character_width > row_width {
48
+ rows.push(input_chars[row_start..index].iter().collect());
49
+ row_starts.push(row_start);
50
+ row_start = index;
51
+ visual_width = 0;
52
+ }
53
+ visual_width += character_width;
54
+ }
55
+
56
+ if row_start < input_chars.len() || input_chars.last() == Some(&'\n') || rows.is_empty() {
57
+ rows.push(input_chars[row_start..].iter().collect());
58
+ row_starts.push(row_start);
59
+ }
60
+
61
+ let cursor_pos = cursor_pos.min(input_chars.len());
62
+ let mut cursor_row = rows.len().saturating_sub(1);
63
+ for (index, start) in row_starts.iter().copied().enumerate() {
64
+ let end = start + rows[index].chars().count();
65
+ let next_starts_here = row_starts.get(index + 1) == Some(&cursor_pos);
66
+ if cursor_pos < end || (cursor_pos == end && !next_starts_here) {
67
+ cursor_row = index;
68
+ break;
69
+ }
70
+ if cursor_pos == start {
71
+ cursor_row = index;
72
+ break;
73
+ }
74
+ }
75
+ let cursor_col = cursor_pos.saturating_sub(row_starts[cursor_row]);
76
+ (rows, cursor_row, cursor_col)
77
+ }
78
+
79
+ /// Shared `/`, `$`, and `@` completion matcher used across Full-Screen TUI and Classic CLI.
80
+ pub(crate) fn match_composer_suggestions(
81
+ chars: &[char],
82
+ cursor: usize,
83
+ current_dir: &Path,
84
+ ) -> Vec<(String, String)> {
85
+ let cursor = cursor.min(chars.len());
86
+ let before: String = chars[..cursor].iter().collect();
87
+ if before.starts_with('/') {
88
+ let prefix = before.to_lowercase();
89
+ let has_space = before.contains(char::is_whitespace);
90
+ let query = &prefix[1..];
91
+ let mut matches: Vec<_> = super::SLASH_COMMANDS
92
+ .iter()
93
+ .filter(|command| {
94
+ if !command.on_cli() || !command.token.starts_with('/') {
95
+ return false;
96
+ }
97
+ let token_lower = command.token.to_lowercase();
98
+ if has_space {
99
+ token_lower.starts_with(&prefix)
100
+ } else {
101
+ token_lower.starts_with(&prefix)
102
+ || command.description.to_lowercase().contains(query)
103
+ }
104
+ })
105
+ .collect();
106
+ if !matches.is_empty() {
107
+ matches.sort_by_key(|command| !command.token.to_lowercase().starts_with(&prefix));
108
+ return matches
109
+ .into_iter()
110
+ .map(|command| (command.token.clone(), command.description.clone()))
111
+ .collect();
112
+ }
113
+ }
114
+ if before.starts_with('$') && !before.contains(char::is_whitespace) {
115
+ let prefix = before[1..].to_lowercase();
116
+ return super::load_all_available_skills(current_dir)
117
+ .into_iter()
118
+ .filter(|skill| skill.name.to_lowercase().starts_with(&prefix))
119
+ .map(|skill| {
120
+ (
121
+ format!("${}", skill.name),
122
+ skill.description.unwrap_or_default(),
123
+ )
124
+ })
125
+ .collect();
126
+ }
127
+ let start = chars[..cursor]
128
+ .iter()
129
+ .rposition(|c| c.is_whitespace())
130
+ .map_or(0, |index| index + 1);
131
+ if chars.get(start) == Some(&'@') {
132
+ let query: String = chars[start..cursor].iter().collect();
133
+ return super::collect_mention_candidates(&query, current_dir)
134
+ .into_iter()
135
+ .map(|candidate| (candidate.label, candidate.description))
136
+ .collect();
137
+ }
138
+ Vec::new()
139
+ }
140
+
141
+ #[cfg(test)]
142
+ mod tests {
143
+ use super::*;
144
+
145
+ #[test]
146
+ fn wrap_input_visual_handles_newlines_and_thai_marks() {
147
+ let chars: Vec<char> = "สวัสดี\nโลก".chars().collect();
148
+ let (rows, row, col) = wrap_input_visual_into_rows(&chars, 20, chars.len());
149
+ assert_eq!(rows, vec!["สวัสดี".to_string(), "โลก".to_string()]);
150
+ assert_eq!(row, 1);
151
+ assert_eq!(col, 3);
152
+ }
153
+
154
+ #[test]
155
+ fn match_composer_suggestions_matches_slash_commands() {
156
+ let chars: Vec<char> = "/res".chars().collect();
157
+ let results = match_composer_suggestions(&chars, chars.len(), Path::new("."));
158
+ assert!(results.iter().any(|(token, _)| token == "/resume"));
159
+
160
+ let chars_r: Vec<char> = "/r".chars().collect();
161
+ let results_r = match_composer_suggestions(&chars_r, chars_r.len(), Path::new("."));
162
+ let resume_pos = results_r.iter().position(|(t, _)| t == "/resume");
163
+ let retry_pos = results_r.iter().position(|(t, _)| t == "/retry");
164
+ assert!(resume_pos.is_some() && retry_pos.is_some());
165
+ assert!(resume_pos.unwrap() < retry_pos.unwrap());
166
+ }
167
+
168
+ #[test]
169
+ fn match_composer_suggestions_matches_plan_list_order_and_subcommand() {
170
+ let chars_p: Vec<char> = "/p".chars().collect();
171
+ let results_p = match_composer_suggestions(&chars_p, chars_p.len(), Path::new("."));
172
+ let plan_idx = results_p.iter().position(|(t, _)| t == "/plan");
173
+ let plan_list_idx = results_p.iter().position(|(t, _)| t == "/plan list");
174
+ assert!(plan_idx.is_some());
175
+ assert!(plan_list_idx.is_some());
176
+ assert_eq!(plan_list_idx.unwrap(), plan_idx.unwrap() + 1);
177
+
178
+ let chars_plan_space: Vec<char> = "/plan ".chars().collect();
179
+ let results_plan =
180
+ match_composer_suggestions(&chars_plan_space, chars_plan_space.len(), Path::new("."));
181
+ assert_eq!(
182
+ results_plan.first().map(|(t, _)| t.as_str()),
183
+ Some("/plan list")
184
+ );
185
+ }
186
+ }
@@ -59,7 +59,7 @@ pub fn format_workspace_with_branch(path: &Path) -> String {
59
59
  /// matching on a fixed prefix is safe — user-typed text starting with the
60
60
  /// same characters is vanishingly unlikely and, worst case, only affects
61
61
  /// display color, not the text actually sent.
62
- const PLACEHOLDER_PREFIXES: [&str; 2] = ["[Pasted text #", "[Image #"];
62
+ const PLACEHOLDER_PREFIXES: [&str; 3] = ["[Pasted text #", "[Pasted Content ", "[Image #"];
63
63
 
64
64
  fn matches_placeholder_prefix(chars: &[char], i: usize) -> bool {
65
65
  PLACEHOLDER_PREFIXES.iter().any(|prefix| {
@@ -92,15 +92,90 @@ pub fn format_placeholders(s: &str) -> String {
92
92
  }
93
93
  result
94
94
  }
95
+
95
96
  pub fn is_thai_zero_width(c: char) -> bool {
96
97
  let cp = c as u32;
97
98
  cp == 0x0E31 || (0x0E34..=0x0E3A).contains(&cp) || (0x0E47..=0x0E4E).contains(&cp)
98
99
  }
100
+
101
+ pub fn is_cjk_wide(c: char) -> bool {
102
+ let val = c as u32;
103
+ (0x4E00..=0x9FFF).contains(&val)
104
+ || (0x3400..=0x4DBF).contains(&val)
105
+ || (0x20000..=0x2A6DF).contains(&val)
106
+ || (0xAC00..=0xD7AF).contains(&val)
107
+ || (0x3040..=0x309F).contains(&val)
108
+ || (0x30A0..=0x30FF).contains(&val)
109
+ }
110
+
99
111
  pub fn char_visual_width(c: char) -> usize {
100
- if is_thai_zero_width(c) { 0 } else { 1 }
112
+ if is_thai_zero_width(c) {
113
+ 0
114
+ } else if is_cjk_wide(c) {
115
+ 2
116
+ } else {
117
+ unicode_width::UnicodeWidthChar::width(c).unwrap_or(1)
118
+ }
101
119
  }
120
+
102
121
  pub fn string_visual_width(s: &str) -> usize {
103
- s.chars().map(char_visual_width).sum()
122
+ display_width(s)
123
+ }
124
+
125
+ /// Canonical terminal display width accounting for ANSI CSI sequences, Thai
126
+ /// zero-width combining marks, and East-Asian / emoji wide glyphs.
127
+ pub fn display_width(s: &str) -> usize {
128
+ let mut count = 0;
129
+ let mut chars = s.chars().peekable();
130
+ while let Some(c) = chars.next() {
131
+ if c == '\u{1b}' && chars.peek() == Some(&'[') {
132
+ chars.next();
133
+ for c2 in chars.by_ref() {
134
+ if c2.is_ascii_alphabetic() {
135
+ break;
136
+ }
137
+ }
138
+ continue;
139
+ }
140
+ if is_thai_zero_width(c) {
141
+ continue;
142
+ }
143
+ if is_cjk_wide(c) {
144
+ count += 2;
145
+ } else {
146
+ count += unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
147
+ }
148
+ }
149
+ count
150
+ }
151
+
152
+ /// Canonical width-aware string clipper with `…` ellipsis used across TUI
153
+ /// widgets (`resume_picker`, `tool_activity`, `chat_tui`, etc.).
154
+ pub fn truncate_to_width(text: &str, max_width: usize) -> String {
155
+ if display_width(text) <= max_width {
156
+ return text.to_owned();
157
+ }
158
+ if max_width == 0 {
159
+ return String::new();
160
+ }
161
+ let mut result = String::new();
162
+ let mut width = 0usize;
163
+ for ch in text.chars() {
164
+ let ch_width = if is_thai_zero_width(ch) {
165
+ 0
166
+ } else if is_cjk_wide(ch) {
167
+ 2
168
+ } else {
169
+ unicode_width::UnicodeWidthChar::width(ch).unwrap_or(0)
170
+ };
171
+ if width + ch_width > max_width.saturating_sub(1) {
172
+ break;
173
+ }
174
+ result.push(ch);
175
+ width += ch_width;
176
+ }
177
+ result.push('…');
178
+ result
104
179
  }
105
180
  pub fn active_model<'a>(provider: &str, config: &'a mint_core::MintConfig) -> &'a str {
106
181
  match provider {
@@ -114,6 +114,14 @@ impl MentionKind {
114
114
  }
115
115
 
116
116
  pub fn collect_mention_candidates(query: &str, current_dir: &Path) -> Vec<MentionCandidate> {
117
+ collect_mention_candidates_with_recents(query, current_dir, &[])
118
+ }
119
+
120
+ pub fn collect_mention_candidates_with_recents(
121
+ query: &str,
122
+ current_dir: &Path,
123
+ recent_files: &[String],
124
+ ) -> Vec<MentionCandidate> {
117
125
  let prefix = query.strip_prefix('@').unwrap_or(query).to_lowercase();
118
126
  let mut candidates = Vec::new();
119
127
 
@@ -156,7 +164,43 @@ pub fn collect_mention_candidates(query: &str, current_dir: &Path) -> Vec<Mentio
156
164
  }
157
165
  }
158
166
 
159
- // 3. Workspace files and folders
167
+ // 3. Recently modified/written session files (prioritized)
168
+ let mut auto_recents = Vec::new();
169
+ let effective_recents: &[String] = if !recent_files.is_empty() {
170
+ recent_files
171
+ } else {
172
+ if let Ok(changes) = mint_core::git::diff::read_workspace_git_diff(current_dir) {
173
+ for c in changes {
174
+ auto_recents.push(c.path);
175
+ }
176
+ }
177
+ &auto_recents
178
+ };
179
+
180
+ let mut seen_recents = std::collections::HashSet::new();
181
+ for file in effective_recents {
182
+ let clean = file.trim_start_matches('@').trim();
183
+ if clean.is_empty() {
184
+ continue;
185
+ }
186
+ let file_name = std::path::Path::new(clean)
187
+ .file_name()
188
+ .map(|s| s.to_string_lossy().to_string())
189
+ .unwrap_or_else(|| clean.to_string());
190
+ if file_name.to_lowercase().starts_with(&prefix)
191
+ || clean.to_lowercase().starts_with(&prefix)
192
+ || prefix.is_empty()
193
+ {
194
+ seen_recents.insert(file_name.clone());
195
+ candidates.push(MentionCandidate {
196
+ label: format!("@{file_name}"),
197
+ description: "Recently edited in this session".into(),
198
+ kind: MentionKind::File,
199
+ });
200
+ }
201
+ }
202
+
203
+ // 4. Workspace files and folders
160
204
  if let Ok(entries) = std::fs::read_dir(current_dir) {
161
205
  let mut file_entries = Vec::new();
162
206
  for entry in entries.flatten() {
@@ -166,6 +210,7 @@ pub fn collect_mention_candidates(query: &str, current_dir: &Path) -> Vec<Mentio
166
210
  || name == "node_modules"
167
211
  || name == "dist"
168
212
  || name == "build"
213
+ || seen_recents.contains(&name)
169
214
  {
170
215
  continue;
171
216
  }
@@ -1827,4 +1872,19 @@ mod tests {
1827
1872
  );
1828
1873
  }
1829
1874
  }
1875
+
1876
+ #[test]
1877
+ fn mention_candidates_prioritize_recently_edited_files() {
1878
+ let temp_dir = std::env::current_dir().unwrap();
1879
+ let recents = vec![
1880
+ "generate_harness_pdf.py".to_string(),
1881
+ "CalculationCard.tsx".to_string(),
1882
+ ];
1883
+ let candidates = collect_mention_candidates_with_recents("@Calc", &temp_dir, &recents);
1884
+ assert!(
1885
+ candidates
1886
+ .iter()
1887
+ .any(|c| c.label == "@CalculationCard.tsx" && c.description.contains("Recent"))
1888
+ );
1889
+ }
1830
1890
  }