@pheem49/mint 1.14.0 → 1.16.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 (925) hide show
  1. package/Cargo.lock +1258 -35
  2. package/Cargo.toml +5 -2
  3. package/LICENSE +21 -654
  4. package/README.md +93 -756
  5. package/Release_Note.md +53 -446
  6. package/apps/mint-companion/src-tauri/Cargo.toml +20 -0
  7. package/apps/mint-companion/src-tauri/build.rs +3 -0
  8. package/apps/mint-companion/src-tauri/src/main.rs +468 -0
  9. package/apps/mint-companion/tsconfig.json +20 -0
  10. package/apps/mint-companion/vite.config.ts +3 -0
  11. package/components.json +19 -0
  12. package/crates/mint-cli/Cargo.toml +9 -1
  13. package/crates/mint-cli/src/agent/approval_prompts.rs +16 -3
  14. package/crates/mint-cli/src/agent/live_status.rs +545 -148
  15. package/crates/mint-cli/src/agent/markdown_render.rs +601 -61
  16. package/crates/mint-cli/src/agent.rs +723 -258
  17. package/crates/mint-cli/src/commands/agent.rs +83 -74
  18. package/crates/mint-cli/src/commands/eval.rs +9 -5
  19. package/crates/mint-cli/src/commands/git.rs +204 -0
  20. package/crates/mint-cli/src/commands/integrations.rs +35 -0
  21. package/crates/mint-cli/src/commands/knowledge.rs +13 -32
  22. package/crates/mint-cli/src/commands/mod.rs +81 -2
  23. package/crates/mint-cli/src/commands/tailnet_web.rs +235 -0
  24. package/crates/mint-cli/src/image.rs +32 -11
  25. package/crates/mint-cli/src/interactive/chat_tui.rs +7853 -0
  26. package/crates/mint-cli/src/interactive/command_ui.rs +477 -0
  27. package/crates/mint-cli/src/interactive/format.rs +8 -0
  28. package/crates/mint-cli/src/interactive/input_box.rs +94 -8
  29. package/crates/mint-cli/src/interactive/picker.rs +180 -37
  30. package/crates/mint-cli/src/interactive/resume_picker.rs +686 -0
  31. package/crates/mint-cli/src/interactive/slash_commands.rs +1071 -501
  32. package/crates/mint-cli/src/interactive/thought_viewer.rs +286 -0
  33. package/crates/mint-cli/src/interactive/tool_activity.rs +328 -0
  34. package/crates/mint-cli/src/interactive.rs +454 -166
  35. package/crates/mint-cli/src/main.rs +93 -26
  36. package/crates/mint-cli/src/markdown.rs +12 -5
  37. package/crates/mint-cli/src/mcp.rs +5 -0
  38. package/crates/mint-cli/src/mod_host.rs +6 -0
  39. package/crates/mint-cli/src/mods_cli.rs +62 -0
  40. package/crates/mint-cli/src/mods_ui.rs +187 -0
  41. package/crates/mint-cli/src/onboard.rs +37 -61
  42. package/crates/mint-cli/src/plugins_cli.rs +8 -5
  43. package/crates/mint-cli/src/setup.rs +10 -8
  44. package/crates/mint-cli/src/terminal_theme.rs +189 -0
  45. package/crates/mint-cli/src/updater.rs +112 -23
  46. package/crates/mint-cli/tests/library_intake.rs +77 -0
  47. package/crates/mint-cli/tests/mods_testing.rs +133 -0
  48. package/crates/mint-companion-protocol/Cargo.toml +10 -0
  49. package/crates/mint-companion-protocol/src/lib.rs +163 -0
  50. package/crates/mint-core/Cargo.toml +6 -3
  51. package/crates/mint-core/build.rs +26 -0
  52. package/crates/mint-core/src/agent/agent_loop.rs +1 -0
  53. package/crates/mint-core/src/agent/chat.rs +1003 -209
  54. package/crates/mint-core/src/agent/code_tools.rs +98 -2
  55. package/crates/mint-core/src/agent/memory/sources.rs +240 -0
  56. package/crates/mint-core/src/agent/memory.rs +805 -33
  57. package/crates/mint-core/src/agent/skill_inventory.rs +325 -0
  58. package/crates/mint-core/src/agent/skills.rs +437 -90
  59. package/crates/mint-core/src/agent.rs +1 -0
  60. package/crates/mint-core/src/api_server/routes/background_jobs.rs +310 -0
  61. package/crates/mint-core/src/api_server/routes/chat.rs +233 -190
  62. package/crates/mint-core/src/api_server/routes/html_preview.rs +77 -0
  63. package/crates/mint-core/src/api_server/routes/library.rs +97 -0
  64. package/crates/mint-core/src/api_server/routes/linked_folders.rs +118 -3
  65. package/crates/mint-core/src/api_server/routes/media_gen.rs +10 -57
  66. package/crates/mint-core/src/api_server/routes/misc.rs +114 -121
  67. package/crates/mint-core/src/api_server/routes/mod.rs +6 -0
  68. package/crates/mint-core/src/api_server/routes/mods.rs +31 -0
  69. package/crates/mint-core/src/api_server/routes/sessions.rs +85 -0
  70. package/crates/mint-core/src/api_server/routes/skills_subagents.rs +12 -42
  71. package/crates/mint-core/src/api_server/routes/slash.rs +50 -1
  72. package/crates/mint-core/src/api_server/routes/sources.rs +101 -0
  73. package/crates/mint-core/src/api_server.rs +140 -12
  74. package/crates/mint-core/src/avatar_bridge.rs +13 -2
  75. package/crates/mint-core/src/browser/cdp.rs +74 -83
  76. package/crates/mint-core/src/browser/evidence.rs +260 -0
  77. package/crates/mint-core/src/browser/input.rs +1 -1
  78. package/crates/mint-core/src/browser/interact.rs +98 -124
  79. package/crates/mint-core/src/browser/lifecycle.rs +75 -4
  80. package/crates/mint-core/src/browser/mod.rs +16 -2
  81. package/crates/mint-core/src/browser/navigate.rs +27 -19
  82. package/crates/mint-core/src/browser/observe.js +40 -0
  83. package/crates/mint-core/src/browser/session.rs +1610 -0
  84. package/crates/mint-core/src/companion.rs +716 -0
  85. package/crates/mint-core/src/cron/scheduler.rs +34 -2
  86. package/crates/mint-core/src/git/branches.rs +348 -0
  87. package/crates/mint-core/src/git/checkpoint.rs +40 -0
  88. package/crates/mint-core/src/git/diff.rs +206 -0
  89. package/crates/mint-core/src/git/mod.rs +10 -2
  90. package/crates/mint-core/src/integrations/channels.rs +88 -345
  91. package/crates/mint-core/src/integrations/mcp.rs +397 -50
  92. package/crates/mint-core/src/integrations/mcp_calls.rs +823 -0
  93. package/crates/mint-core/src/integrations/mcp_catalog.rs +267 -0
  94. package/crates/mint-core/src/integrations/mcp_device.rs +319 -0
  95. package/crates/mint-core/src/integrations/mcp_result.rs +356 -0
  96. package/crates/mint-core/src/integrations/webhooks.rs +539 -0
  97. package/crates/mint-core/src/integrations.rs +2 -0
  98. package/crates/mint-core/src/lib.rs +107 -40
  99. package/crates/mint-core/src/live_sync.rs +28 -9
  100. package/crates/mint-core/src/media/auto_shorts.rs +1 -0
  101. package/crates/mint-core/src/media/gemini_live.rs +145 -14
  102. package/crates/mint-core/src/media/image_models.rs +3 -27
  103. package/crates/mint-core/src/media/image_workflow.rs +216 -0
  104. package/crates/mint-core/src/media/library/intake.rs +144 -0
  105. package/crates/mint-core/src/media/library/migration.rs +56 -0
  106. package/crates/mint-core/src/media/library/removal.rs +55 -0
  107. package/crates/mint-core/src/media/library/tests.rs +730 -0
  108. package/crates/mint-core/src/media/library/thumbnails.rs +72 -0
  109. package/crates/mint-core/src/media/library.rs +545 -0
  110. package/crates/mint-core/src/media/mic_transcribe.rs +1 -0
  111. package/crates/mint-core/src/media/model_catalog.rs +375 -0
  112. package/crates/mint-core/src/media/pictures.rs +79 -222
  113. package/crates/mint-core/src/media/subtitle.rs +1 -0
  114. package/crates/mint-core/src/media/video_models.rs +3 -22
  115. package/crates/mint-core/src/media.rs +3 -0
  116. package/crates/mint-core/src/mods/manager.rs +1124 -0
  117. package/crates/mint-core/src/mods/mod.rs +569 -0
  118. package/crates/mint-core/src/mods/operations.rs +204 -0
  119. package/crates/mint-core/src/mods/session.rs +146 -0
  120. package/crates/mint-core/src/mods/slash.rs +59 -0
  121. package/crates/mint-core/src/mods/telemetry.rs +306 -0
  122. package/crates/mint-core/src/orchestration/approvals.rs +344 -0
  123. package/crates/mint-core/src/orchestration/context_render.rs +105 -0
  124. package/crates/mint-core/src/orchestration/mcp_review.rs +153 -0
  125. package/crates/mint-core/src/orchestration/memory_skill.rs +110 -33
  126. package/crates/mint-core/src/orchestration/mod.rs +3129 -481
  127. package/crates/mint-core/src/orchestration/mod_authoring_regressions.rs +209 -0
  128. package/crates/mint-core/src/orchestration/progress_clock.rs +80 -0
  129. package/crates/mint-core/src/orchestration/repeated_failures.rs +116 -0
  130. package/crates/mint-core/src/orchestration/review_regressions.rs +449 -0
  131. package/crates/mint-core/src/orchestration/run_control.rs +196 -0
  132. package/crates/mint-core/src/orchestration/tools/browser.rs +16 -139
  133. package/crates/mint-core/src/orchestration/tools/files.rs +161 -14
  134. package/crates/mint-core/src/orchestration/tools/media.rs +37 -41
  135. package/crates/mint-core/src/orchestration/tools/misc.rs +5 -3
  136. package/crates/mint-core/src/orchestration/tools/mod.rs +2 -0
  137. package/crates/mint-core/src/orchestration/tools/mods.rs +161 -0
  138. package/crates/mint-core/src/orchestration/tools/plugins_mcp.rs +303 -71
  139. package/crates/mint-core/src/orchestration/tools/shell.rs +95 -2
  140. package/crates/mint-core/src/orchestration/tools/web.rs +1 -1
  141. package/crates/mint-core/src/orchestration/workspace_helpers.rs +4 -1
  142. package/crates/mint-core/src/prompts/agent.rs +75 -31
  143. package/crates/mint-core/src/prompts/chat.rs +1 -1
  144. package/crates/mint-core/src/prompts/persona.rs +1 -1
  145. package/crates/mint-core/src/prompts/tool_catalog.rs +121 -27
  146. package/crates/mint-core/src/search/knowledge.rs +7 -1
  147. package/crates/mint-core/src/search/linked_folders/index.rs +896 -0
  148. package/crates/mint-core/src/search/linked_folders.rs +473 -131
  149. package/crates/mint-core/src/slash/catalog.rs +1 -1
  150. package/crates/mint-core/src/slash/mod.rs +644 -33
  151. package/crates/mint-core/src/slash/model_fetcher.rs +12 -3
  152. package/crates/mint-core/src/slash/models.rs +66 -10
  153. package/crates/mint-core/src/slash/mods.rs +155 -0
  154. package/crates/mint-core/src/system/bg_shell.rs +1129 -95
  155. package/crates/mint-core/src/system/config.rs +235 -10
  156. package/crates/mint-core/src/system/html_preview.rs +398 -0
  157. package/crates/mint-core/src/system/mint_web.rs +164 -0
  158. package/crates/mint-core/src/system/release_notes.rs +18 -0
  159. package/crates/mint-core/src/system/shell.rs +21 -6
  160. package/crates/mint-core/src/system/workspace.rs +292 -0
  161. package/crates/mint-core/src/system/workspace_history.rs +604 -0
  162. package/crates/mint-core/src/system.rs +5 -0
  163. package/crates/mint-core/tests/background_retention.rs +34 -0
  164. package/crates/mint-core/tests/background_shutdown.rs +24 -0
  165. package/crates/mint-core/tests/library_http.rs +209 -0
  166. package/crates/mint-core/tests/mcp_contract.rs +283 -0
  167. package/crates/mint-core/tests/mcp_stdio.rs +11 -10
  168. package/crates/mint-core/tests/media_model_catalog_http.rs +403 -0
  169. package/crates/mint-core/tests/memory_persistence.rs +39 -0
  170. package/crates/mint-core/tests/mods_http.rs +215 -0
  171. package/crates/mint-core/tests/terminal_shutdown.rs +109 -0
  172. package/crates/mint-mod-runtime/Cargo.toml +18 -0
  173. package/crates/mint-mod-runtime/src/lib.rs +507 -0
  174. package/crates/mint-mod-runtime/src/sdk.js +56 -0
  175. package/crates/mint-mod-runtime/src/test_sdk.js +32 -0
  176. package/crates/mint-mod-runtime/src/testing.rs +94 -0
  177. package/crates/mint-mod-runtime/tests/runtime.rs +156 -0
  178. package/docs/mods/AUTHORING.md +61 -0
  179. package/docs/mods/README.md +139 -0
  180. package/docs/mods/TESTING.md +56 -0
  181. package/docs/mods/mint-mods.d.ts +66 -0
  182. package/examples/device-mcp/README.md +22 -0
  183. package/examples/device-mcp/contract.md +49 -0
  184. package/examples/device-mcp/mock-device.mjs +66 -0
  185. package/examples/device-mcp/server.mjs +48 -0
  186. package/examples/device-mcp/server.test.mjs +78 -0
  187. package/examples/mods/task-checklist/README.md +1 -0
  188. package/examples/mods/task-checklist/main.ts +17 -0
  189. package/examples/mods/task-checklist/manifest.json +1 -0
  190. package/examples/mods/task-checklist/tests/checklist.test.ts +15 -0
  191. package/examples/mods/token-meter/README.md +1 -0
  192. package/examples/mods/token-meter/main.ts +8 -0
  193. package/examples/mods/token-meter/manifest.json +1 -0
  194. package/examples/mods/tool-guard/README.md +1 -0
  195. package/examples/mods/tool-guard/main.ts +9 -0
  196. package/examples/mods/tool-guard/manifest.json +1 -0
  197. package/out/web/assets/CliSessionsView-CtzUJpK_.css +1 -0
  198. package/out/web/assets/CliSessionsView-D7hw0Mgj.js +1 -0
  199. package/out/web/assets/CodeReviewPage-C7bEOJfd.js +4 -0
  200. package/out/web/assets/CodeReviewPage-Dte9uLiL.css +1 -0
  201. package/out/web/assets/CommandPalette-0RrF-AOT.js +1 -0
  202. package/out/web/assets/ImageStudioPanel-CdxkAf_r.js +1 -0
  203. package/out/web/assets/ImageStudioPanel-D0lnhQV5.css +1 -0
  204. package/out/web/assets/LibraryPage--Qfisp6D.js +2 -0
  205. package/out/web/assets/LibraryPage-DAxUTAg7.css +1 -0
  206. package/out/web/assets/LinkedFoldersView-2nfZbDgN.js +1 -0
  207. package/out/web/assets/McpRegistryPicker-h9uS8_wT.js +1 -0
  208. package/out/web/assets/McpServersView-slfQJzNB.js +1 -0
  209. package/out/web/assets/MintDashboard-BSR5XpXh.js +25 -0
  210. package/out/web/assets/MintDashboard-CWUcCGhd.css +1 -0
  211. package/out/web/assets/PluginsView-l7LSm503.js +1 -0
  212. package/out/web/assets/ScheduledTasksView-CttmLbq1.js +1 -0
  213. package/out/web/assets/SettingsWindow-BCMLYvEV.js +4 -0
  214. package/out/web/assets/SettingsWindow-DcMVmU9u.css +1 -0
  215. package/out/web/assets/SkillsView-rNs1qr_U.js +2 -0
  216. package/out/web/assets/ToolSurfacePage-B9DzEPWN.js +37 -0
  217. package/out/web/assets/ToolSurfacePage-CTfgggF2.css +1 -0
  218. package/out/web/assets/VeoStudioPanel-DOj7_WR3.css +1 -0
  219. package/out/web/assets/VeoStudioPanel-DiJs_Cg2.js +4 -0
  220. package/out/web/assets/WorkspacePanel-CbJ1PFoZ.js +1 -0
  221. package/out/web/assets/WorkspaceSelector-B2xIp04D.js +1 -0
  222. package/out/web/assets/WorkspaceSelector-Ca8uJOsS.css +1 -0
  223. package/out/web/assets/{abnfDiagram-VCTEODGH-DAOBA7ZG.js → abnfDiagram-VCTEODGH-CD0CjoGl.js} +1 -1
  224. package/out/web/assets/{architecture-7GRP2DOG-CgSwQOPQ.js → architecture-7GRP2DOG-RKV83OeZ.js} +1 -1
  225. package/out/web/assets/{architectureDiagram-5GKGNRK7-CYp3yupP.js → architectureDiagram-5GKGNRK7-nN_HAFct.js} +1 -1
  226. package/out/web/assets/{blockDiagram-NRAW4CY4-CyWybDWr.js → blockDiagram-NRAW4CY4-BrrqzHW5.js} +1 -1
  227. package/out/web/assets/{c4Diagram-UCG6FXSJ-BvY3U709.js → c4Diagram-UCG6FXSJ-DTdV8_zE.js} +1 -1
  228. package/out/web/assets/{chunk-75Z2AOVW-TLXCoZYv.js → chunk-75Z2AOVW-_M0z5olV.js} +1 -1
  229. package/out/web/assets/chunk-CKBBP62Z-BpQEgeHF.js +2 -0
  230. package/out/web/assets/{chunk-CLGD4ZFX-xjsKtNS7.js → chunk-CLGD4ZFX-D54yGvGS.js} +1 -1
  231. package/out/web/assets/chunk-DSNCTWBM-C-gvT2ru.js +2 -0
  232. package/out/web/assets/chunk-DU6HZSFF-CnFngIhW.js +128 -0
  233. package/out/web/assets/{chunk-F27PBJKO-BFpgF55A.js → chunk-F27PBJKO-BqtWrnEO.js} +1 -1
  234. package/out/web/assets/{chunk-G27WJ6UU-DfF7qW-C.js → chunk-G27WJ6UU-Ckoq5mi2.js} +1 -1
  235. package/out/web/assets/{chunk-GMAD6QVW-C-558kh_.js → chunk-GMAD6QVW-BuhEcgHq.js} +1 -1
  236. package/out/web/assets/{chunk-JQ64N6SF-wJmeGKAw.js → chunk-JQ64N6SF-CF8T7GiZ.js} +1 -1
  237. package/out/web/assets/{chunk-LCL6LL3I-C1wHD8Ie.js → chunk-LCL6LL3I-BhXV9zyH.js} +1 -1
  238. package/out/web/assets/{chunk-OBVCFTLP-BsRTPBqs.js → chunk-OBVCFTLP-CRIkunUB.js} +1 -1
  239. package/out/web/assets/{chunk-P2QGCYS3-HZIiM7Ni.js → chunk-P2QGCYS3-UFEU6BxZ.js} +1 -1
  240. package/out/web/assets/{chunk-POPQ4Y6H-Dk9wvVb0.js → chunk-POPQ4Y6H-CQcrNbjr.js} +1 -1
  241. package/out/web/assets/{chunk-PWAF6VOD-BmjYLNbq.js → chunk-PWAF6VOD-Cc2uVDsG.js} +1 -1
  242. package/out/web/assets/{chunk-R7TYR2AO-CaHIf-lK.js → chunk-R7TYR2AO-DiCxzdAY.js} +1 -1
  243. package/out/web/assets/{chunk-RHFEMEQ7-_T561hOU.js → chunk-RHFEMEQ7-b7hnPmrR.js} +1 -1
  244. package/out/web/assets/{chunk-SVP7TREG-CM-GGYoY.js → chunk-SVP7TREG-Bu_yYb-V.js} +1 -1
  245. package/out/web/assets/{chunk-TEH6E4GO-K2A8Vg2C.js → chunk-TEH6E4GO-G4Cdlyrt.js} +1 -1
  246. package/out/web/assets/classDiagram-DTDB5LWJ-RnC2mYO4.js +1 -0
  247. package/out/web/assets/classDiagram-v2-JRS7N3AN-RnC2mYO4.js +1 -0
  248. package/out/web/assets/config-CnvCChUM.js +2 -0
  249. package/out/web/assets/config-DM6oIVO3.css +1 -0
  250. package/out/web/assets/core-DsKkQ7ig.js +1 -0
  251. package/out/web/assets/{cynefin-OW5HDTMX-ClzaB6cc.js → cynefin-OW5HDTMX-CnzQfOGp.js} +1 -1
  252. package/out/web/assets/{cynefinDiagram-5FMLGOSQ-D_JzZBzf.js → cynefinDiagram-5FMLGOSQ-DVtQnfpm.js} +1 -1
  253. package/out/web/assets/{dagre-3AP2YEHR-BDuUeMHj.js → dagre-3AP2YEHR-DG76_k5_.js} +1 -1
  254. package/out/web/assets/{diagram-S7CK7UJ4-DgtcfE8P.js → diagram-S7CK7UJ4-CaouUoze.js} +1 -1
  255. package/out/web/assets/{diagram-UQ7AKVKN-DTLoEYti.js → diagram-UQ7AKVKN-B4bEc26B.js} +1 -1
  256. package/out/web/assets/{diagram-VSXAHHWV-ChGl9pXz.js → diagram-VSXAHHWV-BJ86Ov2Y.js} +1 -1
  257. package/out/web/assets/{diagram-VX7I27RA-Be8DWz0S.js → diagram-VX7I27RA-7hH5IPYf.js} +1 -1
  258. package/out/web/assets/{diagram-Z3DM3KII-BMXjRTOF.js → diagram-Z3DM3KII-CCM4clo8.js} +1 -1
  259. package/out/web/assets/dist-js-BkYNznHX.js +1 -0
  260. package/out/web/assets/{ebnfDiagram-PWID7BFC-8Q9pl1CJ.js → ebnfDiagram-PWID7BFC-Da7WAM3k.js} +1 -1
  261. package/out/web/assets/{erDiagram-SSCWMZ5O-DjH8Lnkn.js → erDiagram-SSCWMZ5O-CzzmnZWT.js} +1 -1
  262. package/out/web/assets/event-Cdt7Byyj.js +1 -0
  263. package/out/web/assets/{eventmodeling-NTZA5JFV-BKUH_DOp.js → eventmodeling-NTZA5JFV-xcfsA0XD.js} +1 -1
  264. package/out/web/assets/fira-code-latin-600-normal-BmSSHshu.woff +0 -0
  265. package/out/web/assets/fira-code-latin-600-normal-CvYZpPHg.woff2 +0 -0
  266. package/out/web/assets/fira-code-symbols2-600-normal-Dvq6IQE5.woff +0 -0
  267. package/out/web/assets/flowDiagram-A5DVABFB-JdtH1673.js +1 -0
  268. package/out/web/assets/{ganttDiagram-EL5Y4UJY-D7fZKJNM.js → ganttDiagram-EL5Y4UJY-B3ClFGmL.js} +1 -1
  269. package/out/web/assets/{gitGraph-4MIJSDKK-BypWaaBU.js → gitGraph-4MIJSDKK-BZ_y_cDU.js} +1 -1
  270. package/out/web/assets/{gitGraphDiagram-WWUBYQGX-DWiw_3bq.js → gitGraphDiagram-WWUBYQGX-drhRSYm3.js} +1 -1
  271. package/out/web/assets/index-BZ9xgmMP.css +1 -0
  272. package/out/web/assets/index-Ca8cCS4h.js +168 -0
  273. package/out/web/assets/{info-A6RAGUB7-KkAUxT4j.js → info-A6RAGUB7-B7I3K0F1.js} +1 -1
  274. package/out/web/assets/{infoDiagram-RXCK75RN-BNb6E48y.js → infoDiagram-RXCK75RN-deX_1MPh.js} +1 -1
  275. package/out/web/assets/{ishikawaDiagram-5VMMS53U-Caa-JoAK.js → ishikawaDiagram-5VMMS53U-s38yo28y.js} +1 -1
  276. package/out/web/assets/{journeyDiagram-EYS64GPL-DOrmFGz6.js → journeyDiagram-EYS64GPL-CZn2kB8b.js} +1 -1
  277. package/out/web/assets/jsx-runtime-DN4NhQUK.js +1 -0
  278. package/out/web/assets/{kanban-definition-3QL26DDD-BOWCYcK0.js → kanban-definition-3QL26DDD-DodjgySL.js} +1 -1
  279. package/out/web/assets/markdown-CMhYIcWB.js +39 -0
  280. package/out/web/assets/{mermaid-parser.core-BQqjCkS_.js → mermaid-parser.core-Whu2rlJC.js} +3 -3
  281. package/out/web/assets/mermaid.core-CKyPo8VH.js +11 -0
  282. package/out/web/assets/{mindmap-definition-FBJOCRG2-jTceWpr0.js → mindmap-definition-FBJOCRG2-260WBDxZ.js} +1 -1
  283. package/out/web/assets/modelManager-CUaGWMWu.js +2 -0
  284. package/out/web/assets/oauthManager-DlNhXJr6.js +1 -0
  285. package/out/web/assets/{packet-AYTQ26CC-CJ5RXgol.js → packet-AYTQ26CC-CEeIRKRG.js} +1 -1
  286. package/out/web/assets/{pegDiagram-XKGWAZYB-y8wMtd0y.js → pegDiagram-XKGWAZYB-BYadI-KO.js} +1 -1
  287. package/out/web/assets/{pie-WAS4IAKB-72fTBbl4.js → pie-WAS4IAKB-D8uf4dh5.js} +1 -1
  288. package/out/web/assets/{pieDiagram-E7YTZNPT-e0O02qWq.js → pieDiagram-E7YTZNPT-CjSDy4hO.js} +1 -1
  289. package/out/web/assets/{quadrantDiagram-AXDQQJYC-C9phYLom.js → quadrantDiagram-AXDQQJYC-B8D3gU5d.js} +1 -1
  290. package/out/web/assets/{radar-RG4KPBEZ-BAU2hzUT.js → radar-RG4KPBEZ-DZJxnokY.js} +1 -1
  291. package/out/web/assets/{railroad-74A4TZTK-B0bCAVHC.js → railroad-74A4TZTK-DBysUiUi.js} +1 -1
  292. package/out/web/assets/{railroad-abnf-HS5TGJTU-WhxO7ndE.js → railroad-abnf-HS5TGJTU-CHWxoJYg.js} +1 -1
  293. package/out/web/assets/{railroad-ebnf-LZEXJU2U-BkA0m7-F.js → railroad-ebnf-LZEXJU2U-DDv7X6bY.js} +1 -1
  294. package/out/web/assets/{railroad-peg-WCYAUIDC-BsVpHIaj.js → railroad-peg-WCYAUIDC-DpfcYYNJ.js} +1 -1
  295. package/out/web/assets/{railroadDiagram-O6MQD6OU-CyOLN65Q.js → railroadDiagram-O6MQD6OU-CYtyv0t3.js} +1 -1
  296. package/out/web/assets/{requirementDiagram-EFPCY7ZU-DQXJ7Dud.js → requirementDiagram-EFPCY7ZU-D2X-Ewik.js} +1 -1
  297. package/out/web/assets/{sankeyDiagram-P5KCCOFB-B2XLyIIc.js → sankeyDiagram-P5KCCOFB-Bn2x6C4Y.js} +1 -1
  298. package/out/web/assets/{sequenceDiagram-WJ2MYXX4-DGQY4wwm.js → sequenceDiagram-WJ2MYXX4-BvFLKtPd.js} +1 -1
  299. package/out/web/assets/{stateDiagram-HBIQ2CUA-DHZpDwNy.js → stateDiagram-HBIQ2CUA-Bx0GWeaY.js} +1 -1
  300. package/out/web/assets/stateDiagram-v2-4QOOHH4V--hol1yg_.js +1 -0
  301. package/out/web/assets/studio-B1mk40wD.js +1 -0
  302. package/out/web/assets/{swimlanes-XN3QIQJK-Bn_jcZz-.js → swimlanes-XN3QIQJK-CCma0T7L.js} +1 -1
  303. package/out/web/assets/swimlanesDiagram-VK2B7HYN-BkohoVsU.js +8 -0
  304. package/out/web/assets/{timeline-definition-24CTP7MA-CdTERuJZ.js → timeline-definition-24CTP7MA-iCRhhJ7C.js} +1 -1
  305. package/out/web/assets/{treeView-Q6P3EWNA-TKBKQG6R.js → treeView-Q6P3EWNA-DZ-y08qp.js} +1 -1
  306. package/out/web/assets/{treemap-WGGIJYW6-BP7gQoiC.js → treemap-WGGIJYW6-De0CxzMx.js} +1 -1
  307. package/out/web/assets/{vennDiagram-4TSXK5OY-BHUmxDuK.js → vennDiagram-4TSXK5OY-w_7aZ6xe.js} +1 -1
  308. package/out/web/assets/{wardley-WFR3VGLG-9bct5AAN.js → wardley-WFR3VGLG-DT0y0xJ4.js} +1 -1
  309. package/out/web/assets/{wardleyDiagram-VM6X3IG4-BfJwDDso.js → wardleyDiagram-VM6X3IG4-DAdXELbQ.js} +1 -1
  310. package/out/web/assets/window-BJCVtyET.js +1 -0
  311. package/out/web/assets/{xychartDiagram-S5SC5T6Z-Dqd9Mvpo.js → xychartDiagram-S5SC5T6Z-IZIXf3Vo.js} +1 -1
  312. package/out/web/index-web.html +4 -4
  313. package/out/web/index.html +4 -4
  314. package/package.json +80 -20
  315. package/shared-modules/.github/workflows/ci.yml +120 -0
  316. package/shared-modules/.github/workflows/update.yaml +28 -0
  317. package/slash-commands.json +16 -2
  318. package/src/bin/auto-build.js +66 -0
  319. package/src/bin/desktop.js +113 -0
  320. package/src/bin/desktop.test.js +113 -0
  321. package/src/bin/index.js +18 -25
  322. package/src/bin/index.test.js +89 -0
  323. package/src/bin/measure-effects.mjs +53 -0
  324. package/src/bin/package.test.js +28 -0
  325. package/src/bin/release-notes.test.js +84 -0
  326. package/src/bin/styles.test.js +20 -0
  327. package/src/bin/web.js +5 -0
  328. package/src/bin/web.test.js +39 -0
  329. package/src/renderer/index-web.html +1 -0
  330. package/src/renderer/index.html +1 -2
  331. package/src/renderer/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  332. package/src/renderer/shared/agentProgress.ts +201 -3
  333. package/src/renderer/shared/components/AgentActivityDrawer.tsx +9 -8
  334. package/src/renderer/shared/components/AgentActivityTable.tsx +193 -133
  335. package/src/renderer/shared/components/ApprovalCard.test.tsx +24 -0
  336. package/src/renderer/shared/components/ApprovalCard.tsx +10 -3
  337. package/src/renderer/shared/components/ArtifactPreviewPanel.tsx +91 -40
  338. package/src/renderer/shared/components/AuthGate.tsx +79 -48
  339. package/src/renderer/shared/components/BackgroundTerminals.tsx +181 -0
  340. package/src/renderer/shared/components/CalculationCard.tsx +41 -15
  341. package/src/renderer/shared/components/ChatCodeBlock.tsx +8 -4
  342. package/src/renderer/shared/components/ChatMessageItem.test.tsx +68 -0
  343. package/src/renderer/shared/components/ChatMessageItem.tsx +52 -33
  344. package/src/renderer/shared/components/ChatPanel.test.tsx +21 -0
  345. package/src/renderer/shared/components/ChatPanel.tsx +676 -415
  346. package/src/renderer/shared/components/ChunkErrorBoundary.tsx +115 -64
  347. package/src/renderer/shared/components/CliSessionsView.test.tsx +125 -0
  348. package/src/renderer/shared/components/CliSessionsView.tsx +543 -0
  349. package/src/renderer/shared/components/CodeReviewPage.tsx +668 -0
  350. package/src/renderer/shared/components/CollapsiblePasteInput.tsx +90 -0
  351. package/src/renderer/shared/components/CommandPalette.tsx +8 -8
  352. package/src/renderer/shared/components/ContextCompactionStatus.tsx +17 -0
  353. package/src/renderer/shared/components/DashboardSidebar.tsx +853 -229
  354. package/src/renderer/shared/components/DesktopTitlebar.tsx +371 -0
  355. package/src/renderer/shared/components/DiffReviewModal.tsx +3 -3
  356. package/src/renderer/shared/components/ErrorNotice.tsx +29 -0
  357. package/src/renderer/shared/components/FormattedMessage.tsx +11 -0
  358. package/src/renderer/shared/components/GeminiLiveOverlay.css +86 -0
  359. package/src/renderer/shared/components/GeminiLiveOverlay.test.tsx +24 -0
  360. package/src/renderer/shared/components/GeminiLiveOverlay.tsx +85 -101
  361. package/src/renderer/shared/components/GitBranchSelector.tsx +442 -0
  362. package/src/renderer/shared/components/GlideSelect.css +138 -0
  363. package/src/renderer/shared/components/GlideSelect.d.ts +31 -0
  364. package/src/renderer/shared/components/GlideSelect.jsx +266 -0
  365. package/src/renderer/shared/components/ImageGenCard.tsx +31 -12
  366. package/src/renderer/shared/components/ImageSearchCard.tsx +76 -65
  367. package/src/renderer/shared/components/ImageStudioPanel.tsx +49 -49
  368. package/src/renderer/shared/components/InlineThoughtNote.tsx +27 -0
  369. package/src/renderer/shared/components/LibraryItemCard.tsx +72 -0
  370. package/src/renderer/shared/components/LibraryPage.tsx +165 -0
  371. package/src/renderer/shared/components/LibraryPreview.tsx +59 -0
  372. package/src/renderer/shared/components/LinkedFoldersView.tsx +365 -46
  373. package/src/renderer/shared/components/McpRegistryPicker.tsx +1 -1
  374. package/src/renderer/shared/components/McpServersView.tsx +51 -30
  375. package/src/renderer/shared/components/McpToolAllowlist.tsx +2 -4
  376. package/src/renderer/shared/components/McpToolResults.tsx +99 -0
  377. package/src/renderer/shared/components/MermaidCard.tsx +14 -8
  378. package/src/renderer/shared/components/MintDashboard.tsx +1002 -831
  379. package/src/renderer/shared/components/ModSessionControls.tsx +8 -0
  380. package/src/renderer/shared/components/ModUi.test.tsx +65 -0
  381. package/src/renderer/shared/components/ModUi.tsx +26 -0
  382. package/src/renderer/shared/components/ModelSelectorPopover.tsx +547 -219
  383. package/src/renderer/shared/components/ModsPanel.tsx +156 -0
  384. package/src/renderer/shared/components/PlanChecklistWidget.tsx +1 -1
  385. package/src/renderer/shared/components/PluginsView.tsx +1 -1
  386. package/src/renderer/shared/components/ProgressActivity.tsx +17 -0
  387. package/src/renderer/shared/components/ReviewHistory.tsx +42 -0
  388. package/src/renderer/shared/components/RewindModal.tsx +11 -18
  389. package/src/renderer/shared/components/RunSummaryDashboard.tsx +8 -14
  390. package/src/renderer/shared/components/ScheduledTasksView.tsx +11 -10
  391. package/src/renderer/shared/components/ScreenCaptureDialog.css +221 -0
  392. package/src/renderer/shared/components/ScreenCaptureDialog.tsx +199 -0
  393. package/src/renderer/shared/components/SelectField.test.tsx +27 -0
  394. package/src/renderer/shared/components/SelectField.tsx +38 -0
  395. package/src/renderer/shared/components/SessionSources.tsx +48 -0
  396. package/src/renderer/shared/components/SessionSourcesPopover.tsx +61 -0
  397. package/src/renderer/shared/components/Settings/AgentsTab.tsx +41 -45
  398. package/src/renderer/shared/components/Settings/AudioTab.tsx +16 -15
  399. package/src/renderer/shared/components/Settings/AutomationTab.tsx +10 -60
  400. package/src/renderer/shared/components/Settings/AvatarCropModal.tsx +368 -0
  401. package/src/renderer/shared/components/Settings/GeneralTab.tsx +368 -113
  402. package/src/renderer/shared/components/Settings/MemoryTab.test.tsx +37 -0
  403. package/src/renderer/shared/components/Settings/MemoryTab.tsx +71 -31
  404. package/src/renderer/shared/components/Settings/NotificationsTab.test.tsx +15 -0
  405. package/src/renderer/shared/components/Settings/NotificationsTab.tsx +84 -0
  406. package/src/renderer/shared/components/Settings/PluginsTab.tsx +20 -12
  407. package/src/renderer/shared/components/Settings/ProfileTab.tsx +29 -5
  408. package/src/renderer/shared/components/Settings/SearchableModelCombobox.tsx +411 -0
  409. package/src/renderer/shared/components/Settings/ThemeTab.tsx +416 -165
  410. package/src/renderer/shared/components/SkillsView.tsx +41 -16
  411. package/src/renderer/shared/components/SlashChoicePanel.tsx +88 -0
  412. package/src/renderer/shared/components/SlashSuggestions.test.tsx +33 -0
  413. package/src/renderer/shared/components/SlashSuggestions.tsx +41 -12
  414. package/src/renderer/shared/components/SourcesBlock.tsx +55 -51
  415. package/src/renderer/shared/components/StockCard.tsx +58 -24
  416. package/src/renderer/shared/components/SubagentDagView.tsx +4 -4
  417. package/src/renderer/shared/components/TerminalDock.tsx +241 -0
  418. package/src/renderer/shared/components/ThinkingBlock.tsx +14 -8
  419. package/src/renderer/shared/components/ToolSurfacePage.test.tsx +32 -0
  420. package/src/renderer/shared/components/ToolSurfacePage.tsx +568 -0
  421. package/src/renderer/shared/components/ToolsPanelToggle.tsx +13 -0
  422. package/src/renderer/shared/components/UiFeatureCard.tsx +267 -0
  423. package/src/renderer/shared/components/UiGridCard.tsx +188 -0
  424. package/src/renderer/shared/components/UiMockupWidget.tsx +149 -0
  425. package/src/renderer/shared/components/VeoStudioPanel.tsx +22 -37
  426. package/src/renderer/shared/components/ViewedImages.tsx +36 -0
  427. package/src/renderer/shared/components/VirtualChatHistory.tsx +82 -0
  428. package/src/renderer/shared/components/VoicePill.css +85 -0
  429. package/src/renderer/shared/components/VoicePill.d.ts +12 -0
  430. package/src/renderer/shared/components/VoicePill.jsx +190 -0
  431. package/src/renderer/shared/components/VoicePill.test.tsx +22 -0
  432. package/src/renderer/shared/components/WeatherCard.tsx +3 -3
  433. package/src/renderer/shared/components/WorkspaceContextPopover.tsx +138 -0
  434. package/src/renderer/shared/components/WorkspacePaneResizer.tsx +92 -0
  435. package/src/renderer/shared/components/WorkspacePreviewActions.test.tsx +44 -0
  436. package/src/renderer/shared/components/WorkspacePreviewActions.tsx +19 -0
  437. package/src/renderer/shared/components/WorkspaceSelector.tsx +154 -0
  438. package/src/renderer/shared/components/background-terminals.css +292 -0
  439. package/src/renderer/shared/components/ui/README.md +52 -0
  440. package/src/renderer/shared/components/ui/button.tsx +37 -0
  441. package/src/renderer/shared/components/ui/demo.tsx +26 -0
  442. package/src/renderer/shared/components/ui/error-recovery.css +35 -0
  443. package/src/renderer/shared/components/ui/error-recovery.tsx +35 -0
  444. package/src/renderer/shared/components/ui/voice-powered-orb.css +6 -0
  445. package/src/renderer/shared/components/ui/voice-powered-orb.tsx +374 -0
  446. package/src/renderer/shared/components/workspace-selector.css +248 -0
  447. package/src/renderer/shared/constants/config.ts +57 -3
  448. package/src/renderer/shared/constants/models.ts +189 -2
  449. package/src/renderer/shared/constants/slashCommands.ts +1 -1
  450. package/src/renderer/shared/conversation/coordinator.test.ts +27 -0
  451. package/src/renderer/shared/conversation/coordinator.ts +82 -0
  452. package/src/renderer/shared/conversation/lifecycle.test.ts +362 -0
  453. package/src/renderer/shared/conversation/lifecycle.ts +418 -0
  454. package/src/renderer/shared/conversation/projectNavigation.test.ts +105 -0
  455. package/src/renderer/shared/conversation/projectNavigation.ts +39 -0
  456. package/src/renderer/shared/conversation/sourcesTransport.ts +27 -0
  457. package/src/renderer/shared/conversation/syncView.test.ts +13 -0
  458. package/src/renderer/shared/conversation/syncView.ts +6 -0
  459. package/src/renderer/shared/conversation/transport.test.ts +163 -0
  460. package/src/renderer/shared/conversation/transport.ts +159 -0
  461. package/src/renderer/shared/conversation/useConversationCoordinator.ts +64 -0
  462. package/src/renderer/shared/css/agent-observability.css +46 -15
  463. package/src/renderer/shared/css/auth-gate.css +114 -15
  464. package/src/renderer/shared/css/chat-ui-cards.css +495 -0
  465. package/src/renderer/shared/css/code-review.css +856 -0
  466. package/src/renderer/shared/css/code-sessions.css +479 -0
  467. package/src/renderer/shared/css/collapsed-paste.css +35 -0
  468. package/src/renderer/shared/css/effects.css +15 -0
  469. package/src/renderer/shared/css/error-notice.css +60 -0
  470. package/src/renderer/shared/css/file-changes.css +132 -0
  471. package/src/renderer/shared/css/image-studio.css +16 -16
  472. package/src/renderer/shared/css/library.css +476 -0
  473. package/src/renderer/shared/css/management-views.css +415 -27
  474. package/src/renderer/shared/css/mcp-results.css +11 -0
  475. package/src/renderer/shared/css/model-selector.css +543 -85
  476. package/src/renderer/shared/css/mods.css +77 -0
  477. package/src/renderer/shared/css/notification-notice.css +28 -0
  478. package/src/renderer/shared/css/session-sources-popover.css +9 -0
  479. package/src/renderer/shared/css/session-sources.css +23 -0
  480. package/src/renderer/shared/css/settings/agents.css +1 -1
  481. package/src/renderer/shared/css/settings/automation.css +13 -13
  482. package/src/renderer/shared/css/settings/general.css +526 -5
  483. package/src/renderer/shared/css/settings/memory.css +154 -0
  484. package/src/renderer/shared/css/settings/notifications.css +11 -0
  485. package/src/renderer/shared/css/settings/plugins.css +5 -5
  486. package/src/renderer/shared/css/settings/profile.css +331 -1
  487. package/src/renderer/shared/css/settings/theme.css +656 -3
  488. package/src/renderer/shared/css/slash-choice.css +95 -0
  489. package/src/renderer/shared/css/slash-suggestions.css +68 -0
  490. package/src/renderer/shared/css/sources-block.css +91 -0
  491. package/src/renderer/shared/css/thinking-status.css +46 -0
  492. package/src/renderer/shared/css/tools-panel.css +7 -0
  493. package/src/renderer/shared/css/ui-tailwind.css +19 -0
  494. package/src/renderer/shared/css/veo-studio.css +16 -15
  495. package/src/renderer/shared/css/viewed-images.css +15 -0
  496. package/src/renderer/shared/fonts.css +46 -0
  497. package/src/renderer/shared/fonts.ts +10 -37
  498. package/src/renderer/shared/lib/utils.ts +6 -0
  499. package/src/renderer/shared/library/controller.test.ts +61 -0
  500. package/src/renderer/shared/library/controller.ts +57 -0
  501. package/src/renderer/shared/library/transport.test.ts +26 -0
  502. package/src/renderer/shared/library/transport.ts +60 -0
  503. package/src/renderer/shared/library/useLibraryFile.ts +38 -0
  504. package/src/renderer/shared/mods.ts +58 -0
  505. package/src/renderer/shared/platform.test.ts +90 -0
  506. package/src/renderer/shared/platform.ts +219 -7
  507. package/src/renderer/shared/theme/themeManager.test.ts +43 -0
  508. package/src/renderer/shared/theme/themeManager.ts +157 -0
  509. package/src/renderer/shared/theme/themes.ts +116 -0
  510. package/src/renderer/shared/theme/tokens.css +318 -0
  511. package/src/renderer/shared/types.ts +137 -2
  512. package/src/renderer/shared/utils/agentActivity.test.ts +75 -0
  513. package/src/renderer/shared/utils/agentActivity.ts +168 -8
  514. package/src/renderer/shared/utils/approval.tsx +7 -0
  515. package/src/renderer/shared/utils/artifactBrowser.test.ts +80 -0
  516. package/src/renderer/shared/utils/artifactBrowser.ts +41 -0
  517. package/src/renderer/shared/utils/cardJson.test.ts +35 -0
  518. package/src/renderer/shared/utils/cardJson.ts +30 -0
  519. package/src/renderer/shared/utils/contextCompaction.test.tsx +48 -0
  520. package/src/renderer/shared/utils/contextCompaction.ts +21 -0
  521. package/src/renderer/shared/utils/effectVisibility.ts +9 -0
  522. package/src/renderer/shared/utils/fileIcons.ts +1 -1
  523. package/src/renderer/shared/utils/markdown.tsx +330 -81
  524. package/src/renderer/shared/utils/markdown.webImages.test.tsx +40 -0
  525. package/src/renderer/shared/utils/mcpResults.test.tsx +91 -0
  526. package/src/renderer/shared/utils/modViewport.ts +7 -0
  527. package/src/renderer/shared/utils/nativeRecordingSession.test.ts +61 -0
  528. package/src/renderer/shared/utils/nativeRecordingSession.ts +46 -0
  529. package/src/renderer/shared/utils/notificationSettings.test.ts +14 -0
  530. package/src/renderer/shared/utils/notificationSettings.ts +23 -0
  531. package/src/renderer/shared/utils/notifications.test.ts +51 -0
  532. package/src/renderer/shared/utils/notifications.ts +54 -0
  533. package/src/renderer/shared/utils/pastedText.test.ts +20 -0
  534. package/src/renderer/shared/utils/pastedText.ts +13 -0
  535. package/src/renderer/shared/utils/progressCache.test.ts +32 -0
  536. package/src/renderer/shared/utils/providers.ts +8 -3
  537. package/src/renderer/shared/utils/screenCapture.ts +43 -0
  538. package/src/renderer/shared/utils/sessionSources.test.ts +31 -0
  539. package/src/renderer/shared/utils/sessionSources.ts +34 -0
  540. package/src/renderer/shared/utils/slashCommandProcessor.ts +5 -2
  541. package/src/renderer/shared/utils/sourceAttachments.ts +28 -0
  542. package/src/renderer/shared/utils/speech.ts +51 -9
  543. package/src/renderer/shared/utils/syntaxHighlight.test.tsx +22 -0
  544. package/src/renderer/shared/utils/syntaxHighlight.tsx +26 -0
  545. package/src/renderer/shared/utils/ui.ts +4 -64
  546. package/src/renderer/shared/utils/useGeminiLiveVoice.ts +49 -5
  547. package/src/renderer/shared/utils/useLatestCallback.ts +9 -0
  548. package/src/renderer/shared/utils/useNativeVoiceInput.ts +80 -72
  549. package/src/renderer/shared/utils/useSessionSources.ts +33 -0
  550. package/src/renderer/shared/utils/viewedImages.ts +26 -0
  551. package/src/renderer/shared/utils/workspacePane.test.ts +33 -0
  552. package/src/renderer/shared/utils/workspacePane.ts +20 -0
  553. package/src/renderer/shared/utils/workspacePreview.test.ts +24 -0
  554. package/src/renderer/shared/utils/workspacePreview.ts +16 -0
  555. package/src/renderer/shared/utils/workspaceSnapshot.test.ts +49 -0
  556. package/src/renderer/shared/utils/workspaceSnapshot.ts +26 -0
  557. package/src/renderer/shared/utils/workspaces.ts +10 -0
  558. package/src/renderer/shared/workspaceUndo.ts +88 -0
  559. package/src/renderer/src/App.tsx +16 -1
  560. package/src/renderer/src/components/BrowserShell.tsx +108 -0
  561. package/src/renderer/src/components/BrowserSuggestionsPopup.tsx +91 -0
  562. package/src/renderer/src/components/LiveTranslateControls.tsx +65 -0
  563. package/src/renderer/src/components/MintDashboard.tsx +4 -2
  564. package/src/renderer/src/components/PicturesLibrary.tsx +2 -287
  565. package/src/renderer/src/components/ScreenPicker.css +227 -0
  566. package/src/renderer/src/components/ScreenPicker.tsx +312 -526
  567. package/src/renderer/src/components/SettingsWindow.tsx +48 -5
  568. package/src/renderer/src/components/SpotlightWindow.tsx +1 -0
  569. package/src/renderer/src/components/WidgetWindow.tsx +27 -3
  570. package/src/renderer/src/components/WorkspacePanel.tsx +269 -61
  571. package/src/renderer/src/components/browser-shell.css +185 -0
  572. package/src/renderer/src/components/browser-suggestions-popup.css +55 -0
  573. package/src/renderer/src/components/liveTranslateFrames.test.ts +44 -0
  574. package/src/renderer/src/components/liveTranslateFrames.ts +45 -0
  575. package/src/renderer/src/components/liveTranslateLanguage.ts +6 -0
  576. package/src/renderer/src/css/app-shell.css +400 -204
  577. package/src/renderer/src/css/chat.css +1666 -594
  578. package/src/renderer/src/css/dashboard.css +11 -0
  579. package/src/renderer/src/css/desktop-tools.css +928 -0
  580. package/src/renderer/src/css/misc.css +5 -60
  581. package/src/renderer/src/css/pictures.css +2 -407
  582. package/src/renderer/src/css/settings/base.css +394 -157
  583. package/src/renderer/src/css/sidebar-workspace.css +1072 -376
  584. package/src/renderer/src/css/spotlight.css +15 -26
  585. package/src/renderer/src/css/tool-surfaces.css +784 -0
  586. package/src/renderer/src/css/widget.css +36 -8
  587. package/src/renderer/src/env.d.ts +4 -9
  588. package/src/renderer/src/hooks/useProviderModels.ts +2 -9
  589. package/src/renderer/src/index.css +53 -46
  590. package/src/renderer/src/main.tsx +12 -0
  591. package/src/renderer/src/proactiveSuggestions.ts +29 -0
  592. package/src/renderer/src/tauri.ts +302 -339
  593. package/src/renderer/src/voiceInput.ts +2 -1
  594. package/src/renderer/src-web/App.tsx +40 -0
  595. package/src/renderer/src-web/components/MintDashboard.tsx +4 -2
  596. package/src/renderer/src-web/components/PicturesLibrary.tsx +2 -284
  597. package/src/renderer/src-web/components/PullToRefresh.tsx +107 -0
  598. package/src/renderer/src-web/components/SettingsWindow.tsx +36 -5
  599. package/src/renderer/src-web/css/app-shell.css +188 -207
  600. package/src/renderer/src-web/css/chat.css +1236 -507
  601. package/src/renderer/src-web/css/dashboard.css +11 -0
  602. package/src/renderer/src-web/css/misc.css +3 -55
  603. package/src/renderer/src-web/css/mobile.css +679 -0
  604. package/src/renderer/src-web/css/pictures.css +2 -415
  605. package/src/renderer/src-web/css/pull-to-refresh.css +54 -0
  606. package/src/renderer/src-web/css/settings/base.css +353 -134
  607. package/src/renderer/src-web/css/settings/mobile.css +320 -0
  608. package/src/renderer/src-web/css/sidebar-workspace.css +546 -187
  609. package/src/renderer/src-web/css/spotlight.css +9 -26
  610. package/src/renderer/src-web/css/widget.css +1 -6
  611. package/src/renderer/src-web/env.d.ts +4 -12
  612. package/src/renderer/src-web/hooks/useProviderModels.ts +2 -9
  613. package/src/renderer/src-web/index.css +53 -96
  614. package/src/renderer/src-web/main.tsx +12 -0
  615. package/src/renderer/src-web/proactiveSuggestions.ts +3 -0
  616. package/src/renderer/src-web/tauri.ts +309 -364
  617. package/src/renderer/tests/backgroundTerminals.browser.html +81 -0
  618. package/src/renderer/tests/chatPanelFixture.ts +23 -0
  619. package/src/renderer/tests/chatPerformance.browser.html +71 -0
  620. package/src/renderer/tests/cliSessions.browser.html +82 -0
  621. package/src/renderer/tests/collapsedPaste.browser.html +70 -0
  622. package/src/renderer/tests/composerMenus.browser.html +116 -0
  623. package/src/renderer/tests/contextCompaction.browser.html +17 -0
  624. package/src/renderer/tests/conversationLifecycle.browser.html +70 -0
  625. package/src/renderer/tests/effectsPerformance.browser.html +13 -0
  626. package/src/renderer/tests/errorRecovery.browser.html +21 -0
  627. package/src/renderer/tests/glideSelect.browser.html +71 -0
  628. package/src/renderer/tests/glideSelect.preview.html +27 -0
  629. package/src/renderer/tests/library.browser.html +1 -0
  630. package/src/renderer/tests/library.browser.tsx +78 -0
  631. package/src/renderer/tests/liveOrb.browser.html +49 -0
  632. package/src/renderer/tests/liveVoice.browser.html +29 -0
  633. package/src/renderer/tests/modInput.browser.html +22 -0
  634. package/src/renderer/tests/sessionSources.browser.html +1 -0
  635. package/src/renderer/tests/sessionSources.browser.tsx +107 -0
  636. package/src/renderer/tests/skills.browser.html +24 -0
  637. package/src/renderer/tests/toolsPanel.browser.html +1 -0
  638. package/src/renderer/tests/toolsPanel.browser.tsx +113 -0
  639. package/src/renderer/tests/viewedImages.browser.html +1 -0
  640. package/src/renderer/tests/viewedImages.browser.tsx +93 -0
  641. package/src/renderer/tests/voicePill.browser.html +80 -0
  642. package/src/renderer/tests/voicePill.preview.html +34 -0
  643. package/src/renderer/tests/webSources.browser.html +1 -0
  644. package/src/renderer/tests/webSources.browser.tsx +98 -0
  645. package/src/renderer/tests/workspaceUndo.browser.html +92 -0
  646. package/src-tauri/Cargo.toml +40 -0
  647. package/src-tauri/build.rs +3 -0
  648. package/src-tauri/src/app/lifecycle.rs +277 -0
  649. package/src-tauri/src/app/mod.rs +4 -0
  650. package/src-tauri/src/app/shortcuts.rs +40 -0
  651. package/src-tauri/src/app/tray.rs +52 -0
  652. package/src-tauri/src/background/events.rs +70 -0
  653. package/src-tauri/src/background/headless.rs +230 -0
  654. package/src-tauri/src/background/mod.rs +4 -0
  655. package/src-tauri/src/background/proactive.rs +262 -0
  656. package/src-tauri/src/commands/approvals.rs +20 -0
  657. package/src-tauri/src/commands/auth.rs +127 -0
  658. package/src-tauri/src/commands/conversation.rs +642 -0
  659. package/src-tauri/src/commands/integrations.rs +157 -0
  660. package/src-tauri/src/commands/media.rs +179 -0
  661. package/src-tauri/src/commands/mod.rs +11 -0
  662. package/src-tauri/src/commands/mods.rs +6 -0
  663. package/src-tauri/src/commands/settings.rs +141 -0
  664. package/src-tauri/src/commands/voice.rs +153 -0
  665. package/src-tauri/src/commands/workspace.rs +501 -0
  666. package/src-tauri/src/lib.rs +10 -0
  667. package/src-tauri/src/main.rs +3 -0
  668. package/src-tauri/src/native/actions.rs +206 -0
  669. package/src-tauri/src/native/browser.rs +252 -0
  670. package/src-tauri/src/native/capture.rs +695 -0
  671. package/src-tauri/src/native/discord_rpc.rs +99 -0
  672. package/src-tauri/src/native/integrations.rs +126 -0
  673. package/src-tauri/src/native/mod.rs +12 -0
  674. package/src-tauri/src/native/notifications.rs +51 -0
  675. package/src-tauri/src/native/plugins.rs +14 -0
  676. package/src-tauri/src/native/system.rs +250 -0
  677. package/src-tauri/src/native/terminal.rs +201 -0
  678. package/src-tauri/src/native/updater.rs +148 -0
  679. package/src-tauri/src/native/windows.rs +143 -0
  680. package/tsconfig.json +2 -0
  681. package/vite.config.ts +4 -1
  682. package/vite.config.web.ts +5 -0
  683. package/.cargo/config.toml +0 -13
  684. package/.claude/settings.local.json +0 -182
  685. package/.codex +0 -0
  686. package/CLAUDE.md +0 -102
  687. package/out/web/Live2DCubismCore.js +0 -9
  688. package/out/web/assets/MintDashboard-DWSqucgb.js +0 -57
  689. package/out/web/assets/SettingsWindow-DFM2aj5B.js +0 -2
  690. package/out/web/assets/chunk-CKBBP62Z-nr1uKPj9.js +0 -2
  691. package/out/web/assets/chunk-DSNCTWBM-Dtj8Rqsd.js +0 -2
  692. package/out/web/assets/chunk-DU6HZSFF-2tv5K1wT.js +0 -128
  693. package/out/web/assets/classDiagram-DTDB5LWJ-BdSFArgw.js +0 -1
  694. package/out/web/assets/classDiagram-v2-JRS7N3AN-BdSFArgw.js +0 -1
  695. package/out/web/assets/core-DImejIUG.js +0 -1
  696. package/out/web/assets/dist-js-BxuKEVWM.js +0 -1
  697. package/out/web/assets/event-BNl8Zahw.js +0 -1
  698. package/out/web/assets/fira-code-cyrillic-400-normal-UC0NFL4U.woff2 +0 -0
  699. package/out/web/assets/fira-code-cyrillic-400-normal-dc1q3VD7.woff +0 -0
  700. package/out/web/assets/fira-code-cyrillic-500-normal-Bu7VCL72.woff +0 -0
  701. package/out/web/assets/fira-code-cyrillic-500-normal-CX35h3Mg.woff2 +0 -0
  702. package/out/web/assets/fira-code-cyrillic-ext-400-normal-Buh61xzB.woff +0 -0
  703. package/out/web/assets/fira-code-cyrillic-ext-400-normal-txZ9Fk_1.woff2 +0 -0
  704. package/out/web/assets/fira-code-cyrillic-ext-500-normal-CYRspHgm.woff2 +0 -0
  705. package/out/web/assets/fira-code-cyrillic-ext-500-normal-CwtLmSFD.woff +0 -0
  706. package/out/web/assets/fira-code-greek-400-normal-B2Gh_Y8s.woff2 +0 -0
  707. package/out/web/assets/fira-code-greek-400-normal-DobhaxvF.woff +0 -0
  708. package/out/web/assets/fira-code-greek-500-normal-CVO0IRNt.woff2 +0 -0
  709. package/out/web/assets/fira-code-greek-500-normal-DEXIWiey.woff +0 -0
  710. package/out/web/assets/fira-code-greek-ext-400-normal-DR7mBgIM.woff2 +0 -0
  711. package/out/web/assets/fira-code-greek-ext-400-normal-DSPUheWg.woff +0 -0
  712. package/out/web/assets/fira-code-greek-ext-500-normal-BO3iq16n.woff2 +0 -0
  713. package/out/web/assets/fira-code-greek-ext-500-normal-CMiNLXER.woff +0 -0
  714. package/out/web/assets/fira-code-latin-ext-400-normal-DG73JjOf.woff +0 -0
  715. package/out/web/assets/fira-code-latin-ext-400-normal-KSMg0QLl.woff2 +0 -0
  716. package/out/web/assets/fira-code-latin-ext-500-normal-CaANqXjI.woff +0 -0
  717. package/out/web/assets/fira-code-latin-ext-500-normal-Crd9e_oI.woff2 +0 -0
  718. package/out/web/assets/flowDiagram-A5DVABFB-BpnHUSU7.js +0 -1
  719. package/out/web/assets/index-CVRg270z.css +0 -1
  720. package/out/web/assets/index-DxZQr9yD.js +0 -10
  721. package/out/web/assets/inter-cyrillic-400-normal-HOLc17fK.woff +0 -0
  722. package/out/web/assets/inter-cyrillic-400-normal-obahsSVq.woff2 +0 -0
  723. package/out/web/assets/inter-cyrillic-500-normal-BasfLYem.woff2 +0 -0
  724. package/out/web/assets/inter-cyrillic-500-normal-CxZf_p3X.woff +0 -0
  725. package/out/web/assets/inter-cyrillic-600-normal-4D_pXhcN.woff +0 -0
  726. package/out/web/assets/inter-cyrillic-600-normal-CWCymEST.woff2 +0 -0
  727. package/out/web/assets/inter-cyrillic-ext-400-normal-BQZuk6qB.woff2 +0 -0
  728. package/out/web/assets/inter-cyrillic-ext-400-normal-DQukG94-.woff +0 -0
  729. package/out/web/assets/inter-cyrillic-ext-500-normal-B0yAr1jD.woff2 +0 -0
  730. package/out/web/assets/inter-cyrillic-ext-500-normal-BmqWE9Dz.woff +0 -0
  731. package/out/web/assets/inter-cyrillic-ext-600-normal-Bcila6Z-.woff +0 -0
  732. package/out/web/assets/inter-cyrillic-ext-600-normal-Dfes3d0z.woff2 +0 -0
  733. package/out/web/assets/inter-greek-400-normal-B4URO6DV.woff2 +0 -0
  734. package/out/web/assets/inter-greek-400-normal-q2sYcFCs.woff +0 -0
  735. package/out/web/assets/inter-greek-500-normal-BIZE56-Y.woff2 +0 -0
  736. package/out/web/assets/inter-greek-500-normal-Xzm54t5V.woff +0 -0
  737. package/out/web/assets/inter-greek-600-normal-BZpKdvQh.woff +0 -0
  738. package/out/web/assets/inter-greek-600-normal-plRanbMR.woff2 +0 -0
  739. package/out/web/assets/inter-greek-ext-400-normal-DGGRlc-M.woff2 +0 -0
  740. package/out/web/assets/inter-greek-ext-400-normal-KugGGMne.woff +0 -0
  741. package/out/web/assets/inter-greek-ext-500-normal-2j5mBUwD.woff +0 -0
  742. package/out/web/assets/inter-greek-ext-500-normal-C4iEst2y.woff2 +0 -0
  743. package/out/web/assets/inter-greek-ext-600-normal-B8X0CLgF.woff +0 -0
  744. package/out/web/assets/inter-greek-ext-600-normal-DRtmH8MT.woff2 +0 -0
  745. package/out/web/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
  746. package/out/web/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
  747. package/out/web/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
  748. package/out/web/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
  749. package/out/web/assets/inter-latin-600-normal-CiBQ2DWP.woff +0 -0
  750. package/out/web/assets/inter-latin-600-normal-LgqL8muc.woff2 +0 -0
  751. package/out/web/assets/inter-latin-ext-400-normal-77YHD8bZ.woff +0 -0
  752. package/out/web/assets/inter-latin-ext-400-normal-C1nco2VV.woff2 +0 -0
  753. package/out/web/assets/inter-latin-ext-500-normal-BxGbmqWO.woff +0 -0
  754. package/out/web/assets/inter-latin-ext-500-normal-CV4jyFjo.woff2 +0 -0
  755. package/out/web/assets/inter-latin-ext-600-normal-CIVaiw4L.woff +0 -0
  756. package/out/web/assets/inter-latin-ext-600-normal-D2bJ5OIk.woff2 +0 -0
  757. package/out/web/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff +0 -0
  758. package/out/web/assets/inter-vietnamese-400-normal-DMkecbls.woff2 +0 -0
  759. package/out/web/assets/inter-vietnamese-500-normal-DOriooB6.woff2 +0 -0
  760. package/out/web/assets/inter-vietnamese-500-normal-mJboJaSs.woff +0 -0
  761. package/out/web/assets/inter-vietnamese-600-normal-BuLX-rYi.woff +0 -0
  762. package/out/web/assets/inter-vietnamese-600-normal-Cc8MFFhd.woff2 +0 -0
  763. package/out/web/assets/kanit-latin-400-normal-BUlsQXH3.woff2 +0 -0
  764. package/out/web/assets/kanit-latin-400-normal-HVZWrfev.woff +0 -0
  765. package/out/web/assets/kanit-latin-500-normal-CX_4XcKi.woff2 +0 -0
  766. package/out/web/assets/kanit-latin-500-normal-qBha86O4.woff +0 -0
  767. package/out/web/assets/kanit-latin-600-normal-0lS78yAd.woff2 +0 -0
  768. package/out/web/assets/kanit-latin-600-normal-DlacpeTg.woff +0 -0
  769. package/out/web/assets/kanit-latin-ext-400-normal-B2xPf58u.woff2 +0 -0
  770. package/out/web/assets/kanit-latin-ext-400-normal-CHvNKHHD.woff +0 -0
  771. package/out/web/assets/kanit-latin-ext-500-normal-BFdWrG22.woff +0 -0
  772. package/out/web/assets/kanit-latin-ext-500-normal-EHs3suD6.woff2 +0 -0
  773. package/out/web/assets/kanit-latin-ext-600-normal-BBDzoLaj.woff +0 -0
  774. package/out/web/assets/kanit-latin-ext-600-normal-DAX3wtuq.woff2 +0 -0
  775. package/out/web/assets/kanit-thai-400-normal-D2nliXKa.woff +0 -0
  776. package/out/web/assets/kanit-thai-400-normal-D80Pglro.woff2 +0 -0
  777. package/out/web/assets/kanit-thai-500-normal-BB8gri04.woff2 +0 -0
  778. package/out/web/assets/kanit-thai-500-normal-Iy_w0a80.woff +0 -0
  779. package/out/web/assets/kanit-thai-600-normal-Bj6oiaDc.woff2 +0 -0
  780. package/out/web/assets/kanit-thai-600-normal-wbTlPO7I.woff +0 -0
  781. package/out/web/assets/kanit-vietnamese-400-normal-DRI8YWyb.woff2 +0 -0
  782. package/out/web/assets/kanit-vietnamese-400-normal-dow6Bnto.woff +0 -0
  783. package/out/web/assets/kanit-vietnamese-500-normal-BzTrpCM3.woff2 +0 -0
  784. package/out/web/assets/kanit-vietnamese-500-normal-T1ndT7MF.woff +0 -0
  785. package/out/web/assets/kanit-vietnamese-600-normal-Bi7fw3I4.woff2 +0 -0
  786. package/out/web/assets/kanit-vietnamese-600-normal-DXCRAa_8.woff +0 -0
  787. package/out/web/assets/mali-latin-400-normal-ByPcZ8Gx.woff +0 -0
  788. package/out/web/assets/mali-latin-400-normal-Cuc6Ykl8.woff2 +0 -0
  789. package/out/web/assets/mali-latin-500-normal-B01M-LP6.woff2 +0 -0
  790. package/out/web/assets/mali-latin-500-normal-Thgqw_9f.woff +0 -0
  791. package/out/web/assets/mali-latin-600-normal-B9TiUNpT.woff2 +0 -0
  792. package/out/web/assets/mali-latin-600-normal-CycpQCwU.woff +0 -0
  793. package/out/web/assets/mali-latin-ext-400-normal-DUVdJQO4.woff +0 -0
  794. package/out/web/assets/mali-latin-ext-400-normal-iCG1ALuJ.woff2 +0 -0
  795. package/out/web/assets/mali-latin-ext-500-normal-BfJE4UMZ.woff2 +0 -0
  796. package/out/web/assets/mali-latin-ext-500-normal-FoLEyOKS.woff +0 -0
  797. package/out/web/assets/mali-latin-ext-600-normal-BTvIfiCp.woff2 +0 -0
  798. package/out/web/assets/mali-latin-ext-600-normal-DAQcLVmD.woff +0 -0
  799. package/out/web/assets/mali-thai-400-normal-BO7A3bLZ.woff2 +0 -0
  800. package/out/web/assets/mali-thai-400-normal-CX9VrjTn.woff +0 -0
  801. package/out/web/assets/mali-thai-500-normal-Cph2qha9.woff +0 -0
  802. package/out/web/assets/mali-thai-500-normal-D89rMT77.woff2 +0 -0
  803. package/out/web/assets/mali-thai-600-normal-DPgXT6rZ.woff +0 -0
  804. package/out/web/assets/mali-thai-600-normal-DlTx3OsB.woff2 +0 -0
  805. package/out/web/assets/mali-vietnamese-400-normal-DUKxzXaz.woff2 +0 -0
  806. package/out/web/assets/mali-vietnamese-400-normal-gFDLvsOK.woff +0 -0
  807. package/out/web/assets/mali-vietnamese-500-normal--OY07q2c.woff +0 -0
  808. package/out/web/assets/mali-vietnamese-500-normal-KDrNRwfW.woff2 +0 -0
  809. package/out/web/assets/mali-vietnamese-600-normal-CtTMUU7T.woff +0 -0
  810. package/out/web/assets/mali-vietnamese-600-normal-DFlJr66u.woff2 +0 -0
  811. package/out/web/assets/mermaid.core-DYGGlqOf.js +0 -11
  812. package/out/web/assets/mitr-latin-400-normal-Cb18vPmZ.woff +0 -0
  813. package/out/web/assets/mitr-latin-400-normal-CwbkLQ4a.woff2 +0 -0
  814. package/out/web/assets/mitr-latin-500-normal-B4JOhzAi.woff +0 -0
  815. package/out/web/assets/mitr-latin-500-normal-BmClOow2.woff2 +0 -0
  816. package/out/web/assets/mitr-latin-ext-400-normal-B3CPG7Yo.woff2 +0 -0
  817. package/out/web/assets/mitr-latin-ext-400-normal-DTx65hiC.woff +0 -0
  818. package/out/web/assets/mitr-latin-ext-500-normal-B_Diajz1.woff2 +0 -0
  819. package/out/web/assets/mitr-latin-ext-500-normal-DYOsVaVT.woff +0 -0
  820. package/out/web/assets/mitr-thai-400-normal-CS4Do6aM.woff +0 -0
  821. package/out/web/assets/mitr-thai-400-normal-CXWaAdVj.woff2 +0 -0
  822. package/out/web/assets/mitr-thai-500-normal-CVdBrEVH.woff2 +0 -0
  823. package/out/web/assets/mitr-thai-500-normal-CW_B5D-i.woff +0 -0
  824. package/out/web/assets/mitr-vietnamese-400-normal-CPMGWyoC.woff2 +0 -0
  825. package/out/web/assets/mitr-vietnamese-400-normal-Z6XhIpdY.woff +0 -0
  826. package/out/web/assets/mitr-vietnamese-500-normal-CDrv0585.woff2 +0 -0
  827. package/out/web/assets/mitr-vietnamese-500-normal-CMQWyq-Z.woff +0 -0
  828. package/out/web/assets/noto-sans-thai-latin-400-normal-BaWBrqFP.woff2 +0 -0
  829. package/out/web/assets/noto-sans-thai-latin-400-normal-Dh_FJjOB.woff +0 -0
  830. package/out/web/assets/noto-sans-thai-latin-500-normal-BC04jsmm.woff +0 -0
  831. package/out/web/assets/noto-sans-thai-latin-500-normal-fyBKClF1.woff2 +0 -0
  832. package/out/web/assets/noto-sans-thai-latin-600-normal-CemcjnVR.woff2 +0 -0
  833. package/out/web/assets/noto-sans-thai-latin-600-normal-Dbc-dNO6.woff +0 -0
  834. package/out/web/assets/noto-sans-thai-latin-ext-400-normal-B-1H0YkH.woff +0 -0
  835. package/out/web/assets/noto-sans-thai-latin-ext-400-normal-nBP59pVw.woff2 +0 -0
  836. package/out/web/assets/noto-sans-thai-latin-ext-500-normal-B-ZWb8SZ.woff2 +0 -0
  837. package/out/web/assets/noto-sans-thai-latin-ext-500-normal-CrEHO7C8.woff +0 -0
  838. package/out/web/assets/noto-sans-thai-latin-ext-600-normal-PkMZo0xa.woff +0 -0
  839. package/out/web/assets/noto-sans-thai-latin-ext-600-normal-maHHBsez.woff2 +0 -0
  840. package/out/web/assets/noto-sans-thai-thai-400-normal-BEOmKAI1.woff2 +0 -0
  841. package/out/web/assets/noto-sans-thai-thai-400-normal-Dxb_unuU.woff +0 -0
  842. package/out/web/assets/noto-sans-thai-thai-500-normal-BOGXO2tw.woff +0 -0
  843. package/out/web/assets/noto-sans-thai-thai-500-normal-CU_X86GQ.woff2 +0 -0
  844. package/out/web/assets/noto-sans-thai-thai-600-normal-CmgB8_JK.woff +0 -0
  845. package/out/web/assets/noto-sans-thai-thai-600-normal-Cs73sVjl.woff2 +0 -0
  846. package/out/web/assets/oauthManager-DNiwM8Rn.js +0 -2
  847. package/out/web/assets/oauthManager-DuamS1Av.css +0 -1
  848. package/out/web/assets/outfit-latin-300-normal-AWLfuCom.woff +0 -0
  849. package/out/web/assets/outfit-latin-300-normal-CRYTwY6m.woff2 +0 -0
  850. package/out/web/assets/outfit-latin-400-normal-BGsTXAXT.woff2 +0 -0
  851. package/out/web/assets/outfit-latin-400-normal-DMwTpYkH.woff +0 -0
  852. package/out/web/assets/outfit-latin-500-normal-ClnHRwRh.woff +0 -0
  853. package/out/web/assets/outfit-latin-500-normal-DKnIMDSk.woff2 +0 -0
  854. package/out/web/assets/outfit-latin-600-normal-B7SfZ07L.woff2 +0 -0
  855. package/out/web/assets/outfit-latin-600-normal-BEfTtDA7.woff +0 -0
  856. package/out/web/assets/outfit-latin-ext-300-normal-BYS6Dpvq.woff2 +0 -0
  857. package/out/web/assets/outfit-latin-ext-300-normal-CMIMshEd.woff +0 -0
  858. package/out/web/assets/outfit-latin-ext-400-normal-5tcqmc2S.woff2 +0 -0
  859. package/out/web/assets/outfit-latin-ext-400-normal-DHm7mdGe.woff +0 -0
  860. package/out/web/assets/outfit-latin-ext-500-normal-DrCvqoFD.woff +0 -0
  861. package/out/web/assets/outfit-latin-ext-500-normal-zeox_O30.woff2 +0 -0
  862. package/out/web/assets/outfit-latin-ext-600-normal-B85nYjL1.woff2 +0 -0
  863. package/out/web/assets/outfit-latin-ext-600-normal-CWJcPgd7.woff +0 -0
  864. package/out/web/assets/preload-helper-D0uHx7FM.js +0 -1
  865. package/out/web/assets/prompt-latin-400-normal-BQ9zjSN8.woff2 +0 -0
  866. package/out/web/assets/prompt-latin-400-normal-CxU4ec_r.woff +0 -0
  867. package/out/web/assets/prompt-latin-500-normal-B1edBDQ9.woff +0 -0
  868. package/out/web/assets/prompt-latin-500-normal-CxzxEHZc.woff2 +0 -0
  869. package/out/web/assets/prompt-latin-600-normal-CSDDLorv.woff +0 -0
  870. package/out/web/assets/prompt-latin-600-normal-hKZWXsc1.woff2 +0 -0
  871. package/out/web/assets/prompt-latin-ext-400-normal-1pHbN9uy.woff +0 -0
  872. package/out/web/assets/prompt-latin-ext-400-normal-DdSafGZ9.woff2 +0 -0
  873. package/out/web/assets/prompt-latin-ext-500-normal--EZ1um7s.woff2 +0 -0
  874. package/out/web/assets/prompt-latin-ext-500-normal-D-lI1EVT.woff +0 -0
  875. package/out/web/assets/prompt-latin-ext-600-normal-C8b50x67.woff +0 -0
  876. package/out/web/assets/prompt-latin-ext-600-normal-Cg9L7iJU.woff2 +0 -0
  877. package/out/web/assets/prompt-thai-400-normal-BrkKv8cO.woff2 +0 -0
  878. package/out/web/assets/prompt-thai-400-normal-DJypnfr3.woff +0 -0
  879. package/out/web/assets/prompt-thai-500-normal-C18pDUoL.woff2 +0 -0
  880. package/out/web/assets/prompt-thai-500-normal-D7YNqUJD.woff +0 -0
  881. package/out/web/assets/prompt-thai-600-normal-CYPViXds.woff +0 -0
  882. package/out/web/assets/prompt-thai-600-normal-MrdfU7zR.woff2 +0 -0
  883. package/out/web/assets/prompt-vietnamese-400-normal-BCPzsgPT.woff2 +0 -0
  884. package/out/web/assets/prompt-vietnamese-400-normal-D4pLyeNK.woff +0 -0
  885. package/out/web/assets/prompt-vietnamese-500-normal-DmzxmPwa.woff2 +0 -0
  886. package/out/web/assets/prompt-vietnamese-500-normal-w_sS_j2F.woff +0 -0
  887. package/out/web/assets/prompt-vietnamese-600-normal-1dyohtNh.woff +0 -0
  888. package/out/web/assets/prompt-vietnamese-600-normal-7QWjJBsF.woff2 +0 -0
  889. package/out/web/assets/sarabun-latin-400-normal-DmguIS-7.woff +0 -0
  890. package/out/web/assets/sarabun-latin-400-normal-URPBxl-K.woff2 +0 -0
  891. package/out/web/assets/sarabun-latin-500-normal-BjUTcdxu.woff2 +0 -0
  892. package/out/web/assets/sarabun-latin-500-normal-CJexTAnl.woff +0 -0
  893. package/out/web/assets/sarabun-latin-600-normal-9FonlkKx.woff +0 -0
  894. package/out/web/assets/sarabun-latin-600-normal-DMD3TROr.woff2 +0 -0
  895. package/out/web/assets/sarabun-latin-ext-400-normal-BbP7GpLL.woff2 +0 -0
  896. package/out/web/assets/sarabun-latin-ext-400-normal-BsmTn0SS.woff +0 -0
  897. package/out/web/assets/sarabun-latin-ext-500-normal-Cot16Bd2.woff +0 -0
  898. package/out/web/assets/sarabun-latin-ext-500-normal-DE1LysqI.woff2 +0 -0
  899. package/out/web/assets/sarabun-latin-ext-600-normal-0tXu9x_i.woff +0 -0
  900. package/out/web/assets/sarabun-latin-ext-600-normal-BRxjKUQ6.woff2 +0 -0
  901. package/out/web/assets/sarabun-thai-400-normal-C2DaJlKK.woff2 +0 -0
  902. package/out/web/assets/sarabun-thai-400-normal-CH-UbWgg.woff +0 -0
  903. package/out/web/assets/sarabun-thai-500-normal-BVssjame.woff2 +0 -0
  904. package/out/web/assets/sarabun-thai-500-normal-C3iDLEzi.woff +0 -0
  905. package/out/web/assets/sarabun-thai-600-normal-3dIAQ-_s.woff2 +0 -0
  906. package/out/web/assets/sarabun-thai-600-normal-DWKeTe6a.woff +0 -0
  907. package/out/web/assets/sarabun-vietnamese-400-normal-DPfkSCMy.woff +0 -0
  908. package/out/web/assets/sarabun-vietnamese-400-normal-igdrzAGY.woff2 +0 -0
  909. package/out/web/assets/sarabun-vietnamese-500-normal-CpRVvGRU.woff +0 -0
  910. package/out/web/assets/sarabun-vietnamese-500-normal-Dpe7Br9m.woff2 +0 -0
  911. package/out/web/assets/sarabun-vietnamese-600-normal-BMbOgAW-.woff +0 -0
  912. package/out/web/assets/sarabun-vietnamese-600-normal-DkxrOlLB.woff2 +0 -0
  913. package/out/web/assets/stateDiagram-v2-4QOOHH4V-s6P9g2_b.js +0 -1
  914. package/out/web/assets/swimlanesDiagram-VK2B7HYN-CV9fOLrV.js +0 -8
  915. package/out/web/assets/tauri-CNrHQ2Gb.js +0 -3
  916. package/out/web/assets/window-DkIhgI7B.js +0 -1
  917. package/skills-lock.json +0 -41
  918. package/src/renderer/public/Live2DCubismCore.js +0 -9
  919. package/src/renderer/src/companionWidget.ts +0 -95
  920. package/src/renderer/src/components/Live2DStage.tsx +0 -389
  921. package/src/renderer/src/components/ModelPanel.tsx +0 -154
  922. package/src/renderer/src/css/model.css +0 -627
  923. package/src/renderer/src-web/companionWidget.ts +0 -37
  924. package/src/renderer/src-web/components/ModelPanel.tsx +0 -12
  925. package/src/renderer/src-web/css/model.css +0 -628
