@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
@@ -49,68 +49,90 @@ pub(super) fn print_diff_header(action: &str, path: &str, additions: usize, dele
49
49
  );
50
50
  }
51
51
 
52
- /// Prints one diff row as a full-width color band (like a diff viewer's gutter
53
- /// highlight), padding `content` with spaces out to the terminal width so the
54
- /// background color fills the row instead of just wrapping the text.
55
- pub(super) fn print_diff_band(bg: &str, line_num: usize, content: &str, term_width: usize) {
56
- let line_num_str = format!("{:>5}", line_num);
57
- let prefix_visible_len = 2 + line_num_str.chars().count() + 1;
58
- // Use the app's East-Asian/Thai-combining-mark-aware width, not a raw
59
- // char count — a naive count over/under-shoots the true terminal column
60
- // width for wide glyphs and zero-width Thai marks, leaving the band short
61
- // of (or past) the terminal edge.
62
- let available_width = term_width.saturating_sub(prefix_visible_len).max(1);
63
- let blank_line_num = " ".repeat(line_num_str.chars().count());
64
-
65
- // A code/diff line longer than the terminal is hard-wrapped (never
66
- // word-wrapped, which would collapse indentation) into multiple physical
67
- // lines so the colored band never overflows past the terminal edge — the
68
- // same class of bug the markdown table renderer had.
69
- for (i, wrapped_line) in markdown::hard_wrap(content, available_width)
70
- .into_iter()
71
- .enumerate()
72
- {
73
- let line_label = if i == 0 {
74
- &line_num_str
75
- } else {
76
- &blank_line_num
77
- };
78
- let content_len = crate::interactive::string_visual_width(&wrapped_line);
79
- let pad_len = available_width.saturating_sub(content_len);
80
- println!(
81
- " {DIM}{line_label}{RESET} {bg}{wrapped_line}{}{RESET}",
82
- " ".repeat(pad_len)
83
- );
84
- }
85
- }
86
-
87
- pub(super) fn print_colored_diff(diff: &str) {
88
- let (term_width, _) = markdown::terminal_size_or_default();
89
- let term_width = term_width as usize;
52
+ pub(crate) fn format_colored_diff_lines(diff: &str, term_width: usize) -> Vec<String> {
53
+ let term_width = term_width.max(20);
54
+ let mut out = Vec::new();
90
55
  let mut current_old_line = 1;
91
56
  let mut current_new_line = 1;
92
57
 
58
+ let push_band = |out: &mut Vec<String>, bg: &str, line_num: usize, content: &str| {
59
+ let line_num_str = format!("{:>5}", line_num);
60
+ let prefix_visible_len = 2 + line_num_str.chars().count() + 1;
61
+ let available_width = term_width.saturating_sub(prefix_visible_len).max(1);
62
+ let blank_line_num = " ".repeat(line_num_str.chars().count());
63
+
64
+ for (i, wrapped_line) in markdown::hard_wrap(content, available_width)
65
+ .into_iter()
66
+ .enumerate()
67
+ {
68
+ let line_label = if i == 0 {
69
+ &line_num_str
70
+ } else {
71
+ &blank_line_num
72
+ };
73
+ let content_len = crate::interactive::string_visual_width(&wrapped_line);
74
+ let pad_len = available_width.saturating_sub(content_len);
75
+ out.push(format!(
76
+ " {DIM}{line_label}{RESET} {bg}{wrapped_line}{}{RESET}",
77
+ " ".repeat(pad_len)
78
+ ));
79
+ }
80
+ };
81
+
93
82
  for line in diff.lines() {
94
83
  if line.starts_with("@@") {
95
- // Still track line numbers from the hunk header, just don't print
96
- // the raw unified-diff header/marker lines — they're noise here.
97
84
  if let Some((old_s, new_s)) = parse_hunk_header(line) {
98
85
  current_old_line = old_s;
99
86
  current_new_line = new_s;
100
87
  }
101
88
  } else if line.starts_with("--- ") || line.starts_with("+++ ") {
102
- // skip: raw diff file-header lines aren't useful in an approval prompt
89
+ // skip raw diff file-header lines
103
90
  } else if let Some(content) = line.strip_prefix('-') {
104
- print_diff_band(BG_DEL, current_old_line, content, term_width);
91
+ push_band(&mut out, BG_DEL, current_old_line, content);
105
92
  current_old_line += 1;
106
93
  } else if let Some(content) = line.strip_prefix('+') {
107
- print_diff_band(BG_ADD, current_new_line, content, term_width);
94
+ push_band(&mut out, BG_ADD, current_new_line, content);
108
95
  current_new_line += 1;
109
96
  } else {
110
97
  let line_num_str = format!("{:>5}", current_new_line);
111
98
  current_old_line += 1;
112
99
  current_new_line += 1;
113
- println!(" {DIM}{line_num_str}{RESET} {line}");
100
+ out.push(format!(" {DIM}{line_num_str}{RESET} {line}"));
114
101
  }
115
102
  }
103
+ out
104
+ }
105
+
106
+ #[allow(dead_code)]
107
+ pub(crate) fn render_diff_to_ratatui_lines(
108
+ diff: &str,
109
+ term_width: usize,
110
+ ) -> Vec<ratatui::text::Line<'static>> {
111
+ let ansi_joined = format_colored_diff_lines(diff, term_width).join("\n");
112
+ if let Ok(parsed) = ansi_joined.into_text() {
113
+ parsed.lines
114
+ } else {
115
+ diff.lines()
116
+ .map(|l| ratatui::text::Line::raw(l.to_string()))
117
+ .collect()
118
+ }
119
+ }
120
+
121
+ pub(super) fn print_colored_diff(diff: &str) {
122
+ let (term_width, _) = markdown::terminal_size_or_default();
123
+ for line in format_colored_diff_lines(diff, term_width as usize) {
124
+ println!("{line}");
125
+ }
126
+ }
127
+
128
+ #[cfg(test)]
129
+ mod tests {
130
+ use super::*;
131
+
132
+ #[test]
133
+ fn render_diff_to_ratatui_lines_formats_hunks_at_requested_width() {
134
+ let diff = "@@ -1,2 +1,2 @@\n-old line\n+new line\n context";
135
+ let lines = render_diff_to_ratatui_lines(diff, 60);
136
+ assert_eq!(lines.len(), 3);
137
+ }
116
138
  }
