@pheem49/mint 1.14.0 → 1.15.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.
- package/CLAUDE.md +1 -1
- package/Cargo.lock +291 -11
- package/Cargo.toml +1 -1
- package/README.md +71 -9
- package/ROADMAP.md +30 -0
- package/Release_Note.md +579 -465
- package/crates/mint-cli/Cargo.toml +3 -1
- package/crates/mint-cli/src/agent/approval_prompts.rs +16 -3
- package/crates/mint-cli/src/agent/live_status.rs +367 -118
- package/crates/mint-cli/src/agent/markdown_render.rs +601 -61
- package/crates/mint-cli/src/agent.rs +452 -215
- package/crates/mint-cli/src/commands/agent.rs +16 -21
- package/crates/mint-cli/src/commands/eval.rs +9 -5
- package/crates/mint-cli/src/commands/git.rs +204 -0
- package/crates/mint-cli/src/commands/integrations.rs +28 -0
- package/crates/mint-cli/src/commands/knowledge.rs +1 -0
- package/crates/mint-cli/src/commands/mod.rs +37 -2
- package/crates/mint-cli/src/commands/tailnet_web.rs +235 -0
- package/crates/mint-cli/src/image.rs +26 -3
- package/crates/mint-cli/src/interactive/chat_tui.rs +4742 -0
- package/crates/mint-cli/src/interactive/command_ui.rs +477 -0
- package/crates/mint-cli/src/interactive/format.rs +8 -0
- package/crates/mint-cli/src/interactive/input_box.rs +38 -2
- package/crates/mint-cli/src/interactive/picker.rs +180 -37
- package/crates/mint-cli/src/interactive/resume_picker.rs +686 -0
- package/crates/mint-cli/src/interactive/slash_commands.rs +868 -450
- package/crates/mint-cli/src/interactive/thought_viewer.rs +286 -0
- package/crates/mint-cli/src/interactive.rs +400 -136
- package/crates/mint-cli/src/main.rs +84 -23
- package/crates/mint-cli/src/markdown.rs +12 -5
- package/crates/mint-cli/src/onboard.rs +32 -30
- package/crates/mint-cli/src/plugins_cli.rs +8 -5
- package/crates/mint-cli/src/setup.rs +10 -8
- package/crates/mint-cli/src/terminal_theme.rs +187 -0
- package/crates/mint-cli/src/updater.rs +56 -21
- package/crates/mint-core/src/agent/agent_loop.rs +1 -0
- package/crates/mint-core/src/agent/chat.rs +843 -177
- package/crates/mint-core/src/agent/code_tools.rs +83 -2
- package/crates/mint-core/src/agent/memory.rs +658 -20
- package/crates/mint-core/src/agent/skills.rs +1 -1
- package/crates/mint-core/src/api_server/routes/chat.rs +31 -5
- package/crates/mint-core/src/api_server/routes/linked_folders.rs +118 -3
- package/crates/mint-core/src/api_server/routes/misc.rs +10 -14
- package/crates/mint-core/src/api_server/routes/sessions.rs +44 -0
- package/crates/mint-core/src/api_server.rs +22 -3
- package/crates/mint-core/src/avatar_bridge.rs +2 -0
- package/crates/mint-core/src/browser/cdp.rs +74 -83
- package/crates/mint-core/src/browser/evidence.rs +260 -0
- package/crates/mint-core/src/browser/input.rs +1 -1
- package/crates/mint-core/src/browser/interact.rs +98 -124
- package/crates/mint-core/src/browser/lifecycle.rs +75 -4
- package/crates/mint-core/src/browser/mod.rs +16 -2
- package/crates/mint-core/src/browser/navigate.rs +27 -19
- package/crates/mint-core/src/browser/observe.js +40 -0
- package/crates/mint-core/src/browser/session.rs +1558 -0
- package/crates/mint-core/src/git/branches.rs +348 -0
- package/crates/mint-core/src/git/checkpoint.rs +40 -0
- package/crates/mint-core/src/git/diff.rs +206 -0
- package/crates/mint-core/src/git/mod.rs +10 -2
- package/crates/mint-core/src/integrations/channels.rs +1 -0
- package/crates/mint-core/src/lib.rs +22 -14
- package/crates/mint-core/src/live_sync.rs +28 -9
- package/crates/mint-core/src/media/auto_shorts.rs +1 -0
- package/crates/mint-core/src/media/gemini_live.rs +12 -1
- package/crates/mint-core/src/media/mic_transcribe.rs +1 -0
- package/crates/mint-core/src/media/subtitle.rs +1 -0
- package/crates/mint-core/src/orchestration/context_render.rs +1 -0
- package/crates/mint-core/src/orchestration/memory_skill.rs +6 -1
- package/crates/mint-core/src/orchestration/mod.rs +1024 -271
- package/crates/mint-core/src/orchestration/tools/browser.rs +16 -139
- package/crates/mint-core/src/orchestration/tools/misc.rs +2 -1
- package/crates/mint-core/src/orchestration/tools/web.rs +1 -1
- package/crates/mint-core/src/prompts/agent.rs +58 -29
- package/crates/mint-core/src/prompts/chat.rs +1 -1
- package/crates/mint-core/src/prompts/persona.rs +1 -1
- package/crates/mint-core/src/prompts/tool_catalog.rs +93 -25
- package/crates/mint-core/src/search/linked_folders/index.rs +732 -0
- package/crates/mint-core/src/search/linked_folders.rs +391 -130
- package/crates/mint-core/src/slash/mod.rs +470 -22
- package/crates/mint-core/src/slash/model_fetcher.rs +12 -3
- package/crates/mint-core/src/slash/models.rs +66 -10
- package/crates/mint-core/src/system/config.rs +230 -0
- package/crates/mint-core/src/system/workspace.rs +228 -0
- package/crates/mint-core/src/system.rs +1 -0
- package/crates/mint-core/tests/memory_persistence.rs +39 -0
- package/mint-presentation-demo.mp4 +0 -0
- package/out/web/assets/CliSessionsView-BxUVWEEL.js +1 -0
- package/out/web/assets/CliSessionsView-CBDTXuQv.css +1 -0
- package/out/web/assets/CodeReviewPage-DkMgfcos.css +1 -0
- package/out/web/assets/CodeReviewPage-jKlmLJwT.js +3 -0
- package/out/web/assets/CommandPalette-DnwPA5hy.js +1 -0
- package/out/web/assets/ImageStudioPanel-BR1XFwhR.js +1 -0
- package/out/web/assets/ImageStudioPanel-D0lnhQV5.css +1 -0
- package/out/web/assets/LinkedFoldersView-bi-CWQ-I.js +1 -0
- package/out/web/assets/McpRegistryPicker-D5tEFnyq.js +1 -0
- package/out/web/assets/McpServersView-BGG1dhFM.js +1 -0
- package/out/web/assets/MintDashboard-BIglDEWX.js +20 -0
- package/out/web/assets/MintDashboard-DSMywSgs.css +1 -0
- package/out/web/assets/ModelPanel-CbJ1PFoZ.js +1 -0
- package/out/web/assets/PicturesLibrary-CQP0fLP1.css +1 -0
- package/out/web/assets/PicturesLibrary-DoYN7T1p.js +1 -0
- package/out/web/assets/PluginsView-CU1HWXGk.js +1 -0
- package/out/web/assets/ScheduledTasksView-Bnl36y2N.js +1 -0
- package/out/web/assets/SettingsWindow-DhWLAn9T.css +1 -0
- package/out/web/assets/SettingsWindow-Dw8L01pw.js +2 -0
- package/out/web/assets/SkillsView-cnl8aY5p.js +2 -0
- package/out/web/assets/ToolSurfacePage-CgsQw6l5.css +1 -0
- package/out/web/assets/ToolSurfacePage-Cz2VJAPZ.js +37 -0
- package/out/web/assets/VeoStudioPanel-D4JnERWR.js +4 -0
- package/out/web/assets/VeoStudioPanel-DOj7_WR3.css +1 -0
- package/out/web/assets/WorkspacePanel-CbJ1PFoZ.js +1 -0
- package/out/web/assets/{abnfDiagram-VCTEODGH-DAOBA7ZG.js → abnfDiagram-VCTEODGH-ByMyfanq.js} +1 -1
- package/out/web/assets/{architecture-7GRP2DOG-CgSwQOPQ.js → architecture-7GRP2DOG-CLq2kEOB.js} +1 -1
- package/out/web/assets/{architectureDiagram-5GKGNRK7-CYp3yupP.js → architectureDiagram-5GKGNRK7-6PoVSmNG.js} +1 -1
- package/out/web/assets/{blockDiagram-NRAW4CY4-CyWybDWr.js → blockDiagram-NRAW4CY4-cKw9mAKi.js} +1 -1
- package/out/web/assets/{c4Diagram-UCG6FXSJ-BvY3U709.js → c4Diagram-UCG6FXSJ-Cqy8N5Ge.js} +1 -1
- package/out/web/assets/{chunk-75Z2AOVW-TLXCoZYv.js → chunk-75Z2AOVW-3qybijF5.js} +1 -1
- package/out/web/assets/chunk-CKBBP62Z-cIrXq6jN.js +2 -0
- package/out/web/assets/{chunk-CLGD4ZFX-xjsKtNS7.js → chunk-CLGD4ZFX-CndDQkww.js} +1 -1
- package/out/web/assets/chunk-DSNCTWBM-Cc4NxvgD.js +2 -0
- package/out/web/assets/chunk-DU6HZSFF-drzh6GsU.js +128 -0
- package/out/web/assets/{chunk-F27PBJKO-BFpgF55A.js → chunk-F27PBJKO-FiY35l4v.js} +1 -1
- package/out/web/assets/{chunk-G27WJ6UU-DfF7qW-C.js → chunk-G27WJ6UU-Bjt9Il4A.js} +1 -1
- package/out/web/assets/{chunk-GMAD6QVW-C-558kh_.js → chunk-GMAD6QVW-Bppi3Ctd.js} +1 -1
- package/out/web/assets/{chunk-JQ64N6SF-wJmeGKAw.js → chunk-JQ64N6SF-DGg5kcEV.js} +1 -1
- package/out/web/assets/{chunk-LCL6LL3I-C1wHD8Ie.js → chunk-LCL6LL3I-CCYcjd7p.js} +1 -1
- package/out/web/assets/{chunk-OBVCFTLP-BsRTPBqs.js → chunk-OBVCFTLP-DJeNil5M.js} +1 -1
- package/out/web/assets/{chunk-P2QGCYS3-HZIiM7Ni.js → chunk-P2QGCYS3-lRzkRjBB.js} +1 -1
- package/out/web/assets/{chunk-POPQ4Y6H-Dk9wvVb0.js → chunk-POPQ4Y6H-DouASMBW.js} +1 -1
- package/out/web/assets/{chunk-PWAF6VOD-BmjYLNbq.js → chunk-PWAF6VOD-DMmD1vVf.js} +1 -1
- package/out/web/assets/{chunk-R7TYR2AO-CaHIf-lK.js → chunk-R7TYR2AO-CYLA5UC5.js} +1 -1
- package/out/web/assets/{chunk-RHFEMEQ7-_T561hOU.js → chunk-RHFEMEQ7-BiXwEcUT.js} +1 -1
- package/out/web/assets/{chunk-SVP7TREG-CM-GGYoY.js → chunk-SVP7TREG-BV-zPslF.js} +1 -1
- package/out/web/assets/{chunk-TEH6E4GO-K2A8Vg2C.js → chunk-TEH6E4GO-B2RGEohZ.js} +1 -1
- package/out/web/assets/classDiagram-DTDB5LWJ-Dq0smRWk.js +1 -0
- package/out/web/assets/classDiagram-v2-JRS7N3AN-Dq0smRWk.js +1 -0
- package/out/web/assets/config-C-ZFtipU.css +1 -0
- package/out/web/assets/config-eEDngJXO.js +1 -0
- package/out/web/assets/core-hQ2VA9Ti.js +1 -0
- package/out/web/assets/{cynefin-OW5HDTMX-ClzaB6cc.js → cynefin-OW5HDTMX-Cas4csHM.js} +1 -1
- package/out/web/assets/{cynefinDiagram-5FMLGOSQ-D_JzZBzf.js → cynefinDiagram-5FMLGOSQ-Bbpj66Y3.js} +1 -1
- package/out/web/assets/{dagre-3AP2YEHR-BDuUeMHj.js → dagre-3AP2YEHR-B0-2RDEd.js} +1 -1
- package/out/web/assets/{diagram-S7CK7UJ4-DgtcfE8P.js → diagram-S7CK7UJ4-BdzEjUyH.js} +1 -1
- package/out/web/assets/{diagram-UQ7AKVKN-DTLoEYti.js → diagram-UQ7AKVKN-CJDwyPxF.js} +1 -1
- package/out/web/assets/{diagram-VSXAHHWV-ChGl9pXz.js → diagram-VSXAHHWV-BxaG1r_B.js} +1 -1
- package/out/web/assets/{diagram-VX7I27RA-Be8DWz0S.js → diagram-VX7I27RA-BMmUqm8f.js} +1 -1
- package/out/web/assets/{diagram-Z3DM3KII-BMXjRTOF.js → diagram-Z3DM3KII-CFtd03eE.js} +1 -1
- package/out/web/assets/dist-js-Bf2bhoku.js +1 -0
- package/out/web/assets/{ebnfDiagram-PWID7BFC-8Q9pl1CJ.js → ebnfDiagram-PWID7BFC-DwAen1hO.js} +1 -1
- package/out/web/assets/{erDiagram-SSCWMZ5O-DjH8Lnkn.js → erDiagram-SSCWMZ5O-BCZSHACj.js} +1 -1
- package/out/web/assets/event-ChBVpRy0.js +1 -0
- package/out/web/assets/{eventmodeling-NTZA5JFV-BKUH_DOp.js → eventmodeling-NTZA5JFV-XHStE4E_.js} +1 -1
- package/out/web/assets/fira-code-latin-600-normal-BmSSHshu.woff +0 -0
- package/out/web/assets/fira-code-latin-600-normal-CvYZpPHg.woff2 +0 -0
- package/out/web/assets/fira-code-symbols2-600-normal-Dvq6IQE5.woff +0 -0
- package/out/web/assets/flowDiagram-A5DVABFB-BLg7nEcd.js +1 -0
- package/out/web/assets/{ganttDiagram-EL5Y4UJY-D7fZKJNM.js → ganttDiagram-EL5Y4UJY-BAEu1Cql.js} +1 -1
- package/out/web/assets/{gitGraph-4MIJSDKK-BypWaaBU.js → gitGraph-4MIJSDKK-r32Vyper.js} +1 -1
- package/out/web/assets/{gitGraphDiagram-WWUBYQGX-DWiw_3bq.js → gitGraphDiagram-WWUBYQGX-z9o9qt6V.js} +1 -1
- package/out/web/assets/index-Btsf4L8M.css +1 -0
- package/out/web/assets/index-D8D9MeRA.js +11 -0
- package/out/web/assets/{info-A6RAGUB7-KkAUxT4j.js → info-A6RAGUB7-BvLaC2cw.js} +1 -1
- package/out/web/assets/{infoDiagram-RXCK75RN-BNb6E48y.js → infoDiagram-RXCK75RN-Be6WAXXw.js} +1 -1
- package/out/web/assets/{ishikawaDiagram-5VMMS53U-Caa-JoAK.js → ishikawaDiagram-5VMMS53U-DZjoHJHC.js} +1 -1
- package/out/web/assets/{journeyDiagram-EYS64GPL-DOrmFGz6.js → journeyDiagram-EYS64GPL-CtVkjkGm.js} +1 -1
- package/out/web/assets/{kanban-definition-3QL26DDD-BOWCYcK0.js → kanban-definition-3QL26DDD-CW5FMv1w.js} +1 -1
- package/out/web/assets/lucide-react-Ip3L6Jtn.js +1 -0
- package/out/web/assets/markdown-BSeeUbJH.js +38 -0
- package/out/web/assets/{mermaid-parser.core-BQqjCkS_.js → mermaid-parser.core-aUgCqsZ5.js} +3 -3
- package/out/web/assets/mermaid.core-D57sEiSL.js +11 -0
- package/out/web/assets/{mindmap-definition-FBJOCRG2-jTceWpr0.js → mindmap-definition-FBJOCRG2-DAmSpM3a.js} +1 -1
- package/out/web/assets/modelManager-tMVmJVAW.js +2 -0
- package/out/web/assets/oauthManager-9jxicDWJ.js +1 -0
- package/out/web/assets/{packet-AYTQ26CC-CJ5RXgol.js → packet-AYTQ26CC-DJ27iIoJ.js} +1 -1
- package/out/web/assets/{pegDiagram-XKGWAZYB-y8wMtd0y.js → pegDiagram-XKGWAZYB-Bj3ii-gS.js} +1 -1
- package/out/web/assets/{pie-WAS4IAKB-72fTBbl4.js → pie-WAS4IAKB-DG5mCUbc.js} +1 -1
- package/out/web/assets/{pieDiagram-E7YTZNPT-e0O02qWq.js → pieDiagram-E7YTZNPT-BrJLPnDA.js} +1 -1
- package/out/web/assets/prompt-latin-700-normal-BfbftDSq.woff +0 -0
- package/out/web/assets/prompt-latin-700-normal-I2gc831J.woff2 +0 -0
- package/out/web/assets/prompt-thai-700-normal-Cb5ilR-c.woff +0 -0
- package/out/web/assets/prompt-thai-700-normal-Cg4aQ0Nn.woff2 +0 -0
- package/out/web/assets/{quadrantDiagram-AXDQQJYC-C9phYLom.js → quadrantDiagram-AXDQQJYC-3x_TmF3j.js} +1 -1
- package/out/web/assets/{radar-RG4KPBEZ-BAU2hzUT.js → radar-RG4KPBEZ-CrXaTTuV.js} +1 -1
- package/out/web/assets/{railroad-74A4TZTK-B0bCAVHC.js → railroad-74A4TZTK-BpqMH9BE.js} +1 -1
- package/out/web/assets/{railroad-abnf-HS5TGJTU-WhxO7ndE.js → railroad-abnf-HS5TGJTU-DHI0uLlV.js} +1 -1
- package/out/web/assets/{railroad-ebnf-LZEXJU2U-BkA0m7-F.js → railroad-ebnf-LZEXJU2U-CQCb316y.js} +1 -1
- package/out/web/assets/{railroad-peg-WCYAUIDC-BsVpHIaj.js → railroad-peg-WCYAUIDC-Ph1giKkH.js} +1 -1
- package/out/web/assets/{railroadDiagram-O6MQD6OU-CyOLN65Q.js → railroadDiagram-O6MQD6OU-BRq6wIxx.js} +1 -1
- package/out/web/assets/{requirementDiagram-EFPCY7ZU-DQXJ7Dud.js → requirementDiagram-EFPCY7ZU-DIJj6G1g.js} +1 -1
- package/out/web/assets/{sankeyDiagram-P5KCCOFB-B2XLyIIc.js → sankeyDiagram-P5KCCOFB-DZajSryq.js} +1 -1
- package/out/web/assets/{sequenceDiagram-WJ2MYXX4-DGQY4wwm.js → sequenceDiagram-WJ2MYXX4-DxJCWyZS.js} +1 -1
- package/out/web/assets/{stateDiagram-HBIQ2CUA-DHZpDwNy.js → stateDiagram-HBIQ2CUA-k6voi8Vf.js} +1 -1
- package/out/web/assets/stateDiagram-v2-4QOOHH4V-DeEc0lGZ.js +1 -0
- package/out/web/assets/studio-B1mk40wD.js +1 -0
- package/out/web/assets/{swimlanes-XN3QIQJK-Bn_jcZz-.js → swimlanes-XN3QIQJK-igYhTjXB.js} +1 -1
- package/out/web/assets/swimlanesDiagram-VK2B7HYN-DpVik9v7.js +8 -0
- package/out/web/assets/{timeline-definition-24CTP7MA-CdTERuJZ.js → timeline-definition-24CTP7MA-Dke2p93p.js} +1 -1
- package/out/web/assets/{treeView-Q6P3EWNA-TKBKQG6R.js → treeView-Q6P3EWNA-hNELnXUs.js} +1 -1
- package/out/web/assets/{treemap-WGGIJYW6-BP7gQoiC.js → treemap-WGGIJYW6-D2B6wDsx.js} +1 -1
- package/out/web/assets/{vennDiagram-4TSXK5OY-BHUmxDuK.js → vennDiagram-4TSXK5OY-CDkW7rUP.js} +1 -1
- package/out/web/assets/{wardley-WFR3VGLG-9bct5AAN.js → wardley-WFR3VGLG-BECdXETi.js} +1 -1
- package/out/web/assets/{wardleyDiagram-VM6X3IG4-BfJwDDso.js → wardleyDiagram-VM6X3IG4-B5BycpQ4.js} +1 -1
- package/out/web/assets/window-5qYXKoW7.js +1 -0
- package/out/web/assets/{xychartDiagram-S5SC5T6Z-Dqd9Mvpo.js → xychartDiagram-S5SC5T6Z-CtJS3b--.js} +1 -1
- package/out/web/index-web.html +3 -4
- package/out/web/index.html +3 -4
- package/package.json +17 -6
- package/skills-lock.json +36 -0
- package/slash-commands.json +9 -0
- package/src/renderer/index-web.html +1 -0
- package/src/renderer/index.html +1 -0
- package/src/renderer/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/src/renderer/shared/agentProgress.ts +168 -3
- package/src/renderer/shared/components/AgentActivityDrawer.tsx +6 -6
- package/src/renderer/shared/components/AgentActivityTable.tsx +192 -132
- package/src/renderer/shared/components/ArtifactPreviewPanel.tsx +4 -4
- package/src/renderer/shared/components/AuthGate.tsx +4 -9
- package/src/renderer/shared/components/CalculationCard.tsx +41 -15
- package/src/renderer/shared/components/ChatMessageItem.tsx +20 -21
- package/src/renderer/shared/components/ChatPanel.tsx +432 -193
- package/src/renderer/shared/components/ChunkErrorBoundary.tsx +198 -49
- package/src/renderer/shared/components/CliSessionsView.tsx +495 -0
- package/src/renderer/shared/components/CodeReviewPage.tsx +565 -0
- package/src/renderer/shared/components/CommandPalette.tsx +8 -8
- package/src/renderer/shared/components/DashboardSidebar.tsx +862 -125
- package/src/renderer/shared/components/DesktopTitlebar.tsx +345 -0
- package/src/renderer/shared/components/DiffReviewModal.tsx +3 -3
- package/src/renderer/shared/components/GitBranchSelector.tsx +442 -0
- package/src/renderer/shared/components/ImageGenCard.tsx +31 -12
- package/src/renderer/shared/components/ImageSearchCard.tsx +59 -38
- package/src/renderer/shared/components/ImageStudioPanel.tsx +40 -41
- package/src/renderer/shared/components/InlineThoughtNote.tsx +27 -0
- package/src/renderer/shared/components/LinkedFoldersView.tsx +365 -46
- package/src/renderer/shared/components/McpRegistryPicker.tsx +1 -1
- package/src/renderer/shared/components/McpServersView.tsx +24 -22
- package/src/renderer/shared/components/MermaidCard.tsx +14 -8
- package/src/renderer/shared/components/MintDashboard.tsx +1141 -508
- package/src/renderer/shared/components/ModelSelectorPopover.tsx +535 -214
- package/src/renderer/shared/components/PlanChecklistWidget.tsx +1 -1
- package/src/renderer/shared/components/PluginsView.tsx +1 -1
- package/src/renderer/shared/components/RewindModal.tsx +6 -6
- package/src/renderer/shared/components/RunSummaryDashboard.tsx +3 -3
- package/src/renderer/shared/components/ScheduledTasksView.tsx +4 -4
- package/src/renderer/shared/components/ScreenCaptureDialog.css +221 -0
- package/src/renderer/shared/components/ScreenCaptureDialog.tsx +199 -0
- package/src/renderer/shared/components/Settings/AgentsTab.tsx +34 -39
- package/src/renderer/shared/components/Settings/AudioTab.tsx +8 -8
- package/src/renderer/shared/components/Settings/AutomationTab.tsx +8 -58
- package/src/renderer/shared/components/Settings/AvatarCropModal.tsx +368 -0
- package/src/renderer/shared/components/Settings/GeneralTab.tsx +335 -81
- package/src/renderer/shared/components/Settings/MemoryTab.tsx +3 -3
- package/src/renderer/shared/components/Settings/PluginsTab.tsx +20 -12
- package/src/renderer/shared/components/Settings/ProfileTab.tsx +29 -5
- package/src/renderer/shared/components/Settings/SearchableModelCombobox.tsx +411 -0
- package/src/renderer/shared/components/Settings/ThemeTab.tsx +401 -158
- package/src/renderer/shared/components/SkillsView.tsx +41 -16
- package/src/renderer/shared/components/SlashSuggestions.tsx +3 -2
- package/src/renderer/shared/components/SourcesBlock.tsx +44 -32
- package/src/renderer/shared/components/StockCard.tsx +58 -24
- package/src/renderer/shared/components/SubagentDagView.tsx +4 -4
- package/src/renderer/shared/components/TerminalDock.tsx +241 -0
- package/src/renderer/shared/components/ThinkingBlock.tsx +14 -8
- package/src/renderer/shared/components/ToolSurfacePage.tsx +520 -0
- package/src/renderer/shared/components/UiFeatureCard.tsx +267 -0
- package/src/renderer/shared/components/UiGridCard.tsx +188 -0
- package/src/renderer/shared/components/UiMockupWidget.tsx +149 -0
- package/src/renderer/shared/components/VeoStudioPanel.tsx +13 -29
- package/src/renderer/shared/components/WeatherCard.tsx +3 -3
- package/src/renderer/shared/components/WorkspaceContextPopover.tsx +133 -0
- package/src/renderer/shared/components/WorkspaceSelector.tsx +149 -0
- package/src/renderer/shared/constants/config.ts +28 -3
- package/src/renderer/shared/constants/models.ts +189 -2
- package/src/renderer/shared/conversation/coordinator.test.ts +27 -0
- package/src/renderer/shared/conversation/coordinator.ts +82 -0
- package/src/renderer/shared/conversation/syncView.test.ts +46 -0
- package/src/renderer/shared/conversation/syncView.ts +43 -0
- package/src/renderer/shared/conversation/useConversationCoordinator.ts +106 -0
- package/src/renderer/shared/css/agent-observability.css +35 -3
- package/src/renderer/shared/css/auth-gate.css +2 -2
- package/src/renderer/shared/css/chat-ui-cards.css +495 -0
- package/src/renderer/shared/css/code-review.css +823 -0
- package/src/renderer/shared/css/code-sessions.css +452 -0
- package/src/renderer/shared/css/file-changes.css +132 -0
- package/src/renderer/shared/css/image-studio.css +16 -16
- package/src/renderer/shared/css/management-views.css +277 -27
- package/src/renderer/shared/css/model-selector.css +442 -42
- package/src/renderer/shared/css/settings/agents.css +1 -1
- package/src/renderer/shared/css/settings/automation.css +13 -13
- package/src/renderer/shared/css/settings/general.css +526 -5
- package/src/renderer/shared/css/settings/plugins.css +5 -5
- package/src/renderer/shared/css/settings/profile.css +331 -1
- package/src/renderer/shared/css/settings/theme.css +651 -3
- package/src/renderer/shared/css/veo-studio.css +16 -15
- package/src/renderer/shared/fonts.ts +19 -37
- package/src/renderer/shared/platform.test.ts +34 -0
- package/src/renderer/shared/platform.ts +173 -6
- package/src/renderer/shared/theme/themeManager.ts +144 -0
- package/src/renderer/shared/theme/themes.ts +116 -0
- package/src/renderer/shared/theme/tokens.css +318 -0
- package/src/renderer/shared/types.ts +82 -1
- package/src/renderer/shared/utils/agentActivity.ts +126 -5
- package/src/renderer/shared/utils/cardJson.test.ts +35 -0
- package/src/renderer/shared/utils/cardJson.ts +30 -0
- package/src/renderer/shared/utils/fileIcons.ts +1 -1
- package/src/renderer/shared/utils/markdown.tsx +329 -80
- package/src/renderer/shared/utils/markdown.webImages.test.tsx +40 -0
- package/src/renderer/shared/utils/providers.ts +8 -3
- package/src/renderer/shared/utils/screenCapture.ts +43 -0
- package/src/renderer/shared/utils/slashCommandProcessor.ts +4 -1
- package/src/renderer/shared/utils/ui.ts +4 -64
- package/src/renderer/src/App.tsx +7 -0
- package/src/renderer/src/components/BrowserShell.tsx +108 -0
- package/src/renderer/src/components/BrowserSuggestionsPopup.tsx +91 -0
- package/src/renderer/src/components/Live2DStage.tsx +32 -9
- package/src/renderer/src/components/MintDashboard.tsx +4 -2
- package/src/renderer/src/components/ModelPanel.tsx +2 -1
- package/src/renderer/src/components/PicturesLibrary.tsx +3 -2
- package/src/renderer/src/components/ScreenPicker.css +190 -0
- package/src/renderer/src/components/ScreenPicker.tsx +185 -540
- package/src/renderer/src/components/SettingsWindow.tsx +40 -2
- package/src/renderer/src/components/SpotlightWindow.tsx +1 -0
- package/src/renderer/src/components/WidgetWindow.tsx +27 -3
- package/src/renderer/src/components/WorkspacePanel.tsx +61 -43
- package/src/renderer/src/components/browser-shell.css +185 -0
- package/src/renderer/src/components/browser-suggestions-popup.css +55 -0
- package/src/renderer/src/components/liveTranslateFrames.test.ts +23 -0
- package/src/renderer/src/components/liveTranslateFrames.ts +34 -0
- package/src/renderer/src/css/app-shell.css +394 -113
- package/src/renderer/src/css/chat.css +1926 -380
- package/src/renderer/src/css/dashboard.css +11 -0
- package/src/renderer/src/css/desktop-tools.css +730 -0
- package/src/renderer/src/css/misc.css +19 -22
- package/src/renderer/src/css/model.css +49 -48
- package/src/renderer/src/css/pictures.css +144 -136
- package/src/renderer/src/css/settings/base.css +357 -123
- package/src/renderer/src/css/sidebar-workspace.css +930 -291
- package/src/renderer/src/css/spotlight.css +15 -26
- package/src/renderer/src/css/tool-surfaces.css +728 -0
- package/src/renderer/src/css/widget.css +35 -7
- package/src/renderer/src/env.d.ts +3 -0
- package/src/renderer/src/hooks/useProviderModels.ts +2 -9
- package/src/renderer/src/index.css +53 -46
- package/src/renderer/src/main.tsx +11 -0
- package/src/renderer/src/tauri.ts +201 -30
- package/src/renderer/src-web/App.tsx +40 -0
- package/src/renderer/src-web/components/MintDashboard.tsx +4 -2
- package/src/renderer/src-web/components/PicturesLibrary.tsx +3 -2
- package/src/renderer/src-web/components/PullToRefresh.tsx +107 -0
- package/src/renderer/src-web/components/SettingsWindow.tsx +28 -2
- package/src/renderer/src-web/css/app-shell.css +174 -96
- package/src/renderer/src-web/css/chat.css +1519 -316
- package/src/renderer/src-web/css/dashboard.css +11 -0
- package/src/renderer/src-web/css/misc.css +17 -17
- package/src/renderer/src-web/css/mobile.css +678 -0
- package/src/renderer/src-web/css/model.css +46 -46
- package/src/renderer/src-web/css/pictures.css +81 -34
- package/src/renderer/src-web/css/pull-to-refresh.css +54 -0
- package/src/renderer/src-web/css/settings/base.css +316 -100
- package/src/renderer/src-web/css/settings/mobile.css +320 -0
- package/src/renderer/src-web/css/sidebar-workspace.css +545 -100
- package/src/renderer/src-web/css/spotlight.css +9 -26
- package/src/renderer/src-web/css/widget.css +0 -5
- package/src/renderer/src-web/env.d.ts +3 -0
- package/src/renderer/src-web/hooks/useProviderModels.ts +2 -9
- package/src/renderer/src-web/index.css +53 -96
- package/src/renderer/src-web/main.tsx +11 -0
- package/src/renderer/src-web/tauri.ts +200 -30
- package/vite.config.web.ts +1 -0
- package/out/web/assets/MintDashboard-DWSqucgb.js +0 -57
- package/out/web/assets/SettingsWindow-DFM2aj5B.js +0 -2
- package/out/web/assets/chunk-CKBBP62Z-nr1uKPj9.js +0 -2
- package/out/web/assets/chunk-DSNCTWBM-Dtj8Rqsd.js +0 -2
- package/out/web/assets/chunk-DU6HZSFF-2tv5K1wT.js +0 -128
- package/out/web/assets/classDiagram-DTDB5LWJ-BdSFArgw.js +0 -1
- package/out/web/assets/classDiagram-v2-JRS7N3AN-BdSFArgw.js +0 -1
- package/out/web/assets/core-DImejIUG.js +0 -1
- package/out/web/assets/dist-js-BxuKEVWM.js +0 -1
- package/out/web/assets/event-BNl8Zahw.js +0 -1
- package/out/web/assets/fira-code-cyrillic-400-normal-UC0NFL4U.woff2 +0 -0
- package/out/web/assets/fira-code-cyrillic-400-normal-dc1q3VD7.woff +0 -0
- package/out/web/assets/fira-code-cyrillic-500-normal-Bu7VCL72.woff +0 -0
- package/out/web/assets/fira-code-cyrillic-500-normal-CX35h3Mg.woff2 +0 -0
- package/out/web/assets/fira-code-cyrillic-ext-400-normal-Buh61xzB.woff +0 -0
- package/out/web/assets/fira-code-cyrillic-ext-400-normal-txZ9Fk_1.woff2 +0 -0
- package/out/web/assets/fira-code-cyrillic-ext-500-normal-CYRspHgm.woff2 +0 -0
- package/out/web/assets/fira-code-cyrillic-ext-500-normal-CwtLmSFD.woff +0 -0
- package/out/web/assets/fira-code-greek-400-normal-B2Gh_Y8s.woff2 +0 -0
- package/out/web/assets/fira-code-greek-400-normal-DobhaxvF.woff +0 -0
- package/out/web/assets/fira-code-greek-500-normal-CVO0IRNt.woff2 +0 -0
- package/out/web/assets/fira-code-greek-500-normal-DEXIWiey.woff +0 -0
- package/out/web/assets/fira-code-greek-ext-400-normal-DR7mBgIM.woff2 +0 -0
- package/out/web/assets/fira-code-greek-ext-400-normal-DSPUheWg.woff +0 -0
- package/out/web/assets/fira-code-greek-ext-500-normal-BO3iq16n.woff2 +0 -0
- package/out/web/assets/fira-code-greek-ext-500-normal-CMiNLXER.woff +0 -0
- package/out/web/assets/fira-code-latin-ext-400-normal-DG73JjOf.woff +0 -0
- package/out/web/assets/fira-code-latin-ext-400-normal-KSMg0QLl.woff2 +0 -0
- package/out/web/assets/fira-code-latin-ext-500-normal-CaANqXjI.woff +0 -0
- package/out/web/assets/fira-code-latin-ext-500-normal-Crd9e_oI.woff2 +0 -0
- package/out/web/assets/flowDiagram-A5DVABFB-BpnHUSU7.js +0 -1
- package/out/web/assets/index-CVRg270z.css +0 -1
- package/out/web/assets/index-DxZQr9yD.js +0 -10
- package/out/web/assets/inter-cyrillic-400-normal-HOLc17fK.woff +0 -0
- package/out/web/assets/inter-cyrillic-400-normal-obahsSVq.woff2 +0 -0
- package/out/web/assets/inter-cyrillic-500-normal-BasfLYem.woff2 +0 -0
- package/out/web/assets/inter-cyrillic-500-normal-CxZf_p3X.woff +0 -0
- package/out/web/assets/inter-cyrillic-600-normal-4D_pXhcN.woff +0 -0
- package/out/web/assets/inter-cyrillic-600-normal-CWCymEST.woff2 +0 -0
- package/out/web/assets/inter-cyrillic-ext-400-normal-BQZuk6qB.woff2 +0 -0
- package/out/web/assets/inter-cyrillic-ext-400-normal-DQukG94-.woff +0 -0
- package/out/web/assets/inter-cyrillic-ext-500-normal-B0yAr1jD.woff2 +0 -0
- package/out/web/assets/inter-cyrillic-ext-500-normal-BmqWE9Dz.woff +0 -0
- package/out/web/assets/inter-cyrillic-ext-600-normal-Bcila6Z-.woff +0 -0
- package/out/web/assets/inter-cyrillic-ext-600-normal-Dfes3d0z.woff2 +0 -0
- package/out/web/assets/inter-greek-400-normal-B4URO6DV.woff2 +0 -0
- package/out/web/assets/inter-greek-400-normal-q2sYcFCs.woff +0 -0
- package/out/web/assets/inter-greek-500-normal-BIZE56-Y.woff2 +0 -0
- package/out/web/assets/inter-greek-500-normal-Xzm54t5V.woff +0 -0
- package/out/web/assets/inter-greek-600-normal-BZpKdvQh.woff +0 -0
- package/out/web/assets/inter-greek-600-normal-plRanbMR.woff2 +0 -0
- package/out/web/assets/inter-greek-ext-400-normal-DGGRlc-M.woff2 +0 -0
- package/out/web/assets/inter-greek-ext-400-normal-KugGGMne.woff +0 -0
- package/out/web/assets/inter-greek-ext-500-normal-2j5mBUwD.woff +0 -0
- package/out/web/assets/inter-greek-ext-500-normal-C4iEst2y.woff2 +0 -0
- package/out/web/assets/inter-greek-ext-600-normal-B8X0CLgF.woff +0 -0
- package/out/web/assets/inter-greek-ext-600-normal-DRtmH8MT.woff2 +0 -0
- package/out/web/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
- package/out/web/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
- package/out/web/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
- package/out/web/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
- package/out/web/assets/inter-latin-600-normal-CiBQ2DWP.woff +0 -0
- package/out/web/assets/inter-latin-600-normal-LgqL8muc.woff2 +0 -0
- package/out/web/assets/inter-latin-ext-400-normal-77YHD8bZ.woff +0 -0
- package/out/web/assets/inter-latin-ext-400-normal-C1nco2VV.woff2 +0 -0
- package/out/web/assets/inter-latin-ext-500-normal-BxGbmqWO.woff +0 -0
- package/out/web/assets/inter-latin-ext-500-normal-CV4jyFjo.woff2 +0 -0
- package/out/web/assets/inter-latin-ext-600-normal-CIVaiw4L.woff +0 -0
- package/out/web/assets/inter-latin-ext-600-normal-D2bJ5OIk.woff2 +0 -0
- package/out/web/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff +0 -0
- package/out/web/assets/inter-vietnamese-400-normal-DMkecbls.woff2 +0 -0
- package/out/web/assets/inter-vietnamese-500-normal-DOriooB6.woff2 +0 -0
- package/out/web/assets/inter-vietnamese-500-normal-mJboJaSs.woff +0 -0
- package/out/web/assets/inter-vietnamese-600-normal-BuLX-rYi.woff +0 -0
- package/out/web/assets/inter-vietnamese-600-normal-Cc8MFFhd.woff2 +0 -0
- package/out/web/assets/kanit-latin-400-normal-BUlsQXH3.woff2 +0 -0
- package/out/web/assets/kanit-latin-400-normal-HVZWrfev.woff +0 -0
- package/out/web/assets/kanit-latin-500-normal-CX_4XcKi.woff2 +0 -0
- package/out/web/assets/kanit-latin-500-normal-qBha86O4.woff +0 -0
- package/out/web/assets/kanit-latin-600-normal-0lS78yAd.woff2 +0 -0
- package/out/web/assets/kanit-latin-600-normal-DlacpeTg.woff +0 -0
- package/out/web/assets/kanit-latin-ext-400-normal-B2xPf58u.woff2 +0 -0
- package/out/web/assets/kanit-latin-ext-400-normal-CHvNKHHD.woff +0 -0
- package/out/web/assets/kanit-latin-ext-500-normal-BFdWrG22.woff +0 -0
- package/out/web/assets/kanit-latin-ext-500-normal-EHs3suD6.woff2 +0 -0
- package/out/web/assets/kanit-latin-ext-600-normal-BBDzoLaj.woff +0 -0
- package/out/web/assets/kanit-latin-ext-600-normal-DAX3wtuq.woff2 +0 -0
- package/out/web/assets/kanit-thai-400-normal-D2nliXKa.woff +0 -0
- package/out/web/assets/kanit-thai-400-normal-D80Pglro.woff2 +0 -0
- package/out/web/assets/kanit-thai-500-normal-BB8gri04.woff2 +0 -0
- package/out/web/assets/kanit-thai-500-normal-Iy_w0a80.woff +0 -0
- package/out/web/assets/kanit-thai-600-normal-Bj6oiaDc.woff2 +0 -0
- package/out/web/assets/kanit-thai-600-normal-wbTlPO7I.woff +0 -0
- package/out/web/assets/kanit-vietnamese-400-normal-DRI8YWyb.woff2 +0 -0
- package/out/web/assets/kanit-vietnamese-400-normal-dow6Bnto.woff +0 -0
- package/out/web/assets/kanit-vietnamese-500-normal-BzTrpCM3.woff2 +0 -0
- package/out/web/assets/kanit-vietnamese-500-normal-T1ndT7MF.woff +0 -0
- package/out/web/assets/kanit-vietnamese-600-normal-Bi7fw3I4.woff2 +0 -0
- package/out/web/assets/kanit-vietnamese-600-normal-DXCRAa_8.woff +0 -0
- package/out/web/assets/mali-latin-400-normal-ByPcZ8Gx.woff +0 -0
- package/out/web/assets/mali-latin-400-normal-Cuc6Ykl8.woff2 +0 -0
- package/out/web/assets/mali-latin-500-normal-B01M-LP6.woff2 +0 -0
- package/out/web/assets/mali-latin-500-normal-Thgqw_9f.woff +0 -0
- package/out/web/assets/mali-latin-600-normal-B9TiUNpT.woff2 +0 -0
- package/out/web/assets/mali-latin-600-normal-CycpQCwU.woff +0 -0
- package/out/web/assets/mali-latin-ext-400-normal-DUVdJQO4.woff +0 -0
- package/out/web/assets/mali-latin-ext-400-normal-iCG1ALuJ.woff2 +0 -0
- package/out/web/assets/mali-latin-ext-500-normal-BfJE4UMZ.woff2 +0 -0
- package/out/web/assets/mali-latin-ext-500-normal-FoLEyOKS.woff +0 -0
- package/out/web/assets/mali-latin-ext-600-normal-BTvIfiCp.woff2 +0 -0
- package/out/web/assets/mali-latin-ext-600-normal-DAQcLVmD.woff +0 -0
- package/out/web/assets/mali-thai-400-normal-BO7A3bLZ.woff2 +0 -0
- package/out/web/assets/mali-thai-400-normal-CX9VrjTn.woff +0 -0
- package/out/web/assets/mali-thai-500-normal-Cph2qha9.woff +0 -0
- package/out/web/assets/mali-thai-500-normal-D89rMT77.woff2 +0 -0
- package/out/web/assets/mali-thai-600-normal-DPgXT6rZ.woff +0 -0
- package/out/web/assets/mali-thai-600-normal-DlTx3OsB.woff2 +0 -0
- package/out/web/assets/mali-vietnamese-400-normal-DUKxzXaz.woff2 +0 -0
- package/out/web/assets/mali-vietnamese-400-normal-gFDLvsOK.woff +0 -0
- package/out/web/assets/mali-vietnamese-500-normal--OY07q2c.woff +0 -0
- package/out/web/assets/mali-vietnamese-500-normal-KDrNRwfW.woff2 +0 -0
- package/out/web/assets/mali-vietnamese-600-normal-CtTMUU7T.woff +0 -0
- package/out/web/assets/mali-vietnamese-600-normal-DFlJr66u.woff2 +0 -0
- package/out/web/assets/mermaid.core-DYGGlqOf.js +0 -11
- package/out/web/assets/mitr-latin-400-normal-Cb18vPmZ.woff +0 -0
- package/out/web/assets/mitr-latin-400-normal-CwbkLQ4a.woff2 +0 -0
- package/out/web/assets/mitr-latin-500-normal-B4JOhzAi.woff +0 -0
- package/out/web/assets/mitr-latin-500-normal-BmClOow2.woff2 +0 -0
- package/out/web/assets/mitr-latin-ext-400-normal-B3CPG7Yo.woff2 +0 -0
- package/out/web/assets/mitr-latin-ext-400-normal-DTx65hiC.woff +0 -0
- package/out/web/assets/mitr-latin-ext-500-normal-B_Diajz1.woff2 +0 -0
- package/out/web/assets/mitr-latin-ext-500-normal-DYOsVaVT.woff +0 -0
- package/out/web/assets/mitr-thai-400-normal-CS4Do6aM.woff +0 -0
- package/out/web/assets/mitr-thai-400-normal-CXWaAdVj.woff2 +0 -0
- package/out/web/assets/mitr-thai-500-normal-CVdBrEVH.woff2 +0 -0
- package/out/web/assets/mitr-thai-500-normal-CW_B5D-i.woff +0 -0
- package/out/web/assets/mitr-vietnamese-400-normal-CPMGWyoC.woff2 +0 -0
- package/out/web/assets/mitr-vietnamese-400-normal-Z6XhIpdY.woff +0 -0
- package/out/web/assets/mitr-vietnamese-500-normal-CDrv0585.woff2 +0 -0
- package/out/web/assets/mitr-vietnamese-500-normal-CMQWyq-Z.woff +0 -0
- package/out/web/assets/noto-sans-thai-latin-400-normal-BaWBrqFP.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-latin-400-normal-Dh_FJjOB.woff +0 -0
- package/out/web/assets/noto-sans-thai-latin-500-normal-BC04jsmm.woff +0 -0
- package/out/web/assets/noto-sans-thai-latin-500-normal-fyBKClF1.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-latin-600-normal-CemcjnVR.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-latin-600-normal-Dbc-dNO6.woff +0 -0
- package/out/web/assets/noto-sans-thai-latin-ext-400-normal-B-1H0YkH.woff +0 -0
- package/out/web/assets/noto-sans-thai-latin-ext-400-normal-nBP59pVw.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-latin-ext-500-normal-B-ZWb8SZ.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-latin-ext-500-normal-CrEHO7C8.woff +0 -0
- package/out/web/assets/noto-sans-thai-latin-ext-600-normal-PkMZo0xa.woff +0 -0
- package/out/web/assets/noto-sans-thai-latin-ext-600-normal-maHHBsez.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-thai-400-normal-BEOmKAI1.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-thai-400-normal-Dxb_unuU.woff +0 -0
- package/out/web/assets/noto-sans-thai-thai-500-normal-BOGXO2tw.woff +0 -0
- package/out/web/assets/noto-sans-thai-thai-500-normal-CU_X86GQ.woff2 +0 -0
- package/out/web/assets/noto-sans-thai-thai-600-normal-CmgB8_JK.woff +0 -0
- package/out/web/assets/noto-sans-thai-thai-600-normal-Cs73sVjl.woff2 +0 -0
- package/out/web/assets/oauthManager-DNiwM8Rn.js +0 -2
- package/out/web/assets/oauthManager-DuamS1Av.css +0 -1
- package/out/web/assets/outfit-latin-300-normal-AWLfuCom.woff +0 -0
- package/out/web/assets/outfit-latin-300-normal-CRYTwY6m.woff2 +0 -0
- package/out/web/assets/outfit-latin-400-normal-BGsTXAXT.woff2 +0 -0
- package/out/web/assets/outfit-latin-400-normal-DMwTpYkH.woff +0 -0
- package/out/web/assets/outfit-latin-500-normal-ClnHRwRh.woff +0 -0
- package/out/web/assets/outfit-latin-500-normal-DKnIMDSk.woff2 +0 -0
- package/out/web/assets/outfit-latin-600-normal-B7SfZ07L.woff2 +0 -0
- package/out/web/assets/outfit-latin-600-normal-BEfTtDA7.woff +0 -0
- package/out/web/assets/outfit-latin-ext-300-normal-BYS6Dpvq.woff2 +0 -0
- package/out/web/assets/outfit-latin-ext-300-normal-CMIMshEd.woff +0 -0
- package/out/web/assets/outfit-latin-ext-400-normal-5tcqmc2S.woff2 +0 -0
- package/out/web/assets/outfit-latin-ext-400-normal-DHm7mdGe.woff +0 -0
- package/out/web/assets/outfit-latin-ext-500-normal-DrCvqoFD.woff +0 -0
- package/out/web/assets/outfit-latin-ext-500-normal-zeox_O30.woff2 +0 -0
- package/out/web/assets/outfit-latin-ext-600-normal-B85nYjL1.woff2 +0 -0
- package/out/web/assets/outfit-latin-ext-600-normal-CWJcPgd7.woff +0 -0
- package/out/web/assets/preload-helper-D0uHx7FM.js +0 -1
- package/out/web/assets/prompt-latin-ext-400-normal-1pHbN9uy.woff +0 -0
- package/out/web/assets/prompt-latin-ext-400-normal-DdSafGZ9.woff2 +0 -0
- package/out/web/assets/prompt-latin-ext-500-normal--EZ1um7s.woff2 +0 -0
- package/out/web/assets/prompt-latin-ext-500-normal-D-lI1EVT.woff +0 -0
- package/out/web/assets/prompt-latin-ext-600-normal-C8b50x67.woff +0 -0
- package/out/web/assets/prompt-latin-ext-600-normal-Cg9L7iJU.woff2 +0 -0
- package/out/web/assets/prompt-vietnamese-400-normal-BCPzsgPT.woff2 +0 -0
- package/out/web/assets/prompt-vietnamese-400-normal-D4pLyeNK.woff +0 -0
- package/out/web/assets/prompt-vietnamese-500-normal-DmzxmPwa.woff2 +0 -0
- package/out/web/assets/prompt-vietnamese-500-normal-w_sS_j2F.woff +0 -0
- package/out/web/assets/prompt-vietnamese-600-normal-1dyohtNh.woff +0 -0
- package/out/web/assets/prompt-vietnamese-600-normal-7QWjJBsF.woff2 +0 -0
- package/out/web/assets/sarabun-latin-400-normal-DmguIS-7.woff +0 -0
- package/out/web/assets/sarabun-latin-400-normal-URPBxl-K.woff2 +0 -0
- package/out/web/assets/sarabun-latin-500-normal-BjUTcdxu.woff2 +0 -0
- package/out/web/assets/sarabun-latin-500-normal-CJexTAnl.woff +0 -0
- package/out/web/assets/sarabun-latin-600-normal-9FonlkKx.woff +0 -0
- package/out/web/assets/sarabun-latin-600-normal-DMD3TROr.woff2 +0 -0
- package/out/web/assets/sarabun-latin-ext-400-normal-BbP7GpLL.woff2 +0 -0
- package/out/web/assets/sarabun-latin-ext-400-normal-BsmTn0SS.woff +0 -0
- package/out/web/assets/sarabun-latin-ext-500-normal-Cot16Bd2.woff +0 -0
- package/out/web/assets/sarabun-latin-ext-500-normal-DE1LysqI.woff2 +0 -0
- package/out/web/assets/sarabun-latin-ext-600-normal-0tXu9x_i.woff +0 -0
- package/out/web/assets/sarabun-latin-ext-600-normal-BRxjKUQ6.woff2 +0 -0
- package/out/web/assets/sarabun-thai-400-normal-C2DaJlKK.woff2 +0 -0
- package/out/web/assets/sarabun-thai-400-normal-CH-UbWgg.woff +0 -0
- package/out/web/assets/sarabun-thai-500-normal-BVssjame.woff2 +0 -0
- package/out/web/assets/sarabun-thai-500-normal-C3iDLEzi.woff +0 -0
- package/out/web/assets/sarabun-thai-600-normal-3dIAQ-_s.woff2 +0 -0
- package/out/web/assets/sarabun-thai-600-normal-DWKeTe6a.woff +0 -0
- package/out/web/assets/sarabun-vietnamese-400-normal-DPfkSCMy.woff +0 -0
- package/out/web/assets/sarabun-vietnamese-400-normal-igdrzAGY.woff2 +0 -0
- package/out/web/assets/sarabun-vietnamese-500-normal-CpRVvGRU.woff +0 -0
- package/out/web/assets/sarabun-vietnamese-500-normal-Dpe7Br9m.woff2 +0 -0
- package/out/web/assets/sarabun-vietnamese-600-normal-BMbOgAW-.woff +0 -0
- package/out/web/assets/sarabun-vietnamese-600-normal-DkxrOlLB.woff2 +0 -0
- package/out/web/assets/stateDiagram-v2-4QOOHH4V-s6P9g2_b.js +0 -1
- package/out/web/assets/swimlanesDiagram-VK2B7HYN-CV9fOLrV.js +0 -8
- package/out/web/assets/tauri-CNrHQ2Gb.js +0 -3
- package/out/web/assets/window-DkIhgI7B.js +0 -1
|
@@ -2,7 +2,7 @@ use std::io::{self, Write};
|
|
|
2
2
|
use std::path::Path;
|
|
3
3
|
use std::sync::{
|
|
4
4
|
Arc, Mutex,
|
|
5
|
-
atomic::{AtomicBool, Ordering},
|
|
5
|
+
atomic::{AtomicBool, AtomicUsize, Ordering},
|
|
6
6
|
};
|
|
7
7
|
use std::time::{Duration, Instant};
|
|
8
8
|
|
|
@@ -27,19 +27,21 @@ mod markdown_render;
|
|
|
27
27
|
use approval_prompts::*;
|
|
28
28
|
use diff_render::*;
|
|
29
29
|
pub(crate) use live_status::*;
|
|
30
|
-
use markdown_render::*;
|
|
31
|
-
|
|
32
|
-
const RESET: &str =
|
|
33
|
-
const MINT: &str =
|
|
34
|
-
const GREEN: &str =
|
|
35
|
-
const RED: &str =
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
30
|
+
pub(crate) use markdown_render::*;
|
|
31
|
+
|
|
32
|
+
const RESET: &str = crate::terminal_theme::ANSI_RESET;
|
|
33
|
+
const MINT: &str = crate::terminal_theme::ANSI_ACCENT;
|
|
34
|
+
const GREEN: &str = crate::terminal_theme::ANSI_ACCENT;
|
|
35
|
+
const RED: &str = crate::terminal_theme::ANSI_ERROR;
|
|
36
|
+
const YELLOW: &str = crate::terminal_theme::ANSI_WARNING;
|
|
37
|
+
const BLUE: &str = crate::terminal_theme::ANSI_BLUE;
|
|
38
|
+
const CYAN: &str = crate::terminal_theme::ANSI_BLUE;
|
|
39
|
+
const DIM: &str = crate::terminal_theme::ANSI_MUTED;
|
|
40
|
+
const BRIGHT: &str = crate::terminal_theme::ANSI_BRIGHT_TEXT;
|
|
41
|
+
const WHITE: &str = crate::terminal_theme::ANSI_TEXT;
|
|
42
|
+
const BOLD: &str = crate::terminal_theme::ANSI_BOLD;
|
|
43
|
+
const BG_ADD: &str = crate::terminal_theme::ANSI_ADD;
|
|
44
|
+
const BG_DEL: &str = crate::terminal_theme::ANSI_DELETE;
|
|
43
45
|
|
|
44
46
|
#[derive(Debug, Clone, Default)]
|
|
45
47
|
pub struct AgentOptions {
|
|
@@ -58,6 +60,11 @@ pub struct AgentOptions {
|
|
|
58
60
|
/// configured MCP server — set by an `@servername` mention in the typed
|
|
59
61
|
/// query, mirroring the GUI composer's `@` mention picker.
|
|
60
62
|
pub pinned_mcp_server: Option<String>,
|
|
63
|
+
/// Chat ID / session ID for this turn. Defaults to `CHAT_CLI_ID` if omitted.
|
|
64
|
+
pub chat_id: Option<String>,
|
|
65
|
+
/// Full-screen UI event sink. When present, agent output never writes
|
|
66
|
+
/// directly to stdout and stdin remains owned by the TUI controller.
|
|
67
|
+
pub tui: Option<crate::interactive::TuiHandle>,
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
pub async fn run_code_agent(task: &str, root: &Path, config: &MintConfig) -> Result<AgentResult> {
|
|
@@ -327,6 +334,187 @@ fn save_plan_file(root: &Path, plan: &str) -> io::Result<std::path::PathBuf> {
|
|
|
327
334
|
Ok(path)
|
|
328
335
|
}
|
|
329
336
|
|
|
337
|
+
fn tui_persistable_approval(
|
|
338
|
+
tui: &crate::interactive::TuiHandle,
|
|
339
|
+
tool: &str,
|
|
340
|
+
subject: &str,
|
|
341
|
+
title: &str,
|
|
342
|
+
body: String,
|
|
343
|
+
root: &Path,
|
|
344
|
+
permission_rules: &mut Vec<PermissionRule>,
|
|
345
|
+
) -> ApprovalOutcome {
|
|
346
|
+
if let Some(decision) = permission_decision_for(permission_rules, tool, subject, root) {
|
|
347
|
+
return match decision {
|
|
348
|
+
PermissionDecision::Allow => ApprovalOutcome::Approved,
|
|
349
|
+
PermissionDecision::Deny => ApprovalOutcome::Denied,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
match tui.choose(
|
|
353
|
+
title,
|
|
354
|
+
body,
|
|
355
|
+
vec![
|
|
356
|
+
"Yes".into(),
|
|
357
|
+
"Yes, allow for this session".into(),
|
|
358
|
+
"No".into(),
|
|
359
|
+
],
|
|
360
|
+
) {
|
|
361
|
+
Some(0) => ApprovalOutcome::Approved,
|
|
362
|
+
Some(1) => {
|
|
363
|
+
permission_rules.push(PermissionRule {
|
|
364
|
+
tool: tool.to_owned(),
|
|
365
|
+
pattern: subject.to_owned(),
|
|
366
|
+
decision: PermissionDecision::Allow,
|
|
367
|
+
project_root: None,
|
|
368
|
+
});
|
|
369
|
+
ApprovalOutcome::Approved
|
|
370
|
+
}
|
|
371
|
+
_ => ApprovalOutcome::Denied,
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
fn tui_approval(
|
|
376
|
+
tui: &crate::interactive::TuiHandle,
|
|
377
|
+
approval: &AgentApproval,
|
|
378
|
+
root: &Path,
|
|
379
|
+
permission_rules: &mut Vec<PermissionRule>,
|
|
380
|
+
) -> Result<ApprovalOutcome, String> {
|
|
381
|
+
let yes_no = |title: &str, body: String| match tui.choose(
|
|
382
|
+
title,
|
|
383
|
+
body,
|
|
384
|
+
vec!["Approve".into(), "Deny".into()],
|
|
385
|
+
) {
|
|
386
|
+
Some(0) => ApprovalOutcome::Approved,
|
|
387
|
+
_ => ApprovalOutcome::Denied,
|
|
388
|
+
};
|
|
389
|
+
Ok(match approval {
|
|
390
|
+
AgentApproval::WriteFile { path, diff, .. } => tui_persistable_approval(
|
|
391
|
+
tui,
|
|
392
|
+
"write_file",
|
|
393
|
+
path,
|
|
394
|
+
"Create file",
|
|
395
|
+
format!("Path: {path}\n\n{diff}"),
|
|
396
|
+
root,
|
|
397
|
+
permission_rules,
|
|
398
|
+
),
|
|
399
|
+
AgentApproval::ApplyPatch { path, diff, .. } => tui_persistable_approval(
|
|
400
|
+
tui,
|
|
401
|
+
"apply_patch",
|
|
402
|
+
path,
|
|
403
|
+
"Update file",
|
|
404
|
+
format!("Path: {path}\n\n{diff}"),
|
|
405
|
+
root,
|
|
406
|
+
permission_rules,
|
|
407
|
+
),
|
|
408
|
+
AgentApproval::RunShell {
|
|
409
|
+
command,
|
|
410
|
+
mode,
|
|
411
|
+
background,
|
|
412
|
+
} => tui_persistable_approval(
|
|
413
|
+
tui,
|
|
414
|
+
"run_shell",
|
|
415
|
+
command,
|
|
416
|
+
"Local shell command",
|
|
417
|
+
format!(
|
|
418
|
+
"Command: {command}\nMode: {mode}\nBackground: {}",
|
|
419
|
+
if *background { "yes" } else { "no" }
|
|
420
|
+
),
|
|
421
|
+
root,
|
|
422
|
+
permission_rules,
|
|
423
|
+
),
|
|
424
|
+
AgentApproval::NoteWrite { path, .. } => tui_persistable_approval(
|
|
425
|
+
tui,
|
|
426
|
+
"note_write",
|
|
427
|
+
path,
|
|
428
|
+
"Create note",
|
|
429
|
+
format!("Path: {path}"),
|
|
430
|
+
root,
|
|
431
|
+
permission_rules,
|
|
432
|
+
),
|
|
433
|
+
AgentApproval::RunPlugin { name, instruction } => {
|
|
434
|
+
let subject = format!("{name}: {instruction}");
|
|
435
|
+
tui_persistable_approval(
|
|
436
|
+
tui,
|
|
437
|
+
"run_plugin",
|
|
438
|
+
&subject,
|
|
439
|
+
"Run plugin",
|
|
440
|
+
format!("Plugin: {name}\n\n{instruction}"),
|
|
441
|
+
root,
|
|
442
|
+
permission_rules,
|
|
443
|
+
)
|
|
444
|
+
}
|
|
445
|
+
AgentApproval::McpTool {
|
|
446
|
+
server,
|
|
447
|
+
tool,
|
|
448
|
+
arguments,
|
|
449
|
+
} => {
|
|
450
|
+
let body = format!("Server: {server}\nTool: {tool}\nArguments: {arguments}");
|
|
451
|
+
if tui.choose(
|
|
452
|
+
"MCP tool call",
|
|
453
|
+
body,
|
|
454
|
+
vec![format!("Allow all tools on {server}"), "No".into()],
|
|
455
|
+
) == Some(0)
|
|
456
|
+
{
|
|
457
|
+
ApprovalOutcome::Intercepted(mint_core::MCP_ALLOW_ALL_SENTINEL.to_owned())
|
|
458
|
+
} else {
|
|
459
|
+
ApprovalOutcome::Denied
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
AgentApproval::UserApproval { title, prompt } => yes_no(title, prompt.to_owned()),
|
|
463
|
+
AgentApproval::EnterPlanMode { reason } => yes_no("Enter plan mode?", reason.to_owned()),
|
|
464
|
+
AgentApproval::ExitPlanMode { plan } => {
|
|
465
|
+
let outcome = yes_no("Review plan", plan.to_owned());
|
|
466
|
+
if outcome == ApprovalOutcome::Approved {
|
|
467
|
+
match save_plan_file(root, plan) {
|
|
468
|
+
Ok(path) => tui.push_notice(format!("Plan saved to {}", path.display())),
|
|
469
|
+
Err(error) => tui.push_notice(format!("Could not save plan: {error}")),
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
outcome
|
|
473
|
+
}
|
|
474
|
+
AgentApproval::AskUser {
|
|
475
|
+
question,
|
|
476
|
+
options,
|
|
477
|
+
header,
|
|
478
|
+
multi_select,
|
|
479
|
+
} => {
|
|
480
|
+
let title = header.as_deref().unwrap_or("Mint needs your input");
|
|
481
|
+
if options.is_empty() {
|
|
482
|
+
tui.prompt_text(title, question)
|
|
483
|
+
.map(ApprovalOutcome::Intercepted)
|
|
484
|
+
.unwrap_or(ApprovalOutcome::Denied)
|
|
485
|
+
} else {
|
|
486
|
+
let labels = options
|
|
487
|
+
.iter()
|
|
488
|
+
.map(|option| match &option.description {
|
|
489
|
+
Some(description) => format!("{} — {description}", option.label),
|
|
490
|
+
None => option.label.clone(),
|
|
491
|
+
})
|
|
492
|
+
.collect::<Vec<_>>();
|
|
493
|
+
if *multi_select {
|
|
494
|
+
let selected = tui.choose_many(title, question, labels);
|
|
495
|
+
let answer = selected
|
|
496
|
+
.into_iter()
|
|
497
|
+
.filter_map(|index| options.get(index))
|
|
498
|
+
.map(|option| option.label.as_str())
|
|
499
|
+
.collect::<Vec<_>>()
|
|
500
|
+
.join(", ");
|
|
501
|
+
if answer.is_empty() {
|
|
502
|
+
ApprovalOutcome::Denied
|
|
503
|
+
} else {
|
|
504
|
+
ApprovalOutcome::Intercepted(answer)
|
|
505
|
+
}
|
|
506
|
+
} else {
|
|
507
|
+
let choice = tui.choose(title, question, labels);
|
|
508
|
+
choice
|
|
509
|
+
.and_then(|choice| options.get(choice))
|
|
510
|
+
.map(|option| ApprovalOutcome::Intercepted(option.label.clone()))
|
|
511
|
+
.unwrap_or(ApprovalOutcome::Denied)
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
})
|
|
516
|
+
}
|
|
517
|
+
|
|
330
518
|
pub async fn run_code_agent_with_options(
|
|
331
519
|
task: &str,
|
|
332
520
|
root: &Path,
|
|
@@ -338,19 +526,23 @@ pub async fn run_code_agent_with_options(
|
|
|
338
526
|
draft_out: Arc<Mutex<Option<String>>>,
|
|
339
527
|
) -> Result<AgentResult> {
|
|
340
528
|
let started_at = Instant::now();
|
|
529
|
+
crate::interactive::clear_last_thought();
|
|
341
530
|
let thinking_verb = random_thinking_verb();
|
|
342
531
|
let approval_active = Arc::new(AtomicBool::new(false));
|
|
343
532
|
let agent_done = Arc::new(AtomicBool::new(false));
|
|
344
|
-
//
|
|
533
|
+
// Number of tools between start and finish — tells the periodic timer
|
|
345
534
|
// below not to overwrite the live status with "Thinking (Xs)…" text
|
|
346
|
-
// while
|
|
347
|
-
//
|
|
348
|
-
|
|
349
|
-
let tool_running = Arc::new(AtomicBool::new(false));
|
|
535
|
+
// while any tool (e.g. a shell command) is still in flight. A count is
|
|
536
|
+
// needed because parallel tools may finish at different times.
|
|
537
|
+
let tool_running = Arc::new(AtomicUsize::new(0));
|
|
350
538
|
let live_status = Arc::new(Mutex::new(LiveStatus::default()));
|
|
351
539
|
{
|
|
352
540
|
use crossterm::tty::IsTty;
|
|
541
|
+
if let Ok(mut status) = live_status.lock() {
|
|
542
|
+
status.tui = options.tui.clone();
|
|
543
|
+
}
|
|
353
544
|
if options.queueing
|
|
545
|
+
&& options.tui.is_none()
|
|
354
546
|
&& !options.fast_mode
|
|
355
547
|
&& io::stdout().is_tty()
|
|
356
548
|
&& io::stdin().is_tty()
|
|
@@ -372,6 +564,11 @@ pub async fn run_code_agent_with_options(
|
|
|
372
564
|
|
|
373
565
|
let approve_cb = |approval: &AgentApproval| -> Result<ApprovalOutcome, String> {
|
|
374
566
|
approve_approval_active.store(true, Ordering::Relaxed);
|
|
567
|
+
if let Some(tui) = &options.tui {
|
|
568
|
+
let result = tui_approval(tui, approval, root, &mut permission_rules);
|
|
569
|
+
approve_approval_active.store(false, Ordering::Relaxed);
|
|
570
|
+
return result;
|
|
571
|
+
}
|
|
375
572
|
// Synchronous, not polled: `wait_for_escape_interrupt` holds raw
|
|
376
573
|
// mode continuously while the queueing box is live (see its docs),
|
|
377
574
|
// and only reacts to `approval_active` on its next ~15ms tick. Every
|
|
@@ -600,7 +797,7 @@ pub async fn run_code_agent_with_options(
|
|
|
600
797
|
if timer_agent_done.load(Ordering::Relaxed) {
|
|
601
798
|
break;
|
|
602
799
|
}
|
|
603
|
-
if
|
|
800
|
+
if timer_tool_running.load(Ordering::Relaxed) == 0 {
|
|
604
801
|
status.thinking = Some(
|
|
605
802
|
if let Some((attempt, max_attempts)) = status.waiting_for_network {
|
|
606
803
|
waiting_for_network_label(attempt, max_attempts)
|
|
@@ -648,6 +845,7 @@ pub async fn run_code_agent_with_options(
|
|
|
648
845
|
} => {
|
|
649
846
|
if !options.fast_mode
|
|
650
847
|
&& !progress_approval_active.load(Ordering::Relaxed)
|
|
848
|
+
&& progress_tool_running.load(Ordering::Relaxed) == 0
|
|
651
849
|
&& let Ok(mut status) = progress_live_status.lock()
|
|
652
850
|
{
|
|
653
851
|
status.context_pct = context_pct;
|
|
@@ -655,6 +853,7 @@ pub async fn run_code_agent_with_options(
|
|
|
655
853
|
status.input_tokens = input_tokens;
|
|
656
854
|
status.generated_tokens = generated_tokens;
|
|
657
855
|
status.estimated_tokens = estimated_tokens;
|
|
856
|
+
status.active_tool_started = None;
|
|
658
857
|
// Re-derive the animation rate from this real data point
|
|
659
858
|
// every time one arrives (cumulative generated tokens /
|
|
660
859
|
// elapsed turn time so far) — see
|
|
@@ -695,6 +894,7 @@ pub async fn run_code_agent_with_options(
|
|
|
695
894
|
} => {
|
|
696
895
|
if !options.fast_mode
|
|
697
896
|
&& !progress_approval_active.load(Ordering::Relaxed)
|
|
897
|
+
&& progress_tool_running.load(Ordering::Relaxed) == 0
|
|
698
898
|
&& let Ok(mut status) = progress_live_status.lock()
|
|
699
899
|
{
|
|
700
900
|
status.waiting_for_network = Some((attempt, max_attempts));
|
|
@@ -707,8 +907,49 @@ pub async fn run_code_agent_with_options(
|
|
|
707
907
|
&& !progress_approval_active.load(Ordering::Relaxed)
|
|
708
908
|
&& let Ok(mut status) = progress_live_status.lock()
|
|
709
909
|
{
|
|
710
|
-
|
|
711
|
-
|
|
910
|
+
commit_activity_snapshot_if_idle(
|
|
911
|
+
&mut status,
|
|
912
|
+
progress_tool_running.load(Ordering::Relaxed),
|
|
913
|
+
);
|
|
914
|
+
let elapsed = started_at.elapsed();
|
|
915
|
+
print_timeline_note(&mut status, &thought, elapsed, None);
|
|
916
|
+
status.thinking = None;
|
|
917
|
+
status.waiting_for_network = None;
|
|
918
|
+
render_live_status(&mut status);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
AgentProgress::ThinkingDelta {
|
|
922
|
+
id,
|
|
923
|
+
delta,
|
|
924
|
+
elapsed_ms,
|
|
925
|
+
} => {
|
|
926
|
+
if !options.fast_mode {
|
|
927
|
+
let elapsed = Duration::from_millis(elapsed_ms);
|
|
928
|
+
let elapsed_str = crate::interactive::format_thought_elapsed(elapsed);
|
|
929
|
+
crate::interactive::append_thought_delta(&id, &delta, &elapsed_str);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
AgentProgress::ExtendedThinking {
|
|
933
|
+
id,
|
|
934
|
+
thought,
|
|
935
|
+
elapsed_ms,
|
|
936
|
+
} => {
|
|
937
|
+
if !options.fast_mode
|
|
938
|
+
&& !progress_approval_active.load(Ordering::Relaxed)
|
|
939
|
+
&& let Ok(mut status) = progress_live_status.lock()
|
|
940
|
+
{
|
|
941
|
+
commit_activity_snapshot_if_idle(
|
|
942
|
+
&mut status,
|
|
943
|
+
progress_tool_running.load(Ordering::Relaxed),
|
|
944
|
+
);
|
|
945
|
+
let elapsed = elapsed_ms
|
|
946
|
+
.map(Duration::from_millis)
|
|
947
|
+
.unwrap_or_else(|| started_at.elapsed());
|
|
948
|
+
// Extended thinking gets the same timeline-note treatment
|
|
949
|
+
// as regular Thought — `print_timeline_note` already
|
|
950
|
+
// distinguishes internal CoT (long) from short notes via
|
|
951
|
+
// `is_internal_cot` and renders accordingly.
|
|
952
|
+
print_timeline_note(&mut status, &thought, elapsed, id.as_deref());
|
|
712
953
|
status.thinking = None;
|
|
713
954
|
status.waiting_for_network = None;
|
|
714
955
|
render_live_status(&mut status);
|
|
@@ -719,8 +960,13 @@ pub async fn run_code_agent_with_options(
|
|
|
719
960
|
input,
|
|
720
961
|
subagent,
|
|
721
962
|
} => {
|
|
722
|
-
progress_tool_running.
|
|
723
|
-
if !options.fast_mode
|
|
963
|
+
progress_tool_running.fetch_add(1, Ordering::Relaxed);
|
|
964
|
+
if !options.fast_mode
|
|
965
|
+
&& (options.tui.is_some() || !progress_approval_active.load(Ordering::Relaxed))
|
|
966
|
+
{
|
|
967
|
+
if let Ok(mut status) = progress_live_status.lock() {
|
|
968
|
+
status.active_tool_started = Some(std::time::Instant::now());
|
|
969
|
+
}
|
|
724
970
|
if let Some(subagent_name) = subagent {
|
|
725
971
|
// A tool call happening *inside* a running subagent's own
|
|
726
972
|
// nested loop (tagged by `dispatch_one_subagent`) — render
|
|
@@ -794,8 +1040,20 @@ pub async fn run_code_agent_with_options(
|
|
|
794
1040
|
result,
|
|
795
1041
|
subagent,
|
|
796
1042
|
} => {
|
|
797
|
-
progress_tool_running
|
|
798
|
-
|
|
1043
|
+
let remaining = progress_tool_running
|
|
1044
|
+
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |running| {
|
|
1045
|
+
Some(running.saturating_sub(1))
|
|
1046
|
+
})
|
|
1047
|
+
.unwrap_or(0)
|
|
1048
|
+
.saturating_sub(1);
|
|
1049
|
+
if !options.fast_mode
|
|
1050
|
+
&& (options.tui.is_some() || !progress_approval_active.load(Ordering::Relaxed))
|
|
1051
|
+
{
|
|
1052
|
+
if remaining == 0
|
|
1053
|
+
&& let Ok(mut status) = progress_live_status.lock()
|
|
1054
|
+
{
|
|
1055
|
+
status.active_tool_started = None;
|
|
1056
|
+
}
|
|
799
1057
|
if subagent.is_some() {
|
|
800
1058
|
// Nested call inside a subagent finished — the ToolStart
|
|
801
1059
|
// line already shown covers it; only the command-output
|
|
@@ -890,7 +1148,12 @@ pub async fn run_code_agent_with_options(
|
|
|
890
1148
|
if let Ok(mut status) = progress_live_status.lock() {
|
|
891
1149
|
status.thinking = None;
|
|
892
1150
|
status.waiting_for_network = None;
|
|
893
|
-
let mut card = format!(
|
|
1151
|
+
let mut card = format!(
|
|
1152
|
+
"{}┌─ Plan: {} {}\n",
|
|
1153
|
+
crate::terminal_theme::ANSI_ACCENT_BOLD,
|
|
1154
|
+
plan.objective,
|
|
1155
|
+
crate::terminal_theme::ANSI_RESET
|
|
1156
|
+
);
|
|
894
1157
|
for task in &plan.tasks {
|
|
895
1158
|
let mark = match task.status.as_str() {
|
|
896
1159
|
"completed" => "\x1b[32m[✓]\x1b[0m",
|
|
@@ -900,7 +1163,11 @@ pub async fn run_code_agent_with_options(
|
|
|
900
1163
|
};
|
|
901
1164
|
card.push_str(&format!("│ {} {}\n", mark, task.title));
|
|
902
1165
|
}
|
|
903
|
-
card.push_str(
|
|
1166
|
+
card.push_str(&format!(
|
|
1167
|
+
"{}└────────────────────────────────────────{}",
|
|
1168
|
+
crate::terminal_theme::ANSI_ACCENT_BOLD,
|
|
1169
|
+
crate::terminal_theme::ANSI_RESET
|
|
1170
|
+
));
|
|
904
1171
|
status.tasks.push(card.into());
|
|
905
1172
|
render_live_status(&mut status);
|
|
906
1173
|
}
|
|
@@ -909,174 +1176,76 @@ pub async fn run_code_agent_with_options(
|
|
|
909
1176
|
if let Ok(mut status) = progress_live_status.lock() {
|
|
910
1177
|
status.thinking = None;
|
|
911
1178
|
status.waiting_for_network = None;
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
format!("{}
|
|
927
|
-
} else if
|
|
928
|
-
format!("{}
|
|
1179
|
+
commit_activity_snapshot(&mut status);
|
|
1180
|
+
|
|
1181
|
+
let failed_tools: Vec<_> = summary
|
|
1182
|
+
.tool_timeline
|
|
1183
|
+
.iter()
|
|
1184
|
+
.filter(|record| !record.success)
|
|
1185
|
+
.collect();
|
|
1186
|
+
let outcome = if !failed_tools.is_empty() {
|
|
1187
|
+
format!(
|
|
1188
|
+
"{YELLOW}⚠ Completed with {} failure{}{RESET}",
|
|
1189
|
+
failed_tools.len(),
|
|
1190
|
+
if failed_tools.len() == 1 { "" } else { "s" }
|
|
1191
|
+
)
|
|
1192
|
+
} else if summary.outcome == "ROLLED_BACK" {
|
|
1193
|
+
format!("{YELLOW}↺ Rolled back{RESET}")
|
|
1194
|
+
} else if summary.outcome == "FAILED" {
|
|
1195
|
+
format!("{RED}✗ Failed{RESET}")
|
|
929
1196
|
} else {
|
|
930
|
-
format!("{}
|
|
1197
|
+
format!("{GREEN}✓ Completed{RESET}")
|
|
931
1198
|
};
|
|
932
|
-
let
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
│ Duration: {:.1}s\n\
|
|
936
|
-
│ Tokens: {}\n\
|
|
937
|
-
│ Tools: {} calls ({} retries)\n\
|
|
938
|
-
│ Files: {}\n",
|
|
939
|
-
summary.run_id,
|
|
940
|
-
outcome_str,
|
|
1199
|
+
let file_count = summary.files_changed.len();
|
|
1200
|
+
let mut lines = vec![format!(
|
|
1201
|
+
" {outcome} {DIM}in {:.1}s · {} tool{} · {} file{} changed{RESET}",
|
|
941
1202
|
summary.duration_secs,
|
|
942
|
-
tokens_k,
|
|
943
1203
|
summary.tool_calls_count,
|
|
944
|
-
summary.
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
}
|
|
962
|
-
""
|
|
963
|
-
};
|
|
964
|
-
let target_desc = if rec.target.is_empty() {
|
|
965
|
-
String::new()
|
|
966
|
-
} else {
|
|
967
|
-
format!(" [{}]", rec.target)
|
|
968
|
-
};
|
|
969
|
-
card.push_str(&format!(
|
|
970
|
-
"│ {:02}s {} {}{}{}\n",
|
|
971
|
-
rec.step, status_icon, rec.action, target_desc, note
|
|
972
|
-
));
|
|
1204
|
+
if summary.tool_calls_count == 1 {
|
|
1205
|
+
""
|
|
1206
|
+
} else {
|
|
1207
|
+
"s"
|
|
1208
|
+
},
|
|
1209
|
+
file_count,
|
|
1210
|
+
if file_count == 1 { "" } else { "s" },
|
|
1211
|
+
)];
|
|
1212
|
+
|
|
1213
|
+
if file_count > 0 {
|
|
1214
|
+
let mut names: Vec<String> = summary
|
|
1215
|
+
.files_changed
|
|
1216
|
+
.iter()
|
|
1217
|
+
.take(6)
|
|
1218
|
+
.map(|path| display_tool_target(path))
|
|
1219
|
+
.collect();
|
|
1220
|
+
if file_count > names.len() {
|
|
1221
|
+
names.push(format!("+{} more", file_count - names.len()));
|
|
973
1222
|
}
|
|
1223
|
+
lines.push(format!(" {DIM}└ {}{RESET}", names.join(", ")));
|
|
974
1224
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1225
|
+
for record in failed_tools.into_iter().take(3) {
|
|
1226
|
+
let target = if record.target.is_empty() {
|
|
1227
|
+
record.action.clone()
|
|
1228
|
+
} else {
|
|
1229
|
+
format!("{} [{}]", record.action, truncate_line(&record.target, 72))
|
|
1230
|
+
};
|
|
1231
|
+
lines.push(format!(" {RED}└ {target} failed{RESET}"));
|
|
1232
|
+
}
|
|
1233
|
+
insert_permanent_lines(&mut status, &lines);
|
|
978
1234
|
}
|
|
979
1235
|
}
|
|
980
1236
|
}
|
|
981
1237
|
};
|
|
982
1238
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
let on_chunk = |
|
|
986
|
-
// Flip this *before* touching anything else — `agent_loop` still has
|
|
987
|
-
// async work left (memory writes, etc.) after calling `on_chunk`, so
|
|
988
|
-
// the real "done" store below (after `agent_loop.await`) fires too
|
|
989
|
-
// late: the periodic status ticker keeps polling every 150ms in the
|
|
990
|
-
// meantime and, seeing `agent_done` still false, unconditionally
|
|
991
|
-
// resurrects `status.thinking` and re-renders — reconstructing a
|
|
992
|
-
// fresh inline TUI viewport mid-print if that lands while the plain
|
|
993
|
-
// `print!`s below are still spooling out a long answer (e.g. a big
|
|
994
|
-
// markdown table), corrupting the terminal. Setting it here closes
|
|
995
|
-
// that window instead of only the (much narrower) one the
|
|
996
|
-
// `accepting_input` flag below covers.
|
|
997
|
-
chunk_agent_done.store(true, Ordering::Relaxed);
|
|
998
|
-
let mut committed_activity = false;
|
|
999
|
-
if !options.fast_mode
|
|
1000
|
-
&& let Ok(mut status) = chunk_live_status.lock()
|
|
1001
|
-
{
|
|
1002
|
-
status.thinking = None;
|
|
1003
|
-
status.waiting_for_network = None;
|
|
1004
|
-
// Stop accepting keystrokes for the queueing box before it's torn
|
|
1005
|
-
// down below — otherwise a keypress landing between this
|
|
1006
|
-
// `clear_live_status` and the final answer's plain `println!`
|
|
1007
|
-
// would resurrect the box (via `render_live_status`'s lazy
|
|
1008
|
-
// `InlineTui::ensure`) at whatever the cursor's current position
|
|
1009
|
-
// happens to be, underneath the answer that just printed.
|
|
1010
|
-
status.accepting_input = false;
|
|
1011
|
-
committed_activity = commit_activity_snapshot(&mut status);
|
|
1012
|
-
clear_live_status(&mut status);
|
|
1013
|
-
}
|
|
1014
|
-
// Same reasoning as `approve_cb`: drop raw mode synchronously,
|
|
1015
|
-
// right here, rather than leaving `wait_for_escape_interrupt` to
|
|
1016
|
-
// notice `accepting_input` went false on its next tick — the prints
|
|
1017
|
-
// below need cooked mode's `\n` → `\r\n` translation immediately.
|
|
1018
|
-
let _ = crossterm::terminal::disable_raw_mode();
|
|
1019
|
-
let formatted_summary = format_markdown_bold(&sanitize_latex(&summary));
|
|
1020
|
-
// `commit_activity_snapshot` above already ended on a blank line when
|
|
1021
|
-
// it committed anything (e.g. a tool-use activity block) — printing
|
|
1022
|
-
// this leading "\n" unconditionally on top of that stacked two blank
|
|
1023
|
-
// lines before every answer that followed tool use.
|
|
1024
|
-
if committed_activity {
|
|
1025
|
-
print!(" {MINT}Mint:{RESET} ");
|
|
1026
|
-
} else {
|
|
1027
|
-
print!("\n {MINT}Mint:{RESET} ");
|
|
1028
|
-
}
|
|
1029
|
-
avatar_bridge.on_talking(true);
|
|
1030
|
-
render_live_summary(&formatted_summary);
|
|
1031
|
-
avatar_bridge.on_talking(false);
|
|
1032
|
-
|
|
1033
|
-
// Print web search sources if any were collected (grouped by domain)
|
|
1034
|
-
if let Ok(mut status) = chunk_live_status.lock()
|
|
1035
|
-
&& !status.web_sources.is_empty()
|
|
1036
|
-
{
|
|
1037
|
-
println!();
|
|
1038
|
-
println!(" {DIM}Sources:{RESET}");
|
|
1039
|
-
|
|
1040
|
-
let mut domain_groups: Vec<(String, Vec<(String, String)>)> = Vec::new();
|
|
1041
|
-
for (title, url) in status.web_sources.drain(..) {
|
|
1042
|
-
let domain = extract_domain(&url);
|
|
1043
|
-
if let Some(group) = domain_groups.iter_mut().find(|(d, _)| d == &domain) {
|
|
1044
|
-
group.1.push((title, url));
|
|
1045
|
-
} else {
|
|
1046
|
-
domain_groups.push((domain, vec![(title, url)]));
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
for (i, (domain, items)) in domain_groups.iter().enumerate() {
|
|
1051
|
-
let (first_title, first_url) = &items[0];
|
|
1052
|
-
let extra_count = items.len() - 1;
|
|
1053
|
-
if extra_count > 0 {
|
|
1054
|
-
println!(
|
|
1055
|
-
" {DIM}{}.{RESET} {BLUE}{}{RESET} {DIM}({}){RESET} {CYAN}[+{} extra]{RESET}",
|
|
1056
|
-
i + 1,
|
|
1057
|
-
first_title,
|
|
1058
|
-
domain,
|
|
1059
|
-
extra_count
|
|
1060
|
-
);
|
|
1061
|
-
} else {
|
|
1062
|
-
println!(
|
|
1063
|
-
" {DIM}{}.{RESET} {BLUE}{}{RESET} {DIM}({}){RESET}",
|
|
1064
|
-
i + 1,
|
|
1065
|
-
first_title,
|
|
1066
|
-
domain
|
|
1067
|
-
);
|
|
1068
|
-
}
|
|
1069
|
-
println!(" {DIM}{}{RESET}", first_url);
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
println!();
|
|
1074
|
-
};
|
|
1239
|
+
// Providers may still stream internally, but the CLI deliberately waits
|
|
1240
|
+
// for AgentResult.summary so it can render one complete Markdown document.
|
|
1241
|
+
let on_chunk = |_summary: String| {};
|
|
1075
1242
|
|
|
1076
1243
|
let user_name = MemoryStore::open_default()
|
|
1077
1244
|
.ok()
|
|
1078
1245
|
.and_then(|memory| memory.get_profile("name").ok().flatten());
|
|
1079
1246
|
|
|
1247
|
+
let target_chat_id = options.chat_id.as_deref().unwrap_or(CHAT_CLI_ID);
|
|
1248
|
+
|
|
1080
1249
|
let agent_loop = orchestrate_agent_loop(
|
|
1081
1250
|
config,
|
|
1082
1251
|
task,
|
|
@@ -1084,7 +1253,7 @@ pub async fn run_code_agent_with_options(
|
|
|
1084
1253
|
image_data_uri,
|
|
1085
1254
|
None,
|
|
1086
1255
|
video_data_uri,
|
|
1087
|
-
Some(
|
|
1256
|
+
Some(target_chat_id),
|
|
1088
1257
|
None,
|
|
1089
1258
|
user_name.as_deref(),
|
|
1090
1259
|
options.pinned_mcp_server.as_deref(),
|
|
@@ -1111,51 +1280,53 @@ pub async fn run_code_agent_with_options(
|
|
|
1111
1280
|
status.thinking = None;
|
|
1112
1281
|
status.waiting_for_network = None;
|
|
1113
1282
|
status.accepting_input = false;
|
|
1283
|
+
commit_activity_snapshot(&mut status);
|
|
1114
1284
|
if let Ok(mut out) = queued_out.lock() {
|
|
1115
|
-
*out =
|
|
1285
|
+
*out = options
|
|
1286
|
+
.tui
|
|
1287
|
+
.as_ref()
|
|
1288
|
+
.map(|tui| tui.take_queued())
|
|
1289
|
+
.unwrap_or_else(|| status.queued.clone());
|
|
1116
1290
|
}
|
|
1117
1291
|
if let Ok(mut out) = draft_out.lock() {
|
|
1118
|
-
*out =
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
}
|
|
1124
|
-
if res.is_err() {
|
|
1125
|
-
commit_activity_snapshot(&mut status);
|
|
1292
|
+
*out = options
|
|
1293
|
+
.tui
|
|
1294
|
+
.as_ref()
|
|
1295
|
+
.and_then(|tui| tui.draft())
|
|
1296
|
+
.or_else(|| (!status.draft.is_empty()).then(|| status.draft.iter().collect()));
|
|
1126
1297
|
}
|
|
1127
1298
|
clear_live_status(&mut status);
|
|
1128
1299
|
}
|
|
1129
|
-
let res = res.map_err(|e| anyhow!("{}", e))?;
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1300
|
+
let mut res = res.map_err(|e| anyhow!("{}", e))?;
|
|
1301
|
+
if let Ok(status) = live_status.lock() {
|
|
1302
|
+
if res.input_tokens == 0 && status.input_tokens > 0 {
|
|
1303
|
+
res.input_tokens = status.input_tokens;
|
|
1304
|
+
}
|
|
1305
|
+
if res.generated_tokens == 0 && status.generated_tokens > 0 {
|
|
1306
|
+
res.generated_tokens = status.generated_tokens;
|
|
1307
|
+
}
|
|
1133
1308
|
}
|
|
1134
|
-
|
|
1309
|
+
|
|
1310
|
+
let badge_plain = if let Some(orig_provider) = &res.fallback {
|
|
1311
|
+
let reason_suffix = if let Some(reason) = &res.fallback_reason {
|
|
1312
|
+
format!(" ({reason})")
|
|
1313
|
+
} else {
|
|
1314
|
+
String::new()
|
|
1315
|
+
};
|
|
1135
1316
|
format!(
|
|
1136
|
-
"{} • {} → fallback: {} • {}",
|
|
1137
|
-
|
|
1138
|
-
crate::active_model(
|
|
1139
|
-
|
|
1317
|
+
"{} • {}{} → fallback: {} • {}",
|
|
1318
|
+
orig_provider,
|
|
1319
|
+
crate::active_model(orig_provider, config),
|
|
1320
|
+
reason_suffix,
|
|
1321
|
+
res.provider,
|
|
1140
1322
|
res.model
|
|
1141
1323
|
)
|
|
1142
1324
|
} else {
|
|
1143
1325
|
format!("{} • {}", res.provider, res.model)
|
|
1144
1326
|
};
|
|
1145
1327
|
|
|
1146
|
-
// "─ Worked for {elapsed} • {provider} • {model}" as one *labeled*
|
|
1147
|
-
// divider — filled out with more "─" to the same width the box's own
|
|
1148
|
-
// two divider lines use — rather than the provider/model badge and the
|
|
1149
|
-
// elapsed-time label as two separate short lines followed by a third,
|
|
1150
|
-
// unlabeled full-width divider directly under them. The three used to
|
|
1151
|
-
// look like unrelated elements stacked on top of each other; folding
|
|
1152
|
-
// both labels into one rule reads as a single line doing all three
|
|
1153
|
-
// jobs, matching the box below it.
|
|
1154
1328
|
let (tw, _) = markdown::terminal_size_or_default();
|
|
1155
|
-
let width = tw as usize;
|
|
1156
|
-
// `↑` context stays dim with the rest of the footer; `↓` generated drops
|
|
1157
|
-
// out of DIM for its segment so the "what the model actually produced"
|
|
1158
|
-
// number reads a touch stronger than the divider around it.
|
|
1329
|
+
let width = (tw as usize).saturating_sub(6).max(40);
|
|
1159
1330
|
let tokens_suffix = if res.input_tokens > 0 || res.generated_tokens > 0 {
|
|
1160
1331
|
format!(
|
|
1161
1332
|
" • ↑ {} · {RESET}↓ {}{DIM}",
|
|
@@ -1169,11 +1340,77 @@ pub async fn run_code_agent_with_options(
|
|
|
1169
1340
|
"─ Worked for {}{tokens_suffix} • {badge_plain}",
|
|
1170
1341
|
format_elapsed(started_at.elapsed())
|
|
1171
1342
|
);
|
|
1172
|
-
// Count only visible columns for the fill — `tokens_suffix` carries ANSI
|
|
1173
|
-
// codes that don't occupy screen width.
|
|
1174
1343
|
let label_width = strip_ansi_escapes(&label).chars().count();
|
|
1175
|
-
let fill_len = width.saturating_sub(
|
|
1176
|
-
|
|
1344
|
+
let fill_len = width.saturating_sub(label_width + 1);
|
|
1345
|
+
let worked_line = format!("{DIM}{label} {}{RESET}", "─".repeat(fill_len));
|
|
1346
|
+
|
|
1347
|
+
if let Some(tui) = &options.tui {
|
|
1348
|
+
tui.push_assistant(res.summary.clone());
|
|
1349
|
+
if let Ok(mut status) = live_status.lock()
|
|
1350
|
+
&& !status.web_sources.is_empty()
|
|
1351
|
+
{
|
|
1352
|
+
let sources = status
|
|
1353
|
+
.web_sources
|
|
1354
|
+
.drain(..)
|
|
1355
|
+
.enumerate()
|
|
1356
|
+
.map(|(index, (title, url))| format!("{}. {title}\n {url}", index + 1))
|
|
1357
|
+
.collect::<Vec<_>>()
|
|
1358
|
+
.join("\n");
|
|
1359
|
+
tui.push_notice(format!("Sources:\n{sources}"));
|
|
1360
|
+
}
|
|
1361
|
+
tui.push_notice(worked_line);
|
|
1362
|
+
avatar_bridge.on_talking(false);
|
|
1363
|
+
return Ok(res);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
let formatted_summary = format_markdown_bold(&sanitize_latex(&res.summary));
|
|
1367
|
+
print!("\n {MINT}Mint:{RESET} ");
|
|
1368
|
+
avatar_bridge.on_talking(true);
|
|
1369
|
+
render_live_summary(&formatted_summary);
|
|
1370
|
+
avatar_bridge.on_talking(false);
|
|
1371
|
+
println!();
|
|
1372
|
+
|
|
1373
|
+
// Print web search sources once after the streamed answer is complete.
|
|
1374
|
+
if let Ok(mut status) = live_status.lock()
|
|
1375
|
+
&& !status.web_sources.is_empty()
|
|
1376
|
+
{
|
|
1377
|
+
println!(" {DIM}Sources:{RESET}");
|
|
1378
|
+
|
|
1379
|
+
let mut domain_groups: Vec<(String, Vec<(String, String)>)> = Vec::new();
|
|
1380
|
+
for (title, url) in status.web_sources.drain(..) {
|
|
1381
|
+
let domain = extract_domain(&url);
|
|
1382
|
+
if let Some(group) = domain_groups.iter_mut().find(|(d, _)| d == &domain) {
|
|
1383
|
+
group.1.push((title, url));
|
|
1384
|
+
} else {
|
|
1385
|
+
domain_groups.push((domain, vec![(title, url)]));
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
for (i, (domain, items)) in domain_groups.iter().enumerate() {
|
|
1390
|
+
let (first_title, first_url) = &items[0];
|
|
1391
|
+
let extra_count = items.len() - 1;
|
|
1392
|
+
if extra_count > 0 {
|
|
1393
|
+
println!(
|
|
1394
|
+
" {DIM}{}.{RESET} {BLUE}{}{RESET} {DIM}({}){RESET} {CYAN}[+{} extra]{RESET}",
|
|
1395
|
+
i + 1,
|
|
1396
|
+
first_title,
|
|
1397
|
+
domain,
|
|
1398
|
+
extra_count
|
|
1399
|
+
);
|
|
1400
|
+
} else {
|
|
1401
|
+
println!(
|
|
1402
|
+
" {DIM}{}.{RESET} {BLUE}{}{RESET} {DIM}({}){RESET}",
|
|
1403
|
+
i + 1,
|
|
1404
|
+
first_title,
|
|
1405
|
+
domain
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1408
|
+
println!(" {DIM}{}{RESET}", first_url);
|
|
1409
|
+
}
|
|
1410
|
+
println!();
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
println!(" {worked_line}");
|
|
1177
1414
|
|
|
1178
1415
|
Ok(res)
|
|
1179
1416
|
}
|