@@ -222,38 +222,12 @@ pub(super) fn print_table_block(table_lines: &[String], is_first: &mut bool) {
222
222
  }
223
223
  }
224
224
 
225
- pub(super) fn should_show_verification(verification: &str) -> bool {
226
- let normalized = verification.trim().to_ascii_lowercase();
227
- if normalized.is_empty() {
228
- return false;
229
- }
230
- if normalized.starts_with("information retrieved from web search")
231
- || normalized.starts_with("successfully ran background command")
232
- || normalized.starts_with("opened ")
233
- || normalized.contains("background command to open")
234
- || normalized.contains("web search results")
235
- {
236
- return false;
237
- }
238
- !matches!(
239
- normalized.as_str(),
240
- "not run"
241
- | "not run."
242
- | "no checks run"
243
- | "no checks run."
244
- | "no technical task requested"
245
- | "no technical task requested."
246
- | "no technical task requested, just a greeting."
247
- | "not required"
248
- | "not required."
249
- | "none"
250
- | "n/a"
251
- )
252
- }
253
-
254
225
  #[derive(Debug, Default)]
255
226
  pub(super) struct LiveStatus {
227
+ pub(super) tui: Option<crate::interactive::TuiHandle>,
256
228
  pub(super) thinking: Option<String>,
229
+ pub(super) compaction_started: Option<Instant>,
230
+ pub(super) compaction_scopes: std::collections::BTreeMap<String, usize>,
257
231
  /// Context-window usage (0-100) as of the last completed step —
258
232
  /// mirrored here (not just built into `thinking`'s label text) so the
259
233
  /// 150ms elapsed-time ticker in `run_code_agent_with_options` can keep
@@ -305,6 +279,9 @@ pub(super) struct LiveStatus {
305
279
  pub(super) committed_activities: usize,
306
280
  pub(super) committed_tasks: usize,
307
281
  pub(super) spinner_tick: usize,
282
+ /// When the currently running tool began executing — used to animate an
283
+ /// inline spinner and elapsed timer directly on the active tool line.
284
+ pub(super) active_tool_started: Option<Instant>,
308
285
  /// Sources collected from web_search ToolEnd results (title, url)
309
286
  pub(super) web_sources: Vec<(String, String)>,
310
287
  pub(super) inline_tui: InlineTui,
@@ -562,15 +539,43 @@ pub(super) fn strip_ansi_escapes(s: &str) -> String {
562
539
  result
563
540
  }
564
541
 
565
- pub(super) fn is_thai_combining(c: char) -> bool {
566
- matches!(c,
567
- '\u{0e31}' | '\u{0e34}'..='\u{0e37}' | '\u{0e38}'..='\u{0e39}' |
568
- '\u{0e47}'..='\u{0e4e}'
542
+ pub(super) fn compaction_label(elapsed: Duration) -> String {
543
+ format!(
544
+ "Compacting context · {}s • Esc to interrupt",
545
+ elapsed.as_secs()
569
546
  )
570
547
  }
571
548
 
549
+ pub(super) fn update_context_compaction(
550
+ status: &mut LiveStatus,
551
+ phase: &str,
552
+ scope: Option<&str>,
553
+ now: Instant,
554
+ ) {
555
+ let key = scope.unwrap_or("").to_owned();
556
+ if phase == "started" {
557
+ *status.compaction_scopes.entry(key).or_default() += 1;
558
+ status.compaction_started.get_or_insert(now);
559
+ } else {
560
+ if let Some(count) = status.compaction_scopes.get_mut(&key) {
561
+ *count -= 1;
562
+ if *count == 0 {
563
+ status.compaction_scopes.remove(&key);
564
+ }
565
+ }
566
+ if status.compaction_scopes.is_empty() {
567
+ status.compaction_started = None;
568
+ }
569
+ }
570
+ status.waiting_for_network = None;
571
+ }
572
+
572
573
  pub(super) fn apply_wave_effect(text: &str, tick: usize) -> String {
573
- let (label, metadata) = if let Some(idx) = text.rfind(" • Esc to interrupt)") {
574
+ let (label, metadata) = if text.starts_with("Compacting context")
575
+ && let Some(idx) = text.find(" · ")
576
+ {
577
+ (&text[..idx], &text[idx..])
578
+ } else if let Some(idx) = text.rfind(" • Esc to interrupt)") {
574
579
  if let Some(open_paren_idx) = text[..idx].rfind('(') {
575
580
  (&text[..open_paren_idx], &text[open_paren_idx..])
576
581
  } else {
@@ -606,33 +611,10 @@ pub(super) fn apply_wave_effect(text: &str, tick: usize) -> String {
606
611
  let x = (i as f32 * 0.4) - phase;
607
612
  let t = (x.sin() + 1.0) / 2.0; // Oscillates in [0.0, 1.0]
608
613
 
609
- // Stop colors: Dim Gray (70, 70, 70) -> Mint Green (105, 230, 166) -> Cyan (78, 201, 216)
610
- let (r, g, b) = if t < 0.3 {
611
- let local_t = t / 0.3;
612
- let r = 70.0 + (105.0 - 70.0) * local_t;
613
- let g = 70.0 + (230.0 - 70.0) * local_t;
614
- let b = 70.0 + (166.0 - 70.0) * local_t;
615
- (r, g, b)
616
- } else if t < 0.7 {
617
- let local_t = (t - 0.3) / 0.4;
618
- let r = 105.0 + (78.0 - 105.0) * local_t;
619
- let g = 230.0 + (201.0 - 230.0) * local_t;
620
- let b = 166.0 + (216.0 - 166.0) * local_t;
621
- (r, g, b)
622
- } else {
623
- let local_t = (t - 0.7) / 0.3;
624
- let r = 78.0 + (70.0 - 78.0) * local_t;
625
- let g = 201.0 + (70.0 - 201.0) * local_t;
626
- let b = 216.0 + (70.0 - 216.0) * local_t;
627
- (r, g, b)
628
- };
629
-
614
+ let emphasis = if t > 0.72 { BOLD } else { "" };
630
615
  animated_label.push_str(&format!(
631
- "\x1b[1m\x1b[38;2;{};{};{}m{}\x1b[0m",
632
- r.round() as u8,
633
- g.round() as u8,
634
- b.round() as u8,
635
- c
616
+ "{emphasis}{}{c}{RESET}",
617
+ crate::terminal_theme::ANSI_ACCENT
636
618
  ));
637
619
  }
638
620
 
@@ -715,7 +697,10 @@ pub(super) fn compose_queue_box(
715
697
  (lines, cursor_x, cursor_y)
716
698
  }
717
699
 
718
- pub(super) fn render_live_status(status: &mut LiveStatus) {
700
+ /// Renders the live viewport and reports whether a terminal was available to
701
+ /// draw it. Callers use the result to fall back to plain output when Mint is
702
+ /// run without an interactive terminal.
703
+ pub(super) fn render_live_status(status: &mut LiveStatus) -> bool {
719
704
  let mut lines = Vec::new();
720
705
  let explored_start = status.committed_explored.min(status.explored.len());
721
706
  let activities_start = status.committed_activities.min(status.activities.len());
@@ -729,13 +714,17 @@ pub(super) fn render_live_status(status: &mut LiveStatus) {
729
714
  status.spinner_tick += 1;
730
715
 
731
716
  lines.extend(plan_lines(&status.plan_steps, true, tick));
732
- lines.extend(activity_block_lines(
733
- &status.tasks[tasks_start..],
734
- &status.activities[activities_start..],
735
- &status.explored[explored_start..],
736
- true,
737
- tick,
738
- ));
717
+ if status.tui.is_none() {
718
+ lines.extend(activity_block_lines(
719
+ &status.tasks[tasks_start..],
720
+ &status.activities[activities_start..],
721
+ &status.explored[explored_start..],
722
+ true,
723
+ tick,
724
+ status.active_tool_started,
725
+ true,
726
+ ));
727
+ }
739
728
  // Built here (not inline below) so both destinations for it — the old
740
729
  // trailing-line spot in `lines`, and the queue box's own pinned row —
741
730
  // share one animation. When the queue box is about to be drawn, it's
@@ -747,26 +736,6 @@ pub(super) fn render_live_status(status: &mut LiveStatus) {
747
736
  // that — same information, anchored to the thing it describes instead
748
737
  // of to whatever happened to print last.
749
738
  let thinking_display = status.thinking.as_ref().map(|thinking| {
750
- // Moon phases, forced to *text* presentation via `\u{FE0E}` so they
751
- // stay single-column-width instead of the wide (2-column) emoji
752
- // glyph — deliberate, even though it means some terminal fonts only
753
- // ship one fallback glyph for all 8 text-style variants and the
754
- // spin won't visibly animate there. Width consistency was chosen
755
- // over guaranteed animation; swap to a plain-text spinner (e.g.
756
- // Braille patterns) instead of dropping this selector if that
757
- // trade-off ever needs to flip back.
758
- let frames = &[
759
- "🌑\u{FE0E}",
760
- "🌒\u{FE0E}",
761
- "🌓\u{FE0E}",
762
- "🌔\u{FE0E}",
763
- "🌕\u{FE0E}",
764
- "🌖\u{FE0E}",
765
- "🌗\u{FE0E}",
766
- "🌘\u{FE0E}",
767
- ];
768
- let frame = frames[status.spinner_tick % frames.len()];
769
-
770
739
  let dots_frames = &["", ".", "..", "..."];
771
740
  let dots = dots_frames[(status.spinner_tick / 2) % dots_frames.len()];
772
741
 
@@ -781,12 +750,16 @@ pub(super) fn render_live_status(status: &mut LiveStatus) {
781
750
 
782
751
  let waved_thinking = apply_wave_effect(&custom_thinking, status.spinner_tick);
783
752
 
784
- format!("{MINT}{frame}{RESET} {waved_thinking}")
753
+ // A single-column text star matches the GUI and joins the TUI glow.
754
+ format!("{MINT}✦{RESET} {waved_thinking}")
785
755
  });
786
756
  let queue_box_will_show = status.queue_enabled && status.accepting_input;
787
757
  if !queue_box_will_show && let Some(display) = &thinking_display {
788
758
  lines.push(format!(" {display}"));
789
759
  }
760
+ if status.compaction_started.is_some() {
761
+ lines.push(" Summarizing earlier steps to continue working".into());
762
+ }
790
763
  // The queueing follow-up box (see `AgentOptions::queueing`) is appended
791
764
  // after everything above, so it's always the bottom-most thing in the
792
765
  // live region. Deliberately a *different* style from
@@ -831,18 +804,23 @@ pub(super) fn render_live_status(status: &mut LiveStatus) {
831
804
  }
832
805
  }
833
806
 
807
+ if let Some(tui) = &status.tui {
808
+ tui.set_status(lines.iter().map(|line| strip_ansi_escapes(line)).collect());
809
+ return true;
810
+ }
811
+
834
812
  if lines.is_empty() && box_lines.is_empty() {
835
813
  status.inline_tui.teardown();
836
- return;
814
+ return false;
837
815
  }
838
816
 
839
817
  let Ok(terminal) = status.inline_tui.ensure() else {
840
- return;
818
+ return false;
841
819
  };
842
820
  // `draw()` itself only queries the cursor position on the rare path
843
821
  // where it detects the terminal window was actually resized since the
844
822
  // last frame — bracket it too, defensively, for that case.
845
- let _ = with_raw_mode_for_cursor_query(|| {
823
+ with_raw_mode_for_cursor_query(|| {
846
824
  terminal.draw(|frame| {
847
825
  let area = frame.area();
848
826
  // The box (the "Ask anything..." input) claims its own rows
@@ -856,7 +834,7 @@ pub(super) fn render_live_status(status: &mut LiveStatus) {
856
834
  let box_height = (box_lines.len() as u16).min(area.height);
857
835
  let available_for_status = area.height.saturating_sub(box_height);
858
836
 
859
- let mut status_line_count: u16 = 0;
837
+ let mut status_height: u16 = 0;
860
838
  let mut status_paragraph = None;
861
839
  if !lines.is_empty()
862
840
  && let Ok(status_text) = lines.join("\n").into_text()
@@ -874,17 +852,18 @@ pub(super) fn render_live_status(status: &mut LiveStatus) {
874
852
  // bottom of the frame. (`Paragraph::line_count` would do this
875
853
  // exactly, but it's gated behind an unstable ratatui feature.)
876
854
  let wrap_width = area.width.max(1);
877
- status_line_count = status_text
855
+ let status_line_count: u16 = status_text
878
856
  .lines
879
857
  .iter()
880
858
  .map(|line| (line.width().max(1) as u16).div_ceil(wrap_width))
881
859
  .sum();
860
+ status_height = status_line_count.min(available_for_status);
882
861
  status_paragraph = Some(
883
862
  ratatui::widgets::Paragraph::new(status_text)
884
- .wrap(ratatui::widgets::Wrap { trim: false }),
863
+ .wrap(ratatui::widgets::Wrap { trim: false })
864
+ .scroll((status_line_count.saturating_sub(status_height), 0)),
885
865
  );
886
866
  }
887
- let status_height = status_line_count.min(available_for_status);
888
867
  let status_area = ratatui::layout::Rect {
889
868
  height: status_height,
890
869
  ..area
@@ -909,14 +888,31 @@ pub(super) fn render_live_status(status: &mut LiveStatus) {
909
888
  ));
910
889
  }
911
890
  })
912
- });
891
+ })
892
+ .is_ok()
913
893
  }
914
894
 
915
895
  /// Freezes any in-flight activity into scrollback. Returns whether it
916
896
  /// actually committed anything — callers that print their own leading blank
917
897
  /// line right after (e.g. the final answer) use this to skip it when this
918
898
  /// already ended on one, instead of stacking two.
899
+ pub(super) fn commit_activity_snapshot_if_idle(
900
+ status: &mut LiveStatus,
901
+ active_tools: usize,
902
+ ) -> bool {
903
+ if active_tools > 0 {
904
+ return false;
905
+ }
906
+ commit_activity_snapshot(status)
907
+ }
908
+
919
909
  pub(super) fn commit_activity_snapshot(status: &mut LiveStatus) -> bool {
910
+ if status.tui.is_some() {
911
+ status.committed_explored = status.explored.len();
912
+ status.committed_activities = status.activities.len();
913
+ status.committed_tasks = status.tasks.len();
914
+ return false;
915
+ }
920
916
  let explored_start = status.committed_explored.min(status.explored.len());
921
917
  let activities_start = status.committed_activities.min(status.activities.len());
922
918
  let tasks_start = status.committed_tasks.min(status.tasks.len());
@@ -927,16 +923,16 @@ pub(super) fn commit_activity_snapshot(status: &mut LiveStatus) -> bool {
927
923
  &status.explored[explored_start..],
928
924
  false,
929
925
  0,
926
+ None,
927
+ true,
930
928
  );
931
929
  if lines.is_empty() {
932
930
  return false;
933
931
  }
934
932
 
935
933
  let (tw, _) = markdown::terminal_size_or_default();
936
- let width = tw as usize;
937
- lines.push(String::new());
938
- lines.push(format!("{DIM}{}{RESET}", "─".repeat(width)));
939
- lines.push(String::new());
934
+ let width = (tw as usize).saturating_sub(8).max(40);
935
+ lines.push(format!("{DIM} {}{RESET}", "─".repeat(width)));
940
936
  insert_permanent_lines(status, &lines);
941
937
 
942
938
  status.committed_explored = status.explored.len();
@@ -945,19 +941,202 @@ pub(super) fn commit_activity_snapshot(status: &mut LiveStatus) -> bool {
945
941
  true
946
942
  }
947
943
 
948
- pub(super) fn print_timeline_note(status: &mut LiveStatus, thought: &str) {
944
+ #[cfg(test)]
945
+ mod concurrent_activity_tests {
946
+ use super::*;
947
+
948
+ #[test]
949
+ fn unrelated_thought_keeps_in_flight_shell_activity_live() {
950
+ let mut status = LiveStatus::default();
951
+ status.tasks.push(TaskEntry {
952
+ label: "[run_shell] Running command: `sleep 1`...".to_owned(),
953
+ output: Vec::new(),
954
+ });
955
+
956
+ assert!(!commit_activity_snapshot_if_idle(&mut status, 1));
957
+ assert_eq!(status.committed_tasks, 0);
958
+ let live_lines = activity_block_lines(
959
+ &status.tasks[status.committed_tasks..],
960
+ &[],
961
+ &[],
962
+ true,
963
+ 0,
964
+ Some(Instant::now()),
965
+ true,
966
+ );
967
+ assert!(
968
+ live_lines
969
+ .iter()
970
+ .any(|line| line.contains("Running 1 shell command"))
971
+ );
972
+ assert!(live_lines.iter().any(|line| line.contains("sleep 1")));
973
+ }
974
+ }
975
+
976
+ #[cfg(test)]
977
+ mod compact_tui_status_tests {
978
+ use super::*;
979
+
980
+ #[test]
981
+ fn completed_shell_output_does_not_push_command_labels_out_of_live_view() {
982
+ let tasks = vec![
983
+ TaskEntry {
984
+ label: "[run_shell] Running command: `time sleep 10`...".into(),
985
+ output: Vec::new(),
986
+ },
987
+ TaskEntry {
988
+ label: "Finished command: `time sleep 10`".into(),
989
+ output: vec![
990
+ "stdout:".into(),
991
+ "stderr:".into(),
992
+ "real 0m10.002s".into(),
993
+ "user 0m0.002s".into(),
994
+ "sys 0m0.002s".into(),
995
+ ],
996
+ },
997
+ ];
998
+ let mut lines = activity_block_lines(&tasks, &[], &[], true, 0, None, false);
999
+ lines.push(" Herding...".into());
1000
+ let visible = &lines[lines.len().saturating_sub(8)..];
1001
+ assert!(
1002
+ visible
1003
+ .iter()
1004
+ .any(|line| line.contains("Running 1 shell command"))
1005
+ );
1006
+ assert!(visible.iter().any(|line| line.contains("[run_shell]")));
1007
+ assert!(visible.iter().any(|line| line.contains("Finished command")));
1008
+ assert!(!visible.iter().any(|line| line.contains("real 0m10.002s")));
1009
+ let committed = activity_block_lines(&tasks, &[], &[], false, 0, None, true);
1010
+ assert!(committed.iter().any(|line| line.contains("real 0m10.002s")));
1011
+ }
1012
+ }
1013
+
1014
+ pub(super) fn is_internal_cot(text: &str) -> bool {
1015
+ mint_core::orchestration::is_internal_cot(text)
1016
+ }
1017
+
1018
+ pub(super) fn print_extended_thinking(
1019
+ status: &mut LiveStatus,
1020
+ thought: &str,
1021
+ elapsed: Duration,
1022
+ stream_id: Option<&str>,
1023
+ ) {
1024
+ if let Some(tui) = &status.tui {
1025
+ crate::interactive::finish_thought(
1026
+ stream_id,
1027
+ thought,
1028
+ &crate::interactive::format_thought_elapsed(elapsed),
1029
+ );
1030
+ tui.finish_thought(stream_id, thought, elapsed);
1031
+ } else {
1032
+ print_timeline_note(status, thought, elapsed, stream_id);
1033
+ }
1034
+ }
1035
+
1036
+ #[cfg(test)]
1037
+ mod tui_thought_progress_tests {
1038
+ use super::*;
1039
+
1040
+ #[test]
1041
+ fn short_extended_thinking_collapses_in_tui_instead_of_printing_raw_text() {
1042
+ let state = std::sync::Arc::new(std::sync::Mutex::new(
1043
+ crate::interactive::ChatViewState::default(),
1044
+ ));
1045
+ let tui = crate::interactive::ChatTui::handle(state.clone());
1046
+ tui.append_thought_delta("one", "Checking", Duration::from_millis(1200));
1047
+ let mut status = LiveStatus {
1048
+ tui: Some(tui),
1049
+ ..Default::default()
1050
+ };
1051
+ print_extended_thinking(
1052
+ &mut status,
1053
+ "Checking files",
1054
+ Duration::from_millis(1900),
1055
+ Some("one"),
1056
+ );
1057
+ let state = state.lock().unwrap();
1058
+ assert_eq!(state.transcript.len(), 1);
1059
+ assert_eq!(state.transcript[0].text, "Thought · 1.9s (Ctrl+T to view)");
1060
+ }
1061
+ }
1062
+
1063
+ pub(super) fn print_timeline_note(
1064
+ status: &mut LiveStatus,
1065
+ thought: &str,
1066
+ elapsed: Duration,
1067
+ stream_id: Option<&str>,
1068
+ ) {
949
1069
  let thought = thought.trim();
950
1070
  if thought.is_empty() {
951
1071
  return;
952
1072
  }
953
- let (tw, _) = markdown::terminal_size_or_default();
954
- let width = tw as usize;
955
- let options = textwrap::Options::new(width)
956
- .initial_indent(" • ")
957
- .subsequent_indent(" ")
958
- .break_words(true);
959
- let wrapped = textwrap::fill(thought, &options);
960
- insert_permanent_lines(status, &[wrapped]);
1073
+ let elapsed_str = crate::interactive::format_thought_elapsed(elapsed);
1074
+ crate::interactive::finish_thought(stream_id, thought, &elapsed_str);
1075
+
1076
+ if is_internal_cot(thought) {
1077
+ if let Some(tui) = &status.tui {
1078
+ tui.finish_thought(stream_id, thought, elapsed);
1079
+ return;
1080
+ }
1081
+ let summary = format!(
1082
+ "{muted} • Thought for {elapsed_str}{reset} {muted}(Ctrl+T to view){reset}",
1083
+ muted = crate::terminal_theme::ANSI_MUTED,
1084
+ reset = crate::terminal_theme::ANSI_RESET
1085
+ );
1086
+ insert_permanent_lines(status, &[summary]);
1087
+ } else {
1088
+ let clean_note = thought
1089
+ .strip_prefix("•")
1090
+ .or_else(|| thought.strip_prefix("-"))
1091
+ .unwrap_or(thought)
1092
+ .trim();
1093
+ let clean_note = strip_intermediate_greeting(clean_note);
1094
+ let (tw, _) = markdown::terminal_size_or_default();
1095
+ let width = (tw as usize).saturating_sub(2).max(20);
1096
+ let options = textwrap::Options::new(width)
1097
+ .initial_indent(" ")
1098
+ .subsequent_indent(" ")
1099
+ .break_words(true);
1100
+ let wrapped = textwrap::fill(clean_note, &options);
1101
+ let formatted = format!("{WHITE}{wrapped}{RESET}");
1102
+ insert_permanent_lines(status, &[formatted]);
1103
+ }
1104
+ }
1105
+
1106
+ fn strip_intermediate_greeting(text: &str) -> &str {
1107
+ let mut s = text.trim();
1108
+ for prefix in &[
1109
+ "สวัสดีค่ะ",
1110
+ "สวัสดีครับ",
1111
+ "หวัดดีค่ะ",
1112
+ "หวัดดีครับ",
1113
+ "สวัสดี",
1114
+ "Hello",
1115
+ "Hi",
1116
+ "Hey",
1117
+ ] {
1118
+ if let Some(rest) = s.strip_prefix(prefix) {
1119
+ s = rest.trim_start();
1120
+ break;
1121
+ }
1122
+ }
1123
+ if let Some(rest) = s.strip_prefix("พี่") {
1124
+ if let Some(space_idx) = rest.find(' ') {
1125
+ s = rest[space_idx..].trim_start();
1126
+ }
1127
+ } else if let Some(rest) = s.strip_prefix("คุณ") {
1128
+ if let Some(space_idx) = rest.find(' ') {
1129
+ s = rest[space_idx..].trim_start();
1130
+ }
1131
+ }
1132
+ for emoji in &["🌿", "🍃", "✨", "🌱"] {
1133
+ if let Some(rest) = s.strip_prefix(emoji) {
1134
+ s = rest.trim_start();
1135
+ }
1136
+ }
1137
+ let trimmed =
1138
+ s.trim_start_matches(|c: char| c == ',' || c == '-' || c == '—' || c == ' ' || c == '•');
1139
+ if trimmed.is_empty() { text } else { trimmed }
961
1140
  }
962
1141
 
963
1142
  /// Inserts already ANSI-formatted `lines` as permanent content above the
@@ -979,6 +1158,10 @@ pub(super) fn print_timeline_note(status: &mut LiveStatus, thought: &str) {
979
1158
  /// Falls back to plain `println!` if no inline terminal is currently live
980
1159
  /// (e.g. committing before anything has rendered yet this turn).
981
1160
  pub(super) fn insert_permanent_lines(status: &mut LiveStatus, lines: &[String]) {
1161
+ if let Some(tui) = &status.tui {
1162
+ tui.push_notice(lines.join("\n"));
1163
+ return;
1164
+ }
982
1165
  let Some(terminal) = status.inline_tui.terminal.as_mut() else {
983
1166
  // Defensive, same reasoning as `approve_cb`/`on_chunk`: this is a
984
1167
  // plain `println!`, so it needs cooked mode regardless of whether
@@ -991,39 +1174,31 @@ pub(super) fn insert_permanent_lines(status: &mut LiveStatus, lines: &[String])
991
1174
  return;
992
1175
  };
993
1176
 
994
- let (tw, _) = markdown::terminal_size_or_default();
995
- let width = tw as usize;
996
- let mut height: u16 = 0;
997
- for line in lines {
998
- let stripped = strip_ansi_escapes(line);
999
- let line_len = stripped.chars().filter(|&c| !is_thai_combining(c)).count();
1000
- let physical_lines = if width > 0 {
1001
- line_len.div_ceil(width)
1002
- } else {
1003
- 1
1004
- }
1005
- .max(1);
1006
- height = height.saturating_add(physical_lines as u16);
1007
- }
1008
-
1009
1177
  let Ok(text) = lines.join("\n").into_text() else {
1010
1178
  return;
1011
1179
  };
1180
+ let (width, _) = markdown::terminal_size_or_default();
1181
+ let height = wrapped_text_height(&text, width);
1012
1182
  let _ = terminal.insert_before(height, |buf| {
1013
1183
  use ratatui::widgets::Widget as _;
1014
- // `height` above is computed assuming lines longer than the
1015
- // terminal width wrap onto extra rows; without `.wrap(...)` here
1016
- // the `Paragraph` instead clips each source line to a single row,
1017
- // so a long committed line (e.g. a full shell command) reserved
1018
- // more rows than it painted — leaving stray blank rows behind in
1019
- // the scrollback. Wrapping keeps what's actually drawn in sync
1020
- // with what was reserved.
1184
+ // Measure and render with the same Paragraph/Wrap implementation.
1185
+ // Counting Rust chars here under-reserved rows for wide Unicode,
1186
+ // emoji, ANSI-styled spans, and word-boundary wrapping, clipping the
1187
+ // tail of otherwise complete assistant responses in scrollback.
1021
1188
  ratatui::widgets::Paragraph::new(text)
1022
1189
  .wrap(ratatui::widgets::Wrap { trim: false })
1023
1190
  .render(buf.area, buf);
1024
1191
  });
1025
1192
  }
1026
1193
 
1194
+ fn wrapped_text_height(text: &ratatui::text::Text<'_>, width: u16) -> u16 {
1195
+ ratatui::widgets::Paragraph::new(text.clone())
1196
+ .wrap(ratatui::widgets::Wrap { trim: false })
1197
+ .line_count(width)
1198
+ .max(1)
1199
+ .min(u16::MAX as usize) as u16
1200
+ }
1201
+
1027
1202
  /// Tears down the shared inline `ratatui` terminal (if one is currently
1028
1203
  /// live), so whatever prints next — an approval card, a committed activity
1029
1204
  /// snapshot, the final AI answer — starts from a clean, un-managed cursor
@@ -1033,6 +1208,10 @@ pub(super) fn insert_permanent_lines(status: &mut LiveStatus, lines: &[String])
1033
1208
  /// `render_live_status` call reconstructs it fresh, re-querying the
1034
1209
  /// terminal for where the cursor actually is now.
1035
1210
  pub(super) fn clear_live_status(status: &mut LiveStatus) {
1211
+ if let Some(tui) = &status.tui {
1212
+ tui.clear_status();
1213
+ return;
1214
+ }
1036
1215
  if status.inline_tui.terminal.is_none() {
1037
1216
  clear_working_status();
1038
1217
  return;
@@ -1092,7 +1271,46 @@ pub(super) fn activity_summary_line(
1092
1271
  let web_count = activities.len();
1093
1272
  let shell_count = tasks
1094
1273
  .iter()
1095
- .filter(|t| t.label.starts_with("[run_shell]"))
1274
+ .filter(|t| {
1275
+ t.label.starts_with("[run_shell]")
1276
+ || t.label.starts_with("[shell_output]")
1277
+ || t.label.starts_with("[kill_shell]")
1278
+ })
1279
+ .count();
1280
+ let edit_count = tasks
1281
+ .iter()
1282
+ .filter(|t| {
1283
+ t.label.starts_with("[write_file]")
1284
+ || t.label.starts_with("[apply_patch]")
1285
+ || t.label.starts_with("[note_write]")
1286
+ })
1287
+ .count();
1288
+ let git_count = tasks
1289
+ .iter()
1290
+ .filter(|t| t.label.starts_with("[git_"))
1291
+ .count();
1292
+ let subagent_count = tasks
1293
+ .iter()
1294
+ .filter(|t| {
1295
+ t.label.contains("[dispatch_subagent]")
1296
+ || t.label.contains("Subagent dispatched")
1297
+ || t.label.contains("subagent")
1298
+ })
1299
+ .count();
1300
+ let other_count = tasks
1301
+ .iter()
1302
+ .filter(|t| {
1303
+ !t.label.starts_with("[run_shell]")
1304
+ && !t.label.starts_with("[shell_output]")
1305
+ && !t.label.starts_with("[kill_shell]")
1306
+ && !t.label.starts_with("[write_file]")
1307
+ && !t.label.starts_with("[apply_patch]")
1308
+ && !t.label.starts_with("[note_write]")
1309
+ && !t.label.starts_with("[git_")
1310
+ && !t.label.contains("[dispatch_subagent]")
1311
+ && !t.label.contains("Subagent dispatched")
1312
+ && !t.label.contains("subagent")
1313
+ })
1096
1314
  .count();
1097
1315
 
1098
1316
  let mut parts: Vec<String> = Vec::new();
@@ -1108,6 +1326,12 @@ pub(super) fn activity_summary_line(
1108
1326
  if file_count == 1 { "" } else { "s" }
1109
1327
  ));
1110
1328
  }
1329
+ if edit_count > 0 {
1330
+ parts.push(format!(
1331
+ "editing {edit_count} file{}",
1332
+ if edit_count == 1 { "" } else { "s" }
1333
+ ));
1334
+ }
1111
1335
  if dir_count > 0 {
1112
1336
  parts.push(format!(
1113
1337
  "listing {dir_count} director{}",
@@ -1132,8 +1356,33 @@ pub(super) fn activity_summary_line(
1132
1356
  if shell_count == 1 { "" } else { "s" }
1133
1357
  ));
1134
1358
  }
1359
+ if git_count > 0 {
1360
+ parts.push(format!(
1361
+ "checking repository {git_count} time{}",
1362
+ if git_count == 1 { "" } else { "s" }
1363
+ ));
1364
+ }
1365
+ if subagent_count > 0 {
1366
+ parts.push(format!(
1367
+ "dispatching {subagent_count} subagent{}",
1368
+ if subagent_count == 1 { "" } else { "s" }
1369
+ ));
1370
+ }
1371
+ if other_count > 0 {
1372
+ parts.push(format!(
1373
+ "running {other_count} tool{}",
1374
+ if other_count == 1 { "" } else { "s" }
1375
+ ));
1376
+ }
1135
1377
 
1136
1378
  if parts.is_empty() {
1379
+ let total = tasks.len() + activities.len() + explored.len();
1380
+ if total > 0 {
1381
+ return Some(format!(
1382
+ "Running {total} tool{}…",
1383
+ if total == 1 { "" } else { "s" }
1384
+ ));
1385
+ }
1137
1386
  return None;
1138
1387
  }
1139
1388
  let sentence = parts.join(", ");
@@ -1154,20 +1403,68 @@ pub(super) fn activity_block_lines(
1154
1403
  explored: &[ExploredAction],
1155
1404
  animate: bool,
1156
1405
  tick: usize,
1406
+ active_tool_started: Option<Instant>,
1407
+ // Whether to render stored command-output preview lines.
1408
+ include_output: bool,
1157
1409
  ) -> Vec<String> {
1158
1410
  if tasks.is_empty() && activities.is_empty() && explored.is_empty() {
1159
1411
  return Vec::new();
1160
1412
  }
1161
1413
  let char_str = bullet_char(animate, tick);
1162
- let header_text =
1163
- activity_summary_line(tasks, activities, explored).unwrap_or_else(|| "activity".into());
1414
+ let header_text = activity_summary_line(tasks, activities, explored)
1415
+ .unwrap_or_else(|| "Running tools…".into());
1164
1416
  let mut lines = vec![format!(" {BLUE}{char_str}{RESET} {header_text}")];
1165
- lines.extend(tasks_lines(tasks));
1166
- lines.extend(activities_lines(activities));
1167
- lines.extend(explored_lines(explored));
1417
+
1418
+ // Collect all items with their output lines
1419
+ let mut all_items: Vec<(String, Vec<String>)> = Vec::new();
1420
+ for task in tasks.iter().take(24) {
1421
+ all_items.push((task.label.clone(), task.output.clone()));
1422
+ }
1423
+ for act in activities.iter().take(24) {
1424
+ all_items.push((act.clone(), Vec::new()));
1425
+ }
1426
+ let grouped = grouped_explored_actions(explored);
1427
+ for action in grouped.into_iter().take(24) {
1428
+ all_items.push((action, Vec::new()));
1429
+ }
1430
+
1431
+ let total_items = all_items.len();
1432
+ const SPINNER_FRAMES: &[&str] = &["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
1433
+ let spinner = SPINNER_FRAMES[tick % SPINNER_FRAMES.len()];
1434
+
1435
+ for (idx, (label, output)) in all_items.into_iter().enumerate() {
1436
+ let is_active = animate && active_tool_started.is_some() && idx == total_items - 1;
1437
+ let prefix = if idx == 0 {
1438
+ format!("{DIM} └{RESET}")
1439
+ } else {
1440
+ " ".to_string()
1441
+ };
1442
+
1443
+ if is_active {
1444
+ let elapsed_secs = active_tool_started.unwrap().elapsed().as_secs_f64();
1445
+ let timer_suffix = format!(" ({:.1}s)", elapsed_secs);
1446
+ lines.push(format!(
1447
+ "{prefix} {CYAN}{spinner}{RESET} {WHITE}{label}{RESET}{DIM}{timer_suffix}{RESET}"
1448
+ ));
1449
+ } else {
1450
+ lines.push(format!("{prefix} {MINT}✓{RESET} {WHITE}{label}{RESET}"));
1451
+ }
1452
+
1453
+ if include_output {
1454
+ for out_line in &output {
1455
+ lines.push(format!("{DIM} │ {}{RESET}", out_line));
1456
+ }
1457
+ }
1458
+ }
1459
+
1460
+ let raw_total = tasks.len() + activities.len() + explored.len();
1461
+ if raw_total > 24 {
1462
+ lines.push(format!("{DIM} ... {} more{RESET}", raw_total - 24));
1463
+ }
1168
1464
  lines
1169
1465
  }
1170
1466
 
1467
+ #[allow(dead_code)]
1171
1468
  pub(super) fn explored_lines(actions: &[ExploredAction]) -> Vec<String> {
1172
1469
  if actions.is_empty() {
1173
1470
  return Vec::new();
@@ -1178,8 +1475,12 @@ pub(super) fn explored_lines(actions: &[ExploredAction]) -> Vec<String> {
1178
1475
  .take(24)
1179
1476
  .enumerate()
1180
1477
  .map(|(index, action)| {
1181
- let prefix = if index == 0 { " └" } else { " " };
1182
- format!("{DIM}{prefix} {action}{RESET}")
1478
+ if index == 0 {
1479
+ // └ connector in DIM, tool text in WHITE (plain white)
1480
+ format!("{DIM} └{RESET} {WHITE}{action}{RESET}")
1481
+ } else {
1482
+ format!(" {WHITE}{action}{RESET}")
1483
+ }
1183
1484
  })
1184
1485
  .collect();
1185
1486
  if grouped.len() > 24 {
@@ -1267,10 +1568,13 @@ pub(super) fn skill_names_from_memory_recall(result: &str) -> Vec<String> {
1267
1568
  .collect()
1268
1569
  }
1269
1570
 
1270
- pub(super) fn skill_card(skill_name: &str) -> TaskEntry {
1271
- TaskEntry {
1272
- label: format!("Skill({skill_name})"),
1273
- output: vec!["Successfully loaded skill".to_string()],
1571
+ pub(super) fn skill_read_label(skill_name: &str, result: &str) -> String {
1572
+ if result.starts_with(&format!("[Loaded skill: {skill_name}]\n")) {
1573
+ format!("Loaded skill: {skill_name}")
1574
+ } else if result.starts_with("Error:") || result.starts_with("Blocked") {
1575
+ format!("Failed to read skill: {skill_name}")
1576
+ } else {
1577
+ format!("Read skill preview: {skill_name}")
1274
1578
  }
1275
1579
  }
1276
1580
 
@@ -1325,6 +1629,7 @@ pub(super) fn truncate_line(line: &str, max_chars: usize) -> String {
1325
1629
  format!("{head}…")
1326
1630
  }
1327
1631
 
1632
+ #[allow(dead_code)]
1328
1633
  pub(super) fn activities_lines(activities: &[String]) -> Vec<String> {
1329
1634
  if activities.is_empty() {
1330
1635
  return Vec::new();
@@ -1334,8 +1639,12 @@ pub(super) fn activities_lines(activities: &[String]) -> Vec<String> {
1334
1639
  .take(24)
1335
1640
  .enumerate()
1336
1641
  .map(|(index, act)| {
1337
- let prefix = if index == 0 { " └" } else { " " };
1338
- format!("{DIM}{prefix} {act}{RESET}")
1642
+ if index == 0 {
1643
+ // └ connector in DIM, activity text in WHITE (plain white)
1644
+ format!("{DIM} └{RESET} {WHITE}{act}{RESET}")
1645
+ } else {
1646
+ format!(" {WHITE}{act}{RESET}")
1647
+ }
1339
1648
  })
1340
1649
  .collect();
1341
1650
  if activities.len() > 24 {
@@ -1396,14 +1705,19 @@ pub(super) fn plan_lines(steps: &[String], animate: bool, tick: usize) -> Vec<St
1396
1705
  lines
1397
1706
  }
1398
1707
 
1708
+ #[allow(dead_code)]
1399
1709
  pub(super) fn tasks_lines(tasks: &[TaskEntry]) -> Vec<String> {
1400
1710
  if tasks.is_empty() {
1401
1711
  return Vec::new();
1402
1712
  }
1403
1713
  let mut lines = Vec::new();
1404
1714
  for (index, task) in tasks.iter().take(24).enumerate() {
1405
- let prefix = if index == 0 { " └" } else { " " };
1406
- lines.push(format!("{DIM}{prefix} {}{RESET}", task.label));
1715
+ if index == 0 {
1716
+ // └ connector in DIM, task label in WHITE (plain white)
1717
+ lines.push(format!("{DIM} └{RESET} {WHITE}{}{RESET}", task.label));
1718
+ } else {
1719
+ lines.push(format!(" {WHITE}{}{RESET}", task.label));
1720
+ }
1407
1721
  for out_line in &task.output {
1408
1722
  lines.push(format!("{DIM} │ {}{RESET}", out_line));
1409
1723
  }
@@ -1452,6 +1766,18 @@ mod format_token_count_tests {
1452
1766
  }
1453
1767
  }
1454
1768
 
1769
+ #[cfg(test)]
1770
+ mod scrollback_layout_tests {
1771
+ use super::*;
1772
+
1773
+ #[test]
1774
+ fn scrollback_height_accounts_for_wide_unicode() {
1775
+ let text = ratatui::text::Text::raw("🙂🙂🙂");
1776
+
1777
+ assert_eq!(wrapped_text_height(&text, 4), 2);
1778
+ }
1779
+ }
1780
+
1455
1781
  #[cfg(test)]
1456
1782
  mod project_tokens_tests {
1457
1783
  use super::*;
@@ -1498,3 +1824,74 @@ mod ramp_estimate_tests {
1498
1824
  assert_eq!(ramp_estimate(5000, 60.0), 5000);
1499
1825
  }
1500
1826
  }
1827
+
1828
+ #[cfg(test)]
1829
+ mod context_compaction_status_tests {
1830
+ use super::*;
1831
+ #[test]
1832
+ fn compaction_lifecycle_keeps_its_timer_until_success_or_failure() {
1833
+ let now = Instant::now();
1834
+ let mut status = LiveStatus::default();
1835
+ update_context_compaction(&mut status, "started", None, now);
1836
+ assert_eq!(status.compaction_started, Some(now));
1837
+ assert_eq!(
1838
+ compaction_label(Duration::from_secs(12)),
1839
+ "Compacting context · 12s • Esc to interrupt"
1840
+ );
1841
+ for phase in ["completed", "failed"] {
1842
+ update_context_compaction(&mut status, phase, None, now);
1843
+ assert!(status.compaction_started.is_none());
1844
+ update_context_compaction(&mut status, "started", None, now);
1845
+ }
1846
+ }
1847
+ #[test]
1848
+ fn compaction_gradient_moves_while_timer_and_cancel_hint_stay_plain() {
1849
+ let label = compaction_label(Duration::from_secs(12));
1850
+ let first = apply_wave_effect(&label, 0);
1851
+ let next = apply_wave_effect(&label, 12);
1852
+ assert_ne!(first, next);
1853
+ assert!(strip_ansi_escapes(&first).ends_with(" · 12s • Esc to interrupt"));
1854
+ assert!(strip_ansi_escapes(&next).ends_with(" · 12s • Esc to interrupt"));
1855
+ assert_eq!(
1856
+ first.split_once(" · ").unwrap().1,
1857
+ next.split_once(" · ").unwrap().1
1858
+ );
1859
+ }
1860
+ }
1861
+
1862
+ #[cfg(test)]
1863
+ mod parallel_compaction_tests {
1864
+ use super::*;
1865
+ #[test]
1866
+ fn one_child_finishing_does_not_clear_another_child_timer() {
1867
+ let mut status = LiveStatus::default();
1868
+ let now = Instant::now();
1869
+ update_context_compaction(&mut status, "started", Some("A"), now);
1870
+ update_context_compaction(
1871
+ &mut status,
1872
+ "started",
1873
+ Some("B"),
1874
+ now + Duration::from_secs(2),
1875
+ );
1876
+ update_context_compaction(&mut status, "completed", Some("A"), now);
1877
+ assert_eq!(status.compaction_started, Some(now));
1878
+ update_context_compaction(&mut status, "failed", Some("B"), now);
1879
+ assert!(status.compaction_started.is_none());
1880
+ }
1881
+ }
1882
+
1883
+ #[cfg(test)]
1884
+ mod repeated_name_compaction_tests {
1885
+ use super::*;
1886
+ #[test]
1887
+ fn repeated_child_name_keeps_timer_until_both_finish() {
1888
+ let mut status = LiveStatus::default();
1889
+ let now = Instant::now();
1890
+ update_context_compaction(&mut status, "started", Some("reviewer"), now);
1891
+ update_context_compaction(&mut status, "started", Some("reviewer"), now);
1892
+ update_context_compaction(&mut status, "completed", Some("reviewer"), now);
1893
+ assert_eq!(status.compaction_started, Some(now));
1894
+ update_context_compaction(&mut status, "failed", Some("reviewer"), now);
1895
+ assert!(status.compaction_started.is_none());
1896
+ }
1897
+ }