@@ -204,8 +204,16 @@ fn ramp_estimate(estimated: u64, elapsed_secs: f64) -> u64 {
204
204
  }
205
205
 
206
206
  pub(super) fn render_live_summary(summary: &str) {
207
+ use std::io::IsTerminal;
207
208
  let (tw, _) = markdown::terminal_size_or_default();
208
209
  let width = tw as usize;
210
+ let total_lines = summary.lines().count().max(1);
211
+ let pace_delay = if io::stdout().is_terminal() {
212
+ let per_line_ms = (500 / total_lines as u64).clamp(4, 16);
213
+ Duration::from_millis(per_line_ms)
214
+ } else {
215
+ Duration::ZERO
216
+ };
209
217
 
210
218
  let mut is_first = true;
211
219
  let mut table_buffer: Vec<String> = Vec::new();
@@ -219,6 +227,10 @@ pub(super) fn render_live_summary(summary: &str) {
219
227
  if !table_buffer.is_empty() {
220
228
  print_table_block(&table_buffer, &mut is_first);
221
229
  table_buffer.clear();
230
+ if !pace_delay.is_zero() {
231
+ let _ = io::stdout().flush();
232
+ std::thread::sleep(pace_delay);
233
+ }
222
234
  }
223
235
 
224
236
  let indent = if is_first { "" } else { " " };
@@ -234,6 +246,10 @@ pub(super) fn render_live_summary(summary: &str) {
234
246
  } else {
235
247
  print!("\n{wrapped}");
236
248
  }
249
+ if !pace_delay.is_zero() {
250
+ let _ = io::stdout().flush();
251
+ std::thread::sleep(pace_delay);
252
+ }
237
253
  }
238
254
 
239
255
  if !table_buffer.is_empty() {
@@ -544,13 +560,14 @@ pub(super) fn explored_action_label(
544
560
  }
545
561
 
546
562
  pub(super) fn display_tool_target(path: &str) -> String {
547
- if path.trim().is_empty() {
548
- ".".into()
563
+ let trimmed = path.trim();
564
+ if trimmed.is_empty() || trimmed == "." || trimmed == "./" {
565
+ "workspace".into()
549
566
  } else {
550
- Path::new(path)
567
+ Path::new(trimmed)
551
568
  .file_name()
552
569
  .and_then(|name| name.to_str())
553
- .unwrap_or(path)
570
+ .unwrap_or(trimmed)
554
571
  .into()
555
572
  }
556
573
  }
@@ -271,9 +271,251 @@ fn try_render_single_line_custom_block(raw: &str, term_width: usize) -> Option<V
271
271
  None
272
272
  }
273
273
 
274
+ pub(crate) fn is_atomic_fenced_lang(lang: &str) -> bool {
275
+ matches!(
276
+ lang.to_ascii_lowercase().as_str(),
277
+ "ui-grid"
278
+ | "ui_grid"
279
+ | "ui-grid-json"
280
+ | "ui_grid_json"
281
+ | "ui-card"
282
+ | "ui_card"
283
+ | "ui-card-json"
284
+ | "ui_card_json"
285
+ | "ui-feature"
286
+ | "ui_feature"
287
+ | "ui-mockup"
288
+ | "ui_mockup"
289
+ | "ui-mockup-json"
290
+ | "ui_mockup_json"
291
+ | "weather_json"
292
+ | "weather-json"
293
+ | "stock_json"
294
+ | "stock-json"
295
+ | "calculation_json"
296
+ | "calculation-json"
297
+ | "image_search_json"
298
+ | "image-search-json"
299
+ | "image_gen_json"
300
+ | "image-gen-json"
301
+ | "mermaid"
302
+ )
303
+ }
304
+
305
+ /// Splits Markdown into progressive reveal units:
306
+ /// - Regular text, headings, lists, callouts, and standard code blocks split line-by-line.
307
+ /// - Markdown tables (`| ... |`) group all consecutive rows into a single atomic unit.
308
+ /// - Custom UI/JSON/Mermaid fenced blocks (`ui-card`, `ui-grid`, `ui-mockup`, etc.) group the
309
+ /// entire fenced block into a single atomic unit so raw JSON never flashes mid-stream.
310
+ ///
311
+ /// When `is_complete` is `false`, trailing incomplete lines, open tables, and unclosed atomic
312
+ /// fenced blocks are held back until completed.
313
+ pub(crate) fn split_progressive_units(raw: &str, is_complete: bool) -> Vec<String> {
314
+ if raw.is_empty() {
315
+ return Vec::new();
316
+ }
317
+ let slice = if is_complete || raw.ends_with('\n') {
318
+ raw
319
+ } else if let Some(last_nl) = raw.rfind('\n') {
320
+ &raw[..=last_nl]
321
+ } else {
322
+ return Vec::new();
323
+ };
324
+
325
+ let mut units = Vec::new();
326
+ let mut in_code_block = false;
327
+ let mut pending_code_fence: Option<(String, bool)> = None;
328
+ let mut in_atomic_fence = false;
329
+ let mut atomic_fence_buffer: Vec<String> = Vec::new();
330
+ let mut table_buffer: Vec<String> = Vec::new();
331
+
332
+ for raw_line in slice.lines() {
333
+ let line = raw_line.strip_suffix('\r').unwrap_or(raw_line);
334
+ let trimmed = line.trim_start();
335
+
336
+ if !in_code_block && !in_atomic_fence && markdown::is_table_line(trimmed) {
337
+ table_buffer.push(line.to_owned());
338
+ continue;
339
+ }
340
+
341
+ if !table_buffer.is_empty() {
342
+ units.push(table_buffer.join("\n"));
343
+ table_buffer.clear();
344
+ }
345
+
346
+ if trimmed.starts_with("```") {
347
+ if !in_code_block && !in_atomic_fence {
348
+ let after_open = trimmed.trim_start_matches('`').trim();
349
+ if after_open.ends_with("```") && after_open.len() > 3 {
350
+ units.push(line.to_owned());
351
+ continue;
352
+ }
353
+ let lang = if let Some(idx) =
354
+ after_open.find(|c: char| c.is_whitespace() || c == '[' || c == '{')
355
+ {
356
+ &after_open[..idx]
357
+ } else {
358
+ after_open
359
+ };
360
+ if is_atomic_fenced_lang(lang) {
361
+ in_atomic_fence = true;
362
+ atomic_fence_buffer.clear();
363
+ atomic_fence_buffer.push(line.to_owned());
364
+ } else {
365
+ in_code_block = true;
366
+ let can_promote = lang.is_empty() || lang.eq_ignore_ascii_case("plaintext");
367
+ pending_code_fence = Some((line.to_owned(), can_promote));
368
+ }
369
+ } else if in_atomic_fence {
370
+ atomic_fence_buffer.push(line.to_owned());
371
+ units.push(atomic_fence_buffer.join("\n"));
372
+ atomic_fence_buffer.clear();
373
+ in_atomic_fence = false;
374
+ } else {
375
+ in_code_block = false;
376
+ if let Some((fence, _)) = pending_code_fence.take() {
377
+ units.push(format!("{fence}\n{line}"));
378
+ } else {
379
+ units.push(line.to_owned());
380
+ }
381
+ }
382
+ continue;
383
+ }
384
+
385
+ if in_atomic_fence {
386
+ atomic_fence_buffer.push(line.to_owned());
387
+ continue;
388
+ }
389
+
390
+ if let Some((fence, can_promote)) = pending_code_fence.take() {
391
+ let first_token = trimmed
392
+ .find(|c: char| c.is_whitespace() || c == '[' || c == '{')
393
+ .map_or(trimmed, |idx| &trimmed[..idx]);
394
+ if can_promote && is_atomic_fenced_lang(first_token) {
395
+ in_code_block = false;
396
+ in_atomic_fence = true;
397
+ atomic_fence_buffer.clear();
398
+ atomic_fence_buffer.push(fence);
399
+ atomic_fence_buffer.push(line.to_owned());
400
+ } else {
401
+ units.push(format!("{fence}\n{line}"));
402
+ }
403
+ continue;
404
+ }
405
+
406
+ units.push(line.to_owned());
407
+ }
408
+
409
+ if is_complete {
410
+ if !table_buffer.is_empty() {
411
+ units.push(table_buffer.join("\n"));
412
+ }
413
+ if let Some((fence, _)) = pending_code_fence.take() {
414
+ units.push(fence);
415
+ }
416
+ if !atomic_fence_buffer.is_empty() {
417
+ units.push(atomic_fence_buffer.join("\n"));
418
+ }
419
+ }
420
+
421
+ units
422
+ }
423
+
424
+ #[derive(Debug, Default, Clone)]
425
+ pub(crate) struct ProgressiveMarkdownPacer {
426
+ raw_buffer: String,
427
+ completed: bool,
428
+ revealed_units: usize,
429
+ }
430
+
431
+ impl ProgressiveMarkdownPacer {
432
+ pub fn push_delta(&mut self, delta: &str) {
433
+ if !delta.is_empty() {
434
+ self.raw_buffer.push_str(delta);
435
+ }
436
+ }
437
+
438
+ pub fn finish_with_authoritative(&mut self, full_text: &str) {
439
+ self.raw_buffer.clear();
440
+ self.raw_buffer.push_str(full_text);
441
+ self.completed = true;
442
+ }
443
+
444
+ pub fn is_completed(&self) -> bool {
445
+ self.completed
446
+ }
447
+
448
+ pub fn is_done(&self) -> bool {
449
+ if !self.completed {
450
+ return false;
451
+ }
452
+ let units = split_progressive_units(&self.raw_buffer, true);
453
+ self.revealed_units >= units.len()
454
+ }
455
+
456
+ pub fn step(&mut self) -> Option<String> {
457
+ let units = split_progressive_units(&self.raw_buffer, self.completed);
458
+ if self.revealed_units >= units.len() {
459
+ return None;
460
+ }
461
+ let remaining = units.len() - self.revealed_units;
462
+ let advance = if remaining <= 12 {
463
+ 1
464
+ } else {
465
+ (remaining / 10).clamp(1, 4)
466
+ };
467
+ self.revealed_units = (self.revealed_units + advance).min(units.len());
468
+ Some(units[..self.revealed_units].join("\n"))
469
+ }
470
+
471
+ pub fn flush_all(&mut self) -> Option<String> {
472
+ self.completed = true;
473
+ let units = split_progressive_units(&self.raw_buffer, true);
474
+ if units.is_empty() {
475
+ return None;
476
+ }
477
+ self.revealed_units = units.len();
478
+ Some(units.join("\n"))
479
+ }
480
+ }
481
+
274
482
  pub(crate) fn format_markdown_bold(text: &str) -> String {
275
483
  let (term_width, _) = markdown::terminal_size_or_default();
276
- let term_width = term_width as usize;
484
+ format_markdown_bold_with_width(text, term_width as usize)
485
+ }
486
+
487
+ pub(crate) fn render_markdown_to_lines(
488
+ text: &str,
489
+ term_width: usize,
490
+ ) -> (Vec<ratatui::text::Line<'static>>, Vec<String>) {
491
+ if text.is_empty() {
492
+ return (Vec::new(), Vec::new());
493
+ }
494
+ let processed = format_markdown_bold_with_width(text, term_width.max(20));
495
+ if let Ok(parsed) = processed.into_text() {
496
+ let rendered = parsed.lines;
497
+ let plain: Vec<String> = rendered
498
+ .iter()
499
+ .map(|line| {
500
+ line.spans
501
+ .iter()
502
+ .map(|span| span.content.as_ref())
503
+ .collect::<String>()
504
+ })
505
+ .collect();
506
+ (rendered, plain)
507
+ } else {
508
+ let rendered: Vec<ratatui::text::Line<'static>> = text
509
+ .lines()
510
+ .map(|l| ratatui::text::Line::raw(l.to_string()))
511
+ .collect();
512
+ let plain: Vec<String> = text.lines().map(str::to_owned).collect();
513
+ (rendered, plain)
514
+ }
515
+ }
516
+
517
+ pub(crate) fn format_markdown_bold_with_width(text: &str, term_width: usize) -> String {
518
+ let term_width = term_width.max(20);
277
519
 
278
520
  let mut formatted_lines = Vec::new();
279
521
  let mut in_code_block = false;
@@ -282,7 +524,7 @@ pub(crate) fn format_markdown_bold(text: &str) -> String {
282
524
  let mut custom_block_buffer: Vec<String> = Vec::new();
283
525
  let mut active_alert_color: Option<&'static str> = None;
284
526
  // Markdown table buffering: collect consecutive `|...|` lines so we can
285
- // flush them through `render_markdown_table` as a unit (same as classic
527
+ // flush them through `render_markdown_table_with_width` as a unit (same as classic
286
528
  // mode in `render_live_summary`).
287
529
  let mut table_buffer: Vec<String> = Vec::new();
288
530
 
@@ -290,7 +532,10 @@ pub(crate) fn format_markdown_bold(text: &str) -> String {
290
532
  macro_rules! flush_table {
291
533
  () => {
292
534
  if !table_buffer.is_empty() {
293
- let rendered = markdown::render_markdown_table(&table_buffer);
535
+ let rendered = markdown::render_markdown_table_with_width(
536
+ &table_buffer,
537
+ term_width.min(u16::MAX as usize) as u16,
538
+ );
294
539
  for tl in rendered.split('\n') {
295
540
  formatted_lines.push(tl.to_string());
296
541
  }
@@ -347,7 +592,7 @@ pub(crate) fn format_markdown_bold(text: &str) -> String {
347
592
  } else {
348
593
  formatted_lines.push(code_block_top_border(&custom_lang, term_width));
349
594
  for l in buffered.lines() {
350
- formatted_line = format!("{CODE_BORDER}│{RESET} {l}");
595
+ formatted_line = format!(" {l}");
351
596
  formatted_lines.push(formatted_line);
352
597
  }
353
598
  formatted_lines.push(code_block_bottom_border(term_width));
@@ -395,7 +640,7 @@ pub(crate) fn format_markdown_bold(text: &str) -> String {
395
640
  Some(h) => highlight_code_line(h, line),
396
641
  None => line.to_string(),
397
642
  };
398
- formatted_line = format!("{CODE_BORDER}│{RESET} {content}{RESET}");
643
+ formatted_line = format!(" {content}{RESET}");
399
644
  formatted_lines.push(formatted_line);
400
645
  continue;
401
646
  }
@@ -767,4 +1012,96 @@ mod tests {
767
1012
  assert!(output_grid.contains("OptionA"));
768
1013
  assert!(output_grid.contains("Best choice"));
769
1014
  }
1015
+
1016
+ #[test]
1017
+ fn test_progressive_units_splits_text_and_code_line_by_line_but_groups_tables_and_ui_cards() {
1018
+ let doc = "\
1019
+ # Heading
1020
+ Paragraph line 1
1021
+ | Col A | Col B |
1022
+ | --- | --- |
1023
+ | 1 | 2 |
1024
+ After table
1025
+ ```rust
1026
+ fn main() {}
1027
+ ```
1028
+ ```ui-card
1029
+ [{\"title\": \"CardTitle\", \"subtitle\": \"Sub\"}]
1030
+ ```
1031
+ Final line";
1032
+
1033
+ let units = split_progressive_units(doc, true);
1034
+ assert_eq!(
1035
+ units,
1036
+ vec![
1037
+ "# Heading",
1038
+ "Paragraph line 1",
1039
+ "| Col A | Col B |\n| --- | --- |\n| 1 | 2 |",
1040
+ "After table",
1041
+ "```rust\nfn main() {}",
1042
+ "```",
1043
+ "```ui-card\n[{\"title\": \"CardTitle\", \"subtitle\": \"Sub\"}]\n```",
1044
+ "Final line",
1045
+ ]
1046
+ );
1047
+ }
1048
+
1049
+ #[test]
1050
+ fn test_progressive_units_holds_incomplete_table_and_unclosed_ui_card_while_streaming() {
1051
+ let streaming_table = "Intro line\n| Header 1 | Header 2 |\n| --- | --- |\n";
1052
+ let units = split_progressive_units(streaming_table, false);
1053
+ assert_eq!(units, vec!["Intro line"]);
1054
+
1055
+ let streaming_card = "Intro line\n```ui-grid\n[{\"title\": \"A\"}]\n";
1056
+ let units_card = split_progressive_units(streaming_card, false);
1057
+ assert_eq!(units_card, vec!["Intro line"]);
1058
+
1059
+ // Bare opening fence without its first code line is held so an empty code box never flashes
1060
+ let bare_fence = "Intro line\n```rust\n";
1061
+ assert_eq!(
1062
+ split_progressive_units(bare_fence, false),
1063
+ vec!["Intro line"]
1064
+ );
1065
+
1066
+ let streaming_code = "Intro line\n```rust\nlet x = 1;\nlet y = 2;\n";
1067
+ let units_code = split_progressive_units(streaming_code, false);
1068
+ assert_eq!(
1069
+ units_code,
1070
+ vec!["Intro line", "```rust\nlet x = 1;", "let y = 2;"]
1071
+ );
1072
+
1073
+ // Unlabeled fence whose first line is a ui-card keyword is promoted to atomic block
1074
+ let unlabeled_card = "Intro line\n```\nui-card [\n {\"title\": \"A\"}\n]\n";
1075
+ assert_eq!(
1076
+ split_progressive_units(unlabeled_card, false),
1077
+ vec!["Intro line"]
1078
+ );
1079
+ let closed_unlabeled_card = "Intro line\n```\nui-card [\n {\"title\": \"A\"}\n]\n```\n";
1080
+ assert_eq!(
1081
+ split_progressive_units(closed_unlabeled_card, false),
1082
+ vec!["Intro line", "```\nui-card [\n {\"title\": \"A\"}\n]\n```"]
1083
+ );
1084
+ }
1085
+
1086
+ #[test]
1087
+ fn test_progressive_markdown_pacer_reveals_units_progressively_and_flushes() {
1088
+ let mut pacer = ProgressiveMarkdownPacer::default();
1089
+ pacer.push_delta("Line 1\nLine 2\n| A | B |\n| - | - |\n");
1090
+ assert_eq!(pacer.step().as_deref(), Some("Line 1"));
1091
+ assert_eq!(pacer.step().as_deref(), Some("Line 1\nLine 2"));
1092
+ // Table is still open because no non-table line or finish has arrived
1093
+ assert_eq!(pacer.step(), None);
1094
+ assert!(!pacer.is_done());
1095
+
1096
+ pacer.finish_with_authoritative("Line 1\nLine 2\n| A | B |\n| - | - |\nDone");
1097
+ assert_eq!(
1098
+ pacer.step().as_deref(),
1099
+ Some("Line 1\nLine 2\n| A | B |\n| - | - |")
1100
+ );
1101
+ assert_eq!(
1102
+ pacer.step().as_deref(),
1103
+ Some("Line 1\nLine 2\n| A | B |\n| - | - |\nDone")
1104
+ );
1105
+ assert!(pacer.is_done());
1106
+ }
770
1107
  }
@@ -25,7 +25,7 @@ mod diff_render;
25
25
  mod live_status;
26
26
  mod markdown_render;
27
27
  use approval_prompts::*;
28
- use diff_render::*;
28
+ pub(crate) use diff_render::*;
29
29
  pub(crate) use live_status::*;
30
30
  pub(crate) use markdown_render::*;
31
31
 
@@ -483,7 +483,11 @@ fn tui_approval(
483
483
  AgentApproval::UserApproval { title, prompt } => yes_no(title, prompt.to_owned()),
484
484
  AgentApproval::EnterPlanMode { reason } => yes_no("Enter plan mode?", reason.to_owned()),
485
485
  AgentApproval::ExitPlanMode { plan } => {
486
- let outcome = yes_no("Review plan", plan.to_owned());
486
+ tui.push_plan(plan);
487
+ let outcome = yes_no(
488
+ "Review plan",
489
+ "Do you want to proceed with this plan?".to_string(),
490
+ );
487
491
  if outcome == ApprovalOutcome::Approved {
488
492
  match save_plan_file(root, plan) {
489
493
  Ok(path) => tui.push_notice(format!("Plan saved to {}", path.display())),
@@ -1339,7 +1343,7 @@ pub async fn run_code_agent_with_options(
1339
1343
  ..
1340
1344
  } => {
1341
1345
  let remaining = progress_tool_running
1342
- .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |running| {
1346
+ .try_update(Ordering::Relaxed, Ordering::Relaxed, |running| {
1343
1347
  Some(running.saturating_sub(1))
1344
1348
  })
1345
1349
  .unwrap_or(0)
@@ -1509,9 +1513,12 @@ pub async fn run_code_agent_with_options(
1509
1513
  }
1510
1514
  };
1511
1515
 
1512
- // Providers may still stream internally, but the CLI deliberately waits
1513
- // for AgentResult.summary so it can render one complete Markdown document.
1514
- let on_chunk = |_summary: String| {};
1516
+ let chunk_tui = options.tui.clone();
1517
+ let on_chunk = move |delta: String| {
1518
+ if let Some(tui) = &chunk_tui {
1519
+ tui.append_assistant_delta(&delta);
1520
+ }
1521
+ };
1515
1522
 
1516
1523
  let user_name = MemoryStore::open_default()
1517
1524
  .ok()
@@ -1628,17 +1635,18 @@ pub async fn run_code_agent_with_options(
1628
1635
  let worked_line = format!("{DIM}{label} {}{RESET}", "─".repeat(fill_len));
1629
1636
 
1630
1637
  if let Some(tui) = &options.tui {
1631
- tui.push_assistant(res.summary.clone());
1638
+ let mut post_notices = Vec::new();
1632
1639
  if let Ok(mut status) = live_status.lock()
1633
1640
  && !status.web_sources.is_empty()
1634
1641
  {
1635
1642
  let sources = status.web_sources.drain(..).collect::<Vec<_>>();
1636
1643
  let text = format_tui_web_sources(&sources, MAX_TUI_WEB_SOURCES);
1637
1644
  if !text.is_empty() {
1638
- tui.push_notice(text);
1645
+ post_notices.push(text);
1639
1646
  }
1640
1647
  }
1641
- tui.push_notice(worked_line);
1648
+ post_notices.push(worked_line);
1649
+ tui.begin_assistant_reveal(res.summary.clone(), post_notices);
1642
1650
  avatar_bridge.on_talking(false);
1643
1651
  return Ok(res);
1644
1652
  }
@@ -1783,7 +1791,14 @@ mod format_markdown_bold_tests {
1783
1791
  strip_ansi(&out).contains("print(1)"),
1784
1792
  "code content dropped: {out}"
1785
1793
  );
1786
- assert!(out.contains('│'), "missing gutter bar: {out}");
1794
+ assert!(
1795
+ !out.contains('│'),
1796
+ "code block should not have vertical pipe gutter: {out}"
1797
+ );
1798
+ assert!(
1799
+ strip_ansi(&out).contains(" print(1)"),
1800
+ "code block should be indented with 2 spaces: {out}"
1801
+ );
1787
1802
  }
1788
1803
 
1789
1804
  #[test]